PageSourceSearch

https://munskankarna.se/dist/js/app.js?v=RCVFVIG3JT1ifzyHQfVX2UDfc4FGEqZmmuCsRZ4v0BM

js munskankarna.se collected 2026-09-24 23:02:42 UTC 2,085,313 bytes, 55,103 lines download raw bytes

vendor: 4,299 bytes, lines 1-2
1/*! jQuery v2.1.4 | (c) 2005, 2015 jQuery Foundation, Inc. | jquery.org/license */
2!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l=a.document,m="2.1.4",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return n.each(this,a,b)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){return!n.isArray(a)&&a-parseFloat(a)+1>=0},isPlainObject:function(a){return"object"!==n.type(a)||a.nodeType||n.isWindow(a)?!1:a.constructor&&!j.call(a.constructor.prototype,"isPrototypeOf")?!1:!0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=l.createElement("script"),b.text=a,l.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=s(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:g.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=s(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(e=d.call(arguments,2),f=function(){return a.apply(b||this,e.concat(d.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:k}),n.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function s(a){var b="length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*ne
vendor: 18,010 bytes, line 2
2w Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+N+"))|)"+L+"*\\]",P=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+O+")*)|.*)\\)|)",Q=new RegExp(L+"+","g"),R=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),S=new RegExp("^"+L+"*,"+L+"*"),T=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),U=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),V=new RegExp(P),W=new RegExp("^"+N+"$"),X={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+O),PSEUDO:new RegExp("^"+P),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,aa=/[+~]/,ba=/'|\\/g,ca=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),da=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ea=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(fa){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],k=b.nodeType,"string"!=typeof a||!a||1!==k&&9!==k&&11!==k)return d;if(!e&&p){if(11!==k&&(f=_.exec(a)))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return H.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName)return H.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=1!==k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(ba,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+ra(o[l]);w=aa.test(a)&&pa(b.parentNode)||b,x=o.join(",")}if(x)try{return H.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function pa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=g.documentElement,e=g.defaultView,e&&e!==e.top&&(e.addEventListener?e.addEventListener("unload",ea,!1):e.attachEvent&&e.attachEvent("onunload",ea)),p=!f(g),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(g.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(g.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!g.getElementsByName||!g.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(g.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="<a id='"+u+"'></a><select id='"+u+"-\f]' msallowcapture=''><option selected=''></option></select>",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){var b=g.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",P)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===g||a.ownerDocument===v&&t(v,a)?-1:b===g||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,h=[a],i=[b];if(!e||!f)return a===g?-1:b===g?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?la(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},g):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ca,da),a[3]=(a[3]||a[4]||a[5]||"").replace(ca,da),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ca,da).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(Q," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(ca,da),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return W.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(ca,da).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:oa(function(){return[0]}),last:oa(function(a,b){return[b-1]}),eq:oa(function(a,b,c){return[0>c?c+b:c]}),even:oa(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:oa(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:oa(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:oa(function(a,b,c){for(var d=0>c?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=ma(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=na(b);function qa(){}qa.prototype=d.filters=d.pseudos,d.setFilters=new qa,g=ga.tokenize=function(a,b){var c,e,f,g,h,i,j,k=z[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){(!c||(e=S.exec(h)))&&(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=T.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(R," ")}),h=h.slice(c.length));for(g in d.filter)!(e=X[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?ga.error(a):z(a,i).slice(0)};function ra(a){for(var b=0,c=a.length,d="";c>b;b++)d+=a[b].value;return d}function sa(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function ta(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ua(a,b,c){for(var d=0,e=b.length;e>d;d++)ga(a,b[d],c);return c}function va(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function wa(a,b,c,d,e,f){return d&&!d[u]&&(d=wa(d)),e&&!e[u]&&(e=wa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ua(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:va(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=va(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=va(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function xa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=sa(function(a){return a===b},h,!0),l=sa(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[sa(ta(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return wa(i>1&&ta(m),i>1&&ra(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&xa(a.slice(i,e)),f>e&&xa(a=a.slice(e)),f>e&&ra(a))}m.push(c)}return ta(m)}function ya(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=F.call(i));s=va(s)}H.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&ga.uniqueSort(i)}return k&&(w=v,j=t),r};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=xa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,ya(e,d)),f.selector=a}return f},i=ga.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ca,da),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ca,da),aa.test(j[0].type)&&pa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&ra(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,aa.test(a)&&pa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ja(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")}
vendor: 11,889 bytes, lines 2-3
2)||ka("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=n.expr.match.needsContext,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^.[^:#\[\.,]*$/;function x(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return g.call(b,a)>=0!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;c>b;b++)if(n.contains(e[b],this))return!0}));for(b=0;c>b;b++)n.find(a,e[b],d);return d=this.pushStack(c>1?n.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(x(this,a||[],!1))},not:function(a){return this.pushStack(x(this,a||[],!0))},is:function(a){return!!x(this,"string"==typeof a&&u.test(a)?n(a):a||[],!1).length}});var y,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=n.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||y).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:l,!0)),v.test(c[1])&&n.isPlainObject(b))for(c in b)n.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}return d=l.getElementById(c[2]),d&&d.parentNode&&(this.length=1,this[0]=d),this.context=l,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof y.ready?y.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};A.prototype=n.fn,y=n(l);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};n.extend({dir:function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),n.fn.extend({has:function(a){var b=n(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(n.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=u.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.unique(f):f)},index:function(a){return a?"string"==typeof a?g.call(n(a),this[0]):g.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.unique(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){while((a=a[b])&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return n.dir(a,"parentNode")},parentsUntil:function(a,b,c){return n.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return n.dir(a,"nextSibling")},prevAll:function(a){return n.dir(a,"previousSibling")},nextUntil:function(a,b,c){return n.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return n.dir(a,"previousSibling",c)},siblings:function(a){return n.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return n.sibling(a.firstChild)},contents:function(a){return a.contentDocument||n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(C[a]||n.unique(e),B.test(a)&&e.reverse()),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return n.each(a.match(E)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):n.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(b=a.memory&&l,c=!0,g=e||0,e=0,f=h.length,d=!0;h&&f>g;g++)if(h[g].apply(l[0],l[1])===!1&&a.stopOnFalse){b=!1;break}d=!1,h&&(i?i.length&&j(i.shift()):b?h=[]:k.disable())},k={add:function(){if(h){var c=h.length;!function g(b){n.each(b,function(b,c){var d=n.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&g(c)})}(arguments),d?f=h.length:b&&(e=c,j(b))}return this},remove:function(){return h&&n.each(arguments,function(a,b){var c;while((c=n.inArray(b,h,c))>-1)h.splice(c,1),d&&(f>=c&&f--,g>=c&&g--)}),this},has:function(a){return a?n.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],f=0,this},disable:function(){return h=i=b=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,b||k.disable(),this},locked:function(){return!i},fireWith:function(a,b){return!h||c&&!i||(b=b||[],b=[a,b.slice?b.slice():b],d?i.push(b):j(b)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!c}};return k},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&n.isFunction(a.promise)?e:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(H.resolveWith(l,[n]),n.fn.triggerHandler&&(n(l).triggerHandler("ready"),n(l).off("ready"))))}});function I(){l.removeEventListener("DOMContentLoaded",I,!1),a.removeEventListener("load",I,!1),n.ready()}n.ready.promise=function(b){return H||(H=n.Deferred(),"complete"===l.readyState?setTimeout(n.ready):(l.addEventListener("DOMContentLoaded",I,!1),a.addEventListener("load",I,!1))),H.promise(b)},n.ready.promise();var J=n.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)n.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f};n.acceptData=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function K(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=n.expando+K.uid++}K.uid=1,K.accepts=n.acceptData,K.prototype={key:function(a){if(!K.accepts(a))return 0;var b={},c=a[this.expando];if(!c){c=K.uid++;try{b[this.expando]={value:c},Object.defineProperties(a,b)}catch(d){b[this.expando]=c,n.extend(a,b)}}return this.cache[c]||(this.cache[c]={}),c},set:function(a,b,c){var d,e=this.key(a),f=this.cache[e];if("string"==typeof b)f[b]=c;else if(n.isEmptyObject(f))n.extend(this.cache[e],b);else for(d in b)f[d]=b[d];return f},get:function(a,b){var c=this.cache[this.key(a)];return void 0===b?c:c[b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,n.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=this.key(a),g=this.cache[f];if(void 0===b)this.cache[f]={};else{n.isArray(b)?d=b.concat(b.map(n.camelCase)):(e=n.camelCase(b),b in g?d=[b,e]:(d=e,d=d in g?[d]:d.match(E)||[])),c=d.length;while(c--)delete g[d[c]]}},hasData:function(a){return!n.isEmptyObject(this.cache[a[this.expando]]||{})},discard:function(a){a[this.expando]&&delete this.cache[a[this.expando]]}};var L=new K,M=new K,N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(O,"-$1").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}M.set(a,b,c)}else c=void 0;return c}n.extend({hasData:function(a){return M.hasData(a)||L.hasData(a)},data:function(a,b,c){
3return M.access(a,b,c)},removeData:function(a,b){M.remove(a,b)},_data:function(a,b,c){return L.access(a,b,c)},_removeData:function(a,b){L.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=M.get(f),1===f.nodeType&&!L.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));L.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){M.set(this,a)}):J(this,function(b){var c,d=n.camelCase(a);if(f&&void 0===b){if(c=M.get(f,a),void 0!==c)return c;if(c=M.get(f,d),void 0!==c)return c;if(c=P(f,d,void 0),void 0!==c)return c}else this.each(function(){var c=M.get(this,d);M.set(this,d,b),-1!==a.indexOf("-")&&void 0!==c&&M.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){M.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=L.get(a,b),c&&(!d||n.isArray(c)?d=L.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return L.get(a,c)||L.access(a,c,{empty:n.Callbacks("once memory").add(function(){L.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?n.queue(this[0],a):void 0===b?this:this.each(function(){var c=n.queue(this,a,b);n._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&n.dequeue(this,a)})},dequeue:function(a){return this.each(function(){n.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=n.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=L.get(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}
vendor: 7,763 bytes, line 3
3});var Q=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,R=["Top","Right","Bottom","Left"],S=function(a,b){return a=b||a,"none"===n.css(a,"display")||!n.contains(a.ownerDocument,a)},T=/^(?:checkbox|radio)$/i;!function(){var a=l.createDocumentFragment(),b=a.appendChild(l.createElement("div")),c=l.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="<textarea>x</textarea>",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var U="undefined";k.focusinBubbles="onfocusin"in a;var V=/^key/,W=/^(?:mouse|pointer|contextmenu)|click/,X=/^(?:focusinfocus|focusoutblur)$/,Y=/^([^.]*)(?:\.(.+)|)$/;function Z(){return!0}function $(){return!1}function _(){try{return l.activeElement}catch(a){}}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return typeof n!==U&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(E)||[""],j=b.length;while(j--)h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g,!1)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.hasData(a)&&L.get(a);if(r&&(i=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&(delete r.handle,L.remove(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,m,o,p=[d||l],q=j.call(b,"type")?b.type:b,r=j.call(b,"namespace")?b.namespace.split("."):[];if(g=h=d=d||l,3!==d.nodeType&&8!==d.nodeType&&!X.test(q+n.event.triggered)&&(q.indexOf(".")>=0&&(r=q.split("."),q=r.shift(),r.sort()),k=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=r.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:n.makeArray(c,[b]),o=n.event.special[q]||{},e||!o.trigger||o.trigger.apply(d,c)!==!1)){if(!e&&!o.noBubble&&!n.isWindow(d)){for(i=o.delegateType||q,X.test(i+q)||(g=g.parentNode);g;g=g.parentNode)p.push(g),h=g;h===(d.ownerDocument||l)&&p.push(h.defaultView||h.parentWindow||a)}f=0;while((g=p[f++])&&!b.isPropagationStopped())b.type=f>1?i:o.bindType||q,m=(L.get(g,"events")||{})[b.type]&&L.get(g,"handle"),m&&m.apply(g,c),m=k&&g[k],m&&m.apply&&n.acceptData(g)&&(b.result=m.apply(g,c),b.result===!1&&b.preventDefault());return b.type=q,e||b.isDefaultPrevented()||o._default&&o._default.apply(p.pop(),c)!==!1||!n.acceptData(d)||k&&n.isFunction(d[q])&&!n.isWindow(d)&&(h=d[k],h&&(d[k]=null),n.event.triggered=q,d[q](),n.event.triggered=void 0,h&&(d[k]=h)),b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(L.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(g.namespace))&&(a.handleObj=g,a.data=g.data,e=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(a.result=e)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!==this;i=i.parentNode||this)if(i.disabled!==!0||"click"!==a.type){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>=0:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h<b.length&&g.push({elem:this,handlers:b.slice(h)}),g},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){return null==a.which&&(a.which=null!=b.charCode?b.charCode:b.keyCode),a}},mouseHooks:{props:"button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,b){var c,d,e,f=b.button;return null==a.pageX&&null!=b.clientX&&(c=a.target.ownerDocument||l,d=c.documentElement,e=c.body,a.pageX=b.clientX+(d&&d.scrollLeft||e&&e.scrollLeft||0)-(d&&d.clientLeft||e&&e.clientLeft||0),a.pageY=b.clientY+(d&&d.scrollTop||e&&e.scrollTop||0)-(d&&d.clientTop||e&&e.clientTop||0)),a.which||void 0===f||(a.which=1&f?1:2&f?3:4&f?2:0),a}},fix:function(a){if(a[n.expando])return a;var b,c,d,e=a.type,f=a,g=this.fixHooks[e];g||(this.fixHooks[e]=g=W.test(e)?this.mouseHooks:V.test(e)?this.keyHooks:{}),d=g.props?this.props.concat(g.props):this.props,a=new n.Event(f),b=d.length;while(b--)c=d[b],a[c]=f[c];return a.target||(a.target=l),3===a.target.nodeType&&(a.target=a.target.parentNode),g.filter?g.filter(a,f):a},special:{load:{noBubble:!0},focus:{trigger:function(){return this!==_()&&this.focus?(this.focus(),!1):void 0},delegateType:"focusin"},blur:{trigger:function(){return this===_()&&this.blur?(this.blur(),!1):void 0},delegateType:"focusout"},click:{trigger:function(){return"checkbox"===this.type&&this.click&&n.nodeName(this,"input")?(this.click(),!1):void 0},_default:function(a){return n.nodeName(a.target,"a")}},beforeunload:{postDispatch:function(a){void 0!==a.result&&a.originalEvent&&(a.originalEvent.returnValue=a.result)}}},simulate:function(a,b,c,d){var e=n.extend(new n.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?n.event.trigger(e,null,b):n.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},n.removeEvent=function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)},n.Event=function(a,b){return this instanceof n.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&a.returnValue===!1?Z:$):this.type=a,b&&n.extend(this,b),this.timeStamp=a&&a.timeStamp||n.now(),void(this[n.expando]=!0)):new n.Event(a,b)},n.Event.prototype={isDefaultPrevented:$,isPropagationStopped:$,isImmediatePropagationStopped:$,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=Z,a&&a.preventDefault&&a.preventDefault()},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=Z,a&&a.stopPropagation&&a.stopPropagation()},stopImmediatePropagation:function(){var a=this.originalEvent;this.isImmediatePropagationStopped=Z,a&&a.stopImmediatePropagation&&a.stopImmediatePropagation(),this.stopPropagation()}}
vendor: 42,384 bytes, lines 3-4
3,n.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(a,b){n.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return(!e||e!==d&&!n.contains(d,e))&&(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),k.focusinBubbles||n.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){n.event.simulate(b,a.target,n.event.fix(a),!0)};n.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=L.access(d,b);e||d.addEventListener(a,c,!0),L.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=L.access(d,b)-1;e?L.access(d,b,e):(d.removeEventListener(a,c,!0),L.remove(d,b))}}}),n.fn.extend({on:function(a,b,c,d,e){var f,g;if("object"==typeof a){"string"!=typeof b&&(c=c||b,b=void 0);for(g in a)this.on(g,b,c,a[g],e);return this}if(null==c&&null==d?(d=b,c=b=void 0):null==d&&("string"==typeof b?(d=c,c=void 0):(d=c,c=b,b=void 0)),d===!1)d=$;else if(!d)return this;return 1===e&&(f=d,d=function(a){return n().off(a),f.apply(this,arguments)},d.guid=f.guid||(f.guid=n.guid++)),this.each(function(){n.event.add(this,a,d,c,b)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,b,c){var d,e;if(a&&a.preventDefault&&a.handleObj)return d=a.handleObj,n(a.delegateTarget).off(d.namespace?d.origType+"."+d.namespace:d.origType,d.selector,d.handler),this;if("object"==typeof a){for(e in a)this.off(e,b,a[e]);return this}return(b===!1||"function"==typeof b)&&(c=b,b=void 0),c===!1&&(c=$),this.each(function(){n.event.remove(this,a,c,b)})},trigger:function(a,b){return this.each(function(){n.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];return c?n.event.trigger(a,b,c,!0):void 0}});var aa=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,ba=/<([\w:]+)/,ca=/<|&#?\w+;/,da=/<(?:script|style|link)/i,ea=/checked\s*(?:[^=]|=\s*.checked.)/i,fa=/^$|\/(?:java|ecma)script/i,ga=/^true\/(.*)/,ha=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,ia={option:[1,"<select multiple='multiple'>","</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};ia.optgroup=ia.option,ia.tbody=ia.tfoot=ia.colgroup=ia.caption=ia.thead,ia.th=ia.td;function ja(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function ka(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function la(a){var b=ga.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function ma(a,b){for(var c=0,d=a.length;d>c;c++)L.set(a[c],"globalEval",!b||L.get(b[c],"globalEval"))}function na(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(L.hasData(a)&&(f=L.access(a),g=L.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}M.hasData(a)&&(h=M.access(a),i=n.extend({},h),M.set(b,i))}}function oa(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function pa(a,b){var c=b.nodeName.toLowerCase();"input"===c&&T.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}n.extend({clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=oa(h),f=oa(a),d=0,e=f.length;e>d;d++)pa(f[d],g[d]);if(b)if(c)for(f=f||oa(a),g=g||oa(h),d=0,e=f.length;e>d;d++)na(f[d],g[d]);else na(a,h);return g=oa(h,"script"),g.length>0&&ma(g,!i&&oa(a,"script")),h},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,k=b.createDocumentFragment(),l=[],m=0,o=a.length;o>m;m++)if(e=a[m],e||0===e)if("object"===n.type(e))n.merge(l,e.nodeType?[e]:e);else if(ca.test(e)){f=f||k.appendChild(b.createElement("div")),g=(ba.exec(e)||["",""])[1].toLowerCase(),h=ia[g]||ia._default,f.innerHTML=h[1]+e.replace(aa,"<$1></$2>")+h[2],j=h[0];while(j--)f=f.lastChild;n.merge(l,f.childNodes),f=k.firstChild,f.textContent=""}else l.push(b.createTextNode(e));k.textContent="",m=0;while(e=l[m++])if((!d||-1===n.inArray(e,d))&&(i=n.contains(e.ownerDocument,e),f=oa(k.appendChild(e),"script"),i&&ma(f),c)){j=0;while(e=f[j++])fa.test(e.type||"")&&c.push(e)}return k},cleanData:function(a){for(var b,c,d,e,f=n.event.special,g=0;void 0!==(c=a[g]);g++){if(n.acceptData(c)&&(e=c[L.expando],e&&(b=L.cache[e]))){if(b.events)for(d in b.events)f[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);L.cache[e]&&delete L.cache[e]}delete M.cache[c[M.expando]]}}}),n.fn.extend({text:function(a){return J(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&(this.textContent=a)})},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=ja(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=ja(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?n.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||n.cleanData(oa(c)),c.parentNode&&(b&&n.contains(c.ownerDocument,c)&&ma(oa(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(oa(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return J(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!da.test(a)&&!ia[(ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(aa,"<$1></$2>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(oa(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,n.cleanData(oa(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,m=this,o=l-1,p=a[0],q=n.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&ea.test(p))return this.each(function(c){var d=m.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(c=n.buildFragment(a,this[0].ownerDocument,!1,this),d=c.firstChild,1===c.childNodes.length&&(c=d),d)){for(f=n.map(oa(c,"script"),ka),g=f.length;l>j;j++)h=c,j!==o&&(h=n.clone(h,!0,!0),g&&n.merge(f,oa(h,"script"))),b.call(this[j],h,j);if(g)for(i=f[f.length-1].ownerDocument,n.map(f,la),j=0;g>j;j++)h=f[j],fa.test(h.type||"")&&!L.access(h,"globalEval")&&n.contains(i,h)&&(h.src?n._evalUrl&&n._evalUrl(h.src):n.globalEval(h.textContent.replace(ha,"")))}return this}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),g=e.length-1,h=0;g>=h;h++)c=h===g?this:this.clone(!0),n(e[h])[b](c),f.apply(d,c.get());return this.pushStack(d)}});var qa,ra={};function sa(b,c){var d,e=n(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:n.css(e[0],"display");return e.detach(),f}function ta(a){var b=l,c=ra[a];return c||(c=sa(a,b),"none"!==c&&c||(qa=(qa||n("<iframe frameborder='0' width='0' height='0'/>")).appendTo(b.documentElement),b=qa[0].contentDocument,b.write(),b.close(),c=sa(a,b),qa.detach()),ra[a]=c),c}var ua=/^margin/,va=new RegExp("^("+Q+")(?!px)[a-z%]+$","i"),wa=function(b){return b.ownerDocument.defaultView.opener?b.ownerDocument.defaultView.getComputedStyle(b,null):a.getComputedStyle(b,null)};function xa(a,b,c){var d,e,f,g,h=a.style;return c=c||wa(a),c&&(g=c.getPropertyValue(b)||c[b]),c&&(""!==g||n.contains(a.ownerDocument,a)||(g=n.style(a,b)),va.test(g)&&ua.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0!==g?g+"":g}function ya(a,b){return{get:function(){return a()?void delete this.get:(this.get=b).apply(this,arguments)}}}!function(){var b,c,d=l.documentElement,e=l.createElement("div"),f=l.createElement("div");if(f.style){f.style.backgroundClip="content-box",f.cloneNode(!0).style.backgroundClip="",k.clearCloneStyle="content-box"===f.style.backgroundClip,e.style.cssText="border:0;width:0;height:0;top:0;left:-9999px;margin-top:1px;position:absolute",e.appendChild(f);function g(){f.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;margin-top:1%;top:1%;border:1px;padding:1px;width:4px;position:absolute",f.innerHTML="",d.appendChild(e);var g=a.getComputedStyle(f,null);b="1%"!==g.top,c="4px"===g.width,d.removeChild(e)}a.getComputedStyle&&n.extend(k,{pixelPosition:function(){return g(),b},boxSizingReliable:function(){return null==c&&g(),c},reliableMarginRight:function(){var b,c=f.appendChild(l.createElement("div"));return c.style.cssText=f.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0",c.style.marginRight=c.style.width="0",f.style.width="1px",d.appendChild(e),b=!parseFloat(a.getComputedStyle(c,null).marginRight),d.removeChild(e),f.removeChild(c),b}})}}(),n.swap=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};var za=/^(none|table(?!-c[ea]).+)/,Aa=new RegExp("^("+Q+")(.*)$","i"),Ba=new RegExp("^([+-])=("+Q+")","i"),Ca={position:"absolute",visibility:"hidden",display:"block"},Da={letterSpacing:"0",fontWeight:"400"},Ea=["Webkit","O","Moz","ms"];function Fa(a,b){if(b in a)return b;var c=b[0].toUpperCase()+b.slice(1),d=b,e=Ea.length;while(e--)if(b=Ea[e]+c,b in a)return b;return d}function Ga(a,b,c){var d=Aa.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function Ha(a,b,c,d,e){for(var f=c===(d?"border":"content")?4:"width"===b?1:0,g=0;4>f;f+=2)"margin"===c&&(g+=n.css(a,c+R[f],!0,e)),d?("content"===c&&(g-=n.css(a,"padding"+R[f],!0,e)),"margin"!==c&&(g-=n.css(a,"border"+R[f]+"Width",!0,e))):(g+=n.css(a,"padding"+R[f],!0,e),"padding"!==c&&(g+=n.css(a,"border"+R[f]+"Width",!0,e)));return g}function Ia(a,b,c){var d=!0,e="width"===b?a.offsetWidth:a.offsetHeight,f=wa(a),g="border-box"===n.css(a,"boxSizing",!1,f);if(0>=e||null==e){if(e=xa(a,b,f),(0>e||null==e)&&(e=a.style[b]),va.test(e))return e;d=g&&(k.boxSizingReliable()||e===a.style[b]),e=parseFloat(e)||0}return e+Ha(a,b,c||(g?"border":"content"),d,f)+"px"}function Ja(a,b){for(var c,d,e,f=[],g=0,h=a.length;h>g;g++)d=a[g],d.style&&(f[g]=L.get(d,"olddisplay"),c=d.style.display,b?(f[g]||"none"!==c||(d.style.display=""),""===d.style.display&&S(d)&&(f[g]=L.access(d,"olddisplay",ta(d.nodeName)))):(e=S(d),"none"===c&&e||L.set(d,"olddisplay",e?c:n.css(d,"display"))));for(g=0;h>g;g++)d=a[g],d.style&&(b&&"none"!==d.style.display&&""!==d.style.display||(d.style.display=b?f[g]||"":"none"));return a}n.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=xa(a,"opacity");return""===c?"1":c}}}},cssNumber:{columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":"cssFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=n.camelCase(b),i=a.style;return b=n.cssProps[h]||(n.cssProps[h]=Fa(i,h)),g=n.cssHooks[b]||n.cssHooks[h],void 0===c?g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b]:(f=typeof c,"string"===f&&(e=Ba.exec(c))&&(c=(e[1]+1)*e[2]+parseFloat(n.css(a,b)),f="number"),null!=c&&c===c&&("number"!==f||n.cssNumber[h]||(c+="px"),k.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),g&&"set"in g&&void 0===(c=g.set(a,c,d))||(i[b]=c)),void 0)}},css:function(a,b,c,d){var e,f,g,h=n.camelCase(b);return b=n.cssProps[h]||(n.cssProps[h]=Fa(a.style,h)),g=n.cssHooks[b]||n.cssHooks[h],g&&"get"in g&&(e=g.get(a,!0,c)),void 0===e&&(e=xa(a,b,d)),"normal"===e&&b in Da&&(e=Da[b]),""===c||c?(f=parseFloat(e),c===!0||n.isNumeric(f)?f||0:e):e}}),n.each(["height","width"],function(a,b){n.cssHooks[b]={get:function(a,c,d){return c?za.test(n.css(a,"display"))&&0===a.offsetWidth?n.swap(a,Ca,function(){return Ia(a,b,d)}):Ia(a,b,d):void 0},set:function(a,c,d){var e=d&&wa(a);return Ga(a,c,d?Ha(a,b,d,"border-box"===n.css(a,"boxSizing",!1,e),e):0)}}}),n.cssHooks.marginRight=ya(k.reliableMarginRight,function(a,b){return b?n.swap(a,{display:"inline-block"},xa,[a,"marginRight"]):void 0}),n.each({margin:"",padding:"",border:"Width"},function(a,b){n.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];4>d;d++)e[a+R[d]+b]=f[d]||f[d-2]||f[0];return e}},ua.test(a)||(n.cssHooks[a+b].set=Ga)}),n.fn.extend({css:function(a,b){return J(this,function(a,b,c){var d,e,f={},g=0;if(n.isArray(b)){for(d=wa(a),e=b.length;e>g;g++)f[b[g]]=n.css(a,b[g],!1,d);return f}return void 0!==c?n.style(a,b,c):n.css(a,b)},a,b,arguments.length>1)},show:function(){return Ja(this,!0)},hide:function(){return Ja(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){S(this)?n(this).show():n(this).hide()})}});function Ka(a,b,c,d,e){return new Ka.prototype.init(a,b,c,d,e)}n.Tween=Ka,Ka.prototype={constructor:Ka,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(n.cssNumber[c]?"":"px")},cur:function(){var a=Ka.propHooks[this.prop];return a&&a.get?a.get(this):Ka.propHooks._default.get(this)},run:function(a){var b,c=Ka.propHooks[this.prop];return this.options.duration?this.pos=b=n.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Ka.propHooks._default.set(this),this}},Ka.prototype.init.prototype=Ka.prototype,Ka.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=n.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){n.fx.step[a.prop]?n.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[n.cssProps[a.prop]]||n.cssHooks[a.prop])?n.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},Ka.propHooks.scrollTop=Ka.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},n.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},n.fx=Ka.prototype.init,n.fx.step={};var La,Ma,Na=/^(?:toggle|show|hide)$/,Oa=new RegExp("^(?:([+-])=|)("+Q+")([a-z%]*)$","i"),Pa=/queueHooks$/,Qa=[Va],Ra={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=Oa.exec(b),f=e&&e[3]||(n.cssNumber[a]?"":"px"),g=(n.cssNumber[a]||"px"!==f&&+d)&&Oa.exec(n.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,n.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function Sa(){return setTimeout(function(){La=void 0}),La=n.now()}function Ta(a,b){var c,d=0,e={height:a};for(b=b?1:0;4>d;d+=2-b)c=R[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function Ua(a,b,c){for(var d,e=(Ra[b]||[]).concat(Ra["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function Va(a,b,c){var d,e,f,g,h,i,j,k,l=this,m={},o=a.style,p=a.nodeType&&S(a),q=L.get(a,"fxshow");c.queue||(h=n._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,l.always(function(){l.always(function(){h.unqueued--,n.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[o.overflow,o.overflowX,o.overflowY],j=n.css(a,"display"),k="none"===j?L.get(a,"olddisplay")||ta(a.nodeName):j,"inline"===k&&"none"===n.css(a,"float")&&(o.display="inline-block")),c.overflow&&(o.overflow="hidden",l.always(function(){o.overflow=c.overflow[0],o.overflowX=c.overflow[1],o.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],Na.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(p?"hide":"show")){if("show"!==e||!q||void 0===q[d])continue;p=!0}m[d]=q&&q[d]||n.style(a,d)}else j=void 0;if(n.isEmptyObject(m))"inline"===("none"===j?ta(a.nodeName):j)&&(o.display=j);else{q?"hidden"in q&&(p=q.hidden):q=L.access(a,"fxshow",{}),f&&(q.hidden=!p),p?n(a).show():l.done(function(){n(a).hide()}),l.done(function(){var b;L.remove(a,"fxshow");for(b in m)n.style(a,b,m[b])});for(d in m)g=Ua(p?q[d]:0,d,l),d in q||(q[d]=g.start,p&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function Wa(a,b){var c,d,e,f,g;for(c in a)if(d=n.camelCase(c),e=b[d],f=a[c],n.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=n.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function Xa(a,b,c){var d,e,f=0,g=Qa.length,h=n.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=La||Sa(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:n.extend({},b),opts:n.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:La||Sa(),duration:c.duration,tweens:[],createTween:function(b,c){var d=n.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;for(Wa(k,j.opts.specialEasing);g>f;f++)if(d=Qa[f].call(j,a,k,j.opts))return d;return n.map(k,Ua,j),n.isFunction(j.opts.start)&&j.opts.start.call(a,j),n.fx.timer(n.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}n.Animation=n.extend(Xa,{tweener:function(a,b){n.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],Ra[c]=Ra[c]||[],Ra[c].unshift(b)},prefilter:function(a,b){b?Qa.unshift(a):Qa.push(a)}}),n.speed=function(a,b,c){var d=a&&"object"==typeof a?n.extend({},a):{complete:c||!c&&b||n.isFunction(a)&&a,duration:a,easing:c&&b||b&&!n.isFunction(b)&&b};return d.duration=n.fx.off?0:"number"==typeof d.duration?d.duration:d.duration in n.fx.speeds?n.fx.speeds[d.duration]:n.fx.speeds._default,(null==d.queue||d.queue===!0)&&(d.queue="fx"),d.old=d.complete,d.complete=function(){n.isFunction(d.old)&&d.old.call(this),d.queue&&n.dequeue(this,d.queue)},d},n.fn.extend({fadeTo:function(a,b,c,d){return this.filter(S).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=n.isEmptyObject(a),f=n.speed(b,c,d),g=function(){var b=Xa(this,n.extend({},a),f);(e||L.get(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=n.timers,g=L.get(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&Pa.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));(b||!c)&&n.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=L.get(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=n.timers,g=d?d.length:0;for(c.finish=!0,n.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;g>b;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),n.each(["toggle","show","hide"],function(a,b){var c=n.fn[b];n.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(Ta(b,!0),a,d,e)}}),n.each({slideDown:Ta("show"),slideUp:Ta("hide"),slideToggle:Ta("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){n.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),n.timers=[],n.fx.tick=function(){var a,b=0,c=n.timers;for(La=n.now();b<c.length;b++)a=c[b],a()||c[b]!==a||c.splice(b--,1);c.length||n.fx.stop(),La=void 0},n.fx.timer=function(a){n.timers.push(a),a()?n.fx.start():n.timers.pop()},n.fx.interval=13,n.fx.start=function(){Ma||(Ma=setInterval(n.fx.tick,n.fx.interval))},n.fx.stop=function(){clearInterval(Ma),Ma=null},n.fx.speeds={slow:600,fast:200,_default:400},n.fn.delay=function(a,b){return a=n.fx?n.fx.speeds[a]||a:a,b=b||"fx",this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},function(){var a=l.createElement("input"),b=l.createElement("select"),c=b.appendChild(l.createElement("option"));a.type="checkbox",k.checkOn=""!==a.value,k.optSelected=c.selected,b.disabled=!0,k.optDisabled=!c.disabled,a=l.createElement("input"),a.value="t",a.type="radio",k.radioValue="t"===a.value}();var Ya,Za,$a=n.expr.attrHandle;n.fn.extend({attr:function(a,b){return J(this,n.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){n.removeAttr(this,a)})}}),n.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(a&&3!==f&&8!==f&&2!==f)return typeof a.getAttribute===U?n.prop(a,b,c):(1===f&&n.isXMLDoc(a)||(b=b.toLowerCase(),d=n.attrHooks[b]||(n.expr.match.bool.test(b)?Za:Ya)),
4void 0===c?d&&"get"in d&&null!==(e=d.get(a,b))?e:(e=n.find.attr(a,b),null==e?void 0:e):null!==c?d&&"set"in d&&void 0!==(e=d.set(a,c,b))?e:(a.setAttribute(b,c+""),c):void n.removeAttr(a,b))},removeAttr:function(a,b){var c,d,e=0,f=b&&b.match(E);if(f&&1===a.nodeType)while(c=f[e++])d=n.propFix[c]||c,n.expr.match.bool.test(c)&&(a[d]=!1),a.removeAttribute(c)},attrHooks:{type:{set:function(a,b){if(!k.radioValue&&"radio"===b&&n.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}}}),Za={set:function(a,b,c){return b===!1?n.removeAttr(a,c):a.setAttribute(c,c),c}},n.each(n.expr.match.bool.source.match(/\w+/g),function(a,b){var c=$a[b]||n.find.attr;$a[b]=function(a,b,d){var e,f;return d||(f=$a[b],$a[b]=e,e=null!=c(a,b,d)?b.toLowerCase():null,$a[b]=f),e}});var _a=/^(?:input|select|textarea|button)$/i;n.fn.extend({prop:function(a,b){return J(this,n.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[n.propFix[a]||a]})}}),n.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(a,b,c){var d,e,f,g=a.nodeType;if(a&&3!==g&&8!==g&&2!==g)return f=1!==g||!n.isXMLDoc(a),f&&(b=n.propFix[b]||b,e=n.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){return a.hasAttribute("tabindex")||_a.test(a.nodeName)||a.href?a.tabIndex:-1}}}}),k.optSelected||(n.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null}}),n.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){n.propFix[this.toLowerCase()]=this});var ab=/[\t\r\n\f]/g;n.fn.extend({addClass:function(a){var b,c,d,e,f,g,h="string"==typeof a&&a,i=0,j=this.length;if(n.isFunction(a))return this.each(function(b){n(this).addClass(a.call(this,b,this.className))});if(h)for(b=(a||"").match(E)||[];j>i;i++)if(c=this[i],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(ab," "):" ")){f=0;while(e=b[f++])d.indexOf(" "+e+" ")<0&&(d+=e+" ");g=n.trim(d),c.className!==g&&(c.className=g)}return this},removeClass:function(a){var b,c,d,e,f,g,h=0===arguments.length||"string"==typeof a&&a,i=0,j=this.length;if(n.isFunction(a))return this.each(function(b){n(this).removeClass(a.call(this,b,this.className))});if(h)for(b=(a||"").match(E)||[];j>i;i++)if(c=this[i],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(ab," "):"")){f=0;while(e=b[f++])while(d.indexOf(" "+e+" ")>=0)d=d.replace(" "+e+" "," ");g=a?n.trim(d):"",c.className!==g&&(c.className=g)}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):this.each(n.isFunction(a)?function(c){n(this).toggleClass(a.call(this,c,this.className,b),b)}:function(){if("string"===c){var b,d=0,e=n(this),f=a.match(E)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else(c===U||"boolean"===c)&&(this.className&&L.set(this,"__className__",this.className),this.className=this.className||a===!1?"":L.get(this,"__className__")||"")})},hasClass:function(a){for(var b=" "+a+" ",c=0,d=this.length;d>c;c++)if(1===this[c].nodeType&&(" "+this[c].className+" ").replace(ab," ").indexOf(b)>=0)return!0;return!1}});var bb=/\r/g;n.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=n.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,n(this).val()):a,null==e?e="":"number"==typeof e?e+="":n.isArray(e)&&(e=n.map(e,function(a){return null==a?"":a+""})),b=n.valHooks[this.type]||n.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=n.valHooks[e.type]||n.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(bb,""):null==c?"":c)}}}),n.extend({valHooks:{option:{get:function(a){var b=n.find.attr(a,"value");return null!=b?b:n.trim(n.text(a))}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type||0>e,g=f?null:[],h=f?e+1:d.length,i=0>e?h:f?e:0;h>i;i++)if(c=d[i],!(!c.selected&&i!==e||(k.optDisabled?c.disabled:null!==c.getAttribute("disabled"))||c.parentNode.disabled&&n.nodeName(c.parentNode,"optgroup"))){if(b=n(c).val(),f)return b;g.push(b)}return g},set:function(a,b){var c,d,e=a.options,f=n.makeArray(b),g=e.length;while(g--)d=e[g],(d.selected=n.inArray(d.value,f)>=0)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),n.each(["radio","checkbox"],function(){n.valHooks[this]={set:function(a,b){return n.isArray(b)?a.checked=n.inArray(n(a).val(),b)>=0:void 0}},k.checkOn||(n.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})}),n.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){n.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),n.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}});var cb=n.now(),db=/\?/;n.parseJSON=function(a){return JSON.parse(a+"")},n.parseXML=function(a){var b,c;if(!a||"string"!=typeof a)return null;try{c=new DOMParser,b=c.parseFromString(a,"text/xml")}catch(d){b=void 0}return(!b||b.getElementsByTagName("parsererror").length)&&n.error("Invalid XML: "+a),b};var eb=/#.*$/,fb=/([?&])_=[^&]*/,gb=/^(.*?):[ \t]*([^\r\n]*)$/gm,hb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,ib=/^(?:GET|HEAD)$/,jb=/^\/\//,kb=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,lb={},mb={},nb="*/".concat("*"),ob=a.location.href,pb=kb.exec(ob.toLowerCase())||[];function qb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(E)||[];if(n.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function rb(a,b,c,d){var e={},f=a===mb;function g(h){var i;return e[h]=!0,n.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function sb(a,b){var c,d,e=n.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&n.extend(!0,a,d),a}function tb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}return f?(f!==i[0]&&i.unshift(f),c[f]):void 0}function ub(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}n.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:ob,type:"GET",isLocal:hb.test(pb[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":nb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":n.parseJSON,"text xml":n.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?sb(sb(a,n.ajaxSettings),b):sb(n.ajaxSettings,a)},ajaxPrefilter:qb(lb),ajaxTransport:qb(mb),ajax:function(a,b){"object"==typeof a&&(b=a,a=void 0),b=b||{};var c,d,e,f,g,h,i,j,k=n.ajaxSetup({},b),l=k.context||k,m=k.context&&(l.nodeType||l.jquery)?n(l):n.event,o=n.Deferred(),p=n.Callbacks("once memory"),q=k.statusCode||{},r={},s={},t=0,u="canceled",v={readyState:0,getResponseHeader:function(a){var b;if(2===t){if(!f){f={};while(b=gb.exec(e))f[b[1].toLowerCase()]=b[2]}b=f[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return 2===t?e:null},setRequestHeader:function(a,b){var c=a.toLowerCase();return t||(a=s[c]=s[c]||a,r[a]=b),this},overrideMimeType:function(a){return t||(k.mimeType=a),this},statusCode:function(a){var b;if(a)if(2>t)for(b in a)q[b]=[q[b],a[b]];else v.always(a[v.status]);return this},abort:function(a){var b=a||u;return c&&c.abort(b),x(0,b),this}};if(o.promise(v).complete=p.add,v.success=v.done,v.error=v.fail,k.url=((a||k.url||ob)+"").replace(eb,"").replace(jb,pb[1]+"//"),k.type=b.method||b.type||k.method||k.type,k.dataTypes=n.trim(k.dataType||"*").toLowerCase().match(E)||[""],null==k.crossDomain&&(h=kb.exec(k.url.toLowerCase()),k.crossDomain=!(!h||h[1]===pb[1]&&h[2]===pb[2]&&(h[3]||("http:"===h[1]?"80":"443"))===(pb[3]||("http:"===pb[1]?"80":"443")))),k.data&&k.processData&&"string"!=typeof k.data&&(k.data=n.param(k.data,k.traditional)),rb(lb,k,b,v),2===t)return v;i=n.event&&k.global,i&&0===n.active++&&n.event.trigger("ajaxStart"),k.type=k.type.toUpperCase(),k.hasContent=!ib.test(k.type),d=k.url,k.hasContent||(k.data&&(d=k.url+=(db.test(d)?"&":"?")+k.data,delete k.data),k.cache===!1&&(k.url=fb.test(d)?d.replace(fb,"$1_="+cb++):d+(db.test(d)?"&":"?")+"_="+cb++)),k.ifModified&&(n.lastModified[d]&&v.setRequestHeader("If-Modified-Since",n.lastModified[d]),n.etag[d]&&v.setRequestHeader("If-None-Match",n.etag[d])),(k.data&&k.hasContent&&k.contentType!==!1||b.contentType)&&v.setRequestHeader("Content-Type",k.contentType),v.setRequestHeader("Accept",k.dataTypes[0]&&k.accepts[k.dataTypes[0]]?k.accepts[k.dataTypes[0]]+("*"!==k.dataTypes[0]?", "+nb+"; q=0.01":""):k.accepts["*"]);for(j in k.headers)v.setRequestHeader(j,k.headers[j]);if(k.beforeSend&&(k.beforeSend.call(l,v,k)===!1||2===t))return v.abort();u="abort";for(j in{success:1,error:1,complete:1})v[j](k[j]);if(c=rb(mb,k,b,v)){v.readyState=1,i&&m.trigger("ajaxSend",[v,k]),k.async&&k.timeout>0&&(g=setTimeout(function(){v.abort("timeout")},k.timeout));try{t=1,c.send(r,x)}catch(w){if(!(2>t))throw w;x(-1,w)}}else x(-1,"No Transport");function x(a,b,f,h){var j,r,s,u,w,x=b;2!==t&&(t=2,g&&clearTimeout(g),c=void 0,e=h||"",v.readyState=a>0?4:0,j=a>=200&&300>a||304===a,f&&(u=tb(k,v,f)),u=ub(k,u,v,j),j?(k.ifModified&&(w=v.getResponseHeader("Last-Modified"),w&&(n.lastModified[d]=w),w=v.getResponseHeader("etag"),w&&(n.etag[d]=w)),204===a||"HEAD"===k.type?x="nocontent":304===a?x="notmodified":(x=u.state,r=u.data,s=u.error,j=!s)):(s=x,(a||!x)&&(x="error",0>a&&(a=0))),v.status=a,v.statusText=(b||x)+"",j?o.resolveWith(l,[r,x,v]):o.rejectWith(l,[v,x,s]),v.statusCode(q),q=void 0,i&&m.trigger(j?"ajaxSuccess":"ajaxError",[v,k,j?r:s]),p.fireWith(l,[v,x]),i&&(m.trigger("ajaxComplete",[v,k]),--n.active||n.event.trigger("ajaxStop")))}return v},getJSON:function(a,b,c){return n.get(a,b,c,"json")},getScript:function(a,b){return n.get(a,void 0,b,"script")}}),n.each(["get","post"],function(a,b){n[b]=function(a,c,d,e){return n.isFunction(c)&&(e=e||d,d=c,c=void 0),n.ajax({url:a,type:b,dataType:e,data:c,success:d})}}),n._evalUrl=function(a){return n.ajax({url:a,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})},n.fn.extend({wrapAll:function(a){var b;return n.isFunction(a)?this.each(function(b){n(this).wrapAll(a.call(this,b))}):(this[0]&&(b=n(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this)},wrapInner:function(a){return this.each(n.isFunction(a)?function(b){n(this).wrapInner(a.call(this,b))}:function(){var b=n(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=n.isFunction(a);return this.each(function(c){n(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){n.nodeName(this,"body")||n(this).replaceWith(this.childNodes)}).end()}}),n.expr.filters.hidden=function(a){return a.offsetWidth<=0&&a.offsetHeight<=0},n.expr.filters.visible=function(a){return!n.expr.filters.hidden(a)};var vb=/%20/g,wb=/\[\]$/,xb=/\r?\n/g,yb=/^(?:submit|button|image|reset|file)$/i,zb=/^(?:input|select|textarea|keygen)/i;function Ab(a,b,c,d){var e;if(n.isArray(b))n.each(b,function(b,e){c||wb.test(a)?d(a,e):Ab(a+"["+("object"==typeof e?b:"")+"]",e,c,d)});else if(c||"object"!==n.type(b))d(a,b);else for(e in b)Ab(a+"["+e+"]",b[e],c,d)}n.param=function(a,b){var c,d=[],e=function(a,b){b=n.isFunction(b)?b():null==b?"":b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};if(void 0===b&&(b=n.ajaxSettings&&n.ajaxSettings.traditional),n.isArray(a)||a.jquery&&!n.isPlainObject(a))n.each(a,function(){e(this.name,this.value)});else for(c in a)Ab(c,a[c],b,e);return d.join("&").replace(vb,"+")},n.fn.extend({serialize:function(){return n.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=n.prop(this,"elements");return a?n.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!n(this).is(":disabled")&&zb.test(this.nodeName)&&!yb.test(a)&&(this.checked||!T.test(a))}).map(function(a,b){var c=n(this).val();return null==c?null:n.isArray(c)?n.map(c,function(a){return{name:b.name,value:a.replace(xb,"\r\n")}}):{name:b.name,value:c.replace(xb,"\r\n")}}).get()}}),n.ajaxSettings.xhr=function(){try{return new XMLHttpRequest}catch(a){}};var Bb=0,Cb={},Db={0:200,1223:204},Eb=n.ajaxSettings.xhr();a.attachEvent&&a.attachEvent("onunload",function(){for(var a in Cb)Cb[a]()}),k.cors=!!Eb&&"withCredentials"in Eb,k.ajax=Eb=!!Eb,n.ajaxTransport(function(a){var b;return k.cors||Eb&&!a.crossDomain?{send:function(c,d){var e,f=a.xhr(),g=++Bb;if(f.open(a.type,a.url,a.async,a.username,a.password),a.xhrFields)for(e in a.xhrFields)f[e]=a.xhrFields[e];a.mimeType&&f.overrideMimeType&&f.overrideMimeType(a.mimeType),a.crossDomain||c["X-Requested-With"]||(c["X-Requested-With"]="XMLHttpRequest");for(e in c)f.setRequestHeader(e,c[e]);b=function(a){return function(){b&&(delete Cb[g],b=f.onload=f.onerror=null,"abort"===a?f.abort():"error"===a?d(f.status,f.statusText):d(Db[f.status]||f.status,f.statusText,"string"==typeof f.responseText?{text:f.responseText}:void 0,f.getAllResponseHeaders()))}},f.onload=b(),f.onerror=b("error"),b=Cb[g]=b("abort");try{f.send(a.hasContent&&a.data||null)}catch(h){if(b)throw h}},abort:function(){b&&b()}}:void 0}),n.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(a){return n.globalEval(a),a}}}),n.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),n.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(d,e){b=n("<script>").prop({async:!0,charset:a.scriptCharset,src:a.url}).on("load error",c=function(a){b.remove(),c=null,a&&e("error"===a.type?404:200,a.type)}),l.head.appendChild(b[0])},abort:function(){c&&c()}}}});var Fb=[],Gb=/(=)\?(?=&|$)|\?\?/;n.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=Fb.pop()||n.expando+"_"+cb++;return this[a]=!0,a}}),n.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(Gb.test(b.url)?"url":"string"==typeof b.data&&!(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&Gb.test(b.data)&&"data");return h||"jsonp"===b.dataTypes[0]?(e=b.jsonpCallback=n.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(Gb,"$1"+e):b.jsonp!==!1&&(b.url+=(db.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||n.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,Fb.push(e)),g&&n.isFunction(f)&&f(g[0]),g=f=void 0}),"script"):void 0}),n.parseHTML=function(a,b,c){if(!a||"string"!=typeof a)return null;"boolean"==typeof b&&(c=b,b=!1),b=b||l;var d=v.exec(a),e=!c&&[];return d?[b.createElement(d[1])]:(d=n.buildFragment([a],b,e),e&&e.length&&n(e).remove(),n.merge([],d.childNodes))};var Hb=n.fn.load;n.fn.load=function(a,b,c){if("string"!=typeof a&&Hb)return Hb.apply(this,arguments);var d,e,f,g=this,h=a.indexOf(" ");return h>=0&&(d=n.trim(a.slice(h)),a=a.slice(0,h)),n.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(e="POST"),g.length>0&&n.ajax({url:a,type:e,dataType:"html",data:b}).done(function(a){f=arguments,g.html(d?n("<div>").append(n.parseHTML(a)).find(d):a)}).complete(c&&function(a,b){g.each(c,f||[a.responseText,b,a])}),this},n.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){n.fn[b]=function(a){return this.on(b,a)}}),n.expr.filters.animated=function(a){return n.grep(n.timers,function(b){return a===b.elem}).length};var Ib=a.document.documentElement;function Jb(a){return n.isWindow(a)?a:9===a.nodeType&&a.defaultView}n.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=n.css(a,"position"),l=n(a),m={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=n.css(a,"top"),i=n.css(a,"left"),j=("absolute"===k||"fixed"===k)&&(f+i).indexOf("auto")>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),n.isFunction(b)&&(b=b.call(a,c,h)),null!=b.top&&(m.top=b.top-h.top+g),null!=b.left&&(m.left=b.left-h.left+e),"using"in b?b.using.call(a,m):l.css(m)}},n.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){n.offset.setOffset(this,a,b)});var b,c,d=this[0],e={top:0,left:0},f=d&&d.ownerDocument;if(f)return b=f.documentElement,n.contains(b,d)?(typeof d.getBoundingClientRect!==U&&(e=d.getBoundingClientRect()),c=Jb(f),{top:e.top+c.pageYOffset-b.clientTop,left:e.left+c.pageXOffset-b.clientLeft}):e},position:function(){if(this[0]){var a,b,c=this[0],d={top:0,left:0};return"fixed"===n.css(c,"position")?b=c.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),n.nodeName(a[0],"html")||(d=a.offset()),d.top+=n.css(a[0],"borderTopWidth",!0),d.left+=n.css(a[0],"borderLeftWidth",!0)),{top:b.top-d.top-n.css(c,"marginTop",!0),left:b.left-d.left-n.css(c,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||Ib;while(a&&!n.nodeName(a,"html")&&"static"===n.css(a,"position"))a=a.offsetParent;return a||Ib})}}),n.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(b,c){var d="pageYOffset"===c;n.fn[b]=function(e){return J(this,function(b,e,f){var g=Jb(b);return void 0===f?g?g[c]:b[e]:void(g?g.scrollTo(d?a.pageXOffset:f,d?f:a.pageYOffset):b[e]=f)},b,e,arguments.length,null)}}),n.each(["top","left"],function(a,b){n.cssHooks[b]=ya(k.pixelPosition,function(a,c){return c?(c=xa(a,b),va.test(c)?n(a).position()[b]+"px":c):void 0})}),n.each({Height:"height",Width:"width"},function(a,b){n.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){n.fn[d]=function(d,e){var f=arguments.length&&(c||"boolean"!=typeof d),g=c||(d===!0||e===!0?"margin":"border");return J(this,function(b,c,d){var e;return n.isWindow(b)?b.document.documentElement["client"+a]:9===b.nodeType?(e=b.documentElement,Math.max(b.body["scroll"+a],e["scroll"+a],b.body["offset"+a],e["offset"+a],e["client"+a])):void 0===d?n.css(b,c,g):n.style(b,c,d,g)},b,f?d:void 0,f,null)}})}),n.fn.size=function(){return this.length},n.fn.andSelf=n.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return n});var Kb=a.jQuery,Lb=a.$;return n.noConflict=function(b){return a.$===n&&(a.$=Lb),b&&a.jQuery===n&&(a.jQuery=Kb),n},typeof b===U&&(a.jQuery=a.$=n),n});
5//# sourceMappingURL=jquery.min.map
6/*!
7 * jQuery UI Datepicker 1.11.4
8 * http://jqueryui.com
9 *
10 * Copyright jQuery Foundation and other contributors
11 * Released under the MIT license.
12 * http://jquery.org/license
13 *
14 * http://api.jqueryui.com/datepicker/
15 */
16(function( factory ) {
17	if ( typeof define === "function" && define.amd ) {
18
19		// AMD. Register as an anonymous module.
20		define([
21			"jquery",
22			"./core"
23		], factory );
24	} else {
25
26		// Browser globals
27		factory( jQuery );
28	}
29}(function( $ ) {
vendor: 77,476 bytes, lines 29-2086
29
30
31$.extend($.ui, { datepicker: { version: "1.11.4" } });
32
33var datepicker_instActive;
34
35function datepicker_getZindex( elem ) {
36	var position, value;
37	while ( elem.length && elem[ 0 ] !== document ) {
38		// Ignore z-index if position is set to a value where z-index is ignored by the browser
39		// This makes behavior of this function consistent across browsers
40		// WebKit always returns auto if the element is positioned
41		position = elem.css( "position" );
42		if ( position === "absolute" || position === "relative" || position === "fixed" ) {
43			// IE returns 0 when zIndex is not specified
44			// other browsers return a string
45			// we ignore the case of nested elements with an explicit value of 0
46			// <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
47			value = parseInt( elem.css( "zIndex" ), 10 );
48			if ( !isNaN( value ) && value !== 0 ) {
49				return value;
50			}
51		}
52		elem = elem.parent();
53	}
54
55	return 0;
56}
57/* Date picker manager.
58   Use the singleton instance of this class, $.datepicker, to interact with the date picker.
59   Settings for (groups of) date pickers are maintained in an instance object,
60   allowing multiple different settings on the same page. */
61
62function Datepicker() {
63	this._curInst = null; // The current instance in use
64	this._keyEvent = false; // If the last event was a key event
65	this._disabledInputs = []; // List of date picker inputs that have been disabled
66	this._datepickerShowing = false; // True if the popup picker is showing , false if not
67	this._inDialog = false; // True if showing within a "dialog", false if not
68	this._mainDivId = "ui-datepicker-div"; // The ID of the main datepicker division
69	this._inlineClass = "ui-datepicker-inline"; // The name of the inline marker class
70	this._appendClass = "ui-datepicker-append"; // The name of the append marker class
71	this._triggerClass = "ui-datepicker-trigger"; // The name of the trigger marker class
72	this._dialogClass = "ui-datepicker-dialog"; // The name of the dialog marker class
73	this._disableClass = "ui-datepicker-disabled"; // The name of the disabled covering marker class
74	this._unselectableClass = "ui-datepicker-unselectable"; // The name of the unselectable cell marker class
75	this._currentClass = "ui-datepicker-current-day"; // The name of the current day marker class
76	this._dayOverClass = "ui-datepicker-days-cell-over"; // The name of the day hover marker class
77	this.regional = []; // Available regional settings, indexed by language code
78	this.regional[""] = { // Default regional settings
79		closeText: "Done", // Display text for close link
80		prevText: "Prev", // Display text for previous month link
81		nextText: "Next", // Display text for next month link
82		currentText: "Today", // Display text for current month link
83		monthNames: ["January","February","March","April","May","June",
84			"July","August","September","October","November","December"], // Names of months for drop-down and formatting
85		monthNamesShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], // For formatting
86		dayNames: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], // For formatting
87		dayNamesShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], // For formatting
88		dayNamesMin: ["Su","Mo","Tu","We","Th","Fr","Sa"], // Column headings for days starting at Sunday
89		weekHeader: "Wk", // Column header for week of the year
90		dateFormat: "mm/dd/yy", // See format options on parseDate
91		firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
92		isRTL: false, // True if right-to-left language, false if left-to-right
93		showMonthAfterYear: false, // True if the year select precedes month, false for month then year
94		yearSuffix: "" // Additional text to append to the year in the month headers
95	};
96	this._defaults = { // Global defaults for all the date picker instances
97		showOn: "focus", // "focus" for popup on focus,
98			// "button" for trigger button, or "both" for either
99		showAnim: "fadeIn", // Name of jQuery animation for popup
100		showOptions: {}, // Options for enhanced animations
101		defaultDate: null, // Used when field is blank: actual date,
102			// +/-number for offset from today, null for today
103		appendText: "", // Display text following the input box, e.g. showing the format
104		buttonText: "...", // Text for trigger button
105		buttonImage: "", // URL for trigger button image
106		buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
107		hideIfNoPrevNext: false, // True to hide next/previous month links
108			// if not applicable, false to just disable them
109		navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links
110		gotoCurrent: false, // True if today link goes back to current selection instead
111		changeMonth: false, // True if month can be selected directly, false if only prev/next
112		changeYear: false, // True if year can be selected directly, false if only prev/next
113		yearRange: "c-10:c+10", // Range of years to display in drop-down,
114			// either relative to today's year (-nn:+nn), relative to currently displayed year
115			// (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n)
116		showOtherMonths: false, // True to show dates in other months, false to leave blank
117		selectOtherMonths: false, // True to allow selection of dates in other months, false for unselectable
118		showWeek: false, // True to show week of the year, false to not show it
119		calculateWeek: this.iso8601Week, // How to calculate the week of the year,
120			// takes a Date and returns the number of the week for it
121		shortYearCutoff: "+10", // Short year values < this are in the current century,
122			// > this are in the previous century,
123			// string value starting with "+" for current year + value
124		minDate: null, // The earliest selectable date, or null for no limit
125		maxDate: null, // The latest selectable date, or null for no limit
126		duration: "fast", // Duration of display/closure
127		beforeShowDay: null, // Function that takes a date and returns an array with
128			// [0] = true if selectable, false if not, [1] = custom CSS class name(s) or "",
129			// [2] = cell title (optional), e.g. $.datepicker.noWeekends
130		beforeShow: null, // Function that takes an input field and
131			// returns a set of custom settings for the date picker
132		onSelect: null, // Define a callback function when a date is selected
133		onChangeMonthYear: null, // Define a callback function when the month or year is changed
134		onClose: null, // Define a callback function when the datepicker is closed
135		numberOfMonths: 1, // Number of months to show at a time
136		showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0)
137		stepMonths: 1, // Number of months to step back/forward
138		stepBigMonths: 12, // Number of months to step back/forward for the big links
139		altField: "", // Selector for an alternate field to store selected dates into
140		altFormat: "", // The date format to use for the alternate field
141		constrainInput: true, // The input is constrained by the current date format
142		showButtonPanel: false, // True to show button panel, false to not show it
143		autoSize: false, // True to size the input for the date format, false to leave as is
144		disabled: false // The initial disabled state
145	};
146	$.extend(this._defaults, this.regional[""]);
147	this.regional.en = $.extend( true, {}, this.regional[ "" ]);
148	this.regional[ "en-US" ] = $.extend( true, {}, this.regional.en );
149	this.dpDiv = datepicker_bindHover($("<div id='" + this._mainDivId + "' class='ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>"));
150}
151
152$.extend(Datepicker.prototype, {
153	/* Class name added to elements to indicate already configured with a date picker. */
154	markerClassName: "hasDatepicker",
155
156	//Keep track of the maximum number of rows displayed (see #7043)
157	maxRows: 4,
158
159	// TODO rename to "widget" when switching to widget factory
160	_widgetDatepicker: function() {
161		return this.dpDiv;
162	},
163
164	/* Override the default settings for all instances of the date picker.
165	 * @param  settings  object - the new settings to use as defaults (anonymous object)
166	 * @return the manager object
167	 */
168	setDefaults: function(settings) {
169		datepicker_extendRemove(this._defaults, settings || {});
170		return this;
171	},
172
173	/* Attach the date picker to a jQuery selection.
174	 * @param  target	element - the target input field or division or span
175	 * @param  settings  object - the new settings to use for this date picker instance (anonymous)
176	 */
177	_attachDatepicker: function(target, settings) {
178		var nodeName, inline, inst;
179		nodeName = target.nodeName.toLowerCase();
180		inline = (nodeName === "div" || nodeName === "span");
181		if (!target.id) {
182			this.uuid += 1;
183			target.id = "dp" + this.uuid;
184		}
185		inst = this._newInst($(target), inline);
186		inst.settings = $.extend({}, settings || {});
187		if (nodeName === "input") {
188			this._connectDatepicker(target, inst);
189		} else if (inline) {
190			this._inlineDatepicker(target, inst);
191		}
192	},
193
194	/* Create a new instance object. */
195	_newInst: function(target, inline) {
196		var id = target[0].id.replace(/([^A-Za-z0-9_\-])/g, "\\\\$1"); // escape jQuery meta chars
197		return {id: id, input: target, // associated target
198			selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection
199			drawMonth: 0, drawYear: 0, // month being drawn
200			inline: inline, // is datepicker inline or not
201			dpDiv: (!inline ? this.dpDiv : // presentation div
202			datepicker_bindHover($("<div class='" + this._inlineClass + " ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>")))};
203	},
204
205	/* Attach the date picker to an input field. */
206	_connectDatepicker: function(target, inst) {
207		var input = $(target);
208		inst.append = $([]);
209		inst.trigger = $([]);
210		if (input.hasClass(this.markerClassName)) {
211			return;
212		}
213		this._attachments(input, inst);
214		input.addClass(this.markerClassName).keydown(this._doKeyDown).
215			keypress(this._doKeyPress).keyup(this._doKeyUp);
216		this._autoSize(inst);
217		$.data(target, "datepicker", inst);
218		//If disabled option is true, disable the datepicker once it has been attached to the input (see ticket #5665)
219		if( inst.settings.disabled ) {
220			this._disableDatepicker( target );
221		}
222	},
223
224	/* Make attachments based on settings. */
225	_attachments: function(input, inst) {
226		var showOn, buttonText, buttonImage,
227			appendText = this._get(inst, "appendText"),
228			isRTL = this._get(inst, "isRTL");
229
230		if (inst.append) {
231			inst.append.remove();
232		}
233		if (appendText) {
234			inst.append = $("<span class='" + this._appendClass + "'>" + appendText + "</span>");
235			input[isRTL ? "before" : "after"](inst.append);
236		}
237
238		input.unbind("focus", this._showDatepicker);
239
240		if (inst.trigger) {
241			inst.trigger.remove();
242		}
243
244		showOn = this._get(inst, "showOn");
245		if (showOn === "focus" || showOn === "both") { // pop-up date picker when in the marked field
246			input.focus(this._showDatepicker);
247		}
248		if (showOn === "button" || showOn === "both") { // pop-up date picker when button clicked
249			buttonText = this._get(inst, "buttonText");
250			buttonImage = this._get(inst, "buttonImage");
251			inst.trigger = $(this._get(inst, "buttonImageOnly") ?
252				$("<img/>").addClass(this._triggerClass).
253					attr({ src: buttonImage, alt: buttonText, title: buttonText }) :
254				$("<button type='button'></button>").addClass(this._triggerClass).
255					html(!buttonImage ? buttonText : $("<img/>").attr(
256					{ src:buttonImage, alt:buttonText, title:buttonText })));
257			input[isRTL ? "before" : "after"](inst.trigger);
258			inst.trigger.click(function() {
259				if ($.datepicker._datepickerShowing && $.datepicker._lastInput === input[0]) {
260					$.datepicker._hideDatepicker();
261				} else if ($.datepicker._datepickerShowing && $.datepicker._lastInput !== input[0]) {
262					$.datepicker._hideDatepicker();
263					$.datepicker._showDatepicker(input[0]);
264				} else {
265					$.datepicker._showDatepicker(input[0]);
266				}
267				return false;
268			});
269		}
270	},
271
272	/* Apply the maximum length for the date format. */
273	_autoSize: function(inst) {
274		if (this._get(inst, "autoSize") && !inst.inline) {
275			var findMax, max, maxI, i,
276				date = new Date(2009, 12 - 1, 20), // Ensure double digits
277				dateFormat = this._get(inst, "dateFormat");
278
279			if (dateFormat.match(/[DM]/)) {
280				findMax = function(names) {
281					max = 0;
282					maxI = 0;
283					for (i = 0; i < names.length; i++) {
284						if (names[i].length > max) {
285							max = names[i].length;
286							maxI = i;
287						}
288					}
289					return maxI;
290				};
291				date.setMonth(findMax(this._get(inst, (dateFormat.match(/MM/) ?
292					"monthNames" : "monthNamesShort"))));
293				date.setDate(findMax(this._get(inst, (dateFormat.match(/DD/) ?
294					"dayNames" : "dayNamesShort"))) + 20 - date.getDay());
295			}
296			inst.input.attr("size", this._formatDate(inst, date).length);
297		}
298	},
299
300	/* Attach an inline date picker to a div. */
301	_inlineDatepicker: function(target, inst) {
302		var divSpan = $(target);
303		if (divSpan.hasClass(this.markerClassName)) {
304			return;
305		}
306		divSpan.addClass(this.markerClassName).append(inst.dpDiv);
307		$.data(target, "datepicker", inst);
308		this._setDate(inst, this._getDefaultDate(inst), true);
309		this._updateDatepicker(inst);
310		this._updateAlternate(inst);
311		//If disabled option is true, disable the datepicker before showing it (see ticket #5665)
312		if( inst.settings.disabled ) {
313			this._disableDatepicker( target );
314		}
315		// Set display:block in place of inst.dpDiv.show() which won't work on disconnected elements
316		// http://bugs.jqueryui.com/ticket/7552 - A Datepicker created on a detached div has zero height
317		inst.dpDiv.css( "display", "block" );
318	},
319
320	/* Pop-up the date picker in a "dialog" box.
321	 * @param  input element - ignored
322	 * @param  date	string or Date - the initial date to display
323	 * @param  onSelect  function - the function to call when a date is selected
324	 * @param  settings  object - update the dialog date picker instance's settings (anonymous object)
325	 * @param  pos int[2] - coordinates for the dialog's position within the screen or
326	 *					event - with x/y coordinates or
327	 *					leave empty for default (screen centre)
328	 * @return the manager object
329	 */
330	_dialogDatepicker: function(input, date, onSelect, settings, pos) {
331		var id, browserWidth, browserHeight, scrollX, scrollY,
332			inst = this._dialogInst; // internal instance
333
334		if (!inst) {
335			this.uuid += 1;
336			id = "dp" + this.uuid;
337			this._dialogInput = $("<input type='text' id='" + id +
338				"' style='position: absolute; top: -100px; width: 0px;'/>");
339			this._dialogInput.keydown(this._doKeyDown);
340			$("body").append(this._dialogInput);
341			inst = this._dialogInst = this._newInst(this._dialogInput, false);
342			inst.settings = {};
343			$.data(this._dialogInput[0], "datepicker", inst);
344		}
345		datepicker_extendRemove(inst.settings, settings || {});
346		date = (date && date.constructor === Date ? this._formatDate(inst, date) : date);
347		this._dialogInput.val(date);
348
349		this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null);
350		if (!this._pos) {
351			browserWidth = document.documentElement.clientWidth;
352			browserHeight = document.documentElement.clientHeight;
353			scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
354			scrollY = document.documentElement.scrollTop || document.body.scrollTop;
355			this._pos = // should use actual width/height below
356				[(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY];
357		}
358
359		// move input on screen for focus, but hidden behind dialog
360		this._dialogInput.css("left", (this._pos[0] + 20) + "px").css("top", this._pos[1] + "px");
361		inst.settings.onSelect = onSelect;
362		this._inDialog = true;
363		this.dpDiv.addClass(this._dialogClass);
364		this._showDatepicker(this._dialogInput[0]);
365		if ($.blockUI) {
366			$.blockUI(this.dpDiv);
367		}
368		$.data(this._dialogInput[0], "datepicker", inst);
369		return this;
370	},
371
372	/* Detach a datepicker from its control.
373	 * @param  target	element - the target input field or division or span
374	 */
375	_destroyDatepicker: function(target) {
376		var nodeName,
377			$target = $(target),
378			inst = $.data(target, "datepicker");
379
380		if (!$target.hasClass(this.markerClassName)) {
381			return;
382		}
383
384		nodeName = target.nodeName.toLowerCase();
385		$.removeData(target, "datepicker");
386		if (nodeName === "input") {
387			inst.append.remove();
388			inst.trigger.remove();
389			$target.removeClass(this.markerClassName).
390				unbind("focus", this._showDatepicker).
391				unbind("keydown", this._doKeyDown).
392				unbind("keypress", this._doKeyPress).
393				unbind("keyup", this._doKeyUp);
394		} else if (nodeName === "div" || nodeName === "span") {
395			$target.removeClass(this.markerClassName).empty();
396		}
397
398		if ( datepicker_instActive === inst ) {
399			datepicker_instActive = null;
400		}
401	},
402
403	/* Enable the date picker to a jQuery selection.
404	 * @param  target	element - the target input field or division or span
405	 */
406	_enableDatepicker: function(target) {
407		var nodeName, inline,
408			$target = $(target),
409			inst = $.data(target, "datepicker");
410
411		if (!$target.hasClass(this.markerClassName)) {
412			return;
413		}
414
415		nodeName = target.nodeName.toLowerCase();
416		if (nodeName === "input") {
417			target.disabled = false;
418			inst.trigger.filter("button").
419				each(function() { this.disabled = false; }).end().
420				filter("img").css({opacity: "1.0", cursor: ""});
421		} else if (nodeName === "div" || nodeName === "span") {
422			inline = $target.children("." + this._inlineClass);
423			inline.children().removeClass("ui-state-disabled");
424			inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
425				prop("disabled", false);
426		}
427		this._disabledInputs = $.map(this._disabledInputs,
428			function(value) { return (value === target ? null : value); }); // delete entry
429	},
430
431	/* Disable the date picker to a jQuery selection.
432	 * @param  target	element - the target input field or division or span
433	 */
434	_disableDatepicker: function(target) {
435		var nodeName, inline,
436			$target = $(target),
437			inst = $.data(target, "datepicker");
438
439		if (!$target.hasClass(this.markerClassName)) {
440			return;
441		}
442
443		nodeName = target.nodeName.toLowerCase();
444		if (nodeName === "input") {
445			target.disabled = true;
446			inst.trigger.filter("button").
447				each(function() { this.disabled = true; }).end().
448				filter("img").css({opacity: "0.5", cursor: "default"});
449		} else if (nodeName === "div" || nodeName === "span") {
450			inline = $target.children("." + this._inlineClass);
451			inline.children().addClass("ui-state-disabled");
452			inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
453				prop("disabled", true);
454		}
455		this._disabledInputs = $.map(this._disabledInputs,
456			function(value) { return (value === target ? null : value); }); // delete entry
457		this._disabledInputs[this._disabledInputs.length] = target;
458	},
459
460	/* Is the first field in a jQuery collection disabled as a datepicker?
461	 * @param  target	element - the target input field or division or span
462	 * @return boolean - true if disabled, false if enabled
463	 */
464	_isDisabledDatepicker: function(target) {
465		if (!target) {
466			return false;
467		}
468		for (var i = 0; i < this._disabledInputs.length; i++) {
469			if (this._disabledInputs[i] === target) {
470				return true;
471			}
472		}
473		return false;
474	},
475
476	/* Retrieve the instance data for the target control.
477	 * @param  target  element - the target input field or division or span
478	 * @return  object - the associated instance data
479	 * @throws  error if a jQuery problem getting data
480	 */
481	_getInst: function(target) {
482		try {
483			return $.data(target, "datepicker");
484		}
485		catch (err) {
486			throw "Missing instance data for this datepicker";
487		}
488	},
489
490	/* Update or retrieve the settings for a date picker attached to an input field or division.
491	 * @param  target  element - the target input field or division or span
492	 * @param  name	object - the new settings to update or
493	 *				string - the name of the setting to change or retrieve,
494	 *				when retrieving also "all" for all instance settings or
495	 *				"defaults" for all global defaults
496	 * @param  value   any - the new value for the setting
497	 *				(omit if above is an object or to retrieve a value)
498	 */
499	_optionDatepicker: function(target, name, value) {
500		var settings, date, minDate, maxDate,
501			inst = this._getInst(target);
502
503		if (arguments.length === 2 && typeof name === "string") {
504			return (name === "defaults" ? $.extend({}, $.datepicker._defaults) :
505				(inst ? (name === "all" ? $.extend({}, inst.settings) :
506				this._get(inst, name)) : null));
507		}
508
509		settings = name || {};
510		if (typeof name === "string") {
511			settings = {};
512			settings[name] = value;
513		}
514
515		if (inst) {
516			if (this._curInst === inst) {
517				this._hideDatepicker();
518			}
519
520			date = this._getDateDatepicker(target, true);
521			minDate = this._getMinMaxDate(inst, "min");
522			maxDate = this._getMinMaxDate(inst, "max");
523			datepicker_extendRemove(inst.settings, settings);
524			// reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided
525			if (minDate !== null && settings.dateFormat !== undefined && settings.minDate === undefined) {
526				inst.settings.minDate = this._formatDate(inst, minDate);
527			}
528			if (maxDate !== null && settings.dateFormat !== undefined && settings.maxDate === undefined) {
529				inst.settings.maxDate = this._formatDate(inst, maxDate);
530			}
531			if ( "disabled" in settings ) {
532				if ( settings.disabled ) {
533					this._disableDatepicker(target);
534				} else {
535					this._enableDatepicker(target);
536				}
537			}
538			this._attachments($(target), inst);
539			this._autoSize(inst);
540			this._setDate(inst, date);
541			this._updateAlternate(inst);
542			this._updateDatepicker(inst);
543		}
544	},
545
546	// change method deprecated
547	_changeDatepicker: function(target, name, value) {
548		this._optionDatepicker(target, name, value);
549	},
550
551	/* Redraw the date picker attached to an input field or division.
552	 * @param  target  element - the target input field or division or span
553	 */
554	_refreshDatepicker: function(target) {
555		var inst = this._getInst(target);
556		if (inst) {
557			this._updateDatepicker(inst);
558		}
559	},
560
561	/* Set the dates for a jQuery selection.
562	 * @param  target element - the target input field or division or span
563	 * @param  date	Date - the new date
564	 */
565	_setDateDatepicker: function(target, date) {
566		var inst = this._getInst(target);
567		if (inst) {
568			this._setDate(inst, date);
569			this._updateDatepicker(inst);
570			this._updateAlternate(inst);
571		}
572	},
573
574	/* Get the date(s) for the first entry in a jQuery selection.
575	 * @param  target element - the target input field or division or span
576	 * @param  noDefault boolean - true if no default date is to be used
577	 * @return Date - the current date
578	 */
579	_getDateDatepicker: function(target, noDefault) {
580		var inst = this._getInst(target);
581		if (inst && !inst.inline) {
582			this._setDateFromField(inst, noDefault);
583		}
584		return (inst ? this._getDate(inst) : null);
585	},
586
587	/* Handle keystrokes. */
588	_doKeyDown: function(event) {
589		var onSelect, dateStr, sel,
590			inst = $.datepicker._getInst(event.target),
591			handled = true,
592			isRTL = inst.dpDiv.is(".ui-datepicker-rtl");
593
594		inst._keyEvent = true;
595		if ($.datepicker._datepickerShowing) {
596			switch (event.keyCode) {
597				case 9: $.datepicker._hideDatepicker();
598						handled = false;
599						break; // hide on tab out
600				case 13: sel = $("td." + $.datepicker._dayOverClass + ":not(." +
601									$.datepicker._currentClass + ")", inst.dpDiv);
602						if (sel[0]) {
603							$.datepicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, sel[0]);
604						}
605
606						onSelect = $.datepicker._get(inst, "onSelect");
607						if (onSelect) {
608							dateStr = $.datepicker._formatDate(inst);
609
610							// trigger custom callback
611							onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]);
612						} else {
613							$.datepicker._hideDatepicker();
614						}
615
616						return false; // don't submit the form
617				case 27: $.datepicker._hideDatepicker();
618						break; // hide on escape
619				case 33: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
620							-$.datepicker._get(inst, "stepBigMonths") :
621							-$.datepicker._get(inst, "stepMonths")), "M");
622						break; // previous month/year on page up/+ ctrl
623				case 34: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
624							+$.datepicker._get(inst, "stepBigMonths") :
625							+$.datepicker._get(inst, "stepMonths")), "M");
626						break; // next month/year on page down/+ ctrl
627				case 35: if (event.ctrlKey || event.metaKey) {
628							$.datepicker._clearDate(event.target);
629						}
630						handled = event.ctrlKey || event.metaKey;
631						break; // clear on ctrl or command +end
632				case 36: if (event.ctrlKey || event.metaKey) {
633							$.datepicker._gotoToday(event.target);
634						}
635						handled = event.ctrlKey || event.metaKey;
636						break; // current on ctrl or command +home
637				case 37: if (event.ctrlKey || event.metaKey) {
638							$.datepicker._adjustDate(event.target, (isRTL ? +1 : -1), "D");
639						}
640						handled = event.ctrlKey || event.metaKey;
641						// -1 day on ctrl or command +left
642						if (event.originalEvent.altKey) {
643							$.datepicker._adjustDate(event.target, (event.ctrlKey ?
644								-$.datepicker._get(inst, "stepBigMonths") :
645								-$.datepicker._get(inst, "stepMonths")), "M");
646						}
647						// next month/year on alt +left on Mac
648						break;
649				case 38: if (event.ctrlKey || event.metaKey) {
650							$.datepicker._adjustDate(event.target, -7, "D");
651						}
652						handled = event.ctrlKey || event.metaKey;
653						break; // -1 week on ctrl or command +up
654				case 39: if (event.ctrlKey || event.metaKey) {
655							$.datepicker._adjustDate(event.target, (isRTL ? -1 : +1), "D");
656						}
657						handled = event.ctrlKey || event.metaKey;
658						// +1 day on ctrl or command +right
659						if (event.originalEvent.altKey) {
660							$.datepicker._adjustDate(event.target, (event.ctrlKey ?
661								+$.datepicker._get(inst, "stepBigMonths") :
662								+$.datepicker._get(inst, "stepMonths")), "M");
663						}
664						// next month/year on alt +right
665						break;
666				case 40: if (event.ctrlKey || event.metaKey) {
667							$.datepicker._adjustDate(event.target, +7, "D");
668						}
669						handled = event.ctrlKey || event.metaKey;
670						break; // +1 week on ctrl or command +down
671				default: handled = false;
672			}
673		} else if (event.keyCode === 36 && event.ctrlKey) { // display the date picker on ctrl+home
674			$.datepicker._showDatepicker(this);
675		} else {
676			handled = false;
677		}
678
679		if (handled) {
680			event.preventDefault();
681			event.stopPropagation();
682		}
683	},
684
685	/* Filter entered characters - based on date format. */
686	_doKeyPress: function(event) {
687		var chars, chr,
688			inst = $.datepicker._getInst(event.target);
689
690		if ($.datepicker._get(inst, "constrainInput")) {
691			chars = $.datepicker._possibleChars($.datepicker._get(inst, "dateFormat"));
692			chr = String.fromCharCode(event.charCode == null ? event.keyCode : event.charCode);
693			return event.ctrlKey || event.metaKey || (chr < " " || !chars || chars.indexOf(chr) > -1);
694		}
695	},
696
697	/* Synchronise manual entry and field/alternate field. */
698	_doKeyUp: function(event) {
699		var date,
700			inst = $.datepicker._getInst(event.target);
701
702		if (inst.input.val() !== inst.lastVal) {
703			try {
704				date = $.datepicker.parseDate($.datepicker._get(inst, "dateFormat"),
705					(inst.input ? inst.input.val() : null),
706					$.datepicker._getFormatConfig(inst));
707
708				if (date) { // only if valid
709					$.datepicker._setDateFromField(inst);
710					$.datepicker._updateAlternate(inst);
711					$.datepicker._updateDatepicker(inst);
712				}
713			}
714			catch (err) {
715			}
716		}
717		return true;
718	},
719
720	/* Pop-up the date picker for a given input field.
721	 * If false returned from beforeShow event handler do not show.
722	 * @param  input  element - the input field attached to the date picker or
723	 *					event - if triggered by focus
724	 */
725	_showDatepicker: function(input) {
726		input = input.target || input;
727		if (input.nodeName.toLowerCase() !== "input") { // find from button/image trigger
728			input = $("input", input.parentNode)[0];
729		}
730
731		if ($.datepicker._isDisabledDatepicker(input) || $.datepicker._lastInput === input) { // already here
732			return;
733		}
734
735		var inst, beforeShow, beforeShowSettings, isFixed,
736			offset, showAnim, duration;
737
738		inst = $.datepicker._getInst(input);
739		if ($.datepicker._curInst && $.datepicker._curInst !== inst) {
740			$.datepicker._curInst.dpDiv.stop(true, true);
741			if ( inst && $.datepicker._datepickerShowing ) {
742				$.datepicker._hideDatepicker( $.datepicker._curInst.input[0] );
743			}
744		}
745
746		beforeShow = $.datepicker._get(inst, "beforeShow");
747		beforeShowSettings = beforeShow ? beforeShow.apply(input, [input, inst]) : {};
748		if(beforeShowSettings === false){
749			return;
750		}
751		datepicker_extendRemove(inst.settings, beforeShowSettings);
752
753		inst.lastVal = null;
754		$.datepicker._lastInput = input;
755		$.datepicker._setDateFromField(inst);
756
757		if ($.datepicker._inDialog) { // hide cursor
758			input.value = "";
759		}
760		if (!$.datepicker._pos) { // position below input
761			$.datepicker._pos = $.datepicker._findPos(input);
762			$.datepicker._pos[1] += input.offsetHeight; // add the height
763		}
764
765		isFixed = false;
766		$(input).parents().each(function() {
767			isFixed |= $(this).css("position") === "fixed";
768			return !isFixed;
769		});
770
771		offset = {left: $.datepicker._pos[0], top: $.datepicker._pos[1]};
772		$.datepicker._pos = null;
773		//to avoid flashes on Firefox
774		inst.dpDiv.empty();
775		// determine sizing offscreen
776		inst.dpDiv.css({position: "absolute", display: "block", top: "-1000px"});
777		$.datepicker._updateDatepicker(inst);
778		// fix width for dynamic number of date pickers
779		// and adjust position before showing
780		offset = $.datepicker._checkOffset(inst, offset, isFixed);
781		inst.dpDiv.css({position: ($.datepicker._inDialog && $.blockUI ?
782			"static" : (isFixed ? "fixed" : "absolute")), display: "none",
783			left: offset.left + "px", top: offset.top + "px"});
784
785		if (!inst.inline) {
786			showAnim = $.datepicker._get(inst, "showAnim");
787			duration = $.datepicker._get(inst, "duration");
788			inst.dpDiv.css( "z-index", datepicker_getZindex( $( input ) ) + 1 );
789			$.datepicker._datepickerShowing = true;
790
791			if ( $.effects && $.effects.effect[ showAnim ] ) {
792				inst.dpDiv.show(showAnim, $.datepicker._get(inst, "showOptions"), duration);
793			} else {
794				inst.dpDiv[showAnim || "show"](showAnim ? duration : null);
795			}
796
797			if ( $.datepicker._shouldFocusInput( inst ) ) {
798				inst.input.focus();
799			}
800
801			$.datepicker._curInst = inst;
802		}
803	},
804
805	/* Generate the date picker content. */
806	_updateDatepicker: function(inst) {
807		this.maxRows = 4; //Reset the max number of rows being displayed (see #7043)
808		datepicker_instActive = inst; // for delegate hover events
809		inst.dpDiv.empty().append(this._generateHTML(inst));
810		this._attachHandlers(inst);
811
812		var origyearshtml,
813			numMonths = this._getNumberOfMonths(inst),
814			cols = numMonths[1],
815			width = 17,
816			activeCell = inst.dpDiv.find( "." + this._dayOverClass + " a" );
817
818		if ( activeCell.length > 0 ) {
819			datepicker_handleMouseover.apply( activeCell.get( 0 ) );
820		}
821
822		inst.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width("");
823		if (cols > 1) {
824			inst.dpDiv.addClass("ui-datepicker-multi-" + cols).css("width", (width * cols) + "em");
825		}
826		inst.dpDiv[(numMonths[0] !== 1 || numMonths[1] !== 1 ? "add" : "remove") +
827			"Class"]("ui-datepicker-multi");
828		inst.dpDiv[(this._get(inst, "isRTL") ? "add" : "remove") +
829			"Class"]("ui-datepicker-rtl");
830
831		if (inst === $.datepicker._curInst && $.datepicker._datepickerShowing && $.datepicker._shouldFocusInput( inst ) ) {
832			inst.input.focus();
833		}
834
835		// deffered render of the years select (to avoid flashes on Firefox)
836		if( inst.yearshtml ){
837			origyearshtml = inst.yearshtml;
838			setTimeout(function(){
839				//assure that inst.yearshtml didn't change.
840				if( origyearshtml === inst.yearshtml && inst.yearshtml ){
841					inst.dpDiv.find("select.ui-datepicker-year:first").replaceWith(inst.yearshtml);
842				}
843				origyearshtml = inst.yearshtml = null;
844			}, 0);
845		}
846	},
847
848	// #6694 - don't focus the input if it's already focused
849	// this breaks the change event in IE
850	// Support: IE and jQuery <1.9
851	_shouldFocusInput: function( inst ) {
852		return inst.input && inst.input.is( ":visible" ) && !inst.input.is( ":disabled" ) && !inst.input.is( ":focus" );
853	},
854
855	/* Check positioning to remain on screen. */
856	_checkOffset: function(inst, offset, isFixed) {
857		var dpWidth = inst.dpDiv.outerWidth(),
858			dpHeight = inst.dpDiv.outerHeight(),
859			inputWidth = inst.input ? inst.input.outerWidth() : 0,
860			inputHeight = inst.input ? inst.input.outerHeight() : 0,
861			viewWidth = document.documentElement.clientWidth + (isFixed ? 0 : $(document).scrollLeft()),
862			viewHeight = document.documentElement.clientHeight + (isFixed ? 0 : $(document).scrollTop());
863
864		offset.left -= (this._get(inst, "isRTL") ? (dpWidth - inputWidth) : 0);
865		offset.left -= (isFixed && offset.left === inst.input.offset().left) ? $(document).scrollLeft() : 0;
866		offset.top -= (isFixed && offset.top === (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0;
867
868		// now check if datepicker is showing outside window viewport - move to a better place if so.
869		offset.left -= Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ?
870			Math.abs(offset.left + dpWidth - viewWidth) : 0);
871		offset.top -= Math.min(offset.top, (offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ?
872			Math.abs(dpHeight + inputHeight) : 0);
873
874		return offset;
875	},
876
877	/* Find an object's position on the screen. */
878	_findPos: function(obj) {
879		var position,
880			inst = this._getInst(obj),
881			isRTL = this._get(inst, "isRTL");
882
883		while (obj && (obj.type === "hidden" || obj.nodeType !== 1 || $.expr.filters.hidden(obj))) {
884			obj = obj[isRTL ? "previousSibling" : "nextSibling"];
885		}
886
887		position = $(obj).offset();
888		return [position.left, position.top];
889	},
890
891	/* Hide the date picker from view.
892	 * @param  input  element - the input field attached to the date picker
893	 */
894	_hideDatepicker: function(input) {
895		var showAnim, duration, postProcess, onClose,
896			inst = this._curInst;
897
898		if (!inst || (input && inst !== $.data(input, "datepicker"))) {
899			return;
900		}
901
902		if (this._datepickerShowing) {
903			showAnim = this._get(inst, "showAnim");
904			duration = this._get(inst, "duration");
905			postProcess = function() {
906				$.datepicker._tidyDialog(inst);
907			};
908
909			// DEPRECATED: after BC for 1.8.x $.effects[ showAnim ] is not needed
910			if ( $.effects && ( $.effects.effect[ showAnim ] || $.effects[ showAnim ] ) ) {
911				inst.dpDiv.hide(showAnim, $.datepicker._get(inst, "showOptions"), duration, postProcess);
912			} else {
913				inst.dpDiv[(showAnim === "slideDown" ? "slideUp" :
914					(showAnim === "fadeIn" ? "fadeOut" : "hide"))]((showAnim ? duration : null), postProcess);
915			}
916
917			if (!showAnim) {
918				postProcess();
919			}
920			this._datepickerShowing = false;
921
922			onClose = this._get(inst, "onClose");
923			if (onClose) {
924				onClose.apply((inst.input ? inst.input[0] : null), [(inst.input ? inst.input.val() : ""), inst]);
925			}
926
927			this._lastInput = null;
928			if (this._inDialog) {
929				this._dialogInput.css({ position: "absolute", left: "0", top: "-100px" });
930				if ($.blockUI) {
931					$.unblockUI();
932					$("body").append(this.dpDiv);
933				}
934			}
935			this._inDialog = false;
936		}
937	},
938
939	/* Tidy up after a dialog display. */
940	_tidyDialog: function(inst) {
941		inst.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar");
942	},
943
944	/* Close date picker if clicked elsewhere. */
945	_checkExternalClick: function(event) {
946		if (!$.datepicker._curInst) {
947			return;
948		}
949
950		var $target = $(event.target),
951			inst = $.datepicker._getInst($target[0]);
952
953		if ( ( ( $target[0].id !== $.datepicker._mainDivId &&
954				$target.parents("#" + $.datepicker._mainDivId).length === 0 &&
955				!$target.hasClass($.datepicker.markerClassName) &&
956				!$target.closest("." + $.datepicker._triggerClass).length &&
957				$.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI) ) ) ||
958			( $target.hasClass($.datepicker.markerClassName) && $.datepicker._curInst !== inst ) ) {
959				$.datepicker._hideDatepicker();
960		}
961	},
962
963	/* Adjust one of the date sub-fields. */
964	_adjustDate: function(id, offset, period) {
965		var target = $(id),
966			inst = this._getInst(target[0]);
967
968		if (this._isDisabledDatepicker(target[0])) {
969			return;
970		}
971		this._adjustInstDate(inst, offset +
972			(period === "M" ? this._get(inst, "showCurrentAtPos") : 0), // undo positioning
973			period);
974		this._updateDatepicker(inst);
975	},
976
977	/* Action for current link. */
978	_gotoToday: function(id) {
979		var date,
980			target = $(id),
981			inst = this._getInst(target[0]);
982
983		if (this._get(inst, "gotoCurrent") && inst.currentDay) {
984			inst.selectedDay = inst.currentDay;
985			inst.drawMonth = inst.selectedMonth = inst.currentMonth;
986			inst.drawYear = inst.selectedYear = inst.currentYear;
987		} else {
988			date = new Date();
989			inst.selectedDay = date.getDate();
990			inst.drawMonth = inst.selectedMonth = date.getMonth();
991			inst.drawYear = inst.selectedYear = date.getFullYear();
992		}
993		this._notifyChange(inst);
994		this._adjustDate(target);
995	},
996
997	/* Action for selecting a new month/year. */
998	_selectMonthYear: function(id, select, period) {
999		var target = $(id),
1000			inst = this._getInst(target[0]);
1001
1002		inst["selected" + (period === "M" ? "Month" : "Year")] =
1003		inst["draw" + (period === "M" ? "Month" : "Year")] =
1004			parseInt(select.options[select.selectedIndex].value,10);
1005
1006		this._notifyChange(inst);
1007		this._adjustDate(target);
1008	},
1009
1010	/* Action for selecting a day. */
1011	_selectDay: function(id, month, year, td) {
1012		var inst,
1013			target = $(id);
1014
1015		if ($(td).hasClass(this._unselectableClass) || this._isDisabledDatepicker(target[0])) {
1016			return;
1017		}
1018
1019		inst = this._getInst(target[0]);
1020		inst.selectedDay = inst.currentDay = $("a", td).html();
1021		inst.selectedMonth = inst.currentMonth = month;
1022		inst.selectedYear = inst.currentYear = year;
1023		this._selectDate(id, this._formatDate(inst,
1024			inst.currentDay, inst.currentMonth, inst.currentYear));
1025	},
1026
1027	/* Erase the input field and hide the date picker. */
1028	_clearDate: function(id) {
1029		var target = $(id);
1030		this._selectDate(target, "");
1031	},
1032
1033	/* Update the input field with the selected date. */
1034	_selectDate: function(id, dateStr) {
1035		var onSelect,
1036			target = $(id),
1037			inst = this._getInst(target[0]);
1038
1039		dateStr = (dateStr != null ? dateStr : this._formatDate(inst));
1040		if (inst.input) {
1041			inst.input.val(dateStr);
1042		}
1043		this._updateAlternate(inst);
1044
1045		onSelect = this._get(inst, "onSelect");
1046		if (onSelect) {
1047			onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]);  // trigger custom callback
1048		} else if (inst.input) {
1049			inst.input.trigger("change"); // fire the change event
1050		}
1051
1052		if (inst.inline){
1053			this._updateDatepicker(inst);
1054		} else {
1055			this._hideDatepicker();
1056			this._lastInput = inst.input[0];
1057			if (typeof(inst.input[0]) !== "object") {
1058				inst.input.focus(); // restore focus
1059			}
1060			this._lastInput = null;
1061		}
1062	},
1063
1064	/* Update any alternate field to synchronise with the main field. */
1065	_updateAlternate: function(inst) {
1066		var altFormat, date, dateStr,
1067			altField = this._get(inst, "altField");
1068
1069		if (altField) { // update alternate field too
1070			altFormat = this._get(inst, "altFormat") || this._get(inst, "dateFormat");
1071			date = this._getDate(inst);
1072			dateStr = this.formatDate(altFormat, date, this._getFormatConfig(inst));
1073			$(altField).each(function() { $(this).val(dateStr); });
1074		}
1075	},
1076
1077	/* Set as beforeShowDay function to prevent selection of weekends.
1078	 * @param  date  Date - the date to customise
1079	 * @return [boolean, string] - is this date selectable?, what is its CSS class?
1080	 */
1081	noWeekends: function(date) {
1082		var day = date.getDay();
1083		return [(day > 0 && day < 6), ""];
1084	},
1085
1086	/* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
1087	 * @param  date  Date - the date to get the week for
1088	 * @return  number - the number of the week within the year that contains this date
1089	 */
1090	iso8601Week: function(date) {
1091		var time,
1092			checkDate = new Date(date.getTime());
1093
1094		// Find Thursday of this week starting on Monday
1095		checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7));
1096
1097		time = checkDate.getTime();
1098		checkDate.setMonth(0); // Compare with Jan 1
1099		checkDate.setDate(1);
1100		return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
1101	},
1102
1103	/* Parse a string value into a date object.
1104	 * See formatDate below for the possible formats.
1105	 *
1106	 * @param  format string - the expected format of the date
1107	 * @param  value string - the date in the above format
1108	 * @param  settings Object - attributes include:
1109	 *					shortYearCutoff  number - the cutoff year for determining the century (optional)
1110	 *					dayNamesShort	string[7] - abbreviated names of the days from Sunday (optional)
1111	 *					dayNames		string[7] - names of the days from Sunday (optional)
1112	 *					monthNamesShort string[12] - abbreviated names of the months (optional)
1113	 *					monthNames		string[12] - names of the months (optional)
1114	 * @return  Date - the extracted date value or null if value is blank
1115	 */
1116	parseDate: function (format, value, settings) {
1117		if (format == null || value == null) {
1118			throw "Invalid arguments";
1119		}
1120
1121		value = (typeof value === "object" ? value.toString() : value + "");
1122		if (value === "") {
1123			return null;
1124		}
1125
1126		var iFormat, dim, extra,
1127			iValue = 0,
1128			shortYearCutoffTemp = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff,
1129			shortYearCutoff = (typeof shortYearCutoffTemp !== "string" ? shortYearCutoffTemp :
1130				new Date().getFullYear() % 100 + parseInt(shortYearCutoffTemp, 10)),
1131			dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort,
1132			dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames,
1133			monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort,
1134			monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames,
1135			year = -1,
1136			month = -1,
1137			day = -1,
1138			doy = -1,
1139			literal = false,
1140			date,
1141			// Check whether a format character is doubled
1142			lookAhead = function(match) {
1143				var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
1144				if (matches) {
1145					iFormat++;
1146				}
1147				return matches;
1148			},
1149			// Extract a number from the string value
1150			getNumber = function(match) {
1151				var isDoubled = lookAhead(match),
1152					size = (match === "@" ? 14 : (match === "!" ? 20 :
1153					(match === "y" && isDoubled ? 4 : (match === "o" ? 3 : 2)))),
1154					minSize = (match === "y" ? size : 1),
1155					digits = new RegExp("^\\d{" + minSize + "," + size + "}"),
1156					num = value.substring(iValue).match(digits);
1157				if (!num) {
1158					throw "Missing number at position " + iValue;
1159				}
1160				iValue += num[0].length;
1161				return parseInt(num[0], 10);
1162			},
1163			// Extract a name from the string value and convert to an index
1164			getName = function(match, shortNames, longNames) {
1165				var index = -1,
1166					names = $.map(lookAhead(match) ? longNames : shortNames, function (v, k) {
1167						return [ [k, v] ];
1168					}).sort(function (a, b) {
1169						return -(a[1].length - b[1].length);
1170					});
1171
1172				$.each(names, function (i, pair) {
1173					var name = pair[1];
1174					if (value.substr(iValue, name.length).toLowerCase() === name.toLowerCase()) {
1175						index = pair[0];
1176						iValue += name.length;
1177						return false;
1178					}
1179				});
1180				if (index !== -1) {
1181					return index + 1;
1182				} else {
1183					throw "Unknown name at position " + iValue;
1184				}
1185			},
1186			// Confirm that a literal character matches the string value
1187			checkLiteral = function() {
1188				if (value.charAt(iValue) !== format.charAt(iFormat)) {
1189					throw "Unexpected literal at position " + iValue;
1190				}
1191				iValue++;
1192			};
1193
1194		for (iFormat = 0; iFormat < format.length; iFormat++) {
1195			if (literal) {
1196				if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
1197					literal = false;
1198				} else {
1199					checkLiteral();
1200				}
1201			} else {
1202				switch (format.charAt(iFormat)) {
1203					case "d":
1204						day = getNumber("d");
1205						break;
1206					case "D":
1207						getName("D", dayNamesShort, dayNames);
1208						break;
1209					case "o":
1210						doy = getNumber("o");
1211						break;
1212					case "m":
1213						month = getNumber("m");
1214						break;
1215					case "M":
1216						month = getName("M", monthNamesShort, monthNames);
1217						break;
1218					case "y":
1219						year = getNumber("y");
1220						break;
1221					case "@":
1222						date = new Date(getNumber("@"));
1223						year = date.getFullYear();
1224						month = date.getMonth() + 1;
1225						day = date.getDate();
1226						break;
1227					case "!":
1228						date = new Date((getNumber("!") - this._ticksTo1970) / 10000);
1229						year = date.getFullYear();
1230						month = date.getMonth() + 1;
1231						day = date.getDate();
1232						break;
1233					case "'":
1234						if (lookAhead("'")){
1235							checkLiteral();
1236						} else {
1237							literal = true;
1238						}
1239						break;
1240					default:
1241						checkLiteral();
1242				}
1243			}
1244		}
1245
1246		if (iValue < value.length){
1247			extra = value.substr(iValue);
1248			if (!/^\s+/.test(extra)) {
1249				throw "Extra/unparsed characters found in date: " + extra;
1250			}
1251		}
1252
1253		if (year === -1) {
1254			year = new Date().getFullYear();
1255		} else if (year < 100) {
1256			year += new Date().getFullYear() - new Date().getFullYear() % 100 +
1257				(year <= shortYearCutoff ? 0 : -100);
1258		}
1259
1260		if (doy > -1) {
1261			month = 1;
1262			day = doy;
1263			do {
1264				dim = this._getDaysInMonth(year, month - 1);
1265				if (day <= dim) {
1266					break;
1267				}
1268				month++;
1269				day -= dim;
1270			} while (true);
1271		}
1272
1273		date = this._daylightSavingAdjust(new Date(year, month - 1, day));
1274		if (date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day) {
1275			throw "Invalid date"; // E.g. 31/02/00
1276		}
1277		return date;
1278	},
1279
1280	/* Standard date formats. */
1281	ATOM: "yy-mm-dd", // RFC 3339 (ISO 8601)
1282	COOKIE: "D, dd M yy",
1283	ISO_8601: "yy-mm-dd",
1284	RFC_822: "D, d M y",
1285	RFC_850: "DD, dd-M-y",
1286	RFC_1036: "D, d M y",
1287	RFC_1123: "D, d M yy",
1288	RFC_2822: "D, d M yy",
1289	RSS: "D, d M y", // RFC 822
1290	TICKS: "!",
1291	TIMESTAMP: "@",
1292	W3C: "yy-mm-dd", // ISO 8601
1293
1294	_ticksTo1970: (((1970 - 1) * 365 + Math.floor(1970 / 4) - Math.floor(1970 / 100) +
1295		Math.floor(1970 / 400)) * 24 * 60 * 60 * 10000000),
1296
1297	/* Format a date object into a string value.
1298	 * The format can be combinations of the following:
1299	 * d  - day of month (no leading zero)
1300	 * dd - day of month (two digit)
1301	 * o  - day of year (no leading zeros)
1302	 * oo - day of year (three digit)
1303	 * D  - day name short
1304	 * DD - day name long
1305	 * m  - month of year (no leading zero)
1306	 * mm - month of year (two digit)
1307	 * M  - month name short
1308	 * MM - month name long
1309	 * y  - year (two digit)
1310	 * yy - year (four digit)
1311	 * @ - Unix timestamp (ms since 01/01/1970)
1312	 * ! - Windows ticks (100ns since 01/01/0001)
1313	 * "..." - literal text
1314	 * '' - single quote
1315	 *
1316	 * @param  format string - the desired format of the date
1317	 * @param  date Date - the date value to format
1318	 * @param  settings Object - attributes include:
1319	 *					dayNamesShort	string[7] - abbreviated names of the days from Sunday (optional)
1320	 *					dayNames		string[7] - names of the days from Sunday (optional)
1321	 *					monthNamesShort string[12] - abbreviated names of the months (optional)
1322	 *					monthNames		string[12] - names of the months (optional)
1323	 * @return  string - the date in the above format
1324	 */
1325	formatDate: function (format, date, settings) {
1326		if (!date) {
1327			return "";
1328		}
1329
1330		var iFormat,
1331			dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort,
1332			dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames,
1333			monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort,
1334			monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames,
1335			// Check whether a format character is doubled
1336			lookAhead = function(match) {
1337				var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
1338				if (matches) {
1339					iFormat++;
1340				}
1341				return matches;
1342			},
1343			// Format a number, with leading zero if necessary
1344			formatNumber = function(match, value, len) {
1345				var num = "" + value;
1346				if (lookAhead(match)) {
1347					while (num.length < len) {
1348						num = "0" + num;
1349					}
1350				}
1351				return num;
1352			},
1353			// Format a name, short or long as requested
1354			formatName = function(match, value, shortNames, longNames) {
1355				return (lookAhead(match) ? longNames[value] : shortNames[value]);
1356			},
1357			output = "",
1358			literal = false;
1359
1360		if (date) {
1361			for (iFormat = 0; iFormat < format.length; iFormat++) {
1362				if (literal) {
1363					if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
1364						literal = false;
1365					} else {
1366						output += format.charAt(iFormat);
1367					}
1368				} else {
1369					switch (format.charAt(iFormat)) {
1370						case "d":
1371							output += formatNumber("d", date.getDate(), 2);
1372							break;
1373						case "D":
1374							output += formatName("D", date.getDay(), dayNamesShort, dayNames);
1375							break;
1376						case "o":
1377							output += formatNumber("o",
1378								Math.round((new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000), 3);
1379							break;
1380						case "m":
1381							output += formatNumber("m", date.getMonth() + 1, 2);
1382							break;
1383						case "M":
1384							output += formatName("M", date.getMonth(), monthNamesShort, monthNames);
1385							break;
1386						case "y":
1387							output += (lookAhead("y") ? date.getFullYear() :
1388								(date.getYear() % 100 < 10 ? "0" : "") + date.getYear() % 100);
1389							break;
1390						case "@":
1391							output += date.getTime();
1392							break;
1393						case "!":
1394							output += date.getTime() * 10000 + this._ticksTo1970;
1395							break;
1396						case "'":
1397							if (lookAhead("'")) {
1398								output += "'";
1399							} else {
1400								literal = true;
1401							}
1402							break;
1403						default:
1404							output += format.charAt(iFormat);
1405					}
1406				}
1407			}
1408		}
1409		return output;
1410	},
1411
1412	/* Extract all possible characters from the date format. */
1413	_possibleChars: function (format) {
1414		var iFormat,
1415			chars = "",
1416			literal = false,
1417			// Check whether a format character is doubled
1418			lookAhead = function(match) {
1419				var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
1420				if (matches) {
1421					iFormat++;
1422				}
1423				return matches;
1424			};
1425
1426		for (iFormat = 0; iFormat < format.length; iFormat++) {
1427			if (literal) {
1428				if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
1429					literal = false;
1430				} else {
1431					chars += format.charAt(iFormat);
1432				}
1433			} else {
1434				switch (format.charAt(iFormat)) {
1435					case "d": case "m": case "y": case "@":
1436						chars += "0123456789";
1437						break;
1438					case "D": case "M":
1439						return null; // Accept anything
1440					case "'":
1441						if (lookAhead("'")) {
1442							chars += "'";
1443						} else {
1444							literal = true;
1445						}
1446						break;
1447					default:
1448						chars += format.charAt(iFormat);
1449				}
1450			}
1451		}
1452		return chars;
1453	},
1454
1455	/* Get a setting value, defaulting if necessary. */
1456	_get: function(inst, name) {
1457		return inst.settings[name] !== undefined ?
1458			inst.settings[name] : this._defaults[name];
1459	},
1460
1461	/* Parse existing date and initialise date picker. */
1462	_setDateFromField: function(inst, noDefault) {
1463		if (inst.input.val() === inst.lastVal) {
1464			return;
1465		}
1466
1467		var dateFormat = this._get(inst, "dateFormat"),
1468			dates = inst.lastVal = inst.input ? inst.input.val() : null,
1469			defaultDate = this._getDefaultDate(inst),
1470			date = defaultDate,
1471			settings = this._getFormatConfig(inst);
1472
1473		try {
1474			date = this.parseDate(dateFormat, dates, settings) || defaultDate;
1475		} catch (event) {
1476			dates = (noDefault ? "" : dates);
1477		}
1478		inst.selectedDay = date.getDate();
1479		inst.drawMonth = inst.selectedMonth = date.getMonth();
1480		inst.drawYear = inst.selectedYear = date.getFullYear();
1481		inst.currentDay = (dates ? date.getDate() : 0);
1482		inst.currentMonth = (dates ? date.getMonth() : 0);
1483		inst.currentYear = (dates ? date.getFullYear() : 0);
1484		this._adjustInstDate(inst);
1485	},
1486
1487	/* Retrieve the default date shown on opening. */
1488	_getDefaultDate: function(inst) {
1489		return this._restrictMinMax(inst,
1490			this._determineDate(inst, this._get(inst, "defaultDate"), new Date()));
1491	},
1492
1493	/* A date may be specified as an exact value or a relative one. */
1494	_determineDate: function(inst, date, defaultDate) {
1495		var offsetNumeric = function(offset) {
1496				var date = new Date();
1497				date.setDate(date.getDate() + offset);
1498				return date;
1499			},
1500			offsetString = function(offset) {
1501				try {
1502					return $.datepicker.parseDate($.datepicker._get(inst, "dateFormat"),
1503						offset, $.datepicker._getFormatConfig(inst));
1504				}
1505				catch (e) {
1506					// Ignore
1507				}
1508
1509				var date = (offset.toLowerCase().match(/^c/) ?
1510					$.datepicker._getDate(inst) : null) || new Date(),
1511					year = date.getFullYear(),
1512					month = date.getMonth(),
1513					day = date.getDate(),
1514					pattern = /([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,
1515					matches = pattern.exec(offset);
1516
1517				while (matches) {
1518					switch (matches[2] || "d") {
1519						case "d" : case "D" :
1520							day += parseInt(matches[1],10); break;
1521						case "w" : case "W" :
1522							day += parseInt(matches[1],10) * 7; break;
1523						case "m" : case "M" :
1524							month += parseInt(matches[1],10);
1525							day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
1526							break;
1527						case "y": case "Y" :
1528							year += parseInt(matches[1],10);
1529							day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
1530							break;
1531					}
1532					matches = pattern.exec(offset);
1533				}
1534				return new Date(year, month, day);
1535			},
1536			newDate = (date == null || date === "" ? defaultDate : (typeof date === "string" ? offsetString(date) :
1537				(typeof date === "number" ? (isNaN(date) ? defaultDate : offsetNumeric(date)) : new Date(date.getTime()))));
1538
1539		newDate = (newDate && newDate.toString() === "Invalid Date" ? defaultDate : newDate);
1540		if (newDate) {
1541			newDate.setHours(0);
1542			newDate.setMinutes(0);
1543			newDate.setSeconds(0);
1544			newDate.setMilliseconds(0);
1545		}
1546		return this._daylightSavingAdjust(newDate);
1547	},
1548
1549	/* Handle switch to/from daylight saving.
1550	 * Hours may be non-zero on daylight saving cut-over:
1551	 * > 12 when midnight changeover, but then cannot generate
1552	 * midnight datetime, so jump to 1AM, otherwise reset.
1553	 * @param  date  (Date) the date to check
1554	 * @return  (Date) the corrected date
1555	 */
1556	_daylightSavingAdjust: function(date) {
1557		if (!date) {
1558			return null;
1559		}
1560		date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0);
1561		return date;
1562	},
1563
1564	/* Set the date(s) directly. */
1565	_setDate: function(inst, date, noChange) {
1566		var clear = !date,
1567			origMonth = inst.selectedMonth,
1568			origYear = inst.selectedYear,
1569			newDate = this._restrictMinMax(inst, this._determineDate(inst, date, new Date()));
1570
1571		inst.selectedDay = inst.currentDay = newDate.getDate();
1572		inst.drawMonth = inst.selectedMonth = inst.currentMonth = newDate.getMonth();
1573		inst.drawYear = inst.selectedYear = inst.currentYear = newDate.getFullYear();
1574		if ((origMonth !== inst.selectedMonth || origYear !== inst.selectedYear) && !noChange) {
1575			this._notifyChange(inst);
1576		}
1577		this._adjustInstDate(inst);
1578		if (inst.input) {
1579			inst.input.val(clear ? "" : this._formatDate(inst));
1580		}
1581	},
1582
1583	/* Retrieve the date(s) directly. */
1584	_getDate: function(inst) {
1585		var startDate = (!inst.currentYear || (inst.input && inst.input.val() === "") ? null :
1586			this._daylightSavingAdjust(new Date(
1587			inst.currentYear, inst.currentMonth, inst.currentDay)));
1588			return startDate;
1589	},
1590
1591	/* Attach the onxxx handlers.  These are declared statically so
1592	 * they work with static code transformers like Caja.
1593	 */
1594	_attachHandlers: function(inst) {
1595		var stepMonths = this._get(inst, "stepMonths"),
1596			id = "#" + inst.id.replace( /\\\\/g, "\\" );
1597		inst.dpDiv.find("[data-handler]").map(function () {
1598			var handler = {
1599				prev: function () {
1600					$.datepicker._adjustDate(id, -stepMonths, "M");
1601				},
1602				next: function () {
1603					$.datepicker._adjustDate(id, +stepMonths, "M");
1604				},
1605				hide: function () {
1606					$.datepicker._hideDatepicker();
1607				},
1608				today: function () {
1609					$.datepicker._gotoToday(id);
1610				},
1611				selectDay: function () {
1612					$.datepicker._selectDay(id, +this.getAttribute("data-month"), +this.getAttribute("data-year"), this);
1613					return false;
1614				},
1615				selectMonth: function () {
1616					$.datepicker._selectMonthYear(id, this, "M");
1617					return false;
1618				},
1619				selectYear: function () {
1620					$.datepicker._selectMonthYear(id, this, "Y");
1621					return false;
1622				}
1623			};
1624			$(this).bind(this.getAttribute("data-event"), handler[this.getAttribute("data-handler")]);
1625		});
1626	},
1627
1628	/* Generate the HTML for the current state of the date picker. */
1629	_generateHTML: function(inst) {
1630		var maxDraw, prevText, prev, nextText, next, currentText, gotoDate,
1631			controls, buttonPanel, firstDay, showWeek, dayNames, dayNamesMin,
1632			monthNames, monthNamesShort, beforeShowDay, showOtherMonths,
1633			selectOtherMonths, defaultDate, html, dow, row, group, col, selectedDate,
1634			cornerClass, calender, thead, day, daysInMonth, leadDays, curRows, numRows,
1635			printDate, dRow, tbody, daySettings, otherMonth, unselectable,
1636			tempDate = new Date(),
1637			today = this._daylightSavingAdjust(
1638				new Date(tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate())), // clear time
1639			isRTL = this._get(inst, "isRTL"),
1640			showButtonPanel = this._get(inst, "showButtonPanel"),
1641			hideIfNoPrevNext = this._get(inst, "hideIfNoPrevNext"),
1642			navigationAsDateFormat = this._get(inst, "navigationAsDateFormat"),
1643			numMonths = this._getNumberOfMonths(inst),
1644			showCurrentAtPos = this._get(inst, "showCurrentAtPos"),
1645			stepMonths = this._get(inst, "stepMonths"),
1646			isMultiMonth = (numMonths[0] !== 1 || numMonths[1] !== 1),
1647			currentDate = this._daylightSavingAdjust((!inst.currentDay ? new Date(9999, 9, 9) :
1648				new Date(inst.currentYear, inst.currentMonth, inst.currentDay))),
1649			minDate = this._getMinMaxDate(inst, "min"),
1650			maxDate = this._getMinMaxDate(inst, "max"),
1651			drawMonth = inst.drawMonth - showCurrentAtPos,
1652			drawYear = inst.drawYear;
1653
1654		if (drawMonth < 0) {
1655			drawMonth += 12;
1656			drawYear--;
1657		}
1658		if (maxDate) {
1659			maxDraw = this._daylightSavingAdjust(new Date(maxDate.getFullYear(),
1660				maxDate.getMonth() - (numMonths[0] * numMonths[1]) + 1, maxDate.getDate()));
1661			maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw);
1662			while (this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1)) > maxDraw) {
1663				drawMonth--;
1664				if (drawMonth < 0) {
1665					drawMonth = 11;
1666					drawYear--;
1667				}
1668			}
1669		}
1670		inst.drawMonth = drawMonth;
1671		inst.drawYear = drawYear;
1672
1673		prevText = this._get(inst, "prevText");
1674		prevText = (!navigationAsDateFormat ? prevText : this.formatDate(prevText,
1675			this._daylightSavingAdjust(new Date(drawYear, drawMonth - stepMonths, 1)),
1676			this._getFormatConfig(inst)));
1677
1678		prev = (this._canAdjustMonth(inst, -1, drawYear, drawMonth) ?
1679			"<a class='ui-datepicker-prev ui-corner-all' data-handler='prev' data-event='click'" +
1680			" title='" + prevText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "e" : "w") + "'>" + prevText + "</span></a>" :
1681			(hideIfNoPrevNext ? "" : "<a class='ui-datepicker-prev ui-corner-all ui-state-disabled' title='"+ prevText +"'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "e" : "w") + "'>" + prevText + "</span></a>"));
1682
1683		nextText = this._get(inst, "nextText");
1684		nextText = (!navigationAsDateFormat ? nextText : this.formatDate(nextText,
1685			this._daylightSavingAdjust(new Date(drawYear, drawMonth + stepMonths, 1)),
1686			this._getFormatConfig(inst)));
1687
1688		next = (this._canAdjustMonth(inst, +1, drawYear, drawMonth) ?
1689			"<a class='ui-datepicker-next ui-corner-all' data-handler='next' data-event='click'" +
1690			" title='" + nextText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "w" : "e") + "'>" + nextText + "</span></a>" :
1691			(hideIfNoPrevNext ? "" : "<a class='ui-datepicker-next ui-corner-all ui-state-disabled' title='"+ nextText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "w" : "e") + "'>" + nextText + "</span></a>"));
1692
1693		currentText = this._get(inst, "currentText");
1694		gotoDate = (this._get(inst, "gotoCurrent") && inst.currentDay ? currentDate : today);
1695		currentText = (!navigationAsDateFormat ? currentText :
1696			this.formatDate(currentText, gotoDate, this._getFormatConfig(inst)));
1697
1698		controls = (!inst.inline ? "<button type='button' class='ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all' data-handler='hide' data-event='click'>" +
1699			this._get(inst, "closeText") + "</button>" : "");
1700
1701		buttonPanel = (showButtonPanel) ? "<div class='ui-datepicker-buttonpane ui-widget-content'>" + (isRTL ? controls : "") +
1702			(this._isInRange(inst, gotoDate) ? "<button type='button' class='ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all' data-handler='today' data-event='click'" +
1703			">" + currentText + "</button>" : "") + (isRTL ? "" : controls) + "</div>" : "";
1704
1705		firstDay = parseInt(this._get(inst, "firstDay"),10);
1706		firstDay = (isNaN(firstDay) ? 0 : firstDay);
1707
1708		showWeek = this._get(inst, "showWeek");
1709		dayNames = this._get(inst, "dayNames");
1710		dayNamesMin = this._get(inst, "dayNamesMin");
1711		monthNames = this._get(inst, "monthNames");
1712		monthNamesShort = this._get(inst, "monthNamesShort");
1713		beforeShowDay = this._get(inst, "beforeShowDay");
1714		showOtherMonths = this._get(inst, "showOtherMonths");
1715		selectOtherMonths = this._get(inst, "selectOtherMonths");
1716		defaultDate = this._getDefaultDate(inst);
1717		html = "";
1718		dow;
1719		for (row = 0; row < numMonths[0]; row++) {
1720			group = "";
1721			this.maxRows = 4;
1722			for (col = 0; col < numMonths[1]; col++) {
1723				selectedDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, inst.selectedDay));
1724				cornerClass = " ui-corner-all";
1725				calender = "";
1726				if (isMultiMonth) {
1727					calender += "<div class='ui-datepicker-group";
1728					if (numMonths[1] > 1) {
1729						switch (col) {
1730							case 0: calender += " ui-datepicker-group-first";
1731								cornerClass = " ui-corner-" + (isRTL ? "right" : "left"); break;
1732							case numMonths[1]-1: calender += " ui-datepicker-group-last";
1733								cornerClass = " ui-corner-" + (isRTL ? "left" : "right"); break;
1734							default: calender += " ui-datepicker-group-middle"; cornerClass = ""; break;
1735						}
1736					}
1737					calender += "'>";
1738				}
1739				calender += "<div class='ui-datepicker-header ui-widget-header ui-helper-clearfix" + cornerClass + "'>" +
1740					(/all|left/.test(cornerClass) && row === 0 ? (isRTL ? next : prev) : "") +
1741					(/all|right/.test(cornerClass) && row === 0 ? (isRTL ? prev : next) : "") +
1742					this._generateMonthYearHeader(inst, drawMonth, drawYear, minDate, maxDate,
1743					row > 0 || col > 0, monthNames, monthNamesShort) + // draw month headers
1744					"</div><table class='ui-datepicker-calendar'><thead>" +
1745					"<tr>";
1746				thead = (showWeek ? "<th class='ui-datepicker-week-col'>" + this._get(inst, "weekHeader") + "</th>" : "");
1747				for (dow = 0; dow < 7; dow++) { // days of the week
1748					day = (dow + firstDay) % 7;
1749					thead += "<th scope='col'" + ((dow + firstDay + 6) % 7 >= 5 ? " class='ui-datepicker-week-end'" : "") + ">" +
1750						"<span title='" + dayNames[day] + "'>" + dayNamesMin[day] + "</span></th>";
1751				}
1752				calender += thead + "</tr></thead><tbody>";
1753				daysInMonth = this._getDaysInMonth(drawYear, drawMonth);
1754				if (drawYear === inst.selectedYear && drawMonth === inst.selectedMonth) {
1755					inst.selectedDay = Math.min(inst.selectedDay, daysInMonth);
1756				}
1757				leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7;
1758				curRows = Math.ceil((leadDays + daysInMonth) / 7); // calculate the number of rows to generate
1759				numRows = (isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows); //If multiple months, use the higher number of rows (see #7043)
1760				this.maxRows = numRows;
1761				printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays));
1762				for (dRow = 0; dRow < numRows; dRow++) { // create date picker rows
1763					calender += "<tr>";
1764					tbody = (!showWeek ? "" : "<td class='ui-datepicker-week-col'>" +
1765						this._get(inst, "calculateWeek")(printDate) + "</td>");
1766					for (dow = 0; dow < 7; dow++) { // create date picker days
1767						daySettings = (beforeShowDay ?
1768							beforeShowDay.apply((inst.input ? inst.input[0] : null), [printDate]) : [true, ""]);
1769						otherMonth = (printDate.getMonth() !== drawMonth);
1770						unselectable = (otherMonth && !selectOtherMonths) || !daySettings[0] ||
1771							(minDate && printDate < minDate) || (maxDate && printDate > maxDate);
1772						tbody += "<td class='" +
1773							((dow + firstDay + 6) % 7 >= 5 ? " ui-datepicker-week-end" : "") + // highlight weekends
1774							(otherMonth ? " ui-datepicker-other-month" : "") + // highlight days from other months
1775							((printDate.getTime() === selectedDate.getTime() && drawMonth === inst.selectedMonth && inst._keyEvent) || // user pressed key
1776							(defaultDate.getTime() === printDate.getTime() && defaultDate.getTime() === selectedDate.getTime()) ?
1777							// or defaultDate is current printedDate and defaultDate is selectedDate
1778							" " + this._dayOverClass : "") + // highlight selected day
1779							(unselectable ? " " + this._unselectableClass + " ui-state-disabled": "") +  // highlight unselectable days
1780							(otherMonth && !showOtherMonths ? "" : " " + daySettings[1] + // highlight custom dates
1781							(printDate.getTime() === currentDate.getTime() ? " " + this._currentClass : "") + // highlight selected day
1782							(printDate.getTime() === today.getTime() ? " ui-datepicker-today" : "")) + "'" + // highlight today (if different)
1783							((!otherMonth || showOtherMonths) && daySettings[2] ? " title='" + daySettings[2].replace(/'/g, "&#39;") + "'" : "") + // cell title
1784							(unselectable ? "" : " data-handler='selectDay' data-event='click' data-month='" + printDate.getMonth() + "' data-year='" + printDate.getFullYear() + "'") + ">" + // actions
1785							(otherMonth && !showOtherMonths ? "&#xa0;" : // display for other months
1786							(unselectable ? "<span class='ui-state-default'>" + printDate.getDate() + "</span>" : "<a class='ui-state-default" +
1787							(printDate.getTime() === today.getTime() ? " ui-state-highlight" : "") +
1788							(printDate.getTime() === currentDate.getTime() ? " ui-state-active" : "") + // highlight selected day
1789							(otherMonth ? " ui-priority-secondary" : "") + // distinguish dates from other months
1790							"' href='#'>" + printDate.getDate() + "</a>")) + "</td>"; // display selectable date
1791						printDate.setDate(printDate.getDate() + 1);
1792						printDate = this._daylightSavingAdjust(printDate);
1793					}
1794					calender += tbody + "</tr>";
1795				}
1796				drawMonth++;
1797				if (drawMonth > 11) {
1798					drawMonth = 0;
1799					drawYear++;
1800				}
1801				calender += "</tbody></table>" + (isMultiMonth ? "</div>" +
1802							((numMonths[0] > 0 && col === numMonths[1]-1) ? "<div class='ui-datepicker-row-break'></div>" : "") : "");
1803				group += calender;
1804			}
1805			html += group;
1806		}
1807		html += buttonPanel;
1808		inst._keyEvent = false;
1809		return html;
1810	},
1811
1812	/* Generate the month and year header. */
1813	_generateMonthYearHeader: function(inst, drawMonth, drawYear, minDate, maxDate,
1814			secondary, monthNames, monthNamesShort) {
1815
1816		var inMinYear, inMaxYear, month, years, thisYear, determineYear, year, endYear,
1817			changeMonth = this._get(inst, "changeMonth"),
1818			changeYear = this._get(inst, "changeYear"),
1819			showMonthAfterYear = this._get(inst, "showMonthAfterYear"),
1820			html = "<div class='ui-datepicker-title'>",
1821			monthHtml = "";
1822
1823		// month selection
1824		if (secondary || !changeMonth) {
1825			monthHtml += "<span class='ui-datepicker-month'>" + monthNames[drawMonth] + "</span>";
1826		} else {
1827			inMinYear = (minDate && minDate.getFullYear() === drawYear);
1828			inMaxYear = (maxDate && maxDate.getFullYear() === drawYear);
1829			monthHtml += "<select class='ui-datepicker-month' data-handler='selectMonth' data-event='change'>";
1830			for ( month = 0; month < 12; month++) {
1831				if ((!inMinYear || month >= minDate.getMonth()) && (!inMaxYear || month <= maxDate.getMonth())) {
1832					monthHtml += "<option value='" + month + "'" +
1833						(month === drawMonth ? " selected='selected'" : "") +
1834						">" + monthNamesShort[month] + "</option>";
1835				}
1836			}
1837			monthHtml += "</select>";
1838		}
1839
1840		if (!showMonthAfterYear) {
1841			html += monthHtml + (secondary || !(changeMonth && changeYear) ? "&#xa0;" : "");
1842		}
1843
1844		// year selection
1845		if ( !inst.yearshtml ) {
1846			inst.yearshtml = "";
1847			if (secondary || !changeYear) {
1848				html += "<span class='ui-datepicker-year'>" + drawYear + "</span>";
1849			} else {
1850				// determine range of years to display
1851				years = this._get(inst, "yearRange").split(":");
1852				thisYear = new Date().getFullYear();
1853				determineYear = function(value) {
1854					var year = (value.match(/c[+\-].*/) ? drawYear + parseInt(value.substring(1), 10) :
1855						(value.match(/[+\-].*/) ? thisYear + parseInt(value, 10) :
1856						parseInt(value, 10)));
1857					return (isNaN(year) ? thisYear : year);
1858				};
1859				year = determineYear(years[0]);
1860				endYear = Math.max(year, determineYear(years[1] || ""));
1861				year = (minDate ? Math.max(year, minDate.getFullYear()) : year);
1862				endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear);
1863				inst.yearshtml += "<select class='ui-datepicker-year' data-handler='selectYear' data-event='change'>";
1864				for (; year <= endYear; year++) {
1865					inst.yearshtml += "<option value='" + year + "'" +
1866						(year === drawYear ? " selected='selected'" : "") +
1867						">" + year + "</option>";
1868				}
1869				inst.yearshtml += "</select>";
1870
1871				html += inst.yearshtml;
1872				inst.yearshtml = null;
1873			}
1874		}
1875
1876		html += this._get(inst, "yearSuffix");
1877		if (showMonthAfterYear) {
1878			html += (secondary || !(changeMonth && changeYear) ? "&#xa0;" : "") + monthHtml;
1879		}
1880		html += "</div>"; // Close datepicker_header
1881		return html;
1882	},
1883
1884	/* Adjust one of the date sub-fields. */
1885	_adjustInstDate: function(inst, offset, period) {
1886		var year = inst.drawYear + (period === "Y" ? offset : 0),
1887			month = inst.drawMonth + (period === "M" ? offset : 0),
1888			day = Math.min(inst.selectedDay, this._getDaysInMonth(year, month)) + (period === "D" ? offset : 0),
1889			date = this._restrictMinMax(inst, this._daylightSavingAdjust(new Date(year, month, day)));
1890
1891		inst.selectedDay = date.getDate();
1892		inst.drawMonth = inst.selectedMonth = date.getMonth();
1893		inst.drawYear = inst.selectedYear = date.getFullYear();
1894		if (period === "M" || period === "Y") {
1895			this._notifyChange(inst);
1896		}
1897	},
1898
1899	/* Ensure a date is within any min/max bounds. */
1900	_restrictMinMax: function(inst, date) {
1901		var minDate = this._getMinMaxDate(inst, "min"),
1902			maxDate = this._getMinMaxDate(inst, "max"),
1903			newDate = (minDate && date < minDate ? minDate : date);
1904		return (maxDate && newDate > maxDate ? maxDate : newDate);
1905	},
1906
1907	/* Notify change of month/year. */
1908	_notifyChange: function(inst) {
1909		var onChange = this._get(inst, "onChangeMonthYear");
1910		if (onChange) {
1911			onChange.apply((inst.input ? inst.input[0] : null),
1912				[inst.selectedYear, inst.selectedMonth + 1, inst]);
1913		}
1914	},
1915
1916	/* Determine the number of months to show. */
1917	_getNumberOfMonths: function(inst) {
1918		var numMonths = this._get(inst, "numberOfMonths");
1919		return (numMonths == null ? [1, 1] : (typeof numMonths === "number" ? [1, numMonths] : numMonths));
1920	},
1921
1922	/* Determine the current maximum date - ensure no time components are set. */
1923	_getMinMaxDate: function(inst, minMax) {
1924		return this._determineDate(inst, this._get(inst, minMax + "Date"), null);
1925	},
1926
1927	/* Find the number of days in a given month. */
1928	_getDaysInMonth: function(year, month) {
1929		return 32 - this._daylightSavingAdjust(new Date(year, month, 32)).getDate();
1930	},
1931
1932	/* Find the day of the week of the first of a month. */
1933	_getFirstDayOfMonth: function(year, month) {
1934		return new Date(year, month, 1).getDay();
1935	},
1936
1937	/* Determines if we should allow a "next/prev" month display change. */
1938	_canAdjustMonth: function(inst, offset, curYear, curMonth) {
1939		var numMonths = this._getNumberOfMonths(inst),
1940			date = this._daylightSavingAdjust(new Date(curYear,
1941			curMonth + (offset < 0 ? offset : numMonths[0] * numMonths[1]), 1));
1942
1943		if (offset < 0) {
1944			date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth()));
1945		}
1946		return this._isInRange(inst, date);
1947	},
1948
1949	/* Is the given date in the accepted range? */
1950	_isInRange: function(inst, date) {
1951		var yearSplit, currentYear,
1952			minDate = this._getMinMaxDate(inst, "min"),
1953			maxDate = this._getMinMaxDate(inst, "max"),
1954			minYear = null,
1955			maxYear = null,
1956			years = this._get(inst, "yearRange");
1957			if (years){
1958				yearSplit = years.split(":");
1959				currentYear = new Date().getFullYear();
1960				minYear = parseInt(yearSplit[0], 10);
1961				maxYear = parseInt(yearSplit[1], 10);
1962				if ( yearSplit[0].match(/[+\-].*/) ) {
1963					minYear += currentYear;
1964				}
1965				if ( yearSplit[1].match(/[+\-].*/) ) {
1966					maxYear += currentYear;
1967				}
1968			}
1969
1970		return ((!minDate || date.getTime() >= minDate.getTime()) &&
1971			(!maxDate || date.getTime() <= maxDate.getTime()) &&
1972			(!minYear || date.getFullYear() >= minYear) &&
1973			(!maxYear || date.getFullYear() <= maxYear));
1974	},
1975
1976	/* Provide the configuration settings for formatting/parsing. */
1977	_getFormatConfig: function(inst) {
1978		var shortYearCutoff = this._get(inst, "shortYearCutoff");
1979		shortYearCutoff = (typeof shortYearCutoff !== "string" ? shortYearCutoff :
1980			new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
1981		return {shortYearCutoff: shortYearCutoff,
1982			dayNamesShort: this._get(inst, "dayNamesShort"), dayNames: this._get(inst, "dayNames"),
1983			monthNamesShort: this._get(inst, "monthNamesShort"), monthNames: this._get(inst, "monthNames")};
1984	},
1985
1986	/* Format the given date for display. */
1987	_formatDate: function(inst, day, month, year) {
1988		if (!day) {
1989			inst.currentDay = inst.selectedDay;
1990			inst.currentMonth = inst.selectedMonth;
1991			inst.currentYear = inst.selectedYear;
1992		}
1993		var date = (day ? (typeof day === "object" ? day :
1994			this._daylightSavingAdjust(new Date(year, month, day))) :
1995			this._daylightSavingAdjust(new Date(inst.currentYear, inst.currentMonth, inst.currentDay)));
1996		return this.formatDate(this._get(inst, "dateFormat"), date, this._getFormatConfig(inst));
1997	}
1998});
1999
2000/*
2001 * Bind hover events for datepicker elements.
2002 * Done via delegate so the binding only occurs once in the lifetime of the parent div.
2003 * Global datepicker_instActive, set by _updateDatepicker allows the handlers to find their way back to the active picker.
2004 */
2005function datepicker_bindHover(dpDiv) {
2006	var selector = "button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";
2007	return dpDiv.delegate(selector, "mouseout", function() {
2008			$(this).removeClass("ui-state-hover");
2009			if (this.className.indexOf("ui-datepicker-prev") !== -1) {
2010				$(this).removeClass("ui-datepicker-prev-hover");
2011			}
2012			if (this.className.indexOf("ui-datepicker-next") !== -1) {
2013				$(this).removeClass("ui-datepicker-next-hover");
2014			}
2015		})
2016		.delegate( selector, "mouseover", datepicker_handleMouseover );
2017}
2018
2019function datepicker_handleMouseover() {
2020	if (!$.datepicker._isDisabledDatepicker( datepicker_instActive.inline? datepicker_instActive.dpDiv.parent()[0] : datepicker_instActive.input[0])) {
2021		$(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover");
2022		$(this).addClass("ui-state-hover");
2023		if (this.className.indexOf("ui-datepicker-prev") !== -1) {
2024			$(this).addClass("ui-datepicker-prev-hover");
2025		}
2026		if (this.className.indexOf("ui-datepicker-next") !== -1) {
2027			$(this).addClass("ui-datepicker-next-hover");
2028		}
2029	}
2030}
2031
2032/* jQuery extend now ignores nulls! */
2033function datepicker_extendRemove(target, props) {
2034	$.extend(target, props);
2035	for (var name in props) {
2036		if (props[name] == null) {
2037			target[name] = props[name];
2038		}
2039	}
2040	return target;
2041}
2042
2043/* Invoke the datepicker functionality.
2044   @param  options  string - a command, optionally followed by additional parameters or
2045					Object - settings for attaching new datepicker functionality
2046   @return  jQuery object */
2047$.fn.datepicker = function(options){
2048
2049	/* Verify an empty collection wasn't passed - Fixes #6976 */
2050	if ( !this.length ) {
2051		return this;
2052	}
2053
2054	/* Initialise the date picker. */
2055	if (!$.datepicker.initialized) {
2056		$(document).mousedown($.datepicker._checkExternalClick);
2057		$.datepicker.initialized = true;
2058	}
2059
2060	/* Append datepicker main container to body if not exist. */
2061	if ($("#"+$.datepicker._mainDivId).length === 0) {
2062		$("body").append($.datepicker.dpDiv);
2063	}
2064
2065	var otherArgs = Array.prototype.slice.call(arguments, 1);
2066	if (typeof options === "string" && (options === "isDisabled" || options === "getDate" || options === "widget")) {
2067		return $.datepicker["_" + options + "Datepicker"].
2068			apply($.datepicker, [this[0]].concat(otherArgs));
2069	}
2070	if (options === "option" && arguments.length === 2 && typeof arguments[1] === "string") {
2071		return $.datepicker["_" + options + "Datepicker"].
2072			apply($.datepicker, [this[0]].concat(otherArgs));
2073	}
2074	return this.each(function() {
2075		typeof options === "string" ?
2076			$.datepicker["_" + options + "Datepicker"].
2077				apply($.datepicker, [this].concat(otherArgs)) :
2078			$.datepicker._attachDatepicker(this, options);
2079	});
2080};
2081
2082$.datepicker = new Datepicker(); // singleton instance
2083$.datepicker.initialized = false;
2084$.datepicker.uuid = new Date().getTime();
2085$.datepicker.version = "1.11.4";
2086
2087return $.datepicker;
2088
2089}));
2090
2091/*!
2092 * Modernizr v2.8.3
2093 * www.modernizr.com
2094 *
2095 * Copyright (c) Faruk Ates, Paul Irish, Alex Sexton
2096 * Available under the BSD and MIT licenses: www.modernizr.com/license/
2097 */
2098
2099/*
2100 * Modernizr tests which native CSS3 and HTML5 features are available in
2101 * the current UA and makes the results available to you in two ways:
2102 * as properties on a global Modernizr object, and as classes on the
2103 * <html> element. This information allows you to progressively enhance
2104 * your pages with a granular level of control over the experience.
2105 *
2106 * Modernizr has an optional (not included) conditional resource loader
2107 * called Modernizr.load(), based on Yepnope.js (yepnopejs.com).
2108 * To get a build that includes Modernizr.load(), as well as choosing
2109 * which tests to include, go to www.modernizr.com/download/
2110 *
2111 * Authors        Faruk Ates, Paul Irish, Alex Sexton
2112 * Contributors   Ryan Seddon, Ben Alman
2113 */
2114
2115window.Modernizr = (function( window, document, undefined ) {
2116
2117    var version = '2.8.3',
2118
2119    Modernizr = {},
2120
2121    /*>>cssclasses*/
2122    // option for enabling the HTML classes to be added
2123    enableClasses = true,
2124    /*>>cssclasses*/
2125
2126    docElement = document.documentElement,
2127
2128    /**
2129     * Create our "modernizr" element that we do most feature tests on.
2130     */
2131    mod = 'modernizr',
2132    modElem = document.createElement(mod),
2133    mStyle = modElem.style,
2134
2135    /**
2136     * Create the input element for various Web Forms feature tests.
2137     */
2138    inputElem /*>>inputelem*/ = document.createElement('input') /*>>inputelem*/ ,
2139
2140    /*>>smile*/
2141    smile = ':)',
2142    /*>>smile*/
2143
2144    toString = {}.toString,
2145
2146    // TODO :: make the prefixes more granular
2147    /*>>prefixes*/
2148    // List of property values to set for css tests. See ticket #21
2149    prefixes = ' -webkit- -moz- -o- -ms- '.split(' '),
2150    /*>>prefixes*/
2151
2152    /*>>domprefixes*/
2153    // Following spec is to expose vendor-specific style properties as:
2154    //   elem.style.WebkitBorderRadius
2155    // and the following would be incorrect:
2156    //   elem.style.webkitBorderRadius
2157
2158    // Webkit ghosts their properties in lowercase but Opera & Moz do not.
2159    // Microsoft uses a lowercase `ms` instead of the correct `Ms` in IE8+
2160    //   erik.eae.net/archives/2008/03/10/21.48.10/
2161
2162    // More here: github.com/Modernizr/Modernizr/issues/issue/21
2163    omPrefixes = 'Webkit Moz O ms',
2164
2165    cssomPrefixes = omPrefixes.split(' '),
2166
2167    domPrefixes = omPrefixes.toLowerCase().split(' '),
2168    /*>>domprefixes*/
2169
2170    /*>>ns*/
2171    ns = {'svg': 'http://www.w3.org/2000/svg'},
2172    /*>>ns*/
2173
2174    tests = {},
2175    inputs = {},
2176    attrs = {},
2177
2178    classes = [],
2179
2180    slice = classes.slice,
2181
2182    featureName, // used in testing loop
2183
2184
2185    /*>>teststyles*/
2186    // Inject element with style element and some CSS rules
2187    injectElementWithStyles = function( rule, callback, nodes, testnames ) {
2188
2189      var style, ret, node, docOverflow,
2190          div = document.createElement('div'),
2191          // After page load injecting a fake body doesn't work so check if body exists
2192          body = document.body,
2193          // IE6 and 7 won't return offsetWidth or offsetHeight unless it's in the body element, so we fake it.
2194          fakeBody = body || document.createElement('body');
2195
2196      if ( parseInt(nodes, 10) ) {
2197          // In order not to give false positives we create a node for each test
2198          // This also allows the method to scale for unspecified uses
2199          while ( nodes-- ) {
2200              node = document.createElement('div');
2201              node.id = testnames ? testnames[nodes] : mod + (nodes + 1);
2202              div.appendChild(node);
2203          }
2204      }
2205
2206      // <style> elements in IE6-9 are considered 'NoScope' elements and therefore will be removed
2207      // when injected with innerHTML. To get around this you need to prepend the 'NoScope' element
2208      // with a 'scoped' element, in our case the soft-hyphen entity as it won't mess with our measurements.
2209      // msdn.microsoft.com/en-us/library/ms533897%28VS.85%29.aspx
2210      // Documents served as xml will throw if using &shy; so use xml friendly encoded version. See issue #277
2211      style = ['&#173;','<style id="s', mod, '">', rule, '</style>'].join('');
2212      div.id = mod;
2213      // IE6 will false positive on some tests due to the style element inside the test div somehow interfer
2213ing offsetHeight, so insert it into body or fakebody.
2214      // Opera will act all quirky when injecting elements in documentElement when page is served as xml, needs fakebody too. #270
2215      (body ? div : fakeBody).innerHTML += style;
2216      fakeBody.appendChild(div);
2217      if ( !body ) {
2218          //avoid crashing IE8, if background image is used
2219          fakeBody.style.background = '';
2220          //Safari 5.13/5.1.4 OSX stops loading if ::-webkit-scrollbar is used and scrollbars are visible
2221          fakeBody.style.overflow = 'hidden';
2222          docOverflow = docElement.style.overflow;
2223          docElement.style.overflow = 'hidden';
2224          docElement.appendChild(fakeBody);
2225      }
2226
2227      ret = callback(div, rule);
2228      // If this is done after page load we don't want to remove the body so check if body exists
2229      if ( !body ) {
2230          fakeBody.parentNode.removeChild(fakeBody);
2231          docElement.style.overflow = docOverflow;
2232      } else {
2233          div.parentNode.removeChild(div);
2234      }
2235
2236      return !!ret;
2237
2238    },
2239    /*>>teststyles*/
2240
2241    /*>>mq*/
2242    // adapted from matchMedia polyfill
2243    // by Scott Jehl and Paul Irish
2244    // gist.github.com/786768
2245    testMediaQuery = function( mq ) {
2246
2247      var matchMedia = window.matchMedia || window.msMatchMedia;
2248      if ( matchMedia ) {
2249        return matchMedia(mq) && matchMedia(mq).matches || false;
2250      }
2251
2252      var bool;
2253
2254      injectElementWithStyles('@media ' + mq + ' { #' + mod + ' { position: absolute; } }', function( node ) {
2255        bool = (window.getComputedStyle ?
2256                  getComputedStyle(node, null) :
2257                  node.currentStyle)['position'] == 'absolute';
2258      });
2259
2260      return bool;
2261
2262     },
2263     /*>>mq*/
2264
2265
2266    /*>>hasevent*/
2267    //
2268    // isEventSupported determines if a given element supports the given event
2269    // kangax.github.com/iseventsupported/
2270    //
2271    // The following results are known incorrects:
2272    //   Modernizr.hasEvent("webkitTransitionEnd", elem) // false negative
2273    //   Modernizr.hasEvent("textInput") // in Webkit. github.com/Modernizr/Modernizr/issues/333
2274    //   ...
2275    isEventSupported = (function() {
2276
2277      var TAGNAMES = {
2278        'select': 'input', 'change': 'input',
2279        'submit': 'form', 'reset': 'form',
2280        'error': 'img', 'load': 'img', 'abort': 'img'
2281      };
2282
2283      function isEventSupported( eventName, element ) {
2284
2285        element = element || document.createElement(TAGNAMES[eventName] || 'div');
2286        eventName = 'on' + eventName;
2287
2288        // When using `setAttribute`, IE skips "unload", WebKit skips "unload" and "resize", whereas `in` "catches" those
2289        var isSupported = eventName in element;
2290
2291        if ( !isSupported ) {
2292          // If it has no `setAttribute` (i.e. doesn't implement Node interface), try generic element
2293          if ( !element.setAttribute ) {
2294            element = document.createElement('div');
2295          }
2296          if ( element.setAttribute && element.removeAttribute ) {
2297            element.setAttribute(eventName, '');
2298            isSupported = is(element[eventName], 'function');
2299
2300            // If property was created, "remove it" (by setting value to `undefined`)
2301            if ( !is(element[eventName], 'undefined') ) {
2302              element[eventName] = undefined;
2303            }
2304            element.removeAttribute(eventName);
2305          }
2306        }
2307
2308        element = null;
2309        return isSupported;
2310      }
2311      return isEventSupported;
2312    })(),
2313    /*>>hasevent*/
2314
2315    // TODO :: Add flag for hasownprop ? didn't last time
2316
2317    // hasOwnProperty shim by kangax needed for Safari 2.0 support
2318    _hasOwnProperty = ({}).hasOwnProperty, hasOwnProp;
2319
2320    if ( !is(_hasOwnProperty, 'undefined') && !is(_hasOwnProperty.call, 'undefined') ) {
2321      hasOwnProp = function (object, property) {
2322        return _hasOwnProperty.call(object, property);
2323      };
2324    }
2325    else {
2326      hasOwnProp = function (object, property) { /* yes, this can give false positives/negatives, but most of the time we don't care about those */
2327        return ((property in object) && is(object.constructor.prototype[property], 'undefined'));
2328      };
2329    }
2330
2331    // Adapted from ES5-shim https://github.com/kriskowal/es5-shim/blob/master/es5-shim.js
2332    // es5.github.com/#x15.3.4.5
2333
2334    if (!Function.prototype.bind) {
2335      Function.prototype.bind = function bind(that) {
2336
2337        var target = this;
2338
2339        if (typeof target != "function") {
2340            throw new TypeError();
2341        }
2342
2343        var args = slice.call(arguments, 1),
2344            bound = function () {
2345
2346            if (this instanceof bound) {
2347
2348              var F = function(){};
2349              F.prototype = target.prototype;
2350              var self = new F();
2351
2352              var result = target.apply(
2353                  self,
2354                  args.concat(slice.call(arguments))
2355              );
2356              if (Object(result) === result) {
2357                  return result;
2358              }
2359              return self;
2360
2361            } else {
2362
2363              return target.apply(
2364                  that,
2365                  args.concat(slice.call(arguments))
2366              );
2367
2368            }
2369
2370        };
2371
2372        return bound;
2373      };
2374    }
2375
2376    /**
2377     * setCss applies given styles to the Modernizr DOM node.
2378     */
2379    function setCss( str ) {
2380        mStyle.cssText = str;
2381    }
2382
2383    /**
2384     * setCssAll extrapolates all vendor-specific css strings.
2385     */
2386    function setCssAll( str1, str2 ) {
2387        return setCss(prefixes.join(str1 + ';') + ( str2 || '' ));
2388    }
2389
2390    /**
2391     * is returns a boolean for if typeof obj is exactly type.
2392     */
2393    function is( obj, type ) {
2394        return typeof obj === type;
2395    }
2396
2397    /**
2398     * contains returns a boolean for if substr is found within str.
2399     */
2400    function contains( str, substr ) {
2401        return !!~('' + str).indexOf(substr);
2402    }
2403
2404    /*>>testprop*/
2405
2406    // testProps is a generic CSS / DOM property test.
2407
2408    // In testing support for a given CSS property, it's legit to test:
2409    //    `elem.style[styleName] !== undefined`
2410    // If the property is supported it will return an empty string,
2411    // if unsupported it will return undefined.
2412
2413    // We'll take advantage of this quick test and skip setting a style
2414    // on our modernizr element, but instead just testing undefined vs
2415    // empty string.
2416
2417    // Because the testing of the CSS property names (with "-", as
2418    // opposed to the camelCase DOM properties) is non-portable and
2419    // non-standard but works in WebKit and IE (but not Gecko or Opera),
2420    // we explicitly reject properties with dashes so that authors
2421    // developing in WebKit or IE first don't end up with
2422    // browser-specific content by accident.
2423
2424    function testProps( props, prefixed ) {
2425        for ( var i in props ) {
2426            var prop = props[i];
2427            if ( !contains(prop, "-") && mStyle[prop] !== undefined ) {
2428                return prefixed == 'pfx' ? prop : true;
2429            }
2430        }
2431        return false;
2432    }
2433    /*>>testprop*/
2434
2435    // TODO :: add testDOMProps
2436    /**
2437     * testDOMProps is a generic DOM property test; if a browser supports
2438     *   a certain property, it won't return undefined for it.
2439     */
2440    function testDOMProps( props, obj, elem ) {
2441        for ( var i in props ) {
2442            var item = obj[props[i]];
2443            if ( item !== undefined) {
2444
2445                // return the property name as a string
2446                if (elem === false) return props[i];
2447
2448                // let's bind a function
2449                if (is(item, 'function')){
2450                  // default to autobind unless override
2451                  return item.bind(elem || obj);
2452                }
2453
2454                // return the unbound function or obj or value
2455                return item;
2456            }
2457        }
2458        return false;
2459    }
2460
2461    /*>>testallprops*/
2462    /**
2463     * testPropsAll tests a list of DOM properties we want to check against.
2464     *   We specify literally ALL possible (known and/or likely) properties on
2465     *   the element including the non-vendor prefixed one, for forward-
2466     *   compatibility.
2467     */
2468    function testPropsAll( prop, prefixed, elem ) {
2469
2470        var ucProp  = prop.charAt(0).toUpperCase() + prop.slice(1),
2471            props   = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' ');
2472
2473        // did they call .prefixed('boxSizing') or are we just testing a prop?
2474        if(is(prefixed, "string") || is(prefixed, "undefined")) {
2475          return testProps(props, prefixed);
2476
2477        // otherwise, they called .prefixed('requestAnimationFrame', window[, elem])
2478        } else {
2479          props = (prop + ' ' + (domPrefixes).join(ucProp + ' ') + ucProp).split(' ');
2480          return testDOMProps(props, prefixed, elem);
2481        }
2482    }
2483    /*>>testallprops*/
2484
2485
2486    /**
2487     * Tests
2488     * -----
2489     */
2490
2491    // The *new* flexbox
2492    // dev.w3.org/csswg/css3-flexbox
2493
2494    tests['flexbox'] = function() {
2495      return testPropsAll('flexWrap');
2496    };
2497
2498    // The *old* flexbox
2499    // www.w3.org/TR/2009/WD-css3-flexbox-20090723/
2500
2501    tests['flexboxlegacy'] = function() {
2502        return testPropsAll('boxDirection');
2503    };
2504
2505    // On the S60 and BB Storm, getContext exists, but always returns undefined
2506    // so we actually have to call getContext() to verify
2507    // github.com/Modernizr/Modernizr/issues/issue/97/
2508
2509    tests['canvas'] = function() {
2510        var elem = document.createElement('canvas');
2511        return !!(elem.getContext && elem.getContext('2d'));
2512    };
2513
2514    tests['canvastext'] = function() {
2515        return !!(Modernizr['canvas'] && is(document.createElement('canvas').getContext('2d').fillText, 'function'));
2516    };
2517
2518    // webk.it/70117 is tracking a legit WebGL feature detect proposal
2519
2520    // We do a soft detect which may false positive in order to avoid
2521    // an expensive context creation: bugzil.la/732441
2522
2523    tests['webgl'] = function() {
2524        return !!window.WebGLRenderingContext;
2525    };
2526
2527    /*
2528     * The Modernizr.touch test only indicates if the browser supports
2529     *    touch events, which does not necessarily reflect a touchscreen
2530     *    device, as evidenced by tablets running Windows 7 or, alas,
2531     *    the Palm Pre / WebOS (touch) phones.
2532     *
2533     * Additionally, Chrome (desktop) used to lie about its support on this,
2534     *    but that has since been rectified: crbug.com/36415
2535     *
2536     * We also test for Firefox 4 Multitouch Support.
2537     *
2538     * For more info, see: modernizr.github.com/Modernizr/touch.html
2539     */
2540
2541    tests['touch'] = function() {
2542        var bool;
2543
2544        if(('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) {
2545          bool = true;
2546        } else {
2547          injectElementWithStyles(['@media (',prefixes.join('touch-enabled),('),mod,')','{#modernizr{top:9px;position:absolute}}'].join(''), function( node ) {
2548            bool = node.offsetTop === 9;
2549          });
2550        }
2551
2552        return bool;
2553    };
2554
2555
2556    // geolocation is often considered a trivial feature detect...
2557    // Turns out, it's quite tricky to get right:
2558    //
2559    // Using !!navigator.geolocation does two things we don't want. It:
2560    //   1. Leaks memory in IE9: github.com/Modernizr/Modernizr/issues/513
2561    //   2. Disables page caching in WebKit: webk.it/43956
2562    //
2563    // Meanwhile, in Firefox < 8, an about:config setting could expose
2564    // a false positive that would throw an exception: bugzil.la/688158
2565
2566    tests['geolocation'] = function() {
2567        return 'geolocation' in navigator;
2568    };
2569
2570
2571    tests['postmessage'] = function() {
2572      return !!window.postMessage;
2573    };
2574
2575
2576    // Chrome incognito mode used to throw an exception when using openDatabase
2577    // It doesn't anymore.
2578    tests['websqldatabase'] = function() {
2579      return !!window.openDatabase;
2580    };
2581
2582    // Vendors had inconsistent prefixing with the experimental Indexed DB:
2583    // - Webkit's implementation is accessible through webkitIndexedDB
2584    // - Firefox shipped moz_indexedDB before FF4b9, but since then has been mozIndexedDB
2585    // For speed, we don't test the legacy (and beta-only) indexedDB
2586    tests['indexedDB'] = function() {
2587      return !!testPropsAll("indexedDB", window);
2588    };
2589
2590    // documentMode logic from YUI to filter out IE8 Compat Mode
2591    //   which false positives.
2592    tests['hashchange'] = function() {
2593      return isEventSupported('hashchange', window) && (document.documentMode === undefined || document.documentMode > 7);
2594    };
2595
2596    // Per 1.6:
2597    // This used to be Modernizr.historymanagement but the longer
2598    // name has been deprecated in favor of a shorter and property-matching one.
2599    // The old API is still available in 1.6, but as of 2.0 will throw a warning,
2600    // and in the first release thereafter disappear entirely.
2601    tests['history'] = function() {
2602      return !!(window.history && history.pushState);
2603    };
2604
2605    tests['draganddrop'] = function() {
2606        var div = document.createElement('div');
2607        return ('draggable' in div) || ('ondragstart' in div && 'ondrop' in div);
2608    };
2609
2610    // FF3.6 was EOL'ed on 4/24/12, but the ESR version of FF10
2611    // will be supported until FF19 (2/12/13), at which time, ESR becomes FF17.
2612    // FF10 still uses prefixes, so check for it until then.
2613    // for more ESR info, see: mozilla.org/en-US/firefox/organizations/faq/
2614    tests['websockets'] = function() {
2615        return 'WebSocket' in window || 'MozWebSocket' in window;
2616    };
2617
2618
2619    // css-tricks.com/rgba-browser-support/
2620    tests['rgba'] = function() {
2621        // Set an rgba() color and check the returned value
2622
2623        setCss('background-color:rgba(150,255,150,.5)');
2624
2625        return contains(mStyle.backgroundColor, 'rgba');
2626    };
2627
2628    tests['hsla'] = function() {
2629        // Same as rgba(), in fact, browsers re-map hsla() to rgba() internally,
2630        //   except IE9 who retains it as hsla
2631
2632        setCss('background-color:hsla(120,40%,100%,.5)');
2633
2634        return contains(mStyle.backgroundColor, 'rgba') || contains(mStyle.backgroundColor, 'hsla');
2635    };
2636
2637    tests['multiplebgs'] = function() {
2638        // Setting multiple images AND a color on the background shorthand property
2639        //  and then querying the style.background property value for the number of
2640        //  occurrences of "url(" is a reliable method for detecting ACTUAL support for this!
2641
2642        setCss('background:url(https://),url(https://),red url(https://)');
2643
2644        // If the UA supports multiple backgrounds, there should be three occurrences
2645        //   of the string "url(" in the return value for elemStyle.background
2646
2647        return (/(url\s*\(.*?){3}/).test(mStyle.background);
2648    };
2649
2650
2651
2652    // this will false positive in Opera Mini
2653    //   github.com/Modernizr/Modernizr/issues/396
2654
2655    tests['backgroundsize'] = function() {
2656        return testPropsAll('backgroundSize');
2657    };
2658
2659    tests['borderimage'] = function() {
2660        return testPropsAll('borderImage');
2661    };
2662
2663
2664    // Super comprehensive table about all the unique implementations of
2665    // border-radius: muddledramblings.com/table-of-css3-border-radius-compliance
2666
2667    tests['borderradius'] = function() {
2668        return testPropsAll('borderRadius');
2669    };
2670
2671    // WebOS unfortunately false positives on this test.
2672    tests['boxshadow'] = function() {
2673        return testPropsAll('boxShadow');
2674    };
2675
2676    // FF3.0 will false positive on this test
2677    tests['textshadow'] = function() {
2678        return document.createElement('div').style.textShadow === '';
2679    };
2680
2681
2682    tests['opacity'] = function() {
2683        // Browsers that actually have CSS Opacity implemented have done so
2684        //  according to spec, which means their return values are within the
2685        //  range of [0.0,1.0] - including the leading zero.
2686
2687        setCssAll('opacity:.55');
2688
2689        // The non-literal . in this regex is intentional:
2690        //   German Chrome returns this value as 0,55
2691        // github.com/Modernizr/Modernizr/issues/#issue/59/comment/516632
2692        return (/^0.55$/).test(mStyle.opacity);
2693    };
2694
2695
2696    // Note, Android < 4 will pass this test, but can only animate
2697    //   a single property at a time
2698    //   goo.gl/v3V4Gp
2699    tests['cssanimations'] = function() {
2700        return testPropsAll('animationName');
2701    };
2702
2703
2704    tests['csscolumns'] = function() {
2705        return testPropsAll('columnCount');
2706    };
2707
2708
2709    tests['cssgradients'] = function() {
2710        /**
2711         * For CSS Gradients syntax, please see:
2712         * webkit.org/blog/175/introducing-css-gradients/
2713         * developer.mozilla.org/en/CSS/-moz-linear-gradient
2714         * developer.mozilla.org/en/CSS/-moz-radial-gradient
2715         * dev.w3.org/csswg/css3-images/#gradients-
2716         */
2717
2718        var str1 = 'background-image:',
2719            str2 = 'gradient(linear,left top,right bottom,from(#9f9),to(white));',
2720            str3 = 'linear-gradient(left top,#9f9, white);';
2721
2722        setCss(
2723             // legacy webkit syntax (FIXME: remove when syntax not in use anymore)
2724              (str1 + '-webkit- '.split(' ').join(str2 + str1) +
2725             // standard syntax             // trailing 'background-image:'
2726              prefixes.join(str3 + str1)).slice(0, -str1.length)
2727        );
2728
2729        return contains(mStyle.backgroundImage, 'gradient');
2730    };
2731
2732
2733    tests['cssreflections'] = function() {
2734        return testPropsAll('boxReflect');
2735    };
2736
2737
2738    tests['csstransforms'] = function() {
2739        return !!testPropsAll('transform');
2740    };
2741
2742
2743    tests['csstransforms3d'] = function() {
2744
2745        var ret = !!testPropsAll('perspective');
2746
2747        // Webkit's 3D transforms are passed off to the browser's own graphics renderer.
2748        //   It works fine in Safari on Leopard and Snow Leopard, but not in Chrome in
2749        //   some conditions. As a result, Webkit typically recognizes the syntax but
2750        //   will sometimes throw a false positive, thus we must do a more thorough check:
2751        if ( ret && 'webkitPerspective' in docElement.style ) {
2752
2753          // Webkit allows this media query to succeed only if the feature is enabled.
2754          // `@media (transform-3d),(-webkit-transform-3d){ ... }`
2755          injectElementWithStyles('@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}', function( node, rule ) {
2756            ret = node.offsetLeft === 9 && node.offsetHeight === 3;
2757          });
2758        }
2759        return ret;
2760    };
2761
2762
2763    tests['csstransitions'] = function() {
2764        return testPropsAll('transition');
2765    };
2766
2767
2768    /*>>fontface*/
2769    // @font-face detection routine by Diego Perini
2770    // javascript.nwbox.com/CSSSupport/
2771
2772    // false positives:
2773    //   WebOS github.com/Modernizr/Modernizr/issues/342
2774    //   WP7   github.com/Modernizr/Modernizr/issues/538
2775    tests['fontface'] = function() {
2776        var bool;
2777
2778        injectElementWithStyles('@font-face {font-family:"font";src:url("https://")}', function( node, rule ) {
2779          var style = document.getElementById('smodernizr'),
2780              sheet = style.sheet || style.styleSheet,
2781              cssText = sheet ? (sheet.cssRules && sheet.cssRules[0] ? sheet.cssRules[0].cssText : sheet.cssText || '') : '';
2782
2783          bool = /src/i.test(cssText) && cssText.indexOf(rule.split(' ')[0]) === 0;
2784        });
2785
2786        return bool;
2787    };
2788    /*>>fontface*/
2789
2790    // CSS generated content detection
2791    tests['generatedcontent'] = function() {
2792        var bool;
2793
2794        injectElementWithStyles(['#',mod,'{font:0/0 a}
2794#',mod,':after{content:"',smile,'";visibility:hidden;font:3px/1 a}'].join(''), function( node ) {
2795          bool = node.offsetHeight >= 3;
2796        });
2797
2798        return bool;
2799    };
2800
2801
2802
2803    // These tests evaluate support of the video/audio elements, as well as
2804    // testing what types of content they support.
2805    //
2806    // We're using the Boolean constructor here, so that we can extend the value
2807    // e.g.  Modernizr.video     // true
2808    //       Modernizr.video.ogg // 'probably'
2809    //
2810    // Codec values from : github.com/NielsLeenheer/html5test/blob/9106a8/index.html#L845
2811    //                     thx to NielsLeenheer and zcorpan
2812
2813    // Note: in some older browsers, "no" was a return value instead of empty string.
2814    //   It was live in FF3.5.0 and 3.5.1, but fixed in 3.5.2
2815    //   It was also live in Safari 4.0.0 - 4.0.4, but fixed in 4.0.5
2816
2817    tests['video'] = function() {
2818        var elem = document.createElement('video'),
2819            bool = false;
2820
2821        // IE9 Running on Windows Server SKU can cause an exception to be thrown, bug #224
2822        try {
2823            if ( bool = !!elem.canPlayType ) {
2824                bool      = new Boolean(bool);
2825                bool.ogg  = elem.canPlayType('video/ogg; codecs="theora"')      .replace(/^no$/,'');
2826
2827                // Without QuickTime, this value will be `undefined`. github.com/Modernizr/Modernizr/issues/546
2828                bool.h264 = elem.canPlayType('video/mp4; codecs="avc1.42E01E"') .replace(/^no$/,'');
2829
2830                bool.webm = elem.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/,'');
2831            }
2832
2833        } catch(e) { }
2834
2835        return bool;
2836    };
2837
2838    tests['audio'] = function() {
2839        var elem = document.createElement('audio'),
2840            bool = false;
2841
2842        try {
2843            if ( bool = !!elem.canPlayType ) {
2844                bool      = new Boolean(bool);
2845                bool.ogg  = elem.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,'');
2846                bool.mp3  = elem.canPlayType('audio/mpeg;')               .replace(/^no$/,'');
2847
2848                // Mimetypes accepted:
2849                //   developer.mozilla.org/En/Media_formats_supported_by_the_audio_and_video_elements
2850                //   bit.ly/iphoneoscodecs
2851                bool.wav  = elem.canPlayType('audio/wav; codecs="1"')     .replace(/^no$/,'');
2852                bool.m4a  = ( elem.canPlayType('audio/x-m4a;')            ||
2853                              elem.canPlayType('audio/aac;'))             .replace(/^no$/,'');
2854            }
2855        } catch(e) { }
2856
2857        return bool;
2858    };
2859
2860
2861    // In FF4, if disabled, window.localStorage should === null.
2862
2863    // Normally, we could not test that directly and need to do a
2864    //   `('localStorage' in window) && ` test first because otherwise Firefox will
2865    //   throw bugzil.la/365772 if cookies are disabled
2866
2867    // Also in iOS5 Private Browsing mode, attempting to use localStorage.setItem
2868    // will throw the exception:
2869    //   QUOTA_EXCEEDED_ERRROR DOM Exception 22.
2870    // Peculiarly, getItem and removeItem calls do not throw.
2871
2872    // Because we are forced to try/catch this, we'll go aggressive.
2873
2874    // Just FWIW: IE8 Compat mode supports these features completely:
2875    //   www.quirksmode.org/dom/html5.html
2876    // But IE8 doesn't support either with local files
2877
2878    tests['localstorage'] = function() {
2879        try {
2880            localStorage.setItem(mod, mod);
2881            localStorage.removeItem(mod);
2882            return true;
2883        } catch(e) {
2884            return false;
2885        }
2886    };
2887
2888    tests['sessionstorage'] = function() {
2889        try {
2890            sessionStorage.setItem(mod, mod);
2891            sessionStorage.removeItem(mod);
2892            return true;
2893        } catch(e) {
2894            return false;
2895        }
2896    };
2897
2898
2899    tests['webworkers'] = function() {
2900        return !!window.Worker;
2901    };
2902
2903
2904    tests['applicationcache'] = function() {
2905        return !!window.applicationCache;
2906    };
2907
2908
2909    // Thanks to Erik Dahlstrom
2910    tests['svg'] = function() {
2911        return !!document.createElementNS && !!document.createElementNS(ns.svg, 'svg').createSVGRect;
2912    };
2913
2914    // specifically for SVG inline in HTML, not within XHTML
2915    // test page: paulirish.com/demo/inline-svg
2916    tests['inlinesvg'] = function() {
2917      var div = document.createElement('div');
2918      div.innerHTML = '<svg/>';
2919      return (div.firstChild && div.firstChild.namespaceURI) == ns.svg;
2920    };
2921
2922    // SVG SMIL animation
2923    tests['smil'] = function() {
2924        return !!document.createElementNS && /SVGAnimate/.test(toString.call(document.createElementNS(ns.svg, 'animate')));
2925    };
2926
2927    // This test is only for clip paths in SVG proper, not clip paths on HTML content
2928    // demo: srufaculty.sru.edu/david.dailey/svg/newstuff/clipPath4.svg
2929
2930    // However read the comments to dig into applying SVG clippaths to HTML content here:
2931    //   github.com/Modernizr/Modernizr/issues/213#issuecomment-1149491
2932    tests['svgclippaths'] = function() {
2933        return !!document.createElementNS && /SVGClipPath/.test(toString.call(document.createElementNS(ns.svg, 'clipPath')));
2934    };
2935
2936    /*>>webforms*/
2937    // input features and input types go directly onto the ret object, bypassing the tests loop.
2938    // Hold this guy to execute in a moment.
2939    function webforms() {
2940        /*>>input*/
2941        // Run through HTML5's new input attributes to see if the UA understands any.
2942        // We're using f which is the <input> element created early on
2943        // Mike Taylr has created a comprehensive resource for testing these attributes
2944        //   when applied to all input types:
2945        //   miketaylr.com/code/input-type-attr.html
2946        // spec: www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html#input-type-attr-summary
2947
2948        // Only input placeholder is tested while textarea's placeholder is not.
2949        // Currently Safari 4 and Opera 11 have support only for the input placeholder
2950        // Both tests are available in feature-detects/forms-placeholder.js
2951        Modernizr['input'] = (function( props ) {
2952            for ( var i = 0, len = props.length; i < len; i++ ) {
2953                attrs[ props[i] ] = !!(props[i] in inputElem);
2954            }
2955            if (attrs.list){
2956              // safari false positive's on datalist: webk.it/74252
2957              // see also github.com/Modernizr/Modernizr/issues/146
2958              attrs.list = !!(document.createElement('datalist') && window.HTMLDataListElement);
2959            }
2960            return attrs;
2961        })('autocomplete autofocus list placeholder max min multiple pattern required step'.split(' '));
2962        /*>>input*/
2963
2964        /*>>inputtypes*/
2965        // Run through HTML5's new input types to see if the UA understands any.
2966        //   This is put behind the tests runloop because it doesn't return a
2967        //   true/false like all the other tests; instead, it returns an object
2968        //   containing each input type with its corresponding true/false value
2969
2970        // Big thanks to @miketaylr for the html5 forms expertise. miketaylr.com/
2971        Modernizr['inputtypes'] = (function(props) {
2972
2973            for ( var i = 0, bool, inputElemType, defaultView, len = props.length; i < len; i++ ) {
2974
2975                inputElem.setAttribute('type', inputElemType = props[i]);
2976                bool = inputElem.type !== 'text';
2977
2978                // We first check to see if the type we give it sticks..
2979                // If the type does, we feed it a textual value, which shouldn't be val
2979id.
2980                // If the value doesn't stick, we know there's input sanitization which infers a custom UI
2981                if ( bool ) {
2982
2983                    inputElem.value         = smile;
2984                    inputElem.style.cssText = 'position:absolute;visibility:hidden;';
2985
2986                    if ( /^range$/.test(inputElemType) && inputElem.style.WebkitAppearance !== undefined ) {
2987
2988                      docElement.appendChild(inputElem);
2989                      defaultView = document.defaultView;
2990
2991                      // Safari 2-4 allows the smiley as a value, despite making a slider
2992                      bool =  defaultView.getComputedStyle &&
2993                              defaultView.getComputedStyle(inputElem, null).WebkitAppearance !== 'textfield' &&
2994                              // Mobile android web browser has false positive, so must
2995                              // check the height to see if the widget is actually there.
2996                              (inputElem.offsetHeight !== 0);
2997
2998                      docElement.removeChild(inputElem);
2999
3000                    } else if ( /^(search|tel)$/.test(inputElemType) ){
3001                      // Spec doesn't define any special parsing or detectable UI
3002                      //   behaviors so we pass these through as true
3003
3004                      // Interestingly, opera fails the earlier test, so it doesn't
3005                      //  even make it here.
3006
3007                    } else if ( /^(url|email)$/.test(inputElemType) ) {
3008                      // Real url and email support comes with prebaked validation.
3009                      bool = inputElem.checkValidity && inputElem.checkValidity() === false;
3010
3011                    } else {
3012                      // If the upgraded input compontent rejects the :) text, we got a winner
3013                      bool = inputElem.value != smile;
3014                    }
3015                }
3016
3017                inputs[ props[i] ] = !!bool;
3018            }
3019            return inputs;
3020        })('search tel url email datetime date month week time datetime-local number range color'.split(' '));
3021        /*>>inputtypes*/
3022    }
3023    /*>>webforms*/
3024
3025
3026    // End of test definitions
3027    // -----------------------
3028
3029
3030
3031    // Run through all tests and detect their support in the current UA.
3032    // todo: hypothetically we could be doing an array of tests and use a basic loop here.
3033    for ( var feature in tests ) {
3034        if ( hasOwnProp(tests, feature) ) {
3035            // run the test, throw the return value into the Modernizr,
3036            //   then based on that boolean, define an appropriate className
3037            //   and push it into an array of classes we'll join later.
3038            featureName  = feature.toLowerCase();
3039            Modernizr[featureName] = tests[feature]();
3040
3041            classes.push((Modernizr[featureName] ? '' : 'no-') + featureName);
3042        }
3043    }
3044
3045    /*>>webforms*/
3046    // input tests need to run.
3047    Modernizr.input || webforms();
3048    /*>>webforms*/
3049
3050
3051    /**
3052     * addTest allows the user to define their own feature tests
3053     * the result will be added onto the Modernizr object,
3054     * as well as an appropriate className set on the html element
3055     *
3056     * @param feature - String naming the feature
3057     * @param test - Function returning true if feature is supported, false if not
3058     */
3059     Modernizr.addTest = function ( feature, test ) {
3060       if ( typeof feature == 'object' ) {
3061         for ( var key in feature ) {
3062           if ( hasOwnProp( feature, key ) ) {
3063             Modernizr.addTest( key, feature[ key ] );
3064           }
3065         }
3066       } else {
3067
3068         feature = feature.toLowerCase();
3069
3070         if ( Modernizr[feature] !== undefined ) {
3071           // we're going to quit if you're trying to overwrite an existing test
3072           // if we were to allow it, we'd do this:
3073           //   var re = new RegExp("\\b(no-)?" + feature + "\\b");
3074           //   docElement.className = docElement.className.replace( re, '' );
3075           // but, no rly, stuff 'em.
3076           return Modernizr;
3077         }
3078
3079         test = typeof test == 'function' ? test() : test;
3080
3081         if (typeof enableClasses !== "undefined" && enableClasses) {
3082           docElement.className += ' ' + (test ? '' : 'no-') + feature;
3083         }
3084         Modernizr[feature] = test;
3085
3086       }
3087
3088       return Modernizr; // allow chaining.
3089     };
3090
3091
3092    // Reset modElem.cssText to nothing to reduce memory footprint.
3093    setCss('');
3094    modElem = inputElem = null;
3095
3096    /*>>shiv*/
3097    /**
3098     * @preserve HTML5 Shiv prev3.7.1 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
3099     */
3100    ;(function(window, document) {
3101        /*jshint evil:true */
3102        /** version */
3103        var version = '3.7.0';
3104
3105        /** Preset options */
3106        var options = window.html5 || {};
3107
3108        /** Used to skip problem elements */
3109        var reSkip = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i;
3110
3111        /** Not all elements can be cloned in IE **/
3112        var saveClones = /^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i;
3113
3114        /** Detect whether the browser supports default html5 styles */
3115        var supportsHtml5Styles;
3116
3117        /** Name of the expando, to work with multiple documents or to re-shiv one document */
3118        var expando = '_html5shiv';
3119
3120        /** The id for the the documents expando */
3121        var expanID = 0;
3122
3123        /** Cached data for each document */
3124        var expandoData = {};
3125
3126        /** Detect whether the browser supports unknown elements */
3127        var supportsUnknownElements;
3128
3129        (function() {
3130          try {
3131            var a = document.createElement('a');
3132            a.innerHTML = '<xyz></xyz>';
3133            //if the hidden property is implemented we can assume, that the browser supports basic HTML5 Styles
3134            supportsHtml5Styles = ('hidden' in a);
3135
3136            supportsUnknownElements = a.childNodes.length == 1 || (function() {
3137              // assign a false positive if unable to shiv
3138              (document.createElement)('a');
3139              var frag = document.createDocumentFragment();
3140              return (
3141                typeof frag.cloneNode == 'undefined' ||
3142                typeof frag.createDocumentFragment == 'undefined' ||
3143                typeof frag.createElement == 'undefined'
3144              );
3145            }());
3146          } catch(e) {
3147            // assign a false positive if detection fails => unable to shiv
3148            supportsHtml5Styles = true;
3149            supportsUnknownElements = true;
3150          }
3151
3152        }());
3153
3154        /*--------------------------------------------------------------------------*/
3155
3156        /**
3157         * Creates a style sheet with the given CSS text and adds it to the document.
3158         * @private
3159         * @param {Document} ownerDocument The document.
3160         * @param {String} cssText The CSS text.
3161         * @returns {StyleSheet} The style element.
3162         */
3163        function addStyleSheet(ownerDocument, cssText) {
3164          var p = ownerDocument.createElement('p'),
3165          parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement;
3166
3167          p.innerHTML = 'x<style>' + cssText + '</style>';
3168          return parent.insertBefore(p.lastChild, parent.firstChild);
3169        }
3170
3171        /**
3172         * Returns the value of `html5.elements` as an array.
vendor: 5,515 bytes, lines 3173-3312
3173         * @private
3174         * @returns {Array} An array of shived element node names.
3175         */
3176        function getElements() {
3177          var elements = html5.elements;
3178          return typeof elements == 'string' ? elements.split(' ') : elements;
3179        }
3180
3181        /**
3182         * Returns the data associated to the given document
3183         * @private
3184         * @param {Document} ownerDocument The document.
3185         * @returns {Object} An object of data.
3186         */
3187        function getExpandoData(ownerDocument) {
3188          var data = expandoData[ownerDocument[expando]];
3189          if (!data) {
3190            data = {};
3191            expanID++;
3192            ownerDocument[expando] = expanID;
3193            expandoData[expanID] = data;
3194          }
3195          return data;
3196        }
3197
3198        /**
3199         * returns a shived element for the given nodeName and document
3200         * @memberOf html5
3201         * @param {String} nodeName name of the element
3202         * @param {Document} ownerDocument The context document.
3203         * @returns {Object} The shived element.
3204         */
3205        function createElement(nodeName, ownerDocument, data){
3206          if (!ownerDocument) {
3207            ownerDocument = document;
3208          }
3209          if(supportsUnknownElements){
3210            return ownerDocument.createElement(nodeName);
3211          }
3212          if (!data) {
3213            data = getExpandoData(ownerDocument);
3214          }
3215          var node;
3216
3217          if (data.cache[nodeName]) {
3218            node = data.cache[nodeName].cloneNode();
3219          } else if (saveClones.test(nodeName)) {
3220            node = (data.cache[nodeName] = data.createElem(nodeName)).cloneNode();
3221          } else {
3222            node = data.createElem(nodeName);
3223          }
3224
3225          // Avoid adding some elements to fragments in IE < 9 because
3226          // * Attributes like `name` or `type` cannot be set/changed once an element
3227          //   is inserted into a document/fragment
3228          // * Link elements with `src` attributes that are inaccessible, as with
3229          //   a 403 response, will cause the tab/window to crash
3230          // * Script elements appended to fragments will execute when their `src`
3231          //   or `text` property is set
3232          return node.canHaveChildren && !reSkip.test(nodeName) && !node.tagUrn ? data.frag.appendChild(node) : node;
3233        }
3234
3235        /**
3236         * returns a shived DocumentFragment for the given document
3237         * @memberOf html5
3238         * @param {Document} ownerDocument The context document.
3239         * @returns {Object} The shived DocumentFragment.
3240         */
3241        function createDocumentFragment(ownerDocument, data){
3242          if (!ownerDocument) {
3243            ownerDocument = document;
3244          }
3245          if(supportsUnknownElements){
3246            return ownerDocument.createDocumentFragment();
3247          }
3248          data = data || getExpandoData(ownerDocument);
3249          var clone = data.frag.cloneNode(),
3250          i = 0,
3251          elems = getElements(),
3252          l = elems.length;
3253          for(;i<l;i++){
3254            clone.createElement(elems[i]);
3255          }
3256          return clone;
3257        }
3258
3259        /**
3260         * Shivs the `createElement` and `createDocumentFragment` methods of the document.
3261         * @private
3262         * @param {Document|DocumentFragment} ownerDocument The document.
3263         * @param {Object} data of the document.
3264         */
3265        function shivMethods(ownerDocument, data) {
3266          if (!data.cache) {
3267            data.cache = {};
3268            data.createElem = ownerDocument.createElement;
3269            data.createFrag = ownerDocument.createDocumentFragment;
3270            data.frag = data.createFrag();
3271          }
3272
3273
3274          ownerDocument.createElement = function(nodeName) {
3275            //abort shiv
3276            if (!html5.shivMethods) {
3277              return data.createElem(nodeName);
3278            }
3279            return createElement(nodeName, ownerDocument, data);
3280          };
3281
3282          ownerDocument.createDocumentFragment = Function('h,f', 'return function(){' +
3283                                                          'var n=f.cloneNode(),c=n.createElement;' +
3284                                                          'h.shivMethods&&(' +
3285                                                          // unroll the `createElement` calls
3286                                                          getElements().join().replace(/[\w\-]+/g, function(nodeName) {
3287            data.createElem(nodeName);
3288            data.frag.createElement(nodeName);
3289            return 'c("' + nodeName + '")';
3290          }) +
3291            ');return n}'
3292                                                         )(html5, data.frag);
3293        }
3294
3295        /*--------------------------------------------------------------------------*/
3296
3297        /**
3298         * Shivs the given document.
3299         * @memberOf html5
3300         * @param {Document} ownerDocument The document to shiv.
3301         * @returns {Document} The shived document.
3302         */
3303        function shivDocument(ownerDocument) {
3304          if (!ownerDocument) {
3305            ownerDocument = document;
3306          }
3307          var data = getExpandoData(ownerDocument);
3308
3309          if (html5.shivCSS && !supportsHtml5Styles && !data.hasCSS) {
3310            data.hasCSS = !!addStyleSheet(ownerDocument,
3311                                          // corrects block display not defined in IE6/7/8/9
3312                                          'article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{dis
3312play:block}' +
3313                                            // adds styling not present in IE6/7/8/9
3314                                            'mark{background:#FF0;color:#000}' +
3315                                            // hides non-rendered elements
3316                                            'template{display:none}'
3317                                         );
3318          }
3319          if (!supportsUnknownElements) {
3320            shivMethods(ownerDocument, data);
3321          }
3322          return ownerDocument;
3323        }
3324
3325        /*--------------------------------------------------------------------------*/
3326
3327        /**
3328         * The `html5` object is exposed so that more elements can be shived and
3329         * existing shiving can be detected on iframes.
3330         * @type Object
3331         * @example
3332         *
3333         * // options can be changed before the script is included
3334         * html5 = { 'elements': 'mark section', 'shivCSS': false, 'shivMethods': false };
3335         */
3336        var html5 = {
3337
3338          /**
3339           * An array or space separated string of node names of the elements to shiv.
3340           * @memberOf html5
3341           * @type Array|String
3342           */
3343          'elements': options.elements || 'abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video',
3344
3345          /**
3346           * current version of html5shiv
3347           */
3348          'version': version,
3349
3350          /**
3351           * A flag to indicate that the HTML5 style sheet should be inserted.
3352           * @memberOf html5
3353           * @type Boolean
3354           */
3355          'shivCSS': (options.shivCSS !== false),
3356
3357          /**
3358           * Is equal to true if a browser supports creating unknown/HTML5 elements
3359           * @memberOf html5
3360           * @type boolean
3361           */
3362          'supportsUnknownElements': supportsUnknownElements,
3363
3364          /**
3365           * A flag to indicate that the document's `createElement` and `createDocumentFragment`
3366           * methods should be overwritten.
3367           * @memberOf html5
3368           * @type Boolean
3369           */
3370          'shivMethods': (options.shivMethods !== false),
3371
3372          /**
3373           * A string to describe the type of `html5` object ("default" or "default print").
3374           * @memberOf html5
3375           * @type String
3376           */
3377          'type': 'default',
3378
3379          // shivs the document according to the specified `html5` object options
3380          'shivDocument': shivDocument,
3381
3382          //creates a shived element
3383          createElement: createElement,
3384
3385          //creates a shived documentFragment
3386          createDocumentFragment: createDocumentFragment
3387        };
3388
3389        /*--------------------------------------------------------------------------*/
3390
3391        // expose html5
3392        window.html5 = html5;
3393
3394        // shiv the document
3395        shivDocument(document);
3396
3397    }(this, document));
3398    /*>>shiv*/
3399
3400    // Assign private properties to the return object with prefix
3401    Modernizr._version      = version;
3402
3403    // expose these for the plugin API. Look in the source for how to join() them against your input
3404    /*>>prefixes*/
3405    Modernizr._prefixes     = prefixes;
3406    /*>>prefixes*/
3407    /*>>domprefixes*/
3408    Modernizr._domPrefixes  = domPrefixes;
3409    Modernizr._cssomPrefixes  = cssomPrefixes;
3410    /*>>domprefixes*/
3411
3412    /*>>mq*/
3413    // Modernizr.mq tests a given media query, live against the current state of the window
3414    // A few important notes:
3415    //   * If a browser does not support media queries at all (eg. oldIE) the mq() will always return false
3416    //   * A max-width or orientation query will be evaluated against the current state, which may change later.
3417    //   * You must specify values. Eg. If you are testing support for the min-width media query use:
3418    //       Modernizr.mq('(min-width:0)')
3419    // usage:
3420    // Modernizr.mq('only screen and (max-width:768)')
3421    Modernizr.mq            = testMediaQuery;
3422    /*>>mq*/
3423
3424    /*>>hasevent*/
3425    // Modernizr.hasEvent() detects support for a given event, with an optional element to test on
3426    // Modernizr.hasEvent('gesturestart', elem)
3427    Modernizr.hasEvent      = isEventSupported;
3428    /*>>hasevent*/
3429
3430    /*>>testprop*/
3431    // Modernizr.testProp() investigates whether a given style property is recognized
3432    // Note that the property names must be provided in the camelCase variant.
3433    // Modernizr.testProp('pointerEvents')
3434    Modernizr.testProp      = function(prop){
3435        return testProps([prop]);
3436    };
3437    /*>>testprop*/
3438
3439    /*>>testallprops*/
3440    // Modernizr.testAllProps() investigates whether a given style property,
3441    //   or any of its vendor-prefixed variants, is recognized
3442    // Note that the property names must be provided in the camelCase variant.
3443    // Modernizr.testAllProps('boxSizing')
3444    Modernizr.testAllProps  = testPropsAll;
3445    /*>>testallprops*/
3446
3447
3448    /*>>teststyles*/
3449    // Modernizr.testStyles() allows you to add custom styles to the document and test an element afterwards
3450    // Modernizr.testStyles('#modernizr { position:absolute }', function(elem, rule){ ... })
3451    Modernizr.testStyles    = injectElementWithStyles;
3452    /*>>teststyles*/
3453
3454
3455    /*>>prefixed*/
3456    // Modernizr.prefixed() returns the prefixed or nonprefixed property name variant of your input
3457    // Modernizr.prefixed('boxSizing') // 'MozBoxSizing'
3458
3459    // Properties must be passed as dom-style camelcase, rather than `box-sizing` hypentated style.
3460    // Return values will also be the camelCase variant, if you need to translate that to hypenated style use:
3461    //
3462    //     str.replace(/([A-Z])/g, function(str,m1){ return '-' + m1.toLowerCase(); }).replace(/^ms-/,'-ms-');
3463
3464    // If you're trying to ascertain which transition end event to bind to, you might do something like...
3465    //
3466    //     var transEndEventNames = {
3467    //       'WebkitTransition' : 'webkitTransitionEnd',
3468    //       'MozTransition'    : 'transitionend',
3469    //       'OTransition'      : 'oTransitionEnd',
3470    //       'msTransition'     : 'MSTransitionEnd',
3471    //       'transition'       : 'transitionend'
3472    //     },
3473    //     transEndEventName = transEndEventNames[ Modernizr.prefixed('transition') ];
3474
3475    Modernizr.prefixed      = function(prop, obj, elem){
3476      if(!obj) {
3477        return testPropsAll(prop, 'pfx');
3478      } else {
3479        // Testing DOM property e.g. Modernizr.prefixed('requestAnimationFrame', window) // 'mozRequestAnimationFrame'
3480        return testPropsAll(prop, obj, elem);
3481      }
3482    };
3483    /*>>prefixed*/
3484
3485
3486    /*>>cssclasses*/
3487    // Remove "no-js" class from <html> element, if it exists:
3488    docElement.className = docElement.className.replace(/(^|\s)no-js(\s|$)/, '$1$2') +
3489
3490                            // Add the new classes to the <html> element.
3491                            (enableClasses ? ' js ' + classes.join(' ') : '');
3492    /*>>cssclasses*/
3493
3494    return Modernizr;
3495
3496})(this, this.document);
3497
3498/**
3499 * stacktable.js
3500 * Author & copyright (c) 2012: John Polacek
3501 * CardTable by: Justin McNally (2015)
3502 * Dual MIT & GPL license
3503 *
3504 * Page: http://johnpolacek.github.com/stacktable.js
3505 * Repo: https://github.com/johnpolacek/stacktable.js/
3506 *
3507 * jQuery plugin for stacking tables on small screens
3508 * Requires jQuery version 1.7 or above
3509 *
3510 */
3511;(function($) {
3512  $.fn.cardtable = function(options) {
3513    var $tables = this,
3514        defaults = {headIndex:0},
3515        settings = $.extend({}, defaults, options),
3516        headIndex;
3517
3518    // checking the "headIndex" option presence... or defaults it to 0
3519    if(options && options.headIndex)
3520      headIndex = options.headIndex;
3521    else
3522      headIndex = 0;
3523
3524    return $tables.each(function() {
3525      var $table = $(this);
3526      if ($table.hasClass('stacktable')) {
3527        return;
3528      }
3529      var table_css = $(this).prop('class');
3530      var $stacktable = $('<div></div>');
3531      if (typeof settings.myClass !== 'undefined') $stacktable.addClass(settings.myClass);
3532      var markup = '';
3533      var $caption, $topRow, headMarkup, bodyMarkup, tr_class;
3534
3535      $table.addClass('stacktable large-only');
3536      $caption = $table.find("caption").clone();
3537      $topRow = $table.find('tr').eq(0);
3538
3539      // using rowIndex and cellIndex in order to reduce ambiguity
3540      $table.find('tbody tr').each(function() {
3541
3542        // declaring headMarkup and bodyMarkup, to be used for separately head and body of single records
3543        headMarkup = '';
3544        bodyMarkup = '';
3545        tr_class = $(this).prop('class');
3546        // for the first row, "headIndex" cell is the head of the table
3547        // for the other rows, put the "headIndex" cell as the head for that row
3548        // then iterate through the key/values
3549        $(this).find('td,th').each(function(cellIndex) {
3550          if ($(this).html() !== ''){
3551            bodyMarkup += '<tr class="' + tr_class +'">';
3552            if ($topRow.find('td,th').eq(cellIndex).html()){
3553              bodyMarkup += '<td class="st-key">
vendor: 4,697 bytes, lines 3553-3665
3553'+$topRow.find('td,th').eq(cellIndex).html()+'</td>';
3554            } else {
3555              bodyMarkup += '<td class="st-key"></td>';
3556            }
3557            bodyMarkup += '<td class="st-val '+$(this).prop('class')  +'">'+$(this).html()+'</td>';
3558            bodyMarkup += '</tr>';
3559          }
3560        });
3561
3562        markup += '<table class=" '+ table_css +' stacktable small-only"><tbody>' + headMarkup + bodyMarkup + '</tbody></table>';
3563      });
3564
3565      $table.find('tfoot tr td').each(function(rowIndex,value) {
3566        if ($.trim($(value).text()) !== '') {
3567          markup += '<table class="'+ table_css + ' stacktable small-only"><tbody><tr><td>' + $(value).html() + '</td></tr></tbody></table>';
3568        }
3569      });
3570
3571      $stacktable.prepend($caption);
3572      $stacktable.append($(markup));
3573      $table.before($stacktable);
3574    });
3575  };
3576
3577  $.fn.stacktable = function(options) {
3578    var $tables = this,
3579        defaults = {headIndex:0},
3580        settings = $.extend({}, defaults, options),
3581        headIndex;
3582
3583    // checking the "headIndex" option presence... or defaults it to 0
3584    if(options && options.headIndex)
3585      headIndex = options.headIndex;
3586    else
3587      headIndex = 0;
3588
3589    return $tables.each(function() {
3590      var table_css = $(this).prop('class');
3591      var $stacktable = $('<table class="'+ table_css +' stacktable small-only"><tbody></tbody></table>');
3592      if (typeof settings.myClass !== 'undefined') $stacktable.addClass(settings.myClass);
3593      var markup = '';
3594      var $table, $caption, $topRow, headMarkup, bodyMarkup, tr_class;
3595
3596      $table = $(this);
3597      $table.addClass('stacktable large-only');
3598      $caption = $table.find("caption").clone();
3599      $topRow = $table.find('tr').eq(0);
3600
3601      // using rowIndex and cellIndex in order to reduce ambiguity
3602      $table.find('tr').each(function(rowIndex) {
3603
3604        // declaring headMarkup and bodyMarkup, to be used for separately head and body of single records
3605        headMarkup = '';
3606        bodyMarkup = '';
3607        tr_class = $(this).prop('class');
3608        // for the first row, "headIndex" cell is the head of the table
3609        if (rowIndex === 0) {
3610          // the main heading goes into the markup variable
3611          markup += '<tr class=" '+tr_class +' "><th class="st-head-row st-head-row-main" colspan="2">'+$(this).find('th,td').eq(headIndex).html()+'</th></tr>';
3612        }
3613        else {
3614          // for the other rows, put the "headIndex" cell as the head for that row
3615          // then iterate through the key/values
3616          $(this).find('td,th').each(function(cellIndex) {
3617            if (cellIndex === headIndex) {
3618              headMarkup = '<tr class="'+ tr_class+'"><th class="st-head-row" colspan="2">'+$(this).html()+'</th></tr>';
3619            } else {
3620              if ($(this).html() !== ''){
3621                bodyMarkup += '<tr class="' + tr_class +'">';
3622                if ($topRow.find('td,th').eq(cellIndex).html()){
3623                  bodyMarkup += '<td class="st-key">'+$topRow.find('td,th').eq(cellIndex).html()+'</td>';
3624                } else {
3625                  bodyMarkup += '<td class="st-key"></td>';
3626                }
3627                bodyMarkup += '<td class="st-val '+$(this).prop('class')  +'">'+$(this).html()+'</td>';
3628                bodyMarkup += '</tr>';
3629              }
3630            }
3631          });
3632
3633          markup += headMarkup + bodyMarkup;
3634        }
3635      });
3636
3637      $stacktable.prepend($caption);
3638      $stacktable.append($(markup));
3639      $table.before($stacktable);
3640    });
3641  };
3642
3643 $.fn.stackcolumns = function(options) {
3644    var $tables = this,
3645        defaults = {},
3646        settings = $.extend({}, defaults, options);
3647
3648    return $tables.each(function() {
3649      var $table = $(this);
3650      var num_cols = $table.find('tr').eq(0).find('td,th').length; //first table <tr> must not contain colspans, or add sum(colspan-1) here.
3651      if(num_cols<3) //stackcolumns has no effect on tables with less than 3 columns
3652        return;
3653
3654      var $stackcolumns = $('<table class="stacktable small-only"></table>');
3655      if (typeof settings.myClass !== 'undefined') $stackcolumns.addClass(settings.myClass);
3656      $table.addClass('stacktable large-only');
3657      var tb = $('<tbody></tbody>');
3658      var col_i = 1; //col index starts at 0 -> start copy at second column.
3659
3660      while (col_i < num_cols) {
3661        $table.find('tr').each(function(index) {
3662          var tem = $('<tr></tr>'); // todo opt. copy styles of $this; todo check if parent is thead or tfoot to handle accordingly
3663          if(index === 0) tem.addClass("st-head-row st-head-row-main");
3664          var first = $(this).find('td,th').eq(0).clone().addClass("st-key");
3665          var target = col_i;
3666          // if colspan apply, recompute target for second cell.
3667          if ($(this).find("*[colspan]").length) {
3668            var i =0;
3669            $(this).find('td,th').each(function() {
3670                var cs = $(this).attr("colspan");
3671                if (cs) {
3672                  cs = parseInt(cs, 10);
3673                  target -= cs-1;
3674                  if ((i+cs) > (col_i)) //out of current bounds
3675                    target += i + cs - col_i -1;
3676                  i += cs;
3677                }
3678                else
3679                  i++;
3680
3681                if (i > col_i)
3682                  return false; //target is set; break.
3683            });
3684          }
3685          var second = $(this).find('td,th').eq(target).clone().addClass("st-val").removeAttr("colspan");
3686          tem.append(first, second);
3687          tb.append(tem);
3688        });
3689        ++col_i;
3690      }
3691
3692      $stackcolumns.append($(tb));
3693      $table.before($stackcolumns);
3694    });
3695  };
3696
3697}(jQuery));
3698
3699/**
3700* jquery.matchHeight.js master
3701* http://brm.io/jquery-match-height/
3702* License: MIT
3703*/
3704
3705;(function($) {
3706    /*
3707    *  internal
3708    */
3709
3710    var _previousResizeWidth = -1,
3711        _updateTimeout = -1;
3712
3713    /*
3714    *  _parse
3715    *  value parse utility function
3716    */
3717
3718    var _parse = function(value) {
3719        // parse value and convert NaN to 0
3720        return parseFloat(value) || 0;
3721    };
3722
3723    /*
3724    *  _rows
3725    *  utility function returns array of jQuery selections representing each row
3726    *  (as displayed after float wrapping applied by browser)
3727    */
3728
3729    var _rows = function(elements) {
3730        var tolerance = 1,
3731            $elements = $(elements),
3732            lastTop = null,
3733            rows = [];
3734
3735        // group elements by their top position
3736        $elements.each(function(){
3737            var $that = $(this),
3738                top = $that.offset().top - _parse($that.css('margin-top')),
3739                lastRow = rows.length > 0 ? rows[rows.length - 1] : null;
3740
3741            if (lastRow === null) {
3742                // first item on the row, so just push it
3743                rows.push($that);
3744            } else {
3745                // if the row top is the same, add to the row group
3746                if (Math.floor(Math.abs(lastTop - top)) <= tolerance) {
3747                    rows[rows.length - 1] = lastRow.add($that);
3748                } else {
3749                    // otherwise start a new row group
3750                    rows.push($that);
3751                }
3752            }
3753
3754            // keep track of the last row top
3755            lastTop = top;
3756        });
3757
3758        return rows;
3759    };
3760
3761    /*
3762    *  _parseOptions
3763    *  handle plugin options
3764    */
3765
3766    var _parseOptions = function(options) {
3767        var opts = {
3768            byRow: true,
3769            property: 'height',
3770            target: null,
3771            remove: false
3772        };
3773
3774        if (typeof options === 'object') {
3775            return $.extend(opts, options);
3776        }
3777
3778        if (typeof options === 'boolean') {
3779            opts.byRow = options;
3780        } else if (options === 'remove') {
3781            opts.remove = true;
3782        }
3783
3784        return opts;
3785    };
3786
3787    /*
3788    *  matchHeight
3789    *  plugin definition
3790    */
3791
3792    var matchHeight = $.fn.matchHeight = function(options) {
3793        var opts = _parseOptions(options);
3794
3795        // handle remove
3796        if (opts.remove) {
3797            var that = this;
3798
3799            // remove fixed height from all selected elements
3800            this.css(opts.property, '');
3801
3802            // remove selected elements from all groups
3803            $.each(matchHeight._groups, function(key, group) {
3804                group.elements = group.elements.not(that);
3805            });
3806
3807            // TODO: cleanup empty groups
3808
3809            return this;
3810        }
3811
3812        if (this.length <= 1 && !opts.target) {
3813            return this;
3814        }
3815
3816        // keep track of this group so we can re-apply later on load and resize events
3817        matchHeight._groups.push({
3818            elements: this,
3819            options: opts
3820        });
3821
3822        // match each element's height to the tallest element in the selection
3823        matchHeight._apply(this, opts);
3824
3825        return this;
3826    };
3827
3828    /*
3829    *  plugin global options
3830    */
3831
3832    matchHeight._groups = [];
3833    matchHeight._throttle = 80;
3834    matchHeight._maintainScroll = false;
3835    matchHeight._beforeUpdate = null;
3836    matchHeight._afterUpdate = null;
3837
3838    /*
3839    *  matchHeight._apply
3840    *  apply matchHeight to given elements
3841    */
3842
3843    matchHeight._apply = function(elements, options) {
3844        var opts = _parseOptions(options),
3845            $elements = $(elements),
3846            rows = [$elements];
3847
3848        // take note of scroll position
3849        var scrollTop = $(window).scrollTop(),
3850            htmlHeight = $('html').outerHeight(true);
3851
3852        // get hidden parents
3853        var $hiddenParents = $elements.parents().filter(':hidden');
3854
3855        // cache the original inline style
3856        $hiddenParents.each(function() {
3857            var $that = $(this);
3858            $that.data('style-cache', $that.attr('style'));
3859        });
3860
3861        // temporarily must force hidden parents visible
3862        $hiddenParents.css('display', 'block');
3863
3864        // get rows if using byRow, otherwise assume one row
3865        if (opts.byRow && !opts.target) {
3866
3867            // must first force an arbitrary equal height so floating elements break evenly
3868            $elements.each(function() {
3869                var $that = $(this),
3870                    display = $that.css('display') === 'inline-block' ? 'inline-block' : 'block';
3871
3872                // cache the original inline style
3873                $that.data('style-cache', $that.attr('style'));
3874
3875                $that.css({
3876                    'display': display,
3877                    'padding-top': '0',
3878                    'padding-bottom': '0',
3879                    'margin-top': '0',
3880                    'margin-bottom': '0',
3881                    'border-top-width': '0',
3882                    'border-bottom-width': '0',
3883                    'height': '100px'
3884                });
3885            });
3886
3887            // get the array of rows (based on element top position)
3888            rows = _rows($elements);
3889
3890            // revert original inline styles
3891            $elements.each(function() {
3892                var $that = $(this);
3893                $that.attr('style', $that.data('style-cache') || '');
3894            });
3895        }
3896
3897        $.each(rows, function(key, row) {
3898            var $row = $(row),
3899                targetHeight = 0;
3900
3901            if (!opts.target) {
3902                // skip apply to rows with only one item
3903                if (opts.byRow && $row.length <= 1) {
3904                    $row.css(opts.property, '');
3905                    return;
3906                }
3907
3908                // iterate the row and find the max height
3909                $row.each(function(){
3910                    var $that = $(this),
3911                        display = $that.css('display') === 'inline-block' ? 'inline-block' : 'block';
3912
3913                    // ensure we get the correct actual height (and not a previously set height value)
3914                    var css = { 'display': display };
3915                    css[opts.property] = '';
3916                    $that.css(css);
3917
3918                    // find the max height (including padding, but not margin)
3919                    if ($that.outerHeight(false) > targetHeight) {
3920                        targetHeight = $that.outerHeight(false);
3921                    }
3922
3923                    // revert display block
3924                    $that.css('display', '');
3925                });
3926            } else {
3927                // if target set, use the height of the target element
3928                targetHeight = opts.target.outerHeight(false);
3929            }
3930
3931            // iterate the row and apply the height to all elements
3932            $row.each(function(){
3933                var $that = $(this),
3934                    verticalPadding = 0;
3935
3936                // don't apply to a target
3937                if (opts.target && $that.is(opts.target)) {
3938                    return;
3939                }
3940
3941                // handle padding and border correctly (required when not using border-box)
3942                if ($that.css('box-sizing') !== 'border-box') {
3943                    verticalPadding += _parse($that.css('border-top-width')) + _parse($that.css('border-bottom-width'));
3944                    verticalPadding += _parse($that.css('padding-top')) + _parse($that.css('padding-bottom'));
3945                }
3946
3947                // set the height (accounting for padding and border)
3948                $that.css(opts.property, targetHeight - verticalPadding);
3949            });
3950        });
3951
3952        // revert hidden parents
3953        $hiddenParents.each(function() {
3954            var $that = $(this);
3955            $that.attr('style', $that.data('style-cache') || null);
3956        });
3957
3958        // restore scroll position if enabled
3959        if (matchHeight._maintainScroll) {
3960            $(window).scrollTop((scrollTop / htmlHeight) * $('html').outerHeight(true));
3961        }
3962
3963        return this;
3964    };
3965
3966    /*
3967    *  matchHeight._applyDataApi
3968    *  applies matchHeight to all elements with a data-match-height attribute
3969    */
3970
3971    matchHeight._applyDataApi = function() {
3972        var groups = {};
3973
3974        // generate groups by their groupId set by elements using data-match-height
3975        $('[data-match-height], [data-mh]').each(function() {
3976            var $this = $(this),
3977                groupId = $this.attr('data-mh') || $this.attr('data-match-height');
3978
3979            if (groupId in groups) {
3980                groups[groupId] = groups[groupId].add($this);
3981            } else {
3982                groups[groupId] = $this;
3983            }
3984        });
3985
3986        // apply matchHeight to each group
3987        $.each(groups, function() {
3988            this.matchHeight(true);
3989        });
3990    };
3991
3992    /*
3993    *  matchHeight._update
3994    *  updates matchHeight on all current groups with their correct options
3995    */
3996
3997    var _update = function(event) {
3998        if (matchHeight._beforeUpdate) {
3999            matchHeight._beforeUpdate(event, matchHeight._groups);
4000        }
4001
4002        $.each(matchHeight._groups, function() {
4003            matchHeight._apply(this.elements, this.options);
4004        });
4005
4006        if (matchHeight._afterUpdate) {
4007            matchHeight._afterUpdate(event, matchHeight._groups);
4008        }
4009    };
4010
4011    matchHeight._update = function(throttle, event) {
4012        // prevent update if fired from a resize event
4013        // where the viewport width hasn't actually changed
4014        // fixes an event looping bug in IE8
4015        if (event && event.type === 'resize') {
4016            var windowWidth = $(window).width();
4017            if (windowWidth === _previousResizeWidth) {
4018                return;
4019            }
4020            _previousResizeWidth = windowWidth;
4021        }
4022
4023        // throttle updates
4024        if (!throttle) {
4025            _update(event);
4026        } else if (_updateTimeout === -1) {
4027            _updateTimeout = setTimeout(function() {
4028                _update(event);
4029                _updateTimeout = -1;
4030            }, matchHeight._throttle);
4031        }
4032    };
4033
4034    /*
4035    *  bind events
4036    */
4037
4038    // apply on DOM ready event
4039    $(matchHeight._applyDataApi);
4040
4041    // update heights on load and resize events
4042    $(window).bind('load', function(event) {
4043        matchHeight._update(false, event);
4044    });
4045
4046    // throttled update heights on resize events
4047    $(window).bind('resize orientationchange', function(event) {
4048        matchHeight._update(true, event);
4049    });
4050
4051})(jQuery);
4052
4053/*!
4054 * classie v1.0.1
4055 * class helper functions
4056 * from bonzo https://github.com/ded/bonzo
4057 * MIT license
4058 * 
4059 * classie.has( elem, 'my-class' ) -> true/false
4060 * classie.add( elem, 'my-new-class' )
4061 * classie.remove( elem, 'my-unwanted-class' )
4062 * classie.toggle( elem, 'my-class' )
4063 */
4064
4065/*jshint browser: true, strict: true, undef: true, unused: true */
4066/*global define: false, module: false */
4067
4068( function( window ) {
4069
4070'use strict';
4071
4072// class helper functions from bonzo https://github.com/ded/bonzo
4073
4074function classReg( className ) {
4075  return new RegExp("(^|\\s+)" + className + "(\\s+|$)");
4076}
4077
4078// classList support for class management
4079// altho to be fair, the api sucks because it won't accept multiple classes at once
4080var hasClass, addClass, removeClass;
4081
4082if ( 'classList' in document.documentElement ) {
4083  hasClass = function( elem, c ) {
4084    return elem.classList.contains( c );
4085  };
4086  addClass = function( elem, c ) {
4087    elem.classList.add( c );
4088  };
4089  removeClass = function( elem, c ) {
4090    elem.classList.remove( c );
4091  };
4092}
4093else {
4094  hasClass = function( elem, c ) {
4095    return classReg( c ).test( elem.className );
4096  };
4097  addClass = function( elem, c ) {
4098    if ( !hasClass( elem, c ) ) {
4099      elem.className = elem.className + ' ' + c;
4100    }
4101  };
4102  removeClass = function( elem, c ) {
4103    elem.className = elem.className.replace( classReg( c ), ' ' );
4104  };
4105}
4106
4107function toggleClass( elem, c ) {
4108  var fn = hasClass( elem, c ) ? removeClass : addClass;
4109  fn( elem, c );
4110}
4111
4112var classie = {
4113  // full names
4114  hasClass: hasClass,
4115  addClass: addClass,
4116  removeClass: removeClass,
4117  toggleClass: toggleClass,
4118  // short names
4119  has: hasClass,
4120  add: addClass,
4121  remove: removeClass,
4122  toggle: toggleClass
4123};
4124
4125// transport
4126if ( typeof define === 'function' && define.amd ) {
4127  // AMD
4128  define( classie );
4129} else if ( typeof exports === 'object' ) {
4130  // CommonJS
4131  module.exports = classie;
4132} else {
4133  // browser global
4134  window.classie = classie;
4135}
4136
4137})( window );
4138
4139/**
4140 * sidebarEffects.js v1.0.0
4141 * http://www.codrops.com
4142 *
4143 * Licensed under the MIT license.
4144 * http://www.opensource.org/licenses/mit-license.php
4145 * 
4146 * Copyright 2013, Codrops
4147 * http://www.codrops.com
4148 */
4149 var SidebarMenuEffects = (function() {
4150
4151 	function hasParentClass(e, classname) {
4152		if (e === document) return false;
4153		if (classie.has(e, classname)) {
4154			return true;
4155		}
4156		return e.parentNode && hasParentClass(e.parentNode, classname);
4157	}
4158
4159	// http://coveroverflow.com/a/11381730/989439
4160	function mobilecheck() {
4161		var check = false;
4162		(function(a){if(/(android|ipad|playbook|silk|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera);
4163		return check;
4164	}
4165
4166	function init() {
4167
4168		var container = document.getElementById('st-container'),
4169	            	reset = document.getElementById('closeMenu'),
4170			buttons = Array.prototype.slice.call(document.querySelectorAll('#st-trigger-effects > button')),
4171			sidebarClosers = Array.prototype.slice.call(document.querySelectorAll('.st-menu .close-sidebar')),
4172			// event type (if mobile use touch events)
4173			actionStartedEvent = mobilecheck() ? 'touchstart' : 'click',
4174			actionCompletedEvent = mobilecheck() ? 'touchend' : 'click',
4175			resetMenu = function() {
4176				classie.remove(container, 'st-menu-open');
4177			},
4178			bodyClickFn = function(evt) {
4179				if (!hasParentClass(evt.target, 'st-menu')) {
4180					resetMenu();
4181					document.removeEventListener(actionStartedEvent, bodyClickFn);
4182				}
4183			},
4184			resetClickFn = function(evt) {
4185				if (evt.target == reset) {
4186					resetMenu();
4187					document.removeEventListener(actionStartedEvent, bodyClickFn);
4188				}
4189			};
4190
4191		buttons.forEach(function(el, i) {
4192			var effect = el.getAttribute('data-effect');
4193
4194			el.addEventListener(actionStartedEvent, function(ev) {
4195				ev.stopPropagation();
4196				ev.preventDefault();
4197				container.className = 'st-container'; // clear
4198				classie.add(container, effect);
4199				setTimeout(function() {
4200					classie.add(container, 'st-menu-open');
4201				}, 25);
4202				document.addEventListener(actionStartedEvent, bodyClickFn);
4203				document.addEventListener(actionStartedEvent, resetClickFn);
4204			});
4205		} );
4206
4207		sidebarClosers.forEach(function(el, i) {
4208			el.addEventListener(actionCompletedEvent, function(ev) {
4209				resetMenu();
4210			});
4211		});
4212	}
4213
4214	init();
4215
4216})();
4217
4218/*! Magnific Popup - v1.0.1 - 2015-12-30
4219* http://dimsemenov.com/plugins/magnific-popup/
4220* Copyright (c) 2015 Dmitry Semenov; */
4221!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a("object"==typeof exports?require("jquery"):window.jQuery||window.Zepto)}(function(a){var b,c,d,e,f,g,h="Close",i="BeforeClose",j="AfterClose",k="BeforeAppend",l="MarkupParse",m="Open",n="Change",o="mfp",p="."+o,q="mfp-ready",r="mfp-removing",s="mfp-prevent-close",t=function(){},u=!!window.jQuery,v=a(window),w=function(a,c){b.ev.on(o+a+p,c)},x=function(b,c,d,e){var f=document.createElement("div");return f.className="mfp-"+b,d&&(f.innerHTML=d),e?c&&c.appendChild(f):(f=a(f),c&&f.appendTo(c)),f},y=function(c,d){b.ev.triggerHandler(o+c,d),b.st.callbacks&&(c=c.charAt(0).toLowerCase()+c.slice(1),b.st.callbacks[c]&&b.st.callbacks[c].apply(b,a.isArray(d)?d:[d]))},z=function(c){return c===g&&b.currTemplate.closeBtn||(b.currTemplate.closeBtn=a(b.st.closeMarkup.replace("%title%",b.st.tClose)),g=c),b.currTemplate.closeBtn},A=function(){a.magnificPopup.instance||(b=new t,b.init(),a.magnificPopup.instance=b)},B=function(){var a=document.createElement("p").style,b=["ms","O","Moz","Webkit"];if(void 0!==a.transition)return!0;for(;b.length;)if(b.pop()+"Transition"in a)return!0;return!1};t.prototype={constructor:t,init:function(){var c=navigator.appVersion;b.isIE7=-1!==c.indexOf("MSIE 7."),b.isIE8=-1!==c.indexOf("MSIE 8."),b.isLowIE=b.isIE7||b.isIE8,b.isAndroid=/android/gi.test(c),b.isIOS=/iphone|ipad|ipod/gi.test(c),b.supportsTransition=B(),b.probablyMobile=b.isAndroid||b.isIOS||/(Opera Mini)|Kindle|webOS|BlackBerry|(Opera Mobi)|(Windows Phone)|IEMobile/i.test(navigator.userAgent),d=a(document),b.popupsCache={}},open:function(c){var e;if(c.isObj===!1){b.items=c.items.toArray(),b.index=0;var g,h=c.items;for(e=0;e<h.length;e++)if(g=h[e],g.parsed&&(g=g.el[0]),g===c.el[0]){b.index=e;break}}else b.items=a.isArray(c.items)?c.items:[c.items],b.index=c.index||0;if(b.isOpen)return void b.updateItemHTML();b.types=[],f="",c.mainEl&&c.mainEl.length?b.ev=c.mainEl.eq(0):b.ev=d,c.key?(b.popupsCache[c.key]||(b.popupsCache[c.key]={}),b.currTemplate=b.popupsCache[c.key]):b.currTemplate={},b.st=a.extend(!0,{},a.magnificPopup.defaults,c),b.fixedContentPos="auto"===b.st.fixedContentPos?!b.probablyMobile:b.st.fixedContentPos,b.st.modal&&(b.st.closeOnContentClick=!1,b.st.closeOnBgClick=!1,b.st.showCloseBtn=!1,b.st.enableEscapeKey=!1),b.bgOverlay||(b.bgOverlay=x("bg").on("click"+p,function(){b.close()}),b.wrap=x("wrap").attr("tabindex",-1).on("click"+p,function(a){b._checkIfClose(a.target)&&b.close()}),b.container=x("container",b.wrap)),b.contentContainer=x("content"),b.st.preloader&&(b.preloader=x("preloader",b.container,b.st.tLoading));var i=a.magnificPopup.modules;for(e=0;e<i.length;e++){var j=i[e];j=j.charAt(0).toUpperCase()+j.slice(1),b["init"+j].call(b)}y("BeforeOpen"),b.st.showCloseBtn&&(b.st.closeBtnInside?(w(l,function(a,b,c,d){c.close_replaceWith=z(d.type)}),f+=" mfp-close-btn-in"):b.wrap.append(z())),b.st.alignTop&&(f+=" mfp-align-top"),b.fixedContentPos?b.wrap.css({overflow:b.st.overflowY,overflowX:"hidden",overflowY:b.st.overflowY}):b.wrap.css({top:v.scrollTop(),position:"absolute"}),(b.st.fixedBgPos===!1||"auto"===b.st.fixedBgPos&&!b.fixedContentPos)&&b.bgOverlay.css({height:d.height(),position:"absolute"}),b.st.enableEscapeKey&&d.on("keyup"+p,function(a){27===a.keyCode&&b.close()}),v.on("resize"+p,function(){b.updateSize()}),b.st.closeOnContentClick||(f+=" mfp-auto-cursor"),f&&b.wrap.addClass(f);var k=b.wH=v.height(),n={};if(b.fixedContentPos&&b._hasScrollBar(k)){var o=b._getScrollbarSize();o&&(n.marginRight=o)}b.fixedContentPos&&(b.isIE7?a("body, html").css("overflow","hidden"):n.overflow="hidden");var r=b.st.mainClass;return b.isIE7&&(r+=" mfp-ie7"),r&&b._addClassToMFP(r),b.updateItemHTML(),y("BuildControls"),a("html").css(n),b.bgOverlay.add(b.wrap).prependTo(b.st.prependTo||a(document.body)),b._lastFocusedEl=document.activeElement,setTimeout(function(){b.content?(b._addClassToMFP(q),b._setFocus()):b.bgOverlay.addClass(q),d.on("focusin"+p,b._onFocusIn)},16),b.isOpen=!0,b.updateSize(k),y(m),c},close:function(){b.isOpen&&(y(i),b.isOpen=!1,b.st.removalDelay&&!b.isLowIE&&b.supportsTransition?(b._addClassToMFP(r),setTimeout(function(){b._close()},b.st.removalDelay)):b._close())},_close:function(){y(h);var c=r+" "+q+" ";if(b.bgOverlay.detach(),b.wrap.detach(),b.container.empty(),b.st.mainClass&&(c+=b.st.mainClass+" "),b._removeClassFromMFP(c),b.fixedContentPos){var e={marginRight:""};b.isIE7?a("body, html").css("overflow",""):e.overflow="",a("html").css(e)}d.off("keyup"+p+" focusin"+p),b.ev.off(p),b.wrap.attr("class","mfp-wrap").removeAttr("style"),b.bgOverlay.attr("class","mfp-bg"),b.container.attr("class","mfp-container"),!b.st.showCloseBtn||b.st.closeBtnInside&&b.currTemplate[b.currItem.type]!==!0||b.currTemplate.closeBtn&&b.currTemplate.closeBtn.detach(),b.st.autoFocusLast&&b._lastFocusedEl&&a(b._lastFocusedEl).focus(),b.currItem=null,b.content=null,b.currTemplate=null,b.prevHeight=0,y(j)}
4221,updateSize:function(a){if(b.isIOS){var c=document.documentElement.clientWidth/window.innerWidth,d=window.innerHeight*c;b.wrap.css("height",d),b.wH=d}else b.wH=a||v.height();b.fixedContentPos||b.wrap.css("height",b.wH),y("Resize")},updateItemHTML:function(){var c=b.items[b.index];b.contentContainer.detach(),b.content&&b.content.detach(),c.parsed||(c=b.parseEl(b.index));var d=c.type;if(y("BeforeChange",[b.currItem?b.currItem.type:"",d]),b.currItem=c,!b.currTemplate[d]){var f=b.st[d]?b.st[d].markup:!1;y("FirstMarkupParse",f),f?b.currTemplate[d]=a(f):b.currTemplate[d]=!0}e&&e!==c.type&&b.container.removeClass("mfp-"+e+"-holder");var g=b["get"+d.charAt(0).toUpperCase()+d.slice(1)](c,b.currTemplate[d]);b.appendContent(g,d),c.preloaded=!0,y(n,c),e=c.type,b.container.prepend(b.contentContainer),y("AfterChange")},appendContent:function(a,c){b.content=a,a?b.st.showCloseBtn&&b.st.closeBtnInside&&b.currTemplate[c]===!0?b.content.find(".mfp-close").length||b.content.append(z()):b.content=a:b.content="",y(k),b.container.addClass("mfp-"+c+"-holder"),b.contentContainer.append(b.content)},parseEl:function(c){var d,e=b.items[c];if(e.tagName?e={el:a(e)}:(d=e.type,e={data:e,src:e.src}),e.el){for(var f=b.types,g=0;g<f.length;g++)if(e.el.hasClass("mfp-"+f[g])){d=f[g];break}e.src=e.el.attr("data-mfp-src"),e.src||(e.src=e.el.attr("href"))}return e.type=d||b.st.type||"inline",e.index=c,e.parsed=!0,b.items[c]=e,y("ElementParse",e),b.items[c]},addGroup:function(a,c){var d=function(d){d.mfpEl=this,b._openClick(d,a,c)};c||(c={});var e="click.magnificPopup";c.mainEl=a,c.items?(c.isObj=!0,a.off(e).on(e,d)):(c.isObj=!1,c.delegate?a.off(e).on(e,c.delegate,d):(c.items=a,a.off(e).on(e,d)))},_openClick:function(c,d,e){var f=void 0!==e.midClick?e.midClick:a.magnificPopup.defaults.midClick;if(f||!(2===c.which||c.ctrlKey||c.metaKey||c.altKey||c.shiftKey)){var g=void 0!==e.disableOn?e.disableOn:a.magnificPopup.defaults.disableOn;if(g)if(a.isFunction(g)){if(!g.call(b))return!0}else if(v.width()<g)return!0;c.type&&(c.preventDefault(),b.isOpen&&c.stopPropagation()),e.el=a(c.mfpEl),e.delegate&&(e.items=d.find(e.delegate)),b.open(e)}},updateStatus:function(a,d){if(b.preloader){c!==a&&b.container.removeClass("mfp-s-"+c),d||"loading"!==a||(d=b.st.tLoading);var e={status:a,text:d};y("UpdateStatus",e),a=e.status,d=e.text,b.preloader.html(d),b.preloader.find("a").on("click",function(a){a.stopImmediatePropagation()}),b.container.addClass("mfp-s-"+a),c=a}},_checkIfClose:function(c){if(!a(c).hasClass(s)){var d=b.st.closeOnContentClick,e=b.st.closeOnBgClick;if(d&&e)return!0;if(!b.content||a(c).hasClass("mfp-close")||b.preloader&&c===b.preloader[0])return!0;if(c===b.content[0]||a.contains(b.content[0],c)){if(d)return!0}else if(e&&a.contains(document,c))return!0;return!1}},_addClassToMFP:function(a){b.bgOverlay.addClass(a),b.wrap.addClass(a)},_removeClassFromMFP:function(a){this.bgOverlay.removeClass(a),b.wrap.removeClass(a)},_hasScrollBar:function(a){return(b.isIE7?d.height():document.body.scrollHeight)>(a||v.height())},_setFocus:function(){(b.st.focus?b.content.find(b.st.focus).eq(0):b.wrap).focus()},_onFocusIn:function(c){return c.target===b.wrap[0]||a.contains(b.wrap[0],c.target)?void 0:(b._setFocus(),!1)},_parseMarkup:function(b,c,d){var e;d.data&&(c=a.extend(d.data,c)),y(l,[b,c,d]),a.each(c,function(a,c){if(void 0===c||c===!1)return!0;if(e=a.split("_"),e.length>1){var d=b.find(p+"-"+e[0]);if(d.length>0){var f=e[1];"replaceWith"===f?d[0]!==c[0]&&d.replaceWith(c):"img"===f?d.is("img")?d.attr("src",c):d.replaceWith('<img src="'+c+'" class="'+d.attr("class")+'" />'):d.attr(e[1],c)}}else b.find(p+"-"+a).html(c)})},_getScrollbarSize:function(){if(void 0===b.scrollbarSize){var a=document.createElement("div");a.style.cssText="width: 99px; height: 99px; overflow: scroll; position: absolute; top: -9999px;",document.body.appendChild(a),b.scrollbarSize=a.offsetWidth-a.clientWidth,document.body.removeChild(a)}return b.scrollbarSize}},a.magnificPopup={instance:null,proto:t.prototype,modules:[],open:function(b,c){return A(),b=b?a.extend(!0,{},b):{},b.isObj=!0,b.index=c||0,this.instance.open(b)},close:function(){return a.magnificPopup.instance&&a.magnificPopup.instance.close()},registerModule:function(b,c){c.options&&(a.magnificPopup.defaults[b]=c.options),a.extend(this.proto,c.proto),this.modules.push(b)},defaults:{disableOn:0,key:null,midClick:!1,mainClass:"",preloader:!0,focus:"",closeOnContentClick:!1,closeOnBgClick:!0,closeBtnInside:!0,showCloseBtn:!0,enableEscapeKey:!0,modal:!1,alignTop:!1,removalDelay:0,prependTo:null,fixedContentPos:"auto",fixedBgPos:"auto",overflowY:"auto",closeMarkup:'<button title="%title%" type="button" class="mfp-close">&#215;</button>',tClose:"Close (Esc)",tLoading:"Loading...",autoFocusLast:!0}},a.fn.magnificPopup=function(c){A();var d=a(this);if("string"==typeof c)if("open"===c){var e,f=u?d.data("magnificPopup"):d[0].magnificPopup,g=parseInt(arguments[1],10)||0;f.items?e=f.items[g]:(e=d,f.delegate&&(e=e.find(f.delegate)),e=e.eq(g)),b._openClick({mfpEl:e},d,f)}else b.isOpen&&b[c].apply(b,Array.prototype.slice.call(arguments,1
4221));else c=a.extend(!0,{},c),u?d.data("magnificPopup",c):d[0].magnificPopup=c,b.addGroup(d,c);return d};var C,D,E,F="inline",G=function(){E&&(D.after(E.addClass(C)).detach(),E=null)};a.magnificPopup.registerModule(F,{options:{hiddenClass:"hide",markup:"",tNotFound:"Content not found"},proto:{initInline:function(){b.types.push(F),w(h+"."+F,function(){G()})},getInline:function(c,d){if(G(),c.src){var e=b.st.inline,f=a(c.src);if(f.length){var g=f[0].parentNode;g&&g.tagName&&(D||(C=e.hiddenClass,D=x(C),C="mfp-"+C),E=f.after(D).detach().removeClass(C)),b.updateStatus("ready")}else b.updateStatus("error",e.tNotFound),f=a("<div>");return c.inlineElement=f,f}return b.updateStatus("ready"),b._parseMarkup(d,{},c),d}}});var H,I="ajax",J=function(){H&&a(document.body).removeClass(H)},K=function(){J(),b.req&&b.req.abort()};a.magnificPopup.registerModule(I,{options:{settings:null,cursor:"mfp-ajax-cur",tError:'<a href="%url%">The content</a> could not be loaded.'},proto:{initAjax:function(){b.types.push(I),H=b.st.ajax.cursor,w(h+"."+I,K),w("BeforeChange."+I,K)},getAjax:function(c){H&&a(document.body).addClass(H),b.updateStatus("loading");var d=a.extend({url:c.src,success:function(d,e,f){var g={data:d,xhr:f};y("ParseAjax",g),b.appendContent(a(g.data),I),c.finished=!0,J(),b._setFocus(),setTimeout(function(){b.wrap.addClass(q)},16),b.updateStatus("ready"),y("AjaxContentAdded")},error:function(){J(),c.finished=c.loadError=!0,b.updateStatus("error",b.st.ajax.tError.replace("%url%",c.src))}},b.st.ajax.settings);return b.req=a.ajax(d),""}}});var L,M=function(c){if(c.data&&void 0!==c.data.title)return c.data.title;
4221var d=b.st.image.titleSrc;if(d){if(a.isFunction(d))return d.call(b,c);if(c.el)return c.el.attr(d)||""}return""};a.magnificPopup.registerModule("image",{options:{markup:'<div class="mfp-figure"><div class="mfp-close"></div><figure><div class="mfp-img"></div><figcaption><div class="mfp-bottom-bar"><div class="mfp-title"></div><div class="mfp-counter"></div></div></figcaption></figure></div>',cursor:"mfp-zoom-out-cur",titleSrc:"title",verticalFit:!0,tError:'<a href="%url%">The image</a> could not be loaded.'},proto:{initImage:function(){var c=b.st.image,d=".image";b.types.push("image"),w(m+d,function(){"image"===b.currItem.type&&c.cursor&&a(document.body).addClass(c.cursor)}),w(h+d,function(){c.cursor&&a(document.body).removeClass(c.cursor),v.off("resize"+p)}),w("Resize"+d,b.resizeImage),b.isLowIE&&w("AfterChange",b.resizeImage)},resizeImage:function(){var a=b.currItem;if(a&&a.img&&b.st.image.verticalFit){var c=0;b.isLowIE&&(c=parseInt(a.img.css("padding-top"),10)+parseInt(a.img.css("padding-bottom"),10)),a.img.css("max-height",b.wH-c)}},_onImageHasSize:function(a){a.img&&(a.hasSize=!0,L&&clearInterval(L),a.isCheckingImgSize=!1,y("ImageHasSize",a),a.imgHidden&&(b.content&&b.content.removeClass("mfp-loading"),a.imgHidden=!1))},findImageSize:function(a){var c=0,d=a.img[0],e=function(f){L&&clearInterval(L),L=setInterval(function(){return d.naturalWidth>0?void b._onImageHasSize(a):(c>200&&clearInterval(L),c++,void(3===c?e(10):40===c?e(50):100===c&&e(500)))},f)};e(1)},getImage:function(c,d){var e=0,f=function(){c&&(c.img[0].complete?(c.img.off(".mfploader"),c===b.currItem&&(b._onImageHasSize(c),b.updateStatus("ready")),c.hasSize=!0,c.loaded=!0,y("ImageLoadComplete")):(e++,200>e?setTimeout(f,100):g()))},g=function(){c&&(c.img.off(".mfploader"),c===b.currItem&&(b._onImageHasSize(c),b.updateStatus("error",h.tError.replace("%url%",c.src))),c.hasSize=!0,c.loaded=!0,c.loadError=!0)},h=b.st.image,i=d.find(".mfp-img");if(i.length){var j=document.createElement("img");j.className="mfp-img",c.el&&c.el.find("img").length&&(j.alt=c.el.find("img").attr("alt")),c.img=a(j).on("load.mfploader",f).on("error.mfploader",g),j.src=c.src,i.is("img")&&(c.img=c.img.clone()),j=c.img[0],j.naturalWidth>0?c.hasSize=!0:j.width||(c.hasSize=!1)}return b._parseMarkup(d,{title:M(c),img_replaceWith:c.img},c),b.resizeImage(),c.hasSize?(L&&clearInterval(L),c.loadError?(d.addClass("mfp-loading"),b.updateStatus("error",h.tError.replace("%url%",c.src))):(d.removeClass("mfp-loading"),b.updateStatus("ready")),d):(b.updateStatus("loading"),c.loading=!0,c.hasSize||(c.imgHidden=!0,d.addClass("mfp-loading"),b.findImageSize(c)),d)}}});var N,O=function(){return void 0===N&&(N=void 0!==document.createElement("p").style.MozTransform),N};a.magnificPopup.registerModule("zoom",{options:{enabled:!1,easing:"ease-in-out",duration:300,opener:function(a){return a.is("img")?a:a.find("img")}},proto:{initZoom:function(){var a,c=b.st.zoom,d=".zoom";if(c.enabled&&b.supportsTransition){var e,f,g=c.duration,j=function(a){var b=a.clone().removeAttr("style").removeAttr("class").addClass("mfp-animated-image"),d="all "+c.duration/1e3+"s "+c.easing,e={position:"fixed",zIndex:9999,left:0,top:0,"-webkit-backface-visibility":"hidden"},f="transition";return e["-webkit-"+f]=e["-moz-"+f]=e["-o-"+f]=e[f]=d,b.css(e),b},k=function(){b.content.css("visibility","visible")};w("BuildControls"+d,function(){if(b._allowZoom()){if(clearTimeout(e),b.content.css("visibility","hidden"),a=b._getItemToZoom(),!a)return void k();f=j(a),f.css(b._getOffset()),b.wrap.append(f),e=setTimeout(function(){f.css(b._getOffset(!0)),e=setTimeout(function(){k(),setTimeout(function(){f.remove(),a=f=null,y("ZoomAnimationEnded")},16)},g)},16)}}),w(i+d,function(){if(b._allowZoom()){if(clearTimeout(e),b.st.removalDelay=g,!a){if(a=b._getItemToZoom(),!a)return;f=j(a)}f.css(b._getOffset(!0)),b.wrap.append(f),b.content.css("visibility","hidden"),setTimeout(function(){f.css(b._getOffset())},16)}}),w(h+d,function(){b._allowZoom()&&(k(),f&&f.remove(),a=null)})}},_allowZoom:function(){return"image"===b.currItem.type},_getItemToZoom:function(){return b.currItem.hasSize?b.currItem.img:!1},_getOffset:function(c){var d;d=c?b.currItem.img:b.st.zoom.opener(b.currItem.el||b.currItem);var e=d.offset(),f=parseInt(d.css("padding-top"),10),g=parseInt(d.css("padding-bottom"),10);e.top-=a(window).scrollTop()-f;var h={width:d.width(),height:(u?d.innerHeight():d[0].offsetHeight)-g-f};return O()?h["-moz-transform"]=h.transform="translate("+e.left+"px,"+e.top+"px)":(h.left=e.left,h.top=e.top),h}}});
4221var P="iframe",Q="//about:blank",R=function(a){if(b.currTemplate[P]){var c=b.currTemplate[P].find("iframe");c.length&&(a||(c[0].src=Q),b.isIE8&&c.css("display",a?"block":"none"))}};a.magnificPopup.registerModule(P,{options:{markup:'<div class="mfp-iframe-scaler"><div class="mfp-close"></div><iframe class="mfp-iframe" src="//about:blank" frameborder="0" allowfullscreen></iframe></div>',srcAction:"iframe_src",patterns:{youtube:{index:"youtube.com",id:"v=",src:"//www.youtube.com/embed/%id%?autoplay=1"},vimeo:{index:"vimeo.com/",id:"/",src:"//player.vimeo.com/video/%id%?autoplay=1"},gmaps:{index:"//maps.google.",src:"%id%&output=embed"}}},proto:{initIframe:function(){b.types.push(P),w("BeforeChange",function(a,b,c){b!==c&&(b===P?R():c===P&&R(!0))}),w(h+"."+P,function(){R()})},getIframe:function(c,d){var e=c.src,f=b.st.iframe;a.each(f.patterns,function(){return e.indexOf(this.index)>-1?(this.id&&(e="string"==typeof this.id?e.substr(e.lastIndexOf(this.id)+this.id.length,e.length):this.id.call(this,e)),e=this.src.replace("%id%",e),!1):void 0});var g={};return f.srcAction&&(g[f.srcAction]=e),b._parseMarkup(d,g,c),b.updateStatus("ready"),d}}});var S=function(a){var c=b.items.length;return a>c-1?a-c:0>a?c+a:a},T=function(a,b,c){return a.replace(/%curr%/gi,b+1).replace(/%total%/gi,c)};a.magnificPopup.registerModule("gallery",{options:{enabled:!1,arrowMarkup:'<button title="%title%" type="button" class="mfp-arrow mfp-arrow-%dir%"></button>',preload:[0,2],navigateByImgClick:!0,arrows:!0,tPrev:"Previous (Left arrow key)",tNext:"Next (Right arrow key)",tCounter:"%curr% of %total%"},proto:{initGallery:function(){var c=b.st.gallery,e=".mfp-gallery",g=Boolean(a.fn.mfpFastClick);return b.direction=!0,c&&c.enabled?(f+=" mfp-gallery",w(m+e,function(){c.navigateByImgClick&&b.wrap.on("click"+e,".mfp-img",function(){return b.items.length>1?(b.next(),!1):void 0}),d.on("keydown"+e,function(a){37===a.keyCode?b.prev():39===a.keyCode&&b.next()})}),w("UpdateStatus"+e,function(a,c){c.text&&(c.text=T(c.text,b.currItem.index,b.items.length))}),w(l+e,function(a,d,e,f){var g=b.items.length;e.counter=g>1?T(c.tCounter,f.index,g):""}),w("BuildControls"+e,function(){if(b.items.length>1&&c.arrows&&!b.arrowLeft){var d=c.arrowMarkup,e=b.arrowLeft=a(d.replace(/%title%/gi,c.tPrev).replace(/%dir%/gi,"left")).addClass(s),f=b.arrowRight=a(d.replace(/%title%/gi,c.tNext).replace(/%dir%/gi,"right")).addClass(s),h=g?"mfpFastClick":"click";e[h](function(){b.prev()}),f[h](function(){b.next()}),b.isIE7&&(x("b",e[0],!1,!0),x("a",e[0],!1,!0),x("b",f[0],!1,!0),x("a",f[0],!1,!0)),b.container.append(e.add(f))}}),w(n+e,function(){b._preloadTimeout&&clearTimeout(b._preloadTimeout),b._preloadTimeout=setTimeout(function(){b.preloadNearbyImages(),b._preloadTimeout=null},16)}),void w(h+e,function(){d.off(e),b.wrap.off("click"+e),b.arrowLeft&&g&&b.arrowLeft.add(b.arrowRight).destroyMfpFastClick(),b.arrowRight=b.arrowLeft=null})):!1},next:function(){b.direction=!0,b.index=S(b.index+1),b.updateItemHTML()},prev:function(){b.direction=!1,b.index=S(b.index-1),b.updateItemHTML()},goTo:function(a){b.direction=a>=b.index,b.index=a,b.updateItemHTML()},preloadNearbyImages:function(){var a,c=b.st.gallery.preload,d=Math.min(c[0],b.items.length),e=Math.min(c[1],b.items.length);for(a=1;a<=(b.direction?e:d);a++)b._preloadItem(b.index+a);for(a=1;a<=(b.direction?d:e);a++)b._preloadItem(b.index-a)},_preloadItem:function(c){if(c=S(c),!b.items[c].preloaded){var d=b.items[c];d.parsed||(d=b.parseEl(c)),y("LazyLoad",d),"image"===d.type&&(d.img=a('<img class="mfp-img" />').on("load.mfploader",function(){d.hasSize=!0}).on("error.mfploader",function(){d.hasSize=!0,d.loadError=!0,y("LazyLoadError",d)}).attr("src",d.src)),d.preloaded=!0}}}});var U="retina";a.magnificPopup.registerModule(U,{options:{replaceSrc:function(a){return a.src.replace(/\.\w+$/,function(a){return"@2x"+a})},ratio:1},proto:{initRetina:function(){if(window.devicePixelRatio>1){var a=b.st.retina,c=a.ratio;c=isNaN(c)?c():c,c>1&&(w("ImageHasSize."+U,function(a,b){b.img.css({"max-width":b.img[0].naturalWidth/c,width:"100%"})}),w("ElementParse."+U,function(b,d){d.src=a.replaceSrc(d,c)}))}}}}),function(){var b=1e3,c="ontouchstart"in window,d=function(){v.off("touchmove"+f+" touchend"+f)},e="mfpFastClick",f="."+e;a.fn.mfpFastClick=function(e){return a(this).each(function(){var g,h=a(this);if(c){var i,j,k,l,m,n;h.on("touchstart"+f,function(a){l=!1,n=1,m=a.originalEvent?a.originalEvent.touches[0]:a.touches[0],j=m.clientX,k=m.clientY,v.on("touchmove"+f,function(a){m=a.originalEvent?a.originalEvent.touches:a.touches,n=m.length,m=m[0],(Math.abs(m.clientX-j)>10||Math.abs(m.clientY-k)>10)&&(l=!0,d())}).on("touchend"+f,function(a){d(),l||n>1||(g=!0,a.preventDefault(),clearTimeout(i),i=setTimeout(function(){g=!1},b),e())})})}h.on("click"+f,function(){g||e()})})},a.fn.destroyMfpFastClick=function(){a(this).off("touchstart"+f+" click"+f),c&&v.off("touchmove"+f+" touchend"+f)}}(),A()});
4222/*!
4223 * jQuery Validation Plugin v1.15.1
4224 *
4225 * http://jqueryvalidation.org/
4226 *
4227 * Copyright (c) 2016 Jörn Zaefferer
4228 * Released under the MIT license
4229 */
4230(function( factory ) {
4231	if ( typeof define === "function" && define.amd ) {
4232		define( ["jquery"], factory );
4233	} else if (typeof module === "object" && module.exports) {
4234		module.exports = factory( require( "jquery" ) );
4235	} else {
4236		factory( jQuery );
4237	}
4238}(function( $ ) {
4239
4240$.extend( $.fn, {
4241
4242	// http://jqueryvalidation.org/validate/
4243	validate: function( options ) {
4244
4245		// If nothing is selected, return nothing; can't chain anyway
4246		if ( !this.length ) {
4247			if ( options && options.debug && window.console ) {
4248				console.warn( "Nothing selected, can't validate, returning nothing." );
4249			}
4250			return;
4251		}
4252
4253		// Check if a validator for this form was already created
4254		var validator = $.data( this[ 0 ], "validator" );
4255		if ( validator ) {
4256			return validator;
4257		}
4258
4259		// Add novalidate tag if HTML5.
4260		this.attr( "novalidate", "novalidate" );
4261
4262		validator = new $.validator( options, this[ 0 ] );
4263		$.data( this[ 0 ], "validator", validator );
4264
4265		if ( validator.settings.onsubmit ) {
4266
4267			this.on( "click.validate", ":submit", function( event ) {
4268				if ( validator.settings.submitHandler ) {
4269					validator.submitButton = event.target;
4270				}
4271
4272				// Allow suppressing validation by adding a cancel class to the submit button
4273				if ( $( this ).hasClass( "cancel" ) ) {
4274					validator.cancelSubmit = true;
4275				}
4276
4277				// Allow suppressing validation by adding the html5 formnovalidate attribute to the submit button
4278				if ( $( this ).attr( "formnovalidate" ) !== undefined ) {
4279					validator.cancelSubmit = true;
4280				}
4281			} );
4282
4283			// Validate the form on submit
4284			this.on( "submit.validate", function( event ) {
4285				if ( validator.settings.debug ) {
4286
4287					// Prevent form submit to be able to see console output
4288					event.preventDefault();
4289				}
4290				function handle() {
4291					var hidden, result;
4292					if ( validator.settings.submitHandler ) {
4293						if ( validator.submitButton ) {
4294
4295							// Insert a hidden input as a replacement for the missing submit button
4296							hidden = $( "<input type='hidden'/>" )
4297								.attr( "name", validator.submitButton.name )
4298								.val( $( validator.submitButton ).val() )
4299								.appendTo( validator.currentForm );
4300						}
4301						result = validator.settings.submitHandler.call( validator, validator.currentForm, event );
4302						if ( validator.submitButton ) {
4303
4304							// And clean up afterwards; thanks to no-block-scope, hidden can be referenced
4305							hidden.remove();
4306						}
4307						if ( result !== undefined ) {
4308							return result;
4309						}
4310						return false;
4311					}
4312					return true;
4313				}
4314
4315				// Prevent submit for invalid forms or custom submit handlers
4316				if ( validator.cancelSubmit ) {
4317					validator.cancelSubmit = false;
4318					return handle();
4319				}
4320				if ( validator.form() ) {
4321					if ( validator.pendingRequest ) {
4322						validator.formSubmitted = true;
4323						return false;
4324					}
4325					return handle();
4326				} else {
4327					validator.focusInvalid();
4328					return false;
4329				}
4330			} );
4331		}
4332
4333		return validator;
4334	},
4335
4336	// http://jqueryvalidation.org/valid/
4337	valid: function() {
4338		var valid, validator, errorList;
4339
4340		if ( $( this[ 0 ] ).is( "form" ) ) {
4341			valid = this.validate().form();
4342		} else {
4343			errorList = [];
4344			valid = true;
4345			validator = $( this[ 0 ].form ).validate();
4346			this.each( function() {
4347				valid = validator.element( this ) && valid;
4348				if ( !valid ) {
4349					errorList = errorList.concat( validator.errorList );
4350				}
4351			} );
4352			validator.errorList = errorList;
4353		}
4354		return valid;
4355	},
4356
4357	// http://jqueryvalidation.org/rules/
4358	rules: function( command, argument ) {
4359		var element = this[ 0 ],
4360			settings, staticRules, existingRules, data, param, filtered;
4361
4362		// If nothing is selected, return empty object; can't chain anyway
4363		if ( element == null || element.form == null ) {
4364			return;
4365		}
4366
4367		if ( command ) {
4368			settings = $.data( element.form, "validator" ).settings;
4369			staticRules = settings.rules;
4370			existingRules = $.validator.staticRules( element );
4371			switch ( command ) {
4372			case "add":
4373				$.extend( existingRules, $.validator.normalizeRule( argument ) );
4374
4375				// Remove messages from rules, but allow them to be set separately
4376				delete existingRules.messages;
4377				staticRules[ element.name ] = existingRules;
4378				if ( argument.messages ) {
4379					settings.messages[ element.name ] = $.extend( settings.messages[ element.name ], argument.messages );
4380				}
4381				break;
4382			case "remove":
4383				if ( !argument ) {
4384					delete staticRules[ element.name ];
4385					return existingRules;
4386				}
4387				filtered = {};
4388				$.each( argument.split( /\s/ ), function( index, method ) {
4389					filtered[ method ] = existingRules[ method ];
4390					delete existingRules[ method ];
4391					if ( method === "required" ) {
4392						$( element ).removeAttr( "aria-required" );
4393					}
4394				} );
4395				return filtered;
4396			}
4397		}
4398
4399		data = $.validator.normalizeRules(
4400		$.extend(
4401			{},
4402			$.validator.classRules( element ),
4403			$.validator.attributeRules( element ),
4404			$.validator.dataRules( element ),
4405			$.validator.staticRules( element )
4406		), element );
4407
4408		// Make sure required is at front
4409		if ( data.required ) {
4410			param = data.required;
4411			delete data.required;
4412			data = $.extend( { required: param }, data );
4413			$( element ).attr( "aria-required", "true" );
4414		}
4415
4416		// Make sure remote is at back
4417		if ( data.remote ) {
4418			param = data.remote;
4419			delete data.remote;
4420			data = $.extend( data, { remote: param } );
4421		}
4422
4423		return data;
4424	}
4425} );
4426
4427// Custom selectors
4428$.extend( $.expr[ ":" ], {
4429
4430	// http://jqueryvalidation.org/blank-selector/
4431	blank: function( a ) {
4432		return !$.trim( "" + $( a ).val() );
4433	},
4434
4435	// http://jqueryvalidation.org/filled-selector/
4436	filled: function( a ) {
4437		var val = $( a ).val();
4438		return val !== null && !!$.trim( "" + val );
4439	},
4440
4441	// http://jqueryvalidation.org/unchecked-selector/
4442	unchecked: function( a ) {
4443		return !$( a ).prop( "checked" );
4444	}
4445} );
4446
4447// Constructor for validator
4448$.validator = function( options, form ) {
4449	this.settings = $.extend( true, {}, $.validator.defaults, options );
4450	this.currentForm = form;
4451	this.init();
4452};
4453
4454// http://jqueryvalidation.org/jQuery.validator.format/
4455$.validator.format = function( source, params ) {
4456	if ( arguments.length === 1 ) {
4457		return function() {
4458			var args = $.makeArray( arguments );
4459			args.unshift( source );
4460			return $.validator.format.apply( this, args );
4461		};
4462	}
4463	if ( params === undefined ) {
4464		return source;
4465	}
vendor: 9,941 bytes, lines 4466-4776
4466	if ( arguments.length > 2 && params.constructor !== Array  ) {
4467		params = $.makeArray( arguments ).slice( 1 );
4468	}
4469	if ( params.constructor !== Array ) {
4470		params = [ params ];
4471	}
4472	$.each( params, function( i, n ) {
4473		source = source.replace( new RegExp( "\\{" + i + "\\}", "g" ), function() {
4474			return n;
4475		} );
4476	} );
4477	return source;
4478};
4479
4480$.extend( $.validator, {
4481
4482	defaults: {
4483		messages: {},
4484		groups: {},
4485		rules: {},
4486		errorClass: "error",
4487		pendingClass: "pending",
4488		validClass: "valid",
4489		errorElement: "label",
4490		focusCleanup: false,
4491		focusInvalid: true,
4492		errorContainer: $( [] ),
4493		errorLabelContainer: $( [] ),
4494		onsubmit: true,
4495		ignore: ":hidden",
4496		ignoreTitle: false,
4497		onfocusin: function( element ) {
4498			this.lastActive = element;
4499
4500			// Hide error label and remove error class on focus if enabled
4501			if ( this.settings.focusCleanup ) {
4502				if ( this.settings.unhighlight ) {
4503					this.settings.unhighlight.call( this, element, this.settings.errorClass, this.settings.validClass );
4504				}
4505				this.hideThese( this.errorsFor( element ) );
4506			}
4507		},
4508		onfocusout: function( element ) {
4509			if ( !this.checkable( element ) && ( element.name in this.submitted || !this.optional( element ) ) ) {
4510				this.element( element );
4511			}
4512		},
4513		onkeyup: function( element, event ) {
4514
4515			// Avoid revalidate the field when pressing one of the following keys
4516			// Shift       => 16
4517			// Ctrl        => 17
4518			// Alt         => 18
4519			// Caps lock   => 20
4520			// End         => 35
4521			// Home        => 36
4522			// Left arrow  => 37
4523			// Up arrow    => 38
4524			// Right arrow => 39
4525			// Down arrow  => 40
4526			// Insert      => 45
4527			// Num lock    => 144
4528			// AltGr key   => 225
4529			var excludedKeys = [
4530				16, 17, 18, 20, 35, 36, 37,
4531				38, 39, 40, 45, 144, 225
4532			];
4533
4534			if ( event.which === 9 && this.elementValue( element ) === "" || $.inArray( event.keyCode, excludedKeys ) !== -1 ) {
4535				return;
4536			} else if ( element.name in this.submitted || element.name in this.invalid ) {
4537				this.element( element );
4538			}
4539		},
4540		onclick: function( element ) {
4541
4542			// Click on selects, radiobuttons and checkboxes
4543			if ( element.name in this.submitted ) {
4544				this.element( element );
4545
4546			// Or option elements, check parent select in that case
4547			} else if ( element.parentNode.name in this.submitted ) {
4548				this.element( element.parentNode );
4549			}
4550		},
4551		highlight: function( element, errorClass, validClass ) {
4552			if ( element.type === "radio" ) {
4553				this.findByName( element.name ).addClass( errorClass ).removeClass( validClass );
4554			} else {
4555				$( element ).addClass( errorClass ).removeClass( validClass );
4556			}
4557		},
4558		unhighlight: function( element, errorClass, validClass ) {
4559			if ( element.type === "radio" ) {
4560				this.findByName( element.name ).removeClass( errorClass ).addClass( validClass );
4561			} else {
4562				$( element ).removeClass( errorClass ).addClass( validClass );
4563			}
4564		}
4565	},
4566
4567	// http://jqueryvalidation.org/jQuery.validator.setDefaults/
4568	setDefaults: function( settings ) {
4569		$.extend( $.validator.defaults, settings );
4570	},
4571
4572	messages: {
4573		required: "This field is required.",
4574		remote: "Please fix this field.",
4575		email: "Please enter a valid email address.",
4576		url: "Please enter a valid URL.",
4577		date: "Please enter a valid date.",
4578		dateISO: "Please enter a valid date (ISO).",
4579		number: "Please enter a valid number.",
4580		digits: "Please enter only digits.",
4581		equalTo: "Please enter the same value again.",
4582		maxlength: $.validator.format( "Please enter no more than {0} characters." ),
4583		minlength: $.validator.format( "Please enter at least {0} characters." ),
4584		rangelength: $.validator.format( "Please enter a value between {0} and {1} characters long." ),
4585		range: $.validator.format( "Please enter a value between {0} and {1}." ),
4586		max: $.validator.format( "Please enter a value less than or equal to {0}." ),
4587		min: $.validator.format( "Please enter a value greater than or equal to {0}." ),
4588		step: $.validator.format( "Please enter a multiple of {0}." )
4589	},
4590
4591	autoCreateRanges: false,
4592
4593	prototype: {
4594
4595		init: function() {
4596			this.labelContainer = $( this.settings.errorLabelContainer );
4597			this.errorContext = this.labelContainer.length && this.labelContainer || $( this.currentForm );
4598			this.containers = $( this.settings.errorContainer ).add( this.settings.errorLabelContainer );
4599			this.submitted = {};
4600			this.valueCache = {};
4601			this.pendingRequest = 0;
4602			this.pending = {};
4603			this.invalid = {};
4604			this.reset();
4605
4606			var groups = ( this.groups = {} ),
4607				rules;
4608			$.each( this.settings.groups, function( key, value ) {
4609				if ( typeof value === "string" ) {
4610					value = value.split( /\s/ );
4611				}
4612				$.each( value, function( index, name ) {
4613					groups[ name ] = key;
4614				} );
4615			} );
4616			rules = this.settings.rules;
4617			$.each( rules, function( key, value ) {
4618				rules[ key ] = $.validator.normalizeRule( value );
4619			} );
4620
4621			function delegate( event ) {
4622
4623				// Set form expando on contenteditable
4624				if ( !this.form && this.hasAttribute( "contenteditable" ) ) {
4625					this.form = $( this ).closest( "form" )[ 0 ];
4626				}
4627
4628				var validator = $.data( this.form, "validator" ),
4629					eventType = "on" + event.type.replace( /^validate/, "" ),
4630					settings = validator.settings;
4631				if ( settings[ eventType ] && !$( this ).is( settings.ignore ) ) {
4632					settings[ eventType ].call( validator, this, event );
4633				}
4634			}
4635
4636			$( this.currentForm )
4637				.on( "focusin.validate focusout.validate keyup.validate",
4638					":text, [type='password'], [type='file'], select, textarea, [type='number'], [type='search'], " +
4639					"[type='tel'], [type='url'], [type='email'], [type='datetime'], [type='date'], [type='month'], " +
4640					"[type='week'], [type='time'], [type='datetime-local'], [type='range'], [type='color'], " +
4641					"[type='radio'], [type='checkbox'], [contenteditable]", delegate )
4642
4643				// Support: Chrome, oldIE
4644				// "select" is provided as event.target when clicking a option
4645				.on( "click.validate", "select, option, [type='radio'], [type='checkbox']", delegate );
4646
4647			if ( this.settings.invalidHandler ) {
4648				$( this.currentForm ).on( "invalid-form.validate", this.settings.invalidHandler );
4649			}
4650
4651			// Add aria-required to any Static/Data/Class required fields before first validation
4652			// Screen readers require this attribute to be present before the initial submission http://www.w3.org/TR/WCAG-TECHS/ARIA2.html
4653			$( this.currentForm ).find( "[required], [data-rule-required], .required" ).attr( "aria-required", "true" );
4654		},
4655
4656		// http://jqueryvalidation.org/Validator.form/
4657		form: function() {
4658			this.checkForm();
4659			$.extend( this.submitted, this.errorMap );
4660			this.invalid = $.extend( {}, this.errorMap );
4661			if ( !this.valid() ) {
4662				$( this.currentForm ).triggerHandler( "invalid-form", [ this ] );
4663			}
4664			this.showErrors();
4665			return this.valid();
4666		},
4667
4668		checkForm: function() {
4669			this.prepareForm();
4670			for ( var i = 0, elements = ( this.currentElements = this.elements() ); elements[ i ]; i++ ) {
4671				this.check( elements[ i ] );
4672			}
4673			return this.valid();
4674		},
4675
4676		// http://jqueryvalidation.org/Validator.element/
4677		element: function( element ) {
4678			var cleanElement = this.clean( element ),
4679				checkElement = this.validationTargetFor( cleanElement ),
4680				v = this,
4681				result = true,
4682				rs, group;
4683
4684			if ( checkElement === undefined ) {
4685				delete this.invalid[ cleanElement.name ];
4686			} else {
4687				this.prepareElement( checkElement );
4688				this.currentElements = $( checkElement );
4689
4690				// If this element is grouped, then validate all group elements already
4691				// containing a value
4692				group = this.groups[ checkElement.name ];
4693				if ( group ) {
4694					$.each( this.groups, function( name, testgroup ) {
4695						if ( testgroup === group && name !== checkElement.name ) {
4696							cleanElement = v.validationTargetFor( v.clean( v.findByName( name ) ) );
4697							if ( cleanElement && cleanElement.name in v.invalid ) {
4698								v.currentElements.push( cleanElement );
4699								result = v.check( cleanElement ) && result;
4700							}
4701						}
4702					} );
4703				}
4704
4705				rs = this.check( checkElement ) !== false;
4706				result = result && rs;
4707				if ( rs ) {
4708					this.invalid[ checkElement.name ] = false;
4709				} else {
4710					this.invalid[ checkElement.name ] = true;
4711				}
4712
4713				if ( !this.numberOfInvalids() ) {
4714
4715					// Hide error containers on last error
4716					this.toHide = this.toHide.add( this.containers );
4717				}
4718				this.showErrors();
4719
4720				// Add aria-invalid status for screen readers
4721				$( element ).attr( "aria-invalid", !rs );
4722			}
4723
4724			return result;
4725		},
4726
4727		// http://jqueryvalidation.org/Validator.showErrors/
4728		showErrors: function( errors ) {
4729			if ( errors ) {
4730				var validator = this;
4731
4732				// Add items to error list and map
4733				$.extend( this.errorMap, errors );
4734				this.errorList = $.map( this.errorMap, function( message, name ) {
4735					return {
4736						message: message,
4737						element: validator.findByName( name )[ 0 ]
4738					};
4739				} );
4740
4741				// Remove items from success list
4742				this.successList = $.grep( this.successList, function( element ) {
4743					return !( element.name in errors );
4744				} );
4745			}
4746			if ( this.settings.showErrors ) {
4747				this.settings.showErrors.call( this, this.errorMap, this.errorList );
4748			} else {
4749				this.defaultShowErrors();
4750			}
4751		},
4752
4753		// http://jqueryvalidation.org/Validator.resetForm/
4754		resetForm: function() {
4755			if ( $.fn.resetForm ) {
4756				$( this.currentForm ).resetForm();
4757			}
4758			this.invalid = {};
4759			this.submitted = {};
4760			this.prepareForm();
4761			this.hideErrors();
4762			var elements = this.elements()
4763				.removeData( "previousValue" )
4764				.removeAttr( "aria-invalid" );
4765
4766			this.resetElements( elements );
4767		},
4768
4769		resetElements: function( elements ) {
4770			var i;
4771
4772			if ( this.settings.unhighlight ) {
4773				for ( i = 0; elements[ i ]; i++ ) {
4774					this.settings.unhighlight.call( this, elements[ i ],
4775						this.settings.errorClass, "" );
4776					this.findByName( elements[ i ].name ).removeClass( this.settings.val
4776idClass );
4777				}
4778			} else {
4779				elements
4780					.removeClass( this.settings.errorClass )
4781					.removeClass( this.settings.validClass );
4782			}
4783		},
4784
4785		numberOfInvalids: function() {
4786			return this.objectLength( this.invalid );
4787		},
4788
4789		objectLength: function( obj ) {
4790			/* jshint unused: false */
4791			var count = 0,
4792				i;
4793			for ( i in obj ) {
4794				if ( obj[ i ] ) {
4795					count++;
4796				}
4797			}
4798			return count;
4799		},
4800
4801		hideErrors: function() {
4802			this.hideThese( this.toHide );
4803		},
4804
4805		hideThese: function( errors ) {
4806			errors.not( this.containers ).text( "" );
4807			this.addWrapper( errors ).hide();
4808		},
4809
4810		valid: function() {
4811			return this.size() === 0;
4812		},
4813
4814		size: function() {
4815			return this.errorList.length;
4816		},
4817
4818		focusInvalid: function() {
4819			if ( this.settings.focusInvalid ) {
4820				try {
4821					$( this.findLastActive() || this.errorList.length && this.errorList[ 0 ].element || [] )
4822					.filter( ":visible" )
4823					.focus()
4824
4825					// Manually trigger focusin event; without it, focusin handler isn't called, findLastActive won't have anything to find
4826					.trigger( "focusin" );
4827				} catch ( e ) {
4828
4829					// Ignore IE throwing errors when focusing hidden elements
4830				}
4831			}
4832		},
4833
4834		findLastActive: function() {
4835			var lastActive = this.lastActive;
4836			return lastActive && $.grep( this.errorList, function( n ) {
4837				return n.element.name === lastActive.name;
4838			} ).length === 1 && lastActive;
4839		},
4840
4841		elements: function() {
4842			var validator = this,
4843				rulesCache = {};
4844
4845			// Select all valid inputs inside the form (no submit or reset buttons)
4846			return $( this.currentForm )
4847			.find( "input, select, textarea, [contenteditable]" )
4848			.not( ":submit, :reset, :image, :disabled" )
4849			.not( this.settings.ignore )
4850			.filter( function() {
4851				var name = this.name || $( this ).attr( "name" ); // For contenteditable
4852				if ( !name && validator.settings.debug && window.console ) {
4853					console.error( "%o has no name assigned", this );
4854				}
4855
4856				// Set form expando on contenteditable
4857				if ( this.hasAttribute( "contenteditable" ) ) {
4858					this.form = $( this ).closest( "form" )[ 0 ];
4859				}
4860
4861				// Select only the first element for each name, and only those with rules specified
4862				if ( name in rulesCache || !validator.objectLength( $( this ).rules() ) ) {
4863					return false;
4864				}
4865
4866				rulesCache[ name ] = true;
4867				return true;
4868			} );
4869		},
4870
4871		clean: function( selector ) {
4872			return $( selector )[ 0 ];
4873		},
4874
4875		errors: function() {
4876			var errorClass = this.settings.errorClass.split( " " ).join( "." );
4877			return $( this.settings.errorElement + "." + errorClass, this.errorContext );
4878		},
4879
4880		resetInternals: function() {
4881			this.successList = [];
4882			this.errorList = [];
4883			this.errorMap = {};
4884			this.toShow = $( [] );
4885			this.toHide = $( [] );
4886		},
4887
4888		reset: function() {
4889			this.resetInternals();
4890			this.currentElements = $( [] );
4891		},
4892
4893		prepareForm: function() {
4894			this.reset();
4895			this.toHide = this.errors().add( this.containers );
4896		},
4897
4898		prepareElement: function( element ) {
4899			this.reset();
4900			this.toHide = this.errorsFor( element );
4901		},
4902
4903		elementValue: function( element ) {
4904			var $element = $( element ),
4905				type = element.type,
4906				val, idx;
4907
4908			if ( type === "radio" || type === "checkbox" ) {
4909				return this.findByName( element.name ).filter( ":checked" ).val();
4910			} else if ( type === "number" && typeof element.validity !== "undefined" ) {
4911				return element.validity.badInput ? "NaN" : $element.val();
4912			}
4913
4914			if ( element.hasAttribute( "contenteditable" ) ) {
4915				val = $element.text();
4916			} else {
4917				val = $element.val();
4918			}
4919
4920			if ( type === "file" ) {
4921
4922				// Modern browser (chrome & safari)
4923				if ( val.substr( 0, 12 ) === "C:\\fakepath\\" ) {
4924					return val.substr( 12 );
4925				}
4926
4927				// Legacy browsers
4928				// Unix-based path
4929				idx = val.lastIndexOf( "/" );
4930				if ( idx >= 0 ) {
4931					return val.substr( idx + 1 );
4932				}
4933
4934				// Windows-based path
4935				idx = val.lastIndexOf( "\\" );
4936				if ( idx >= 0 ) {
4937					return val.substr( idx + 1 );
4938				}
4939
4940				// Just the file name
4941				return val;
4942			}
4943
4944			if ( typeof val === "string" ) {
4945				return val.replace( /\r/g, "" );
4946			}
4947			return val;
4948		},
4949
4950		check: function( element ) {
4951			element = this.validationTargetFor( this.clean( element ) );
4952
4953			var rules = $( element ).rules(),
4954				rulesCount = $.map( rules, function( n, i ) {
4955					return i;
4956				} ).length,
4957				dependencyMismatch = false,
4958				val = this.elementValue( element ),
4959				result, method, rule;
4960
4961			// If a normalizer is defined for this element, then
4962			// call it to retreive the changed value instead
4963			// of using the real one.
4964			// Note that `this` in the normalizer is `element`.
4965			if ( typeof rules.normalizer === "function" ) {
4966				val = rules.normalizer.call( element, val );
4967
4968				if ( typeof val !== "string" ) {
4969					throw new TypeError( "The normalizer should return a string value." );
4970				}
4971
4972				// Delete the normalizer from rules to avoid treating
4973				// it as a pre-defined method.
4974				delete rules.normalizer;
4975			}
4976
4977			for ( method in rules ) {
4978				rule = { method: method, parameters: rules[ method ] };
vendor: 23,772 bytes, lines 4979-5719
4979				try {
4980					result = $.validator.methods[ method ].call( this, val, element, rule.parameters );
4981
4982					// If a method indicates that the field is optional and therefore valid,
4983					// don't mark it as valid when there are no other rules
4984					if ( result === "dependency-mismatch" && rulesCount === 1 ) {
4985						dependencyMismatch = true;
4986						continue;
4987					}
4988					dependencyMismatch = false;
4989
4990					if ( result === "pending" ) {
4991						this.toHide = this.toHide.not( this.errorsFor( element ) );
4992						return;
4993					}
4994
4995					if ( !result ) {
4996						this.formatAndAdd( element, rule );
4997						return false;
4998					}
4999				} catch ( e ) {
5000					if ( this.settings.debug && window.console ) {
5001						console.log( "Exception occurred when checking element " + element.id + ", check the '" + rule.method + "' method.", e );
5002					}
5003					if ( e instanceof TypeError ) {
5004						e.message += ".  Exception occurred when checking element " + element.id + ", check the '" + rule.method + "' method.";
5005					}
5006
5007					throw e;
5008				}
5009			}
5010			if ( dependencyMismatch ) {
5011				return;
5012			}
5013			if ( this.objectLength( rules ) ) {
5014				this.successList.push( element );
5015			}
5016			return true;
5017		},
5018
5019		// Return the custom message for the given element and validation method
5020		// specified in the element's HTML5 data attribute
5021		// return the generic message if present and no method specific message is present
5022		customDataMessage: function( element, method ) {
5023			return $( element ).data( "msg" + method.charAt( 0 ).toUpperCase() +
5024				method.substring( 1 ).toLowerCase() ) || $( element ).data( "msg" );
5025		},
5026
5027		// Return the custom message for the given element name and validation method
5028		customMessage: function( name, method ) {
5029			var m = this.settings.messages[ name ];
5030			return m && ( m.constructor === String ? m : m[ method ] );
5031		},
5032
5033		// Return the first defined argument, allowing empty strings
5034		findDefined: function() {
5035			for ( var i = 0; i < arguments.length; i++ ) {
5036				if ( arguments[ i ] !== undefined ) {
5037					return arguments[ i ];
5038				}
5039			}
5040			return undefined;
5041		},
5042
5043		// The second parameter 'rule' used to be a string, and extended to an object literal
5044		// of the following form:
5045		// rule = {
5046		//     method: "method name",
5047		//     parameters: "the given method parameters"
5048		// }
5049		//
5050		// The old behavior still supported, kept to maintain backward compatibility with
5051		// old code, and will be removed in the next major release.
5052		defaultMessage: function( element, rule ) {
5053			if ( typeof rule === "string" ) {
5054				rule = { method: rule };
5055			}
5056
5057			var message = this.findDefined(
5058					this.customMessage( element.name, rule.method ),
5059					this.customDataMessage( element, rule.method ),
5060
5061					// 'title' is never undefined, so handle empty string as undefined
5062					!this.settings.ignoreTitle && element.title || undefined,
5063					$.validator.messages[ rule.method ],
5064					"<strong>Warning: No message defined for " + element.name + "</strong>"
5065				),
5066				theregex = /\$?\{(\d+)\}/g;
5067			if ( typeof message === "function" ) {
5068				message = message.call( this, rule.parameters, element );
5069			} else if ( theregex.test( message ) ) {
5070				message = $.validator.format( message.replace( theregex, "{$1}" ), rule.parameters );
5071			}
5072
5073			return message;
5074		},
5075
5076		formatAndAdd: function( element, rule ) {
5077			var message = this.defaultMessage( element, rule );
5078
5079			this.errorList.push( {
5080				message: message,
5081				element: element,
5082				method: rule.method
5083			} );
5084
5085			this.errorMap[ element.name ] = message;
5086			this.submitted[ element.name ] = message;
5087		},
5088
5089		addWrapper: function( toToggle ) {
5090			if ( this.settings.wrapper ) {
5091				toToggle = toToggle.add( toToggle.parent( this.settings.wrapper ) );
5092			}
5093			return toToggle;
5094		},
5095
5096		defaultShowErrors: function() {
5097			var i, elements, error;
5098			for ( i = 0; this.errorList[ i ]; i++ ) {
5099				error = this.errorList[ i ];
5100				if ( this.settings.highlight ) {
5101					this.settings.highlight.call( this, error.element, this.settings.errorClass, this.settings.validClass );
5102				}
5103				this.showLabel( error.element, error.message );
5104			}
5105			if ( this.errorList.length ) {
5106				this.toShow = this.toShow.add( this.containers );
5107			}
5108			if ( this.settings.success ) {
5109				for ( i = 0; this.successList[ i ]; i++ ) {
5110					this.showLabel( this.successList[ i ] );
5111				}
5112			}
5113			if ( this.settings.unhighlight ) {
5114				for ( i = 0, elements = this.validElements(); elements[ i ]; i++ ) {
5115					this.settings.unhighlight.call( this, elements[ i ], this.settings.errorClass, this.settings.validClass );
5116				}
5117			}
5118			this.toHide = this.toHide.not( this.toShow );
5119			this.hideErrors();
5120			this.addWrapper( this.toShow ).show();
5121		},
5122
5123		validElements: function() {
5124			return this.currentElements.not( this.invalidElements() );
5125		},
5126
5127		invalidElements: function() {
5128			return $( this.errorList ).map( function() {
5129				return this.element;
5130			} );
5131		},
5132
5133		showLabel: function( element, message ) {
5134			var place, group, errorID, v,
5135				error = this.errorsFor( element ),
5136				elementID = this.idOrName( element ),
5137				describedBy = $( element ).attr( "aria-describedby" );
5138
5139			if ( error.length ) {
5140
5141				// Refresh error/success class
5142				error.removeClass( this.settings.validClass ).addClass( this.settings.errorClass );
5143
5144				// Replace message on existing label
5145				error.html( message );
5146			} else {
5147
5148				// Create error element
5149				error = $( "<" + this.settings.errorElement + ">" )
5150					.attr( "id", elementID + "-error" )
5151					.addClass( this.settings.errorClass )
5152					.html( message || "" );
5153
5154				// Maintain reference to the element to be placed into the DOM
5155				place = error;
5156				if ( this.settings.wrapper ) {
5157
5158					// Make sure the element is visible, even in IE
5159					// actually showing the wrapped element is handled elsewhere
5160					place = error.hide().show().wrap( "<" + this.settings.wrapper + "/>" ).parent();
5161				}
5162				if ( this.labelContainer.length ) {
5163					this.labelContainer.append( place );
5164				} else if ( this.settings.errorPlacement ) {
5165					this.settings.errorPlacement.call( this, place, $( element ) );
5166				} else {
5167					place.insertAfter( element );
5168				}
5169
5170				// Link error back to the element
5171				if ( error.is( "label" ) ) {
5172
5173					// If the error is a label, then associate using 'for'
5174					error.attr( "for", elementID );
5175
5176					// If the element is not a child of an associated label, then it's necessary
5177					// to explicitly apply aria-describedby
5178				} else if ( error.parents( "label[for='" + this.escapeCssMeta( elementID ) + "']" ).length === 0 ) {
5179					errorID = error.attr( "id" );
5180
5181					// Respect existing non-error aria-describedby
5182					if ( !describedBy ) {
5183						describedBy = errorID;
5184					} else if ( !describedBy.match( new RegExp( "\\b" + this.escapeCssMeta( errorID ) + "\\b" ) ) ) {
5185
5186						// Add to end of list if not already present
5187						describedBy += " " + errorID;
5188					}
5189					$( element ).attr( "aria-describedby", describedBy );
5190
5191					// If this element is grouped, then assign to all elements in the same group
5192					group = this.groups[ element.name ];
5193					if ( group ) {
5194						v = this;
5195						$.each( v.groups, function( name, testgroup ) {
5196							if ( testgroup === group ) {
5197								$( "[name='" + v.escapeCssMeta( name ) + "']", v.currentForm )
5198									.attr( "aria-describedby", error.attr( "id" ) );
5199							}
5200						} );
5201					}
5202				}
5203			}
5204			if ( !message && this.settings.success ) {
5205				error.text( "" );
5206				if ( typeof this.settings.success === "string" ) {
5207					error.addClass( this.settings.success );
5208				} else {
5209					this.settings.success( error, element );
5210				}
5211			}
5212			this.toShow = this.toShow.add( error );
5213		},
5214
5215		errorsFor: function( element ) {
5216			var name = this.escapeCssMeta( this.idOrName( element ) ),
5217				describer = $( element ).attr( "aria-describedby" ),
5218				selector = "label[for='" + name + "'], label[for='" + name + "'] *";
5219
5220			// 'aria-describedby' should directly reference the error element
5221			if ( describer ) {
5222				selector = selector + ", #" + this.escapeCssMeta( describer )
5223					.replace( /\s+/g, ", #" );
5224			}
5225
5226			return this
5227				.errors()
5228				.filter( selector );
5229		},
5230
5231		// See https://api.jquery.com/category/selectors/, for CSS
5232		// meta-characters that should be escaped in order to be used with JQuery
5233		// as a literal part of a name/id or any selector.
5234		escapeCssMeta: function( string ) {
5235			return string.replace( /([\\!"#$%&'()*+,./:;<=>?@\[\]^`{|}~])/g, "\\$1" );
5236		},
5237
5238		idOrName: function( element ) {
5239			return this.groups[ element.name ] || ( this.checkable( element ) ? element.name : element.id || element.name );
5240		},
5241
5242		validationTargetFor: function( element ) {
5243
5244			// If radio/checkbox, validate first element in group instead
5245			if ( this.checkable( element ) ) {
5246				element = this.findByName( element.name );
5247			}
5248
5249			// Always apply ignore filter
5250			return $( element ).not( this.settings.ignore )[ 0 ];
5251		},
5252
5253		checkable: function( element ) {
5254			return ( /radio|checkbox/i ).test( element.type );
5255		},
5256
5257		findByName: function( name ) {
5258			return $( this.currentForm ).find( "[name='" + this.escapeCssMeta( name ) + "']" );
5259		},
5260
5261		getLength: function( value, element ) {
5262			switch ( element.nodeName.toLowerCase() ) {
5263			case "select":
5264				return $( "option:selected", element ).length;
5265			case "input":
5266				if ( this.checkable( element ) ) {
5267					return this.findByName( element.name ).filter( ":checked" ).length;
5268				}
5269			}
5270			return value.length;
5271		},
5272
5273		depend: function( param, element ) {
5274			return this.dependTypes[ typeof param ] ? this.dependTypes[ typeof param ]( param, element ) : true;
5275		},
5276
5277		dependTypes: {
5278			"boolean": function( param ) {
5279				return param;
5280			},
5281			"string": function( param, element ) {
5282				return !!$( param, element.form ).length;
5283			},
5284			"function": function( param, element ) {
5285				return param( element );
5286			}
5287		},
5288
5289		optional: function( element ) {
5290			var val = this.elementValue( element );
5291			return !$.validator.methods.required.call( this, val, element ) && "dependency-mismatch";
5292		},
5293
5294		startRequest: function( element ) {
5295			if ( !this.pending[ element.name ] ) {
5296				this.pendingRequest++;
5297				$( element ).addClass( this.settings.pendingClass );
5298				this.pending[ element.name ] = true;
5299			}
5300		},
5301
5302		stopRequest: function( element, valid ) {
5303			this.pendingRequest--;
5304
5305			// Sometimes synchronization fails, make sure pendingRequest is never < 0
5306			if ( this.pendingRequest < 0 ) {
5307				this.pendingRequest = 0;
5308			}
5309			delete this.pending[ element.name ];
5310			$( element ).removeClass( this.settings.pendingClass );
5311			if ( valid && this.pendingRequest === 0 && this.formSubmitted && this.form() ) {
5312				$( this.currentForm ).submit();
5313				this.formSubmitted = false;
5314			} else if ( !valid && this.pendingRequest === 0 && this.formSubmitted ) {
5315				$( this.currentForm ).triggerHandler( "invalid-form", [ this ] );
5316				this.formSubmitted = false;
5317			}
5318		},
5319
5320		previousValue: function( element, method ) {
5321			method = typeof method === "string" && method || "remote";
5322
5323			return $.data( element, "previousValue" ) || $.data( element, "previousValue", {
5324				old: null,
5325				valid: true,
5326				message: this.defaultMessage( element, { method: method } )
5327			} );
5328		},
5329
5330		// Cleans up all forms and elements, removes validator-specific events
5331		destroy: function() {
5332			this.resetForm();
5333
5334			$( this.currentForm )
5335				.off( ".validate" )
5336				.removeData( "validator" )
5337				.find( ".validate-equalTo-blur" )
5338					.off( ".validate-equalTo" )
5339					.removeClass( "validate-equalTo-blur" );
5340		}
5341
5342	},
5343
5344	classRuleSettings: {
5345		required: { required: true },
5346		email: { email: true },
5347		url: { url: true },
5348		date: { date: true },
5349		dateISO: { dateISO: true },
5350		number: { number: true },
5351		digits: { digits: true },
5352		creditcard: { creditcard: true }
5353	},
5354
5355	addClassRules: function( className, rules ) {
5356		if ( className.constructor === String ) {
5357			this.classRuleSettings[ className ] = rules;
5358		} else {
5359			$.extend( this.classRuleSettings, className );
5360		}
5361	},
5362
5363	classRules: function( element ) {
5364		var rules = {},
5365			classes = $( element ).attr( "class" );
5366
5367		if ( classes ) {
5368			$.each( classes.split( " " ), function() {
5369				if ( this in $.validator.classRuleSettings ) {
5370					$.extend( rules, $.validator.classRuleSettings[ this ] );
5371				}
5372			} );
5373		}
5374		return rules;
5375	},
5376
5377	normalizeAttributeRule: function( rules, type, method, value ) {
5378
5379		// Convert the value to a number for number inputs, and for text for backwards compability
5380		// allows type="date" and others to be compared as strings
5381		if ( /min|max|step/.test( method ) && ( type === null || /number|range|text/.test( type ) ) ) {
5382			value = Number( value );
5383
5384			// Support Opera Mini, which returns NaN for undefined minlength
5385			if ( isNaN( value ) ) {
5386				value = undefined;
5387			}
5388		}
5389
5390		if ( value || value === 0 ) {
5391			rules[ method ] = value;
5392		} else if ( type === method && type !== "range" ) {
5393
5394			// Exception: the jquery validate 'range' method
5395			// does not test for the html5 'range' type
5396			rules[ method ] = true;
5397		}
5398	},
5399
5400	attributeRules: function( element ) {
5401		var rules = {},
5402			$element = $( element ),
5403			type = element.getAttribute( "type" ),
5404			method, value;
5405
5406		for ( method in $.validator.methods ) {
5407
5408			// Support for <input required> in both html5 and older browsers
5409			if ( method === "required" ) {
5410				value = element.getAttribute( method );
5411
5412				// Some browsers return an empty string for the required attribute
5413				// and non-HTML5 browsers might have required="" markup
5414				if ( value === "" ) {
5415					value = true;
5416				}
5417
5418				// Force non-HTML5 browsers to return bool
5419				value = !!value;
5420			} else {
5421				value = $element.attr( method );
5422			}
5423
5424			this.normalizeAttributeRule( rules, type, method, value );
5425		}
5426
5427		// 'maxlength' may be returned as -1, 2147483647 ( IE ) and 524288 ( safari ) for text inputs
5428		if ( rules.maxlength && /-1|2147483647|524288/.test( rules.maxlength ) ) {
5429			delete rules.maxlength;
5430		}
5431
5432		return rules;
5433	},
5434
5435	dataRules: function( element ) {
5436		var rules = {},
5437			$element = $( element ),
5438			type = element.getAttribute( "type" ),
5439			method, value;
5440
5441		for ( method in $.validator.methods ) {
5442			value = $element.data( "rule" + method.charAt( 0 ).toUpperCase() + method.substring( 1 ).toLowerCase() );
5443			this.normalizeAttributeRule( rules, type, method, value );
5444		}
5445		return rules;
5446	},
5447
5448	staticRules: function( element ) {
5449		var rules = {},
5450			validator = $.data( element.form, "validator" );
5451
5452		if ( validator.settings.rules ) {
5453			rules = $.validator.normalizeRule( validator.settings.rules[ element.name ] ) || {};
5454		}
5455		return rules;
5456	},
5457
5458	normalizeRules: function( rules, element ) {
5459
5460		// Handle dependency check
5461		$.each( rules, function( prop, val ) {
5462
5463			// Ignore rule when param is explicitly false, eg. required:false
5464			if ( val === false ) {
5465				delete rules[ prop ];
5466				return;
5467			}
5468			if ( val.param || val.depends ) {
5469				var keepRule = true;
5470				switch ( typeof val.depends ) {
5471				case "string":
5472					keepRule = !!$( val.depends, element.form ).length;
5473					break;
5474				case "function":
5475					keepRule = val.depends.call( element, element );
5476					break;
5477				}
5478				if ( keepRule ) {
5479					rules[ prop ] = val.param !== undefined ? val.param : true;
5480				} else {
5481					$.data( element.form, "validator" ).resetElements( $( element ) );
5482					delete rules[ prop ];
5483				}
5484			}
5485		} );
5486
5487		// Evaluate parameters
5488		$.each( rules, function( rule, parameter ) {
5489			rules[ rule ] = $.isFunction( parameter ) && rule !== "normalizer" ? parameter( element ) : parameter;
5490		} );
5491
5492		// Clean number parameters
5493		$.each( [ "minlength", "maxlength" ], function() {
5494			if ( rules[ this ] ) {
5495				rules[ this ] = Number( rules[ this ] );
5496			}
5497		} );
5498		$.each( [ "rangelength", "range" ], function() {
5499			var parts;
5500			if ( rules[ this ] ) {
5501				if ( $.isArray( rules[ this ] ) ) {
5502					rules[ this ] = [ Number( rules[ this ][ 0 ] ), Number( rules[ this ][ 1 ] ) ];
5503				} else if ( typeof rules[ this ] === "string" ) {
5504					parts = rules[ this ].replace( /[\[\]]/g, "" ).split( /[\s,]+/ );
5505					rules[ this ] = [ Number( parts[ 0 ] ), Number( parts[ 1 ] ) ];
5506				}
5507			}
5508		} );
5509
5510		if ( $.validator.autoCreateRanges ) {
5511
5512			// Auto-create ranges
5513			if ( rules.min != null && rules.max != null ) {
5514				rules.range = [ rules.min, rules.max ];
5515				delete rules.min;
5516				delete rules.max;
5517			}
5518			if ( rules.minlength != null && rules.maxlength != null ) {
5519				rules.rangelength = [ rules.minlength, rules.maxlength ];
5520				delete rules.minlength;
5521				delete rules.maxlength;
5522			}
5523		}
5524
5525		return rules;
5526	},
5527
5528	// Converts a simple string to a {string: true} rule, e.g., "required" to {required:true}
5529	normalizeRule: function( data ) {
5530		if ( typeof data === "string" ) {
5531			var transformed = {};
5532			$.each( data.split( /\s/ ), function() {
5533				transformed[ this ] = true;
5534			} );
5535			data = transformed;
5536		}
5537		return data;
5538	},
5539
5540	// http://jqueryvalidation.org/jQuery.validator.addMethod/
5541	addMethod: function( name, method, message ) {
5542		$.validator.methods[ name ] = method;
5543		$.validator.messages[ name ] = message !== undefined ? message : $.validator.messages[ name ];
5544		if ( method.length < 3 ) {
5545			$.validator.addClassRules( name, $.validator.normalizeRule( name ) );
5546		}
5547	},
5548
5549	// http://jqueryvalidation.org/jQuery.validator.methods/
5550	methods: {
5551
5552		// http://jqueryvalidation.org/required-method/
5553		required: function( value, element, param ) {
5554
5555			// Check if dependency is met
5556			if ( !this.depend( param, element ) ) {
5557				return "dependency-mismatch";
5558			}
5559			if ( element.nodeName.toLowerCase() === "select" ) {
5560
5561				// Could be an array for select-multiple or a string, both are fine this way
5562				var val = $( element ).val();
5563				return val && val.length > 0;
5564			}
5565			if ( this.checkable( element ) ) {
5566				return this.getLength( value, element ) > 0;
5567			}
5568			return value.length > 0;
5569		},
5570
5571		// http://jqueryvalidation.org/email-method/
5572		email: function( value, element ) {
5573
5574			// From https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address
5575			// Retrieved 2014-01-14
5576			// If you have a problem with this implementation, report a bug against the above spec
5577			// Or use custom methods to implement your own email validation
5578			return this.optional( element ) || /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test( value );
5579		},
5580
5581		// http://jqueryvalidation.org/url-method/
5582		url: function( value, element ) {
5583
5584			// Copyright (c) 2010-2013 Diego Perini, MIT licensed
5585			// https://gist.github.com/dperini/729294
5586			// see also https://mathiasbynens.be/demo/url-regex
5587			// modified to allow protocol-relative URLs
5588			return this.optional( element ) || /^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[/?#]\S*)?$/i.test( value );
5589		},
5590
5591		// http://jqueryvalidation.org/date-method/
5592		date: function( value, element ) {
5593			return this.optional( element ) || !/Invalid|NaN/.test( new Date( value ).toString() );
5594		},
5595
5596		// http://jqueryvalidation.org/dateISO-method/
5597		dateISO: function( value, element ) {
5598			return this.optional( element ) || /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test( value );
5599		},
5600
5601		// http://jqueryvalidation.org/number-method/
5602		number: function( value, element ) {
5603			return this.optional( element ) || /^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test( value );
5604		},
5605
5606		// http://jqueryvalidation.org/digits-method/
5607		digits: function( value, element ) {
5608			return this.optional( element ) || /^\d+$/.test( value );
5609		},
5610
5611		// http://jqueryvalidation.org/minlength-method/
5612		minlength: function( value, element, param ) {
5613			var length = $.isArray( value ) ? value.length : this.getLength( value, element );
5614			return this.optional( element ) || length >= param;
5615		},
5616
5617		// http://jqueryvalidation.org/maxlength-method/
5618		maxlength: function( value, element, param ) {
5619			var length = $.isArray( value ) ? value.length : this.getLength( value, element );
5620			return this.optional( element ) || length <= param;
5621		},
5622
5623		// http://jqueryvalidation.org/rangelength-method/
5624		rangelength: function( value, element, param ) {
5625			var length = $.isArray( value ) ? value.length : this.getLength( value, element );
5626			return this.optional( element ) || ( length >= param[ 0 ] && length <= param[ 1 ] );
5627		},
5628
5629		// http://jqueryvalidation.org/min-method/
5630		min: function( value, element, param ) {
5631			return this.optional( element ) || value >= param;
5632		},
5633
5634		// http://jqueryvalidation.org/max-method/
5635		max: function( value, element, param ) {
5636			return this.optional( element ) || value <= param;
5637		},
5638
5639		// http://jqueryvalidation.org/range-method/
5640		range: function( value, element, param ) {
5641			return this.optional( element ) || ( value >= param[ 0 ] && value <= param[ 1 ] );
5642		},
5643
5644		// http://jqueryvalidation.org/step-method/
5645		step: function( value, element, param ) {
5646			var type = $( element ).attr( "type" ),
5647				errorMessage = "Step attribute on input type " + type + " is not supported.",
5648				supportedTypes = [ "text", "number", "range" ],
5649				re = new RegExp( "\\b" + type + "\\b" ),
5650				notSupported = type && !re.test( supportedTypes.join() ),
5651				decimalPlaces = function( num ) {
5652					var match = ( "" + num ).match( /(?:\.(\d+))?$/ );
5653					if ( !match ) {
5654						return 0;
5655					}
5656
5657					// Number of digits right of decimal point.
5658					return match[ 1 ] ? match[ 1 ].length : 0;
5659				},
5660				toInt = function( num ) {
5661					return Math.round( num * Math.pow( 10, decimals ) );
5662				},
5663				valid = true,
5664				decimals;
5665
5666			// Works only for text, number and range input types
5667			// TODO find a way to support input types date, datetime, datetime-local, month, time and week
5668			if ( notSupported ) {
5669				throw new Error( errorMessage );
5670			}
5671
5672			decimals = decimalPlaces( param );
5673
5674			// Value can't have too many decimals
5675			if ( decimalPlaces( value ) > decimals || toInt( value ) % toInt( param ) !== 0 ) {
5676				valid = false;
5677			}
5678
5679			return this.optional( element ) || valid;
5680		},
5681
5682		// http://jqueryvalidation.org/equalTo-method/
5683		equalTo: function( value, element, param ) {
5684
5685			// Bind to the blur event of the target in order to revalidate whenever the target field is updated
5686			var target = $( param );
5687			if ( this.settings.onfocusout && target.not( ".validate-equalTo-blur" ).length ) {
5688				target.addClass( "validate-equalTo-blur" ).on( "blur.validate-equalTo", function() {
5689					$( element ).valid();
5690				} );
5691			}
5692			return value === target.val();
5693		},
5694
5695		// http://jqueryvalidation.org/remote-method/
5696		remote: function( value, element, param, method ) {
5697			if ( this.optional( element ) ) {
5698				return "dependency-mismatch";
5699			}
5700
5701			method = typeof method === "string" && method || "remote";
5702
5703			var previous = this.previousValue( element, method ),
5704				validator, data, optionDataString;
5705
5706			if ( !this.settings.messages[ element.name ] ) {
5707				this.settings.messages[ element.name ] = {};
5708			}
5709			previous.originalMessage = previous.originalMessage || this.settings.messages[ element.name ][ method ];
5710			this.settings.messages[ element.name ][ method ] = previous.message;
5711
5712			param = typeof param === "string" && { url: param } || param;
5713			optionDataString = $.param( $.extend( { data: value }, param.data ) );
5714			if ( previous.old === optionDataString ) {
5715				return previous.valid;
5716			}
5717
5718			previous.old = optionDataString;
5719			validator = this;
5720			this.startRequest( element );
5721			data = {};
5722			data[ element.name ] = value;
5723			$.ajax( $.extend( true, {
5724				mode: "abort",
5725				port: "validate" + element.name,
5726				dataType: "json",
5727				data: data,
5728				context: validator.currentForm,
5729				success: function( response ) {
5730					var valid = response === true || response === "true",
5731						errors, message, submitted;
5732
5733					validator.settings.messages[ element.name ][ method ] = previous.originalMessage;
5734					if ( valid ) {
5735						submitted = validator.formSubmitted;
5736						validator.resetInternals();
5737						validator.toHide = validator.errorsFor( element );
5738						validator.formSubmitted = submitted;
5739						validator.successList.push( element );
5740						validator.invalid[ element.name ] = false;
5741						validator.showErrors();
5742					} else {
5743						errors = {};
5744						message = response || validator.defaultMessage( element, { method: method, parameters: value } );
5745						errors[ element.name ] = previous.message = message;
5746						validator.invalid[ element.name ] = true;
5747						validator.showErrors( errors );
5748					}
5749					previous.valid = valid;
5750					validator.stopRequest( element, valid );
5751				}
5752			}, param ) );
5753			return "pending";
5754		}
5755	}
5756
5757} );
5758
5759// Ajax mode: abort
5760// usage: $.ajax({ mode: "abort"[, port: "uniqueport"]});
5761// if mode:"abort" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort()
5762
5763var pendingRequests = {},
5764	ajax;
5765
5766// Use a prefilter if available (1.5+)
5767if ( $.ajaxPrefilter ) {
5768	$.ajaxPrefilter( function( settings, _, xhr ) {
5769		var port = settings.port;
5770		if ( settings.mode === "abort" ) {
5771			if ( pendingRequests[ port ] ) {
5772				pendingRequests[ port ].abort();
5773			}
5774			pendingRequests[ port ] = xhr;
5775		}
5776	} );
5777} else {
5778
5779	// Proxy ajax
5780	ajax = $.ajax;
5781	$.ajax = function( settings ) {
5782		var mode = ( "mode" in settings ? settings : $.ajaxSettings ).mode,
5783			port = ( "port" in settings ? settings : $.ajaxSettings ).port;
5784		if ( mode === "abort" ) {
5785			if ( pendingRequests[ port ] ) {
5786				pendingRequests[ port ].abort();
5787			}
5788			pendingRequests[ port ] = ajax.apply( this, arguments );
5789			return pendingRequests[ port ];
5790		}
5791		return ajax.apply( this, arguments );
5792	};
5793}
5794
5795}));
5796/**
5797 * @license AngularJS v1.5.11
5798 * (c) 2010-2017 Google, Inc. http://angularjs.org
5799 * License: MIT
5800 */
5801(function(window) {'use strict';
5802
5803/**
5804 * @description
5805 *
5806 * This object provides a utility for producing rich Error messages within
5807 * Angular. It can be called as follows:
5808 *
5809 * var exampleMinErr = minErr('example');
5810 * throw exampleMinErr('one', 'This {0} is {1}', foo, bar);
5811 *
5812 * The above creates an instance of minErr in the example namespace. The
5813 * resulting error will have a namespaced error code of example.one.  The
5814 * resulting error will replace {0} with the value of foo, and {1} with the
5815 * value of bar. The object is not restricted in the number of arguments it can
5816 * take.
5817 *
5818 * If fewer arguments are specified than necessary for interpolation, the extra
5819 * interpolation markers will be preserved in the final string.
5820 *
5821 * Since data will be parsed statically during a build step, some restrictions
5822 * are applied with respect to how minErr instances are created and called.
5823 * Instances should have names of the form namespaceMinErr for a minErr created
5824 * using minErr('namespace') . Error codes, namespaces and template strings
5825 * should all be static strings, not variables or general expressions.
5826 *
5827 * @param {string} module The namespace to use for the new minErr instance.
5828 * @param {function} ErrorConstructor Custom error constructor to be instantiated when returning
5829 *   error from returned function, for cases when a particular type of error is useful.
5830 * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance
5831 */
5832
5833function minErr(module, ErrorConstructor) {
5834  ErrorConstructor = ErrorConstructor || Error;
5835  return function() {
5836    var SKIP_INDEXES = 2;
5837
5838    var templateArgs = arguments,
5839      code = templateArgs[0],
5840      message = '[' + (module ? module + ':' : '') + code + '] ',
5841      template = templateArgs[1],
5842      paramPrefix, i;
5843
5844    message += template.replace(/\{\d+\}/g, function(match) {
5845      var index = +match.slice(1, -1),
5846        shiftedIndex = index + SKIP_INDEXES;
5847
5848      if (shiftedIndex < templateArgs.length) {
5849        return toDebugString(templateArgs[shiftedIndex]);
5850      }
5851
5852      return match;
5853    });
5854
5855    message += '\nhttp://errors.angularjs.org/1.5.11/' +
5856      (module ? module + '/' : '') + code;
5857
5858    for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
5859      message += paramPrefix + 'p' + (i - SKIP_INDEXES) + '=' +
5860        encodeURIComponent(toDebugString(templateArgs[i]));
5861    }
5862
5863    return new ErrorConstructor(message);
5864  };
5865}
5866
5867/* We need to tell ESLint what variables are being exported */
5868/* exported
5869  angular,
5870  msie,
5871  jqLite,
5872  jQuery,
5873  slice,
5874  splice,
5875  push,
5876  toString,
5877  ngMinErr,
5878  angularModule,
5879  uid,
5880  REGEX_STRING_REGEXP,
5881  VALIDITY_STATE_PROPERTY,
5882
5883  lowercase,
5884  uppercase,
5885  manualLowercase,
5886  manualUppercase,
5887  nodeName_,
5888  isArrayLike,
5889  forEach,
5890  forEachSorted,
5891  reverseParams,
5892  nextUid,
5893  setHashKey,
5894  extend,
5895  toInt,
5896  inherit,
5897  merge,
5898  noop,
5899  identity,
5900  valueFn,
5901  isUndefined,
5902  isDefined,
5903  isObject,
5904  isBlankObject,
5905  isString,
5906  isNumber,
5907  isNumberNaN,
5908  isDate,
5909  isArray,
5910  isFunction,
5911  isRegExp,
5912  isWindow,
5913  isScope,
5914  isFile,
5915  isFormData,
5916  isBlob,
5917  isBoolean,
5918  isPromiseLike,
5919  trim,
5920  escapeForRegexp,
5921  isElement,
5922  makeMap,
5923  includes,
5924  arrayRemove,
5925  copy,
5926  equals,
5927  csp,
5928  jq,
5929  concat,
5930  sliceArgs,
5931  bind,
5932  toJsonReplacer,
5933  toJson,
5934  fromJson,
5935  convertTimezoneToLocal,
5936  timezoneToOffset,
5937  startingTag,
5938  tryDecodeURIComponent,
5939  parseKeyValue,
5940  toKeyValue,
5941  encodeUriSegment,
5942  encodeUriQuery,
5943  angularInit,
5944  bootstrap,
5945  getTestability,
5946  snake_case,
5947  bindJQuery,
5948  assertArg,
5949  assertArgFn,
5950  assertNotHasOwnProperty,
5951  getter,
5952  getBlockNodes,
5953  hasOwnProperty,
5954  createMap,
5955
5956  NODE_TYPE_ELEMENT,
5957  NODE_TYPE_ATTRIBUTE,
5958  NODE_TYPE_TEXT,
5959  NODE_TYPE_COMMENT,
5960  NODE_TYPE_DOCUMENT,
5961  NODE_TYPE_DOCUMENT_FRAGMENT
5962*/
5963
5964////////////////////////////////////
5965
5966/**
5967 * @ngdoc module
5968 * @name ng
5969 * @module ng
5970 * @installation
5971 * @description
5972 *
5973 * # ng (core module)
5974 * The ng module is loaded by default when an AngularJS application is started. The module itself
5975 * contains the essential components for an AngularJS application to function. The table below
5976 * lists a high level breakdown of each of the services/factories, filters, directives and testing
5977 * components available within this core module.
5978 *
5979 * <div doc-module-components="ng"></div>
5980 */
5981
5982var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/;
5983
5984// The name of a form control's ValidityState property.
5985// This is used so that it's possible for internal tests to create mock ValidityStates.
5986var VALIDITY_STATE_PROPERTY = 'validity';
5987
5988var hasOwnProperty = Object.prototype.hasOwnProperty;
5989
5990var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
5991var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
5992
5993
5994var manualLowercase = function(s) {
5995  /* eslint-disable no-bitwise */
5996  return isString(s)
5997      ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
5998      : s;
5999  /* eslint-enable */
6000};
6001var manualUppercase = function(s) {
6002  /* eslint-disable no-bitwise */
6003  return isString(s)
6004      ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
6005      : s;
6006  /* eslint-enable */
6007};
6008
6009
6010// String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
6011// locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
6012// with correct but slower alternatives. See https://github.com/angular/angular.js/issues/11387
6013if ('i' !== 'I'.toLowerCase()) {
6014  lowercase = manualLowercase;
6015  uppercase = manualUppercase;
6016}
6017
6018
6019var
6020    msie,             // holds major version number for IE, or NaN if UA is not IE.
6021    jqLite,           // delay binding since jQuery could be loaded after us.
6022    jQuery,           // delay binding
6023    slice             = [].slice,
6024    splice            = [].splice,
6025    push              = [].push,
6026    toString          = Object.prototype.toString,
6027    getPrototypeOf    = Object.getPrototypeOf,
6028    ngMinErr          = minErr('ng'),
6029
6030    /** @name angular */
6031    angular           = window.angular || (window.angular = {}),
6032    angularModule,
6033    uid               = 0;
6034
6035/**
6036 * documentMode is an IE-only property
6037 * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
6038 */
6039msie = window.document.documentMode;
6040
6041
6042/**
6043 * @private
6044 * @param {*} obj
6045 * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,
6046 *                   String ...)
6047 */
6048function isArrayLike(obj) {
6049
6050  // `null`, `undefined` and `window` are not array-like
6051  if (obj == null || isWindow(obj)) return false;
vendor: 15,053 bytes, lines 6052-6624
6052
6053  // arrays, strings and jQuery/jqLite objects are array like
6054  // * jqLite is either the jQuery or jqLite constructor function
6055  // * we have to check the existence of jqLite first as this method is called
6056  //   via the forEach method when constructing the jqLite object in the first place
6057  if (isArray(obj) || isString(obj) || (jqLite && obj instanceof jqLite)) return true;
6058
6059  // Support: iOS 8.2 (not reproducible in simulator)
6060  // "length" in obj used to prevent JIT error (gh-11508)
6061  var length = 'length' in Object(obj) && obj.length;
6062
6063  // NodeList objects (with `item` method) and
6064  // other objects with suitable length characteristics are array-like
6065  return isNumber(length) &&
6066    (length >= 0 && ((length - 1) in obj || obj instanceof Array) || typeof obj.item === 'function');
6067
6068}
6069
6070/**
6071 * @ngdoc function
6072 * @name angular.forEach
6073 * @module ng
6074 * @kind function
6075 *
6076 * @description
6077 * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
6078 * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value`
6079 * is the value of an object property or an array element, `key` is the object property key or
6080 * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional.
6081 *
6082 * It is worth noting that `.forEach` does not iterate over inherited properties because it filters
6083 * using the `hasOwnProperty` method.
6084 *
6085 * Unlike ES262's
6086 * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
6087 * providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
6088 * return the value provided.
6089 *
6090   ```js
6091     var values = {name: 'misko', gender: 'male'};
6092     var log = [];
6093     angular.forEach(values, function(value, key) {
6094       this.push(key + ': ' + value);
6095     }, log);
6096     expect(log).toEqual(['name: misko', 'gender: male']);
6097   ```
6098 *
6099 * @param {Object|Array} obj Object to iterate over.
6100 * @param {Function} iterator Iterator function.
6101 * @param {Object=} context Object to become context (`this`) for the iterator function.
6102 * @returns {Object|Array} Reference to `obj`.
6103 */
6104
6105function forEach(obj, iterator, context) {
6106  var key, length;
6107  if (obj) {
6108    if (isFunction(obj)) {
6109      for (key in obj) {
6110        // Need to check if hasOwnProperty exists,
6111        // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
6112        if (key !== 'prototype' && key !== 'length' && key !== 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
6113          iterator.call(context, obj[key], key, obj);
6114        }
6115      }
6116    } else if (isArray(obj) || isArrayLike(obj)) {
6117      var isPrimitive = typeof obj !== 'object';
6118      for (key = 0, length = obj.length; key < length; key++) {
6119        if (isPrimitive || key in obj) {
6120          iterator.call(context, obj[key], key, obj);
6121        }
6122      }
6123    } else if (obj.forEach && obj.forEach !== forEach) {
6124        obj.forEach(iterator, context, obj);
6125    } else if (isBlankObject(obj)) {
6126      // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
6127      for (key in obj) {
6128        iterator.call(context, obj[key], key, obj);
6129      }
6130    } else if (typeof obj.hasOwnProperty === 'function') {
6131      // Slow path for objects inheriting Object.prototype, hasOwnProperty check needed
6132      for (key in obj) {
6133        if (obj.hasOwnProperty(key)) {
6134          iterator.call(context, obj[key], key, obj);
6135        }
6136      }
6137    } else {
6138      // Slow path for objects which do not have a method `hasOwnProperty`
6139      for (key in obj) {
6140        if (hasOwnProperty.call(obj, key)) {
6141          iterator.call(context, obj[key], key, obj);
6142        }
6143      }
6144    }
6145  }
6146  return obj;
6147}
6148
6149function forEachSorted(obj, iterator, context) {
6150  var keys = Object.keys(obj).sort();
6151  for (var i = 0; i < keys.length; i++) {
6152    iterator.call(context, obj[keys[i]], keys[i]);
6153  }
6154  return keys;
6155}
6156
6157
6158/**
6159 * when using forEach the params are value, key, but it is often useful to have key, value.
6160 * @param {function(string, *)} iteratorFn
6161 * @returns {function(*, string)}
6162 */
6163function reverseParams(iteratorFn) {
6164  return function(value, key) {iteratorFn(key, value);};
6165}
6166
6167/**
6168 * A consistent way of creating unique IDs in angular.
6169 *
6170 * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before
6171 * we hit number precision issues in JavaScript.
6172 *
6173 * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M
6174 *
6175 * @returns {number} an unique alpha-numeric string
6176 */
6177function nextUid() {
6178  return ++uid;
6179}
6180
6181
6182/**
6183 * Set or clear the hashkey for an object.
6184 * @param obj object
6185 * @param h the hashkey (!truthy to delete the hashkey)
6186 */
6187function setHashKey(obj, h) {
6188  if (h) {
6189    obj.$$hashKey = h;
6190  } else {
6191    delete obj.$$hashKey;
6192  }
6193}
6194
6195
6196function baseExtend(dst, objs, deep) {
6197  var h = dst.$$hashKey;
6198
6199  for (var i = 0, ii = objs.length; i < ii; ++i) {
6200    var obj = objs[i];
6201    if (!isObject(obj) && !isFunction(obj)) continue;
6202    var keys = Object.keys(obj);
6203    for (var j = 0, jj = keys.length; j < jj; j++) {
6204      var key = keys[j];
6205      var src = obj[key];
6206
6207      if (deep && isObject(src)) {
6208        if (isDate(src)) {
6209          dst[key] = new Date(src.valueOf());
6210        } else if (isRegExp(src)) {
6211          dst[key] = new RegExp(src);
6212        } else if (src.nodeName) {
6213          dst[key] = src.cloneNode(true);
6214        } else if (isElement(src)) {
6215          dst[key] = src.clone();
6216        } else {
6217          if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
6218          baseExtend(dst[key], [src], true);
6219        }
6220      } else {
6221        dst[key] = src;
6222      }
6223    }
6224  }
6225
6226  setHashKey(dst, h);
6227  return dst;
6228}
6229
6230/**
6231 * @ngdoc function
6232 * @name angular.extend
6233 * @module ng
6234 * @kind function
6235 *
6236 * @description
6237 * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
6238 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
6239 * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`.
6240 *
6241 * **Note:** Keep in mind that `angular.extend` does not support recursive merge (deep copy). Use
6242 * {@link angular.merge} for this.
6243 *
6244 * @param {Object} dst Destination object.
6245 * @param {...Object} src Source object(s).
6246 * @returns {Object} Reference to `dst`.
6247 */
6248function extend(dst) {
6249  return baseExtend(dst, slice.call(arguments, 1), false);
6250}
6251
6252
6253/**
6254* @ngdoc function
6255* @name angular.merge
6256* @module ng
6257* @kind function
6258*
6259* @description
6260* Deeply extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
6261* to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
6262* by passing an empty object as the target: `var object = angular.merge({}, object1, object2)`.
6263*
6264* Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source
6265* objects, performing a deep copy.
6266*
6267* @param {Object} dst Destination object.
6268* @param {...Object} src Source object(s).
6269* @returns {Object} Reference to `dst`.
6270*/
6271function merge(dst) {
6272  return baseExtend(dst, slice.call(arguments, 1), true);
6273}
6274
6275
6276
6277function toInt(str) {
6278  return parseInt(str, 10);
6279}
6280
6281var isNumberNaN = Number.isNaN || function isNumberNaN(num) {
6282  // eslint-disable-next-line no-self-compare
6283  return num !== num;
6284};
6285
6286
6287function inherit(parent, extra) {
6288  return extend(Object.create(parent), extra);
6289}
6290
6291/**
6292 * @ngdoc function
6293 * @name angular.noop
6294 * @module ng
6295 * @kind function
6296 *
6297 * @description
6298 * A function that performs no operations. This function can be useful when writing code in the
6299 * functional style.
6300   ```js
6301     function foo(callback) {
6302       var result = calculateResult();
6303       (callback || angular.noop)(result);
6304     }
6305   ```
6306 */
6307function noop() {}
6308noop.$inject = [];
6309
6310
6311/**
6312 * @ngdoc function
6313 * @name angular.identity
6314 * @module ng
6315 * @kind function
6316 *
6317 * @description
6318 * A function that returns its first argument. This function is useful when writing code in the
6319 * functional style.
6320 *
6321   ```js
6322   function transformer(transformationFn, value) {
6323     return (transformationFn || angular.identity)(value);
6324   };
6325
6326   // E.g.
6327   function getResult(fn, input) {
6328     return (fn || angular.identity)(input);
6329   };
6330
6331   getResult(function(n) { return n * 2; }, 21);   // returns 42
6332   getResult(null, 21);                            // returns 21
6333   getResult(undefined, 21);                       // returns 21
6334   ```
6335 *
6336 * @param {*} value to be returned.
6337 * @returns {*} the value passed in.
6338 */
6339function identity($) {return $;}
6340identity.$inject = [];
6341
6342
6343function valueFn(value) {return function valueRef() {return value;};}
6344
6345function hasCustomToString(obj) {
6346  return isFunction(obj.toString) && obj.toString !== toString;
6347}
6348
6349
6350/**
6351 * @ngdoc function
6352 * @name angular.isUndefined
6353 * @module ng
6354 * @kind function
6355 *
6356 * @description
6357 * Determines if a reference is undefined.
6358 *
6359 * @param {*} value Reference to check.
6360 * @returns {boolean} True if `value` is undefined.
6361 */
6362function isUndefined(value) {return typeof value === 'undefined';}
6363
6364
6365/**
6366 * @ngdoc function
6367 * @name angular.isDefined
6368 * @module ng
6369 * @kind function
6370 *
6371 * @description
6372 * Determines if a reference is defined.
6373 *
6374 * @param {*} value Reference to check.
6375 * @returns {boolean} True if `value` is defined.
6376 */
6377function isDefined(value) {return typeof value !== 'undefined';}
6378
6379
6380/**
6381 * @ngdoc function
6382 * @name angular.isObject
6383 * @module ng
6384 * @kind function
6385 *
6386 * @description
6387 * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
6388 * considered to be objects. Note that JavaScript arrays are objects.
6389 *
6390 * @param {*} value Reference to check.
6391 * @returns {boolean} True if `value` is an `Object` but not `null`.
6392 */
6393function isObject(value) {
6394  // http://jsperf.com/isobject4
6395  return value !== null && typeof value === 'object';
6396}
6397
6398
6399/**
6400 * Determine if a value is an object with a null prototype
6401 *
6402 * @returns {boolean} True if `value` is an `Object` with a null prototype
6403 */
6404function isBlankObject(value) {
6405  return value !== null && typeof value === 'object' && !getPrototypeOf(value);
6406}
6407
6408
6409/**
6410 * @ngdoc function
6411 * @name angular.isString
6412 * @module ng
6413 * @kind function
6414 *
6415 * @description
6416 * Determines if a reference is a `String`.
6417 *
6418 * @param {*} value Reference to check.
6419 * @returns {boolean} True if `value` is a `String`.
6420 */
6421function isString(value) {return typeof value === 'string';}
6422
6423
6424/**
6425 * @ngdoc function
6426 * @name angular.isNumber
6427 * @module ng
6428 * @kind function
6429 *
6430 * @description
6431 * Determines if a reference is a `Number`.
6432 *
6433 * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`.
6434 *
6435 * If you wish to exclude these then you can use the native
6436 * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite)
6437 * method.
6438 *
6439 * @param {*} value Reference to check.
6440 * @returns {boolean} True if `value` is a `Number`.
6441 */
6442function isNumber(value) {return typeof value === 'number';}
6443
6444
6445/**
6446 * @ngdoc function
6447 * @name angular.isDate
6448 * @module ng
6449 * @kind function
6450 *
6451 * @description
6452 * Determines if a value is a date.
6453 *
6454 * @param {*} value Reference to check.
6455 * @returns {boolean} True if `value` is a `Date`.
6456 */
6457function isDate(value) {
6458  return toString.call(value) === '[object Date]';
6459}
6460
6461
6462/**
6463 * @ngdoc function
6464 * @name angular.isArray
6465 * @module ng
6466 * @kind function
6467 *
6468 * @description
6469 * Determines if a reference is an `Array`. Alias of Array.isArray.
6470 *
6471 * @param {*} value Reference to check.
6472 * @returns {boolean} True if `value` is an `Array`.
6473 */
6474var isArray = Array.isArray;
6475
6476/**
6477 * @ngdoc function
6478 * @name angular.isFunction
6479 * @module ng
6480 * @kind function
6481 *
6482 * @description
6483 * Determines if a reference is a `Function`.
6484 *
6485 * @param {*} value Reference to check.
6486 * @returns {boolean} True if `value` is a `Function`.
6487 */
6488function isFunction(value) {return typeof value === 'function';}
6489
6490
6491/**
6492 * Determines if a value is a regular expression object.
6493 *
6494 * @private
6495 * @param {*} value Reference to check.
6496 * @returns {boolean} True if `value` is a `RegExp`.
6497 */
6498function isRegExp(value) {
6499  return toString.call(value) === '[object RegExp]';
6500}
6501
6502
6503/**
6504 * Checks if `obj` is a window object.
6505 *
6506 * @private
6507 * @param {*} obj Object to check
6508 * @returns {boolean} True if `obj` is a window obj.
6509 */
6510function isWindow(obj) {
6511  return obj && obj.window === obj;
6512}
6513
6514
6515function isScope(obj) {
6516  return obj && obj.$evalAsync && obj.$watch;
6517}
6518
6519
6520function isFile(obj) {
6521  return toString.call(obj) === '[object File]';
6522}
6523
6524
6525function isFormData(obj) {
6526  return toString.call(obj) === '[object FormData]';
6527}
6528
6529
6530function isBlob(obj) {
6531  return toString.call(obj) === '[object Blob]';
6532}
6533
6534
6535function isBoolean(value) {
6536  return typeof value === 'boolean';
6537}
6538
6539
6540function isPromiseLike(obj) {
6541  return obj && isFunction(obj.then);
6542}
6543
6544
6545var TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array]$/;
6546function isTypedArray(value) {
6547  return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value));
6548}
6549
6550function isArrayBuffer(obj) {
6551  return toString.call(obj) === '[object ArrayBuffer]';
6552}
6553
6554
6555var trim = function(value) {
6556  return isString(value) ? value.trim() : value;
6557};
6558
6559// Copied from:
6560// http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021
6561// Prereq: s is a string.
6562var escapeForRegexp = function(s) {
6563  return s
6564    .replace(/([-()[\]{}+?*.$^|,:#<!\\])/g, '\\$1')
6565    // eslint-disable-next-line no-control-regex
6566    .replace(/\x08/g, '\\x08');
6567};
6568
6569
6570/**
6571 * @ngdoc function
6572 * @name angular.isElement
6573 * @module ng
6574 * @kind function
6575 *
6576 * @description
6577 * Determines if a reference is a DOM element (or wrapped jQuery element).
6578 *
6579 * @param {*} value Reference to check.
6580 * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
6581 */
6582function isElement(node) {
6583  return !!(node &&
6584    (node.nodeName  // We are a direct element.
6585    || (node.prop && node.attr && node.find)));  // We have an on and find method part of jQuery API.
6586}
6587
6588/**
6589 * @param str 'key1,key2,...'
6590 * @returns {object} in the form of {key1:true, key2:true, ...}
6591 */
6592function makeMap(str) {
6593  var obj = {}, items = str.split(','), i;
6594  for (i = 0; i < items.length; i++) {
6595    obj[items[i]] = true;
6596  }
6597  return obj;
6598}
6599
6600
6601function nodeName_(element) {
6602  return lowercase(element.nodeName || (element[0] && element[0].nodeName));
6603}
6604
6605function includes(array, obj) {
6606  return Array.prototype.indexOf.call(array, obj) !== -1;
6607}
6608
6609function arrayRemove(array, value) {
6610  var index = array.indexOf(value);
6611  if (index >= 0) {
6612    array.splice(index, 1);
6613  }
6614  return index;
6615}
6616
6617/**
6618 * @ngdoc function
6619 * @name angular.copy
6620 * @module ng
6621 * @kind function
6622 *
6623 * @description
6624 * Creates a deep copy of `source`, which should be an object or an array.
6625 *
6626 * * If no destination is supplied, a copy of the object or array is created.
6627 * * If a destination is provided, all of its elements (for arrays) or properties (for objects)
6628 *   are deleted and then all elements/properties from the source are copied to it.
6629 * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
6630 * * If `source` is identical to `destination` an exception will be thrown.
6631 *
6632 * <br />
6633 * <div class="alert alert-warning">
6634 *   Only enumerable properties are taken into account. Non-enumerable properties (both on `source`
6635 *   and on `destination`) will be ignored.
6636 * </div>
6637 *
6638 * @param {*} source The source that will be used to make a copy.
6639 *                   Can be any type, including primitives, `null`, and `undefined`.
6640 * @param {(Object|Array)=} destination Destination into which the source is copied. If
6641 *     provided, must be of the same type as `source`.
6642 * @returns {*} The copy or updated `destination`, if `destination` was specified.
6643 *
6644 * @example
6645  <example module="copyExample" name="angular-copy">
6646    <file name="index.html">
6647      <div ng-controller="ExampleController">
6648        <form novalidate class="simple-form">
6649          <label>Name: <input type="text" ng-model="user.name" /></label><br />
6650          <label>Age:  <input type="number" ng-model="user.age" /></label><br />
6651          Gender: <label><input type="radio" ng-model="user.gender" value="male" />male</label>
6652                  <label><input type="radio" ng-model="user.gender" value="female" />female</label><br />
6653          <button ng-click="reset()">RESET</button>
6654          <button ng-click="update(user)">SAVE</button>
6655        </form>
6656        <pre>form = {{user | json}}</pre>
6657        <pre>master = {{master | json}}</pre>
6658      </div>
6659    </file>
6660    <file name="script.js">
6661      // Module: copyExample
6662      angular.
6663        module('copyExample', []).
6664        controller('ExampleController', ['$scope', function($scope) {
6665          $scope.master = {};
6666
6667          $scope.reset = function() {
6668            // Example with 1 argument
6669            $scope.user = angular.copy($scope.master);
6670          };
6671
6672          $scope.update = function(user) {
6673            // Example with 2 arguments
6674            angular.copy(user, $scope.master);
6675          };
6676
6677          $scope.reset();
6678        }]);
6679    </file>
6680  </example>
6681 */
6682function copy(source, destination) {
6683  var stackSource = [];
6684  var stackDest = [];
6685
6686  if (destination) {
6687    if (isTypedArray(destination) || isArrayBuffer(destination)) {
6688      throw ngMinErr('cpta', 'Can\'t copy! TypedArray destination cannot be mutated.');
6689    }
6690    if (source === destination) {
6691      throw ngMinErr('cpi', 'Can\'t copy! Source and destination are identical.');
6692    }
6693
6694    // Empty the destination object
6695    if (isArray(destination)) {
6696      destination.length = 0;
6697    } else {
6698      forEach(destination, function(value, key) {
6699        if (key !== '$$hashKey') {
6700          delete destination[key];
6701        }
6702      });
6703    }
6704
6705    stackSource.push(source);
6706    stackDest.push(destination);
6707    return copyRecurse(source, destination);
6708  }
6709
6710  return copyElement(source);
6711
6712  function copyRecurse(source, destination) {
6713    var h = destination.$$hashKey;
6714    var key;
6715    if (isArray(source)) {
6716      for (var i = 0, ii = source.length; i < ii; i++) {
6717        destination.push(copyElement(source[i]));
6718      }
6719    } else if (isBlankObject(source)) {
6720      // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
6721      for (key in source) {
6722        destination[key] = copyElement(source[key]);
6723      }
6724    } else if (source && typeof source.hasOwnProperty === 'function') {
6725      // Slow path, which must rely on hasOwnProperty
6726      for (key in source) {
6727        if (source.hasOwnProperty(key)) {
6728          destination[key] = copyElement(source[key]);
6729        }
6730      }
6731    } else {
6732      // Slowest path --- hasOwnProperty can't be called as a method
6733      for (key in source) {
6734        if (hasOwnProperty.call(source, key)) {
6735          destination[key] = copyElement(source[key]);
6736        }
6737      }
6738    }
6739    setHashKey(destination, h);
6740    return destination;
6741  }
6742
6743  function copyElement(source) {
6744    // Simple values
6745    if (!isObject(source)) {
6746      return source;
6747    }
6748
6749    // Already copied values
6750    var index = stackSource.indexOf(source);
6751    if (index !== -1) {
6752      return stackDest[index];
6753    }
6754
6755    if (isWindow(source) || isScope(source)) {
6756      throw ngMinErr('cpws',
6757        'Can\'t copy! Making copies of Window or Scope instances is not supported.');
6758    }
6759
6760    var needsRecurse = false;
6761    var destination = copyType(source);
6762
6763    if (destination === undefined) {
6764      destination = isArray(source) ? [] : Object.create(getPrototypeOf(source));
6765      needsRecurse = true;
6766    }
6767
6768    stackSource.push(source);
6769    stackDest.push(destination);
6770
6771    return needsRecurse
6772      ? copyRecurse(source, destination)
6773      : destination;
6774  }
6775
6776  function copyType(source) {
6777    switch (toString.call(source)) {
6778      case '[object Int8Array]':
6779      case '[object Int16Array]':
6780      case '[object Int32Array]':
6781      case '[object Float32Array]':
vendor: 2,512 bytes, lines 6782-6854
6782      case '[object Float64Array]':
6783      case '[object Uint8Array]':
6784      case '[object Uint8ClampedArray]':
6785      case '[object Uint16Array]':
6786      case '[object Uint32Array]':
6787        return new source.constructor(copyElement(source.buffer), source.byteOffset, source.length);
6788
6789      case '[object ArrayBuffer]':
6790        // Support: IE10
6791        if (!source.slice) {
6792          // If we're in this case we know the environment supports ArrayBuffer
6793          /* eslint-disable no-undef */
6794          var copied = new ArrayBuffer(source.byteLength);
6795          new Uint8Array(copied).set(new Uint8Array(source));
6796          /* eslint-enable */
6797          return copied;
6798        }
6799        return source.slice(0);
6800
6801      case '[object Boolean]':
6802      case '[object Number]':
6803      case '[object String]':
6804      case '[object Date]':
6805        return new source.constructor(source.valueOf());
6806
6807      case '[object RegExp]':
6808        var re = new RegExp(source.source, source.toString().match(/[^/]*$/)[0]);
6809        re.lastIndex = source.lastIndex;
6810        return re;
6811
6812      case '[object Blob]':
6813        return new source.constructor([source], {type: source.type});
6814    }
6815
6816    if (isFunction(source.cloneNode)) {
6817      return source.cloneNode(true);
6818    }
6819  }
6820}
6821
6822
6823/**
6824 * @ngdoc function
6825 * @name angular.equals
6826 * @module ng
6827 * @kind function
6828 *
6829 * @description
6830 * Determines if two objects or two values are equivalent. Supports value types, regular
6831 * expressions, arrays and objects.
6832 *
6833 * Two objects or values are considered equivalent if at least one of the following is true:
6834 *
6835 * * Both objects or values pass `===` comparison.
6836 * * Both objects or values are of the same type and all of their properties are equal by
6837 *   comparing them with `angular.equals`.
6838 * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
6839 * * Both values represent the same regular expression (In JavaScript,
6840 *   /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
6841 *   representation matches).
6842 *
6843 * During a property comparison, properties of `function` type and properties with names
6844 * that begin with `$` are ignored.
6845 *
6846 * Scope and DOMWindow objects are being compared only by identify (`===`).
6847 *
6848 * @param {*} o1 Object or value to compare.
6849 * @param {*} o2 Object or value to compare.
6850 * @returns {boolean} True if arguments are equal.
6851 *
6852 * @example
6853   <example module="equalsExample" name="equalsExample">
6854     <file name="index.html">
6855      <div ng-controller="ExampleController">
6856        <form novalidate>
6857          <h3>User 1</h3>
6858          Name: <input type="text" ng-model="user1.name">
6859          Age: <input type="number" ng-model="user1.age">
6860
6861          <h3>User 2</h3>
6862          Name: <input type="text" ng-model="user2.name">
6863          Age: <input type="number" ng-model="user2.age">
6864
6865          <div>
6866            <br/>
6867            <input type="button" value="Compare" ng-click="compare()">
6868          </div>
6869          User 1: <pre>{{user1 | json}}</pre>
6870          User 2: <pre>{{user2 | json}}</pre>
6871          Equal: <pre>{{result}}</pre>
6872        </form>
6873      </div>
6874    </file>
6875    <file name="script.js">
6876        angular.module('equalsExample', []).controller('ExampleController', ['$scope', function($scope) {
6877          $scope.user1 = {};
6878          $scope.user2 = {};
6879          $scope.compare = function() {
6880            $scope.result = angular.equals($scope.user1, $scope.user2);
6881          };
6882        }]);
6883    </file>
6884  </example>
6885 */
6886function equals(o1, o2) {
6887  if (o1 === o2) return true;
6888  if (o1 === null || o2 === null) return false;
6889  // eslint-disable-next-line no-self-compare
6890  if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
6891  var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
6892  if (t1 === t2 && t1 === 'object') {
6893    if (isArray(o1)) {
6894      if (!isArray(o2)) return false;
6895      if ((length = o1.length) === o2.length) {
6896        for (key = 0; key < length; key++) {
6897          if (!equals(o1[key], o2[key])) return false;
6898        }
6899        return true;
6900      }
6901    } else if (isDate(o1)) {
6902      if (!isDate(o2)) return false;
6903      return equals(o1.getTime(), o2.getTime());
6904    } else if (isRegExp(o1)) {
6905      if (!isRegExp(o2)) return false;
6906      return o1.toString() === o2.toString();
6907    } else {
6908      if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
6909        isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
6910      keySet = createMap();
6911      for (key in o1) {
6912        if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
6913        if (!equals(o1[key], o2[key])) return false;
6914        keySet[key] = true;
6915      }
6916      for (key in o2) {
6917        if (!(key in keySet) &&
6918            key.charAt(0) !== '$' &&
6919            isDefined(o2[key]) &&
6920            !isFunction(o2[key])) return false;
6921      }
6922      return true;
6923    }
6924  }
6925  return false;
6926}
6927
6928var csp = function() {
6929  if (!isDefined(csp.rules)) {
6930
6931
6932    var ngCspElement = (window.document.querySelector('[ng-csp]') ||
6933                    window.document.querySelector('[data-ng-csp]'));
6934
6935    if (ngCspElement) {
6936      var ngCspAttribute = ngCspElement.getAttribute('ng-csp') ||
6937                    ngCspElement.getAttribute('data-ng-csp');
6938      csp.rules = {
6939        noUnsafeEval: !ngCspAttribute || (ngCspAttribute.indexOf('no-unsafe-eval') !== -1),
6940        noInlineStyle: !ngCspAttribute || (ngCspAttribute.indexOf('no-inline-style') !== -1)
6941      };
6942    } else {
6943      csp.rules = {
6944        noUnsafeEval: noUnsafeEval(),
6945        noInlineStyle: false
6946      };
6947    }
6948  }
6949
6950  return csp.rules;
6951
6952  function noUnsafeEval() {
6953    try {
6954      // eslint-disable-next-line no-new, no-new-func
6955      new Function('');
6956      return false;
6957    } catch (e) {
6958      return true;
6959    }
6960  }
6961};
6962
6963/**
6964 * @ngdoc directive
6965 * @module ng
6966 * @name ngJq
6967 *
6968 * @element ANY
6969 * @param {string=} ngJq the name of the library available under `window`
6970 * to be used for angular.element
6971 * @description
6972 * Use this directive to force the angular.element library.  This should be
6973 * used to force either jqLite by leaving ng-jq blank or setting the name of
6974 * the jquery variable under window (eg. jQuery).
6975 *
6976 * Since angular looks for this directive when it is loaded (doesn't wait for the
6977 * DOMContentLoaded event), it must be placed on an element that comes before the script
6978 * which loads angular. Also, only the first instance of `ng-jq` will be used and all
6979 * others ignored.
6980 *
6981 * @example
6982 * This example shows how to force jqLite using the `ngJq` directive to the `html` tag.
6983 ```html
6984 <!doctype html>
6985 <html ng-app ng-jq>
6986 ...
6987 ...
6988 </html>
6989 ```
6990 * @example
6991 * This example shows how to use a jQuery based library of a different name.
6992 * The library name must be available at the top most 'window'.
6993 ```html
6994 <!doctype html>
6995 <html ng-app ng-jq="jQueryLib">
6996 ...
6997 ...
6998 </html>
6999 ```
7000 */
7001var jq = function() {
7002  if (isDefined(jq.name_)) return jq.name_;
7003  var el;
7004  var i, ii = ngAttrPrefixes.length, prefix, name;
7005  for (i = 0; i < ii; ++i) {
7006    prefix = ngAttrPrefixes[i];
7007    el = window.document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]');
7008    if (el) {
7009      name = el.getAttribute(prefix + 'jq');
7010      break;
7011    }
7012  }
7013
7014  return (jq.name_ = name);
7015};
7016
7017function concat(array1, array2, index) {
7018  return array1.concat(slice.call(array2, index));
7019}
7020
7021function sliceArgs(args, startIndex) {
7022  return slice.call(args, startIndex || 0);
7023}
7024
7025
7026/**
7027 * @ngdoc function
7028 * @name angular.bind
7029 * @module ng
7030 * @kind function
7031 *
7032 * @description
7033 * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
7034 * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
7035 * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
7036 * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
7037 *
7038 * @param {Object} self Context which `fn` should be evaluated in.
7039 * @param {function()} fn Function to be bound.
7040 * @param {...*} args Optional arguments to be prebound to the `fn` function call.
7041 * @returns {function()} Function that wraps the `fn` with all the specified bindings.
7042 */
7043function bind(self, fn) {
7044  var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
7045  if (isFunction(fn) && !(fn instanceof RegExp)) {
7046    return curryArgs.length
7047      ? function() {
7048          return arguments.length
7049            ? fn.apply(self, concat(curryArgs, arguments, 0))
7050            : fn.apply(self, curryArgs);
7051        }
7052      : function() {
7053          return arguments.length
7054            ? fn.apply(self, arguments)
7055            : fn.call(self);
7056        };
7057  } else {
7058    // In IE, native methods are not functions so they cannot be bound (note: they don't need to be).
7059    return fn;
7060  }
7061}
7062
7063
7064function toJsonReplacer(key, value) {
7065  var val = value;
7066
7067  if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
7068    val = undefined;
7069  } else if (isWindow(value)) {
7070    val = '$WINDOW';
7071  } else if (value &&  window.document === value) {
7072    val = '$DOCUMENT';
7073  } else if (isScope(value)) {
7074    val = '$SCOPE';
7075  }
7076
7077  return val;
7078}
7079
7080
7081/**
7082 * @ngdoc function
7083 * @name angular.toJson
7084 * @module ng
7085 * @kind function
7086 *
7087 * @description
7088 * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
7089 * stripped since angular uses this notation internally.
7090 *
7091 * @param {Object|Array|Date|string|number|boolean} obj Input to be serialized into JSON.
7092 * @param {boolean|number}
7092 [pretty=2] If set to true, the JSON output will contain newlines and whitespace.
7093 *    If set to an integer, the JSON output will contain that many spaces per indentation.
7094 * @returns {string|undefined} JSON-ified string representing `obj`.
7095 * @knownIssue
7096 *
7097 * The Safari browser throws a `RangeError` instead of returning `null` when it tries to stringify a `Date`
7098 * object with an invalid date value. The only reliable way to prevent this is to monkeypatch the
7099 * `Date.prototype.toJSON` method as follows:
7100 *
7101 * ```
7102 * var _DatetoJSON = Date.prototype.toJSON;
7103 * Date.prototype.toJSON = function() {
7104 *   try {
7105 *     return _DatetoJSON.call(this);
7106 *   } catch(e) {
7107 *     if (e instanceof RangeError) {
7108 *       return null;
7109 *     }
7110 *     throw e;
7111 *   }
7112 * };
7113 * ```
7114 *
7115 * See https://github.com/angular/angular.js/pull/14221 for more information.
7116 */
7117function toJson(obj, pretty) {
7118  if (isUndefined(obj)) return undefined;
7119  if (!isNumber(pretty)) {
7120    pretty = pretty ? 2 : null;
7121  }
7122  return JSON.stringify(obj, toJsonReplacer, pretty);
7123}
7124
7125
7126/**
7127 * @ngdoc function
7128 * @name angular.fromJson
7129 * @module ng
7130 * @kind function
7131 *
7132 * @description
7133 * Deserializes a JSON string.
7134 *
7135 * @param {string} json JSON string to deserialize.
7136 * @returns {Object|Array|string|number} Deserialized JSON string.
7137 */
7138function fromJson(json) {
7139  return isString(json)
7140      ? JSON.parse(json)
7141      : json;
7142}
7143
7144
7145var ALL_COLONS = /:/g;
7146function timezoneToOffset(timezone, fallback) {
7147  // IE/Edge do not "understand" colon (`:`) in timezone
7148  timezone = timezone.replace(ALL_COLONS, '');
7149  var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
7150  return isNumberNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
7151}
7152
7153
7154function addDateMinutes(date, minutes) {
7155  date = new Date(date.getTime());
7156  date.setMinutes(date.getMinutes() + minutes);
7157  return date;
7158}
7159
7160
7161function convertTimezoneToLocal(date, timezone, reverse) {
7162  reverse = reverse ? -1 : 1;
7163  var dateTimezoneOffset = date.getTimezoneOffset();
7164  var timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
7165  return addDateMinutes(date, reverse * (timezoneOffset - dateTimezoneOffset));
7166}
7167
7168
7169/**
7170 * @returns {string} Returns the string representation of the element.
7171 */
7172function startingTag(element) {
7173  element = jqLite(element).clone();
7174  try {
7175    // turns out IE does not let you set .html() on elements which
7176    // are not allowed to have children. So we just ignore it.
7177    element.empty();
7178  } catch (e) { /* empty */ }
7179  var elemHtml = jqLite('<div>').append(element).html();
7180  try {
7181    return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
7182        elemHtml.
7183          match(/^(<[^>]+>)/)[1].
7184          replace(/^<([\w-]+)/, function(match, nodeName) {return '<' + lowercase(nodeName);});
7185  } catch (e) {
7186    return lowercase(elemHtml);
7187  }
7188
7189}
7190
7191
7192/////////////////////////////////////////////////
7193
7194/**
7195 * Tries to decode the URI component without throwing an exception.
7196 *
7197 * @private
7198 * @param str value potential URI component to check.
7199 * @returns {boolean} True if `value` can be decoded
7200 * with the decodeURIComponent function.
7201 */
7202function tryDecodeURIComponent(value) {
7203  try {
7204    return decodeURIComponent(value);
7205  } catch (e) {
7206    // Ignore any invalid uri component.
7207  }
7208}
7209
7210
7211/**
7212 * Parses an escaped url query string into key-value pairs.
7213 * @returns {Object.<string,boolean|Array>}
7214 */
7215function parseKeyValue(/**string*/keyValue) {
7216  var obj = {};
7217  forEach((keyValue || '').split('&'), function(keyValue) {
7218    var splitPoint, key, val;
7219    if (keyValue) {
7220      key = keyValue = keyValue.replace(/\+/g,'%20');
7221      splitPoint = keyValue.indexOf('=');
7222      if (splitPoint !== -1) {
7223        key = keyValue.substring(0, splitPoint);
7224        val = keyValue.substring(splitPoint + 1);
7225      }
7226      key = tryDecodeURIComponent(key);
7227      if (isDefined(key)) {
7228        val = isDefined(val) ? tryDecodeURIComponent(val) : true;
7229        if (!hasOwnProperty.call(obj, key)) {
7230          obj[key] = val;
7231        } else if (isArray(obj[key])) {
7232          obj[key].push(val);
7233        } else {
7234          obj[key] = [obj[key],val];
7235        }
7236      }
7237    }
7238  });
7239  return obj;
7240}
7241
7242function toKeyValue(obj) {
7243  var parts = [];
7244  forEach(obj, function(value, key) {
7245    if (isArray(value)) {
vendor: 4,355 bytes, lines 7246-7367
7246      forEach(value, function(arrayValue) {
7247        parts.push(encodeUriQuery(key, true) +
7248                   (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
7249      });
7250    } else {
7251    parts.push(encodeUriQuery(key, true) +
7252               (value === true ? '' : '=' + encodeUriQuery(value, true)));
7253    }
7254  });
7255  return parts.length ? parts.join('&') : '';
7256}
7257
7258
7259/**
7260 * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
7261 * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
7262 * segments:
7263 *    segment       = *pchar
7264 *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
7265 *    pct-encoded   = "%" HEXDIG HEXDIG
7266 *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
7267 *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
7268 *                     / "*" / "+" / "," / ";" / "="
7269 */
7270function encodeUriSegment(val) {
7271  return encodeUriQuery(val, true).
7272             replace(/%26/gi, '&').
7273             replace(/%3D/gi, '=').
7274             replace(/%2B/gi, '+');
7275}
7276
7277
7278/**
7279 * This method is intended for encoding *key* or *value* parts of query component. We need a custom
7280 * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
7281 * encoded per http://tools.ietf.org/html/rfc3986:
7282 *    query       = *( pchar / "/" / "?" )
7283 *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
7284 *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
7285 *    pct-encoded   = "%" HEXDIG HEXDIG
7286 *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
7287 *                     / "*" / "+" / "," / ";" / "="
7288 */
7289function encodeUriQuery(val, pctEncodeSpaces) {
7290  return encodeURIComponent(val).
7291             replace(/%40/gi, '@').
7292             replace(/%3A/gi, ':').
7293             replace(/%24/g, '$').
7294             replace(/%2C/gi, ',').
7295             replace(/%3B/gi, ';').
7296             replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
7297}
7298
7299var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
7300
7301function getNgAttribute(element, ngAttr) {
7302  var attr, i, ii = ngAttrPrefixes.length;
7303  for (i = 0; i < ii; ++i) {
7304    attr = ngAttrPrefixes[i] + ngAttr;
7305    if (isString(attr = element.getAttribute(attr))) {
7306      return attr;
7307    }
7308  }
7309  return null;
7310}
7311
7312function allowAutoBootstrap(document) {
7313  var script = document.currentScript;
7314  var src = script && script.getAttribute('src');
7315
7316  if (!src) {
7317    return true;
7318  }
7319
7320  var link = document.createElement('a');
7321  link.href = src;
7322
7323  if (document.location.origin === link.origin) {
7324    // Same-origin resources are always allowed, even for non-whitelisted schemes.
7325    return true;
7326  }
7327  // Disabled bootstrapping unless angular.js was loaded from a known scheme used on the web.
7328  // This is to prevent angular.js bundled with browser extensions from being used to bypass the
7329  // content security policy in web pages and other browser extensions.
7330  switch (link.protocol) {
7331    case 'http:':
7332    case 'https:':
7333    case 'ftp:':
7334    case 'blob:':
7335    case 'file:':
7336    case 'data:':
7337      return true;
7338    default:
7339      return false;
7340  }
7341}
7342
7343// Cached as it has to run during loading so that document.currentScript is available.
7344var isAutoBootstrapAllowed = allowAutoBootstrap(window.document);
7345
7346/**
7347 * @ngdoc directive
7348 * @name ngApp
7349 * @module ng
7350 *
7351 * @element ANY
7352 * @param {angular.Module} ngApp an optional application
7353 *   {@link angular.module module} name to load.
7354 * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be
7355 *   created in "strict-di" mode. This means that the application will fail to invoke functions which
7356 *   do not use explicit function annotation (and are thus unsuitable for minification), as described
7357 *   in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in
7358 *   tracking down the root of these bugs.
7359 *
7360 * @description
7361 *
7362 * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
7363 * designates the **root element** of the application and is typically placed near the root element
7364 * of the page - e.g. on the `<body>` or `<html>` tags.
7365 *
7366 * There are a few things to keep in mind when using `ngApp`:
7367 * - only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngAp
7367p`
7368 *   found in the document will be used to define the root element to auto-bootstrap as an
7369 *   application. To run multiple applications in an HTML document you must manually bootstrap them using
7370 *   {@link angular.bootstrap} instead.
7371 * - AngularJS applications cannot be nested within each other.
7372 * - Do not use a directive that uses {@link ng.$compile#transclusion transclusion} on the same element as `ngApp`.
7373 *   This includes directives such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and
7374 *   {@link ngRoute.ngView `ngView`}.
7375 *   Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector},
7376 *   causing animations to stop working and making the injector inaccessible from outside the app.
7377 *
7378 * You can specify an **AngularJS module** to be used as the root module for the application.  This
7379 * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It
7380 * should contain the application code needed or have dependencies on other modules that will
7381 * contain the code. See {@link angular.module} for more information.
7382 *
7383 * In the example below if the `ngApp` directive were not placed on the `html` element then the
7384 * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
7385 * would not be resolved to `3`.
7386 *
7387 * `ngApp` is the easiest, and most common way to bootstrap an application.
7388 *
7389 <example module="ngAppDemo" name="ng-app">
7390   <file name="index.html">
7391   <div ng-controller="ngAppDemoController">
7392     I can add: {{a}} + {{b}} =  {{ a+b }}
7393   </div>
7394   </file>
7395   <file name="script.js">
7396   angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
7397     $scope.a = 1;
7398     $scope.b = 2;
7399   });
7400   </file>
7401 </example>
7402 *
7403 * Using `ngStrictDi`, you would see something like this:
7404 *
7405 <example ng-app-included="true" name="strict-di">
7406   <file name="index.html">
7407   <div ng-app="ngAppStrictDemo" ng-strict-di>
7408       <div ng-controller="GoodController1">
7409           I can add: {{a}} + {{b}} =  {{ a+b }}
7410
7411           <p>This renders because the controller does not fail to
7412              instantiate, by using explicit annotation style (see
7413              script.js for details)
7414           </p>
7415       </div>
7416
7417       <div ng-controller="GoodController2">
7418           Name: <input ng-model="name"><br />
7419           Hello, {{name}}!
7420
7421           <p>This renders because the controller does not fail to
7422              instantiate, by using explicit annotation style
7423              (see script.js for details)
7424           </p>
7425       </div>
7426
7427       <div ng-controller="BadController">
7428           I can add: {{a}} + {{b}} =  {{ a+b }}
7429
7430           <p>The controller could not be instantiated, due to relying
7431              on automatic function annotations (which are disabled in
7432              strict mode). As such, the content of this section is not
7433              interpolated, and there should be an error in your web console.
7434           </p>
7435       </div>
7436   </div>
7437   </file>
7438   <file name="script.js">
7439   angular.module('ngAppStrictDemo', [])
7440     // BadController will fail to instantiate, due to relying on automatic function annotation,
7441     // rather than an explicit annotation
7442     .controller('BadController', function($scope) {
7443       $scope.a = 1;
7444       $scope.b = 2;
7445     })
7446     // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated,
7447     // due to using explicit annotations using the array style and $inject property, respectively.
7448     .controller('GoodController1', ['$scope', function($scope) {
7449       $scope.a = 1;
7450       $scope.b = 2;
7451     }])
7452     .controller('GoodController2', GoodController2);
7453     function GoodController2($scope) {
7454       $scope.name = 'World';
7455     }
7456     GoodController2.$inject = ['$scope'];
7457   </file>
7458   <file name="style.css">
7459   div[ng-controller] {
7460       margin-bottom: 1em;
7461       -webkit-border-radius: 4px;
7462       border-radius: 4px;
7463       border: 1px solid;
7464       padding: .5em;
7465   }
7466   div[ng-controller^=Good] {
7467       border-color: #d6e9c6;
7468       background-color: #dff0d8;
7469       color: #3c763d;
7470   }
7471   div[ng-controller^=Bad] {
7472       border-color: #ebccd1;
7473       background-color: #f2dede;
7474       color: #a94442;
7475       margin-bottom: 0;
7476   }
7477   </file>
7478 </example>
7479 */
7480function angularInit(element, bootstrap) {
7481  var appElement,
7482      module,
7483      config = {};
7484
7485  // The element `element` has priority over any other element.
7486  forEach(ngAttrPrefixes, function(prefix) {
7487    var name = prefix + 'app';
7488
7489    if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
7490      appElement = element;
7491      module = element.getAttribute(name);
7492    }
7493  });
7494  forEach(ngAttrPrefixes, function(prefix) {
7495    var name = prefix + 'app';
7496    var candidate;
7497
7498    if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
7499      appElement = candidate;
7500      module = candidate.getAttribute(name);
7501    }
7502  });
7503  if (appElement) {
7504    if (!isAutoBootstrapAllowed) {
7505      window.console.error('Angular: disabling automatic bootstrap. <script> protocol indicates ' +
7506          'an extension, document.location.href does not match.');
7507      return;
7508    }
7509    config.strictDi = getNgAttribute(appElement, 'strict-di') !== null;
7510    bootstrap(appElement, module ? [module] : [], config);
7511  }
7512}
7513
7514/**
7515 * @ngdoc function
7516 * @name angular.bootstrap
7517 * @module ng
7518 * @description
7519 * Use this function to manually start up angular application.
7520 *
7521 * For more information, see the {@link guide/bootstrap Bootstrap guide}.
7522 *
7523 * Angular will detect if it has been loaded into the browser more than once and only allow the
7524 * first loaded script to be bootstrapped and will report a warning to the browser console for
7525 * each of the subsequent scripts. This prevents strange results in applications, where otherwise
7526 * multiple instances of Angular try to work on the DOM.
7527 *
7528 * <div class="alert alert-warning">
7529 * **Note:** Protractor based end-to-end tests cannot use this function to bootstrap manually.
7530 * They must use {@link ng.directive:ngApp ngApp}.
7531 * </div>
7532 *
7533 * <div class="alert alert-warning">
7534 * **Note:** Do not bootstrap the app on an element with a directive that uses {@link ng.$compile#transclusion transclusion},
7535 * such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and {@link ngRoute.ngView `ngView`}.
7536 * Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector},
7537 * causing animations to stop working and making the injector inaccessible from outside the app.
7538 * </div>
7539 *
7540 * ```html
7541 * <!doctype html>
7542 * <html>
7543 * <body>
7544 * <div ng-controller="WelcomeController">
7545 *   {{greeting}}
7546 * </div>
7547 *
7548 * <script src="angular.js"></script>
7549 * <script>
7550 *   var app = angular.module('demo', [])
7551 *   .controller('WelcomeController', function($scope) {
7552 *       $scope.greeting = 'Welcome!';
7553 *   });
7554 *   angular.bootstrap(document, ['demo']);
7555 * </script>
7556 * </body>
7557 * </html>
7558 * ```
7559 *
7560 * @param {DOMElement} element DOM element which is the root of angular application.
7561 * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
7562 *     Each item in the array should be the name of a predefined module or a (DI annotated)
7563 *     function that will be invoked by the injector as a `config` block.
7564 *     See: {@link angular.module modules}
7565 * @param {Object=} config an object for defining configuration options for the application. The
7566 *     following keys are supported:
7567 *
7568 * * `strictDi` - disable automatic function annotation for the application. This is meant to
7569 *   assist in finding bugs which break minified code. Defaults to `false`.
7570 *
7571 * @returns {auto.$injector} Returns the newly created injector for this app.
7572 */
7573function bootstrap(element, modules, config) {
7574  if (!isObject(config)) config = {};
7575  var defaultConfig = {
7576    strictDi: false
7577  };
7578  config = extend(defaultConfig, config);
7579  var doBootstrap = function() {
7580    element = jqLite(element);
7581
7582    if (element.injector()) {
7583      var tag = (element[0] === window.document) ? 'document' : startingTag(element);
7584      // Encode angle brackets to prevent input from being sanitized to empty string #8683.
7585      throw ngMinErr(
7586          'btstrpd',
7587          'App already bootstrapped with this element \'{0}\'',
7588          tag.replace(/</,'&lt;').replace(/>/,'&gt;'));
7589    }
7590
7591    modules = modules || [];
7592    modules.unshift(['$provide', function($provide) {
7593      $provide.value('$rootElement', element);
7594    }]);
7595
7596    if (config.debugInfoEnabled) {
7597      // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
7598      modules.push(['$compileProvider', function($compileProvider) {
7599        $compileProvider.debugInfoEnabled(true);
7600      }]);
7601    }
7602
7603    modules.unshift('ng');
7604    var injector = createInjector(modules, config.strictDi);
7605    injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
7606       function bootstrapApply(scope, element, compile, injector) {
7607        scope.$apply(function() {
7608          element.data('$injector', injector);
7609          compile(element)(scope);
7610        });
7611      }]
7612    );
7613    return injector;
7614  };
7615
7616  var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
7617  var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
7618
7619  if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
7620    config.debugInfoEnabled = true;
7621    window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
7622  }
7623
7624  if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
7625    return doBootstrap();
7626  }
7627
7628  window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
7629  angular.resumeBootstrap = function(extraModules) {
7630    forEach(extraModules, function(module) {
7631      modules.push(module);
7632    });
7633    return doBootstrap();
7634  };
7635
7636  if (isFunction(angular.resumeDeferredBootstrap)) {
7637    angular.resumeDeferredBootstrap();
7638  }
7639}
7640
7641/**
7642 * @ngdoc function
7643 * @name angular.reloadWithDebugInfo
7644 * @module ng
7645 * @description
7646 * Use this function to reload the current application with debug information turned on.
7647 * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`.
7648 *
7649 * See {@link ng.$compileProvider#debugInfoEnabled} for more.
7650 */
7651function reloadWithDebugInfo() {
7652  window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;
7653  window.location.reload();
7654}
7655
7656/**
7657 * @name angular.getTestability
7658 * @module ng
7659 * @description
7660 * Get the testability service for the instance of Angular on the given
7661 * element.
7662 * @param {DOMElement} element DOM element which is the root of angular application.
7663 */
7664function getTestability(rootElement) {
7665  var injector = angular.element(rootElement).injector();
7666  if (!injector) {
7667    throw ngMinErr('test',
7668      'no injector found for element argument to getTestability');
7669  }
7670  return injector.get('$$testability');
7671}
7672
7673var SNAKE_CASE_REGEXP = /[A-Z]/g;
7674function snake_case(name, separator) {
7675  separator = separator || '_';
7676  return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
7677    return (pos ? separator : '') + letter.toLowerCase();
7678  });
7679}
7680
7681var bindJQueryFired = false;
vendor: 8,941 bytes, lines 7682-7966
7682function bindJQuery() {
7683  var originalCleanData;
7684
7685  if (bindJQueryFired) {
7686    return;
7687  }
7688
7689  // bind to jQuery if present;
7690  var jqName = jq();
7691  jQuery = isUndefined(jqName) ? window.jQuery :   // use jQuery (if present)
7692           !jqName             ? undefined     :   // use jqLite
7693                                 window[jqName];   // use jQuery specified by `ngJq`
7694
7695  // Use jQuery if it exists with proper functionality, otherwise default to us.
7696  // Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
7697  // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
7698  // versions. It will not work for sure with jQuery <1.7, though.
7699  if (jQuery && jQuery.fn.on) {
7700    jqLite = jQuery;
7701    extend(jQuery.fn, {
7702      scope: JQLitePrototype.scope,
7703      isolateScope: JQLitePrototype.isolateScope,
7704      controller: JQLitePrototype.controller,
7705      injector: JQLitePrototype.injector,
7706      inheritedData: JQLitePrototype.inheritedData
7707    });
7708
7709    // All nodes removed from the DOM via various jQuery APIs like .remove()
7710    // are passed through jQuery.cleanData. Monkey-patch this method to fire
7711    // the $destroy event on all removed nodes.
7712    originalCleanData = jQuery.cleanData;
7713    jQuery.cleanData = function(elems) {
7714      var events;
7715      for (var i = 0, elem; (elem = elems[i]) != null; i++) {
7716        events = jQuery._data(elem, 'events');
7717        if (events && events.$destroy) {
7718          jQuery(elem).triggerHandler('$destroy');
7719        }
7720      }
7721      originalCleanData(elems);
7722    };
7723  } else {
7724    jqLite = JQLite;
7725  }
7726
7727  angular.element = jqLite;
7728
7729  // Prevent double-proxying.
7730  bindJQueryFired = true;
7731}
7732
7733/**
7734 * throw error if the argument is falsy.
7735 */
7736function assertArg(arg, name, reason) {
7737  if (!arg) {
7738    throw ngMinErr('areq', 'Argument \'{0}\' is {1}', (name || '?'), (reason || 'required'));
7739  }
7740  return arg;
7741}
7742
7743function assertArgFn(arg, name, acceptArrayAnnotation) {
7744  if (acceptArrayAnnotation && isArray(arg)) {
7745      arg = arg[arg.length - 1];
7746  }
7747
7748  assertArg(isFunction(arg), name, 'not a function, got ' +
7749      (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));
7750  return arg;
7751}
7752
7753/**
7754 * throw error if the name given is hasOwnProperty
7755 * @param  {String} name    the name to test
7756 * @param  {String} context the context in which the name is used, such as module or directive
7757 */
7758function assertNotHasOwnProperty(name, context) {
7759  if (name === 'hasOwnProperty') {
7760    throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
7761  }
7762}
7763
7764/**
7765 * Return the value accessible from the object by path. Any undefined traversals are ignored
7766 * @param {Object} obj starting object
7767 * @param {String} path path to traverse
7768 * @param {boolean} [bindFnToScope=true]
7769 * @returns {Object} value as accessible by path
7770 */
7771//TODO(misko): this function needs to be removed
7772function getter(obj, path, bindFnToScope) {
7773  if (!path) return obj;
7774  var keys = path.split('.');
7775  var key;
7776  var lastInstance = obj;
7777  var len = keys.length;
7778
7779  for (var i = 0; i < len; i++) {
7780    key = keys[i];
7781    if (obj) {
7782      obj = (lastInstance = obj)[key];
7783    }
7784  }
7785  if (!bindFnToScope && isFunction(obj)) {
7786    return bind(lastInstance, obj);
7787  }
7788  return obj;
7789}
7790
7791/**
7792 * Return the DOM siblings between the first and last node in the given array.
7793 * @param {Array} array like object
7794 * @returns {Array} the inputted object or a jqLite collection containing the nodes
7795 */
7796function getBlockNodes(nodes) {
7797  // TODO(perf): update `nodes` instead of creating a new object?
7798  var node = nodes[0];
7799  var endNode = nodes[nodes.length - 1];
7800  var blockNodes;
7801
7802  for (var i = 1; node !== endNode && (node = node.nextSibling); i++) {
7803    if (blockNodes || nodes[i] !== node) {
7804      if (!blockNodes) {
7805        blockNodes = jqLite(slice.call(nodes, 0, i));
7806      }
7807      blockNodes.push(node);
7808    }
7809  }
7810
7811  return blockNodes || nodes;
7812}
7813
7814
7815/**
7816 * Creates a new object without a prototype. This object is useful for lookup without having to
7817 * guard against prototypically inherited properties via hasOwnProperty.
7818 *
7819 * Related micro-benchmarks:
7820 * - http://jsperf.com/object-create2
7821 * - http://jsperf.com/proto-map-lookup/2
7822 * - http://jsperf.com/for-in-vs-object-keys2
7823 *
7824 * @returns {Object}
7825 */
7826function createMap() {
7827  return Object.create(null);
7828}
7829
7830var NODE_TYPE_ELEMENT = 1;
7831var NODE_TYPE_ATTRIBUTE = 2;
7832var NODE_TYPE_TEXT = 3;
7833var NODE_TYPE_COMMENT = 8;
7834var NODE_TYPE_DOCUMENT = 9;
7835var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
7836
7837/**
7838 * @ngdoc type
7839 * @name angular.Module
7840 * @module ng
7841 * @description
7842 *
7843 * Interface for configuring angular {@link angular.module modules}.
7844 */
7845
7846function setupModuleLoader(window) {
7847
7848  var $injectorMinErr = minErr('$injector');
7849  var ngMinErr = minErr('ng');
7850
7851  function ensure(obj, name, factory) {
7852    return obj[name] || (obj[name] = factory());
7853  }
7854
7855  var angular = ensure(window, 'angular', Object);
7856
7857  // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
7858  angular.$$minErr = angular.$$minErr || minErr;
7859
7860  return ensure(angular, 'module', function() {
7861    /** @type {Object.<string, angular.Module>} */
7862    var modules = {};
7863
7864    /**
7865     * @ngdoc function
7866     * @name angular.module
7867     * @module ng
7868     * @description
7869     *
7870     * The `angular.module` is a global place for creating, registering and retrieving Angular
7871     * modules.
7872     * All modules (angular core or 3rd party) that should be available to an application must be
7873     * registered using this mechanism.
7874     *
7875     * Passing one argument retrieves an existing {@link angular.Module},
7876     * whereas passing more than one argument creates a new {@link angular.Module}
7877     *
7878     *
7879     * # Module
7880     *
7881     * A module is a collection of services, directives, controllers, filters, and configuration information.
7882     * `angular.module` is used to configure the {@link auto.$injector $injector}.
7883     *
7884     * ```js
7885     * // Create a new module
7886     * var myModule = angular.module('myModule', []);
7887     *
7888     * // register a new service
7889     * myModule.value('appName', 'MyCoolApp');
7890     *
7891     * // configure existing services inside initialization blocks.
7892     * myModule.config(['$locationProvider', function($locationProvider) {
7893     *   // Configure existing providers
7894     *   $locationProvider.hashPrefix('!');
7895     * }]);
7896     * ```
7897     *
7898     * Then you can create an injector and load your modules like this:
7899     *
7900     * ```js
7901     * var injector = angular.injector(['ng', 'myModule'])
7902     * ```
7903     *
7904     * However it's more likely that you'll just use
7905     * {@link ng.directive:ngApp ngApp} or
7906     * {@link angular.bootstrap} to simplify this process for you.
7907     *
7908     * @param {!string} name The name of the module to create or retrieve.
7909     * @param {!Array.<string>=} requires If specified then new module is being created. If
7910     *        unspecified then the module is being retrieved for further configuration.
7911     * @param {Function=} configFn Optional configuration function for the module. Same as
7912     *        {@link angular.Module#config Module#config()}.
7913     * @returns {angular.Module} new module with the {@link angular.Module} api.
7914     */
7915    return function module(name, requires, configFn) {
7916      var assertNotHasOwnProperty = function(name, context) {
7917        if (name === 'hasOwnProperty') {
7918          throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
7919        }
7920      };
7921
7922      assertNotHasOwnProperty(name, 'module');
7923      if (requires && modules.hasOwnProperty(name)) {
7924        modules[name] = null;
7925      }
7926      return ensure(modules, name, function() {
7927        if (!requires) {
7928          throw $injectorMinErr('nomod', 'Module \'{0}\' is not available! You either misspelled ' +
7929             'the module name or forgot to load it. If registering a module ensure that you ' +
7930             'specify the dependencies as the second argument.', name);
7931        }
7932
7933        /** @type {!Array.<Array.<*>>} */
7934        var invokeQueue = [];
7935
7936        /** @type {!Array.<Function>} */
7937        var configBlocks = [];
7938
7939        /** @type {!Array.<Function>} */
7940        var runBlocks = [];
7941
7942        var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
7943
7944        /** @type {angular.Module} */
7945        var moduleInstance = {
7946          // Private state
7947          _invokeQueue: invokeQueue,
7948          _configBlocks: configBlocks,
7949          _runBlocks: runBlocks,
7950
7951          /**
7952           * @ngdoc property
7953           * @name angular.Module#requires
7954           * @module ng
7955           *
7956           * @description
7957           * Holds the list of modules which the injector will load before the current module is
7958           * loaded.
7959           */
7960          requires: requires,
7961
7962          /**
7963           * @ngdoc property
7964           * @name angular.Module#name
7965           * @module ng
7966           *
7967           * @description
7968           * Name of the module.
7969           */
7970          name: name,
7971
7972
7973          /**
7974           * @ngdoc method
7975           * @name angular.Module#provider
7976           * @module ng
7977           * @param {string} name service name
7978           * @param {Function} providerType Construction function for creating new instance of the
7979           *                                service.
7980           * @description
7981           * See {@link auto.$provide#provider $provide.provider()}.
7982           */
7983          provider: invokeLaterAndSetModuleName('$provide', 'provider'),
7984
7985          /**
7986           * @ngdoc method
7987           * @name angular.Module#factory
7988           * @module ng
7989           * @param {string} name service name
7990           * @param {Function} providerFunction Function for creating new instance of the service.
7991           * @description
7992           * See {@link auto.$provide#factory $provide.factory()}.
7993           */
7994          factory: invokeLaterAndSetModuleName('$provide', 'factory'),
7995
7996          /**
7997           * @ngdoc method
7998           * @name angular.Module#service
7999           * @module ng
8000           * @param {string} name service name
8001           * @param {Function} constructor A constructor function that will be instantiated.
8002           * @description
8003           * See {@link auto.$provide#service $provide.service()}.
8004           */
8005          service: invokeLaterAndSetModuleName('$provide', 'service'),
8006
8007          /**
8008           * @ngdoc method
8009           * @name angular.Module#value
8010           * @module ng
8011           * @param {string} name service name
8012           * @param {*} object Service instance object.
8013           * @description
8014           * See {@link auto.$provide#value $provide.value()}.
8015           */
8016          value: invokeLater('$provide', 'value'),
8017
8018          /**
8019           * @ngdoc method
8020           * @name angular.Module#constant
8021           * @module ng
8022           * @param {string} name constant name
8023           * @param {*} object Constant value.
8024           * @description
8025           * Because the constants are fixed, they get applied before other provide methods.
8026           * See {@link auto.$provide#constant $provide.constant()}.
8027           */
8028          constant: invokeLater('$provide', 'constant', 'unshift'),
8029
8030           /**
8031           * @ngdoc method
8032           * @name angular.Module#decorator
8033           * @module ng
8034           * @param {string} name The name of the service to decorate.
8035           * @param {Function} decorFn This function will be invoked when the service needs to be
8036           *                           instantiated and should return the decorated service instance.
8037           * @description
8038           * See {@link auto.$provide#decorator $provide.decorator()}.
8039           */
8040          decorator: invokeLaterAndSetModuleName('$provide', 'decorator'),
8041
8042          /**
8043           * @ngdoc method
8044           * @name angular.Module#animation
8045           * @module ng
8046           * @param {string} name animation name
8047           * @param {Function} animationFactory Factory function for creating new instance of an
8048           *                                    animation.
8049           * @description
8050           *
8051           * **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
8052           *
8053           *
8054           * Defines an animation hook that can be later used with
8055           * {@link $animate $animate} service and directives that use this service.
8056           *
8057           * ```js
8058           * module.animation('.animation-name', function($inject1, $inject2) {
8059           *   return {
8060           *     eventName : function(element, done) {
8061           *       //code to run the animation
8062           *       //once complete, then run done()
8063           *       return function cancellationFunction(element) {
8064           *         //code to cancel the animation
8065           *       }
8066           *     }
8067           *   }
8068           * })
8069           * ```
8070           *
8071           * See {@link ng.$animateProvider#register $animateProvider.register()} and
8072           * {@link ngAnimate ngAnimate module} for more information.
8073           */
8074          animation: invokeLaterAndSetModuleName('$animateProvider', 'register'),
8075
8076          /**
8077           * @ngdoc method
8078           * @name angular.Module#filter
8079           * @module ng
8080           * @param {string} name Filter name - this must be a valid angular expression identifier
8081           * @param {Function} filterFactory Factory function for creating new instance of filter.
8082           * @description
8083           * See {@link ng.$filterProvider#register $filterProvider.register()}.
8084           *
8085           * <div class="alert alert-warning">
8086           * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
8087           * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
8088           * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
8089           * (`myapp_subsection_filterx`).
8090           * </div>
8091           */
8092          filter: invokeLaterAndSetModuleName('$filterProvider', 'register'),
8093
8094          /**
8095           * @ngdoc method
8096           * @name angular.Module#controller
8097           * @module ng
8098           * @param {string|Object} name Controller name, or an object map of controllers where the
8099           *    keys are the names and the values are the constructors.
8100           * @param {Function} constructor Controller constructor function.
8101           * @description
8102           * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
8103           */
8104          controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'),
8105
8106          /**
8107           * @ngdoc method
8108           * @name angular.Module#directive
8109           * @module ng
8110           * @param {string|Object} name Directive name, or an object map of directives where the
8111           *    keys are the names and the values are the factories.
8112           * @param {Function} directiveFactory Factory function for creating new instance of
8113           * directives.
8114           * @description
8115           * See {@link ng.$compileProvider#directive $compileProvider.directive()}.
8116           */
8117          directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
8118
8119          /**
8120           * @ngdoc method
8121           * @name angular.Module#component
8122           * @module ng
8123           * @param {string} name Name of the component in camel-case (i.e. myComp which will match as my-comp)
8124           * @param {Object} options Component definition object (a simplified
8125           *    {@link ng.$compile#directive-definition-object directive definition object})
8126           *
8127           * @description
8128           * See {@link ng.$compileProvider#component $compileProvider.component()}.
8129           */
8130          component: invokeLaterAndSetModuleName('$compileProvider', 'component'),
8131
8132          /**
8133           * @ngdoc method
8134           * @name angular.Module#config
8135           * @module ng
8136           * @param {Function} configFn Execute this function on module load. Useful for service
8137           *    configuration.
8138           * @description
8139           * Use this method to register work which needs to be performed on module loading.
8140           * For more about how to configure services, see
8141           * {@link providers#provider-recipe Provider Recipe}.
8142           */
8143          config: config,
8144
8145          /**
8146           * @ngdoc method
8147           * @name angular.Module#run
8148           * @module ng
8149           * @param {Function} initializationFn Execute this function after injector creation.
8150           *    Useful for application initialization.
8151           * @description
8152           * Use this method to register work which should be performed when the injector is done
8153           * loading all modules.
8154           */
8155          run: function(block) {
8156            runBlocks.push(block);
8157            return this;
8158          }
8159        };
8160
8161        if (configFn) {
8162          config(configFn);
8163        }
8164
8165        return moduleInstance;
8166
8167        /**
8168         * @param {string} provider
8169         * @param {string} method
8170         * @param {String=} insertMethod
8171         * @returns {angular.Module}
8172         */
8173        function invokeLater(provider, method, insertMethod, queue) {
8174          if (!queue) queue = invokeQueue;
8175          return function() {
8176            queue[insertMethod || 'push']([provider, method, arguments]);
8177            return moduleInstance;
8178          };
8179        }
8180
8181        /**
8182         * @param {string} provider
8183         * @param {string} method
8184         * @returns {angular.Module}
8185         */
vendor: 8,464 bytes, lines 8186-8490
8186        function invokeLaterAndSetModuleName(provider, method) {
8187          return function(recipeName, factoryFunction) {
8188            if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
8189            invokeQueue.push([provider, method, arguments]);
8190            return moduleInstance;
8191          };
8192        }
8193      });
8194    };
8195  });
8196
8197}
8198
8199/* global shallowCopy: true */
8200
8201/**
8202 * Creates a shallow copy of an object, an array or a primitive.
8203 *
8204 * Assumes that there are no proto properties for objects.
8205 */
8206function shallowCopy(src, dst) {
8207  if (isArray(src)) {
8208    dst = dst || [];
8209
8210    for (var i = 0, ii = src.length; i < ii; i++) {
8211      dst[i] = src[i];
8212    }
8213  } else if (isObject(src)) {
8214    dst = dst || {};
8215
8216    for (var key in src) {
8217      if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
8218        dst[key] = src[key];
8219      }
8220    }
8221  }
8222
8223  return dst || src;
8224}
8225
8226/* global toDebugString: true */
8227
8228function serializeObject(obj) {
8229  var seen = [];
8230
8231  return JSON.stringify(obj, function(key, val) {
8232    val = toJsonReplacer(key, val);
8233    if (isObject(val)) {
8234
8235      if (seen.indexOf(val) >= 0) return '...';
8236
8237      seen.push(val);
8238    }
8239    return val;
8240  });
8241}
8242
8243function toDebugString(obj) {
8244  if (typeof obj === 'function') {
8245    return obj.toString().replace(/ \{[\s\S]*$/, '');
8246  } else if (isUndefined(obj)) {
8247    return 'undefined';
8248  } else if (typeof obj !== 'string') {
8249    return serializeObject(obj);
8250  }
8251  return obj;
8252}
8253
8254/* global angularModule: true,
8255  version: true,
8256
8257  $CompileProvider,
8258
8259  htmlAnchorDirective,
8260  inputDirective,
8261  inputDirective,
8262  formDirective,
8263  scriptDirective,
8264  selectDirective,
8265  optionDirective,
8266  ngBindDirective,
8267  ngBindHtmlDirective,
8268  ngBindTemplateDirective,
8269  ngClassDirective,
8270  ngClassEvenDirective,
8271  ngClassOddDirective,
8272  ngCloakDirective,
8273  ngControllerDirective,
8274  ngFormDirective,
8275  ngHideDirective,
8276  ngIfDirective,
8277  ngIncludeDirective,
8278  ngIncludeFillContentDirective,
8279  ngInitDirective,
8280  ngNonBindableDirective,
8281  ngPluralizeDirective,
8282  ngRepeatDirective,
8283  ngShowDirective,
8284  ngStyleDirective,
8285  ngSwitchDirective,
8286  ngSwitchWhenDirective,
8287  ngSwitchDefaultDirective,
8288  ngOptionsDirective,
8289  ngTranscludeDirective,
8290  ngModelDirective,
8291  ngListDirective,
8292  ngChangeDirective,
8293  patternDirective,
8294  patternDirective,
8295  requiredDirective,
8296  requiredDirective,
8297  minlengthDirective,
8298  minlengthDirective,
8299  maxlengthDirective,
8300  maxlengthDirective,
8301  ngValueDirective,
8302  ngModelOptionsDirective,
8303  ngAttributeAliasDirectives,
8304  ngEventDirectives,
8305
8306  $AnchorScrollProvider,
8307  $AnimateProvider,
8308  $CoreAnimateCssProvider,
8309  $$CoreAnimateJsProvider,
8310  $$CoreAnimateQueueProvider,
8311  $$AnimateRunnerFactoryProvider,
8312  $$AnimateAsyncRunFactoryProvider,
8313  $BrowserProvider,
8314  $CacheFactoryProvider,
8315  $ControllerProvider,
8316  $DateProvider,
8317  $DocumentProvider,
8318  $ExceptionHandlerProvider,
8319  $FilterProvider,
8320  $$ForceReflowProvider,
8321  $InterpolateProvider,
8322  $IntervalProvider,
8323  $$HashMapProvider,
8324  $HttpProvider,
8325  $HttpParamSerializerProvider,
8326  $HttpParamSerializerJQLikeProvider,
8327  $HttpBackendProvider,
8328  $xhrFactoryProvider,
8329  $jsonpCallbacksProvider,
8330  $LocationProvider,
8331  $LogProvider,
8332  $ParseProvider,
8333  $RootScopeProvider,
8334  $QProvider,
8335  $$QProvider,
8336  $$SanitizeUriProvider,
8337  $SceProvider,
8338  $SceDelegateProvider,
8339  $SnifferProvider,
8340  $TemplateCacheProvider,
8341  $TemplateRequestProvider,
8342  $$TestabilityProvider,
8343  $TimeoutProvider,
8344  $$RAFProvider,
8345  $WindowProvider,
8346  $$jqLiteProvider,
8347  $$CookieReaderProvider
8348*/
8349
8350
8351/**
8352 * @ngdoc object
8353 * @name angular.version
8354 * @module ng
8355 * @description
8356 * An object that contains information about the current AngularJS version.
8357 *
8358 * This object has the following properties:
8359 *
8360 * - `full` – `{string}` – Full version string, such as "0.9.18".
8361 * - `major` – `{number}` – Major version number, such as "0".
8362 * - `minor` – `{number}` – Minor version number, such as "9".
8363 * - `dot` – `{number}` – Dot version number, such as "18".
8364 * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
8365 */
8366var version = {
8367  // These placeholder strings will be replaced by grunt's `build` task.
8368  // They need to be double- or single-quoted.
8369  full: '1.5.11',
8370  major: 1,
8371  minor: 5,
8372  dot: 11,
8373  codeName: 'princely-quest'
8374};
8375
8376
8377function publishExternalAPI(angular) {
8378  extend(angular, {
8379    'bootstrap': bootstrap,
8380    'copy': copy,
8381    'extend': extend,
8382    'merge': merge,
8383    'equals': equals,
8384    'element': jqLite,
8385    'forEach': forEach,
8386    'injector': createInjector,
8387    'noop': noop,
8388    'bind': bind,
8389    'toJson': toJson,
8390    'fromJson': fromJson,
8391    'identity': identity,
8392    'isUndefined': isUndefined,
8393    'isDefined': isDefined,
8394    'isString': isString,
8395    'isFunction': isFunction,
8396    'isObject': isObject,
8397    'isNumber': isNumber,
8398    'isElement': isElement,
8399    'isArray': isArray,
8400    'version': version,
8401    'isDate': isDate,
8402    'lowercase': lowercase,
8403    'uppercase': uppercase,
8404    'callbacks': {$$counter: 0},
8405    'getTestability': getTestability,
8406    '$$minErr': minErr,
8407    '$$csp': csp,
8408    'reloadWithDebugInfo': reloadWithDebugInfo
8409  });
8410
8411  angularModule = setupModuleLoader(window);
8412
8413  angularModule('ng', ['ngLocale'], ['$provide',
8414    function ngModule($provide) {
8415      // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
8416      $provide.provider({
8417        $$sanitizeUri: $$SanitizeUriProvider
8418      });
8419      $provide.provider('$compile', $CompileProvider).
8420        directive({
8421            a: htmlAnchorDirective,
8422            input: inputDirective,
8423            textarea: inputDirective,
8424            form: formDirective,
8425            script: scriptDirective,
8426            select: selectDirective,
8427            option: optionDirective,
8428            ngBind: ngBindDirective,
8429            ngBindHtml: ngBindHtmlDirective,
8430            ngBindTemplate: ngBindTemplateDirective,
8431            ngClass: ngClassDirective,
8432            ngClassEven: ngClassEvenDirective,
8433            ngClassOdd: ngClassOddDirective,
8434            ngCloak: ngCloakDirective,
8435            ngController: ngControllerDirective,
8436            ngForm: ngFormDirective,
8437            ngHide: ngHideDirective,
8438            ngIf: ngIfDirective,
8439            ngInclude: ngIncludeDirective,
8440            ngInit: ngInitDirective,
8441            ngNonBindable: ngNonBindableDirective,
8442            ngPluralize: ngPluralizeDirective,
8443            ngRepeat: ngRepeatDirective,
8444            ngShow: ngShowDirective,
8445            ngStyle: ngStyleDirective,
8446            ngSwitch: ngSwitchDirective,
8447            ngSwitchWhen: ngSwitchWhenDirective,
8448            ngSwitchDefault: ngSwitchDefaultDirective,
8449            ngOptions: ngOptionsDirective,
8450            ngTransclude: ngTranscludeDirective,
8451            ngModel: ngModelDirective,
8452            ngList: ngListDirective,
8453            ngChange: ngChangeDirective,
8454            pattern: patternDirective,
8455            ngPattern: patternDirective,
8456            required: requiredDirective,
8457            ngRequired: requiredDirective,
8458            minlength: minlengthDirective,
8459            ngMinlength: minlengthDirective,
8460            maxlength: maxlengthDirective,
8461            ngMaxlength: maxlengthDirective,
8462            ngValue: ngValueDirective,
8463            ngModelOptions: ngModelOptionsDirective
8464        }).
8465        directive({
8466          ngInclude: ngIncludeFillContentDirective
8467        }).
8468        directive(ngAttributeAliasDirectives).
8469        directive(ngEventDirectives);
8470      $provide.provider({
8471        $anchorScroll: $AnchorScrollProvider,
8472        $animate: $AnimateProvider,
8473        $animateCss: $CoreAnimateCssProvider,
8474        $$animateJs: $$CoreAnimateJsProvider,
8475        $$animateQueue: $$CoreAnimateQueueProvider,
8476        $$AnimateRunner: $$AnimateRunnerFactoryProvider,
8477        $$animateAsyncRun: $$AnimateAsyncRunFactoryProvider,
8478        $browser: $BrowserProvider,
8479        $cacheFactory: $CacheFactoryProvider,
8480        $controller: $ControllerProvider,
8481        $document: $DocumentProvider,
8482        $exceptionHandler: $ExceptionHandlerProvider,
8483        $filter: $FilterProvider,
8484        $$forceReflow: $$ForceReflowProvider,
8485        $interpolate: $InterpolateProvider,
8486        $interval: $IntervalProvider,
8487        $http: $HttpProvider,
8488        $httpParamSerializer: $HttpParamSerializerProvider,
8489        $httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
8490        $httpBackend: $HttpBackendProvider,
8491        $xhrFactory: $xhrFactoryProvider,
8492        $jsonpCallbacks: $jsonpCallbacksProvider,
8493        $location: $LocationProvider,
8494        $log: $LogProvider,
8495        $parse: $ParseProvider,
8496        $rootScope: $RootScopeProvider,
8497        $q: $QProvider,
8498        $$q: $$QProvider,
8499        $sce: $SceProvider,
8500        $sceDelegate: $SceDelegateProvider,
8501        $sniffer: $SnifferProvider,
8502        $templateCache: $TemplateCacheProvider,
8503        $templateRequest: $TemplateRequestProvider,
8504        $$testability: $$TestabilityProvider,
8505        $timeout: $TimeoutProvider,
8506        $window: $WindowProvider,
8507        $$rAF: $$RAFProvider,
8508        $$jqLite: $$jqLiteProvider,
8509        $$HashMap: $$HashMapProvider,
8510        $$cookieReader: $$CookieReaderProvider
8511      });
8512    }
8513  ]);
8514}
8515
8516/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8517 *     Any commits to this file should be reviewed with security in mind.  *
8518 *   Changes to this file can potentially create security vulnerabilities. *
8519 *          An approval from 2 Core members with history of modifying      *
8520 *                         this file is required.                          *
8521 *                                                                         *
8522 *  Does the change somehow allow for arbitrary javascript to be executed? *
8523 *    Or allows for someone to change the prototype of built-in objects?   *
8524 *     Or gives undesired access to variables likes document or window?    *
8525 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
8526
8527/* global JQLitePrototype: true,
8528  addEventListenerFn: true,
8529  removeEventListenerFn: true,
8530  BOOLEAN_ATTR: true,
8531  ALIASED_ATTR: true
8532*/
8533
8534//////////////////////////////////
8535//JQLite
8536//////////////////////////////////
8537
8538/**
8539 * @ngdoc function
8540 * @name angular.element
8541 * @module ng
8542 * @kind function
8543 *
8544 * @description
8545 * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
8546 *
8547 * If jQuery is available, `angular.element` is an alias for the
8548 * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
8549 * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or **jqLite**.
8550 *
8551 * jqLite is a tiny, API-compatible subset of jQuery that allows
8552 * Angular to manipulate the DOM in a cross-browser compatible way. jqLite implements only the most
8553 * commonly needed functionality with the goal of having a very small footprint.
8554 *
8555 * To use `jQuery`, simply ensure it is loaded before the `angular.js` file. You can also use the
8556 * {@link ngJq `ngJq`} directive to specify that jqlite should be used over jQuery, or to use a
8557 * specific version of jQuery if multiple versions exist on the page.
8558 *
8559 * <div class="alert alert-info">**Note:** All element references in Angular are always wrapped with jQuery or
8560 * jqLite (such as the element argument in a directive's compile / link function). They are never raw DOM references.</div>
8561 *
8562 * <div class="alert alert-warning">**Note:** Keep in mind that this function will not find elements
8563 * by tag name / CSS selector. For lookups by tag name, try instead `angular.element(document).find(...)`
8564 * or `$document.find()`, or use the standard DOM APIs, e.g. `document.querySelectorAll()`.</div>
8565 *
8566 * ## Angular's jqLite
8567 * jqLite provides only the following jQuery methods:
8568 *
8569 * - [`addClass()`](http://api.jquery.com/addClass/) - Does not support a function as first argument
8570 * - [`after()`](http://api.jquery.com/after/)
8571 * - [`append()`](http://api.jquery.com/append/)
8572 * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
8573 * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
8574 * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
8575 * - [`clone()`](http://api.jquery.com/clone/)
8576 * - [`contents()`](http://api.jquery.com/contents/)
8577 * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`.
8578 *   As a setter, does not convert numbers to strings or append 'px', and also does not have automatic property prefixing.
8579 * - [`data()`](http://api.jquery.com/data/)
8580 * - [`detach()`](http://api.jquery.com/detach/)
8581 * - [`empty()`](http://api.jquery.com/empty/)
8582 * - [`eq()`](http://api.jquery.com/eq/)
8583 * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
8584 * - [`hasClass()`](http://api.jquery.com/hasClass/)
8585 * - [`html()`](http://api.jquery.com/html/)
8586 * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
8587 * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
8588 * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces, selectors or event object as parameter
8589 * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
8590 * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
8591 * - [`prepend()`](http://api.jquery.com/prepend/)
8592 * - [`prop()`](http://api.jquery.com/prop/)
8593 * - [`ready()`](http://api.jquery.com/ready/)
8594 * - [`remove()`](http://api.jquery.com/remove/)
8595 * - [`removeAttr()`](http://api.jquery.com/removeAttr/) - Does not support multiple attributes
8596 * - [`removeClass()`](http://api.jquery.com/removeClass/) - Does not support a function as first argument
8597 * - [`removeData()`](http://api.jquery.com/removeData/)
8598 * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
8599 * - [`text()`](http://api.jquery.com/text/)
8600 * - [`toggleClass()`](http://api.jquery.com/toggleClass/) - Does not support a function as first argument
8601 * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers
8602 * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces or event object as parameter
8603 * - [`val()`](http://api.jquery.com/val/)
8604 * - [`wrap()`](http://api.jquery.com/wrap/)
8605 *
8606 * ## jQuery/jqLite Extras
8607 * Angular also provides the following additional methods and events to both jQuery and jqLite:
8608 *
8609 * ### Events
8610 * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction a
8610pis and fires this event
8611 *    on all DOM nodes being removed.  This can be used to clean up any 3rd party bindings to the DOM
8612 *    element before it is removed.
8613 *
8614 * ### Methods
8615 * - `controller(name)` - retrieves the controller of the current element or its parent. By default
8616 *   retrieves controller associated with the `ngController` directive. If `name` is provided as
8617 *   camelCase directive name, then the controller for this directive will be retrieved (e.g.
8618 *   `'ngModel'`).
8619 * - `injector()` - retrieves the injector of the current element or its parent.
8620 * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
8621 *   element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to
8622 *   be enabled.
8623 * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
8624 *   current element. This getter should be used only on elements that contain a directive which starts a new isolate
8625 *   scope. Calling `scope()` on this element always returns the original non-isolate scope.
8626 *   Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
8627 * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
8628 *   parent element is reached.
8629 *
8630 * @knownIssue You cannot spy on `angular.element` if you are using Jasmine version 1.x. See
8631 * https://github.com/angular/angular.js/issues/14251 for more information.
8632 *
8633 * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
8634 * @returns {Object} jQuery object.
8635 */
8636
8637JQLite.expando = 'ng339';
8638
8639var jqCache = JQLite.cache = {},
8640    jqId = 1,
8641    addEventListenerFn = function(element, type, fn) {
8642      element.addEventListener(type, fn, false);
8643    },
8644    removeEventListenerFn = function(element, type, fn) {
8645      element.removeEventListener(type, fn, false);
8646    };
8647
8648/*
8649 * !!! This is an undocumented "private" function !!!
8650 */
8651JQLite._data = function(node) {
8652  //jQuery always returns an object on cache miss
8653  return this.cache[node[this.expando]] || {};
8654};
8655
8656function jqNextId() { return ++jqId; }
8657
8658
8659var SPECIAL_CHARS_REGEXP = /([:\-_]+(.))/g;
8660var MOZ_HACK_REGEXP = /^moz([A-Z])/;
8661var MOUSE_EVENT_MAP = { mouseleave: 'mouseout', mouseenter: 'mouseover' };
8662var jqLiteMinErr = minErr('jqLite');
8663
8664/**
8665 * Converts snake_case to camelCase.
8666 * Also there is special case for Moz prefix starting with upper case letter.
8667 * @param name Name to normalize
8668 */
8669function camelCase(name) {
8670  return name.
8671    replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
8672      return offset ? letter.toUpperCase() : letter;
8673    }).
8674    replace(MOZ_HACK_REGEXP, 'Moz$1');
8675}
8676
8677var SINGLE_TAG_REGEXP = /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/;
8678var HTML_REGEXP = /<|&#?\w+;/;
8679var TAG_NAME_REGEXP = /<([\w:-]+)/;
8680var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi;
8681
8682var wrapMap = {
8683  'option': [1, '<select multiple="multiple">', '</select>'],
8684
8685  'thead': [1, '<table>', '</table>'],
8686  'col': [2, '<table><colgroup>', '</colgroup></table>'],
8687  'tr': [2, '<table><tbody>', '</tbody></table>'],
8688  'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],
8689  '_default': [0, '', '']
8690};
8691
8692wrapMap.optgroup = wrapMap.option;
8693wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
8694wrapMap.th = wrapMap.td;
8695
8696
8697function jqLiteIsTextNode(html) {
8698  return !HTML_REGEXP.test(html);
8699}
8700
8701function jqLiteAcceptsData(node) {
8702  // The window object can accept data but has no nodeType
8703  // Otherwise we are only interested in elements (1) and documents (9)
8704  var nodeType = node.nodeType;
8705  return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
8706}
8707
8708function jqLiteHasData(node) {
8709  for (var key in jqCache[node.ng339]) {
8710    return true;
8711  }
8712  return false;
8713}
8714
8715function jqLiteCleanData(nodes) {
8716  for (var i = 0, ii = nodes.length; i < ii; i++) {
8717    jqLiteRemoveData(nodes[i]);
8718  }
8719}
8720
8721function jqLiteBuildFragment(html, context) {
8722  var tmp, tag, wrap,
8723      fragment = context.createDocumentFragment(),
8724      nodes = [], i;
8725
8726  if (jqLiteIsTextNode(html)) {
8727    // Convert non-html into a text node
8728    nodes.push(context.createTextNode(html));
8729  } else {
8730    // Convert html into DOM nodes
8731    tmp = fragment.appendChild(context.createElement('div'));
8732    tag = (TAG_NAME_REGEXP.exec(html) || ['', ''])[1].toLowerCase();
8733    wrap = wrapMap[tag] || wrapMap._default;
8734    tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, '<$1></$2>') + wrap[2];
8735
8736    // Descend through wrappers to the right content
8737    i = wrap[0];
8738    while (i--) {
8739      tmp = tmp.lastChild;
8740    }
8741
8742    nodes = concat(nodes, tmp.childNodes);
8743
8744    tmp = fragment.firstChild;
8745    tmp.textContent = '';
8746  }
8747
8748  // Remove wrapper from fragment
8749  fragment.textContent = '';
8750  fragment.innerHTML = ''; // Clear inner HTML
8751  forEach(nodes, function(node) {
8752    fragment.appendChild(node);
8753  });
8754
8755  return fragment;
8756}
8757
8758function jqLiteParseHTML(html, context) {
8759  context = context || window.document;
8760  var parsed;
8761
8762  if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
8763    return [context.createElement(parsed[1])];
8764  }
8765
8766  if ((parsed = jqLiteBuildFragment(html, context))) {
8767    return parsed.childNodes;
8768  }
8769
8770  return [];
8771}
8772
8773function jqLiteWrapNode(node, wrapper) {
8774  var parent = node.parentNode;
8775
8776  if (parent) {
8777    parent.replaceChild(wrapper, node);
8778  }
8779
8780  wrapper.appendChild(node);
8781}
8782
8783
8784// IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
8785var jqLiteContains = window.Node.prototype.contains || /** @this */ function(arg) {
8786  // eslint-disable-next-line no-bitwise
8787  return !!(this.compareDocumentPosition(arg) & 16);
8788};
8789
8790/////////////////////////////////////////////
8791function JQLite(element) {
8792  if (element instanceof JQLite) {
8793    return element;
8794  }
8795
8796  var argIsString;
8797
8798  if (isString(element)) {
8799    element = trim(element);
8800    argIsString = true;
8801  }
8802  if (!(this instanceof JQLite)) {
8803    if (argIsString && element.charAt(0) !== '<') {
8804      throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
8805    }
8806    return new JQLite(element);
8807  }
8808
8809  if (argIsString) {
8810    jqLiteAddNodes(this, jqLiteParseHTML(element));
8811  } else {
8812    jqLiteAddNodes(this, element);
8813  }
8814}
8815
8816function jqLiteClone(element) {
8817  return element.cloneNode(true);
8818}
8819
8820function jqLiteDealoc(element, onlyDescendants) {
8821  if (!onlyDescendants) jqLiteRemoveData(element);
8822
8823  if (element.querySelectorAll) {
8824    var descendants = element.querySelectorAll('*');
8825    for (var i = 0, l = descendants.length; i < l; i++) {
8826      jqLiteRemoveData(descendants[i]);
8827    }
8828  }
8829}
8830
8831function jqLiteOff(element, type, fn, unsupported) {
8832  if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
8833
8834  var expandoStore = jqLiteExpandoStore(element);
8835  var events = expandoStore && expandoStore.events;
8836  var handle = expandoStore && expandoStore.handle;
8837
8838  if (!handle) return; //no listeners registered
8839
8840  if (!type) {
8841    for (type in events) {
8842      if (type !== '$destroy') {
8843        removeEventListenerFn(element, type, handle);
8844      }
8845      delete events[type];
8846    }
8847  } else {
8848
8849    var removeHandler = function(type) {
8850      var listenerFns = events[type];
8851      if (isDefined(fn)) {
8852        arrayRemove(listenerFns || [], fn);
8853      }
8854      if (!(isDefined(fn) && listenerFns && listenerFns.length > 0)) {
8855        removeEventListenerFn(element, type, handle);
8856        delete events[type];
8857      }
8858    };
8859
8860    forEach(type.split(' '), function(type) {
8861      removeHandler(type);
8862      if (MOUSE_EVENT_MAP[type]) {
8863        removeHandler(MOUSE_EVENT_MAP[type]);
8864      }
8865    });
8866  }
8867}
8868
8869function jqLiteRemoveData(element, name) {
8870  var expandoId = element.ng339;
8871  var expandoStore = expandoId && jqCache[expandoId];
8872
8873  if (expandoStore) {
8874    if (name) {
8875      delete expandoStore.data[name];
8876      return;
8877    }
8878
8879    if (expandoStore.handle) {
8880      if (expandoStore.events.$destroy) {
8881        expandoStore.handle({}, '$destroy');
8882      }
8883      jqLiteOff(element);
8884    }
8885    delete jqCache[expandoId];
8886    element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
8887  }
8888}
8889
8890
8891function jqLiteExpandoStore(element, createIfNecessary) {
8892  var expandoId = element.ng339,
8893      expandoStore = expandoId && jqCache[expandoId];
8894
8895  if (createIfNecessary && !expandoStore) {
8896    element.ng339 = expandoId = jqNextId();
8897    expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined};
8898  }
8899
8900  return expandoStore;
8901}
8902
8903
8904function jqLiteData(element, key, value) {
8905  if (jqLiteAcceptsData(element)) {
8906
8907    var isSimpleSetter = isDefined(value);
8908    var isSimpleGetter = !isSimpleSetter && key && !isObject(key);
8909    var massGetter = !key;
8910    var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter);
8911    var data = expandoStore && expandoStore.data;
8912
8913    if (isSimpleSetter) { // data('key', value)
8914      data[key] = value;
8915    } else {
8916      if (massGetter) {  // data()
8917        return data;
8918      } else {
8919        if (isSimpleGetter) { // data('key')
8920          // don't force creation of expandoStore if it doesn't exist yet
8921          return data && data[key];
8922        } else { // mass-setter: data({key1: val1, key2: val2})
8923          extend(data, key);
8924        }
8925      }
8926    }
8927  }
8928}
8929
8930function jqLiteHasClass(element, selector) {
8931  if (!element.getAttribute) return false;
8932  return ((' ' + (element.getAttribute('class') || '') + ' ').replace(/[\n\t]/g, ' ').
8933      indexOf(' ' + selector + ' ') > -1);
8934}
8935
8936function jqLiteRemoveClass(element, cssClasses) {
8937  if (cssClasses && element.setAttribute) {
8938    forEach(cssClasses.split(' '), function(cssClass) {
8939      element.setAttribute('class', trim(
8940          (' ' + (element.getAttribute('class') || '') + ' ')
8941          .replace(/[\n\t]/g, ' ')
8942          .replace(' ' + trim(cssClass) + ' ', ' '))
8943      );
8944    });
8945  }
8946}
8947
8948function jqLiteAddClass(element, cssClasses) {
8949  if (cssClasses && element.setAttribute) {
8950    var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
8951                            .replace(/[\n\t]/g, ' ');
8952
8953    forEach(cssClasses.split(' '), function(cssClass) {
8954      cssClass = trim(cssClass);
8955      if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
8956        existingClasses += cssClass + ' ';
8957      }
8958    });
8959
8960    element.setAttribute('class', trim(existingClasses));
8961  }
8962}
8963
8964
8965function jqLiteAddNodes(root, elements) {
8966  // THIS CODE IS VERY HOT. Don't make changes without benchmarking.
8967
8968  if (elements) {
8969
8970    // if a Node (the most common case)
8971    if (elements.nodeType) {
8972      root[root.length++] = elements;
8973    } else {
8974      var length = elements.length;
8975
8976      // if an Array or NodeList and not a Window
8977      if (typeof length === 'number' && elements.window !== elements) {
8978        if (length) {
8979          for (var i = 0; i < length; i++) {
8980            root[root.length++] = elements[i];
8981          }
8982        }
8983      } else {
8984        root[root.length++] = elements;
8985      }
8986    }
8987  }
8988}
8989
8990
8991function jqLiteController(element, name) {
8992  return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller');
8993}
8994
8995function jqLiteInheritedData(element, name, value) {
8996  // if element is the document object work with the html element instead
8997  // this makes $(document).scope() possible
8998  if (element.nodeType === NODE_TYPE_DOCUMENT) {
8999    element = element.documentElement;
9000  }
9001  var names = isArray(name) ? name : [name];
9002
9003  while (element) {
9004    for (var i = 0, ii = names.length; i < ii; i++) {
9005      if (isDefined(value = jqLite.data(element, names[i]))) return value;
9006    }
9007
9008    // If dealing with a document fragment node with a host element, and no parent, use the host
9009    // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
9010    // to lookup parent controllers.
9011    element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
9012  }
9013}
9014
9015function jqLiteEmpty(element) {
9016  jqLiteDealoc(element, true);
9017  while (element.firstChild) {
9018    element.removeChild(element.firstChild);
9019  }
9020}
9021
9022function jqLiteRemove(element, keepData) {
9023  if (!keepData) jqLiteDealoc(element);
9024  var parent = element.parentNode;
9025  if (parent) parent.removeChild(element);
9026}
9027
9028
9029function jqLiteDocumentLoaded(action, win) {
9030  win = win || window;
9031  if (win.document.readyState === 'complete') {
9032    // Force the action to be run async for consistent behavior
9033    // from the action's point of view
9034    // i.e. it will definitely not be in a $apply
9035    win.setTimeout(action);
9036  } else {
9037    // No need to unbind this handler as load is only ever called once
9038    jqLite(win).on('load', action);
9039  }
9040}
9041
9042//////////////////////////////////////////
9043// Functions which are declared directly.
9044//////////////////////////////////////////
9045var JQLitePrototype = JQLite.prototype = {
9046  ready: function(fn) {
9047    var fired = false;
9048
9049    function trigger() {
9050      if (fired) return;
9051      fired = true;
9052      fn();
9053    }
9054
9055    // check if document is already loaded
9056    if (window.document.readyState === 'complete') {
9057      window.setTimeout(trigger);
9058    } else {
9059      this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
9060      // we can not use jqLite since we are not done loading and jQuery could be loaded later.
9061      // eslint-disable-next-line new-cap
9062      JQLite(window).on('load', trigger); // fallback to window.onload for others
9063    }
9064  },
9065  toString: function() {
9066    var value = [];
9067    forEach(this, function(e) { value.push('' + e);});
9068    return '[' + value.join(', ') + ']';
9069  },
9070
9071  eq: function(index) {
9072      return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
9073  },
9074
9075  length: 0,
9076  push: push,
9077  sort: [].sort,
9078  splice: [].splice
9079};
9080
9081//////////////////////////////////////////
9082// Functions iterating getter/setters.
9083// these functions return self on setter and
9084// value on get.
9085//////////////////////////////////////////
9086var BOOLEAN_ATTR = {};
9087forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
9088  BOOLEAN_ATTR[lowercase(value)] = value;
9089});
9090var BOOLEAN_ELEMENTS = {};
9091forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
9092  BOOLEAN_ELEMENTS[value] = true;
9093});
9094var ALIASED_ATTR = {
9095  'ngMinlength': 'minlength',
9096  'ngMaxlength': 'maxlength',
9097  'ngMin': 'min',
9098  'ngMax': 'max',
9099  'ngPattern': 'pattern'
9100};
9101
9102function getBooleanAttrName(element, name) {
9103  // check dom last since we will most likely fail on name
9104  var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
9105
9106  // booleanAttr is here twice to minimize DOM access
9107  return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
9108}
9109
9110function getAliasedAttrName(name) {
9111  return ALIASED_ATTR[name];
9112}
9113
9114forEach({
9115  data: jqLiteData,
9116  removeData: jqLiteRemoveData,
9117  hasData: jqLiteHasData,
9118  cleanData: jqLiteCleanData
9119}, function(fn, name) {
9120  JQLite[name] = fn;
9121});
9122
9123forEach({
9124  data: jqLiteData,
9125  inheritedData: jqLiteInheritedData,
9126
9127  scope: function(element) {
9128    // Can't use jqLiteData here directly so we stay compatible with jQuery!
9129    return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
9130  },
9131
9132  isolateScope: function(element) {
9133    // Can't use jqLiteData here directly so we stay compatible with jQuery!
9134    return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
9135  },
9136
9137  controller: jqLiteController,
9138
9139  injector: function(element) {
9140    return jqLiteInheritedData(element, '$injector');
9141  },
9142
9143  removeAttr: function(element, name) {
9144    element.removeAttribute(name);
9145  },
9146
9147  hasClass: jqLiteHasClass,
9148
9149  css: function(element, name, value) {
9150    name = camelCase(name);
9151
9152    if (isDefined(value)) {
9153      element.style[name] = value;
9154    } else {
9155      return element.style[name];
9156    }
9157  },
9158
9159  attr: function(element, name, value) {
9160    var nodeType = element.nodeType;
9161    if (nodeType === NODE_TYPE_TEXT || nodeType === NODE_TYPE_ATTRIBUTE || nodeType === NODE_TYPE_COMMENT) {
9162      return;
9163    }
9164    var lowercasedName = lowercase(name);
9165    if (BOOLEAN_ATTR[lowercasedName]) {
9166      if (isDefined(value)) {
9167        if (value) {
9168          element[name] = true;
9169          element.setAttribute(name, lowercasedName);
9170        } else {
9171          element[name] = false;
9172          element.removeAttribute(lowercasedName);
9173        }
9174      } else {
9175        return (element[name] ||
9176                 (element.attributes.getNamedItem(name) || noop).specified)
9177               ? lowercasedName
9178               : undefined;
9179      }
9180    } else if (isDefined(value)) {
9181      element.setAttribute(name, value);
9182    } else if (element.getAttribute) {
9183      // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
9184      // some elements (e.g. Document) don't have get attribute, so return undefined
9185      var ret = element.getAttribute(name, 2);
9186      // normalize non-existing attributes to undefined (as jQuery)
9187      return ret === null ? undefined : ret;
9188    }
9189  },
9190
9191  prop: function(element, name, value) {
9192    if (isDefined(value)) {
9193      element[name] = value;
9194    } else {
9195      return element[name];
9196    }
9197  },
9198
9199  text: (function() {
9200    getText.$dv = '';
9201    return getText;
9202
9203    function getText(element, value) {
9204      if (isUndefined(value)) {
9205        var nodeType = element.nodeType;
9206        return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
9207      }
9208      element.textContent = value;
9209    }
9210  })(),
9211
9212  val: function(element, value) {
9213    if (isUndefined(value)) {
9214      if (element.multiple && nodeName_(element) === 'select') {
9215        var result = [];
vendor: 10,287 bytes, lines 9216-9558
9216        forEach(element.options, function(option) {
9217          if (option.selected) {
9218            result.push(option.value || option.text);
9219          }
9220        });
9221        return result.length === 0 ? null : result;
9222      }
9223      return element.value;
9224    }
9225    element.value = value;
9226  },
9227
9228  html: function(element, value) {
9229    if (isUndefined(value)) {
9230      return element.innerHTML;
9231    }
9232    jqLiteDealoc(element, true);
9233    element.innerHTML = value;
9234  },
9235
9236  empty: jqLiteEmpty
9237}, function(fn, name) {
9238  /**
9239   * Properties: writes return selection, reads return first value
9240   */
9241  JQLite.prototype[name] = function(arg1, arg2) {
9242    var i, key;
9243    var nodeCount = this.length;
9244
9245    // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
9246    // in a way that survives minification.
9247    // jqLiteEmpty takes no arguments but is a setter.
9248    if (fn !== jqLiteEmpty &&
9249        (isUndefined((fn.length === 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2))) {
9250      if (isObject(arg1)) {
9251
9252        // we are a write, but the object properties are the key/values
9253        for (i = 0; i < nodeCount; i++) {
9254          if (fn === jqLiteData) {
9255            // data() takes the whole object in jQuery
9256            fn(this[i], arg1);
9257          } else {
9258            for (key in arg1) {
9259              fn(this[i], key, arg1[key]);
9260            }
9261          }
9262        }
9263        // return self for chaining
9264        return this;
9265      } else {
9266        // we are a read, so read the first child.
9267        // TODO: do we still need this?
9268        var value = fn.$dv;
9269        // Only if we have $dv do we iterate over all, otherwise it is just the first element.
9270        var jj = (isUndefined(value)) ? Math.min(nodeCount, 1) : nodeCount;
9271        for (var j = 0; j < jj; j++) {
9272          var nodeValue = fn(this[j], arg1, arg2);
9273          value = value ? value + nodeValue : nodeValue;
9274        }
9275        return value;
9276      }
9277    } else {
9278      // we are a write, so apply to all children
9279      for (i = 0; i < nodeCount; i++) {
9280        fn(this[i], arg1, arg2);
9281      }
9282      // return self for chaining
9283      return this;
9284    }
9285  };
9286});
9287
9288function createEventHandler(element, events) {
9289  var eventHandler = function(event, type) {
9290    // jQuery specific api
9291    event.isDefaultPrevented = function() {
9292      return event.defaultPrevented;
9293    };
9294
9295    var eventFns = events[type || event.type];
9296    var eventFnsLength = eventFns ? eventFns.length : 0;
9297
9298    if (!eventFnsLength) return;
9299
9300    if (isUndefined(event.immediatePropagationStopped)) {
9301      var originalStopImmediatePropagation = event.stopImmediatePropagation;
9302      event.stopImmediatePropagation = function() {
9303        event.immediatePropagationStopped = true;
9304
9305        if (event.stopPropagation) {
9306          event.stopPropagation();
9307        }
9308
9309        if (originalStopImmediatePropagation) {
9310          originalStopImmediatePropagation.call(event);
9311        }
9312      };
9313    }
9314
9315    event.isImmediatePropagationStopped = function() {
9316      return event.immediatePropagationStopped === true;
9317    };
9318
9319    // Some events have special handlers that wrap the real handler
9320    var handlerWrapper = eventFns.specialHandlerWrapper || defaultHandlerWrapper;
9321
9322    // Copy event handlers in case event handlers array is modified during execution.
9323    if ((eventFnsLength > 1)) {
9324      eventFns = shallowCopy(eventFns);
9325    }
9326
9327    for (var i = 0; i < eventFnsLength; i++) {
9328      if (!event.isImmediatePropagationStopped()) {
9329        handlerWrapper(element, event, eventFns[i]);
9330      }
9331    }
9332  };
9333
9334  // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all
9335  //       events on `element`
9336  eventHandler.elem = element;
9337  return eventHandler;
9338}
9339
9340function defaultHandlerWrapper(element, event, handler) {
9341  handler.call(element, event);
9342}
9343
9344function specialMouseHandlerWrapper(target, event, handler) {
9345  // Refer to jQuery's implementation of mouseenter & mouseleave
9346  // Read about mouseenter and mouseleave:
9347  // http://www.quirksmode.org/js/events_mouse.html#link8
9348  var related = event.relatedTarget;
9349  // For mousenter/leave call the handler if related is outside the target.
9350  // NB: No relatedTarget if the mouse left/entered the browser window
9351  if (!related || (related !== target && !jqLiteContains.call(target, related))) {
9352    handler.call(target, event);
9353  }
9354}
9355
9356//////////////////////////////////////////
9357// Functions iterating traversal.
9358// These functions chain results into a single
9359// selector.
9360//////////////////////////////////////////
9361forEach({
9362  removeData: jqLiteRemoveData,
9363
9364  on: function jqLiteOn(element, type, fn, unsupported) {
9365    if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
9366
9367    // Do not add event handlers to non-elements because they will not be cleaned up.
9368    if (!jqLiteAcceptsData(element)) {
9369      return;
9370    }
9371
9372    var expandoStore = jqLiteExpandoStore(element, true);
9373    var events = expandoStore.events;
9374    var handle = expandoStore.handle;
9375
9376    if (!handle) {
9377      handle = expandoStore.handle = createEventHandler(element, events);
9378    }
9379
9380    // http://jsperf.com/string-indexof-vs-split
9381    var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
9382    var i = types.length;
9383
9384    var addHandler = function(type, specialHandlerWrapper, noEventListener) {
9385      var eventFns = events[type];
9386
9387      if (!eventFns) {
9388        eventFns = events[type] = [];
9389        eventFns.specialHandlerWrapper = specialHandlerWrapper;
9390        if (type !== '$destroy' && !noEventListener) {
9391          addEventListenerFn(element, type, handle);
9392        }
9393      }
9394
9395      eventFns.push(fn);
9396    };
9397
9398    while (i--) {
9399      type = types[i];
9400      if (MOUSE_EVENT_MAP[type]) {
9401        addHandler(MOUSE_EVENT_MAP[type], specialMouseHandlerWrapper);
9402        addHandler(type, undefined, true);
9403      } else {
9404        addHandler(type);
9405      }
9406    }
9407  },
9408
9409  off: jqLiteOff,
9410
9411  one: function(element, type, fn) {
9412    element = jqLite(element);
9413
9414    //add the listener twice so that when it is called
9415    //you can remove the original function and still be
9416    //able to call element.off(ev, fn) normally
9417    element.on(type, function onFn() {
9418      element.off(type, fn);
9419      element.off(type, onFn);
9420    });
9421    element.on(type, fn);
9422  },
9423
9424  replaceWith: function(element, replaceNode) {
9425    var index, parent = element.parentNode;
9426    jqLiteDealoc(element);
9427    forEach(new JQLite(replaceNode), function(node) {
9428      if (index) {
9429        parent.insertBefore(node, index.nextSibling);
9430      } else {
9431        parent.replaceChild(node, element);
9432      }
9433      index = node;
9434    });
9435  },
9436
9437  children: function(element) {
9438    var children = [];
9439    forEach(element.childNodes, function(element) {
9440      if (element.nodeType === NODE_TYPE_ELEMENT) {
9441        children.push(element);
9442      }
9443    });
9444    return children;
9445  },
9446
9447  contents: function(element) {
9448    return element.contentDocument || element.childNodes || [];
9449  },
9450
9451  append: function(element, node) {
9452    var nodeType = element.nodeType;
9453    if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
9454
9455    node = new JQLite(node);
9456
9457    for (var i = 0, ii = node.length; i < ii; i++) {
9458      var child = node[i];
9459      element.appendChild(child);
9460    }
9461  },
9462
9463  prepend: function(element, node) {
9464    if (element.nodeType === NODE_TYPE_ELEMENT) {
9465      var index = element.firstChild;
9466      forEach(new JQLite(node), function(child) {
9467        element.insertBefore(child, index);
9468      });
9469    }
9470  },
9471
9472  wrap: function(element, wrapNode) {
9473    jqLiteWrapNode(element, jqLite(wrapNode).eq(0).clone()[0]);
9474  },
9475
9476  remove: jqLiteRemove,
9477
9478  detach: function(element) {
9479    jqLiteRemove(element, true);
9480  },
9481
9482  after: function(element, newElement) {
9483    var index = element, parent = element.parentNode;
9484
9485    if (parent) {
9486      newElement = new JQLite(newElement);
9487
9488      for (var i = 0, ii = newElement.length; i < ii; i++) {
9489        var node = newElement[i];
9490        parent.insertBefore(node, index.nextSibling);
9491        index = node;
9492      }
9493    }
9494  },
9495
9496  addClass: jqLiteAddClass,
9497  removeClass: jqLiteRemoveClass,
9498
9499  toggleClass: function(element, selector, condition) {
9500    if (selector) {
9501      forEach(selector.split(' '), function(className) {
9502        var classCondition = condition;
9503        if (isUndefined(classCondition)) {
9504          classCondition = !jqLiteHasClass(element, className);
9505        }
9506        (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);
9507      });
9508    }
9509  },
9510
9511  parent: function(element) {
9512    var parent = element.parentNode;
9513    return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;
9514  },
9515
9516  next: function(element) {
9517    return element.nextElementSibling;
9518  },
9519
9520  find: function(element, selector) {
9521    if (element.getElementsByTagName) {
9522      return element.getElementsByTagName(selector);
9523    } else {
9524      return [];
9525    }
9526  },
9527
9528  clone: jqLiteClone,
9529
9530  triggerHandler: function(element, event, extraParameters) {
9531
9532    var dummyEvent, eventFnsCopy, handlerArgs;
9533    var eventName = event.type || event;
9534    var expandoStore = jqLiteExpandoStore(element);
9535    var events = expandoStore && expandoStore.events;
9536    var eventFns = events && events[eventName];
9537
9538    if (eventFns) {
9539      // Create a dummy event to pass to the handlers
9540      dummyEvent = {
9541        preventDefault: function() { this.defaultPrevented = true; },
9542        isDefaultPrevented: function() { return this.defaultPrevented === true; },
9543        stopImmediatePropagation: function() { this.immediatePropagationStopped = true; },
9544        isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; },
9545        stopPropagation: noop,
9546        type: eventName,
9547        target: element
9548      };
9549
9550      // If a custom event was provided then extend our dummy event with it
9551      if (event.type) {
9552        dummyEvent = extend(dummyEvent, event);
9553      }
9554
9555      // Copy event handlers in case event handlers array is modified during execution.
9556      eventFnsCopy = shallowCopy(eventFns);
9557      handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
9558
9559      forEach(eventFnsCopy, function(fn) {
9560        if (!dummyEvent.isImmediatePropagationStopped()) {
9561          fn.apply(element, handlerArgs);
9562        }
9563      });
9564    }
9565  }
9566}, function(fn, name) {
9567  /**
9568   * chaining functions
9569   */
9570  JQLite.prototype[name] = function(arg1, arg2, arg3) {
9571    var value;
9572
9573    for (var i = 0, ii = this.length; i < ii; i++) {
9574      if (isUndefined(value)) {
9575        value = fn(this[i], arg1, arg2, arg3);
9576        if (isDefined(value)) {
9577          // any function which returns a value needs to be wrapped
9578          value = jqLite(value);
9579        }
9580      } else {
9581        jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
9582      }
9583    }
9584    return isDefined(value) ? value : this;
9585  };
9586});
9587
9588// bind legacy bind/unbind to on/off
9589JQLite.prototype.bind = JQLite.prototype.on;
9590JQLite.prototype.unbind = JQLite.prototype.off;
9591
9592
9593// Provider for private $$jqLite service
9594/** @this */
9595function $$jqLiteProvider() {
9596  this.$get = function $$jqLite() {
9597    return extend(JQLite, {
9598      hasClass: function(node, classes) {
9599        if (node.attr) node = node[0];
9600        return jqLiteHasClass(node, classes);
9601      },
9602      addClass: function(node, classes) {
9603        if (node.attr) node = node[0];
9604        return jqLiteAddClass(node, classes);
9605      },
9606      removeClass: function(node, classes) {
9607        if (node.attr) node = node[0];
9608        return jqLiteRemoveClass(node, classes);
9609      }
9610    });
9611  };
9612}
9613
9614/**
9615 * Computes a hash of an 'obj'.
9616 * Hash of a:
9617 *  string is string
9618 *  number is number as string
9619 *  object is either result of calling $$hashKey function on the object or uniquely generated id,
9620 *         that is also assigned to the $$hashKey property of the object.
9621 *
9622 * @param obj
9623 * @returns {string} hash string such that the same input will have the same hash string.
9624 *         The resulting string key is in 'type:hashKey' format.
9625 */
9626function hashKey(obj, nextUidFn) {
9627  var key = obj && obj.$$hashKey;
9628
9629  if (key) {
9630    if (typeof key === 'function') {
9631      key = obj.$$hashKey();
9632    }
9633    return key;
9634  }
9635
9636  var objType = typeof obj;
9637  if (objType === 'function' || (objType === 'object' && obj !== null)) {
9638    key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)();
9639  } else {
9640    key = objType + ':' + obj;
9641  }
9642
9643  return key;
9644}
9645
9646/**
9647 * HashMap which can use objects as keys
9648 */
9649function HashMap(array, isolatedUid) {
9650  if (isolatedUid) {
9651    var uid = 0;
9652    this.nextUid = function() {
9653      return ++uid;
9654    };
9655  }
9656  forEach(array, this.put, this);
9657}
9658HashMap.prototype = {
9659  /**
9660   * Store key value pair
9661   * @param key key to store can be any type
9662   * @param value value to store can be any type
9663   */
9664  put: function(key, value) {
9665    this[hashKey(key, this.nextUid)] = value;
9666  },
9667
9668  /**
9669   * @param key
9670   * @returns {Object} the value for the key
9671   */
9672  get: function(key) {
9673    return this[hashKey(key, this.nextUid)];
9674  },
9675
9676  /**
9677   * Remove the key/value pair
9678   * @param key
9679   */
9680  remove: function(key) {
9681    var value = this[key = hashKey(key, this.nextUid)];
9682    delete this[key];
9683    return value;
9684  }
9685};
9686
9687var $$HashMapProvider = [/** @this */function() {
9688  this.$get = [function() {
9689    return HashMap;
9690  }];
9691}];
9692
9693/**
9694 * @ngdoc function
9695 * @module ng
9696 * @name angular.injector
9697 * @kind function
9698 *
9699 * @description
9700 * Creates an injector object that can be used for retrieving services as well as for
9701 * dependency injection (see {@link guide/di dependency injection}).
9702 *
9703 * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
9704 *     {@link angular.module}. The `ng` module must be explicitly added.
9705 * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which
9706 *     disallows argument name annotation inference.
9707 * @returns {injector} Injector object. See {@link auto.$injector $injector}.
9708 *
9709 * @example
9710 * Typical usage
9711 * ```js
9712 *   // create an injector
9713 *   var $injector = angular.injector(['ng']);
9714 *
9715 *   // use the injector to kick off your application
9716 *   // use the type inference to auto inject arguments, or use impl
9716icit injection
9717 *   $injector.invoke(function($rootScope, $compile, $document) {
9718 *     $compile($document)($rootScope);
9719 *     $rootScope.$digest();
9720 *   });
9721 * ```
9722 *
9723 * Sometimes you want to get access to the injector of a currently running Angular app
9724 * from outside Angular. Perhaps, you want to inject and compile some markup after the
9725 * application has been bootstrapped. You can do this using the extra `injector()` added
9726 * to JQuery/jqLite elements. See {@link angular.element}.
9727 *
9728 * *This is fairly rare but could be the case if a third party library is injecting the
9729 * markup.*
9730 *
9731 * In the following example a new block of HTML containing a `ng-controller`
9732 * directive is added to the end of the document body by JQuery. We then compile and link
9733 * it into the current AngularJS scope.
9734 *
9735 * ```js
9736 * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
9737 * $(document.body).append($div);
9738 *
9739 * angular.element(document).injector().invoke(function($compile) {
9740 *   var scope = angular.element($div).scope();
9741 *   $compile($div)(scope);
9742 * });
9743 * ```
9744 */
9745
9746
9747/**
9748 * @ngdoc module
9749 * @name auto
9750 * @installation
9751 * @description
9752 *
9753 * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
9754 */
9755
9756var ARROW_ARG = /^([^(]+?)=>/;
9757var FN_ARGS = /^[^(]*\(\s*([^)]*)\)/m;
9758var FN_ARG_SPLIT = /,/;
9759var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
9760var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
9761var $injectorMinErr = minErr('$injector');
9762
9763function stringifyFn(fn) {
9764  // Support: Chrome 50-51 only
9765  // Creating a new string by adding `' '` at the end, to hack around some bug in Chrome v50/51
9766  // (See https://github.com/angular/angular.js/issues/14487.)
9767  // TODO (gkalpak): Remove workaround when Chrome v52 is released
9768  return Function.prototype.toString.call(fn) + ' ';
9769}
9770
9771function extractArgs(fn) {
9772  var fnText = stringifyFn(fn).replace(STRIP_COMMENTS, ''),
9773      args = fnText.match(ARROW_ARG) || fnText.match(FN_ARGS);
9774  return args;
9775}
9776
9777function anonFn(fn) {
9778  // For anonymous functions, showing at the very least the function signature can help in
9779  // debugging.
9780  var args = extractArgs(fn);
9781  if (args) {
9782    return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
9783  }
9784  return 'fn';
9785}
9786
9787function annotate(fn, strictDi, name) {
9788  var $inject,
9789      argDecl,
9790      last;
9791
9792  if (typeof fn === 'function') {
9793    if (!($inject = fn.$inject)) {
9794      $inject = [];
9795      if (fn.length) {
9796        if (strictDi) {
9797          if (!isString(name) || !name) {
9798            name = fn.name || anonFn(fn);
9799          }
9800          throw $injectorMinErr('strictdi',
9801            '{0} is not using explicit annotation and cannot be invoked in strict mode', name);
9802        }
9803        argDecl = extractArgs(fn);
9804        forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
9805          arg.replace(FN_ARG, function(all, underscore, name) {
9806            $inject.push(name);
9807          });
9808        });
9809      }
9810      fn.$inject = $inject;
9811    }
9812  } else if (isArray(fn)) {
9813    last = fn.length - 1;
9814    assertArgFn(fn[last], 'fn');
9815    $inject = fn.slice(0, last);
9816  } else {
9817    assertArgFn(fn, 'fn', true);
9818  }
9819  return $inject;
9820}
9821
9822///////////////////////////////////////
9823
9824/**
9825 * @ngdoc service
9826 * @name $injector
9827 *
9828 * @description
9829 *
9830 * `$injector` is used to retrieve object instances as defined by
9831 * {@link auto.$provide provider}, instantiate types, invoke methods,
9832 * and load modules.
9833 *
9834 * The following always holds true:
9835 *
9836 * ```js
9837 *   var $injector = angular.injector();
9838 *   expect($injector.get('$injector')).toBe($injector);
9839 *   expect($injector.invoke(function($injector) {
9840 *     return $injector;
9841 *   })).toBe($injector);
9842 * ```
9843 *
9844 * # Injection Function Annotation
9845 *
9846 * JavaScript does not have annotations, and annotations are needed for dependency injection. The
9847 * following are all valid ways of annotating function with injection arguments and are equivalent.
9848 *
9849 * ```js
9850 *   // inferred (only works if code not minified/obfuscated)
9851 *   $injector.invoke(function(serviceA){});
9852 *
9853 *   // annotated
9854 *   function explicit(serviceA) {};
9855 *   explicit.$inject = ['serviceA'];
9856 *   $injector.invoke(explicit);
9857 *
9858 *   // inline
9859 *   $injector.invoke(['serviceA', function(serviceA){}]);
9860 * ```
9861 *
9862 * ## Inference
9863 *
9864 * In JavaScript calling `toString()` on a function returns the function definition. The definition
9865 * can then be parsed and the function arguments can be extracted. This method of discovering
9866 * annotations is disallowed when the injector is in strict mode.
9867 * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the
9868 * argument names.
9869 *
9870 * ## `$inject` Annotation
9871 * By adding an `$inject` property onto a function the injection parameters can be specified.
9872 *
9873 * ## Inline
9874 * As an array of injection names, where the last item in the array is the function to call.
9875 */
9876
9877/**
9878 * @ngdoc method
9879 * @name $injector#get
9880 *
9881 * @description
9882 * Return an instance of the service.
9883 *
9884 * @param {string} name The name of the instance to retrieve.
9885 * @param {string=} caller An optional string to provide the origin of the function call for error messages.
9886 * @return {*} The instance.
9887 */
9888
9889/**
9890 * @ngdoc method
9891 * @name $injector#invoke
9892 *
9893 * @description
9894 * Invoke the method and supply the method arguments from the `$injector`.
9895 *
9896 * @param {Function|Array.<string|Function>} fn The injectable function to invoke. Function parameters are
9897 *   injected according to the {@link guide/di $inject Annotation} rules.
9898 * @param {Object=} self The `this` for the invoked method.
9899 * @param {Object=} locals Optional object. If preset then any argument names are read from this
9900 *                         object first, before the `$injector` is consulted.
9901 * @returns {*} the value returned by the invoked `fn` function.
9902 */
9903
9904/**
9905 * @ngdoc method
9906 * @name $injector#has
9907 *
9908 * @description
9909 * Allows the user to query if the particular service exists.
9910 *
9911 * @param {string} name Name of the service to query.
9912 * @returns {boolean} `true` if injector has given service.
9913 */
9914
9915/**
9916 * @ngdoc method
9917 * @name $injector#instantiate
9918 * @description
9919 * Create a new instance of JS type. The method takes a constructor function, invokes the new
9920 * operator, and supplies all of the arguments to the constructor function as specified by the
9921 * constructor annotation.
9922 *
9923 * @param {Function} Type Annotated constructor function.
9924 * @param {Object=} locals Optional object. If preset then any argument names are read from this
9925 * object first, before the `$injector` is consulted.
9926 * @returns {Object} new instance of `Type`.
9927 */
9928
9929/**
9930 * @ngdoc method
9931 * @name $injector#annotate
9932 *
9933 * @description
9934 * Returns an array of service names which the function is requesting for injection. This API is
9935 * used by the injector to determine which services need to be injected into the function when the
9936 * function is invoked. There are three ways in which the function can be annotated with the needed
9937 * dependencies.
9938 *
9939 * # Argument names
9940 *
9941 * The simplest form is to extract the dependencies from the arguments of the function. This is done
9942 * by converting the function into a string using `toString()` method and extracting the argument
9943 * names.
9944 * ```js
9945 *   // Given
9946 *   function MyController($scope, $route) {
9947 *     // ...
9948 *   }
9949 *
9950 *   // Then
9951 *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
9952 * ```
9953 *
9954 * You can disallow this method by using strict injection mode.
9955 *
9956 * This method does not work with code minification / obfuscation. For this reason the following
9957 * annotation strategies are supported.
9958 *
9959 * # The `$inject` property
9960 *
9961 * If a function has an `$inject` property and its value is an array of strings, then the strings
9962 * represent names of services to be injected into the function.
9963 * ```js
9964 *   // Given
9965 *   var MyController = function(obfuscatedScope, obfuscatedRoute) {
9966 *     // ...
9967 *   }
9968 *   // Define function dependencies
9969 *   MyController['$inject'] = ['$scope', '$route'];
9970 *
9971 *   // Then
9972 *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
9973 * ```
9974 *
9975 * # The array notation
9976 *
9977 * It is often desirable to inline Injected functions and that's when setting the `$inject` property
9978 * is very inconvenient. In these situations using the array notation to specify the dependencies in
9979 * a way that survives minification is a better choice:
9980 *
9981 * ```js
9982 *   // We wish to write this (not minification / obfuscation safe)
9983 *   injector.invoke(function($compile, $rootScope) {
9984 *     // ...
9985 *   });
9986 *
9987 *   // We are forced to write break inlining
9988 *   var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
9989 *     // ...
9990 *   };
9991 *   tmpFn.$inject = ['$compile', '$rootScope'];
9992 *   injector.invoke(tmpFn);
9993 *
9994 *   // To better support inline function the inline annotation is supported
9995 *   injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
9996 *     // ...
9997 *   }]);
9998 *
9999 *   // Therefore
10000 *   expect(injector.annotate(
10001 *      ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
10002 *    ).toEqual(['$compile', '$rootScope']);
10003 * ```
10004 *
10005 * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
10006 * be retrieved as described above.
10007 *
10008 * @param {boolean=} [strictDi=false] Disallow argument name annotation inference.
10009 *
10010 * @returns {Array.<string>} The names of the services which the function requires.
10011 */
10012
10013
10014
10015/**
10016 * @ngdoc service
10017 * @name $provide
10018 *
10019 * @description
10020 *
10021 * The {@link auto.$provide $provide} service has a number of methods for registering components
10022 * with the {@link auto.$injector $injector}. Many of these functions are also exposed on
10023 * {@link angular.Module}.
10024 *
10025 * An Angular **service** is a singleton object created by a **service factory**.  These **service
10026 * factories** are functions which, in turn, are created by a **service provider**.
10027 * The **service providers** are constructor functions. When instantiated they must contain a
10028 * property called `$get`, which holds the **service factory** function.
10029 *
10030 * When you request a service, the {@link auto.$injector $injector} is responsible for finding the
10031 * correct **service provider**, instantiating it and then calling its `$get` **service factory**
10032 * function to get the instance of the **service**.
10033 *
10034 * Often services have no configuration options and there is no need to add methods to the service
10035 * provider.  The provider will be no more than a constructor function with a `$get` property. For
10036 * these cases the {@link auto.$provide $provide} service has additional helper methods to register
10037 * services without specifying a provider.
10038 *
10039 * * {@link auto.$provide#provider provider(name, provider)} - registers a **service provider** with the
10040 *     {@link auto.$injector $injector}
10041 * * {@link auto.$provide#constant constant(name, obj)} - registers a value/object that can be accessed by
10042 *     providers and services.
10043 * * {@link auto.$provide#value value(name, obj)} - registers a value/object that can only be accessed by
10044 *     services, not providers.
10045 * * {@link auto.$provide#factory factory(name, fn)} - registers a service **factory function**
10046 *     that will be wrapped in a **service provider** object, whose `$get` property will contain the
10047 *     given factory function.
10048 * * {@link auto.$provide#service service(name, Fn)} - registers a **constructor function**
10049 *     that will be wrapped in a **service provider** object, whose `$get` property will instantiate
10050 *      a new object using the given constructor function.
10051 * * {@link auto.$provide#decorator decorator(name, decorFn)} - registers a **decorator function** that
10052 *      will be able to modify or replace the implementation of another service.
10053 *
10054 * See the individual methods for more information and examples.
10055 */
10056
10057/**
10058 * @ngdoc method
10059 * @name $provide#provider
10060 * @description
10061 *
10062 * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions
10063 * are constructor functions, whose instances are responsible for "providing" a factory for a
10064 * service.
10065 *
10066 * Service provider names start with the name of the service they provide followed by `Provider`.
10067 * For example, the {@link ng.$log $log} service has a provider called
10068 * {@link ng.$logProvider $logProvider}.
10069 *
10070 * Service provider objects can have additional methods which allow configuration of the provider
10071 * and its service. Importantly, you can configure what kind of service is created by the `$get`
10072 * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
10073 * method {@link ng.$logProvider#debugEnabled debugEnabled}
10074 * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
10075 * console or not.
10076 *
10077 * @param {string} name The name of the instance. NOTE: the provider will be available under `name +
10078                        'Provider'` key.
10079 * @param {(Object|function())} provider If the provider is:
10080 *
10081 *   - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
10082 *     {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.
10083 *   - `Constructor`: a new instance of the provider will be created using
10084 *     {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.
10085 *
10086 * @returns {Object} registered provider instance
10087
10088 * @example
10089 *
10090 * The following example shows how to create a simple event tracking service and register it using
10091 * {@link auto.$provide#provider $provide.provider()}.
10092 *
10093 * ```js
10094 *  // Define the eventTracker provider
10095 *  function EventTrackerProvider() {
10096 *    var trackingUrl = '/track';
10097 *
10098 *    // A provider method for configuring where the tracked events should been saved
10099 *    this.setTrackingUrl = function(url) {
10100 *      trackingUrl = url;
10101 *    };
10102 *
10103 *    // The service factory function
10104 *    this.$get = ['$http', function($http) {
10105 *      var trackedEvents = {};
10106 *      return {
10107 *        // Call this to track an event
10108 *        event: function(event) {
10109 *          var count = trackedEvents[event] || 0;
10110 *          count += 1;
10111 *          trackedEvents[event] = count;
10112 *          return count;
10113 *        },
10114 *        // Call this to save the tracked events to the trackingUrl
10115 *        save: function() {
10116 *          $http.post(trackingUrl, trackedEvents);
10117 *        }
10118 *      };
10119 *    }];
10120 *  }
10121 *
10122 *  describe('eventTracker', function() {
10123 *    var postSpy;
10124 *
10125 *    beforeEach(module(function($provide) {
10126 *      // Register the eventTracker provider
10127 *      $provide.provider('eventTracker', EventTrackerProvider);
10128 *    }));
10129 *
10130 *    beforeEach(module(function(eventTrackerProvider) {
10131 *      // Configure eventTracker provider
10132 *      eventTrackerProvider.setTrackingUrl('/custom-track');
10133 *    }));
10134 *
10135 *    it('tracks events', inject(function(eventTracker) {
10136 *      expect(eventTracker.event('login')).toEqual(1);
10137 *      expect(eventTracker.event('login')).toEqual(2);
10138 *    }));
10139 *
10140 *    it('saves to the tracking url', inject(function(eventTracker, $http) {
10141 *      postSpy = spyOn($http, 'post');
10142 *      eventTracker.event('login');
10143 *      eventTracker.save();
10144 *      expect(postSpy).toHaveBeenCalled();
10145 *      expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
10146 *      expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
10147 *      expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
10148 *    }));
10149 *  });
10150 * ```
10151 */
10152
10153/**
10154 * @ngdoc method
10155 * @name $provide#factory
10156 * @description
10157 *
10158 * Register a **service factory**, which will be called to return the service instance.
10159 * This is short for registering a service where its provider consists of only a `$get` property,
10160 * which is the given service factory function.
10161 * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to
10162 * configure your service in a provider.
10163 *
10164 * @param {string} name The name of the instance.
10165 * @param {Function|Array.<string|Function>} $getFn The injectable $getFn for the instance creation.
10166 *                      Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`.
10167 * @returns {Object} registered provider instance
10168 *
10169 * @example
10170 * Here is an example of registering a service
10171 * ```js
10172 *   $provide.factory('ping', ['$http', function($http) {
10173 *     return function ping() {
10174 *       return $http.send('/ping');
10175 *     };
10176 *   }]);
10177 * ```
10178 * You would then inject and use this service like this:
10179 * ```js
10180 *   someModule.controller('Ctrl', ['ping', function(ping) {
10181 *     ping();
10182 *   }]);
10183 * ```
10184 */
10185
10186
10187/**
10188 * @ngdoc method
10189 * @name $provide#service
10190 * @description
10191 *
10192 * Register a **service constructor**, which will be invoked with `new` to create the service
10193 * instance.
10194 * This is short for registering a service where its provider's `$get` property is a factory
10195 * function that returns an instance instantiated by the injector from the service constructor
10196 * function.
10197 *
10198 * Internally it looks a bit like this:
10199 *
10200 * ```
10201 * {
10202 *   $get: function() {
10203 *     return $injector.instantiate(constructor);
10204 *   }
10205 * }
10206 * ```
10207 *
10208 *
10209 * You should use {@link auto.$provide#service $provide.service(class)} if you define your service
10210 * as a type/class.
10211 *
10212 * @param {string} name The name of the instance.
10213 * @param {Function|Array.<string|Function>} constructor An injectable class (constructor function)
10214 *     that will be instantiated.
10215 * @returns {Object} registered provider instance
10216 *
10217 * @example
10218 * Here is an example of registering a service using
10219 * {@link auto.$provide#service $provide.service(class)}.
10220 * ```js
10221 *   var Ping = function($http) {
10222 *     this.$http = $http;
10223 *   };
10224 *
10225 *   Ping.$inject = ['$http'];
10226 *
10227 *   Ping.prototype.send = function() {
10228 *     return this.$http.get('/ping');
10229 *   };
10230 *   $provide.service('ping', Ping);
10231 * ```
10232 * You would then inject and use this service like this:
10233 * ```js
10234 *   someModule.controller('Ctrl', ['ping', function(ping) {
10235 *     ping.send();
10236 *   }]);
10237 * ```
10238 */
10239
10240
10241/**
10242 * @ngdoc method
10243 * @name $provide#value
10244 * @description
10245 *
10246 * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
10247 * number, an array, an object or a function. This is short for registering a service where its
10248 * provider's `$get` property is a factory function that takes no arguments and returns the **value
10249 * service**. That also means it is not possible to inject other services into a value service.
10250 *
10251 * Value services are similar to constant services, except that they cannot be injected into a
10252 * module configuration function (see {@link angular.Module#config}) but they can be overridden by
10253 * an Angular {@link auto.$provide#decorator decorator}.
10254 *
10255 * @param {string} name The name of the instance.
10256 * @param {*} value The value.
10257 * @returns {Object} registered provider instance
10258 *
10259 * @example
10260 * Here are some examples of creating value services.
10261 * ```js
10262 *   $provide.value('ADMIN_USER', 'admin');
10263 *
10264 *   $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
10265 *
10266 *   $provide.value('halfOf', function(value) {
10267 *     return value / 2;
10268 *   });
10269 * ```
10270 */
10271
10272
10273/**
10274 * @ngdoc method
10275 * @name $provide#constant
10276 * @description
10277 *
10278 * Register a **constant service** with the {@link auto.$injector $injector}, such as a string,
10279 * a number, an array, an object or a function. Like the {@link auto.$provide#value value}, it is not
10280 * possible to inject other services into a constant.
10281 *
10282 * But unlike {@link auto.$provide#value value}, a constant can be
10283 * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
10284 * be overridden by an Angular {@link auto.$provide#decorator decorator}.
10285 *
10286 * @param {string} name The name of the constant.
10287 * @param {*} value The constant value.
10288 * @returns {Object} registered instance
10289 *
10290 * @example
10291 * Here a some examples of creating constants:
10292 * ```js
10293 *   $provide.constant('SHARD_HEIGHT', 306);
10294 *
10295 *   $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
10296 *
10297 *   $provide.constant('double', function(value) {
10298 *     return value * 2;
10299 *   });
10300 * ```
10301 */
10302
10303
10304/**
10305 * @ngdoc method
10306 * @name $provide#decorator
10307 * @description
10308 *
10309 * Register a **decorator function** with the {@link auto.$injector $injector}. A decorator function
10310 * intercepts the creation of a service, allowing it to override or modify the behavior of the
10311 * service. The return value of the decorator function may be the original service, or a new service
10312 * that replaces (or wraps and delegates to) the original service.
10313 *
10314 * You can find out more about using decorators in the {@link guide/decorators} guide.
10315 *
10316 * @param {string} name The name of the service to decorate.
10317 * @param {Function|Array.<string|Function>} decorator This function will be invoked when the service needs to be
10318 *    provided and should return the decorated service instance. The function is called using
10319 *    the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.
10320 *    Local injection arguments:
10321 *
10322 *    * `$delegate` - The original service instance, which can be replaced, monkey patched, configured,
10323 *      decorated or delegated to.
10324 *
10325 * @example
10326 * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
10327 * calls to {@link ng.$log#error $log.warn()}.
10328 * ```js
10329 *   $provide.decorator('$log', ['$delegate', function($delegate) {
10330 *     $delegate.warn = $delegate.error;
10331 *     return $delegate;
10332 *   }]);
10333 * ```
10334 */
10335
10336
10337function createInjector(modulesToLoad, strictDi) {
10338  strictDi = (strictDi === true);
10339  var INSTANTIATING = {},
10340      providerSuffix = 'Provider',
10341      path = [],
10342      loadedModules = new HashMap([], true),
10343      providerCache = {
10344        $provide: {
10345            provider: supportObject(provider),
10346            factory: supportObject(factory),
10347            service: supportObject(service),
10348            value: supportObject(value),
10349            constant: supportObject(constant),
10350            decorator: decorator
10351          }
10352      },
10353      providerInjector = (providerCache.$injector =
10354          createInternalInjector(providerCache, function(serviceName, caller) {
10355            if (angular.isString(caller)) {
10356              path.push(caller);
10357            }
10358            throw $injectorMinErr('unpr', 'Unknown provider: {0}', path.join(' <- '));
10359          })),
10360      instanceCache = {},
10361      protoInstanceInjector =
10362          createInternalInjector(instanceCache, function(serviceName, caller) {
10363            var provider = providerInjector.get(serviceName + providerSuffix, caller);
10364            return instanceInjector.invoke(
10365                provider.$get, provider, undefined, serviceName);
10366          }),
10367      instanceInjector = protoInstanceInjector;
10368
10369  providerCache['$injector' + providerSuffix] = { $get: valueFn(protoInstanceInjector) };
10370  var runBlocks = loadModules(modulesToLoad);
10371  instanceInjector = protoInstanceInjector.get('$injector');
10372  instanceInjector.strictDi = strictDi;
10373  forEach(runBlocks, function(fn) { if (fn) instanceInjector.invoke(fn); });
10374
10375  return instanceInjector;
10376
10377  ////////////////////////////////////
10378  // $provider
10379  ////////////////////////////////////
10380
10381  function supportObject(delegate) {
10382    return function(key, value) {
10383      if (isObject(key)) {
vendor: 5,751 bytes, lines 10384-10549
10384        forEach(key, reverseParams(delegate));
10385      } else {
10386        return delegate(key, value);
10387      }
10388    };
10389  }
10390
10391  function provider(name, provider_) {
10392    assertNotHasOwnProperty(name, 'service');
10393    if (isFunction(provider_) || isArray(provider_)) {
10394      provider_ = providerInjector.instantiate(provider_);
10395    }
10396    if (!provider_.$get) {
10397      throw $injectorMinErr('pget', 'Provider \'{0}\' must define $get factory method.', name);
10398    }
10399    return (providerCache[name + providerSuffix] = provider_);
10400  }
10401
10402  function enforceReturnValue(name, factory) {
10403    return /** @this */ function enforcedReturnValue() {
10404      var result = instanceInjector.invoke(factory, this);
10405      if (isUndefined(result)) {
10406        throw $injectorMinErr('undef', 'Provider \'{0}\' must return a value from $get factory method.', name);
10407      }
10408      return result;
10409    };
10410  }
10411
10412  function factory(name, factoryFn, enforce) {
10413    return provider(name, {
10414      $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
10415    });
10416  }
10417
10418  function service(name, constructor) {
10419    return factory(name, ['$injector', function($injector) {
10420      return $injector.instantiate(constructor);
10421    }]);
10422  }
10423
10424  function value(name, val) { return factory(name, valueFn(val), false); }
10425
10426  function constant(name, value) {
10427    assertNotHasOwnProperty(name, 'constant');
10428    providerCache[name] = value;
10429    instanceCache[name] = value;
10430  }
10431
10432  function decorator(serviceName, decorFn) {
10433    var origProvider = providerInjector.get(serviceName + providerSuffix),
10434        orig$get = origProvider.$get;
10435
10436    origProvider.$get = function() {
10437      var origInstance = instanceInjector.invoke(orig$get, origProvider);
10438      return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
10439    };
10440  }
10441
10442  ////////////////////////////////////
10443  // Module Loading
10444  ////////////////////////////////////
10445  function loadModules(modulesToLoad) {
10446    assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array');
10447    var runBlocks = [], moduleFn;
10448    forEach(modulesToLoad, function(module) {
10449      if (loadedModules.get(module)) return;
10450      loadedModules.put(module, true);
10451
10452      function runInvokeQueue(queue) {
10453        var i, ii;
10454        for (i = 0, ii = queue.length; i < ii; i++) {
10455          var invokeArgs = queue[i],
10456              provider = providerInjector.get(invokeArgs[0]);
10457
10458          provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
10459        }
10460      }
10461
10462      try {
10463        if (isString(module)) {
10464          moduleFn = angularModule(module);
10465          runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
10466          runInvokeQueue(moduleFn._invokeQueue);
10467          runInvokeQueue(moduleFn._configBlocks);
10468        } else if (isFunction(module)) {
10469            runBlocks.push(providerInjector.invoke(module));
10470        } else if (isArray(module)) {
10471            runBlocks.push(providerInjector.invoke(module));
10472        } else {
10473          assertArgFn(module, 'module');
10474        }
10475      } catch (e) {
10476        if (isArray(module)) {
10477          module = module[module.length - 1];
10478        }
10479        if (e.message && e.stack && e.stack.indexOf(e.message) === -1) {
10480          // Safari & FF's stack traces don't contain error.message content
10481          // unlike those of Chrome and IE
10482          // So if stack doesn't contain message, we create a new string that contains both.
10483          // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
10484          // eslint-disable-next-line no-ex-assign
10485          e = e.message + '\n' + e.stack;
10486        }
10487        throw $injectorMinErr('modulerr', 'Failed to instantiate module {0} due to:\n{1}',
10488                  module, e.stack || e.message || e);
10489      }
10490    });
10491    return runBlocks;
10492  }
10493
10494  ////////////////////////////////////
10495  // internal Injector
10496  ////////////////////////////////////
10497
10498  function createInternalInjector(cache, factory) {
10499
10500    function getService(serviceName, caller) {
10501      if (cache.hasOwnProperty(serviceName)) {
10502        if (cache[serviceName] === INSTANTIATING) {
10503          throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
10504                    serviceName + ' <- ' + path.join(' <- '));
10505        }
10506        return cache[serviceName];
10507      } else {
10508        try {
10509          path.unshift(serviceName);
10510          cache[serviceName] = INSTANTIATING;
10511          cache[serviceName] = factory(serviceName, caller);
10512          return cache[serviceName];
10513        } catch (err) {
10514          if (cache[serviceName] === INSTANTIATING) {
10515            delete cache[serviceName];
10516          }
10517          throw err;
10518        } finally {
10519          path.shift();
10520        }
10521      }
10522    }
10523
10524
10525    function injectionArgs(fn, locals, serviceName) {
10526      var args = [],
10527          $inject = createInjector.$$annotate(fn, strictDi, serviceName);
10528
10529      for (var i = 0, length = $inject.length; i < length; i++) {
10530        var key = $inject[i];
10531        if (typeof key !== 'string') {
10532          throw $injectorMinErr('itkn',
10533                  'Incorrect injection token! Expected service name as string, got {0}', key);
10534        }
10535        args.push(locals && locals.hasOwnProperty(key) ? locals[key] :
10536                                                         getService(key, serviceName));
10537      }
10538      return args;
10539    }
10540
10541    function isClass(func) {
10542      // IE 9-11 do not support classes and IE9 leaks with the code below.
10543      if (msie <= 11) {
10544        return false;
10545      }
10546      // Support: Edge 12-13 only
10547      // See: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/6156135/
10548      return typeof func === 'function'
10549        && /^(?:class\b|constructor\()/.test(stringifyFn(func))
vendor: 13,053 bytes, lines 10549-10932
10549;
10550    }
10551
10552    function invoke(fn, self, locals, serviceName) {
10553      if (typeof locals === 'string') {
10554        serviceName = locals;
10555        locals = null;
10556      }
10557
10558      var args = injectionArgs(fn, locals, serviceName);
10559      if (isArray(fn)) {
10560        fn = fn[fn.length - 1];
10561      }
10562
10563      if (!isClass(fn)) {
10564        // http://jsperf.com/angularjs-invoke-apply-vs-switch
10565        // #5388
10566        return fn.apply(self, args);
10567      } else {
10568        args.unshift(null);
10569        return new (Function.prototype.bind.apply(fn, args))();
10570      }
10571    }
10572
10573
10574    function instantiate(Type, locals, serviceName) {
10575      // Check if Type is annotated and use just the given function at n-1 as parameter
10576      // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
10577      var ctor = (isArray(Type) ? Type[Type.length - 1] : Type);
10578      var args = injectionArgs(Type, locals, serviceName);
10579      // Empty object at position 0 is ignored for invocation with `new`, but required.
10580      args.unshift(null);
10581      return new (Function.prototype.bind.apply(ctor, args))();
10582    }
10583
10584
10585    return {
10586      invoke: invoke,
10587      instantiate: instantiate,
10588      get: getService,
10589      annotate: createInjector.$$annotate,
10590      has: function(name) {
10591        return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
10592      }
10593    };
10594  }
10595}
10596
10597createInjector.$$annotate = annotate;
10598
10599/**
10600 * @ngdoc provider
10601 * @name $anchorScrollProvider
10602 * @this
10603 *
10604 * @description
10605 * Use `$anchorScrollProvider` to disable automatic scrolling whenever
10606 * {@link ng.$location#hash $location.hash()} changes.
10607 */
10608function $AnchorScrollProvider() {
10609
10610  var autoScrollingEnabled = true;
10611
10612  /**
10613   * @ngdoc method
10614   * @name $anchorScrollProvider#disableAutoScrolling
10615   *
10616   * @description
10617   * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
10618   * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
10619   * Use this method to disable automatic scrolling.
10620   *
10621   * If automatic scrolling is disabled, one must explicitly call
10622   * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
10623   * current hash.
10624   */
10625  this.disableAutoScrolling = function() {
10626    autoScrollingEnabled = false;
10627  };
10628
10629  /**
10630   * @ngdoc service
10631   * @name $anchorScroll
10632   * @kind function
10633   * @requires $window
10634   * @requires $location
10635   * @requires $rootScope
10636   *
10637   * @description
10638   * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the
10639   * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified
10640   * in the
10641   * [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#an-indicated-part-of-the-document).
10642   *
10643   * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
10644   * match any anchor whenever it changes. This can be disabled by calling
10645   * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.
10646   *
10647   * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
10648   * vertical scroll-offset (either fixed or dynamic).
10649   *
10650   * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of
10651   *                       {@link ng.$location#hash $location.hash()} will be used.
10652   *
10653   * @property {(number|function|jqLite)} yOffset
10654   * If set, specifies a vertical scroll-offset. This is often useful when there are fixed
10655   * positioned elements at the top of the page, such as navbars, headers etc.
10656   *
10657   * `yOffset` can be specified in various ways:
10658   * - **number**: A fixed number of pixels to be used as offset.<br /><br />
10659   * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return
10660   *   a number representing the offset (in pixels).<br /><br />
10661   * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from
10662   *   the top of the page to the element's bottom will be used as offset.<br />
10663   *   **Note**: The element will be taken into account only as long as its `position` is set to
10664   *   `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust
10665   *   their height and/or positioning according to the viewport's size.
10666   *
10667   * <br />
10668   * <div class="alert alert-warning">
10669   * In order for `yOffset` to work properly, scrolling should take place on the document's root and
10670   * not some child element.
10671   * </div>
10672   *
10673   * @example
10674     <example module="anchorScrollExample" name="anchor-scroll">
10675       <file name="index.html">
10676         <div id="scrollArea" ng-controller="ScrollController">
10677           <a ng-click="gotoBottom()">Go to bottom</a>
10678           <a id="bottom"></a> You're at the bottom!
10679         </div>
10680       </file>
10681       <file name="script.js">
10682         angular.module('anchorScrollExample', [])
10683           .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
10684             function($scope, $location, $anchorScroll) {
10685               $scope.gotoBottom = function() {
10686                 // set the location.hash to the id of
10687                 // the element you wish to scroll to.
10688                 $location.hash('bottom');
10689
10690                 // call $anchorScroll()
10691                 $anchorScroll();
10692               };
10693             }]);
10694       </file>
10695       <file name="style.css">
10696         #scrollArea {
10697           height: 280px;
10698           overflow: auto;
10699         }
10700
10701         #bottom {
10702           display: block;
10703           margin-top: 2000px;
10704         }
10705       </file>
10706     </example>
10707   *
10708   * <hr />
10709   * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).
10710   * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.
10711   *
10712   * @example
10713     <example module="anchorScrollOffsetExample" name="anchor-scroll-offset">
10714       <file name="index.html">
10715         <div class="fixed-header" ng-controller="headerCtrl">
10716           <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]">
10717             Go to anchor {{x}}
10718           </a>
10719         </div>
10720         <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]">
10721           Anchor {{x}} of 5
10722         </div>
10723       </file>
10724       <file name="script.js">
10725         angular.module('anchorScrollOffsetExample', [])
10726           .run(['$anchorScroll', function($anchorScroll) {
10727             $anchorScroll.yOffset = 50;   // always scroll by 50 extra pixels
10728           }])
10729           .controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
10730             function($anchorScroll, $location, $scope) {
10731               $scope.gotoAnchor = function(x) {
10732                 var newHash = 'anchor' + x;
10733                 if ($location.hash() !== newHash) {
10734                   // set the $location.hash to `newHash` and
10735                   // $anchorScroll will automatically scroll to it
10736                   $location.hash('anchor' + x);
10737                 } else {
10738                   // call $anchorScroll() explicitly,
10739                   // since $location.hash hasn't changed
10740                   $anchorScroll();
10741                 }
10742               };
10743             }
10744           ]);
10745       </file>
10746       <file name="style.css">
10747         body {
10748           padding-top: 50px;
10749         }
10750
10751         .anchor {
10752           border: 2px dashed DarkOrchid;
10753           padding: 10px 10px 200px 10px;
10754         }
10755
10756         .fixed-header {
10757           background-color: rgba(0, 0, 0, 0.2);
10758           height: 50px;
10759           position: fixed;
10760           top: 0; left: 0; right: 0;
10761         }
10762
10763         .fixed-header > a {
10764           display: inline-block;
10765           margin: 5px 15px;
10766         }
10767       </file>
10768     </example>
10769   */
10770  this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
10771    var document = $window.document;
10772
10773    // Helper function to get first anchor from a NodeList
10774    // (using `Array#some()` instead of `angular#forEach()` since it's more performant
10775    //  and working in all supported browsers.)
10776    function getFirstAnchor(list) {
10777      var result = null;
10778      Array.prototype.some.call(list, function(element) {
10779        if (nodeName_(element) === 'a') {
10780          result = element;
10781          return true;
10782        }
10783      });
10784      return result;
10785    }
10786
10787    function getYOffset() {
10788
10789      var offset = scroll.yOffset;
10790
10791      if (isFunction(offset)) {
10792        offset = offset();
10793      } else if (isElement(offset)) {
10794        var elem = offset[0];
10795        var style = $window.getComputedStyle(elem);
10796        if (style.position !== 'fixed') {
10797          offset = 0;
10798        } else {
10799          offset = elem.getBoundingClientRect().bottom;
10800        }
10801      } else if (!isNumber(offset)) {
10802        offset = 0;
10803      }
10804
10805      return offset;
10806    }
10807
10808    function scrollTo(elem) {
10809      if (elem) {
10810        elem.scrollIntoView();
10811
10812        var offset = getYOffset();
10813
10814        if (offset) {
10815          // `offset` is the number of pixels we should scroll UP in order to align `elem` properly.
10816          // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the
10817          // top of the viewport.
10818          //
10819          // IF the number of pixels from the top of `elem` to the end of the page's content is less
10820          // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some
10821          // way down the page.
10822          //
10823          // This is often the case for elements near the bottom of the page.
10824          //
10825          // In such cases we do not need to scroll the whole `offset` up, just the difference between
10826          // the top of the element and the offset, which is enough to align the top of `elem` at the
10827          // desired position.
10828          var elemTop = elem.getBoundingClientRect().top;
10829          $window.scrollBy(0, elemTop - offset);
10830        }
10831      } else {
10832        $window.scrollTo(0, 0);
10833      }
10834    }
10835
10836    function scroll(hash) {
10837      // Allow numeric hashes
10838      hash = isString(hash) ? hash : isNumber(hash) ? hash.toString() : $location.hash();
10839      var elm;
10840
10841      // empty hash, scroll to the top of the page
10842      if (!hash) scrollTo(null);
10843
10844      // element with given id
10845      else if ((elm = document.getElementById(hash))) scrollTo(elm);
10846
10847      // first anchor with given name :-D
10848      else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);
10849
10850      // no element and hash === 'top', scroll to the top of the page
10851      else if (hash === 'top') scrollTo(null);
10852    }
10853
10854    // does not scroll when user clicks on anchor link that is currently on
10855    // (no url change, no $location.hash() change), browser native does scroll
10856    if (autoScrollingEnabled) {
10857      $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
10858        function autoScrollWatchAction(newVal, oldVal) {
10859          // skip the initial scroll if $location.hash is empty
10860          if (newVal === oldVal && newVal === '') return;
10861
10862          jqLiteDocumentLoaded(function() {
10863            $rootScope.$evalAsync(scroll);
10864          });
10865        });
10866    }
10867
10868    return scroll;
10869  }];
10870}
10871
10872var $animateMinErr = minErr('$animate');
10873var ELEMENT_NODE = 1;
10874var NG_ANIMATE_CLASSNAME = 'ng-animate';
10875
10876function mergeClasses(a,b) {
10877  if (!a && !b) return '';
10878  if (!a) return b;
10879  if (!b) return a;
10880  if (isArray(a)) a = a.join(' ');
10881  if (isArray(b)) b = b.join(' ');
10882  return a + ' ' + b;
10883}
10884
10885function extractElementNode(element) {
10886  for (var i = 0; i < element.length; i++) {
10887    var elm = element[i];
10888    if (elm.nodeType === ELEMENT_NODE) {
10889      return elm;
10890    }
10891  }
10892}
10893
10894function splitClasses(classes) {
10895  if (isString(classes)) {
10896    classes = classes.split(' ');
10897  }
10898
10899  // Use createMap() to prevent class assumptions involving property names in
10900  // Object.prototype
10901  var obj = createMap();
10902  forEach(classes, function(klass) {
10903    // sometimes the split leaves empty string values
10904    // incase extra spaces were applied to the options
10905    if (klass.length) {
10906      obj[klass] = true;
10907    }
10908  });
10909  return obj;
10910}
10911
10912// if any other type of options value besides an Object value is
10913// passed into the $animate.method() animation then this helper code
10914// will be run which will ignore it. While this patch is not the
10915// greatest solution to this, a lot of existing plugins depend on
10916// $animate to either call the callback (< 1.2) or return a promise
10917// that can be changed. This helper function ensures that the options
10918// are wiped clean incase a callback function is provided.
10919function prepareAnimateOptions(options) {
10920  return isObject(options)
10921      ? options
10922      : {};
10923}
10924
10925var $$CoreAnimateJsProvider = /** @this */ function() {
10926  this.$get = noop;
10927};
10928
10929// this is prefixed with Core since it conflicts with
10930// the animateQueueProvider defined in ngAnimate/animateQueue.js
10931var $$CoreAnimateQueueProvider = /** @this */ function() {
10932  var postDigestQueue = new 
vendor: 4,027 bytes, lines 10932-11067
10932HashMap();
10933  var postDigestElements = [];
10934
10935  this.$get = ['$$AnimateRunner', '$rootScope',
10936       function($$AnimateRunner,   $rootScope) {
10937    return {
10938      enabled: noop,
10939      on: noop,
10940      off: noop,
10941      pin: noop,
10942
10943      push: function(element, event, options, domOperation) {
10944        if (domOperation) {
10945          domOperation();
10946        }
10947
10948        options = options || {};
10949        if (options.from) {
10950          element.css(options.from);
10951        }
10952        if (options.to) {
10953          element.css(options.to);
10954        }
10955
10956        if (options.addClass || options.removeClass) {
10957          addRemoveClassesPostDigest(element, options.addClass, options.removeClass);
10958        }
10959
10960        var runner = new $$AnimateRunner();
10961
10962        // since there are no animations to run the runner needs to be
10963        // notified that the animation call is complete.
10964        runner.complete();
10965        return runner;
10966      }
10967    };
10968
10969
10970    function updateData(data, classes, value) {
10971      var changed = false;
10972      if (classes) {
10973        classes = isString(classes) ? classes.split(' ') :
10974                  isArray(classes) ? classes : [];
10975        forEach(classes, function(className) {
10976          if (className) {
10977            changed = true;
10978            data[className] = value;
10979          }
10980        });
10981      }
10982      return changed;
10983    }
10984
10985    function handleCSSClassChanges() {
10986      forEach(postDigestElements, function(element) {
10987        var data = postDigestQueue.get(element);
10988        if (data) {
10989          var existing = splitClasses(element.attr('class'));
10990          var toAdd = '';
10991          var toRemove = '';
10992          forEach(data, function(status, className) {
10993            var hasClass = !!existing[className];
10994            if (status !== hasClass) {
10995              if (status) {
10996                toAdd += (toAdd.length ? ' ' : '') + className;
10997              } else {
10998                toRemove += (toRemove.length ? ' ' : '') + className;
10999              }
11000            }
11001          });
11002
11003          forEach(element, function(elm) {
11004            if (toAdd) {
11005              jqLiteAddClass(elm, toAdd);
11006            }
11007            if (toRemove) {
11008              jqLiteRemoveClass(elm, toRemove);
11009            }
11010          });
11011          postDigestQueue.remove(element);
11012        }
11013      });
11014      postDigestElements.length = 0;
11015    }
11016
11017
11018    function addRemoveClassesPostDigest(element, add, remove) {
11019      var data = postDigestQueue.get(element) || {};
11020
11021      var classesAdded = updateData(data, add, true);
11022      var classesRemoved = updateData(data, remove, false);
11023
11024      if (classesAdded || classesRemoved) {
11025
11026        postDigestQueue.put(element, data);
11027        postDigestElements.push(element);
11028
11029        if (postDigestElements.length === 1) {
11030          $rootScope.$$postDigest(handleCSSClassChanges);
11031        }
11032      }
11033    }
11034  }];
11035};
11036
11037/**
11038 * @ngdoc provider
11039 * @name $animateProvider
11040 *
11041 * @description
11042 * Default implementation of $animate that doesn't perform any animations, instead just
11043 * synchronously performs DOM updates and resolves the returned runner promise.
11044 *
11045 * In order to enable animations the `ngAnimate` module has to be loaded.
11046 *
11047 * To see the functional implementation check out `src/ngAnimate/animate.js`.
11048 */
11049var $AnimateProvider = ['$provide', /** @this */ function($provide) {
11050  var provider = this;
11051
11052  this.$$registeredAnimations = Object.create(null);
11053
11054   /**
11055   * @ngdoc method
11056   * @name $animateProvider#register
11057   *
11058   * @description
11059   * Registers a new injectable animation factory function. The factory function produces the
11060   * animation object which contains callback functions for each event that is expected to be
11061   * animated.
11062   *
11063   *   * `eventFn`: `function(element, ... , doneFunction, options)`
11064   *   The element to animate, the `doneFunction` and the options fed into the animation. Depending
11065   *   on the type of animation additional arguments will be injected into the animation function. The
11066   *   list below explains the function signatures for the different animation methods:
11067   *
11068   *   - setClass: function(element, addedClasses, removedClasses, doneFunction, options)
11069   *   - addClass: function(element, addedClasses, doneFunction, options)
11070   *   - removeClass: function(element, removedClasses, doneFunction, options)
11071   *   - enter, leave, move: function(element, doneFunction, options)
11072   *   - animate: function(element, fromStyles, toStyles, doneFunction, options)
11073   *
11074   *   Make sure to trigger the `doneFunction` once the animation is fully complete.
11075   *
11076   * ```js
11077   *   return {
11078   *     //enter, leave, move signature
11079   *     eventFn : function(element, done, options) {
11080   *       //code to run the animation
11081   *       //once complete, then run done()
11082   *       return function endFunction(wasCancelled) {
11083   *         //code to cancel the animation
11084   *       }
11085   *     }
11086   *   }
11087   * ```
11088   *
11089   * @param {string} name The name of the animation (this is what the class-based CSS value will be compared to).
11090   * @param {Function} factory The factory function that will be executed to return the animation
11091   *                           object.
11092   */
11093  this.register = function(name, factory) {
11094    if (name && name.charAt(0) !== '.') {
11095      throw $animateMinErr('notcsel', 'Expecting class selector starting with \'.\' got \'{0}\'.', name);
11096    }
11097
11098    var key = name + '-animation';
11099    provider.$$registeredAnimations[name.substr(1)] = key;
11100    $provide.factory(key, factory);
11101  };
11102
11103  /**
11104   * @ngdoc method
11105   * @name $animateProvider#classNameFilter
11106   *
11107   * @description
11108   * Sets and/or returns the CSS class regular expression that is checked when performing
11109   * an animation. Upon bootstrap the classNameFilter value is not set at all and will
11110   * therefore enable $animate to attempt to perform an animation on any element that is triggered.
11111   * When setting the `classNameFilter` value, animations will only be performed on elements
11112   * that successfully match the filter expression. This in turn can boost performance
11113   * for low-powered devices as well as applications containing a lot of structural operations.
11114   * @param {RegExp=} expression The className expression which will be checked against all animations
11115   * @return {RegExp} The current CSS className expression value. If null then there is no expression value
11116   */
11117  this.classNameFilter = function(expression) {
11118    if (arguments.length === 1) {
11119      this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
11120      if (this.$$classNameFilter) {
11121        var reservedRegex = new RegExp('(\\s+|\\/)' + NG_ANIMATE_CLASSNAME + '(\\s+|\\/)');
11122        if (reservedRegex.test(this.$$classNameFilter.toString())) {
11123          throw $animateMinErr('nongcls','$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME);
11124
11125        }
11126      }
11127    }
11128    return this.$$classNameFilter;
11129  };
11130
11131  this.$get = ['$$animateQueue', function($$animateQueue) {
11132    function domInsert(element, parentElement, afterElement) {
11133      // if for some reason the previous element was removed
11134      // from the dom sometime before this code runs then let's
11135      // just stick to using the parent element as the anchor
11136      if (afterElement) {
11137        var afterNode = extractElementNode(afterElement);
11138        if (afterNode && !afterNode.parentNode && !afterNode.previousElementSibling) {
11139          afterElement = null;
11140        }
11141      }
11142      if (afterElement) {
11143        afterElement.after(element);
11144      } else {
11145        parentElement.prepend(element);
11146      }
11147    }
11148
11149    /**
11150     * @ngdoc service
11151     * @name $animate
11152     * @description The $animate service exposes a series of DOM utility methods that provide support
11153     * for animation hooks. The default behavior is the application of DOM operations, however,
11154     * when an animation is detected (and animations are enabled), $animate will do the heavy lifting
11155     * to ensure that animation runs with the triggered DOM operation.
11156     *
11157     * By default $animate doesn't trigger any animations. This is because the `ngAnimate` module isn't
11158     * included and only when it is active then the animation hooks that `$animate` triggers will be
11159     * functional. Once active then all structural `ng-` directives will trigger animations as they perform
11160     * their DOM-related operations (enter, leave and move). Other directives such as `ngClass`,
11161     * `ngShow`, `ngHide` and `ngMessages` also provide support for animations.
11162     *
11163     * It is recommended that the`$animate` service is always used when executing DOM-related procedures within directives.
11164     *
11165     * To learn more about enabling animation support, click here to visit the
11166     * {@link ngAnimate ngAnimate module page}.
11167     */
11168    return {
11169      // we don't call it directly since non-existant arguments may
11170      // be interpreted as null within the sub enabled function
11171
11172      /**
11173       *
11174       * @ngdoc method
11175       * @name $animate#on
11176       * @kind function
11177       * @description Sets up an event listener to fire whenever the animation event (enter, leave, move, etc...)
11178       *    has fired on the given element or among any of its children. Once the listener is fired, the provided callback
11179       *    is fired with the following params:
11180       *
11181       * ```js
11182       * $animate.on('enter', container,
11183       *    function callback(element, phase) {
11184       *      // cool we detected an enter animation within the container
11185       *    }
11186       * );
11187       * ```
11188       *
11189       * @param {string} event the animation event that will be captured (e.g. enter, leave, move, addClass, removeClass, etc...)
11190       * @param {DOMElement} container the container element that will capture each of the animation events that are fired on itself
11191       *     as well as among its children
11192       * @param {Function} callback the callback function that will be fired when the listener is triggered
11193       *
11194       * The arguments present in the callback function are:
11195       * * `element` - The captured DOM element that the animation was fired on.
11196       * * `phase` - The phase of the animation. The two possible phases are **start** (when the animation starts) and **close** (when it ends).
11197       */
11198      on: $$animateQueue.on,
11199
11200      /**
11201       *
11202       * @ngdoc method
11203       * @name $animate#off
11204       * @kind function
11205       * @description Deregisters an event listener based on the event which has been associated with the provided element. This method
11206       * can be used in three different ways depending on the arguments:
11207       *
11208       * ```js
11209       * // remove all the animation event listeners listening for `enter`
11210       * $animate.off('enter');
11211       *
11212       * // remove listeners for all animation events from the container element
11213       * $animate.off(container);
11214       *
11215       * // remove all the animation event listeners listening for `enter` on the given element and its children
11216       * $animate.off('enter', container);
11217       *
11218       * // remove the event listener function provided by `callback` that is set
11219       * // to listen for `enter` on the given `container` as well as its children
11220       * $animate.off('enter', container, callback);
11221       * ```
11222       *
11223       * @param {string|DOMElement} event|container the animation event (e.g. enter, leave, move,
11224       * addClass, removeClass, etc...), or the container element. If it is the element, all other
11225       * arguments are ignored.
11226       * @param {DOMElement=} container the container element the event listener was placed on
11227       * @param {Function=} callback the callback function that was registered as the listener
11228       */
11229      off: $$animateQueue.off,
11230
11231      /**
11232       * @ngdoc method
11233       * @name $animate#pin
11234       * @kind function
11235       * @description Associates the provided element with a host parent element to allow the element to be animated even if it exists
11236       *    outside of the DOM structure of the Angular application. By doing so, any animation triggered via `$animate` can be issued on the
11237       *    element despite being outside the realm of the application or within another application. Say for example if the application
11238       *    was bootstrapped on an element that is somewhere inside of the `<body>` tag, but we wanted to allow for an element to be situated
11239       *    as a direct child of `document.body`, then this can be achieved by pinning the element via `$animate.pin(element)`. Keep in mind
11240       *    that calling `$animate.pin(element, parentElement)` will not actually insert into the DOM anywhere; it will just create the association.
11241       *
11242       *    Note that this feature is only active when the `ngAnimate` module is used.
11243       *
11244       * @param {DOMElement} element the external element that will be pinned
11245       * @param {DOMElement} parentElement the host parent element that will be associated with the external element
11246       */
11247      pin: $$animateQueue.pin,
11248
11249      /**
11250       *
11251       * @ngdoc method
11252       * @name $animate#enabled
11253       * @kind function
11254       * @description Used to get and set whether animations are enabled or not on the entire application or on an element and its children. This
11255       * function can be called in four ways:
11256       *
11257       * ```js
11258       * // returns true or false
11259       * $animate.enabled();
11260       *
11261       * // changes the enabled state for all animations
11262       * $animate.enabled(false);
11263       * $animate.enabled(true);
11264       *
11265       * // returns true or false if animations are enabled for an element
11266       * $animate.enabled(element);
11267       *
11268       * // changes the enabled state for an element and its children
11269       * $animate.enabled(element, true);
11270       * $animate.enabled(element, false);
11271       * ```
11272       *
11273       * @param {DOMElement=} element the element that will be considered for checking/setting the enabled state
11274       * @param {boolean=} enabled whether or not the animations will be enabled for the element
11275       *
11276       * @return {boolean} whether or not animations are enabled
11277       */
11278      enabled: $$animateQueue.enabled,
11279
11280      /**
11281       * @ngdoc method
11282       * @name $animate#cancel
11283       * @kind function
11284       * @description Cancels the provided animation.
11285       *
11286       * @param {Promise} animationPromise The animation promise that is returned when an animation is started.
11287       */
11288      cancel: function(runner) {
11289        if (runner.end) {
11290          runner.end();
11291        }
11292      },
11293
11294      /**
11295       *
11296       * @ngdoc method
11297       * @name $animate#enter
11298       * @kind function
11299       * @description Inserts the element into the DOM either after the `after` element (if provided) or
11300       *   as the first child within the `parent` element and then triggers an animation.
11301       *   A promise is returned that will be resolved during the next digest once the animation
11302       *   has completed.
11303       *
11304       * @param {DOMElement} element the element which will be inserted into the DOM
11305       * @param {DOMElement} parent the parent element which will append the element as
11306       *   a child (so long as the after element is not present)
11307       * @param {DOMElement=} after the sibling element after which the element will be appended
11308       * @param {object=} options an optional collection of options/styles that will be applied to the element.
11309       *   The object can have the following properties:
11310       *
11311       *   - **addClass** - `{string}` - space-separated CSS classes to add to element
11312       *   - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
11313       *   - **removeClass** - `{string}` - space-separated CSS classes to remove from element
11314       *   - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
11315       *
11316       * @return {Promise} the animation callback promise
11317       */
11318      enter: function(element, parent, after, options) {
11319        parent = parent && jqLite(parent);
11320        after = after && jqLite(after);
11321        parent = parent || after.parent();
11322        domInsert(element, parent, after);
11323        return $$animateQueue.push(element, 'enter', prepareAnimateOptions(options));
11324      },
11325
11326      /**
11327       *
11328       * @ngdoc method
11329       * @name $animate#move
11330       * @kind function
11331       * @description Inserts (moves) the element into its new position in the DOM either after
11332       *   the `after` element (if provided) or as the first child within the `parent` element
11333       *   and then triggers an animation. A promise is returned that will be resolved
11334       *   during the next digest once the animation has completed.
11335       *
11336       * @param {DOMElement} element the element which will be moved into the new DOM position
11337       * @param {DOMElement} parent the parent element which will append the element as
11338       *   a child (so long as the after element is not present)
11339       * @param {DOMElement=} after the sibling element after which the element will be appended
11340       * @param {object=} options an optional collection of options/styles that will be applied to the element.
11341       *   The object can have the following properties:
11342       *
11343       *   - **addClass** - `{string}` - space-separated CSS classes to add to element
11344       *   - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
11345       *   - **removeClass** - `{string}` - space-separated CSS classes to remove from element
11346       *   - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
11347       *
11348       * @return {Promise} the animation callback promise
11349       */
11350      move: function(element, parent, after, options) {
11351        parent = parent && jqLite(parent);
11352        after = after && jqLite(after);
11353        parent = parent || after.parent();
11354        domInsert(element, parent, after);
11355        return $$animateQueue.push(element, 'move', prepareAnimateOptions(options));
11356      },
11357
11358      /**
11359       * @ngdoc method
11360       * @name $animate#leave
11361       * @kind function
11362       * @description Triggers an animation and then removes the element from the DOM.
11363       * When the function is called a promise is returned that will be resolved during the next
11364       * digest once the animation has completed.
11365       *
11366       * @param {DOMElement} element the element which will be removed from the DOM
11367       * @param {object=} options an optional collection of options/styles that will be applied to the element.
11368       *   The object can have the following properties:
11369       *
11370       *   - **addClass** - `{string}` - space-separated CSS classes to add to element
11371       *   - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
11372       *   - **removeClass** - `{string}` - space-separated CSS classes to remove from element
11373       *   - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
11374       *
11375       * @return {Promise} the animation callback promise
11376       */
11377      leave: function(element, options) {
11378        return $$animateQueue.push(element, 'leave', prepareAnimateOptions(options), function() {
11379          element.remove();
11380        });
11381      },
11382
11383      /**
11384       * @ngdoc method
11385       * @name $animate#addClass
11386       * @kind function
11387       *
11388       * @description Triggers an addClass animation surrounding the addition of the provided CSS class(es). Upon
11389       *   execution, the addClass operation will only be handled after the next digest and it will not trigger an
11390       *   animation if element already contains the CSS class or if the class is removed at a later step.
11391       *   Note that class-based animations are treated differently compared to structural animations
11392       *   (like enter, move and leave) since the CSS classes may be added/removed at different points
11393       *   depending if CSS or JavaScript animations are used.
11394       *
11395       * @param {DOMElement} element the element which the CSS classes will be applied to
11396       * @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces)
11397       * @param {object=} options an optional collection of options/styles that will be applied to the element.
11398       *   The object can have the following properties:
11399       *
11400       *   - **addClass** - `{string}` - space-separated CSS classes to add to element
11401       *   - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
11402       *   - **removeClass** - `{string}` - space-separated CSS classes to remove from element
11403       *   - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
11404       *
11405       * @return {Promise} the animation callback promise
11406       */
11407      addClass: function(element, className, options) {
11408        options = prepareAnimateOptions(options);
11409        options.addClass = mergeClasses(options.addclass, className);
11410        return $$animateQueue.push(element, 'addClass', options);
11411      },
11412
11413      /**
11414       * @ngdoc method
11415       * @name $animate#removeClass
11416       * @kind function
11417       *
11418       * @description Triggers a removeClass animation surrounding the removal of the provided CSS class(es). Upon
11419       *   execution, the removeClass operation will only be handled after the next digest and it will not trigger an
11420       *   animation if element does not contain the CSS class or if the class is added at a later step.
11421       *   Note that class-based animations are treated differently compared to structural animations
11422       *   (like enter, move and leave) since the CSS classes may be added/removed at different points
11423       *   depending if CSS or JavaScript animations are used.
11424       *
11425       * @param {DOMElement} element the element which the CSS classes will be applied to
11426       * @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces)
11427       * @param {object=} options an optional collection of options/styles that will be applied to the element.
11428       *   The object can have the following properties:
11429       *
11430       *   - **addClass** - `{string}` - space-separated CSS classes to add to element
11431       *   - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
11432       *   - **removeClass** - `{string}` - space-separated CSS classes to remove from element
11433       *   - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
11434       *
11435       * @return {Promise} the animation callback promise
11436       */
11437      removeClass: function(element, className, options) {
11438        options = prepareAnimateOptions(options);
11439        options.removeClass = mergeClasses(options.removeClass, className);
11440        return $$animateQueue.push(element, 'removeClass', options);
11441      },
11442
11443      /**
11444       * @ngdoc method
11445       * @name $animate#setClass
11446       * @kind function
11447       *
11448       * @description Performs both the addition and removal of a CSS classes on an element and (during the process)
11449       *    triggers an animation surrounding the class addition/removal. Much like `$animate.addClass` and
11450       *    `$animate.removeClass`, `setClass` will only evaluate the classes being added/removed once a digest has
11451       *    passed. Note that class-based animations are treated differently compared to structural animations
11452       *    (like enter, move and leave) since the CSS classes may be added/removed at different points
11453       *    depending if CSS or JavaScript animations are used.
11454       *
11455       * @param {DOMElement} element the element which the CSS classes will be applied to
11456       * @param {string} add the CSS class(es) that will be added (multiple classes are separated via spaces)
11457       * @param {string} remove the CSS class(es) that will be removed (multiple classes are separated via spaces)
11458       * @param {object=} options an optional collection of options/styles that will be applied to the element.
11459       *   The object can have the following properties:
11460       *
11461       *   - **addClass** - `{string}` - space-separated CSS classes to add to element
11462       *   - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
11463       *   - **removeClass** - `{string}` - space-separated CSS classes to remove from element
11464       *   - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
11465       *
11466       * @return {Promise} the animation callback promise
11467       */
11468      setClass: function(element, add, remove, options) {
11469        options = prepareAnimateOptions(options);
11470        options.addClass = mergeClasses(options.addClass, add);
11471        options.removeClass = mergeClasses(options.removeClass, remove);
11472        return $$animateQueue.push(element, 'setClass', options);
11473      },
11474
11475      /**
11476       * @ngdoc method
11477       * @name $animate#animate
11478       * @kind function
11479       *
11480       * @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element.
11481       * If any detected CSS transition, keyframe or JavaScript matches the provided className value, then the animation will take
11482       * on the provided styles. For example, if a transition animation is set for the given className, then the provided `from` and
11483       * `to` styles will be applied alongside the given transition. If the CSS style provided in `from` does not have a corresponding
11484       * style in `to`, the style in `from` is applied immediately, and no animation is run.
11485       * If a JavaScript animation is detected then the provided styles will be given in as function parameters into the `animate`
11486       * method (or as part of the `options` parameter):
11487       *
11488       * ```js
11489       * ngModule.animation('.my-inline-animation', function() {
11490       *   return {
11491       *     animate : function(element, from, to, done, options) {
11492       *       //animation
11493       *       done();
11494       *     }
11495       *   }
11496       * });
11497       * ```
11498       *
11499       * @param {DOMElement} element the element which the CSS styles will be applied to
11500       * @param {object} from the from (starting) CSS styles that will be applied to the element and across the animation.
11501       * @param {object} to the to (destination) CSS styles that will be applied to the element and across the animation.
11502       * @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If
11503       *    this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element.
11504       *    (Note that if no animation is detected then this value will not be applied to the element.)
11505       * @param {object=} options an optional collection of options/styles that will be applied to the element.
11506       *   The object can have the following properties:
11507       *
11508       *   - **addClass** - `{string}` - space-separated CSS classes to add to element
11509       *   - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
11510       *   - **removeClass** - `{string}` - space-separated CSS classes to remove from element
11511       *   - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
11512       *
11513       * @return {Promise} the animation callback promise
11514       */
11515      animate: function(element, from, to, className, options) {
11516        options = prepareAnimateOptions(options);
11517        options.from = options.from ? extend(options.from, from) : from;
11518        options.to   = options.to   ? extend(options.to, to)     : to;
11519
11520        className = className || 'ng-inline-animate';
11521        options.tempClasses = mergeClasses(options.tempClasses, className);
11522        return $$animateQueue.push(element, 'animate', options);
11523      }
11524    };
11525  }];
11526}];
11527
11528var $$AnimateAsyncRunFactoryProvider = /** @this */ function() {
11529  this.$get = ['$$rAF', function($$rAF) {
11530    var waitQueue = [];
11531
11532    function waitForTick(fn) {
11533      waitQueue.push(fn);
11534      if (waitQueue.length > 1) return;
11535      $$rAF(function() {
11536        for (var i = 0; i < waitQueue.length; i++) {
11537          waitQueue[i]();
11538        }
11539        waitQueue = [];
11540      });
11541    }
11542
11543    return function() {
11544      var passed = false;
11545      waitForTick(function() {
11546        passed = true;
11547      });
11548      return function(callback) {
11549        if (passed) {
11550          callback();
11551        } else {
11552          waitForTick(callback);
11553        }
11554      };
11555    };
11556  }];
11557};
11558
11559var $$AnimateRunnerFactoryProvider = /** @this */ function() {
11560  this.$get = ['$q', '$sniffer', '$$animateAsyncRun', '$document', '$timeout',
11561       function($q,   $sniffer,   $$animateAsyncRun,   $document,   $timeout) {
11562
11563    var INITIAL_STATE = 0;
11564    var DONE_PENDING_STATE = 1;
11565    var DONE_COMPLETE_STATE = 2;
11566
11567    AnimateRunner.chain = function(chain, callback) {
11568      var index = 0;
11569
11570      next();
11571      function next() {
11572        if (index === chain.length) {
11573          callback(true);
11574          return;
11575        }
11576
11577        chain[index](function(response) {
11578          if (response === false) {
11579            callback(false);
11580            return;
11581          }
11582          index++;
11583          next();
11584        });
11585      }
11586    };
11587
11588    AnimateRunner.all = function(runners, callback) {
11589      var count = 0;
11590      var status = true;
vendor: 5,265 bytes, lines 11591-11807
11591      forEach(runners, function(runner) {
11592        runner.done(onProgress);
11593      });
11594
11595      function onProgress(response) {
11596        status = status && response;
11597        if (++count === runners.length) {
11598          callback(status);
11599        }
11600      }
11601    };
11602
11603    function AnimateRunner(host) {
11604      this.setHost(host);
11605
11606      var rafTick = $$animateAsyncRun();
11607      var timeoutTick = function(fn) {
11608        $timeout(fn, 0, false);
11609      };
11610
11611      this._doneCallbacks = [];
11612      this._tick = function(fn) {
11613        var doc = $document[0];
11614
11615        // the document may not be ready or attached
11616        // to the module for some internal tests
11617        if (doc && doc.hidden) {
11618          timeoutTick(fn);
11619        } else {
11620          rafTick(fn);
11621        }
11622      };
11623      this._state = 0;
11624    }
11625
11626    AnimateRunner.prototype = {
11627      setHost: function(host) {
11628        this.host = host || {};
11629      },
11630
11631      done: function(fn) {
11632        if (this._state === DONE_COMPLETE_STATE) {
11633          fn();
11634        } else {
11635          this._doneCallbacks.push(fn);
11636        }
11637      },
11638
11639      progress: noop,
11640
11641      getPromise: function() {
11642        if (!this.promise) {
11643          var self = this;
11644          this.promise = $q(function(resolve, reject) {
11645            self.done(function(status) {
11646              if (status === false) {
11647                reject();
11648              } else {
11649                resolve();
11650              }
11651            });
11652          });
11653        }
11654        return this.promise;
11655      },
11656
11657      then: function(resolveHandler, rejectHandler) {
11658        return this.getPromise().then(resolveHandler, rejectHandler);
11659      },
11660
11661      'catch': function(handler) {
11662        return this.getPromise()['catch'](handler);
11663      },
11664
11665      'finally': function(handler) {
11666        return this.getPromise()['finally'](handler);
11667      },
11668
11669      pause: function() {
11670        if (this.host.pause) {
11671          this.host.pause();
11672        }
11673      },
11674
11675      resume: function() {
11676        if (this.host.resume) {
11677          this.host.resume();
11678        }
11679      },
11680
11681      end: function() {
11682        if (this.host.end) {
11683          this.host.end();
11684        }
11685        this._resolve(true);
11686      },
11687
11688      cancel: function() {
11689        if (this.host.cancel) {
11690          this.host.cancel();
11691        }
11692        this._resolve(false);
11693      },
11694
11695      complete: function(response) {
11696        var self = this;
11697        if (self._state === INITIAL_STATE) {
11698          self._state = DONE_PENDING_STATE;
11699          self._tick(function() {
11700            self._resolve(response);
11701          });
11702        }
11703      },
11704
11705      _resolve: function(response) {
11706        if (this._state !== DONE_COMPLETE_STATE) {
11707          forEach(this._doneCallbacks, function(fn) {
11708            fn(response);
11709          });
11710          this._doneCallbacks.length = 0;
11711          this._state = DONE_COMPLETE_STATE;
11712        }
11713      }
11714    };
11715
11716    return AnimateRunner;
11717  }];
11718};
11719
11720/* exported $CoreAnimateCssProvider */
11721
11722/**
11723 * @ngdoc service
11724 * @name $animateCss
11725 * @kind object
11726 * @this
11727 *
11728 * @description
11729 * This is the core version of `$animateCss`. By default, only when the `ngAnimate` is included,
11730 * then the `$animateCss` service will actually perform animations.
11731 *
11732 * Click here {@link ngAnimate.$animateCss to read the documentation for $animateCss}.
11733 */
11734var $CoreAnimateCssProvider = function() {
11735  this.$get = ['$$rAF', '$q', '$$AnimateRunner', function($$rAF, $q, $$AnimateRunner) {
11736
11737    return function(element, initialOptions) {
11738      // all of the animation functions should create
11739      // a copy of the options data, however, if a
11740      // parent service has already created a copy then
11741      // we should stick to using that
11742      var options = initialOptions || {};
11743      if (!options.$$prepared) {
11744        options = copy(options);
11745      }
11746
11747      // there is no point in applying the styles since
11748      // there is no animation that goes on at all in
11749      // this version of $animateCss.
11750      if (options.cleanupStyles) {
11751        options.from = options.to = null;
11752      }
11753
11754      if (options.from) {
11755        element.css(options.from);
11756        options.from = null;
11757      }
11758
11759      var closed, runner = new $$AnimateRunner();
11760      return {
11761        start: run,
11762        end: run
11763      };
11764
11765      function run() {
11766        $$rAF(function() {
11767          applyAnimationContents();
11768          if (!closed) {
11769            runner.complete();
11770          }
11771          closed = true;
11772        });
11773        return runner;
11774      }
11775
11776      function applyAnimationContents() {
11777        if (options.addClass) {
11778          element.addClass(options.addClass);
11779          options.addClass = null;
11780        }
11781        if (options.removeClass) {
11782          element.removeClass(options.removeClass);
11783          options.removeClass = null;
11784        }
11785        if (options.to) {
11786          element.css(options.to);
11787          options.to = null;
11788        }
11789      }
11790    };
11791  }];
11792};
11793
11794/* global stripHash: true */
11795
11796/**
11797 * ! This is a private undocumented service !
11798 *
11799 * @name $browser
11800 * @requires $log
11801 * @description
11802 * This object has two goals:
11803 *
11804 * - hide all the global state in the browser caused by the window object
11805 * - abstract away all the browser specific features and inconsistencies
11806 *
11807 * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
11808 * service, which can be used for convenient testing of the application without the interaction with
11809 * the real browser apis.
11810 */
11811/**
11812 * @param {object} window The global window object.
11813 * @param {object} document jQuery wrapped document.
11814 * @param {object} $log window.console or an object with the same interface.
11815 * @param {object} $sniffer $sniffer service
11816 */
11817function Browser(window, document, $log, $sniffer) {
11818  var self = this,
11819      location = window.location,
11820      history = window.history,
11821      setTimeout = window.setTimeout,
11822      clearTimeout = window.clearTimeout,
11823      pendingDeferIds = {};
11824
11825  self.isMock = false;
11826
11827  var outstandingRequestCount = 0;
11828  var outstandingRequestCallbacks = [];
11829
11830  // TODO(vojta): remove this temporary api
11831  self.$$completeOutstandingRequest = completeOutstandingRequest;
11832  self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
11833
11834  /**
11835   * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
11836   * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
11837   */
11838  function completeOutstandingRequest(fn) {
11839    try {
11840      fn.apply(null, sliceArgs(arguments, 1));
11841    } finally {
11842      outstandingRequestCount--;
11843      if (outstandingRequestCount === 0) {
11844        while (outstandingRequestCallbacks.length) {
11845          try {
11846            outstandingRequestCallbacks.pop()();
11847          } catch (e) {
11848            $log.error(e);
11849          }
11850        }
11851      }
11852    }
11853  }
11854
11855  function getHash(url) {
11856    var index = url.indexOf('#');
11857    return index === -1 ? '' : url.substr(index);
11858  }
11859
11860  /**
11861   * @private
11862   * Note: this method is used only by scenario runner
11863   * TODO(vojta): prefix this method with $$ ?
11864   * @param {function()} callback Function that will be called when no outstanding request
11865   */
11866  self.notifyWhenNoOutstandingRequests = function(callback) {
11867    if (outstandingRequestCount === 0) {
11868      callback();
11869    } else {
11870      outstandingRequestCallbacks.push(callback);
11871    }
11872  };
11873
11874  //////////////////////////////////////////////////////////////
11875  // URL API
11876  //////////////////////////////////////////////////////////////
11877
11878  var cachedState, lastHistoryState,
11879      lastBrowserUrl = location.href,
11880      baseElement = document.find('base'),
11881      pendingLocation = null,
11882      getCurrentState = !$sniffer.history ? noop : function getCurrentState() {
11883        try {
11884          return history.state;
11885        } catch (e) {
11886          // MSIE can reportedly throw when there is no state (UNCONFIRMED).
11887        }
11888      };
11889
11890  cacheState();
11891  lastHistoryState = cachedState;
11892
11893  /**
11894   * @name $browser#url
11895   *
11896   * @description
11897   * GETTER:
11898   * Without any argument, this method just returns current value of location.href.
11899   *
11900   * SETTER:
11901   * With at least one argument, this method sets url to new value.
11902   * If html5 history api supported, pushState/replaceState is used, otherwise
11903   * location.href/location.replace is used.
11904   * Returns its own instance to allow chaining
11905   *
11906   * NOTE: this api is intended for use only by the $location service. Please use the
11907   * {@link ng.$location $location service} to change url.
11908   *
11909   * @param {string} url New url (when used as setter)
11910   * @param {boolean=} replace Should new url replace current history record?
11911   * @param {object=} state object to use with pushState/replaceState
11912   */
11913  self.url = function(url, replace, state) {
11914    // In modern browsers `history.state` is `null` by default; treating it separately
11915    // from `undefined` would cause `$browser.url('/foo')` to change `history.state`
11916    // to undefined via `pushState`. Instead, let's change `undefined` to `null` here.
11917    if (isUndefined(state)) {
11918      state = null;
11919    }
11920
11921    // Android Browser BFCache causes location, history reference to become stale.
11922    if (location !== window.location) location = window.location;
11923    if (history !== window.history) history = window.history;
11924
11925    // setter
11926    if (url) {
11927      var sameState = lastHistoryState === state;
11928
11929      // Don't change anything if previous and current URLs and states match. This also prevents
11930      // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
11931      // See https://github.com/angular/angular.js/commit/ffb2701
11932      if (lastBrowserUrl === url && (!$sniffer.history || sameState)) {
11933        return self;
11934      }
11935      var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
11936      lastBrowserUrl = url;
11937      lastHistoryState = state;
11938      // Don't use history API if only the hash changed
11939      // due to a bug in IE10/IE11 which leads
11940      // to not firing a `hashchange` nor `popstate` event
11941      // in some cases (see #9143).
11942      if ($sniffer.history && (!sameBase || !sameState)) {
11943        history[replace ? 'replaceState' : 'pushState'](state, '', url);
11944        cacheState();
11945        // Do the assignment again so that those two variables are referentially identical.
11946        lastHistoryState = cachedState;
11947      } else {
11948        if (!sameBase) {
11949          pendingLocation = url;
11950        }
11951        if (replace) {
11952          location.replace(url);
11953        } else if (!sameBase) {
11954          location.href = url;
11955        } else {
11956          location.hash = getHash(url);
11957        }
11958        if (location.href !== url) {
11959          pendingLocation = url;
11960        }
11961      }
11962      if (pendingLocation) {
11963        pendingLocation = url;
11964      }
11965      return self;
11966    // getter
11967    } else {
11968      // - pendingLocation is needed as browsers don't allow to read out
11969      //   the new location.href if a reload happened or if there is a bug like in iOS 9 (see
11970      //   https://openradar.appspot.com/22186109).
11971      // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
11972      return pendingLocation || location.href.replace(/%27/g,'\'');
11973    }
11974  };
11975
11976  /**
11977   * @name $browser#state
11978   *
11979   * @description
11980   * This method is a getter.
11981   *
11982   * Return history.state or null if history.state is undefined.
11983   *
11984   * @returns {object} state
11985   */
11986  self.state = function() {
11987    return cachedState;
11988  };
11989
11990  var urlChangeListeners = [],
11991      urlChangeInit = false;
11992
11993  function cacheStateAndFireUrlChange() {
11994    pendingLocation = null;
11995    cacheState();
11996    fireUrlChange();
11997  }
11998
11999  // This variable should be used *only* inside the cacheState function.
12000  var lastCachedState = null;
12001  function cacheState() {
12002    // This should be the only place in $browser where `history.state` is read.
12003    cachedState = getCurrentState();
12004    cachedState = isUndefined(cachedState) ? null : cachedState;
12005
12006    // Prevent callbacks fo fire twice if both hashchange & popstate were fired.
12007    if (equals(cachedState, lastCachedState)) {
12008      cachedState = lastCachedState;
12009    }
12010    lastCachedState = cachedState;
12011  }
12012
12013  function fireUrlChange() {
12014    if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) {
12015      return;
12016    }
12017
12018    lastBrowserUrl = self.url();
12019    lastHistoryState = cachedState;
12020    forEach(urlChangeListeners, function(listener) {
12021      listener(self.url(), cachedState);
12022    });
12023  }
12024
12025  /**
12026   * @name $browser#onUrlChange
12027   *
12028   * @description
12029   * Register callback function that will be called, when url changes.
12030   *
12031   * It's only called when the url is changed from outside of angular:
12032   * - user types different url into address bar
12033   * - user clicks on history (forward/back) button
12034   * - user clicks on a link
12035   *
12036   * It's not called when url is changed by $browser.url() method
12037   *
12038   * The listener gets called with new url as parameter.
12039   *
12040   * NOTE: this api is intended for use only by the $location service. Please use the
12041   * {@link ng.$location $location service} to monitor url changes in angular apps.
12042   *
12043   * @param {function(string)} listener Listener function to be called when url changes.
12044   * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
12045   */
12046  self.onUrlChange = function(callback) {
12047    // TODO(vojta): refactor to use node's syntax for events
12048    if (!urlChangeInit) {
12049      // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
12050      // don't fire popstate when user change the address bar and don't fire hashchange when url
12051      // changed by push/replaceState
12052
12053      // html5 history api - popstate event
12054      if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange);
12055      // hashchange event
12056      jqLite(window).on('hashchange', cacheStateAndFireUrlChange);
12057
12058      urlChangeInit = true;
12059    }
12060
12061    urlChangeListeners.push(callback);
12062    return callback;
12063  };
12064
12065  /**
12066   * @private
12067   * Remove popstate and hashchange handler from window.
12068   *
12069   * NOTE: this api is intended for use only by $rootScope.
12070   */
12071  self.$$applicationDestroyed = function() {
12072    jqLite(window).off('hashchange popstate', cacheStateAndFireUrlChange);
12073  };
12074
12075  /**
12076   * Checks whether the url has changed outside of Angular.
12077   * Needs to be exported to be able to check for changes that have been done in sync,
12078   * as hashchange/popstate events fire in async.
12079   */
12080  self.$$checkUrlChange = fireUrlChange;
12081
12082  //////////////////////////////////////////////////////////////
12083  // Misc API
12084  //////////////////////////////////////////////////////////////
12085
12086  /**
12087   * @name $browser#baseHref
12088   *
12089   * @description
12090   * Returns current <base href>
12091   * (always relative - without domain)
12092   *
12093   * @returns {string} The current base href
12094   */
12095  self.baseHref = function() {
12096    var href = baseElement.attr('href');
12097    return href ? href.replace(/^(https?:)?\/\/[^/]*/, '') : '';
12098  };
12099
12100  /**
12101   * @name $browser#defer
12102   * @param {function()} fn A function, who's execution should be deferred.
12103   * @param {number=} [delay=0] of milliseconds to defer the function execution.
12104   * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
12105   *
12106   * @description
12107   * Executes a fn asynchronously via `setTimeout(fn, delay)`.
12108   *
12109   * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
12110   * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
12111   * via `$browser.defer.flush()`.
12112   *
12113   */
12114  self.defer = function(fn, delay) {
12115    var timeoutId;
12116    outstandingRequestCount++;
12117    timeoutId = setTimeout(function() {
12118      delete pendingDeferIds[timeoutId];
12119      completeOutstandingRequest(fn);
12120    }, delay || 0);
12121    pendingDeferIds[timeoutId] = true;
12122    return timeoutId;
12123  };
12124
12125
12126  /**
12127   * @name $browser#defer.cancel
12128   *
12129   * @description
12130   * Cancels a deferred task identified with `deferId`.
12131   *
12132   * @param {*} deferId Token returned by the `$browser.defer` function.
12133   * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
12134   *                    canceled.
12135   */
12136  self.defer.cancel = function(deferId) {
12137    if (pendingDeferIds[deferId]) {
12138      delete pendingDeferIds[deferId];
12139      clearTimeout(deferId);
12140      completeOutstandingRequest(noop);
12141      return true;
12142    }
12143    return false;
12144  };
12145
12146}
12147
12148/** @this */
12149function $BrowserProvider() {
12150  this.$get = ['$window', '$log', '$sniffer', '$document',
12151      function($window, $log, $sniffer, $document) {
12152        return new Browser($window, $document, $log, $sniffer);
12153      }];
12154}
12155
12156/**
12157 * @ngdoc service
12158 * @name $cacheFactory
12159 * @this
12160 *
12161 * @description
12162 * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
12163 * them.
12164 *
12165 * ```js
12166 *
12167 *  var cache = $cacheFactory('cacheId');
12168 *  expect($cacheFactory.get('cacheId')).toBe(cache);
12169 *  expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
12170 *
12171 *  cache.put("key", "value");
12172 *  cache.put("another key", "another value");
12173 *
12174 *  // We've specified no options on creation
12175 *  expect(cache.info()).toEqual({id: 'cacheId', size: 2});
12176 *
12177 * ```
12178 *
12179 *
12180 * @param {string} cacheId Name or id of the newly created cache.
12181 * @param {object=} options Options object that specifies the cache behavior. Properties:
12182 *
12183 *   - `{number=}` `capacity` — turns the cache into LRU cache.
12184 *
12185 * @returns {object} Newly created cache object with the following set of methods:
12186 *
12187 * - `{object}` `info()` — Returns id, size, and options of cache.
12188 * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns
12189 *   it.
12190 * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
12191 * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
12192 * - `{void}` `removeAll()` — Removes all cached values.
12193 * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
12194 *
12195 * @example
12196   <example module="cacheExampleApp" name="cache-factory">
12197     <file name="index.html">
12198       <div ng-controller="CacheController">
12199         <input ng-model="newCacheKey" placeholder="Key">
12200         <input ng-model="newCacheValue" placeholder="Value">
12201         <button ng-click="put(newCacheKey, newCacheValue)">Cache</button>
12202
12203         <p ng-if="keys.length">Cached Values</p>
12204         <div ng-repeat="key in keys">
12205           <span ng-bind="key"></span>
12206           <span>: </span>
12207           <b ng-bind="cache.get(key)"></b>
12208         </div>
12209
12210         <p>Cache Info</p>
12211         <div ng-repeat="(key, value) in cache.info()">
12212           <span ng-bind="key"></span>
12213           <span>: </span>
12214           <b ng-bind="value"></b>
12215         </div>
12216       </div>
12217     </file>
12218     <file name="script.js">
12219       angular.module('cacheExampleApp', []).
12220         controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {
12221           $scope.keys = [];
12222           $scope.cache = $cacheFactory('cacheId');
12223           $scope.put = function(key, value) {
12224             if (angular.isUndefined($scope.cache.get(key))) {
12225               $scope.keys.push(key);
12226             }
12227             $scope.cache.put(key, angular.isUndefined(value) ? null : value);
12228           };
12229         }]);
12230     </file>
12231     <file name="style.css">
12232       p {
12233         margin: 10px 0 3px;
12234       }
12235     </file>
12236   </example>
12237 */
12238function $CacheFactoryProvider() {
12239
12240  this.$get = function() {
12241    var caches = {};
12242
12243    function cacheFactory(cacheId, options) {
12244      if (cacheId in caches) {
12245        throw minErr('$cacheFactory')('iid', 'CacheId \'{0}\' is already taken!', cacheId);
12246      }
12247
12248      var size = 0,
12249          stats = extend({}, options, {id: cacheId}),
12250          data = createMap(),
12251          capacity = (options && options.capacity) || Number.MAX_VALUE,
12252          lruHash = createMap(),
12253          freshEnd = null,
12254          staleEnd = null;
12255
12256      /**
12257       * @ngdoc type
12258       * @name $cacheFactory.Cache
12259       *
12260       * @description
12261       * A cache object used to store and retrieve data, primarily used by
12262       * {@link $http $http} and the {@link ng.directive:script script} directive to cache
12263       * templates and other data.
12264       *
12265       * ```js
12266       *  angular.module('superCache')
12267       *    .factory('superCache', ['$cacheFactory', function($cacheFactory) {
12268       *      return $cacheFactory('super-cache');
12269       *    }]);
12270       * ```
12271       *
12272       * Example test:
12273       *
12274       * ```js
12275       *  it('should behave like a cache', inject(function(superCache) {
12276       *    superCache.put('key', 'value');
12277       *    superCache.put('another key', 'another value');
12278       *
12279       *    expect(superCache.info()).toEqual({
12280       *      id: 'super-cache',
12281       *      size: 2
12282       *    });
12283       *
12284       *    superCache.remove('another key');
12285       *    expect(superCache.get('another key')).toBeUndefined();
12286       *
12287       *    superCache.removeAll();
12288       *    expect(superCache.info()).toEqual({
12289       *      id: 'super-cache',
12290       *      size: 0
12291       *    });
12292       *  }));
12293       * ```
12294       */
12295      return (caches[cacheId] = {
12296
12297        /**
12298         * @ngdoc method
12299         * @name $cacheFactory.Cache#put
12300         * @kind function
12301         *
12302         * @description
12303         * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be
12304         * retrieved later, and incrementing the size of the cache if the key was not already
12305         * present in the cache. If behaving like an LRU cache, it will also remove stale
12306         * entries from the set.
12307         *
12308         * It will not insert undefined values into the cache.
12309         *
12310         * @param {string} key the key under which the cached data is stored.
12311         * @param {*} value the value to store alongside the key. If it is undefined, the key
12312         *    will not be stored.
12313         * @returns {*} the value stored.
12314         */
12315        put: function(key, value) {
12316          if (isUndefined(value)) return;
12317          if (capacity < Number.MAX_VALUE) {
12318            var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
12319
12320            refresh(lruEntry);
12321          }
12322
12323          if (!(key in data)) size++;
12324          data[key] = value;
12325
12326          if (size > capacity) {
12327            this.remove(staleEnd.key);
12328          }
12329
12330          return value;
12331        },
12332
12333        /**
12334         * @ngdoc method
12335         * @name $cacheFactory.Cache#get
12336         * @kind function
12337         *
12338         * @description
12339         * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.
12340         *
12341         * @param {string} key the key of the data to be retrieved
12342         * @returns {*} the value stored.
12343         */
12344        get: function(key) {
12345          if (capacity < Number.MAX_VALUE) {
12346            var lruEntry = lruHash[key];
12347
12348            if (!lruEntry) return;
12349
12350            refresh(lruEntry);
12351          }
12352
12353          return data[key];
12354        },
12355
12356
12357        /**
12358         * @ngdoc method
12359         * @name $cacheFactory.Cache#remove
12360         * @kind function
12361         *
12362         * @description
12363         * Removes an entry from the {@link $cacheFactory.Cache Cache} object.
12364         *
12365         * @param {string} key the key of the entry to be removed
12366         */
12367        remove: function(key) {
12368          if (capacity < Number.MAX_VALUE) {
12369            var lruEntry = lruHash[key];
12370
12371            if (!lruEntry) return;
12372
12373            if (lruEntry === freshEnd) freshEnd = lruEntry.p;
12374            if (lruEntry === staleEnd) staleEnd = lruEntry.n;
12375            link(lruEntry.n,lruEntry.p);
12376
12377            delete lruHash[key];
12378          }
12379
12380          if (!(key in data)) return;
12381
12382          delete data[key];
12383          size--;
12384        },
12385
12386
12387        /**
12388         * @ngdoc method
12389         * @name $cacheFactory.Cache#removeAll
12390         * @kind function
12391         *
12392         * @description
12393         * Clears the cache object of any entries.
12394         */
12395        removeAll: function() {
12396          data = createMap();
12397          size = 0;
12398          lruHash = createMap();
12399          freshEnd = staleEnd = null;
12400        },
12401
12402
12403        /**
12404         * @ngdoc method
12405         * @name $cacheFactory.Cache#destroy
12406         * @kind function
12407         *
vendor: 4,055 bytes, lines 12408-12553
12408         * @description
12409         * Destroys the {@link $cacheFactory.Cache Cache} object entirely,
12410         * removing it from the {@link $cacheFactory $cacheFactory} set.
12411         */
12412        destroy: function() {
12413          data = null;
12414          stats = null;
12415          lruHash = null;
12416          delete caches[cacheId];
12417        },
12418
12419
12420        /**
12421         * @ngdoc method
12422         * @name $cacheFactory.Cache#info
12423         * @kind function
12424         *
12425         * @description
12426         * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.
12427         *
12428         * @returns {object} an object with the following properties:
12429         *   <ul>
12430         *     <li>**id**: the id of the cache instance</li>
12431         *     <li>**size**: the number of entries kept in the cache instance</li>
12432         *     <li>**...**: any additional properties from the options object when creating the
12433         *       cache.</li>
12434         *   </ul>
12435         */
12436        info: function() {
12437          return extend({}, stats, {size: size});
12438        }
12439      });
12440
12441
12442      /**
12443       * makes the `entry` the freshEnd of the LRU linked list
12444       */
12445      function refresh(entry) {
12446        if (entry !== freshEnd) {
12447          if (!staleEnd) {
12448            staleEnd = entry;
12449          } else if (staleEnd === entry) {
12450            staleEnd = entry.n;
12451          }
12452
12453          link(entry.n, entry.p);
12454          link(entry, freshEnd);
12455          freshEnd = entry;
12456          freshEnd.n = null;
12457        }
12458      }
12459
12460
12461      /**
12462       * bidirectionally links two entries of the LRU linked list
12463       */
12464      function link(nextEntry, prevEntry) {
12465        if (nextEntry !== prevEntry) {
12466          if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
12467          if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
12468        }
12469      }
12470    }
12471
12472
12473  /**
12474   * @ngdoc method
12475   * @name $cacheFactory#info
12476   *
12477   * @description
12478   * Get information about all the caches that have been created
12479   *
12480   * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
12481   */
12482    cacheFactory.info = function() {
12483      var info = {};
12484      forEach(caches, function(cache, cacheId) {
12485        info[cacheId] = cache.info();
12486      });
12487      return info;
12488    };
12489
12490
12491  /**
12492   * @ngdoc method
12493   * @name $cacheFactory#get
12494   *
12495   * @description
12496   * Get access to a cache object by the `cacheId` used when it was created.
12497   *
12498   * @param {string} cacheId Name or id of a cache to access.
12499   * @returns {object} Cache object identified by the cacheId or undefined if no such cache.
12500   */
12501    cacheFactory.get = function(cacheId) {
12502      return caches[cacheId];
12503    };
12504
12505
12506    return cacheFactory;
12507  };
12508}
12509
12510/**
12511 * @ngdoc service
12512 * @name $templateCache
12513 * @this
12514 *
12515 * @description
12516 * The first time a template is used, it is loaded in the template cache for quick retrieval. You
12517 * can load templates directly into the cache in a `script` tag, or by consuming the
12518 * `$templateCache` service directly.
12519 *
12520 * Adding via the `script` tag:
12521 *
12522 * ```html
12523 *   <script type="text/ng-template" id="templateId.html">
12524 *     <p>This is the content of the template</p>
12525 *   </script>
12526 * ```
12527 *
12528 * **Note:** the `script` tag containing the template does not need to be included in the `head` of
12529 * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE,
12530 * element with ng-app attribute), otherwise the template will be ignored.
12531 *
12532 * Adding via the `$templateCache` service:
12533 *
12534 * ```js
12535 * var myApp = angular.module('myApp', []);
12536 * myApp.run(function($templateCache) {
12537 *   $templateCache.put('templateId.html', 'This is the content of the template');
12538 * });
12539 * ```
12540 *
12541 * To retrieve the template later, simply use it in your component:
12542 * ```js
12543 * myApp.component('myComponent', {
12544 *    templateUrl: 'templateId.html'
12545 * });
12546 * ```
12547 *
12548 * or get it via the `$templateCache` service:
12549 * ```js
12550 * $templateCache.get('templateId.html')
12551 * ```
12552 *
12553 * See {@link ng.$cacheFactory $cacheFactory}.
vendor: 5,589 bytes, lines 12553-12682
12553
12554 *
12555 */
12556function $TemplateCacheProvider() {
12557  this.$get = ['$cacheFactory', function($cacheFactory) {
12558    return $cacheFactory('templates');
12559  }];
12560}
12561
12562/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
12563 *     Any commits to this file should be reviewed with security in mind.  *
12564 *   Changes to this file can potentially create security vulnerabilities. *
12565 *          An approval from 2 Core members with history of modifying      *
12566 *                         this file is required.                          *
12567 *                                                                         *
12568 *  Does the change somehow allow for arbitrary javascript to be executed? *
12569 *    Or allows for someone to change the prototype of built-in objects?   *
12570 *     Or gives undesired access to variables like document or window?    *
12571 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
12572
12573/* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
12574 *
12575 * DOM-related variables:
12576 *
12577 * - "node" - DOM Node
12578 * - "element" - DOM Element or Node
12579 * - "$node" or "$element" - jqLite-wrapped node or element
12580 *
12581 *
12582 * Compiler related stuff:
12583 *
12584 * - "linkFn" - linking fn of a single directive
12585 * - "nodeLinkFn" - function that aggregates all linking fns for a particular node
12586 * - "childLinkFn" -  function that aggregates all linking fns for child nodes of a particular node
12587 * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
12588 */
12589
12590
12591/**
12592 * @ngdoc service
12593 * @name $compile
12594 * @kind function
12595 *
12596 * @description
12597 * Compiles an HTML string or DOM into a template and produces a template function, which
12598 * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
12599 *
12600 * The compilation is a process of walking the DOM tree and matching DOM elements to
12601 * {@link ng.$compileProvider#directive directives}.
12602 *
12603 * <div class="alert alert-warning">
12604 * **Note:** This document is an in-depth reference of all directive options.
12605 * For a gentle introduction to directives with examples of common use cases,
12606 * see the {@link guide/directive directive guide}.
12607 * </div>
12608 *
12609 * ## Comprehensive Directive API
12610 *
12611 * There are many different options for a directive.
12612 *
12613 * The difference resides in the return value of the factory function.
12614 * You can either return a {@link $compile#directive-definition-object Directive Definition Object (see below)}
12615 * that defines the directive properties, or just the `postLink` function (all other properties will have
12616 * the default values).
12617 *
12618 * <div class="alert alert-success">
12619 * **Best Practice:** It's recommended to use the "directive definition object" form.
12620 * </div>
12621 *
12622 * Here's an example directive declared with a Directive Definition Object:
12623 *
12624 * ```js
12625 *   var myModule = angular.module(...);
12626 *
12627 *   myModule.directive('directiveName', function factory(injectables) {
12628 *     var directiveDefinitionObject = {
12629 *       {@link $compile#-priority- priority}: 0,
12630 *       {@link $compile#-template- template}: '<div></div>', // or // function(tElement, tAttrs) { ... },
12631 *       // or
12632 *       // {@link $compile#-templateurl- templateUrl}: 'directive.html', // or // function(tElement, tAttrs) { ... },
12633 *       {@link $compile#-transclude- transclude}: false,
12634 *       {@link $compile#-restrict- restrict}: 'A',
12635 *       {@link $compile#-templatenamespace- templateNamespace}: 'html',
12636 *       {@link $compile#-scope- scope}: false,
12637 *       {@link $compile#-controller- controller}: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
12638 *       {@link $compile#-controlleras- controllerAs}: 'stringIdentifier',
12639 *       {@link $compile#-bindtocontroller- bindToController}: false,
12640 *       {@link $compile#-require- require}: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
12641 *       {@link $compile#-multielement- multiElement}: false,
12642 *       {@link $compile#-compile- compile}: function compile(tElement, tAttrs, transclude) {
12643 *         return {
12644 *            {@link $compile#pre-linking-function pre}: function preLink(scope, iElement, iAttrs, controller) { ... },
12645 *            {@link $compile#post-linking-function post}: function postLink(scope, iElement, iAttrs, controller) { ... }
12646 *         }
12647 *         // or
12648 *         // return function postLink( ... ) { ... }
12649 *       },
12650 *       // or
12651 *       // {@link $compile#-link- link}: {
12652 *       //  {@link $compile#pre-linking-function pre}: function preLink(scope, iElement, iAttrs, controller) { ... },
12653 *       //  {@link $compile#post-linking-function post}: function postLink(scope, iElement, iAttrs, controller) { ... }
12654 *       // }
12655 *       // or
12656 *       // {@link $compile#-link- link}: function postLink( ... ) { ... }
12657 *     };
12658 *     return directiveDefinitionObject;
12659 *   });
12660 * ```
12661 *
12662 * <div class="alert alert-warning">
12663 * **Note:** Any unspecified options will use the default value. You can see the default values below.
12664 * </div>
12665 *
12666 * Therefore the above can be simplified as:
12667 *
12668 * ```js
12669 *   var myModule = angular.module(...);
12670 *
12671 *   myModule.directive('directiveName', function factory(injectables) {
12672 *     var directiveDefinitionObject = {
12673 *       link: function postLink(scope, iElement, iAttrs) { ... }
12674 *     };
12675 *     return directiveDefinitionObject;
12676 *     // or
12677 *     // return function postLink(scope, iElement, iAttrs) { ... }
12678 *   });
12679 * ```
12680 *
12681 * ### Life-cycle hooks
12682 * Directive controllers can provide the following methods that are called by Angular
12682 at points in the life-cycle of the
12683 * directive:
12684 * * `$onInit()` - Called on each controller after all the controllers on an element have been constructed and
12685 *   had their bindings initialized (and before the pre &amp; post linking functions for the directives on
12686 *   this element). This is a good place to put initialization code for your controller.
12687 * * `$onChanges(changesObj)` - Called whenever one-way (`<`) or interpolation (`@`) bindings are updated. The
12688 *   `changesObj` is a hash whose keys are the names of the bound properties that have changed, and the values are an
12689 *   object of the form `{ currentValue, previousValue, isFirstChange() }`. Use this hook to trigger updates within a
12690 *   component such as cloning the bound value to prevent accidental mutation of the outer value.
12691 * * `$doCheck()` - Called on each turn of the digest cycle. Provides an opportunity to detect and act on
12692 *   changes. Any actions that you wish to take in response to the changes that you detect must be
12693 *   invoked from this hook; implementing this has no effect on when `$onChanges` is called. For example, this hook
12694 *   could be useful if you wish to perform a deep equality check, or to check a Date object, changes to which w
12694ould not
12695 *   be detected by Angular's change detector and thus not trigger `$onChanges`. This hook is invoked with no arguments;
12696 *   if detecting changes, you must store the previous value(s) for comparison to the current values.
12697 * * `$onDestroy()` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
12698 *   external resources, watches and event handlers. Note that components have their `$onDestroy()` hooks called in
12699 *   the same order as the `$scope.$broadcast` events are triggered, which is top down. This means that parent
12700 *   components will have their `$onDestroy()` hook called before child components.
12701 * * `$postLink()` - Called after this controller's element and its children have been linked. Similar to the post-link
12702 *   function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
12703 *   Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
12704 *   they are waiting for their template to load asynchronously and their own compilation and linking has been
12705 *   suspended until that occurs.
12706 *
12707 * #### Comparison with Angular 2 life-cycle hooks
12708 * Angular 2 also uses life-cycle hooks for its components. While the Angular 1 life-cycle hooks are similar there are
12709 * some differences that you should be aware of, especially when it comes to moving your co
12709de from Angular 1 to Angular 2:
12710 *
12711 * * Angular 1 hooks are prefixed with `$`, such as `$onInit`. Angular 2 hooks are prefixed with `ng`, such as `ngOnInit`.
12712 * * Angular 1 hooks can be defined on the controller prototype or added to the controller inside its constructor.
12713 *   In Angular 2 you can only define hooks on the prototype of the Component class.
12714 * * Due to the differences in change-detection, you may get many more calls to `$doCheck` in Angular 1 than you would to
12715 *   `ngDoCheck` in Angular 2
12716 * * Changes to the model inside `$doCheck` will trigger new turns of the digest loop, which will cause the changes to be
12717 *   propagated throughout the application.
12718 *   Angular 2 does not allow the `ngDoCheck` hook to trigger a change outside of the component. It will either throw an
12719 *   error or do nothing depending upon the state of `enableProdMode()`.
12720 *
12721 * #### Life-cycle hook examples
12722 *
12723 * This example shows how you can check for mutations to a Date object even though the identity of the object
12724 * has not changed.
12725 *
12726 * <example name="doCheckDateExample" module="do-check-module">
12727 *   <file name="app.js">
12728 *     angular.module('do-check-module', [])
12729 *       .component('app', {
12730 *         template:
12731 *           'Month: <input ng-model="$ctrl.month" ng-change="$ctrl.updateDate()">' +
12732 *           'Date: {{ $ctrl.date }}' +
12733 *           '<test date="$ctrl.date"></test>',
12734 *         controller: function() {
12735 *           this.date = new Date();
12736 *           this.month = this.date.getMonth();
12737 *           this.updateDate = function() {
12738 *             this.date.setMonth(this.month);
12739 *           };
12740 *         }
12741 *       })
12742 *       .component('test', {
12743 *         bindings: { date: '<' },
12744 *         template:
12745 *           '<pre>{{ $ctrl.log | json }}</pre>',
12746 *         controller: function() {
12747 *           var previousValue;
12748 *           this.log = [];
12749 *           this.$doCheck = function() {
12750 *             var currentValue = this.date && this.date.valueOf();
12751 *             if (previousValue !== currentValue) {
12752 *               this.log.push('doCheck: date mutated: ' + this.date);
12753 *               previousValue = currentValue;
12754 *             }
12755 *           };
12756 *         }
12757 *       });
12758 *   </file>
12759 *   <file name="index.html">
12760 *     <app></app>
12761 *   </file>
12762 * </example>
12763 *
12764 * This example show how you might use `$doCheck` to trigger changes in your component's input
12764s even if the
12765 * actual identity of the component doesn't change. (Be aware that cloning and deep equality checks on large
12766 * arrays or objects can have a negative impact on your application performance)
12767 *
12768 * <example name="doCheckArrayExample" module="do-check-module">
12769 *   <file name="index.html">
12770 *     <div ng-init="items = []">
12771 *       <button ng-click="items.push(items.length)">Add Item</button>
12772 *       <button ng-click="items = []">Reset Items</button>
12773 *       <pre>{{ items }}</pre>
12774 *       <test items="items"></test>
12775 *     </div>
12776 *   </file>
12777 *   <file name="app.js">
12778 *      angular.module('do-check-module', [])
12779 *        .component('test', {
12780 *          bindings: { items: '<' },
12781 *          template:
12782 *            '<pre>{{ $ctrl.log | json }}</pre>',
12783 *          controller: function() {
12784 *            this.log = [];
12785 *
12786 *            this.$doCheck = function() {
12787 *              if (this.items_ref !== this.items) {
12788 *                this.log.push('doCheck: items changed');
12789 *                this.items_ref = this.items;
12790 *              }
12791 *              if (!angular.equals(this.items_clone, this.items)) {
12792 *                this.log.push('doCheck: items mutated');
12793 *                this.items_clone = angular.copy(this.items);
12794 *              }
12795 *            };
12796 *          }
12797 *        });
12798 *   </file>
12799 * </example>
12800 *
12801 *
12802 * ### Directive Definition Object
12803 *
12804 * The directive definition object provides instructions to the {@link ng.$compile
12805 * compiler}. The attributes are:
12806 *
12807 * #### `multiElement`
12808 * When this property is set to true (default is `false`), the HTML compiler will collect DOM nodes between
12809 * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
12810 * together as the directive elements. It is recommended that this feature be used on directives
12811 * which are not strictly behavioral (such as {@link ngClick}), and which
12812 * do not manipulate or replace child nodes (such as {@link ngInclude}).
12813 *
12814 * #### `priority`
12815 * When there are multiple directives defined on a single DOM element, sometimes it
12816 * is necessary to specify the order in which the directives are applied. The `priority` is used
12817 * to sort the directives before their `compile` functions get called. Priority is defined as a
12818 * number. Directives with greater numerical `priority` are compiled first. Pre-link functions
12819 * are also run in priority order, but post-link functions are run in reverse order. The order
12820 * of directives with the same priority is undefined. The default priority is `0`.
12821 *
12822 * #### `terminal`
12823 * If set to true then the current `priority` will be the last set of directives
12824 * which will execute (any directives at the current priority will still execute
12825 * as the order of execution on same `priority` is undefined). Note that expressions
12826 * and other directives used in the directive's template will also be excluded from execution.
12827 *
12828 * #### `scope`
12829 * The scope property can be `false`, `true`, or an object:
12830 *
12831 * * **`false` (default):** No scope will be created for the directive. The directive will use its
12832 * parent's scope.
12833 *
12834 * * **`true`:** A new child scope that prototypically inherits from its parent will be created for
12835 * the directive's element. If multiple directives on the same element request a new scope,
12836 * only one new scope is created.
12837 *
12838 * * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's element. The
12839 * 'isolate' scope differs from normal scope in that it does not prototypically inherit from its parent
12840 * scope. This is useful when creating reusable components, which should not accidentally read or modify
12841 * data in the parent scope.
12842 *
12843 * The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the
12844 * directive's element. These local properties are useful for aliasing values for templates. The keys in
12845 * the object hash map to the name of the property on the isolate scope; the values define how the property
12846 * is bound to the parent scope, via matching attributes on the directive's element:
12847 *
12848 * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
12849 *   always a string since DOM attributes are strings. If no `attr` name is specified then the
12850 *   attribute name is assumed to be the same as the local name. Given `<my-component
12851 *   my-attr="hello {{name}}">` and the isolate scope definition `scope: { localName:'@myAttr' }`,
12852 *   the directive's scope property `localName` will reflect the interpolated value of `hello
12853 *   {{name}}`. As the `name` attribute changes so will the `localName` property on the directive's
12854 *   scope. The `name` is read from the parent scope (not the directive's scope).
12855 *
12856 * * `=` or `=attr` - set up a bidirectional binding between a local scope property and an expression
12857 *   passed via the attribute `attr`. The expression is evaluated in the context of the parent scope.
12858 *   If no `attr` name is specified then the attribute name is assumed to be the same as the local
12859 *   name. Given `<my-component my-attr="parentModel">` and the isolate scope definition `scope: {
12860 *   localModel: '=myAttr' }`, the property `localModel` on the directive's scope will reflect the
12861 *   value of `parentModel` on the parent scope. Changes to `parentModel` will be reflected in
12862 *   `localModel` and vice versa. Optional attributes should be marked as such with a question mark:
12863 *   `=?` or `=?attr`. If the binding expression is non-assignable, or if the attribute isn't
12864 *   optional and doesn't exist, an exception ({@link error/$compile/nonassign `$compile:nonassign`})
12865 *   will be thrown upon discovering changes to the local value, since it will be impossible to sync
12866 *   them back to the parent scope. By default, the {@link ng.$rootScope.Scope#$watch `$watch`}
12867 *   method is used for tracking changes, and the equality check is based on object identity.
12868 *   However, if an object literal or an array literal is passed as the binding expression, the
12869 *   equality check is done by value (using the {@link angular.equals} function). It's also possible
12870 *   to watch the evaluated value shallowly with {@link ng.$rootScope.Scope#$watchCollection
12871 *   `$watchCollection`}: use `=*` or `=*attr` (`=*?` or `=*?attr` if the attribute is optional).
12872 *
12873  * * `<` or `<attr` - set up a one-way (one-directional) binding between a local scope property and an
12874 *   expression passed via the attribute `attr`. The expression is evaluated in the context of the
12875 *   parent scope. If no `attr` name is specified then the attribute name is assumed to be the same as the
12876 *   local name. You can also make the binding optional by adding `?`: `<?` or `<?attr`.
12877 *
12878 *   For example, given `<my-component my-attr="parentModel">` and directive definition of
12879 *   `scope: { localModel:'<myAttr' }`, then the isolated scope property `localModel` will reflect the
12880 *   value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
12881 *   in `localModel`, but changes in `localModel` will not reflect in `parentModel`. There are however
12882 *   two caveats:
12883 *     1. one-way binding does not copy the value from the parent to the isolate scope, it simply
12884 *     sets the same value. That means if your bound value is an object, changes to its properties
12885 *     in the isolated scope will be reflected in the parent scope (because both reference the same object).
12886 *     2. one-way binding watches changes to the **identity** of the parent value. That means the
12887 *     {@link ng.$rootScope.Scope#$watch `$watch`} on the parent value only fires if the reference
12888 *     to the value has changed. In most cases, this should not be of concern, but can be important
12889 *     to know if you one-way bind to an object, and then replace that object in the isolated scope.
12890 *     If you now change a property of the object in your parent scope, the change will not be
12891 *     propagated to the isolated scope, because the identity of the object on the parent scope
12892 *     has not changed. Instead you must assign a new object.
12893 *
12894 *   One-way binding is useful if you do not plan to propagate changes to your isolated scope bindings
12895 *   back to the parent. However, it does not make this completely impossible.
12896 *
12897 * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope. If
12898 *   no `attr` name is specified then the attribute name is assumed to be the same as the local name.
12899 *   Given `<my-component my-attr="count = count + value">` and the isolate scope definition `scope: {
12900 *   localFn:'&myAttr' }`, the isolate scope property `localFn` will point to a function wrapper for
12901 *   the `count = count + value` expression. Often it's desirable to pass data from the isolated scope
12902 *   via an expression to the parent scope. This can be done by passing a map of local variable names
12903 *   and values into the expression wrapper fn. For example, if the expression is `increment(amount)`
12904 *   then we can specify the amount value by calling the `localFn` as `localFn({amount: 22})`.
12905 *
12906 * In general it's possible to apply more than one directive to one element, but there might be limitations
12907 * depending on the type of scope required by the directives. The following points will help explain these limitations.
12908 * For simplicity only two directives are taken into account, but it is also applicable for several directives:
12909 *
12910 * * **no scope** + **no scope** => Two directives which don't require their own scope will use their parent's scope
12911 * * **child scope** + **no scope** =>  Both directives will share one single child scope
12912 * * **child scope** + **child scope** =>  Both directives will share one single child scope
12913 * * **isolated scope** + **no scope** =>  The isolated directive will use it's own created isolated scope. The other directive will use
12914 * its parent's scope
12915 * * **isolated scope** + **child scope** =>  **Won't work!** Only one scope can be related to one element. Therefore these directives cannot
12916 * be applied to the same element.
12917 * * **isolated scope** + **isolated scope**  =>  **Won't work!** Only one scope can be related to one element. Therefore these directives
12918 * cannot be applied to the same element.
12919 *
12920 *
12921 * #### `bindToController`
12922 * This property is used to bind scope properties directly to the controller. It can be either
12923 * `true` or an object hash with the same format as the `scope` property.
12924 *
12925 * When an isolate scope is used for a directive (see above), `bindToController: true` will
12926 * allow a component to have its properties bound to the controller, rather than to scope.
12927 *
12928 * After the controller is instantiated, the initial values of the isolate scope bindings will be bound to the controller
12929 * properties. You can access these bindings once they have been initialized by providing a controller method called
12930 * `$onInit`, which is called after all the controllers on an element have been constructed and had their bindings
12931 * initialized.
12932 *
12933 * <div class="alert alert-warning">
12934 * **Deprecation warning:** although bindings for non-ES6 class controllers are currently
12935 * bound to `this` before the controller constructor is called, this use is now deprecated. Please place initialization
12936 * code that relies upon bindings inside a `$onInit` method on the controller, instead.
12937 * </div>
12938 *
12939 * It is also possible to set `bindToController` to an object hash with the same format as the `scope` property.
12940 * This will set up the scope bindings to the controller directly. Note that `scope` can still be used
12941 * to define which kind of scope is created. By default, no scope is created. Use `scope: {}` to create an isolate
12942 * scope (useful for component directives).
12943 *
12944 * If both `bindToController` and `scope` are defined and have object hashes, `bindToController` overrides `scope`.
12945 *
12946 *
12947 * #### `controller`
12948 * Controller constructor function. The controller is instantiated before the
12949 * pre-linking phase and can be accessed by other directives (see
12950 * `require` attribute). This allows the directives to communicate with each other and augment
12951 * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
12952 *
12953 * * `$scope` - Current scope associated with the element
12954 * * `$element` - Current element
12955 * * `$attrs` - Current attributes object for the element
12956 * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
12957 *   `function([scope], cloneLinkingFn, futureParentElement, slotName)`:
12958 *    * `scope`: (optional) override the scope.
12959 *    * `cloneLinkingFn`: (optional) argument to create clones of the original transcluded content.
12960 *    * `futureParentElement` (optional):
12961 *        * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
12962 *        * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
12963 *        * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
12964 *          and when the `cloneLinkingFn` is passed,
12965 *          as those elements need to created and cloned in a special way when they are defined outside their
12966 *          usual containers (e.g. like `<svg>`).
12967 *        * See also the `directive.templateNamespace` property.
12968 *    * `slotName`: (optional) the name of the slot to transclude. If falsy (e.g. `null`, `undefined` or `''`)
12969 *      then the default transclusion is provided.
12970 *    The `$transclude` function also has a method on it, `$transclude.isSlotFilled(slotName)`, which returns
12971 *    `true` if the specified slot contains content (i.e. one or more DOM nodes).
12972 *
12973 * #### `require`
12974 * Require another directive and inject its controller as the fourth argument to the linking function. The
12975 * `require` property can be a string, an array or an object:
12976 * * a **string** containing the name of the directive to pass to the linking function
12977 * * an **array** containing the names of directives to pass to the linking function. The argument passed to the
12978 * linking function will be an array of controllers in the same order as the names in the `require` property
12979 * * an **object** whose property values are the names of the directives to pass to the linking function. The argument
12980 * passed to the linking function will also be an object with matching keys, whose values will hold the corresponding
12981 * controllers.
12982 *
12983 * If the `require` property is an object and `bindToController` is truthy, then the required controllers are
12984 * bound to the controller using the keys of the `require` property. This binding occurs after all the controllers
12985 * have been constructed but before `$onInit` is called.
12986 * If the name of the required controller is the same as the local name (the key), the name can be
12987 * omitted. For example, `{parentDir: '^^'}` is equivalent to `{parentDir: '^^parentDir'}`.
12988 * See the {@link $compileProvider#component} helper for an example of how this can be used.
12989 * If no such required directive(s) can be found, or if the directive does not have a controller, then an error is
12990 * raised (unless no link function is specified and the required controllers are not being bound to the directive
12991 * controller, in which case error checking is skipped). The name can be prefixed with:
12992 *
12993 * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
12994 * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
12995 * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
12996 * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
12997 * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
12998 *   `null` to the `link` fn if not found.
12999 * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
13000 *   `null` to the `link` fn if not found.
13001 *
13002 *
13003 * #### `controllerAs`
13004 * Identifier name for a reference to the controller in the directive's scope.
13005 * This allows the controller to be referenced from the directive template. This is especially
13006 * useful when a directive is used as component, i.e. with an `isolate` scope. It's also possible
13007 * to use it in a directive without an `isolate` / `new` scope, but you need to be aware that the
13008 * `controllerAs` reference might overwrite a property that already exists on the parent scope.
13009 *
13010 *
13011 * #### `restrict`
13012 * String of subset of `EACM` which restricts the directive to a specific directive
13013 * declaration style. If omitted, the defaults (elements and attributes) are used.
13014 *
13015 * * `E` - Element name (default): `<my-directive></my-directive>`
13016 * * `A` - Attribute (default): `<div my-directive="exp"></div>`
13017 * * `C` - Class: `<div class="my-directive: exp;"></div>`
13018 * * `M` - Comment: `<!-- directive: my-directive exp -->`
13019 *
13020 *
13021 * #### `templateNamespace`
13022 * String representing the document type used by the markup in the template.
13023 * AngularJS needs this information as those elements need to be created and cloned
13024 * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`.
13025 *
13026 * * `html` - All root nodes in the template are HTML. Root nodes may also be
13027 *   top-level elements such as `<svg>` or `<math>`.
13028 * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`).
13029 * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`).
13030 *
13031 * If no `templateNamespace` is specified, then the namespace is considered to be `html`.
13032 *
13033 * #### `template`
13034 * HTML markup that may:
13035 * * Replace the contents of the directive's element (default).
13036 * * Replace the directive's element itself (if `replace` is true - DEPRECATED).
13037 * * Wrap the contents of the directive's element (if `transclude` is true).
13038 *
13039 * Value may be:
13040 *
13041 * * A string. For example `<div red-on-hover>{{delete_str}}</div>`.
13042 * * A function which takes two arguments `tElement` and `tAttrs` (described 
13042in the `compile`
13043 *   function api below) and returns a string value.
13044 *
13045 *
13046 * #### `templateUrl`
13047 * This is similar to `template` but the template is loaded from the specified URL, asynchronously.
13048 *
13049 * Because template loading is asynchronous the compiler will suspend compilation of directives on that element
13050 * for later when the template has been resolved.  In the meantime it will continue to compile and link
13051 * sibling and parent elements as though this element had not contained any directives.
13052 *
13053 * The compiler does not suspend the entire compilation to wait for templates to be loaded because this
13054 * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the
13055 * case when only one deeply nested directive has `templateUrl`.
13056 *
13057 * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache}
13058 *
13059 * You can specify `templateUrl` as a string representing the URL or as a function which takes two
13060 * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
13061 * a string value representing the url.  In either case, the template URL is passed through {@link
13062 * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
13063 *
13064 *
13065 * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0)
13066 * specify what the template should replace. Defaults to `false`.
13067 *
13068 * * `true` - the template will replace the directive's element.
13069 * * `false` - the template will replace the contents of the directive's element.
13070 *
13071 * The replacement process migrates all of the attributes / classes from the old element to the new
13072 * one. See the {@link guide/directive#template-expanding-directive
13073 * Directives Guide} for an example.
13074 *
13075 * There are very few scenarios where element replacement is required for the application function,
13076 * the main one being reusable custom components that are used within SVG contexts
13077 * (because SVG doesn't work with custom elements in the DOM tree).
13078 *
13079 * #### `transclude`
13080 * Extract the contents of the element where the directive appears and make it available to the directive.
13081 * The contents are compiled and provided to the directive as a **transclusion function**. See the
13082 * {@link $compile#transclusion Transclusion} section below.
13083 *
13084 *
13085 * #### `compile`
13086 *
13087 * ```js
13088 *   function compile(tElement, tAttrs, transclude) { ... }
13089 * ```
13090 *
13091 * The compile function deals with transforming the template DOM. Since most directives do not do
13092 * template transformation, it is not used often. The compile function takes the following arguments:
13093 *
13094 *   * `tElement` - template element - The element where the directive has been declared. It is
13095 *     safe to do template transformation on the element and child elements only.
13096 *
13097 *   * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
13098 *     between all directive compile functions.
13099 *
13100 *   * `transclude` -  [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
13101 *
13102 * <div class="alert alert-warning">
13103 * **Note:** The template instance and the link instance may be different objects if the template has
13104 * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
13105 * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
13106 * should be done in a linking function rather than in a compile function.
13107 * </div>
13108
13109 * <div class="alert alert-warning">
13110 * **Note:** The compile function cannot handle directives that recursively use themselves in their
13111 * own templates or compile functions. Compiling these directives results in an infinite loop and
13112 * stack overflow errors.
13113 *
13114 * This can be avoided by manually using $compile
vendor: 5,458 bytes, lines 13114-13218
13114 in the postLink function to imperatively compile
13115 * a directive's template instead of relying on automatic template compilation via `template` or
13116 * `templateUrl` declaration or manual compilation inside the compile function.
13117 * </div>
13118 *
13119 * <div class="alert alert-danger">
13120 * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
13121 *   e.g. does not know about the right outer scope. Please use the transclude function that is passed
13122 *   to the link function instead.
13123 * </div>
13124
13125 * A compile function can have a return value which can be either a function or an object.
13126 *
13127 * * returning a (post-link) function - is equivalent to registering the linking function via the
13128 *   `link` property of the config object when the compile function is empty.
13129 *
13130 * * returning an object with function(s) registered via `pre` and `post` properties - allows you to
13131 *   control when a linking function should be called during the linking phase. See info about
13132 *   pre-linking and post-linking functions below.
13133 *
13134 *
13135 * #### `link`
13136 * This property is used only if the `compile` property is not defined.
13137 *
13138 * ```js
13139 *   function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
13140 * ```
13141 *
13142 * The link function is responsible for registering DOM listeners as well as updating the DOM. It is
13143 * executed after the template has been cloned. This is where most of the directive logic will be
13144 * put.
13145 *
13146 *   * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the
13147 *     directive for registering {@link ng.$rootScope.Scope#$watch watches}.
13148 *
13149 *   * `iElement` - instance element - The element where the directive is to be used. It is safe to
13150 *     manipulate the children of the element only in `postLink` function since the children have
13151 *     already been linked.
13152 *
13153 *   * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
13154 *     between all directive linking functions.
13155 *
13156 *   * `controller` - the directive's required controller instance(s) - Instances are shared
13157 *     among all directives, which allows the directives to use the controllers as a communication
13158 *     channel. The exact value depends on the directive's `require` property:
13159 *       * no controller(s) required: the directive's own controller, or `undefined` if it doesn't have one
13160 *       * `string`: the controller instance
13161 *       * `array`: array of controller instances
13162 *
13163 *     If a required controller cannot be found, and it is optional, the instance is `null`,
13164 *     otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown.
13165 *
13166 *     Note that you can also require the directive's own controller - it will be made available like
13167 *     any other controller.
13168 *
13169 *   * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
13170 *     This is the same as the `$transclude` parameter of directive controllers,
13171 *     see {@link ng.$compile#-controller- the controller section for details}.
13172 *     `function([scope], cloneLinkingFn, futureParentElement)`.
13173 *
13174 * #### Pre-linking function
13175 *
13176 * Executed before the child elements are linked. Not safe to do DOM transformation since the
13177 * compiler linking function will fail to locate the correct elements for linking.
13178 *
13179 * #### Post-linking function
13180 *
13181 * Executed after the child elements are linked.
13182 *
13183 * Note that child elements that contain `templateUrl` directives will not have been compiled
13184 * and linked since they are waiting for their template to load asynchronously and their own
13185 * compilation and linking has been suspended until that occurs.
13186 *
13187 * It is safe to do DOM transformation in the post-linking function on elements that are not waiting
13188 * for their async templates to be resolved.
13189 *
13190 *
13191 * ### Transclusion
13192 *
13193 * Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and
13194 * copying them to another part of the DOM, while maintaining their connection to the original AngularJS
13195 * scope from where they were taken.
13196 *
13197 * Transclusion is used (often with {@link ngTransclude}) to insert the
13198 * original contents of a directive's element into a specified place in the template of the directive.
13199 * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded
13200 * content has access to the properties on the scope from which it was taken, even if the directive
13201 * has isolated scope.
13202 * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}.
13203 *
13204 * This makes it possible for the widget to have private state for its template, while the transcluded
13205 * content has access to its originating scope.
13206 *
13207 * <div class="alert alert-warning">
13208 * **Note:** When testing an element transclude directive you must not place the directive at the root of the
13209 * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
13210 * Testing Transclusion Directives}.
13211 * </div>
13212 *
13213 * There are three kinds of transclusion depending upon whether you want to transclude just the contents of the
13214 * directive's element, the entire element or multiple parts of the element contents:
13215 *
13216 * * `true` - transclude the content (i.e. the child nodes) of the directive's element.
13217 * * `'element'` - transclude the whole of the directive's element including any directives on this
13218 *   element that 
13218defined at a lower priority than this directive. When used, the `template`
13219 *   property is ignored.
13220 * * **`{...}` (an object hash):** - map elements of the content onto transclusion "slots" in the template.
13221 *
13222 * **Mult-slot transclusion** is declared by providing an object for the `transclude` property.
13223 *
13224 * This object is a map where the keys are the name of the slot to fill and the value is an element selector
13225 * used to match the HTML to the slot. The element selector should be in normalized form (e.g. `myElement`)
13226 * and will match the standard element variants (e.g. `my-element`, `my:element`, `data-my-element`, etc).
13227 *
13228 * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
13229 *
13230 * If the element selector is prefixed with a `?` then that slot is optional.
13231 *
13232 * For example, the transclude object `{ slotA: '?myCustomElement' }` maps `<my-custom-element>` elements to
13233 * the `slotA` slot, which can be accessed via the `$transclude` function or via the {@link ngTransclude} directive.
13234 *
13235 * Slots that are not marked as optional (`?`) will trigger a compile time error if there are no matching elements
13236 * in the transclude content. If you wish to know if an optional slot was filled with content, then you can call
13237 * `$transclude.isSlotFilled(slotName)` on the transclude function passed to the directive's link function and
13238 * injectable into the directive's controller.
13239 *
13240 *
13241 * #### Transclusion Functions
13242 *
13243 * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
13244 * function** to the directive's `link` function and `controller`. This transclusion function is a special
13245 * **linking function** that will return the compiled contents linked to a new transclusion scope.
13246 *
13247 * <div class="alert alert-info">
13248 * If you are just using {@link ngTransclude} then you don't need to worry about this function, since
13249 * ngTransclude will deal with it for us.
13250 * </div>
13251 *
13252 * If you want to manually control the insertion and removal of the transcluded content in your directive
13253 * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery
13254 * object that contains the compiled DOM, which is linked to the correct transclusion scope.
13255 *
13256 * When you call a transclusion function you can pass in a **clone attach function**. This function accepts
13257 * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded
13258 * content and the `scope` is the newly created transclusion scope, which the clone will be linked to.
13259 *
13260 * <div class="alert alert-info">
13261 * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a transclude function
13262 * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
13263 * </div>
13264 *
13265 * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone
13266 * attach function**:
13267 *
13268 * ```js
13269 * var transcludedContent, transclusionScope;
13270 *
13271 * $transclude(function(clone, scope) {
13272 *   element.append(clone);
13273 *   transcludedContent = clone;
13274 *   transclusionScope = scope;
13275 * });
13276 * ```
13277 *
13278 * Later, if you want to remove the transcluded content from your DOM then you should also destroy the
13279 * associated transclusion scope:
13280 *
13281 * ```js
13282 * transcludedContent.remove();
13283 * transclusionScope.$destroy();
13284 * ```
13285 *
13286 * <div class="alert alert-info">
13287 * **Best Practice**: if you intend to add and remove transcluded content manually in your directive
13288 * (by calling the transclude function to get the DOM and calling `element.remove()` to remove it),
13289 * then you are also responsible for calling `$destroy` on the transclusion scope.
13290 * </div>
13291 *
13292 * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
13293 * automatically destroy their transcluded clones as necessary so you do not need to worry about this if
13294 * you are simply using {@link ngTransclude} to inject the transclusion into your directive.
13295 *
13296 *
13297 * #### Transclusion Scopes
13298 *
13299 * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion
13300 * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed
13301 * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it
13302 * was taken.
13303 *
13304 * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look
13305 * like this:
13306 *
13307 * ```html
13308 * <div ng-app>
13309 *   <div isolate>
13310 *     <div transclusion>
13311 *     </div>
13312 *   </div>
13313 * </div>
13314 * ```
13315 *
13316 * The `$parent` scope hierarchy will look like this:
13317 *
13318   ```
13319   - $rootScope
13320     - isolate
13321       - transclusion
13322   ```
13323 *
13324 * but the scopes will inherit prototypically from different scopes to their `$parent`.
13325 *
13326   ```
13327   - $rootScope
13328     - transclusion
13329   - isolate
13330   ```
13331 *
13332 *
13333 * ### Attributes
13334 *
13335 * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
13336 * `link()` or `compile()` functions. It has a variety of uses.
13337 *
13338 * * *Accessing normalized attribute names:* Directives like 'ngBind' can be expressed in many ways:
13339 *   'ng:bind', `data-ng-bind`, or 'x-ng-bind'. The attributes object allows for normalized access
13340 *   to the attributes.
13341 *
13342 * * *Directive inter-communication:* All directives share the same instance of the attributes
13343 *   object which allows the directives to use the attributes object as inter directive
13344 *   communication.
13345 *
13346 * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
13347 *   allowing other directives to read the interpolated value.
13348 *
13349 * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
13350 *   that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
13351 *   the only way to easily get the actual value because during the linking phase the interpolation
13352 *   hasn't been evaluated yet and so the value is at this time set to `undefined`.
13353 *
13354 * ```js
13355 * function linkingFn(scope, elm, attrs, ctrl) {
13356 *   // get the attribute value
13357 *   console.log(attrs.ngModel);
13358 *
13359 *   // change the attribute
13360 *   attrs.$set('ngModel', 'new value');
13361 *
13362 *   // observe changes to interpolated attribute
13363 *   attrs.$observe('ngModel', function(value) {
13364 *     console.log('ngModel has changed value to ' + value);
13365 *   });
13366 * }
13367 * ```
13368 *
13369 * ## Example
13370 *
13371 * <div class="alert alert-warning">
13372 * **Note**: Typically directives are registered with `module.directive`. The example below is
13373 * to illustrate how `$compile` works.
13374 * </div>
13375 *
13376 <example module="compileExample" name="compile">
13377   <file name="index.html">
13378    <script>
13379      angular.module('compileExample', [], function($compileProvider) {
13380        // configure new 'compile' directive by passing a directive
13381        // factory function. The factory function injects the '$compile'
13382        $compileProvider.directive('compile', function($compile) {
13383          // directive factory creates a link function
13384          return function(scope, element, attrs) {
13385            scope.$watch(
13386              function(scope) {
13387                 // watch the 'compile' expression for changes
13388                return scope.$eval(attrs.compile);
13389              },
13390              function(value) {
13391                // when the 'compile' expression changes
13392                // assign it into the current DOM
13393                element.html(value);
13394
13395                // compile the new DOM and link it to the current
13396                // scope.
13397                // NOTE: we only compile .childNodes so that
13398                // we don't get into infinite loop compiling ourselves
13399                $compile(element.contents())(scope);
13400              }
13401            );
13402          };
13403        });
13404      })
13405      .controller('GreeterController', ['$scope', function($scope) {
13406        $scope.name = 'Angular';
13407        $scope.html = 'Hello {{name}}';
13408      }]);
13409    </script>
13410    <div ng-controller="GreeterController">
13411      <input ng-model="name"> <br/>
13412      <textarea ng-model="html"></textarea> <br/>
13413      <div compile="html"></div>
13414    </div>
13415   </file>
13416   <file name="protractor.js" type="protractor">
13417     it('should auto compile', function() {
13418       var textarea = $('textarea');
13419       var output = $('div[compile]');
13420       // The initial state reads 'Hello Angular'.
13421       expect(output.getText()).toBe('Hello Angular');
13422       textarea.clear();
13423       textarea.sendKeys('{{name}}!');
13424       expect(output.getText()).toBe('Angular!');
13425     });
13426   </file>
13427 </example>
13428
13429 *
13430 *
13431 * @param {string|DOMElement} element Element or HTML string to compile into a template function.
13432 * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED.
13433 *
13434 * <div class="alert alert-danger">
13435 * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it
13436 *   e.g. will not use the right outer scope. Please pass the transclude function as a
13437 *   `parentBoundTranscludeFn` to the link function instead.
13438 * </div>
13439 *
13440 * @param {number} maxPriority only apply directives lower than given priority (Only effects the
13441 *                 root element(s), not their children)
13442 * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template
13443 * (a DOM element/tree) to a scope. Where:
13444 *
13445 *  * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
13446 *  * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
13447 *  `template` and call the `cloneAttachFn` function allowing the caller to attach the
13448 *  cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
13449 *  called as: <br/> `cloneAttachFn(clonedElement, scope)` where:
13450 *
13451 *      * `clonedElement` - is a clone of the original `element` passed into the compiler.
13452 *      * `scope` - is the current scope with which the linking function is working with.
13453 *
13454 *  * `options` - An optional object hash with linking options. If `options` is provided, then the following
13455 *  keys may be used to control linking behavior:
13456 *
13457 *      * `parentBoundTranscludeFn` - the transclude function made available to
13458 *        directives; if given, it will be passed through to the link functions of
13459 *        directives found in `element` during compilation.
13460 *      * `transcludeControllers` - an object hash with keys that map controller names
13461 *        to a hash with the key `instance`, which maps to the controller instance;
13462 *        if given, it will make the controllers available to directives on the compileNode:
13463 *        ```
13464 *        {
13465 *          parent: {
13466 *            instance: parentControllerInstance
13467 *          }
13468 *        }
13469 *        ```
13470 *      * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
13471 *        the cloned elements; only needed for transcludes that are allowed to contain non html
13472 *        elements (e.g. SVG elements). See also the directive.controller property.
13473 *
13474 * Calling the linking function returns the element of the template. It is either the original
13475 * element passed in, or the clone of the element if the `cloneAttachFn` is provided.
13476 *
13477 * After linking the view is not updated until after a call to $digest which typically is done by
13478 * Angular automatically.
13479 *
13480 * If you need access to the bound view, there are two ways to do it:
13481 *
13482 * - If you are not asking the linking function to clone the template, create the DOM element(s)
13483 *   before you send them to the compiler and keep this reference around.
13484 *   ```js
13485 *     var element = $compile('<p>{{total}}</p>')(scope);
13486 *   ```
13487 *
13488 * - if on the other hand, you need the element to be cloned, the view reference from the original
13489 *   example would not point to the clone, but rather to the original template that was cloned. In
13490 *   this case, you can access the clone via the cloneAttachFn:
13491 *   ```js
13492 *     var templateElement = angular.element('<p>{{total}}</p>'),
13493 *         scope = ....;
13494 *
13495 *     var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
13496 *       //attach the clone to DOM document at the right place
13497 *     });
13498 *
13499 *     //now we have reference to the cloned DOM via `clonedElement`
13500 *   ```
13501 *
13502 *
13503 * For information on how the compiler works, see the
13504 * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
13505 *
13506 * @knownIssue
13507 *
13508 * ### Double Compilation
13509 *
13510   Double compilation occurs when an already compiled part of the DOM gets
13511   compiled again. This is an undesired effect and can lead to misbehaving directives, performance issues,
13512   and memory leaks. Refer to the Compiler Guide {@link guide/compiler#double-compilation-and-how-to-avoid-it
13513   section on double compilation} for an in-depth explanation and ways to avoid it.
13514 *
13515 */
13516
13517var $compileMinErr = minErr('$compile');
13518
13519function UNINITIALIZED_VALUE() {}
13520var _UNINITIALIZED_VALUE = new UNINITIALIZED_VALUE();
13521
13522/**
13523 * @ngdoc provider
13524 * @name $compileProvider
13525 *
13526 * @description
13527 */
vendor: 15,193 bytes, lines 13528-13895
13528$CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
13529/** @this */
13530function $CompileProvider($provide, $$sanitizeUriProvider) {
13531  var hasDirectives = {},
13532      Suffix = 'Directive',
13533      COMMENT_DIRECTIVE_REGEXP = /^\s*directive:\s*([\w-]+)\s+(.*)$/,
13534      CLASS_DIRECTIVE_REGEXP = /(([\w-]+)(?::([^;]+))?;?)/,
13535      ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
13536      REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
13537
13538  // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
13539  // The assumption is that future DOM event attribute names will begin with
13540  // 'on' and be composed of only English letters.
13541  var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
13542  var bindingCache = createMap();
13543
13544  function parseIsolateBindings(scope, directiveName, isController) {
13545    var LOCAL_REGEXP = /^\s*([@&<]|=(\*?))(\??)\s*([\w$]*)\s*$/;
13546
13547    var bindings = createMap();
13548
13549    forEach(scope, function(definition, scopeName) {
13550      if (definition in bindingCache) {
13551        bindings[scopeName] = bindingCache[definition];
13552        return;
13553      }
13554      var match = definition.match(LOCAL_REGEXP);
13555
13556      if (!match) {
13557        throw $compileMinErr('iscp',
13558            'Invalid {3} for directive \'{0}\'.' +
13559            ' Definition: {... {1}: \'{2}\' ...}',
13560            directiveName, scopeName, definition,
13561            (isController ? 'controller bindings definition' :
13562            'isolate scope definition'));
13563      }
13564
13565      bindings[scopeName] = {
13566        mode: match[1][0],
13567        collection: match[2] === '*',
13568        optional: match[3] === '?',
13569        attrName: match[4] || scopeName
13570      };
13571      if (match[4]) {
13572        bindingCache[definition] = bindings[scopeName];
13573      }
13574    });
13575
13576    return bindings;
13577  }
13578
13579  function parseDirectiveBindings(directive, directiveName) {
13580    var bindings = {
13581      isolateScope: null,
13582      bindToController: null
13583    };
13584    if (isObject(directive.scope)) {
13585      if (directive.bindToController === true) {
13586        bindings.bindToController = parseIsolateBindings(directive.scope,
13587                                                         directiveName, true);
13588        bindings.isolateScope = {};
13589      } else {
13590        bindings.isolateScope = parseIsolateBindings(directive.scope,
13591                                                     directiveName, false);
13592      }
13593    }
13594    if (isObject(directive.bindToController)) {
13595      bindings.bindToController =
13596          parseIsolateBindings(directive.bindToController, directiveName, true);
13597    }
13598    if (bindings.bindToController && !directive.controller) {
13599      // There is no controller
13600      throw $compileMinErr('noctrl',
13601            'Cannot bind to controller without directive \'{0}\'s controller.',
13602            directiveName);
13603    }
13604    return bindings;
13605  }
13606
13607  function assertValidDirectiveName(name) {
13608    var letter = name.charAt(0);
13609    if (!letter || letter !== lowercase(letter)) {
13610      throw $compileMinErr('baddir', 'Directive/Component name \'{0}\' is invalid. The first character must be a lowercase letter', name);
13611    }
13612    if (name !== name.trim()) {
13613      throw $compileMinErr('baddir',
13614            'Directive/Component name \'{0}\' is invalid. The name should not contain leading or trailing whitespaces',
13615            name);
13616    }
13617  }
13618
13619  function getDirectiveRequire(directive) {
13620    var require = directive.require || (directive.controller && directive.name);
13621
13622    if (!isArray(require) && isObject(require)) {
13623      forEach(require, function(value, key) {
13624        var match = value.match(REQUIRE_PREFIX_REGEXP);
13625        var name = value.substring(match[0].length);
13626        if (!name) require[key] = match[0] + key;
13627      });
13628    }
13629
13630    return require;
13631  }
13632
13633  function getDirectiveRestrict(restrict, name) {
13634    if (restrict && !(isString(restrict) && /[EACM]/.test(restrict))) {
13635      throw $compileMinErr('badrestrict',
13636          'Restrict property \'{0}\' of directive \'{1}\' is invalid',
13637          restrict,
13638          name);
13639    }
13640
13641    return restrict || 'EA';
13642  }
13643
13644  /**
13645   * @ngdoc method
13646   * @name $compileProvider#directive
13647   * @kind function
13648   *
13649   * @description
13650   * Register a new directive with the compiler.
13651   *
13652   * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
13653   *    will match as <code>ng-bind</code>), or an object map of directives where the keys are the
13654   *    names and the values are the factories.
13655   * @param {Function|Array} directiveFactory An injectable directive factory function. See the
13656   *    {@link guide/directive directive guide} and the {@link $compile compile API} for more info.
13657   * @returns {ng.$compileProvider} Self for chaining.
13658   */
13659  this.directive = function registerDirective(name, directiveFactory) {
13660    assertArg(name, 'name');
13661    assertNotHasOwnProperty(name, 'directive');
13662    if (isString(name)) {
13663      assertValidDirectiveName(name);
13664      assertArg(directiveFactory, 'directiveFactory');
13665      if (!hasDirectives.hasOwnProperty(name)) {
13666        hasDirectives[name] = [];
13667        $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
13668          function($injector, $exceptionHandler) {
13669            var directives = [];
13670            forEach(hasDirectives[name], function(directiveFactory, index) {
13671              try {
13672                var directive = $injector.invoke(directiveFactory);
13673                if (isFunction(directive)) {
13674                  directive = { compile: valueFn(directive) };
13675                } else if (!directive.compile && directive.link) {
13676                  directive.compile = valueFn(directive.link);
13677                }
13678                directive.priority = directive.priority || 0;
13679                directive.index = index;
13680                directive.name = directive.name || name;
13681                directive.require = getDirectiveRequire(directive);
13682                directive.restrict = getDirectiveRestrict(directive.restrict, name);
13683                directive.$$moduleName = directiveFactory.$$moduleName;
13684                directives.push(directive);
13685              } catch (e) {
13686                $exceptionHandler(e);
13687              }
13688            });
13689            return directives;
13690          }]);
13691      }
13692      hasDirectives[name].push(directiveFactory);
13693    } else {
13694      forEach(name, reverseParams(registerDirective));
13695    }
13696    return this;
13697  };
13698
13699  /**
13700   * @ngdoc method
13701   * @name $compileProvider#component
13702   * @module ng
13703   * @param {string} name Name of the component in camelCase (i.e. `myComp` which will match `<my-comp>`)
13704   * @param {Object} options Component definition object (a simplified
13705   *    {@link ng.$compile#directive-definition-object directive definition object}),
13706   *    with the following properties (all optional):
13707   *
13708   *    - `controller` – `{(string|function()=}` – controller constructor function that should be
13709   *      associated with newly created scope or the name of a {@link ng.$compile#-controller-
13710   *      registered controller} if passed as a string. An empty `noop` function by default.
13711   *    - `controllerAs` – `{string=}` – identifier name for to reference the controller in the component's scope.
13712   *      If present, the controller will be published to scope under the `controllerAs` name.
13713   *      If not present, this will default to be `$ctrl`.
13714   *    - `template` – `{string=|function()=}` – html template as a string or a function that
13715   *      returns an html template as a string which should be used as the contents of this component.
13716   *      Empty string by default.
13717   *
13718   *      If `template` is a function, then it is {@link auto.$injector#invoke injected} with
13719   *      the following locals:
13720   *
13721   *      - `$element` - Current element
13722   *      - `$attrs` - Current attributes object for the element
13723   *
13724   *    - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html
13725   *      template that should be used  as the contents of this component.
13726   *
13727   *      If `templateUrl` is a function, then it is {@link auto.$injector#invoke injected} with
13728   *      the following locals:
13729   *
13730   *      - `$element` - Current element
13731   *      - `$attrs` - Current attributes object for the element
13732   *
13733   *    - `bindings` – `{object=}` – defines bindings between DOM attributes and component properties.
13734   *      Component properties are always bound to the component controller and not to the scope.
13735   *      See {@link ng.$compile#-bindtocontroller- `bindToController`}.
13736   *    - `transclude` – `{boolean=}` – whether {@link $compile#transclusion content transclusion} is enabled.
13737   *      Disabled by default.
13738   *    - `require` - `{Object<string, string>=}` - requires the controllers of other directives and binds them to
13739   *      this component's controller. The object keys specify the property names under which the required
13740   *      controllers (object values) will be bound. See {@link ng.$compile#-require- `require`}.
13741   *    - `$...` – additional properties to attach to the directive factory function and the controller
13742   *      constructor function. (This is used by the component router to annotate)
13743   *
13744   * @returns {ng.$compileProvider} the compile provider itself, for chaining of function calls.
13745   * @description
13746   * Register a **component definition** with the compiler. This is a shorthand for registering a special
13747   * type of directive, which represents a self-contained UI component in your application. Such components
13748   * are always isolated (i.e. `scope: {}`) and are always restricted to elements (i.e. `restrict: 'E'`).
13749   *
13750   * Component definitions are very simple and do not require as much configuration as defining general
13751   * directives. Component definitions usually consist only of a template and a controller backing it.
13752   *
13753   * In order to make the definition easier, components enforce best practices like use of `controllerAs`,
13754   * `bindToController`. They always have **isolate scope** and are restricted to elements.
13755   *
13756   * Here are a few examples of how you would usually define components:
13757   *
13758   * ```js
13759   *   var myMod = angular.module(...);
13760   *   myMod.component('myComp', {
13761   *     template: '<div>My name is {{$ctrl.name}}</div>',
13762   *     controller: function() {
13763   *       this.name = 'shahar';
13764   *     }
13765   *   });
13766   *
13767   *   myMod.component('myComp', {
13768   *     template: '<div>My name is {{$ctrl.name}}</div>',
13769   *     bindings: {name: '@'}
13770   *   });
13771   *
13772   *   myMod.component('myComp', {
13773   *     templateUrl: 'views/my-comp.html',
13774   *     controller: 'MyCtrl',
13775   *     controllerAs: 'ctrl',
13776   *     bindings: {name: '@'}
13777   *   });
13778   *
13779   * ```
13780   * For more examples, and an in-depth guide, see the {@link guide/component component guide}.
13781   *
13782   * <br />
13783   * See also {@link ng.$compileProvider#directive $compileProvider.directive()}.
13784   */
13785  this.component = function registerComponent(name, options) {
13786    var controller = options.controller || function() {};
13787
13788    function factory($injector) {
13789      function makeInjectable(fn) {
13790        if (isFunction(fn) || isArray(fn)) {
13791          return /** @this */ function(tElement, tAttrs) {
13792            return $injector.invoke(fn, this, {$element: tElement, $attrs: tAttrs});
13793          };
13794        } else {
13795          return fn;
13796        }
13797      }
13798
13799      var template = (!options.template && !options.templateUrl ? '' : options.template);
13800      var ddo = {
13801        controller: controller,
13802        controllerAs: identifierForController(options.controller) || options.controllerAs || '$ctrl',
13803        template: makeInjectable(template),
13804        templateUrl: makeInjectable(options.templateUrl),
13805        transclude: options.transclude,
13806        scope: {},
13807        bindToController: options.bindings || {},
13808        restrict: 'E',
13809        require: options.require
13810      };
13811
13812      // Copy annotations (starting with $) over to the DDO
13813      forEach(options, function(val, key) {
13814        if (key.charAt(0) === '$') ddo[key] = val;
13815      });
13816
13817      return ddo;
13818    }
13819
13820    // TODO(pete) remove the following `forEach` before we release 1.6.0
13821    // The [email protected] looks for the annotations on the controller constructor
13822    // Nothing in Angular looks for annotations on the factory function but we can't remove
13823    // it from 1.5.x yet.
13824
13825    // Copy any annotation properties (starting with $) over to the factory and controller constructor functions
13826    // These could be used by libraries such as the new component router
13827    forEach(options, function(val, key) {
13828      if (key.charAt(0) === '$') {
13829        factory[key] = val;
13830        // Don't try to copy over annotations to named controller
13831        if (isFunction(controller)) controller[key] = val;
13832      }
13833    });
13834
13835    factory.$inject = ['$injector'];
13836
13837    return this.directive(name, factory);
13838  };
13839
13840
13841  /**
13842   * @ngdoc method
13843   * @name $compileProvider#aHrefSanitizationWhitelist
13844   * @kind function
13845   *
13846   * @description
13847   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
13848   * urls during a[href] sanitization.
13849   *
13850   * The sanitization is a security measure aimed at preventing XSS attacks via html links.
13851   *
13852   * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
13853   * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
13854   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
13855   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
13856   *
13857   * @param {RegExp=} regexp New regexp to whitelist urls with.
13858   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
13859   *    chaining otherwise.
13860   */
13861  this.aHrefSanitizationWhitelist = function(regexp) {
13862    if (isDefined(regexp)) {
13863      $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
13864      return this;
13865    } else {
13866      return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
13867    }
13868  };
13869
13870
13871  /**
13872   * @ngdoc method
13873   * @name $compileProvider#imgSrcSanitizationWhitelist
13874   * @kind function
13875   *
13876   * @description
13877   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
13878   * urls during img[src] sanitization.
13879   *
13880   * The sanitization is a security measure aimed at prevent XSS attacks via html links.
13881   *
13882   * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
13883   * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
13884   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
13885   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
13886   *
13887   * @param {RegExp=} regexp New regexp to whitelist urls with.
13888   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
13889   *    chaining otherwise.
13890   */
13891  this.imgSrcSanitizationWhitelist = function(regexp) {
13892    if (isDefined(regexp)) {
13893      $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
13894      return this;
13895    } else {
13896      return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
13897    }
13898  };
13899
13900  /**
13901   * @ngdoc method
13902   * @name  $compileProvider#debugInfoEnabled
13903   *
13904   * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the
13905   * current debugInfoEnabled state
13906   * @returns {*} current value if used as getter or itself (chaining) if used as setter
13907   *
13908   * @kind function
13909   *
13910   * @description
13911   * Call this method to enable/disable various debug runtime information in the compiler such as adding
13912   * binding information and a reference to the current scope on to DOM elements.
13913   * If enabled, the compiler will add the following to DOM elements that have been bound to the scope
13914   * * `ng-binding` CSS class
13915   * * `$binding` data property containing an array of the binding expressions
13916   *
13917   * You may want to disable this in production for a significant performance boost. See
13918   * {@link guide/production#disabling-debug-data Disabling Debug Data} for more.
13919   *
13920   * The default value is true.
13921   */
13922  var debugInfoEnabled = true;
13923  this.debugInfoEnabled = function(enabled) {
13924    if (isDefined(enabled)) {
13925      debugInfoEnabled = enabled;
13926      return this;
13927    }
13928    return debugInfoEnabled;
13929  };
13930
13931  /**
13932   * @ngdoc method
13933   * @name  $compileProvider#preAssignBindingsEnabled
13934   *
13935   * @param {boolean=} enabled update the preAssignBindingsEnabled state if provided, otherwise just return the
13936   * current preAssignBindingsEnabled state
13937   * @returns {*} current value if used as getter or itself (chaining) if used as setter
13938   *
13939   * @kind function
13940   *
13941   * @description
13942   * Call this method to enable/disable whether directive controllers are assigned bindings before
13943   * calling the controller's constructor.
13944   * If enabled (true), the compiler assigns the value of each of the bindings to the
13945   * properties of the controller object before the constructor of this object is called.
13946   *
13947   * If disabled (false), the compiler calls the constructor first before assigning bindings.
13948   *
13949   * The default value is true in Angular 1.5.x but will switch to false in Angular 1.6.x.
13950   */
13951  var preAssignBindingsEnabled = true;
13952  this.preAssignBindingsEnabled = function(enabled) {
13953    if (isDefined(enabled)) {
13954      preAssignBindingsEnabled = enabled;
13955      return this;
13956    }
13957    return preAssignBindingsEnabled;
13958  };
13959
13960
13961  var TTL = 10;
13962  /**
13963   * @ngdoc method
13964   * @name $compileProvider#onChangesTtl
13965   * @description
13966   *
13967   * Sets the number of times `$onChanges` hooks can trigger new changes before giving up and
13968   * assuming that the model is unstable.
13969   *
13970   * The current default is 10 iterations.
13971   *
13972   * In complex applications it's possible that dependencies between `$onChanges` hooks and bindings will result
13973   * in several iterations of calls to these hooks. However if an application needs more than the default 10
13974   * iterations to stabilize then you should investigate what is causing the model to continuously change during
13975   * the `$onChanges` hook execution.
13976   *
13977   * Increasing the TTL could have performance implications, so you should not change it without proper justification.
13978   *
13979   * @param {number} limit The number of `$onChanges` hook iterations.
13980   * @returns {number|object} the current limit (or `this` if called as a setter for chaining)
13981   */
13982  this.onChangesTtl = function(value) {
13983    if (arguments.length) {
13984      TTL = value;
13985      return this;
13986    }
13987    return TTL;
13988  };
13989
13990  var commentDirectivesEnabledConfig = true;
13991  /**
13992   * @ngdoc method
13993   * @name $compileProvider#commentDirectivesEnabled
13994   * @description
13995   *
13996   * It indicates to the compiler
13997   * whether or not directives on comments should be compiled.
13998   * Defaults to `true`.
13999   *
14000   * Calling this function with false disables the compilation of directives
14001   * on comments for the whole application.
14002   * This results in a compilation performance gain,
14003   * as the compiler doesn't have to check comments when looking for directives.
14004   * This should however only be used if you are sure that no comment directives are used in
14005   * the application (including any 3rd party directives).
14006   *
14007   * @param {boolean} enabled `false` if the compiler may ignore directives on comments
14008   * @returns {boolean|object} the current value (or `this` if called as a setter for chaining)
14009   */
14010  this.commentDirectivesEnabled = function(value) {
14011    if (arguments.length) {
14012      commentDirectivesEnabledConfig = value;
14013      return this;
14014    }
14015    return commentDirectivesEnabledConfig;
14016  };
14017
14018
14019  var cssClassDirectivesEnabledConfig = true;
14020  /**
14021   * @ngdoc method
14022   * @name $compileProvider#cssClassDirectivesEnabled
14023   * @description
14024   *
14025   * It indicates to the compiler
14026   * whether or not directives on element classes should be compiled.
14027   * Defaults to `true`.
14028   *
14029   * Calling this function with false disables the compilation of directives
14030   * on element classes for the whole application.
14031   * This results in a compilation performance gain,
14032   * as the compiler doesn't have to check element classes when looking for directives.
14033   * This should however only be used if you are sure that no class directives are used in
14034   * the application (including any 3rd party directives).
14035   *
14036   * @param {boolean} enabled `false` if the compiler may ignore directives on element classes
14037   * @returns {boolean|object} the current value (or `this` if called as a setter for chaining)
14038   */
14039  this.cssClassDirectivesEnabled = function(value) {
14040    if (arguments.length) {
14041      cssClassDirectivesEnabledConfig = value;
14042      return this;
14043    }
14044    return cssClassDirectivesEnabledConfig;
14045  };
14046
14047  this.$get = [
14048            '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
14049            '$controller', '$rootScope', '$sce', '$animate', '$$sanitizeUri',
14050    function($injector,   $interpolate,   $exceptionHandler,   $templateRequest,   $parse,
14051             $controller,   $rootScope,   $sce,   $animate,   $$sanitizeUri) {
14052
14053    var SIMPLE_ATTR_NAME = /^\w/;
14054    var specialAttrHolder = window.document.createElement('div');
14055
14056
14057    var commentDirectivesEnabled = commentDirectivesEnabledConfig;
14058    var cssClassDirectivesEnabled = cssClassDirectivesEnabledConfig;
14059
14060
14061    var onChangesTtl = TTL;
14062    // The onChanges hooks should all be run together in a single digest
14063    // When changes occur, the call to trigger their hooks will be added to this queue
14064    var onChangesQueue;
14065
14066    // This function is called in a $$postDigest to trigger all the onChanges hooks in a single digest
14067    function flushOnChangesQueue() {
14068      try {
14069        if (!(--onChangesTtl)) {
14070          // We have hit the TTL limit so reset everything
14071          onChangesQueue = undefined;
14072          throw $compileMinErr('infchng', '{0} $onChanges() iterations reached. Aborting!\n', TTL);
14073        }
14074        // We must run this hook in an apply since the $$postDigest runs outside apply
14075        $rootScope.$apply(function() {
14076          var errors = [];
14077          for (var i = 0, ii = onChangesQueue.length; i < ii; ++i) {
14078            try {
14079              onChangesQueue[i]();
14080            } catch (e) {
14081              errors.push(e);
14082            }
14083          }
14084          // Reset the queue to trigger a new schedule next time there is a change
14085          onChangesQueue = undefined;
14086          if (errors.length) {
14087            throw errors;
14088          }
14089        });
14090      } finally {
14091        onChangesTtl++;
14092      }
14093    }
14094
14095
14096    function Attributes(element, attributesToCopy) {
14097      if (attributesToCopy) {
14098        var keys = Object.keys(attributesToCopy);
14099        var i, l, key;
14100
14101        for (i = 0, l = keys.length; i < l; i++) {
14102          key = keys[i];
14103          this[key] = attributesToCopy[key];
14104        }
14105      } else {
14106        this.$attr = {};
14107      }
14108
14109      this.$$element = element;
14110    }
14111
14112    Attributes.prototype = {
14113      /**
14114       * @ngdoc method
14115       * @name $compile.directive.Attributes#$normalize
14116       * @kind function
14117       *
14118       * @description
14119       * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or
14120       * `data-`) to its normalized, camelCase form.
14121       *
14122       * Also there is special case for Moz prefix starting with upper case letter.
14123       *
14124       * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
14125       *
14126       * @param {string} name Name to normalize
14127       */
14128      $normalize: directiveNormalize,
14129
14130
14131      /**
14132       * @ngdoc method
14133       * @name $compile.directive.Attributes#$addClass
14134       * @kind function
14135       *
14136       * @description
14137       * Adds the CSS class value specified by the classVal parameter to the element. If animations
14138       * are enabled then an animation will be triggered for the class addition.
14139       *
14140       * @param {string} classVal The className value that will be added to the element
14141       */
14142      $addClass: function(classVal) {
14143        if (classVal && classVal.length > 0) {
14144          $animate.addClass(this.$$element, classVal);
14145        }
14146      },
14147
14148      /**
14149       * @ngdoc method
14150       * @name $compile.directive.Attributes#$removeClass
14151       * @kind function
14152       *
14153       * @description
14154       * Removes the CSS class value specified by the classVal parameter from the element. If
14155       * animations are enabled then an animation will be triggered for the class removal.
14156       *
14157       * @param {string} classVal The className value that will be removed from the element
14158       */
14159      $removeClass: function(classVal) {
14160        if (classVal && classVal.length > 0) {
14161          $animate.removeClass(this.$$element, classVal);
14162        }
14163      },
14164
14165      /**
14166       * @ngdoc method
14167       * @name $compile.directive.Attributes#$updateClass
14168       * @kind function
14169       *
14170       * @description
14171       * Adds and removes the appropriate CSS class values to the element based on the difference
14172       * between the new and old CSS class values (specified as newClasses and oldClasses).
14173       *
14174       * @param {string} newClasses The current CSS className value
14175       * @param {string} oldClasses The former CSS className value
14176       */
14177      $updateClass: function(newClasses, oldClasses) {
14178        var toAdd = tokenDifference(newClasses, oldClasses);
14179        if (toAdd && toAdd.length) {
14180          $animate.addClass(this.$$element, toAdd);
14181        }
14182
14183        var toRemove = tokenDifference(oldClasses, newClasses);
14184        if (toRemove && toRemove.length) {
14185          $animate.removeClass(this.$$element, toRemove);
14186        }
14187      },
14188
14189      /**
14190       * Set a normalized attribute on the element in a way such that all directives
14191       * can share the attribute. This function properly handles boolean attributes.
14192       * @param {string} key Normalized key. (ie ngAttribute)
14193       * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
14194       * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
14195       *     Defaults to true.
14196       * @param {string=} attrName Optional none normalized name. Defaults to key.
14197       */
14198      $set: function(key, value, writeAttr, attrName) {
14199        // TODO: decide whether or not to throw an error if "class"
14200        //is set through this function since it may cause $updateClass to
14201        //become unstable.
14202
14203        var node = this.$$element[0],
14204            booleanKey = getBooleanAttrName(node, key),
14205            aliasedKey = getAliasedAttrName(key),
14206            observer = key,
14207            nodeName;
14208
14209        if (booleanKey) {
14210          this.$$element.prop(key, value);
14211          attrName = booleanKey;
14212        } else if (aliasedKey) {
14213          this[aliasedKey] = value;
14214          observer = aliasedKey;
14215        }
14216
14217        this[key] = value;
14218
14219        // translate normalized key to actual key
14220        if (attrName) {
14221          this.$attr[key] = attrName;
14222        } else {
14223          attrName = this.$attr[key];
14224          if (!attrName) {
14225            this.$attr[key] = attrName = snake_case(key, '-');
14226          }
14227        }
14228
14229        nodeName = nodeName_(this.$$element);
14230
14231        if ((nodeName === 'a' && (key === 'href' || key === 'xlinkHref')) ||
14232            (nodeName === 'img' && key === 'src')) {
14233          // sanitize a[href] and img[src] values
14234          this[key] = value = $$sanitizeUri(value, key === 'src');
14235        } else if (nodeName === 'img' && key === 'srcset' && isDefined(value)) {
14236          // sanitize img[srcset] values
14237          var result = '';
14238
14239          // first check if there are spaces because it's not the same pattern
14240          var trimmedSrcset = trim(value);
14241          //                (   999x   ,|   999w   ,|   ,|,   )
14242          var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;
14243          var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/;
14244
14245          // split srcset into tuple of uri and descriptor except for the last item
14246          var rawUris = trimmedSrcset.split(pattern);
14247
14248          // for each tuples
14249          var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
14250          for (var i = 0; i < nbrUrisWith2parts; i++) {
14251            var innerIdx = i * 2;
14252            // sanitize the uri
14253            result += $$sanitizeUri(trim(rawUris[innerIdx]), true);
14254            // add the descriptor
14255            result += (' ' + trim(rawUris[innerIdx + 1]));
14256          }
14257
14258          // split the last item into uri and descriptor
14259          var lastTuple = trim(rawUris[i * 2]).split(/\s/);
14260
14261          // sanitize the last uri
14262          result += $$sanitizeUri(trim(lastTuple[0]), true);
14263
14264          // and add the last descriptor if any
14265          if (lastTuple.length === 2) {
14266            result += (' ' + trim(lastTuple[1]));
14267          }
14268          this[key] = value = result;
14269        }
14270
14271        if (writeAttr !== false) {
14272          if (value === null || isUndefined(value)) {
14273            this.$$element.removeAttr(attrName);
14274          } else {
14275            if (SIMPLE_ATTR_NAME.test(attrName)) {
14276              this.$$element.attr(attrName, value);
14277            } else {
14278              setSpecialAttr(this.$$element[0], attrName, value);
14279            }
14280          }
14281        }
14282
14283        // fire observers
14284        var $$observers = this.$$observers;
14285        if ($$observers) {
vendor: 29,459 bytes, lines 14286-14985
14286          forEach($$observers[observer], function(fn) {
14287            try {
14288              fn(value);
14289            } catch (e) {
14290              $exceptionHandler(e);
14291            }
14292          });
14293        }
14294      },
14295
14296
14297      /**
14298       * @ngdoc method
14299       * @name $compile.directive.Attributes#$observe
14300       * @kind function
14301       *
14302       * @description
14303       * Observes an interpolated attribute.
14304       *
14305       * The observer function will be invoked once during the next `$digest` following
14306       * compilation. The observer is then invoked whenever the interpolated value
14307       * changes.
14308       *
14309       * @param {string} key Normalized key. (ie ngAttribute) .
14310       * @param {function(interpolatedValue)} fn Function that will be called whenever
14311                the interpolated value of the attribute changes.
14312       *        See the {@link guide/interpolation#how-text-and-attribute-bindings-work Interpolation
14313       *        guide} for more info.
14314       * @returns {function()} Returns a deregistration function for this observer.
14315       */
14316      $observe: function(key, fn) {
14317        var attrs = this,
14318            $$observers = (attrs.$$observers || (attrs.$$observers = createMap())),
14319            listeners = ($$observers[key] || ($$observers[key] = []));
14320
14321        listeners.push(fn);
14322        $rootScope.$evalAsync(function() {
14323          if (!listeners.$$inter && attrs.hasOwnProperty(key) && !isUndefined(attrs[key])) {
14324            // no one registered attribute interpolation function, so lets call it manually
14325            fn(attrs[key]);
14326          }
14327        });
14328
14329        return function() {
14330          arrayRemove(listeners, fn);
14331        };
14332      }
14333    };
14334
14335    function setSpecialAttr(element, attrName, value) {
14336      // Attributes names that do not start with letters (such as `(click)`) cannot be set using `setAttribute`
14337      // so we have to jump through some hoops to get such an attribute
14338      // https://github.com/angular/angular.js/pull/13318
14339      specialAttrHolder.innerHTML = '<span ' + attrName + '>';
14340      var attributes = specialAttrHolder.firstChild.attributes;
14341      var attribute = attributes[0];
14342      // We have to remove the attribute from its container element before we can add it to the destination element
14343      attributes.removeNamedItem(attribute.name);
14344      attribute.value = value;
14345      element.attributes.setNamedItem(attribute);
14346    }
14347
14348    function safeAddClass($element, className) {
14349      try {
14350        $element.addClass(className);
14351      } catch (e) {
14352        // ignore, since it means that we are trying to set class on
14353        // SVG element, where class name is read-only.
14354      }
14355    }
14356
14357
14358    var startSymbol = $interpolate.startSymbol(),
14359        endSymbol = $interpolate.endSymbol(),
14360        denormalizeTemplate = (startSymbol === '{{' && endSymbol  === '}}')
14361            ? identity
14362            : function denormalizeTemplate(template) {
14363              return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
14364        },
14365        NG_ATTR_BINDING = /^ngAttr[A-Z]/;
14366    var MULTI_ELEMENT_DIR_RE = /^(.+)Start$/;
14367
14368    compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {
14369      var bindings = $element.data('$binding') || [];
14370
14371      if (isArray(binding)) {
14372        bindings = bindings.concat(binding);
14373      } else {
14374        bindings.push(binding);
14375      }
14376
14377      $element.data('$binding', bindings);
14378    } : noop;
14379
14380    compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) {
14381      safeAddClass($element, 'ng-binding');
14382    } : noop;
14383
14384    compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) {
14385      var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope';
14386      $element.data(dataName, scope);
14387    } : noop;
14388
14389    compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) {
14390      safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
14391    } : noop;
14392
14393    compile.$$createComment = function(directiveName, comment) {
14394      var content = '';
14395      if (debugInfoEnabled) {
14396        content = ' ' + (directiveName || '') + ': ';
14397        if (comment) content += comment + ' ';
14398      }
14399      return window.document.createComment(content);
14400    };
14401
14402    return compile;
14403
14404    //================================
14405
14406    function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,
14407                        previousCompileContext) {
14408      if (!($compileNodes instanceof jqLite)) {
14409        // jquery always rewraps, whereas we need to preserve the original selector so that we can
14410        // modify it.
14411        $compileNodes = jqLite($compileNodes);
14412      }
14413
14414      var NOT_EMPTY = /\S+/;
14415
14416      // We can not compile top level text elements since text nodes can be merged and we will
14417      // not be able to attach scope data to them, so we will wrap them in <span>
14418      for (var i = 0, len = $compileNodes.length; i < len; i++) {
14419        var domNode = $compileNodes[i];
14420
14421        if (domNode.nodeType === NODE_TYPE_TEXT && domNode.nodeValue.match(NOT_EMPTY) /* non-empty */) {
14422          jqLiteWrapNode(domNode, $compileNodes[i] = window.document.createElement('span'));
14423        }
14424      }
14425
14426      var compositeLinkFn =
14427              compileNodes($compileNodes, transcludeFn, $compileNodes,
14428                           maxPriority, ignoreDirective, previousCompileContext);
14429      compile.$$addScopeClass($compileNodes);
14430      var namespace = null;
14431      return function publicLinkFn(scope, cloneConnectFn, options) {
14432        assertArg(scope, 'scope');
14433
14434        if (previousCompileContext && previousCompileContext.needsNewScope) {
14435          // A parent directive did a replace and a directive on this element asked
14436          // for transclusion, which caused us to lose a layer of element on which
14437          // we could hold the new transclusion scope, so we will create it manually
14438          // here.
14439          scope = scope.$parent.$new();
14440        }
14441
14442        options = options || {};
14443        var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
14444          transcludeControllers = options.transcludeControllers,
14445          futureParentElement = options.futureParentElement;
14446
14447        // When `parentBoundTranscludeFn` is passed, it is a
14448        // `controllersBoundTransclude` function (it was previously passed
14449        // as `transclude` to directive.link) so we must unwrap it to get
14450        // its `boundTranscludeFn`
14451        if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) {
14452          parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude;
14453        }
14454
14455        if (!namespace) {
14456          namespace = detectNamespaceForChildElements(futureParentElement);
14457        }
14458        var $linkNode;
14459        if (namespace !== 'html') {
14460          // When using a directive with replace:true and templateUrl the $compileNodes
14461          // (or a child element inside of them)
14462          // might change, so we need to recreate the namespace adapted compileNodes
14463          // for call to the link function.
14464          // Note: This will already clone the nodes...
14465          $linkNode = jqLite(
14466            wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html())
14467          );
14468        } else if (cloneConnectFn) {
14469          // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
14470          // and sometimes changes the structure of the DOM.
14471          $linkNode = JQLitePrototype.clone.call($compileNodes);
14472        } else {
14473          $linkNode = $compileNodes;
14474        }
14475
14476        if (transcludeControllers) {
14477          for (var controllerName in transcludeControllers) {
14478            $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance);
14479          }
14480        }
14481
14482        compile.$$addScopeInfo($linkNode, scope);
14483
14484        if (cloneConnectFn) cloneConnectFn($linkNode, scope);
14485        if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
14486        return $linkNode;
14487      };
14488    }
14489
14490    function detectNamespaceForChildElements(parentElement) {
14491      // TODO: Make this detect MathML as well...
14492      var node = parentElement && parentElement[0];
14493      if (!node) {
14494        return 'html';
14495      } else {
14496        return nodeName_(node) !== 'foreignobject' && toString.call(node).match(/SVG/) ? 'svg' : 'html';
14497      }
14498    }
14499
14500    /**
14501     * Compile function matches each node in nodeList against the directives. Once all directives
14502     * for a particular node are collected their compile functions are executed. The compile
14503     * functions return values - the linking functions - are combined into a composite linking
14504     * function, which is the a linking function for the node.
14505     *
14506     * @param {NodeList} nodeList an array of nodes or NodeList to compile
14507     * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
14508     *        scope argument is auto-generated to the new child of the transcluded parent scope.
14509     * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
14510     *        the rootElement must be set the jqLite collection of the compile root. This is
14511     *        needed so that the jqLite collection items can be replaced with widgets.
14512     * @param {number=} maxPriority Max directive priority.
14513     * @returns {Function} A composite linking function of all of the matched directives or null.
14514     */
14515    function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
14516                            previousCompileContext) {
14517      var linkFns = [],
14518          attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound;
14519
14520      for (var i = 0; i < nodeList.length; i++) {
14521        attrs = new Attributes();
14522
14523        // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
14524        directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,
14525                                        ignoreDirective);
14526
14527        nodeLinkFn = (directives.length)
14528            ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,
14529                                      null, [], [], previousCompileContext)
14530            : null;
14531
14532        if (nodeLinkFn && nodeLinkFn.scope) {
14533          compile.$$addScopeClass(attrs.$$element);
14534        }
14535
14536        childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
14537                      !(childNodes = nodeList[i].childNodes) ||
14538                      !childNodes.length)
14539            ? null
14540            : compileNodes(childNodes,
14541                 nodeLinkFn ? (
14542                  (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement)
14543                     && nodeLinkFn.transclude) : transcludeFn);
14544
14545        if (nodeLinkFn || childLinkFn) {
14546          linkFns.push(i, nodeLinkFn, childLinkFn);
14547          linkFnFound = true;
14548          nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn;
14549        }
14550
14551        //use the previous context only for the first element in the virtual group
14552        previousCompileContext = null;
14553      }
14554
14555      // return a linking function if we have found anything, null otherwise
14556      return linkFnFound ? compositeLinkFn : null;
14557
14558      function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
14559        var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn;
14560        var stableNodeList;
14561
14562
14563        if (nodeLinkFnFound) {
14564          // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our
14565          // offsets don't get screwed up
14566          var nodeListLength = nodeList.length;
14567          stableNodeList = new Array(nodeListLength);
14568
14569          // create a sparse array by only copying the elements which have a linkFn
14570          for (i = 0; i < linkFns.length; i += 3) {
14571            idx = linkFns[i];
14572            stableNodeList[idx] = nodeList[idx];
14573          }
14574        } else {
14575          stableNodeList = nodeList;
14576        }
14577
14578        for (i = 0, ii = linkFns.length; i < ii;) {
14579          node = stableNodeList[linkFns[i++]];
14580          nodeLinkFn = linkFns[i++];
14581          childLinkFn = linkFns[i++];
14582
14583          if (nodeLinkFn) {
14584            if (nodeLinkFn.scope) {
14585              childScope = scope.$new();
14586              compile.$$addScopeInfo(jqLite(node), childScope);
14587            } else {
14588              childScope = scope;
14589            }
14590
14591            if (nodeLinkFn.transcludeOnThisElement) {
14592              childBoundTranscludeFn = createBoundTranscludeFn(
14593                  scope, nodeLinkFn.transclude, parentBoundTranscludeFn);
14594
14595            } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
14596              childBoundTranscludeFn = parentBoundTranscludeFn;
14597
14598            } else if (!parentBoundTranscludeFn && transcludeFn) {
14599              childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn);
14600
14601            } else {
14602              childBoundTranscludeFn = null;
14603            }
14604
14605            nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);
14606
14607          } else if (childLinkFn) {
14608            childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
14609          }
14610        }
14611      }
14612    }
14613
14614    function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {
14615      function boundTranscludeFn(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
14616
14617        if (!transcludedScope) {
14618          transcludedScope = scope.$new(false, containingScope);
14619          transcludedScope.$$transcluded = true;
14620        }
14621
14622        return transcludeFn(transcludedScope, cloneFn, {
14623          parentBoundTranscludeFn: previousBoundTranscludeFn,
14624          transcludeControllers: controllers,
14625          futureParentElement: futureParentElement
14626        });
14627      }
14628
14629      // We need  to attach the transclusion slots onto the `boundTranscludeFn`
14630      // so that they are available inside the `controllersBoundTransclude` function
14631      var boundSlots = boundTranscludeFn.$$slots = createMap();
14632      for (var slotName in transcludeFn.$$slots) {
14633        if (transcludeFn.$$slots[slotName]) {
14634          boundSlots[slotName] = createBoundTranscludeFn(scope, transcludeFn.$$slots[slotName], previousBoundTranscludeFn);
14635        } else {
14636          boundSlots[slotName] = null;
14637        }
14638      }
14639
14640      return boundTranscludeFn;
14641    }
14642
14643    /**
14644     * Looks for directives on the given node and adds them to the directive collection which is
14645     * sorted.
14646     *
14647     * @param node Node to search.
14648     * @param directives An array to which the directives are added to. This array is sorted before
14649     *        the function returns.
14650     * @param attrs The shared attrs object which is used to populate the normalized attributes.
14651     * @param {number=} maxPriority Max directive priority.
14652     */
14653    function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
14654      var nodeType = node.nodeType,
14655          attrsMap = attrs.$attr,
14656          match,
14657          nodeName,
14658          className;
14659
14660      switch (nodeType) {
14661        case NODE_TYPE_ELEMENT: /* Element */
14662
14663          nodeName = nodeName_(node);
14664
14665          // use the node name: <directive>
14666          addDirective(directives,
14667              directiveNormalize(nodeName), 'E', maxPriority, ignoreDirective);
14668
14669          // iterate over the attributes
14670          for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
14671                   j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
14672            var attrStartName = false;
14673            var attrEndName = false;
14674
14675            attr = nAttrs[j];
14676            name = attr.name;
14677            value = trim(attr.value);
14678
14679            // support ngAttr attribute binding
14680            ngAttrName = directiveNormalize(name);
14681            isNgAttr = NG_ATTR_BINDING.test(ngAttrName);
14682            if (isNgAttr) {
14683              name = name.replace(PREFIX_REGEXP, '')
14684                .substr(8).replace(/_(.)/g, function(match, letter) {
14685                  return letter.toUpperCase();
14686                });
14687            }
14688
14689            var multiElementMatch = ngAttrName.match(MULTI_ELEMENT_DIR_RE);
14690            if (multiElementMatch && directiveIsMultiElement(multiElementMatch[1])) {
14691              attrStartName = name;
14692              attrEndName = name.substr(0, name.length - 5) + 'end';
14693              name = name.substr(0, name.length - 6);
14694            }
14695
14696            nName = directiveNormalize(name.toLowerCase());
14697            attrsMap[nName] = name;
14698            if (isNgAttr || !attrs.hasOwnProperty(nName)) {
14699                attrs[nName] = value;
14700                if (getBooleanAttrName(node, nName)) {
14701                  attrs[nName] = true; // presence means true
14702                }
14703            }
14704            addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
14705            addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
14706                          attrEndName);
14707          }
14708
14709          if (nodeName === 'input' && node.getAttribute('type') === 'hidden') {
14710            // Hidden input elements can have strange behaviour when navigating back to the page
14711            // This tells the browser not to try to cache and reinstate previous values
14712            node.setAttribute('autocomplete', 'off');
14713          }
14714
14715          // use class as directive
14716          if (!cssClassDirectivesEnabled) break;
14717          className = node.className;
14718          if (isObject(className)) {
14719              // Maybe SVGAnimatedString
14720              className = className.animVal;
14721          }
14722          if (isString(className) && className !== '') {
14723            while ((match = CLASS_DIRECTIVE_REGEXP.exec(className))) {
14724              nName = directiveNormalize(match[2]);
14725              if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {
14726                attrs[nName] = trim(match[3]);
14727              }
14728              className = className.substr(match.index + match[0].length);
14729            }
14730          }
14731          break;
14732        case NODE_TYPE_TEXT: /* Text Node */
14733          if (msie === 11) {
14734            // Workaround for #11781
14735            while (node.parentNode && node.nextSibling && node.nextSibling.nodeType === NODE_TYPE_TEXT) {
14736              node.nodeValue = node.nodeValue + node.nextSibling.nodeValue;
14737              node.parentNode.removeChild(node.nextSibling);
14738            }
14739          }
14740          addTextInterpolateDirective(directives, node.nodeValue);
14741          break;
14742        case NODE_TYPE_COMMENT: /* Comment */
14743          if (!commentDirectivesEnabled) break;
14744          collectCommentDirectives(node, directives, attrs, maxPriority, ignoreDirective);
14745          break;
14746      }
14747
14748      directives.sort(byPriority);
14749      return directives;
14750    }
14751
14752    function collectCommentDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
14753      // function created because of performance, try/catch disables
14754      // the optimization of the whole function #14848
14755      try {
14756        var match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
14757        if (match) {
14758          var nName = directiveNormalize(match[1]);
14759          if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
14760            attrs[nName] = trim(match[2]);
14761          }
14762        }
14763      } catch (e) {
14764        // turns out that under some circumstances IE9 throws errors when one attempts to read
14765        // comment's node value.
14766        // Just ignore it and continue. (Can't seem to reproduce in test case.)
14767      }
14768    }
14769
14770    /**
14771     * Given a node with a directive-start it collects all of the siblings until it finds
14772     * directive-end.
14773     * @param node
14774     * @param attrStart
14775     * @param attrEnd
14776     * @returns {*}
14777     */
14778    function groupScan(node, attrStart, attrEnd) {
14779      var nodes = [];
14780      var depth = 0;
14781      if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
14782        do {
14783          if (!node) {
14784            throw $compileMinErr('uterdir',
14785                      'Unterminated attribute, found \'{0}\' but no matching \'{1}\' found.',
14786                      attrStart, attrEnd);
14787          }
14788          if (node.nodeType === NODE_TYPE_ELEMENT) {
14789            if (node.hasAttribute(attrStart)) depth++;
14790            if (node.hasAttribute(attrEnd)) depth--;
14791          }
14792          nodes.push(node);
14793          node = node.nextSibling;
14794        } while (depth > 0);
14795      } else {
14796        nodes.push(node);
14797      }
14798
14799      return jqLite(nodes);
14800    }
14801
14802    /**
14803     * Wrapper for linking function which converts normal linking function into a grouped
14804     * linking function.
14805     * @param linkFn
14806     * @param attrStart
14807     * @param attrEnd
14808     * @returns {Function}
14809     */
14810    function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
14811      return function groupedElementsLink(scope, element, attrs, controllers, transcludeFn) {
14812        element = groupScan(element[0], attrStart, attrEnd);
14813        return linkFn(scope, element, attrs, controllers, transcludeFn);
14814      };
14815    }
14816
14817    /**
14818     * A function generator that is used to support both eager and lazy compilation
14819     * linking function.
14820     * @param eager
14821     * @param $compileNodes
14822     * @param transcludeFn
14823     * @param maxPriority
14824     * @param ignoreDirective
14825     * @param previousCompileContext
14826     * @returns {Function}
14827     */
14828    function compilationGenerator(eager, $compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext) {
14829      var compiled;
14830
14831      if (eager) {
14832        return compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
14833      }
14834      return /** @this */ function lazyCompilation() {
14835        if (!compiled) {
14836          compiled = compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
14837
14838          // Null out all of these references in order to make them eligible for garbage collection
14839          // since this is a potentially long lived closure
14840          $compileNodes = transcludeFn = previousCompileContext = null;
14841        }
14842        return compiled.apply(this, arguments);
14843      };
14844    }
14845
14846    /**
14847     * Once the directives have been collected, their compile functions are executed. This method
14848     * is responsible for inlining directive templates as well as terminating the application
14849     * of the directives if the terminal directive has been reached.
14850     *
14851     * @param {Array} directives Array of collected directives to execute their compile function.
14852     *        this needs to be pre-sorted by priority order.
14853     * @param {Node} compileNode The raw DOM node to apply the compile functions to
14854     * @param {Object} templateAttrs The shared attribute function
14855     * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
14856     *                                                  scope argument is auto-generated to the new
14857     *                                                  child of the transcluded parent scope.
14858     * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
14859     *                              argument has the root jqLite array so that we can replace nodes
14860     *                              on it.
14861     * @param {Object=} originalReplaceDirective An optional directive that will be ignored when
14862     *                                           compiling the transclusion.
14863     * @param {Array.<Function>} preLinkFns
14864     * @param {Array.<Function>} postLinkFns
14865     * @param {Object} previousCompileContext Context used for previous compilation of the current
14866     *                                        node
14867     * @returns {Function} linkFn
14868     */
14869    function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
14870                                   jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,
14871                                   previousCompileContext) {
14872      previousCompileContext = previousCompileContext || {};
14873
14874      var terminalPriority = -Number.MAX_VALUE,
14875          newScopeDirective = previousCompileContext.newScopeDirective,
14876          controllerDirectives = previousCompileContext.controllerDirectives,
14877          newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
14878          templateDirective = previousCompileContext.templateDirective,
14879          nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
14880          hasTranscludeDirective = false,
14881          hasTemplate = false,
14882          hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
14883          $compileNode = templateAttrs.$$element = jqLite(compileNode),
14884          directive,
14885          directiveName,
14886          $template,
14887          replaceDirective = originalReplaceDirective,
14888          childTranscludeFn = transcludeFn,
14889          linkFn,
14890          didScanForMultipleTransclusion = false,
14891          mightHaveMultipleTransclusionError = false,
14892          directiveValue;
14893
14894      // executes all directives on the current element
14895      for (var i = 0, ii = directives.length; i < ii; i++) {
14896        directive = directives[i];
14897        var attrStart = directive.$$start;
14898        var attrEnd = directive.$$end;
14899
14900        // collect multiblock sections
14901        if (attrStart) {
14902          $compileNode = groupScan(compileNode, attrStart, attrEnd);
14903        }
14904        $template = undefined;
14905
14906        if (terminalPriority > directive.priority) {
14907          break; // prevent further processing of directives
14908        }
14909
14910        directiveValue = directive.scope;
14911
14912        if (directiveValue) {
14913
14914          // skip the check for directives with async templates, we'll check the derived sync
14915          // directive when the template arrives
14916          if (!directive.templateUrl) {
14917            if (isObject(directiveValue)) {
14918              // This directive is trying to add an isolated scope.
14919              // Check that there is no scope of any kind already
14920              assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective,
14921                                directive, $compileNode);
14922              newIsolateScopeDirective = directive;
14923            } else {
14924              // This directive is trying to add a child scope.
14925              // Check that there is no isolated scope already
14926              assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,
14927                                $compileNode);
14928            }
14929          }
14930
14931          newScopeDirective = newScopeDirective || directive;
14932        }
14933
14934        directiveName = directive.name;
14935
14936        // If we encounter a condition that can result in transclusion on the directive,
14937        // then scan ahead in the remaining directives for others that may cause a multiple
14938        // transclusion error to be thrown during the compilation process.  If a matching directive
14939        // is found, then we know that when we encounter a transcluded directive, we need to eagerly
14940        // compile the `transclude` function rather than doing it lazily in order to throw
14941        // exceptions at the correct time
14942        if (!didScanForMultipleTransclusion && ((directive.replace && (directive.templateUrl || directive.template))
14943            || (directive.transclude && !directive.$$tlb))) {
14944                var candidateDirective;
14945
14946                for (var scanningIndex = i + 1; (candidateDirective = directives[scanningIndex++]);) {
14947                    if ((candidateDirective.transclude && !candidateDirective.$$tlb)
14948                        || (candidateDirective.replace && (candidateDirective.templateUrl || candidateDirective.template))) {
14949                        mightHaveMultipleTransclusionError = true;
14950                        break;
14951                    }
14952                }
14953
14954                didScanForMultipleTransclusion = true;
14955        }
14956
14957        if (!directive.templateUrl && directive.controller) {
14958          controllerDirectives = controllerDirectives || createMap();
14959          assertNoDuplicate('\'' + directiveName + '\' controller',
14960              controllerDirectives[directiveName], directive, $compileNode);
14961          controllerDirectives[directiveName] = directive;
14962        }
14963
14964        directiveValue = directive.transclude;
14965
14966        if (directiveValue) {
14967          hasTranscludeDirective = true;
14968
14969          // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
14970          // This option should only be used by directives that know how to safely handle element transclusion,
14971          // where the transcluded nodes are added or replaced after linking.
14972          if (!directive.$$tlb) {
14973            assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
14974            nonTlbTranscludeDirective = directive;
14975          }
14976
14977          if (directiveValue === 'element') {
14978            hasElementTranscludeDirective = true;
14979            terminalPriority = directive.priority;
14980            $template = $compileNode;
14981            $compileNode = templateAttrs.$$element =
14982                jqLite(compile.$$createComment(directiveName, templateAttrs[directiveName]));
14983            compileNode = $compileNode[0];
14984            replaceWith(jqCollection, sliceArgs($template), compileNode);
14985
14986            // Support: Chrome < 50
14987            // https://github.com/angular/angular.js/issues/14041
14988
14989            // In the versions of V8 prior to Chrome 50, the document fragment that is created
14990            // in the `replaceWith` function is improperly garbage collected despite still
14991            // being referenced by the `parentNode` property of all of the child nodes.  By adding
14992            // a reference to the fragment via a different property, we can avoid that incorrect
14993            // behavior.
14994            // TODO: remove this line after Chrome 50 has been released
14995            $template[0].$$parentNode = $template[0].parentNode;
14996
14997            childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, terminalPriority,
14998                                        replaceDirective && replaceDirective.name, {
14999                                          // Don't pass in:
15000                                          // - controllerDirectives - otherwise we'll create duplicates controllers
15001                                          // - newIsolateScopeDirective or templateDirective - comb
vendor: 12,791 bytes, lines 15001-15281
15001ining templates with
15002                                          //   element transclusion doesn't make sense.
15003                                          //
15004                                          // We need only nonTlbTranscludeDirective so that we prevent putting transclusion
15005                                          // on the same element more than once.
15006                                          nonTlbTranscludeDirective: nonTlbTranscludeDirective
15007                                        });
15008          } else {
15009
15010            var slots = createMap();
15011
15012            $template = jqLite(jqLiteClone(compileNode)).contents();
15013
15014            if (isObject(directiveValue)) {
15015
15016              // We have transclusion slots,
15017              // collect them up, compile them and store their transclusion functions
15018              $template = [];
15019
15020              var slotMap = createMap();
15021              var filledSlots = createMap();
15022
15023              // Parse the element selectors
15024              forEach(directiveValue, function(elementSelector, slotName) {
15025                // If an element selector starts with a ? then it is optional
15026                var optional = (elementSelector.charAt(0) === '?');
15027                elementSelector = optional ? elementSelector.substring(1) : elementSelector;
15028
15029                slotMap[elementSelector] = slotName;
15030
15031                // We explicitly assign `null` since this implies that a slot was defined but not filled.
15032                // Later when calling boundTransclusion functions with a slot name we only error if the
15033                // slot is `undefined`
15034                slots[slotName] = null;
15035
15036                // filledSlots contains `true` for all slots that are either optional or have been
15037                // filled. This is used to check that we have not missed any required slots
15038                filledSlots[slotName] = optional;
15039              });
15040
15041              // Add the matching elements into their slot
15042              forEach($compileNode.contents(), function(node) {
15043                var slotName = slotMap[directiveNormalize(nodeName_(node))];
15044                if (slotName) {
15045                  filledSlots[slotName] = true;
15046                  slots[slotName] = slots[slotName] || [];
15047                  slots[slotName].push(node);
15048                } else {
15049                  $template.push(node);
15050                }
15051              });
15052
15053              // Check for required slots that were not filled
15054              forEach(filledSlots, function(filled, slotName) {
15055                if (!filled) {
15056                  throw $compileMinErr('reqslot', 'Required transclusion slot `{0}` was not filled.', slotName);
15057                }
15058              });
15059
15060              for (var slotName in slots) {
15061                if (slots[slotName]) {
15062                  // Only define a transclusion function if the slot was filled
15063                  slots[slotName] = compilationGenerator(mightHaveMultipleTransclusionError, slots[slotName], transcludeFn);
15064                }
15065              }
15066            }
15067
15068            $compileNode.empty(); // clear contents
15069            childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, undefined,
15070                undefined, { needsNewScope: directive.$$isolateScope || directive.$$newScope});
15071            childTranscludeFn.$$slots = slots;
15072          }
15073        }
15074
15075        if (directive.template) {
15076          hasTemplate = true;
15077          assertNoDuplicate('template', templateDirective, directive, $compileNode);
15078          templateDirective = directive;
15079
15080          directiveValue = (isFunction(directive.template))
15081              ? directive.template($compileNode, templateAttrs)
15082              : directive.template;
15083
15084          directiveValue = denormalizeTemplate(directiveValue);
15085
15086          if (directive.replace) {
15087            replaceDirective = directive;
15088            if (jqLiteIsTextNode(directiveValue)) {
15089              $template = [];
15090            } else {
15091              $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
15092            }
15093            compileNode = $template[0];
15094
15095            if ($template.length !== 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
15096              throw $compileMinErr('tplrt',
15097                  'Template for directive \'{0}\' must have exactly one root element. {1}',
15098                  directiveName, '');
15099            }
15100
15101            replaceWith(jqCollection, $compileNode, compileNode);
15102
15103            var newTemplateAttrs = {$attr: {}};
15104
15105            // combine directives from the original node and from the template:
15106            // - take the array of directives for this element
15107            // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)
15108            // - collect directives from the template and sort them by priority
15109            // - combine directives as: processed + template + unprocessed
15110            var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
15111            var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
15112
15113            if (newIsolateScopeDirective || newScopeDirective) {
15114              // The original directive caused the current element to be replaced but this element
15115              // also needs to have a new scope, so we need to tell the template directives
15116              // that they would need to get their scope from further up, if they require transclusion
15117              markDirectiveScope(templateDirectives, newIsolateScopeDirective, newScopeDirective);
15118            }
15119            directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
15120            mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
15121
15122            ii = directives.length;
15123          } else {
15124            $compileNode.html(directiveValue);
15125          }
15126        }
15127
15128        if (directive.templateUrl) {
15129          hasTemplate = true;
15130          assertNoDuplicate('template', templateDirective, directive, $compileNode);
15131          templateDirective = directive;
15132
15133          if (directive.replace) {
15134            replaceDirective = directive;
15135          }
15136
15137          // eslint-disable-next-line no-func-assign
15138          nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
15139              templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
15140                controllerDirectives: controllerDirectives,
15141                newScopeDirective: (newScopeDirective !== directive) && newScopeDirective,
15142                newIsolateScopeDirective: newIsolateScopeDirective,
15143                templateDirective: templateDirective,
15144                nonTlbTranscludeDirective: nonTlbTranscludeDirective
15145              });
15146          ii = directives.length;
15147        } else if (directive.compile) {
15148          try {
15149            linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
15150            var context = directive.$$originalDirective || directive;
15151            if (isFunction(linkFn)) {
15152              addLinkFns(null, bind(context, linkFn), attrStart, attrEnd);
15153            } else if (linkFn) {
15154              addLinkFns(bind(context, linkFn.pre), bind(context, linkFn.post), attrStart, attrEnd);
15155            }
15156          } catch (e) {
15157            $exceptionHandler(e, startingTag($compileNode));
15158          }
15159        }
15160
15161        if (directive.terminal) {
15162          nodeLinkFn.terminal = true;
15163          terminalPriority = Math.max(terminalPriority, directive.priority);
15164        }
15165
15166      }
15167
15168      nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
15169      nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
15170      nodeLinkFn.templateOnThisElement = hasTemplate;
15171      nodeLinkFn.transclude = childTranscludeFn;
15172
15173      previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
15174
15175      // might be normal or delayed nodeLinkFn depending on if templateUrl is present
15176      return nodeLinkFn;
15177
15178      ////////////////////
15179
15180      function addLinkFns(pre, post, attrStart, attrEnd) {
15181        if (pre) {
15182          if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
15183          pre.require = directive.require;
15184          pre.directiveName = directiveName;
15185          if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
15186            pre = cloneAndAnnotateFn(pre, {isolateScope: true});
15187          }
15188          preLinkFns.push(pre);
15189        }
15190        if (post) {
15191          if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
15192          post.require = directive.require;
15193          post.directiveName = directiveName;
15194          if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
15195            post = cloneAndAnnotateFn(post, {isolateScope: true});
15196          }
15197          postLinkFns.push(post);
15198        }
15199      }
15200
15201      function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
15202        var i, ii, linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element,
15203            attrs, scopeBindingInfo;
15204
15205        if (compileNode === linkNode) {
15206          attrs = templateAttrs;
15207          $element = templateAttrs.$$element;
15208        } else {
15209          $element = jqLite(linkNode);
15210          attrs = new Attributes($element, templateAttrs);
15211        }
15212
15213        controllerScope = scope;
15214        if (newIsolateScopeDirective) {
15215          isolateScope = scope.$new(true);
15216        } else if (newScopeDirective) {
15217          controllerScope = scope.$parent;
15218        }
15219
15220        if (boundTranscludeFn) {
15221          // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn`
15222          // is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
15223          transcludeFn = controllersBoundTransclude;
15224          transcludeFn.$$boundTransclude = boundTranscludeFn;
15225          // expose the slots on the `$transclude` function
15226          transcludeFn.isSlotFilled = function(slotName) {
15227            return !!boundTranscludeFn.$$slots[slotName];
15228          };
15229        }
15230
15231        if (controllerDirectives) {
15232          elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope, newIsolateScopeDirective);
15233        }
15234
15235        if (newIsolateScopeDirective) {
15236          // Initialize isolate scope bindings for new isolate scope directive.
15237          compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||
15238              templateDirective === newIsolateScopeDirective.$$originalDirective)));
15239          compile.$$addScopeClass($element, true);
15240          isolateScope.$$isolateBindings =
15241              newIsolateScopeDirective.$$isolateBindings;
15242          scopeBindingInfo = initializeDirectiveBindings(scope, attrs, isolateScope,
15243                                        isolateScope.$$isolateBindings,
15244                                        newIsolateScopeDirective);
15245          if (scopeBindingInfo.removeWatches) {
15246            isolateScope.$on('$destroy', scopeBindingInfo.removeWatches);
15247          }
15248        }
15249
15250        // Initialize bindToController bindings
15251        for (var name in elementControllers) {
15252          var controllerDirective = controllerDirectives[name];
15253          var controller = elementControllers[name];
15254          var bindings = controllerDirective.$$bindings.bindToController;
15255
15256          if (preAssignBindingsEnabled) {
15257            if (bindings) {
15258              controller.bindingInfo =
15259                initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
15260            } else {
15261              controller.bindingInfo = {};
15262            }
15263
15264            var controllerResult = controller();
15265            if (controllerResult !== controller.instance) {
15266              // If the controller constructor has a return value, overwrite the instance
15267              // from setupControllers
15268              controller.instance = controllerResult;
15269              $element.data('$' + controllerDirective.name + 'Controller', controllerResult);
15270              if (controller.bindingInfo.removeWatches) {
15271                controller.bindingInfo.removeWatches();
15272              }
15273              controller.bindingInfo =
15274                initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
15275            }
15276          } else {
15277            controller.instance = controller();
15278            $element.data('$' + controllerDirective.name + 'Controller', controller.instance);
15279            controller.bindingInfo =
15280              initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
15281          }
vendor: 5,779 bytes, lines 15282-15422
15282        }
15283
15284        // Bind the required controllers to the controller, if `require` is an object and `bindToController` is truthy
15285        forEach(controllerDirectives, function(controllerDirective, name) {
15286          var require = controllerDirective.require;
15287          if (controllerDirective.bindToController && !isArray(require) && isObject(require)) {
15288            extend(elementControllers[name].instance, getControllers(name, require, $element, elementControllers));
15289          }
15290        });
15291
15292        // Handle the init and destroy lifecycle hooks on all controllers that have them
15293        forEach(elementControllers, function(controller) {
15294          var controllerInstance = controller.instance;
15295          if (isFunction(controllerInstance.$onChanges)) {
15296            try {
15297              controllerInstance.$onChanges(controller.bindingInfo.initialChanges);
15298            } catch (e) {
15299              $exceptionHandler(e);
15300            }
15301          }
15302          if (isFunction(controllerInstance.$onInit)) {
15303            try {
15304              controllerInstance.$onInit();
15305            } catch (e) {
15306              $exceptionHandler(e);
15307            }
15308          }
15309          if (isFunction(controllerInstance.$doCheck)) {
15310            controllerScope.$watch(function() { controllerInstance.$doCheck(); });
15311            controllerInstance.$doCheck();
15312          }
15313          if (isFunction(controllerInstance.$onDestroy)) {
15314            controllerScope.$on('$destroy', function callOnDestroyHook() {
15315              controllerInstance.$onDestroy();
15316            });
15317          }
15318        });
15319
15320        // PRELINKING
15321        for (i = 0, ii = preLinkFns.length; i < ii; i++) {
15322          linkFn = preLinkFns[i];
15323          invokeLinkFn(linkFn,
15324              linkFn.isolateScope ? isolateScope : scope,
15325              $element,
15326              attrs,
15327              linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
15328              transcludeFn
15329          );
15330        }
15331
15332        // RECURSION
15333        // We only pass the isolate scope, if the isolate directive has a template,
15334        // otherwise the child elements do not belong to the isolate directive.
15335        var scopeToChild = scope;
15336        if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {
15337          scopeToChild = isolateScope;
15338        }
15339        if (childLinkFn) {
15340          childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
15341        }
15342
15343        // POSTLINKING
15344        for (i = postLinkFns.length - 1; i >= 0; i--) {
15345          linkFn = postLinkFns[i];
15346          invokeLinkFn(linkFn,
15347              linkFn.isolateScope ? isolateScope : scope,
15348              $element,
15349              attrs,
15350              linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
15351              transcludeFn
15352          );
15353        }
15354
15355        // Trigger $postLink lifecycle hooks
15356        forEach(elementControllers, function(controller) {
15357          var controllerInstance = controller.instance;
15358          if (isFunction(controllerInstance.$postLink)) {
15359            controllerInstance.$postLink();
15360          }
15361        });
15362
15363        // This is the function that is injected as `$transclude`.
15364        // Note: all arguments are optional!
15365        function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement, slotName) {
15366          var transcludeControllers;
15367          // No scope passed in:
15368          if (!isScope(scope)) {
15369            slotName = futureParentElement;
15370            futureParentElement = cloneAttachFn;
15371            cloneAttachFn = scope;
15372            scope = undefined;
15373          }
15374
15375          if (hasElementTranscludeDirective) {
15376            transcludeControllers = elementControllers;
15377          }
15378          if (!futureParentElement) {
15379            futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
15380          }
15381          if (slotName) {
15382            // slotTranscludeFn can be one of three things:
15383            //  * a transclude function - a filled slot
15384            //  * `null` - an optional slot that was not filled
15385            //  * `undefined` - a slot that was not declared (i.e. invalid)
15386            var slotTranscludeFn = boundTranscludeFn.$$slots[slotName];
15387            if (slotTranscludeFn) {
15388              return slotTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
15389            } else if (isUndefined(slotTranscludeFn)) {
15390              throw $compileMinErr('noslot',
15391               'No parent directive that requires a transclusion with slot name "{0}". ' +
15392               'Element: {1}',
15393               slotName, startingTag($element));
15394            }
15395          } else {
15396            return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
15397          }
15398        }
15399      }
15400    }
15401
15402    function getControllers(directiveName, require, $element, elementControllers) {
15403      var value;
15404
15405      if (isString(require)) {
15406        var match = require.match(REQUIRE_PREFIX_REGEXP);
15407        var name = require.substring(match[0].length);
15408        var inheritType = match[1] || match[3];
15409        var optional = match[2] === '?';
15410
15411        //If only parents then start at the parent element
15412        if (inheritType === '^^') {
15413          $element = $element.parent();
15414        //Otherwise attempt getting the controller from elementControllers in case
15415        //the element is transcluded (and has no data) and to avoid .data if possible
15416        } else {
15417          value = elementControllers && elementControllers[name];
15418          value = value && value.instance;
15419        }
15420
15421        if (!value) {
15422          var dataName = '$' + name + 'Controller';
vendor: 38,712 bytes, lines 15423-16434
15423          value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName);
15424        }
15425
15426        if (!value && !optional) {
15427          throw $compileMinErr('ctreq',
15428              'Controller \'{0}\', required by directive \'{1}\', can\'t be found!',
15429              name, directiveName);
15430        }
15431      } else if (isArray(require)) {
15432        value = [];
15433        for (var i = 0, ii = require.length; i < ii; i++) {
15434          value[i] = getControllers(directiveName, require[i], $element, elementControllers);
15435        }
15436      } else if (isObject(require)) {
15437        value = {};
15438        forEach(require, function(controller, property) {
15439          value[property] = getControllers(directiveName, controller, $element, elementControllers);
15440        });
15441      }
15442
15443      return value || null;
15444    }
15445
15446    function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope, newIsolateScopeDirective) {
15447      var elementControllers = createMap();
15448      for (var controllerKey in controllerDirectives) {
15449        var directive = controllerDirectives[controllerKey];
15450        var locals = {
15451          $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
15452          $element: $element,
15453          $attrs: attrs,
15454          $transclude: transcludeFn
15455        };
15456
15457        var controller = directive.controller;
15458        if (controller === '@') {
15459          controller = attrs[directive.name];
15460        }
15461
15462        var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
15463
15464        // For directives with element transclusion the element is a comment.
15465        // In this case .data will not attach any data.
15466        // Instead, we save the controllers for the element in a local hash and attach to .data
15467        // later, once we have the actual element.
15468        elementControllers[directive.name] = controllerInstance;
15469        $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
15470      }
15471      return elementControllers;
15472    }
15473
15474    // Depending upon the context in which a directive finds itself it might need to have a new isolated
15475    // or child scope created. For instance:
15476    // * if the directive has been pulled into a template because another directive with a higher priority
15477    // asked for element transclusion
15478    // * if the directive itself asks for transclusion but it is at the root of a template and the original
15479    // element was replaced. See https://github.com/angular/angular.js/issues/12936
15480    function markDirectiveScope(directives, isolateScope, newScope) {
15481      for (var j = 0, jj = directives.length; j < jj; j++) {
15482        directives[j] = inherit(directives[j], {$$isolateScope: isolateScope, $$newScope: newScope});
15483      }
15484    }
15485
15486    /**
15487     * looks up the directive and decorates it with exception handling and proper parameters. We
15488     * call this the boundDirective.
15489     *
15490     * @param {string} name name of the directive to look up.
15491     * @param {string} location The directive must be found in specific format.
15492     *   String containing any of theses characters:
15493     *
15494     *   * `E`: element name
15495     *   * `A': attribute
15496     *   * `C`: class
15497     *   * `M`: comment
15498     * @returns {boolean} true if directive was added.
15499     */
15500    function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,
15501                          endAttrName) {
15502      if (name === ignoreDirective) return null;
15503      var match = null;
15504      if (hasDirectives.hasOwnProperty(name)) {
15505        for (var directive, directives = $injector.get(name + Suffix),
15506            i = 0, ii = directives.length; i < ii; i++) {
15507          directive = directives[i];
15508          if ((isUndefined(maxPriority) || maxPriority > directive.priority) &&
15509               directive.restrict.indexOf(location) !== -1) {
15510            if (startAttrName) {
15511              directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
15512            }
15513            if (!directive.$$bindings) {
15514              var bindings = directive.$$bindings =
15515                  parseDirectiveBindings(directive, directive.name);
15516              if (isObject(bindings.isolateScope)) {
15517                directive.$$isolateBindings = bindings.isolateScope;
15518              }
15519            }
15520            tDirectives.push(directive);
15521            match = directive;
15522          }
15523        }
15524      }
15525      return match;
15526    }
15527
15528
15529    /**
15530     * looks up the directive and returns true if it is a multi-element directive,
15531     * and therefore requires DOM nodes between -start and -end markers to be grouped
15532     * together.
15533     *
15534     * @param {string} name name of the directive to look up.
15535     * @returns true if directive was registered as multi-element.
15536     */
15537    function directiveIsMultiElement(name) {
15538      if (hasDirectives.hasOwnProperty(name)) {
15539        for (var directive, directives = $injector.get(name + Suffix),
15540            i = 0, ii = directives.length; i < ii; i++) {
15541          directive = directives[i];
15542          if (directive.multiElement) {
15543            return true;
15544          }
15545        }
15546      }
15547      return false;
15548    }
15549
15550    /**
15551     * When the element is replaced with HTML template then the new attributes
15552     * on the template need to be merged with the existing attributes in the DOM.
15553     * The desired effect is to have both of the attributes present.
15554     *
15555     * @param {object} dst destination attributes (original DOM)
15556     * @param {object} src source attributes (from the directive template)
15557     */
15558    function mergeTemplateAttributes(dst, src) {
15559      var srcAttr = src.$attr,
15560          dstAttr = dst.$attr;
15561
15562      // reapply the old attributes to the new element
15563      forEach(dst, function(value, key) {
15564        if (key.charAt(0) !== '$') {
15565          if (src[key] && src[key] !== value) {
15566            value += (key === 'style' ? ';' : ' ') + src[key];
15567          }
15568          dst.$set(key, value, true, srcAttr[key]);
15569        }
15570      });
15571
15572      // copy the new attributes on the old attrs object
15573      forEach(src, function(value, key) {
15574        // Check if we already set this attribute in the loop above.
15575        // `dst` will never contain hasOwnProperty as DOM parser won't let it.
15576        // You will get an "InvalidCharacterError: DOM Exception 5" error if you
15577        // have an attribute like "has-own-property" or "data-has-own-property", etc.
15578        if (!dst.hasOwnProperty(key) && key.charAt(0) !== '$') {
15579          dst[key] = value;
15580
15581          if (key !== 'class' && key !== 'style') {
15582            dstAttr[key] = srcAttr[key];
15583          }
15584        }
15585      });
15586    }
15587
15588
15589    function compileTemplateUrl(directives, $compileNode, tAttrs,
15590        $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
15591      var linkQueue = [],
15592          afterTemplateNodeLinkFn,
15593          afterTemplateChildLinkFn,
15594          beforeTemplateCompileNode = $compileNode[0],
15595          origAsyncDirective = directives.shift(),
15596          derivedSyncDirective = inherit(origAsyncDirective, {
15597            templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
15598          }),
15599          templateUrl = (isFunction(origAsyncDirective.templateUrl))
15600              ? origAsyncDirective.templateUrl($compileNode, tAttrs)
15601              : origAsyncDirective.templateUrl,
15602          templateNamespace = origAsyncDirective.templateNamespace;
15603
15604      $compileNode.empty();
15605
15606      $templateRequest(templateUrl)
15607        .then(function(content) {
15608          var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
15609
15610          content = denormalizeTemplate(content);
15611
15612          if (origAsyncDirective.replace) {
15613            if (jqLiteIsTextNode(content)) {
15614              $template = [];
15615            } else {
15616              $template = removeComments(wrapTemplate(templateNamespace, trim(content)));
15617            }
15618            compileNode = $template[0];
15619
15620            if ($template.length !== 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
15621              throw $compileMinErr('tplrt',
15622                  'Template for directive \'{0}\' must have exactly one root element. {1}',
15623                  origAsyncDirective.name, templateUrl);
15624            }
15625
15626            tempTemplateAttrs = {$attr: {}};
15627            replaceWith($rootElement, $compileNode, compileNode);
15628            var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
15629
15630            if (isObject(origAsyncDirective.scope)) {
15631              // the original directive that caused the template to be loaded async required
15632              // an isolate scope
15633              markDirectiveScope(templateDirectives, true);
15634            }
15635            directives = templateDirectives.concat(directives);
15636            mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
15637          } else {
15638            compileNode = beforeTemplateCompileNode;
15639            $compileNode.html(content);
15640          }
15641
15642          directives.unshift(derivedSyncDirective);
15643
15644          afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
15645              childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
15646              previousCompileContext);
15647          forEach($rootElement, function(node, i) {
15648            if (node === compileNode) {
15649              $rootElement[i] = $compileNode[0];
15650            }
15651          });
15652          afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
15653
15654          while (linkQueue.length) {
15655            var scope = linkQueue.shift(),
15656                beforeTemplateLinkNode = linkQueue.shift(),
15657                linkRootElement = linkQueue.shift(),
15658                boundTranscludeFn = linkQueue.shift(),
15659                linkNode = $compileNode[0];
15660
15661            if (scope.$$destroyed) continue;
15662
15663            if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
15664              var oldClasses = beforeTemplateLinkNode.className;
15665
15666              if (!(previousCompileContext.hasElementTranscludeDirective &&
15667                  origAsyncDirective.replace)) {
15668                // it was cloned therefore we have to clone as well.
15669                linkNode = jqLiteClone(compileNode);
15670              }
15671              replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
15672
15673              // Copy in CSS classes from original node
15674              safeAddClass(jqLite(linkNode), oldClasses);
15675            }
15676            if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
15677              childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
15678            } else {
15679              childBoundTranscludeFn = boundTranscludeFn;
15680            }
15681            afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
15682              childBoundTranscludeFn);
15683          }
15684          linkQueue = null;
15685        });
15686
15687      return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
15688        var childBoundTranscludeFn = boundTranscludeFn;
15689        if (scope.$$destroyed) return;
15690        if (linkQueue) {
15691          linkQueue.push(scope,
15692                         node,
15693                         rootElement,
15694                         childBoundTranscludeFn);
15695        } else {
15696          if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
15697            childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
15698          }
15699          afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn);
15700        }
15701      };
15702    }
15703
15704
15705    /**
15706     * Sorting function for bound directives.
15707     */
15708    function byPriority(a, b) {
15709      var diff = b.priority - a.priority;
15710      if (diff !== 0) return diff;
15711      if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;
15712      return a.index - b.index;
15713    }
15714
15715    function assertNoDuplicate(what, previousDirective, directive, element) {
15716
15717      function wrapModuleNameIfDefined(moduleName) {
15718        return moduleName ?
15719          (' (module: ' + moduleName + ')') :
15720          '';
15721      }
15722
15723      if (previousDirective) {
15724        throw $compileMinErr('multidir', 'Multiple directives [{0}{1}, {2}{3}] asking for {4} on: {5}',
15725            previousDirective.name, wrapModuleNameIfDefined(previousDirective.$$moduleName),
15726            directive.name, wrapModuleNameIfDefined(directive.$$moduleName), what, startingTag(element));
15727      }
15728    }
15729
15730
15731    function addTextInterpolateDirective(directives, text) {
15732      var interpolateFn = $interpolate(text, true);
15733      if (interpolateFn) {
15734        directives.push({
15735          priority: 0,
15736          compile: function textInterpolateCompileFn(templateNode) {
15737            var templateNodeParent = templateNode.parent(),
15738                hasCompileParent = !!templateNodeParent.length;
15739
15740            // When transcluding a template that has bindings in the root
15741            // we don't have a parent and thus need to add the class during linking fn.
15742            if (hasCompileParent) compile.$$addBindingClass(templateNodeParent);
15743
15744            return function textInterpolateLinkFn(scope, node) {
15745              var parent = node.parent();
15746              if (!hasCompileParent) compile.$$addBindingClass(parent);
15747              compile.$$addBindingInfo(parent, interpolateFn.expressions);
15748              scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
15749                node[0].nodeValue = value;
15750              });
15751            };
15752          }
15753        });
15754      }
15755    }
15756
15757
15758    function wrapTemplate(type, template) {
15759      type = lowercase(type || 'html');
15760      switch (type) {
15761      case 'svg':
15762      case 'math':
15763        var wrapper = window.document.createElement('div');
15764        wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
15765        return wrapper.childNodes[0].childNodes;
15766      default:
15767        return template;
15768      }
15769    }
15770
15771
15772    function getTrustedContext(node, attrNormalizedName) {
15773      if (attrNormalizedName === 'srcdoc') {
15774        return $sce.HTML;
15775      }
15776      var tag = nodeName_(node);
15777      // All tags with src attributes require a RESOURCE_URL value, except for
15778      // img and various html5 media tags.
15779      if (attrNormalizedName === 'src' || attrNormalizedName === 'ngSrc') {
15780        if (['img', 'video', 'audio', 'source', 'track'].indexOf(tag) === -1) {
15781          return $sce.RESOURCE_URL;
15782        }
15783      // maction[xlink:href] can source SVG.  It's not limited to <maction>.
15784      } else if (attrNormalizedName === 'xlinkHref' ||
15785          (tag === 'form' && attrNormalizedName === 'action')
15786      ) {
15787        return $sce.RESOURCE_URL;
15788      }
15789    }
15790
15791
15792    function addAttrInterpolateDirective(node, directives, value, name, isNgAttr) {
15793      var trustedContext = getTrustedContext(node, name);
15794      var mustHaveExpression = !isNgAttr;
15795      var allOrNothing = ALL_OR_NOTHING_ATTRS[name] || isNgAttr;
15796
15797      var interpolateFn = $interpolate(value, mustHaveExpression, trustedContext, allOrNothing);
15798
15799      // no interpolation found -> ignore
15800      if (!interpolateFn) return;
15801
15802      if (name === 'multiple' && nodeName_(node) === 'select') {
15803        throw $compileMinErr('selmulti',
15804            'Binding to the \'multiple\' attribute is not supported. Element: {0}',
15805            startingTag(node));
15806      }
15807
15808      directives.push({
15809        priority: 100,
15810        compile: function() {
15811            return {
15812              pre: function attrInterpolatePreLinkFn(scope, element, attr) {
15813                var $$observers = (attr.$$observers || (attr.$$observers = createMap()));
15814
15815                if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
15816                  throw $compileMinErr('nodomevents',
15817                      'Interpolations for HTML DOM event attributes are disallowed.  Please use the ' +
15818                          'ng- versions (such as ng-click instead of onclick) instead.');
15819                }
15820
15821                // If the attribute has changed since last $interpolate()ed
15822                var newValue = attr[name];
15823                if (newValue !== value) {
15824                  // we need to interpolate again since the attribute value has been updated
15825                  // (e.g. by another directive's compile function)
15826                  // ensure unset/empty values make interpolateFn falsy
15827                  interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing);
15828                  value = newValue;
15829                }
15830
15831                // if attribute was updated so that there is no interpolation going on we don't want to
15832                // register any observers
15833                if (!interpolateFn) return;
15834
15835                // initialize attr object so that it's ready in case we need the value for isolate
15836                // scope initialization, otherwise the value would not be available from isolate
15837                // directive's linking fn during linking phase
15838                attr[name] = interpolateFn(scope);
15839
15840                ($$observers[name] || ($$observers[name] = [])).$$inter = true;
15841                (attr.$$observers && attr.$$observers[name].$$scope || scope).
15842                  $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
15843                    //special case for class attribute addition + removal
15844                    //so that class changes can tap into the animation
15845                    //hooks provided by the $animate service. Be sure to
15846                    //skip animations when the first digest occurs (when
15847                    //both the new and the old values are the same) since
15848                    //the CSS classes are the non-interpolated values
15849                    if (name === 'class' && newValue !== oldValue) {
15850                      attr.$updateClass(newValue, oldValue);
15851                    } else {
15852                      attr.$set(name, newValue);
15853                    }
15854                  });
15855              }
15856            };
15857          }
15858      });
15859    }
15860
15861
15862    /**
15863     * This is a special jqLite.replaceWith, which can replace items which
15864     * have no parents, provided that the containing jqLite collection is provided.
15865     *
15866     * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
15867     *                               in the root of the tree.
15868     * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep
15869     *                                  the shell, but replace its DOM node reference.
15870     * @param {Node} newNode The new DOM node.
15871     */
15872    function replaceWith($rootElement, elementsToRemove, newNode) {
15873      var firstElementToRemove = elementsToRemove[0],
15874          removeCount = elementsToRemove.length,
15875          parent = firstElementToRemove.parentNode,
15876          i, ii;
15877
15878      if ($rootElement) {
15879        for (i = 0, ii = $rootElement.length; i < ii; i++) {
15880          if ($rootElement[i] === firstElementToRemove) {
15881            $rootElement[i++] = newNode;
15882            for (var j = i, j2 = j + removeCount - 1,
15883                     jj = $rootElement.length;
15884                 j < jj; j++, j2++) {
15885              if (j2 < jj) {
15886                $rootElement[j] = $rootElement[j2];
15887              } else {
15888                delete $rootElement[j];
15889              }
15890            }
15891            $rootElement.length -= removeCount - 1;
15892
15893            // If the replaced element is also the jQuery .context then replace it
15894            // .context is a deprecated jQuery api, so we should set it only when jQuery set it
15895            // http://api.jquery.com/context/
15896            if ($rootElement.context === firstElementToRemove) {
15897              $rootElement.context = newNode;
15898            }
15899            break;
15900          }
15901        }
15902      }
15903
15904      if (parent) {
15905        parent.replaceChild(newNode, firstElementToRemove);
15906      }
15907
15908      // Append all the `elementsToRemove` to a fragment. This will...
15909      // - remove them from the DOM
15910      // - allow them to still be traversed with .nextSibling
15911      // - allow a single fragment.qSA to fetch all elements being removed
15912      var fragment = window.document.createDocumentFragment();
15913      for (i = 0; i < removeCount; i++) {
15914        fragment.appendChild(elementsToRemove[i]);
15915      }
15916
15917      if (jqLite.hasData(firstElementToRemove)) {
15918        // Copy over user data (that includes Angular's $scope etc.). Don't copy private
15919        // data here because there's no public interface in jQuery to do that and copying over
15920        // event listeners (which is the main use of private data) wouldn't work anyway.
15921        jqLite.data(newNode, jqLite.data(firstElementToRemove));
15922
15923        // Remove $destroy event listeners from `firstElementToRemove`
15924        jqLite(firstElementToRemove).off('$destroy');
15925      }
15926
15927      // Cleanup any data/listeners on the elements and children.
15928      // This includes invoking the $destroy event on any elements with listeners.
15929      jqLite.cleanData(fragment.querySelectorAll('*'));
15930
15931      // Update the jqLite collection to only contain the `newNode`
15932      for (i = 1; i < removeCount; i++) {
15933        delete elementsToRemove[i];
15934      }
15935      elementsToRemove[0] = newNode;
15936      elementsToRemove.length = 1;
15937    }
15938
15939
15940    function cloneAndAnnotateFn(fn, annotation) {
15941      return extend(function() { return fn.apply(null, arguments); }, fn, annotation);
15942    }
15943
15944
15945    function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) {
15946      try {
15947        linkFn(scope, $element, attrs, controllers, transcludeFn);
15948      } catch (e) {
15949        $exceptionHandler(e, startingTag($element));
15950      }
15951    }
15952
15953
15954    // Set up $watches for isolate scope and controller bindings.
15955    function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
15956      var removeWatchCollection = [];
15957      var initialChanges = {};
15958      var changes;
15959      forEach(bindings, function initializeBinding(definition, scopeName) {
15960        var attrName = definition.attrName,
15961        optional = definition.optional,
15962        mode = definition.mode, // @, =, <, or &
15963        lastValue,
15964        parentGet, parentSet, compare, removeWatch;
15965
15966        switch (mode) {
15967
15968          case '@':
15969            if (!optional && !hasOwnProperty.call(attrs, attrName)) {
15970              destination[scopeName] = attrs[attrName] = undefined;
15971            }
15972            removeWatch = attrs.$observe(attrName, function(value) {
15973              if (isString(value) || isBoolean(value)) {
15974                var oldValue = destination[scopeName];
15975                recordChanges(scopeName, value, oldValue);
15976                destination[scopeName] = value;
15977              }
15978            });
15979            attrs.$$observers[attrName].$$scope = scope;
15980            lastValue = attrs[attrName];
15981            if (isString(lastValue)) {
15982              // If the attribute has been provided then we trigger an interpolation to ensure
15983              // the value is there for use in the link fn
15984              destination[scopeName] = $interpolate(lastValue)(scope);
15985            } else if (isBoolean(lastValue)) {
15986              // If the attributes is one of the BOOLEAN_ATTR then Angular will have converted
15987              // the value to boolean rather than a string, so we special case this situation
15988              destination[scopeName] = lastValue;
15989            }
15990            initialChanges[scopeName] = new SimpleChange(_UNINITIALIZED_VALUE, destination[scopeName]);
15991            removeWatchCollection.push(removeWatch);
15992            break;
15993
15994          case '=':
15995            if (!hasOwnProperty.call(attrs, attrName)) {
15996              if (optional) break;
15997              attrs[attrName] = undefined;
15998            }
15999            if (optional && !attrs[attrName]) break;
16000
16001            parentGet = $parse(attrs[attrName]);
16002            if (parentGet.literal) {
16003              compare = equals;
16004            } else {
16005              // eslint-disable-next-line no-self-compare
16006              compare = function simpleCompare(a, b) { return a === b || (a !== a && b !== b); };
16007            }
16008            parentSet = parentGet.assign || function() {
16009              // reset the change, or we will throw this exception on every $digest
16010              lastValue = destination[scopeName] = parentGet(scope);
16011              throw $compileMinErr('nonassign',
16012                  'Expression \'{0}\' in attribute \'{1}\' used with directive \'{2}\' is non-assignable!',
16013                  attrs[attrName], attrName, directive.name);
16014            };
16015            lastValue = destination[scopeName] = parentGet(scope);
16016            var parentValueWatch = function parentValueWatch(parentValue) {
16017              if (!compare(parentValue, destination[scopeName])) {
16018                // we are out of sync and need to copy
16019                if (!compare(parentValue, lastValue)) {
16020                  // parent changed and it has precedence
16021                  destination[scopeName] = parentValue;
16022                } else {
16023                  // if the parent can be assigned then do so
16024                  parentSet(scope, parentValue = destination[scopeName]);
16025                }
16026              }
16027              lastValue = parentValue;
16028              return lastValue;
16029            };
16030            parentValueWatch.$stateful = true;
16031            if (definition.collection) {
16032              removeWatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
16033            } else {
16034              removeWatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
16035            }
16036            removeWatchCollection.push(removeWatch);
16037            break;
16038
16039          case '<':
16040            if (!hasOwnProperty.call(attrs, attrName)) {
16041              if (optional) break;
16042              attrs[attrName] = undefined;
16043            }
16044            if (optional && !attrs[attrName]) break;
16045
16046            parentGet = $parse(attrs[attrName]);
16047            var deepWatch = parentGet.literal;
16048
16049            var initialValue = destination[scopeName] = parentGet(scope);
16050            initialChanges[scopeName] = new SimpleChange(_UNINITIALIZED_VALUE, destination[scopeName]);
16051
16052            removeWatch = scope.$watch(parentGet, function parentValueWatchAction(newValue, oldValue) {
16053              if (oldValue === newValue) {
16054                if (oldValue === initialValue || (deepWatch && equals(oldValue, initialValue))) {
16055                  return;
16056                }
16057                oldValue = initialValue;
16058              }
16059              recordChanges(scopeName, newValue, oldValue);
16060              destination[scopeName] = newValue;
16061            }, deepWatch);
16062
16063            removeWatchCollection.push(removeWatch);
16064            break;
16065
16066          case '&':
16067            // Don't assign Object.prototype method to scope
16068            parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop;
16069
16070            // Don't assign noop to destination if expression is not valid
16071            if (parentGet === noop && optional) break;
16072
16073            destination[scopeName] = function(locals) {
16074              return parentGet(scope, locals);
16075            };
16076            break;
16077        }
16078      });
16079
16080      function recordChanges(key, currentValue, previousValue) {
16081        if (isFunction(destination.$onChanges) && currentValue !== previousValue &&
16082            // eslint-disable-next-line no-self-compare
16083            (currentValue === currentValue || previousValue === previousValue)) {
16084          // If we have not already scheduled the top level onChangesQueue handler then do so now
16085          if (!onChangesQueue) {
16086            scope.$$postDigest(flushOnChangesQueue);
16087            onChangesQueue = [];
16088          }
16089          // If we have not already queued a trigger of onChanges for this controller then do so now
16090          if (!changes) {
16091            changes = {};
16092            onChangesQueue.push(triggerOnChangesHook);
16093          }
16094          // If the has been a change on this property already then we need to reuse the previous value
16095          if (changes[key]) {
16096            previousValue = changes[key].previousValue;
16097          }
16098          // Store this change
16099          changes[key] = new SimpleChange(previousValue, currentValue);
16100        }
16101      }
16102
16103      function triggerOnChangesHook() {
16104        destination.$onChanges(changes);
16105        // Now clear the changes so that we schedule onChanges when more changes arrive
16106        changes = undefined;
16107      }
16108
16109      return {
16110        initialChanges: initialChanges,
16111        removeWatches: removeWatchCollection.length && function removeWatches() {
16112          for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) {
16113            removeWatchCollection[i]();
16114          }
16115        }
16116      };
16117    }
16118  }];
16119}
16120
16121function SimpleChange(previous, current) {
16122  this.previousValue = previous;
16123  this.currentValue = current;
16124}
16125SimpleChange.prototype.isFirstChange = function() { return this.previousValue === _UNINITIALIZED_VALUE; };
16126
16127
16128var PREFIX_REGEXP = /^((?:x|data)[:\-_])/i;
16129/**
16130 * Converts all accepted directives format into proper directive name.
16131 * @param name Name to normalize
16132 */
16133function directiveNormalize(name) {
16134  return camelCase(name.replace(PREFIX_REGEXP, ''));
16135}
16136
16137/**
16138 * @ngdoc type
16139 * @name $compile.directive.Attributes
16140 *
16141 * @description
16142 * A shared object between directive compile / linking functions which contains normalized DOM
16143 * element attributes. The values reflect current binding state `{{ }}`. The normalization is
16144 * needed since all of these are treated as equivalent in Angular:
16145 *
16146 * ```
16147 *    <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
16148 * ```
16149 */
16150
16151/**
16152 * @ngdoc property
16153 * @name $compile.directive.Attributes#$attr
16154 *
16155 * @description
16156 * A map of DOM element attribute names to the normalized name. This is
16157 * needed to do reverse lookup from normalized name back to actual name.
16158 */
16159
16160
16161/**
16162 * @ngdoc method
16163 * @name $compile.directive.Attributes#$set
16164 * @kind function
16165 *
16166 * @description
16167 * Set DOM element attribute value.
16168 *
16169 *
16170 * @param {string} name Normalized element attribute name of the property to modify. The name is
16171 *          reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
16172 *          property to the original name.
16173 * @param {string} value Value to set the attribute to. The value can be an interpolated string.
16174 */
16175
16176
16177
16178/**
16179 * Closure compiler type information
16180 */
16181
16182function nodesetLinkingFn(
16183  /* angular.Scope */ scope,
16184  /* NodeList */ nodeList,
16185  /* Element */ rootElement,
16186  /* function(Function) */ boundTranscludeFn
16187) {}
16188
16189function directiveLinkingFn(
16190  /* nodesetLinkingFn */ nodesetLinkingFn,
16191  /* angular.Scope */ scope,
16192  /* Node */ node,
16193  /* Element */ rootElement,
16194  /* function(Function) */ boundTranscludeFn
16195) {}
16196
16197function tokenDifference(str1, str2) {
16198  var values = '',
16199      tokens1 = str1.split(/\s+/),
16200      tokens2 = str2.split(/\s+/);
16201
16202  outer:
16203  for (var i = 0; i < tokens1.length; i++) {
16204    var token = tokens1[i];
16205    for (var j = 0; j < tokens2.length; j++) {
16206      if (token === tokens2[j]) continue outer;
16207    }
16208    values += (values.length > 0 ? ' ' : '') + token;
16209  }
16210  return values;
16211}
16212
16213function removeComments(jqNodes) {
16214  jqNodes = jqLite(jqNodes);
16215  var i = jqNodes.length;
16216
16217  if (i <= 1) {
16218    return jqNodes;
16219  }
16220
16221  while (i--) {
16222    var node = jqNodes[i];
16223    if (node.nodeType === NODE_TYPE_COMMENT ||
16224       (node.nodeType === NODE_TYPE_TEXT && node.nodeValue.trim() === '')) {
16225         splice.call(jqNodes, i, 1);
16226    }
16227  }
16228  return jqNodes;
16229}
16230
16231var $controllerMinErr = minErr('$controller');
16232
16233
16234var CNTRL_REG = /^(\S+)(\s+as\s+([\w$]+))?$/;
16235function identifierForController(controller, ident) {
16236  if (ident && isString(ident)) return ident;
16237  if (isString(controller)) {
16238    var match = CNTRL_REG.exec(controller);
16239    if (match) return match[3];
16240  }
16241}
16242
16243
16244/**
16245 * @ngdoc provider
16246 * @name $controllerProvider
16247 * @this
16248 *
16249 * @description
16250 * The {@link ng.$controller $controller service} is used by Angular to create new
16251 * controllers.
16252 *
16253 * This provider allows controller registration via the
16254 * {@link ng.$controllerProvider#register register} method.
16255 */
16256function $ControllerProvider() {
16257  var controllers = {},
16258      globals = false;
16259
16260  /**
16261   * @ngdoc method
16262   * @name $controllerProvider#has
16263   * @param {string} name Controller name to check.
16264   */
16265  this.has = function(name) {
16266    return controllers.hasOwnProperty(name);
16267  };
16268
16269  /**
16270   * @ngdoc method
16271   * @name $controllerProvider#register
16272   * @param {string|Object} name Controller name, or an object map of controllers where the keys are
16273   *    the names and the values are the constructors.
16274   * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
16275   *    annotations in the array notation).
16276   */
16277  this.register = function(name, constructor) {
16278    assertNotHasOwnProperty(name, 'controller');
16279    if (isObject(name)) {
16280      extend(controllers, name);
16281    } else {
16282      controllers[name] = constructor;
16283    }
16284  };
16285
16286  /**
16287   * @ngdoc method
16288   * @name $controllerProvider#allowGlobals
16289   *
16290   * @deprecated
16291   * sinceVersion="v1.3.0"
16292   * removeVersion="v1.7.0"
16293   * This method of finding controllers has been deprecated.
16294   *
16295   * @description If called, allows `$controller` to find controller constructors on `window`   *
16296   */
16297  this.allowGlobals = function() {
16298    globals = true;
16299  };
16300
16301
16302  this.$get = ['$injector', '$window', function($injector, $window) {
16303
16304    /**
16305     * @ngdoc service
16306     * @name $controller
16307     * @requires $injector
16308     *
16309     * @param {Function|string} constructor If called with a function then it's considered to be the
16310     *    controller constructor function. Otherwise it's considered to be a string which is used
16311     *    to retrieve the controller constructor using the following steps:
16312     *
16313     *    * check if a controller with given name is registered via `$controllerProvider`
16314     *    * check if evaluating the string on the current scope returns a constructor
16315     *    * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
16316     *      `window` object (not recommended)
16317     *
16318     *    The string can use the `controller as property` syntax, where the controller instance is published
16319     *    as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
16320     *    to work correctly.
16321     *
16322     * @param {Object} locals Injection locals for Controller.
16323     * @return {Object} Instance of given controller.
16324     *
16325     * @description
16326     * `$controller` service is responsible for instantiating controllers.
16327     *
16328     * It's just a simple call to {@link auto.$injector $injector}, but extracted into
16329     * a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
16330     */
16331    return function $controller(expression, locals, later, ident) {
16332      // PRIVATE API:
16333      //   param `later` --- indicates that the controller's constructor is invoked at a later time.
16334      //                     If true, $controller will allocate the object with the correct
16335      //                     prototype chain, but will not invoke the controller until a returned
16336      //                     callback is invoked.
16337      //   param `ident` --- An optional label which overrides the label parsed from the controller
16338      //                     expression, if any.
16339      var instance, match, constructor, identifier;
16340      later = later === true;
16341      if (ident && isString(ident)) {
16342        identifier = ident;
16343      }
16344
16345      if (isString(expression)) {
16346        match = expression.match(CNTRL_REG);
16347        if (!match) {
16348          throw $controllerMinErr('ctrlfmt',
16349            'Badly formed controller string \'{0}\'. ' +
16350            'Must match `__name__ as __id__` or `__name__`.', expression);
16351        }
16352        constructor = match[1];
16353        identifier = identifier || match[3];
16354        expression = controllers.hasOwnProperty(constructor)
16355            ? controllers[constructor]
16356            : getter(locals.$scope, constructor, true) ||
16357                (globals ? getter($window, constructor, true) : undefined);
16358
16359        if (!expression) {
16360          throw $controllerMinErr('ctrlreg',
16361            'The controller with the name \'{0}\' is not registered.', constructor);
16362        }
16363
16364        assertArgFn(expression, constructor, true);
16365      }
16366
16367      if (later) {
16368        // Instantiate controller later:
16369        // This machinery is used to create an instance of the object before calling the
16370        // controller's constructor itself.
16371        //
16372        // This allows properties to be added to the controller before the constructor is
16373        // invoked. Primarily, this is used for isolate scope bindings in $compile.
16374        //
16375        // This feature is not intended for use by applications, and is thus not documented
16376        // publicly.
16377        // Object creation: http://jsperf.com/create-constructor/2
16378        var controllerPrototype = (isArray(expression) ?
16379          expression[expression.length - 1] : expression).prototype;
16380        instance = Object.create(controllerPrototype || null);
16381
16382        if (identifier) {
16383          addIdentifier(locals, identifier, instance, constructor || expression.name);
16384        }
16385
16386        return extend(function $controllerInit() {
16387          var result = $injector.invoke(expression, instance, locals, constructor);
16388          if (result !== instance && (isObject(result) || isFunction(result))) {
16389            instance = result;
16390            if (identifier) {
16391              // If result changed, re-assign controllerAs value to scope.
16392              addIdentifier(locals, identifier, instance, constructor || expression.name);
16393            }
16394          }
16395          return instance;
16396        }, {
16397          instance: instance,
16398          identifier: identifier
16399        });
16400      }
16401
16402      instance = $injector.instantiate(expression, locals, constructor);
16403
16404      if (identifier) {
16405        addIdentifier(locals, identifier, instance, constructor || expression.name);
16406      }
16407
16408      return instance;
16409    };
16410
16411    function addIdentifier(locals, identifier, instance, name) {
16412      if (!(locals && isObject(locals.$scope))) {
16413        throw minErr('$controller')('noscp',
16414          'Cannot export controller \'{0}\' as \'{1}\'! No $scope object provided via `locals`.',
16415          name, identifier);
16416      }
16417
16418      locals.$scope[identifier] = instance;
16419    }
16420  }];
16421}
16422
16423/**
16424 * @ngdoc service
16425 * @name $document
16426 * @requires $window
16427 * @this
16428 *
16429 * @description
16430 * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
16431 *
16432 * @example
16433   <example module="documentExample" name="document">
16434     <file name="index.html">
16435       <div ng-controller="ExampleController">
16436         <p>$document title: <b ng-bind="title"></b></p>
16437         <p>window.document title: <b ng-bind="windowTitle"></b></p>
16438       </div>
16439     </file>
16440     <file name="script.js">
16441       angular.module('documentExample', [])
16442         .controller('ExampleController', ['$scope', '$document', function($scope, $document) {
16443           $scope.title = $document[0].title;
16444           $scope.windowTitle = angular.element(window.document)[0].title;
16445         }]);
16446     </file>
16447   </example>
16448 */
16449function $DocumentProvider() {
16450  this.$get = ['$window', function(window) {
16451    return jqLite(window.document);
16452  }];
16453}
16454
16455/**
16456 * @ngdoc service
16457 * @name $exceptionHandler
16458 * @requires ng.$log
16459 * @this
16460 *
16461 * @description
16462 * Any uncaught exception in angular expressions is delegated to this service.
16463 * The default implementation simply delegates to `$log.error` which logs it into
16464 * the browser console.
16465 *
16466 * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
16467 * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
16468 *
16469 * ## Example:
16470 *
16471 * The example below will overwrite the default `$exceptionHandler` in order to (a) log uncaught
16472 * errors to the backend for later inspection by the developers and (b) to use `$log.warn()` instead
16473 * of `$log.error()`.
16474 *
16475 * ```js
16476 *   angular.
16477 *     module('exceptionOverwrite', []).
16478 *     factory('$exceptionHandler', ['$log', 'logErrorsToBackend', function($log, logErrorsToBackend) {
16479 *       return function myExceptionHandler(exception, cause) {
16480 *         logErrorsToBackend(exception, cause);
16481 *         $log.warn(exception, cause);
16482 *       };
16483 *     }
16483]);
16484 * ```
16485 *
16486 * <hr />
16487 * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind`
16488 * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler}
16489 * (unless executed during a digest).
16490 *
16491 * If you wish, you can manually delegate exceptions, e.g.
16492 * `try { ... } catch(e) { $exceptionHandler(e); }`
16493 *
16494 * @param {Error} exception Exception associated with the error.
16495 * @param {string=} cause Optional information about the context in which
16496 *       the error was thrown.
16497 *
16498 */
16499function $ExceptionHandlerProvider() {
16500  this.$get = ['$log', function($log) {
16501    return function(exception, cause) {
16502      $log.error.apply($log, arguments);
16503    };
16504  }];
16505}
16506
16507var $$ForceReflowProvider = /** @this */ function() {
16508  this.$get = ['$document', function($document) {
16509    return function(domNode) {
16510      //the line below will force the browser to perform a repaint so
16511      //that all the animated elements within the animation frame will
16512      //be properly updated and drawn on screen. This is required to
16513      //ensure that the preparation animation is properly flushed so that
16514      //the active state picks up from there. DO NOT REMOVE THIS LINE.
16515      //DO NOT OPTIMIZE THIS LINE. THE MINIFIER WILL REMOVE IT OTHERWISE WHICH
16516      //WILL RESULT IN AN UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND
16517      //WILL TAKE YEARS AWAY FROM YOUR LIFE.
16518      if (domNode) {
16519        if (!domNode.nodeType && domNode instanceof jqLite) {
16520          domNode = domNode[0];
16521        }
16522      } else {
16523        domNode = $document[0].body;
16524      }
16525      return domNode.offsetWidth + 1;
16526    };
16527  }];
16528};
16529
16530var APPLICATION_JSON = 'application/json';
16531var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
16532var JSON_START = /^\[|^\{(?!\{)/;
16533var JSON_ENDS = {
16534  '[': /]$/,
16535  '{': /}$/
16536};
16537var JSON_PROTECTION_PREFIX = /^\)]\}',?\n/;
16538var $httpMinErr = minErr('$http');
16539var $httpMinErrLegacyFn = function(method) {
16540  return function() {
16541    throw $httpMinErr('legacy', 'The method `{0}` on the promise returned from `$http` has been disabled.', method);
16542  };
16543};
16544
16545function serializeValue(v) {
16546  if (isObject(v)) {
16547    return isDate(v) ? v.toISOString() : toJson(v);
16548  }
16549  return v;
16550}
16551
16552
16553/** @this */
16554function $HttpParamSerializerProvider() {
16555  /**
16556   * @ngdoc service
16557   * @name $httpParamSerializer
16558   * @description
16559   *
16560   * Default {@link $http `$http`} params serializer that converts objects to strings
16561   * according to the following rules:
16562   *
16563   * * `{'foo': 'bar'}` results in `foo=bar`
16564   * * `{'foo': Date.now()}` results in `foo=2015-04-01T09%3A50%3A49.262Z` (`toISOString()` and encoded representation of a Date object)
16565   * * `{'foo': ['bar', 'baz']}` results in `foo=bar&foo=baz` (repeated key for each array element)
16566   * * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D` (stringified and encoded representation of an object)
16567   *
16568   * Note that serializer will sort the request parameters alphabetically.
16569   * */
16570
16571  this.$get = function() {
16572    return function ngParamSerializer(params) {
16573      if (!params) return '';
16574      var parts = [];
16575      forEachSorted(params, function(value, key) {
16576        if (value === null || isUndefined(value)) return;
16577        if (isArray(value)) {
16578          forEach(value, function(v) {
16579            parts.push(encodeUriQuery(key)  + '=' + encodeUriQuery(serializeValue(v)));
16580          });
16581        } else {
16582          parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(value)));
16583        }
16584      });
16585
16586      return parts.join('&');
16587    };
16588  };
16589}
16590
16591/** @this */
16592function $HttpParamSerializerJQLikeProvider() {
16593  /**
16594   * @ngdoc service
16595   * @name $httpParamSerializerJQLike
16596   *
16597   * @description
16598   *
16599   * Alternative {@link $http `$http`} params serializer that follows
16600   * jQuery's [`param()`](http://api.jquery.com/jquery.param/) method logic.
16601   * The serializer will also sort the params alphabetically.
16602   *
16603   * To use it for serializing `$http` request parameters, set it as the `paramSerializer` property:
16604   *
16605   * ```js
16606   * $http({
16607   *   url: myUrl,
16608   *   method: 'GET',
16609   *   params: myParams,
16610   *   paramSerializer: '$httpParamSerializerJQLike'
16611   * });
16612   * ```
16613   *
16614   * It is also possible to set it as the default `paramSerializer` in the
16615   * {@link $httpProvider#defaults `$httpProvider`}.
16616   *
16617   * Additionally, you can inject the serializer and use it explicitly, for example to serialize
16618   * form data for submission:
16619   *
16620   * ```js
16621   * .controller(function($http, $httpParamSerializerJQLike) {
16622   *   //...
16623   *
16624   *   $http({
16625   *     url: myUrl,
16626   *     method: 'POST',
16627   *     data: $httpParamSerializerJQLike(myData),
16628   *     headers: {
16629   *       'Content-Type': 'application/x-www-form-urlencoded'
16630   *     }
16631   *   });
16632   *
16633   * });
16634   * ```
16635   *
16636   * */
16637  this.$get = function() {
16638    return function jQueryLikeParamSerializer(params) {
16639      if (!params) return '';
16640      var parts = [];
16641      serialize(params, '', true);
16642      return parts.join('&');
16643
16644      function serialize(toSerialize, prefix, topLevel) {
16645        if (toSerialize === null || isUndefined(toSerialize)) return;
16646        if (isArray(toSerialize)) {
16647          forEach(toSerialize, function(value, index) {
16648            serialize(value, prefix + '[' + (isObject(value) ? index : '') + ']');
16649          });
16650        } else if (isObject(toSerialize) && !isDate(toSerialize)) {
16651          forEachSorted(toSerialize, function(value, key) {
16652            serialize(value, prefix +
16653                (topLevel ? '' : '[') +
16654                key +
16655                (topLevel ? '' : ']'));
16656          });
16657        } else {
16658          parts.push(encodeUriQuery(prefix) + '=' + encodeUriQuery(serializeValue(toSerialize)));
16659        }
16660      }
16661    };
16662  };
16663}
16664
16665function defaultHttpResponseTransform(data, headers) {
16666  if (isString(data)) {
16667    // Strip json vulnerability protection prefix and trim whitespace
16668    var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim();
16669
16670    if (tempData) {
16671      var contentType = headers('Content-Type');
16672      if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
16673        data = fromJson(tempData);
16674      }
16675    }
16676  }
16677
16678  return data;
16679}
16680
16681function isJsonLike(str) {
16682    var jsonStart = str.match(JSON_START);
16683    return jsonStart && JSON_ENDS[jsonStart[0]].test(str);
16684}
16685
16686/**
16687 * Parse headers into key value object
16688 *
16689 * @param {string} headers Raw headers as a string
16690 * @returns {Object} Parsed headers as key value object
16691 */
16692function parseHeaders(headers) {
16693  var parsed = createMap(), i;
16694
16695  function fillInParsed(key, val) {
16696    if (key) {
16697      parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
16698    }
16699  }
16700
16701  if (isString(headers)) {
16702    forEach(headers.split('\n'), function(line) {
16703      i = line.indexOf(':');
vendor: 8,606 bytes, lines 16704-16958
16704      fillInParsed(lowercase(trim(line.substr(0, i))), trim(line.substr(i + 1)));
16705    });
16706  } else if (isObject(headers)) {
16707    forEach(headers, function(headerVal, headerKey) {
16708      fillInParsed(lowercase(headerKey), trim(headerVal));
16709    });
16710  }
16711
16712  return parsed;
16713}
16714
16715
16716/**
16717 * Returns a function that provides access to parsed headers.
16718 *
16719 * Headers are lazy parsed when first requested.
16720 * @see parseHeaders
16721 *
16722 * @param {(string|Object)} headers Headers to provide access to.
16723 * @returns {function(string=)} Returns a getter function which if called with:
16724 *
16725 *   - if called with an argument returns a single header value or null
16726 *   - if called with no arguments returns an object containing all headers.
16727 */
16728function headersGetter(headers) {
16729  var headersObj;
16730
16731  return function(name) {
16732    if (!headersObj) headersObj =  parseHeaders(headers);
16733
16734    if (name) {
16735      var value = headersObj[lowercase(name)];
16736      if (value === undefined) {
16737        value = null;
16738      }
16739      return value;
16740    }
16741
16742    return headersObj;
16743  };
16744}
16745
16746
16747/**
16748 * Chain all given functions
16749 *
16750 * This function is used for both request and response transforming
16751 *
16752 * @param {*} data Data to transform.
16753 * @param {function(string=)} headers HTTP headers getter fn.
16754 * @param {number} status HTTP status code of the response.
16755 * @param {(Function|Array.<Function>)} fns Function or an array of functions.
16756 * @returns {*} Transformed data.
16757 */
16758function transformData(data, headers, status, fns) {
16759  if (isFunction(fns)) {
16760    return fns(data, headers, status);
16761  }
16762
16763  forEach(fns, function(fn) {
16764    data = fn(data, headers, status);
16765  });
16766
16767  return data;
16768}
16769
16770
16771function isSuccess(status) {
16772  return 200 <= status && status < 300;
16773}
16774
16775
16776/**
16777 * @ngdoc provider
16778 * @name $httpProvider
16779 * @this
16780 *
16781 * @description
16782 * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service.
16783 * */
16784function $HttpProvider() {
16785  /**
16786   * @ngdoc property
16787   * @name $httpProvider#defaults
16788   * @description
16789   *
16790   * Object containing default values for all {@link ng.$http $http} requests.
16791   *
16792   * - **`defaults.cache`** - {boolean|Object} - A boolean value or object created with
16793   * {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of HTTP responses
16794   * by default. See {@link $http#caching $http Caching} for more information.
16795   *
16796   * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
16797   * Defaults value is `'XSRF-TOKEN'`.
16798   *
16799   * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
16800   * XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
16801   *
16802   * - **`defaults.headers`** - {Object} - Default headers for all $http requests.
16803   * Refer to {@link ng.$http#setting-http-headers $http} for documentation on
16804   * setting default headers.
16805   *     - **`defaults.headers.common`**
16806   *     - **`defaults.headers.post`**
16807   *     - **`defaults.headers.put`**
16808   *     - **`defaults.headers.patch`**
16809   *
16810   *
16811   * - **`defaults.paramSerializer`** - `{string|function(Object<string,string>):string}` - A function
16812   *  used to the prepare string representation of request parameters (specified as an object).
16813   *  If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}.
16814   *  Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}.
16815   *
16816   **/
16817  var defaults = this.defaults = {
16818    // transform incoming response data
16819    transformResponse: [defaultHttpResponseTransform],
16820
16821    // transform outgoing request data
16822    transformRequest: [function(d) {
16823      return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d;
16824    }],
16825
16826    // default headers
16827    headers: {
16828      common: {
16829        'Accept': 'application/json, text/plain, */*'
16830      },
16831      post:   shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
16832      put:    shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
16833      patch:  shallowCopy(CONTENT_TYPE_APPLICATION_JSON)
16834    },
16835
16836    xsrfCookieName: 'XSRF-TOKEN',
16837    xsrfHeaderName: 'X-XSRF-TOKEN',
16838
16839    paramSerializer: '$httpParamSerializer'
16840  };
16841
16842  var useApplyAsync = false;
16843  /**
16844   * @ngdoc method
16845   * @name $httpProvider#useApplyAsync
16846   * @description
16847   *
16848   * Configure $http service to combine processing of multiple http responses received at around
16849   * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in
16850   * significant performance improvement for bigger applications that make many HTTP requests
16851   * concurrently (common during application bootstrap).
16852   *
16853   * Defaults to false. If no value is specified, returns the current configured value.
16854   *
16855   * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred
16856   *    "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window
16857   *    to load and share the same digest cycle.
16858   *
16859   * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
16860   *    otherwise, returns the current configured value.
16861   **/
16862  this.useApplyAsync = function(value) {
16863    if (isDefined(value)) {
16864      useApplyAsync = !!value;
16865      return this;
16866    }
16867    return useApplyAsync;
16868  };
16869
16870  var useLegacyPromise = true;
16871  /**
16872   * @ngdoc method
16873   * @name $httpProvider#useLegacyPromiseExtensions
16874   * @description
16875   *
16876   * @deprecated
16877   * sinceVersion="v1.4.4"
16878   * removeVersion="v1.6.0"
16879   * This method will be removed in v1.6.0 along with the legacy promise methods.
16880   *
16881   * Configure `$http` service to return promises without the shorthand methods `success` and `error`.
16882   * This should be used to make sure that applications work without these methods.
16883   *
16884   * Defaults to true. If no value is specified, returns the current configured value.
16885   *
16886   * @param {boolean=} value If true, `$http` will return a promise with the deprecated legacy `success` and `error` methods.
16887   *
16888   * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
16889   *    otherwise, returns the current configured value.
16890   **/
16891  this.useLegacyPromiseExtensions = function(value) {
16892    if (isDefined(value)) {
16893      useLegacyPromise = !!value;
16894      return this;
16895    }
16896    return useLegacyPromise;
16897  };
16898
16899  /**
16900   * @ngdoc property
16901   * @name $httpProvider#interceptors
16902   * @description
16903   *
16904   * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http}
16905   * pre-processing of request or postprocessing of responses.
16906   *
16907   * These service factories are ordered by request, i.e. they are applied in the same order as the
16908   * array, on request, but reverse order, on response.
16909   *
16910   * {@link ng.$http#interceptors Interceptors detailed info}
16911   **/
16912  var interceptorFactories = this.interceptors = [];
16913
16914  this.$get = ['$httpBackend', '$$cookieReader', '$cacheFactory', '$rootScope', '$q', '$injector',
16915      function($httpBackend, $$cookieReader, $cacheFactory, $rootScope, $q, $injector) {
16916
16917    var defaultCache = $cacheFactory('$http');
16918
16919    /**
16920     * Make sure that default param serializer is exposed as a function
16921     */
16922    defaults.paramSerializer = isString(defaults.paramSerializer) ?
16923      $injector.get(defaults.paramSerializer) : defaults.paramSerializer;
16924
16925    /**
16926     * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
16927     * The reversal is needed so that we can build up the interception chain around the
16928     * server request.
16929     */
16930    var reversedInterceptors = [];
16931
16932    forEach(interceptorFactories, function(interceptorFactory) {
16933      reversedInterceptors.unshift(isString(interceptorFactory)
16934          ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
16935    });
16936
16937    /**
16938     * @ngdoc service
16939     * @kind function
16940     * @name $http
16941     * @requires ng.$httpBackend
16942     * @requires $cacheFactory
16943     * @requires $rootScope
16944     * @requires $q
16945     * @requires $injector
16946     *
16947     * @description
16948     * The `$http` service is a core Angular service that facilitates communication with the remote
16949     * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest)
16950     * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP).
16951     *
16952     * For unit testing applications that use `$http` service, see
16953     * {@link ngMock.$httpBackend $httpBackend mock}.
16954     *
16955     * For a higher level of abstraction, please check out the {@link ngResource.$resource
16956     * $resource} service.
16957     *
16958     * The $http API is based on the {@link ng.$q deferred/promise APIs}
16958 exposed by
16959     * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
16960     * it is important to familiarize yourself with these APIs and the guarantees they provide.
16961     *
16962     *
16963     * ## General usage
16964     * The `$http` service is a function which takes a single argument — a {@link $http#usage configuration object} —
16965     * that is used to generate an HTTP request and returns  a {@link ng.$q promise}.
16966     *
16967     * ```js
16968     *   // Simple GET request example:
16969     *   $http({
16970     *     method: 'GET',
16971     *     url: '/someUrl'
16972     *   }).then(function successCallback(response) {
16973     *       // this callback will be called asynchronously
16974     *       // when the response is available
16975     *     }, function errorCallback(response) {
16976     *       // called asynchronously if an error occurs
16977     *       // or server returns response with an error status.
16978     *     });
16979     * ```
16980     *
16981     * The response object has these properties:
16982     *
16983     *   - **data** – `{string|Object}` – The response body transformed with the transform
16984     *     functions.
16985     *   - **status** – `{number}` – HTTP status code of the response.
16986     *   - **headers** – `{function([headerName])}` – Header getter function.
16987     *   - **config** – `{Object}` – The configuration object that was used to generate the request.
16988     *   - **statusText** – `{string}` – HTTP status text of the response.
16989     *
16990     * A response status code between 200 and 299 is considered a success status and will result in
16991     * the success callback being called. Any response status code outside of that range is
16992     * considered an error status and will result in the error callback being called.
16993     * Also, status codes less than -1 are normalized to zero. -1 usually means the request was
16994     * aborted, e.g. using a `config.timeout`.
16995     * Note that if the response is a redirect, XMLHttpRequest will transparently follow it, meaning
16996     * that the outcome (success or error) will be determined by the final response status code.
16997     *
16998     *
16999     * ## Shortcut methods
17000     *
17001     * Shortcut methods are also available. All shortcut methods require passing in the URL, and
17002     * request data must be passed in for POST/PUT requests. An optional config can be passed as the
17003     * last argument.
17004     *
17005     * ```js
17006     *   $http.get('/someUrl', config).then(successCallback, errorCallback);
17007     *   $http.post('/someUrl', data, config).then(successCallback, errorCallback);
17008     * ```
17009     *
17010     * Complete list of shortcut methods:
17011     *
17012     * - {@link ng.$http#get $http.get}
17013     * - {@link ng.$http#head $http.head}
17014     * - {@link ng.$http#post $http.post}
17015     * - {@link ng.$http#put $http.put}
17016     * - {@link ng.$http#delete $http.delete}
17017     * - {@link ng.$http#jsonp $http.jsonp}
17018     * - {@link ng.$http#patch $http.patch}
17019     *
17020     *
17021     * ## Writing Unit Tests that use $http
17022     * When unit testing (using {@link ngMock ngMock}), it is necessary to call
17023     * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
17024     * request using trained responses.
17025     *
17026     * ```
17027     * $httpBackend.expectGET(...);
17028     * $http.get(...);
17029     * $httpBackend.flush();
17030     * ```
17031     *
17032     * ## Deprecation Notice
17033     * <div class="alert alert-danger">
17034     *   The `$http` legacy promise methods `success` and `error` have been deprecated and will be
17035     *   removed in v1.6.0.
17036     *   Use the standard `then` method instead.
17037     *   If {@link $httpProvider#useLegacyPromiseExtensions `$httpProvider.useLegacyPromiseExtensions`} is set to
17038     *   `false` then these methods will throw {@link $http:legacy `$http/legacy`} error.
17039     * </div>
17040     *
17041     * ## Setting HTTP Headers
17042     *
17043     * The $http service will automatically add certain HTTP headers to all requests. These defaults
17044     * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
17045     * object, which currently contains this default configuration:
17046     *
17047     * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
17048     *   - <code>Accept: application/json, text/plain, \*&#65279;/&#65279;\*</code>
17049     * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
17050     *   - `Content-Type: application/json`
17051     * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
17052     *   - `Content-Type: application/json`
17053     *
17054     * To add or overwrite these defaults, simply add or remove a property from these configuration
17055     * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
17056     * with the lowercased HTTP method name as the key, e.g.
17057     * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }`.
17058     *
17059     * The defaults can also be set at runtime via the `$http.defaults` object in the same
17060     * fashion. For example:
17061     *
17062     * ```
17063     * module.run(function($http) {
17064     *   $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w';
17065     * });
17066     * ```
17067     *
17068     * In addition, you can supply a `headers` property in the config object passed when
17069     * calling `$http(config)`, which overrides the defaults without changing them globally.
17070     *
17071     * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,
17072     * Use the `headers` property, setting the desired header to `undefined`. For example:
17073     *
17074     * ```js
17075     * var req = {
17076     *  method: 'POST',
17077     *  url: 'http://example.com',
17078     *  headers: {
17079     *    'Content-Type': undefined
17080     *  },
17081     *  data: { test: 'test' }
17082     * }
17083     *
17084     * $http(req).then(function(){...}, function(){...});
17085     * ```
17086     *
17087     * ## Transforming Requests and Responses
17088     *
17089     * Both requests and responses can be transformed using transformation functions: `transformRequest`
17090     * and `transformResponse`. These properties can be a single function that returns
17091     * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions,
17092     * which allows you to `push` or `unshift` a new transformation function into the transformation chain.
17093     *
17094     * <div class="alert alert-warning">
17095     * **Note:** Angular does not make a copy of the `data` parameter before it is passed into the `transformRequest` pipeline.
17096     * That means changes to the properties of `data` are not local to the transform function (since Javascript passes objects by reference).
17097     * For example, when calling `$http.get(url, $scope.myObject)`, modifications to the object's properties in a transformRequest
17098     * function will be reflected on the scope and in any templates where the object is data-bound.
17099     * To prevent this, transform functions should have no side-effects.
17100     * If you need to modify properties, it is recommended to make a copy of the data, or create new object to return.
17101     * </div>
17102     *
17103     * ### Default Transformations
17104     *
17105     * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
17106     * `defaults.transformResponse` properties. If a request does not provide its own transformations
17107     * then these will be applied.
17108     *
17109     * You can augment or replace the default transformations by modifying these properties by adding to or
17110     * replacing the array.
17111     *
17112     * Angular provides the following default transformations:
17113     *
17114     * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`):
17115     *
17116     * - If the `data` property of the request configuration object contains an object, serialize it
17117     *   into JSON format.
17118     *
17119     * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`):
17120     *
17121     *  - If XSRF prefix is detected, strip it (see Security Considerations section below).
17122     *  - If JSON response is detected,
vendor: 7,183 bytes, lines 17122-17282
17122 deserialize it using a JSON parser.
17123     *
17124     *
17125     * ### Overriding the Default Transformations Per Request
17126     *
17127     * If you wish to override the request/response transformations only for a single request then provide
17128     * `transformRequest` and/or `transformResponse` properties on the configuration object passed
17129     * into `$http`.
17130     *
17131     * Note that if you provide these properties on the config object the default transformations will be
17132     * overwritten. If you wish to augment the default transformations then you must include them in your
17133     * local transformation array.
17134     *
17135     * The following code demonstrates adding a new response transformation to be run after the default response
17136     * transformations have been run.
17137     *
17138     * ```js
17139     * function appendTransform(defaults, transform) {
17140     *
17141     *   // We can't guarantee that the default transformation is an array
17142     *   defaults = angular.isArray(defaults) ? defaults : [defaults];
17143     *
17144     *   // Append the new transformation to the defaults
17145     *   return defaults.concat(transform);
17146     * }
17147     *
17148     * $http({
17149     *   url: '...',
17150     *   method: 'GET',
17151     *   transformResponse: appendTransform($http.defaults.transformResponse, function(value) {
17152     *     return doTransform(value);
17153     *   })
17154     * });
17155     * ```
17156     *
17157     *
17158     * ## Caching
17159     *
17160     * {@link ng.$http `$http`} responses are not cached by default. To enable caching, you must
17161     * set the config.cache value or the default cache value to TRUE or to a cache object (created
17162     * with {@link ng.$cacheFactory `$cacheFactory`}). If defined, the value of config.cache takes
17163     * precedence over the default cache value.
17164     *
17165     * In order to:
17166     *   * cache all responses - set the default cache value to TRUE or to a cache object
17167     *   * cache a specific response - set config.cache value to TRUE or to a cache object
17168     *
17169     * If caching is enabled, but neither the default cache nor config.cache are set to a cache object,
17170     * then the default `$cacheFactory("$http")` object is used.
17171     *
17172     * The default cache value can be set by updating the
17173     * {@link ng.$http#defaults `$http.defaults.cache`} property or the
17174     * {@link $httpProvider#defaults `$httpProvider.defaults.cache`} property.
17175     *
17176     * When caching is enabled, {@link ng.$http `$http`} stores the response from the server using
17177     * the relevant cache object. The next time the same request is made, the response is returned
17178     * from the cache without sending a request to the server.
17179     *
17180     * Take note that:
17181     *
17182     *   * Only GET and JSONP requests are cached.
17183     *   * The cache key is the request URL including search parameters; headers are not considered.
17184     *   * Cached responses are returned asynchronously, in the same way as responses from the server.
17185     *   * If multiple identical requests are made using the same cache, which is not yet populated,
17186     *     one request will be made to the server and remaining requests will return the same response.
17187     *   * A cache-control header on the response does not affect if or how responses are cached.
17188     *
17189     *
17190     * ## Interceptors
17191     *
17192     * Before you start creating interceptors, be sure to understand the
17193     * {@link ng.$q $q and deferred/promise APIs}.
17194     *
17195     * For purposes of global error handling, authentication, or any kind of synchronous or
17196     * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
17197     * able to intercept requests before they are handed to the server and
17198     * responses before they are handed over to the application code that
17199     * initiated these requests. The interceptors leverage the {@link ng.$q
17200     * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
17201     *
17202     * The interceptors are service factories that are registered with the `$httpProvider` by
17203     * adding them to the `$httpProvider.interceptors` array. The factory is called and
17204     * injected with dependencies (if specified) and returns the interceptor.
17205     *
17206     * There are two kinds of interceptors (and two kinds of rejection interceptors):
17207     *
17208     *   * `request`: interceptors get called with a http {@link $http#usage config} object. The function is free to
17209     *     modify the `config` object or create a new one. The function needs to return the `config`
17210     *     object directly, or a promise containing the `config` or a new `config` object.
17211     *   * `requestError`: interceptor gets called when a previous interceptor threw an error or
17212     *     resolved with a rejection.
17213     *   * `response`: interceptors get called with http `response` object. The function is free to
17214     *     modify the `response` object or create a new one. The function needs to return the `response`
17215     *     object directly, or as a promise containing the `response` or a new `response` object.
17216     *   * `responseError`: interceptor gets called when a previous interceptor threw an error or
17217     *     resolved with a rejection.
17218     *
17219     *
17220     * ```js
17221     *   // register the interceptor as a service
17222     *   $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
17223     *     return {
17224     *       // optional method
17225     *       'request': function(config) {
17226     *         // do something on success
17227     *         return config;
17228     *       },
17229     *
17230     *       // optional method
17231     *      'requestError': function(rejection) {
17232     *         // do something on error
17233     *         if (canRecover(rejection)) {
17234     *           return responseOrNewPromise
17235     *         }
17236     *         return $q.reject(rejection);
17237     *       },
17238     *
17239     *
17240     *
17241     *       // optional method
17242     *       'response': function(response) {
17243     *         // do something on success
17244     *         return response;
17245     *       },
17246     *
17247     *       // optional method
17248     *      'responseError': function(rejection) {
17249     *         // do something on error
17250     *         if (canRecover(rejection)) {
17251     *           return responseOrNewPromise
17252     *         }
17253     *         return $q.reject(rejection);
17254     *       }
17255     *     };
17256     *   });
17257     *
17258     *   $httpProvider.interceptors.push('myHttpInterceptor');
17259     *
17260     *
17261     *   // alternatively, register the interceptor via an anonymous factory
17262     *   $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
17263     *     return {
17264     *      'request': function(config) {
17265     *          // same as above
17266     *       },
17267     *
17268     *       'response': function(response) {
17269     *          // same as above
17270     *       }
17271     *     };
17272     *   });
17273     * ```
17274     *
17275     * ## Security Considerations
17276     *
17277     * When designing web applications, consider security threats from:
17278     *
17279     * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
17280     * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)
17281     *
17282     * Both server and the client must cooperate in order to eliminate these threats. Angular
17282 comes
17283     * pre-configured with strategies that address these issues, but for this to work backend server
17284     * cooperation is required.
17285     *
17286     * ### JSON Vulnerability Protection
17287     *
17288     * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
17289     * allows third party website to turn your JSON resource URL into
17290     * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To
17291     * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
17292     * Angular will automatically strip the prefix before processing it as JSON.
17293     *
17294     * For example if your server needs to return:
17295     * ```js
17296     * ['one','two']
17297     * ```
17298     *
17299     * which is vulnerable to attack, your server can return:
17300     * ```js
17301     * )]}',
17302     * ['one','two']
17303     * ```
17304     *
17305     * Angular will strip the prefix, before processing the JSON.
17306     *
17307     *
17308     * ### Cross Site Request Forgery (XSRF) Protection
17309     *
17310     * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is an attack technique by
17311     * which the attacker can trick an authenticated user into unknowingly executing actions on your
17312     * website. Angular provides a mechanism to counter XSRF. When performing XHR requests, the
17313     * $http service reads a token from a cookie (by default, `XSRF-TOKEN`) and sets it as an HTTP
17314     * header (`X-XSRF-TOKEN`). Since only JavaScript that runs on your domain could read the
17315     * cookie, your server can be assured that the XHR came from JavaScript running on your domain.
17316     * The header will not be set for cross-domain requests.
17317     *
17318     * To take advantage of this, your server needs to set a token in a JavaScript readable session
17319     * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
17320     * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
17321     * that only JavaScript running on your domain could have sent the request. The token must be
17322     * unique for each user and must be verifiable by the server (to prevent the JavaScript from
17323     * making up its own tokens). We recommend that the token is a digest of your site's
17324     * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography&#41;)
17325     * for added security.
17326     *
17327     * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
17328     * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
17329     * or the per-request config object.
17330     *
17331     * In order to prevent collisions in environments where multiple Angular apps share the
17332     * same domain or subdomain, we recommend that each application uses unique cookie name.
17333     *
17334     * @param {object} config Object describing the request to be made and how it should be
17335     *    processed. The object has following properties:
17336     *
17337     *    - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
17338     *    - **url** – `{string}` – Absolute or relative URL of the resource that is being requested.
17339     *    - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be serialized
17340     *      with the `paramSerializer` and appended as GET parameters.
17341     *    - **data** – `{string|Object}` – Data to be sent as the request message data.
17342     *    - **headers** – `{Object}` – Map of strings or functions which return strings representing
17343     *      HTTP headers to send to the server. If the return value of a function is null, the
17344     *      header will not be sent. Functions accept a config object as an argument.
17345     *    - **eventHandlers** - `{Object}` - Event listeners to be bound to the XMLHttpRequest object.
17346     *      To bind events to the XMLHttpRequest upload object, use `uploadEventHandlers`.
17347     *      The handler will be called in the context of a `$apply` block.
17348     *    - **uploadEventHandlers** - `{Object}` - Event listeners to be bound to the XMLHttpRequest upload
17349     *      object. To bind events to the XMLHttpRequest object, use `eventHandlers`.
17350     *      The handler will be called in the context of a `$apply` block.
17351     *    - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
17352     *    - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
17353     *    - **transformRequest** –
17354     *      `{function(data, headersGetter)|Array.<function(data, headersGetter)>
17354}` –
17355     *      transform function or an array of such functions. The transform function takes the http
17356     *      request body and headers and returns its transformed (typically serialized) version.
17357     *      See {@link ng.$http#overriding-the-default-transformations-per-request
17358     *      Overriding the Default Transformations}
17359     *    - **transformResponse** –
17360     *      `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` –
17361     *      transform function or an array of such functions. The transform function takes the http
17362     *      response body, headers and status and returns its transformed (typically deserialized) version.
17363     *      See {@link ng.$http#overriding-the-default-transformations-per-request
17364     *      Overriding the Default Transformations}
17365     *    - **paramSerializer** - `{string|function(Object<string,string>):string}` - A function used to
17366     *      prepare the string representation of request parameters (specified as an object).
17367     *      If specified as string, it is interpreted as function registered with the
17368     *      {@link $injector $injector}, which means you can create your own serializer
17369     *      by registering it as a {@link auto.$provide#service service}.
17370     *      The default serializer is the {@link $httpParamSerializer $httpParamSerializer};
17371     *      alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike}
17372     *    - **cache** – `{boolean|Object}` – A boolean value or object created with
17373     *      {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of the HTTP response.
17374     *      See {@link $http#caching $http Caching} for more information.
17375     *    - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
17376     *      that should abort the request when resolved.
17377     *    - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
17378     *      XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
17379     *      for more information.
17380     *    - **responseType** - `{string}` - see
17381     *      [XMLHttpRequest.responseType](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#xmlhttprequest-responsetype).
17382     *
17383     * @returns {HttpPromise} Returns a {@link ng.$q `Promise}` that will be resolved to a response object
17384     *                        when the request succeeds or fails.
17385     *
17386     *
17387     * @property {Array.<Object>} pendingRequests Array of config objects for currently pending
17388     *   requests. This is primarily meant to be used for debugging purposes.
17389     *
17390     *
17391     * @example
17392<example module="httpExample" name="http-service">
17393<file name="index.html">
17394  <div ng-controller="FetchController">
17395    <select ng-model="method" aria-label="Request method">
17396      <option>GET</option>
17397      <option>JSONP</option>
17398    </select>
17399    <input type="text" ng-model="url" size="80" aria-label="URL" />
17400    <button id="fetchbtn" ng-click="fetch()">fetch</button><br>
17401    <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
17402    <button id="samplejsonpbtn"
17403      ng-click="updateModel('JSONP',
17404                    'https://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">
17405      Sample JSONP
17406    </button>
17407    <button id="invalidjsonpbtn"
17408      ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist&callback=JSON_CALLBACK')">
17409        Invalid JSONP
17410      </button>
17411    <pre>http status code: {{status}}</pre>
17412    <pre>http response data: {{data}}</pre>
17413  </div>
17414</file>
17415<file name="script.js">
17416  angular.module('httpExample', [])
17417    .controller('FetchController', ['$scope', '$http', '$templateCache',
17418      function($scope, $http, $templateCache) {
17419        $scope.method = 'GET';
17420        $scope.url = 'http-hello.html';
17421
17422        $scope.fetch = function() {
17423          $scope.code = null;
17424          $scope.response = null;
17425
17426          $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
17427            then(function(response) {
17428              $scope.status = response.status;
17429              $scope.data = response.data;
17430            }, function(response) {
17431              $scope.data = response.data || 'Request failed';
17432              $scope.status = response.status;
17433          });
17434        };
17435
17436        $scope.updateModel = function(method, url) {
17437          $scope.method = method;
17438          $scope.url = url;
17439        };
17440      }]);
17441</file>
17442<file name="http-hello.html">
17443  Hello, $http!
17444</file>
17445<file name="protractor.js" type="protractor">
17446  var status = element(by.binding('status'));
17447  var data = element(by.binding('data'));
17448  var fetchBtn = element(by.id('fetchbtn'));
17449  var sampleGetBtn = element(by.id('samplegetbtn'));
17450  var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));
17451
17452  it('should make an xhr GET request', function() {
17453    sampleGetBtn.click();
17454    fetchBtn.click();
17455    expect(status.getText()).toMatch('200');
17456    expect(data.getText()).toMatch(/Hello, \$http!/);
17457  });
17458
17459// Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185
17460// it('should make a JSONP request to angularjs.org', function() {
17461//   var sampleJsonpBtn = element(by.id('samplejsonpbtn'));
17462//   sampleJsonpBtn.click();
17463//   fetchBtn.click();
17464//   expect(status.getText()).toMatch('200');
17465//   expect(data.getText()).toMatch(/Super Hero!/);
17466// });
17467
17468  it('should make JSONP request to invalid URL and invoke the error handler',
17469      function() {
17470    invalidJsonpBtn.click();
17471    fetchBtn.click();
17472    expect(status.getText()).toMatch('0');
17473    expect(data.getText()).toMatch('Request failed');
17474  });
17475</file>
17476</example>
17477     */
17478    function $http(requestConfig) {
17479
17480      if (!isObject(requestConfig)) {
17481        throw minErr('$http')('badreq', 'Http request configuration must be an object.  Received: {0}', requestConfig);
17482      }
17483
17484      if (!isString(requestConfig.url)) {
17485        throw minErr('$http')('badreq', 'Http request configuration url must be a string.  Received: {0}', requestConfig.url);
17486      }
17487
17488      var config = extend({
17489        method: 'get',
17490        transformRequest: defaults.transformRequest,
17491        transformResponse: defaults.transformResponse,
17492        paramSerializer: defaults.paramSerializer
17493      }, requestConfig);
17494
17495      config.headers = mergeHeaders(requestConfig);
17496      config.method = uppercase(config.method);
17497      config.paramSerializer = isString(config.paramSerializer) ?
17498          $injector.get(config.paramSerializer) : config.paramSerializer;
17499
17500      var requestInterceptors = [];
17501      var responseInterceptors = [];
17502      var promise = $q.when(config);
17503
17504      // apply interceptors
17505      forEach(reversedInterceptors, function(interceptor) {
17506        if (interceptor.request || interceptor.requestError) {
17507          requestInterceptors.unshift(interceptor.request, interceptor.requestError);
17508        }
17509        if (interceptor.response || interceptor.responseError) {
17510          responseInterceptors.push(interceptor.response, interceptor.responseError);
17511        }
17512      });
17513
17514      promise = chainInterceptors(promise, requestInterceptors);
17515      promise = promise.then(serverRequest);
17516      promise = chainInterceptors(promise, responseInterceptors);
17517
17518      if (useLegacyPromise) {
17519        promise.success = function(fn) {
17520          assertArgFn(fn, 'fn');
17521
17522          promise.then(function(response) {
17523            fn(response.data, response.status, response.headers, config);
17524          });
17525          return promise;
17526        };
17527
17528        promise.error = function(fn) {
17529          assertArgFn(fn, 'fn');
17530
17531          promise.then(null, function(response) {
17532            fn(response.data, response.status, response.headers, config);
17533          });
17534          return promise;
17535        };
17536      } else {
17537        promise.success = $httpMinErrLegacyFn('success');
17538        promise.error = $httpMinErrLegacyFn('error');
17539      }
17540
17541      return promise;
17542
17543
17544      function chainInterceptors(promise, interceptors) {
17545        for (var i = 0, ii = interceptors.length; i < ii;) {
17546          var thenFn = interceptors[i++];
17547          var rejectFn = interceptors[i++];
17548
17549          promise = promise.then(thenFn, rejectFn);
17550        }
17551
17552        interceptors.length = 0;
17553
17554        return promise;
17555      }
17556
17557      function executeHeaderFns(headers, config) {
17558        var headerContent, processedHeaders = {};
17559
17560        forEach(headers, function(headerFn, header) {
17561          if (isFunction(headerFn)) {
17562            headerContent = headerFn(config);
17563            if (headerContent != null) {
17564              processedHeaders[header] = headerContent;
17565            }
17566          } else {
17567            processedHeaders[header] = headerFn;
17568          }
17569        });
17570
17571        return processedHeaders;
17572      }
17573
17574      function mergeHeaders(config) {
17575        var defHeaders = defaults.headers,
17576            reqHeaders = extend({}, config.headers),
17577            defHeaderName, lowercaseDefHeaderName, reqHeaderName;
17578
17579        defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
17580
17581        // using for-in instead of forEach to avoid unnecessary iteration after header has been found
17582        defaultHeadersIteration:
17583        for (defHeaderName in defHeaders) {
17584          lowercaseDefHeaderName = lowercase(defHeaderName);
17585
17586          for (reqHeaderName in reqHeaders) {
17587            if (lowercase(reqHeaderName) === lowercaseDefHeaderName) {
17588              continue defaultHeadersIteration;
17589            }
17590          }
17591
17592          reqHeaders[defHeaderName] = defHeaders[defHeaderName];
17593        }
17594
17595        // execute if header value is a function for merged headers
17596        return executeHeaderFns(reqHeaders, shallowCopy(config));
17597      }
17598
vendor: 14,919 bytes, lines 17599-18059
17599      function serverRequest(config) {
17600        var headers = config.headers;
17601        var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
17602
17603        // strip content-type if data is undefined
17604        if (isUndefined(reqData)) {
17605          forEach(headers, function(value, header) {
17606            if (lowercase(header) === 'content-type') {
17607              delete headers[header];
17608            }
17609          });
17610        }
17611
17612        if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
17613          config.withCredentials = defaults.withCredentials;
17614        }
17615
17616        // send request
17617        return sendReq(config, reqData).then(transformResponse, transformResponse);
17618      }
17619
17620      function transformResponse(response) {
17621        // make a copy since the response must be cacheable
17622        var resp = extend({}, response);
17623        resp.data = transformData(response.data, response.headers, response.status,
17624                                  config.transformResponse);
17625        return (isSuccess(response.status))
17626          ? resp
17627          : $q.reject(resp);
17628      }
17629    }
17630
17631    $http.pendingRequests = [];
17632
17633    /**
17634     * @ngdoc method
17635     * @name $http#get
17636     *
17637     * @description
17638     * Shortcut method to perform `GET` request.
17639     *
17640     * @param {string} url Relative or absolute URL specifying the destination of the request
17641     * @param {Object=} config Optional configuration object
17642     * @returns {HttpPromise} Future object
17643     */
17644
17645    /**
17646     * @ngdoc method
17647     * @name $http#delete
17648     *
17649     * @description
17650     * Shortcut method to perform `DELETE` request.
17651     *
17652     * @param {string} url Relative or absolute URL specifying the destination of the request
17653     * @param {Object=} config Optional configuration object
17654     * @returns {HttpPromise} Future object
17655     */
17656
17657    /**
17658     * @ngdoc method
17659     * @name $http#head
17660     *
17661     * @description
17662     * Shortcut method to perform `HEAD` request.
17663     *
17664     * @param {string} url Relative or absolute URL specifying the destination of the request
17665     * @param {Object=} config Optional configuration object
17666     * @returns {HttpPromise} Future object
17667     */
17668
17669    /**
17670     * @ngdoc method
17671     * @name $http#jsonp
17672     *
17673     * @description
17674     * Shortcut method to perform `JSONP` request.
17675     * If you would like to customize where and how the callbacks are stored then try overriding
17676     * or decorating the {@link $jsonpCallbacks} service.
17677     *
17678     * @param {string} url Relative or absolute URL specifying the destination of the request.
17679     *                     The name of the callback should be the string `JSON_CALLBACK`.
17680     * @param {Object=} config Optional configuration object
17681     * @returns {HttpPromise} Future object
17682     */
17683    createShortMethods('get', 'delete', 'head', 'jsonp');
17684
17685    /**
17686     * @ngdoc method
17687     * @name $http#post
17688     *
17689     * @description
17690     * Shortcut method to perform `POST` request.
17691     *
17692     * @param {string} url Relative or absolute URL specifying the destination of the request
17693     * @param {*} data Request content
17694     * @param {Object=} config Optional configuration object
17695     * @returns {HttpPromise} Future object
17696     */
17697
17698    /**
17699     * @ngdoc method
17700     * @name $http#put
17701     *
17702     * @description
17703     * Shortcut method to perform `PUT` request.
17704     *
17705     * @param {string} url Relative or absolute URL specifying the destination of the request
17706     * @param {*} data Request content
17707     * @param {Object=} config Optional configuration object
17708     * @returns {HttpPromise} Future object
17709     */
17710
17711     /**
17712      * @ngdoc method
17713      * @name $http#patch
17714      *
17715      * @description
17716      * Shortcut method to perform `PATCH` request.
17717      *
17718      * @param {string} url Relative or absolute URL specifying the destination of the request
17719      * @param {*} data Request content
17720      * @param {Object=} config Optional configuration object
17721      * @returns {HttpPromise} Future object
17722      */
17723    createShortMethodsWithData('post', 'put', 'patch');
17724
17725        /**
17726         * @ngdoc property
17727         * @name $http#defaults
17728         *
17729         * @description
17730         * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
17731         * default headers, withCredentials as well as request and response transformations.
17732         *
17733         * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
17734         */
17735    $http.defaults = defaults;
17736
17737
17738    return $http;
17739
17740
17741    function createShortMethods(names) {
17742      forEach(arguments, function(name) {
17743        $http[name] = function(url, config) {
17744          return $http(extend({}, config || {}, {
17745            method: name,
17746            url: url
17747          }));
17748        };
17749      });
17750    }
17751
17752
17753    function createShortMethodsWithData(name) {
17754      forEach(arguments, function(name) {
17755        $http[name] = function(url, data, config) {
17756          return $http(extend({}, config || {}, {
17757            method: name,
17758            url: url,
17759            data: data
17760          }));
17761        };
17762      });
17763    }
17764
17765
17766    /**
17767     * Makes the request.
17768     *
17769     * !!! ACCESSES CLOSURE VARS:
17770     * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
17771     */
17772    function sendReq(config, reqData) {
17773      var deferred = $q.defer(),
17774          promise = deferred.promise,
17775          cache,
17776          cachedResp,
17777          reqHeaders = config.headers,
17778          url = buildUrl(config.url, config.paramSerializer(config.params));
17779
17780      $http.pendingRequests.push(config);
17781      promise.then(removePendingReq, removePendingReq);
17782
17783
17784      if ((config.cache || defaults.cache) && config.cache !== false &&
17785          (config.method === 'GET' || config.method === 'JSONP')) {
17786        cache = isObject(config.cache) ? config.cache
17787              : isObject(defaults.cache) ? defaults.cache
17788              : defaultCache;
17789      }
17790
17791      if (cache) {
17792        cachedResp = cache.get(url);
17793        if (isDefined(cachedResp)) {
17794          if (isPromiseLike(cachedResp)) {
17795            // cached request has already been sent, but there is no response yet
17796            cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult);
17797          } else {
17798            // serving from cache
17799            if (isArray(cachedResp)) {
17800              resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
17801            } else {
17802              resolvePromise(cachedResp, 200, {}, 'OK');
17803            }
17804          }
17805        } else {
17806          // put the promise for the non-transformed response into cache as a placeholder
17807          cache.put(url, promise);
17808        }
17809      }
17810
17811
17812      // if we won't have the response in cache, set the xsrf headers and
17813      // send the request to the backend
17814      if (isUndefined(cachedResp)) {
17815        var xsrfValue = urlIsSameOrigin(config.url)
17816            ? $$cookieReader()[config.xsrfCookieName || defaults.xsrfCookieName]
17817            : undefined;
17818        if (xsrfValue) {
17819          reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
17820        }
17821
17822        $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
17823            config.withCredentials, config.responseType,
17824            createApplyHandlers(config.eventHandlers),
17825            createApplyHandlers(config.uploadEventHandlers));
17826      }
17827
17828      return promise;
17829
17830      function createApplyHandlers(eventHandlers) {
17831        if (eventHandlers) {
17832          var applyHandlers = {};
17833          forEach(eventHandlers, function(eventHandler, key) {
17834            applyHandlers[key] = function(event) {
17835              if (useApplyAsync) {
17836                $rootScope.$applyAsync(callEventHandler);
17837              } else if ($rootScope.$$phase) {
17838                callEventHandler();
17839              } else {
17840                $rootScope.$apply(callEventHandler);
17841              }
17842
17843              function callEventHandler() {
17844                eventHandler(event);
17845              }
17846            };
17847          });
17848          return applyHandlers;
17849        }
17850      }
17851
17852
17853      /**
17854       * Callback registered to $httpBackend():
17855       *  - caches the response if desired
17856       *  - resolves the raw $http promise
17857       *  - calls $apply
17858       */
17859      function done(status, response, headersString, statusText) {
17860        if (cache) {
17861          if (isSuccess(status)) {
17862            cache.put(url, [status, response, parseHeaders(headersString), statusText]);
17863          } else {
17864            // remove promise from the cache
17865            cache.remove(url);
17866          }
17867        }
17868
17869        function resolveHttpPromise() {
17870          resolvePromise(response, status, headersString, statusText);
17871        }
17872
17873        if (useApplyAsync) {
17874          $rootScope.$applyAsync(resolveHttpPromise);
17875        } else {
17876          resolveHttpPromise();
17877          if (!$rootScope.$$phase) $rootScope.$apply();
17878        }
17879      }
17880
17881
17882      /**
17883       * Resolves the raw $http promise.
17884       */
17885      function resolvePromise(response, status, headers, statusText) {
17886        //status: HTTP response status code, 0, -1 (aborted by timeout / promise)
17887        status = status >= -1 ? status : 0;
17888
17889        (isSuccess(status) ? deferred.resolve : deferred.reject)({
17890          data: response,
17891          status: status,
17892          headers: headersGetter(headers),
17893          config: config,
17894          statusText: statusText
17895        });
17896      }
17897
17898      function resolvePromiseWithResult(result) {
17899        resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText);
17900      }
17901
17902      function removePendingReq() {
17903        var idx = $http.pendingRequests.indexOf(config);
17904        if (idx !== -1) $http.pendingRequests.splice(idx, 1);
17905      }
17906    }
17907
17908
17909    function buildUrl(url, serializedParams) {
17910      if (serializedParams.length > 0) {
17911        url += ((url.indexOf('?') === -1) ? '?' : '&') + serializedParams;
17912      }
17913      return url;
17914    }
17915  }];
17916}
17917
17918/**
17919 * @ngdoc service
17920 * @name $xhrFactory
17921 * @this
17922 *
17923 * @description
17924 * Factory function used to create XMLHttpRequest objects.
17925 *
17926 * Replace or decorate this service to create your own custom XMLHttpRequest objects.
17927 *
17928 * ```
17929 * angular.module('myApp', [])
17930 * .factory('$xhrFactory', function() {
17931 *   return function createXhr(method, url) {
17932 *     return new window.XMLHttpRequest({mozSystem: true});
17933 *   };
17934 * });
17935 * ```
17936 *
17937 * @param {string} method HTTP method of the request (GET, POST, PUT, ..)
17938 * @param {string} url URL of the request.
17939 */
17940function $xhrFactoryProvider() {
17941  this.$get = function() {
17942    return function createXhr() {
17943      return new window.XMLHttpRequest();
17944    };
17945  };
17946}
17947
17948/**
17949 * @ngdoc service
17950 * @name $httpBackend
17951 * @requires $jsonpCallbacks
17952 * @requires $document
17953 * @requires $xhrFactory
17954 * @this
17955 *
17956 * @description
17957 * HTTP backend used by the {@link ng.$http service} that delegates to
17958 * XMLHttpRequest object or JSONP and deals with browser incompatibilities.
17959 *
17960 * You should never need to use this service directly, instead use the higher-level abstractions:
17961 * {@link ng.$http $http} or {@link ngResource.$resource $resource}.
17962 *
17963 * During testing this implementation is swapped with {@link ngMock.$httpBackend mock
17964 * $httpBackend} which can be trained with responses.
17965 */
17966function $HttpBackendProvider() {
17967  this.$get = ['$browser', '$jsonpCallbacks', '$document', '$xhrFactory', function($browser, $jsonpCallbacks, $document, $xhrFactory) {
17968    return createHttpBackend($browser, $xhrFactory, $browser.defer, $jsonpCallbacks, $document[0]);
17969  }];
17970}
17971
17972function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
17973  // TODO(vojta): fix the signature
17974  return function(method, url, post, callback, headers, timeout, withCredentials, responseType, eventHandlers, uploadEventHandlers) {
17975    $browser.$$incOutstandingRequestCount();
17976    url = url || $browser.url();
17977
17978    if (lowercase(method) === 'jsonp') {
17979      var callbackPath = callbacks.createCallback(url);
17980      var jsonpDone = jsonpReq(url, callbackPath, function(status, text) {
17981        // jsonpReq only ever sets status to 200 (OK), 404 (ERROR) or -1 (WAITING)
17982        var response = (status === 200) && callbacks.getResponse(callbackPath);
17983        completeRequest(callback, status, response, '', text);
17984        callbacks.removeCallback(callbackPath);
17985      });
17986    } else {
17987
17988      var xhr = createXhr(method, url);
17989
17990      xhr.open(method, url, true);
17991      forEach(headers, function(value, key) {
17992        if (isDefined(value)) {
17993            xhr.setRequestHeader(key, value);
17994        }
17995      });
17996
17997      xhr.onload = function requestLoaded() {
17998        var statusText = xhr.statusText || '';
17999
18000        // responseText is the old-school way of retrieving response (supported by IE9)
18001        // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
18002        var response = ('response' in xhr) ? xhr.response : xhr.responseText;
18003
18004        // normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
18005        var status = xhr.status === 1223 ? 204 : xhr.status;
18006
18007        // fix status code when it is 0 (0 status is undocumented).
18008        // Occurs when accessing file resources or on Android 4.1 stock browser
18009        // while retrieving files from application cache.
18010        if (status === 0) {
18011          status = response ? 200 : urlResolve(url).protocol === 'file' ? 404 : 0;
18012        }
18013
18014        completeRequest(callback,
18015            status,
18016            response,
18017            xhr.getAllResponseHeaders(),
18018            statusText);
18019      };
18020
18021      var requestError = function() {
18022        // The response is always empty
18023        // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
18024        completeRequest(callback, -1, null, null, '');
18025      };
18026
18027      xhr.onerror = requestError;
18028      xhr.onabort = requestError;
18029      xhr.ontimeout = requestError;
18030
18031      forEach(eventHandlers, function(value, key) {
18032          xhr.addEventListener(key, value);
18033      });
18034
18035      forEach(uploadEventHandlers, function(value, key) {
18036        xhr.upload.addEventListener(key, value);
18037      });
18038
18039      if (withCredentials) {
18040        xhr.withCredentials = true;
18041      }
18042
18043      if (responseType) {
18044        try {
18045          xhr.responseType = responseType;
18046        } catch (e) {
18047          // WebKit added support for the json responseType value on 09/03/2013
18048          // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
18049          // known to throw when setting the value "json" as the response type. Other older
18050          // browsers implementing the responseType
18051          //
18052          // The json response type can be ignored if not supported, because JSON payloads are
18053          // parsed on the client-side regardless.
18054          if (responseType !== 'json') {
18055            throw e;
18056          }
18057        }
18058      }
18059
18060      xhr.send(isUndefined(post) ? null : post);
18061    }
18062
18063    if (timeout > 0) {
18064      var timeoutId = $browserDefer(timeoutRequest, timeout);
18065    } else if (isPromiseLike(timeout)) {
18066      timeout.then(timeoutRequest);
18067    }
18068
18069
18070    function timeoutRequest() {
18071      if (jsonpDone) {
18072        jsonpDone();
18073      }
18074      if (xhr) {
18075        xhr.abort();
18076      }
18077    }
18078
18079    function completeRequest(callback, status, response, headersString, statusText) {
18080      // cancel timeout and subsequent timeout promise resolution
18081      if (isDefined(timeoutId)) {
18082        $browserDefer.cancel(timeoutId);
18083      }
18084      jsonpDone = xhr = null;
18085
18086      callback(status, response, headersString, statusText);
18087      $browser.$$completeOutstandingRequest(noop);
18088    }
18089  };
18090
18091  function jsonpReq(url, callbackPath, done) {
18092    url = url.replace('JSON_CALLBACK', callbackPath);
18093    // we can't use jQuery/jqLite here because jQuery does crazy stuff with script elements, e.g.:
18094    // - fetches local scripts via XHR and evals them
18095    // - adds and immediately removes script elements from the document
18096    var script = rawDocument.createElement('script'), callback = null;
18097    script.type = 'text/javascript';
18098    script.src = url;
18099    script.async = true;
18100
18101    callback = function(event) {
18102      removeEventListenerFn(script, 'load', callback);
18103      removeEventListenerFn(script, 'error', callback);
18104      rawDocument.body.removeChild(script);
18105      script = null;
18106      var status = -1;
18107      var text = 'unknown';
18108
18109      if (event) {
18110        if (event.type === 'load' && !callbacks.wasCalled(callbackPath)) {
18111          event = { type: 'error' };
18112        }
18113        text = event.type;
18114        status = event.type === 'error' ? 404 : 200;
18115      }
18116
18117      if (done) {
18118        done(status, text);
18119      }
18120    };
18121
18122    addEventListenerFn(script, 'load', callback);
18123    addEventListenerFn(script, 'error', callback);
18124    rawDocument.body.appendChild(script);
18125    return callback;
18126  }
18127}
18128
18129var $interpolateMinErr = angular.$interpolateMinErr = minErr('$interpolate');
18130$interpolateMinErr.throwNoconcat = function(text) {
18131  throw $interpolateMinErr('noconcat',
18132      'Error while interpolating: {0}\nStrict Contextual Escaping disallows ' +
18133      'interpolations that concatenate multiple expressions when a trusted value is ' +
18134      'required.  See http://docs.angularjs.org/api/ng.$sce', text);
18135};
18136
18137$interpolateMinErr.interr = function(text, err) {
18138  return $interpolateMinErr('interr', 'Can\'t interpolate: {0}\n{1}', text, err.toString());
18139};
18140
18141/**
18142 * @ngdoc provider
18143 * @name $interpolateProvider
18144 * @this
18145 *
18146 * @description
18147 *
18148 * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
18149 *
18150 * <div class="alert alert-danger">
18151 * This feature is sometimes used to mix different markup languages, e.g. to wrap an Angular
18152 * template within a Python Jinja template (or any other template language). Mixing templating
18153 * languages is **very dangerous**. The embedding template language will not safely escape Angular
18154 * expressions, so any user-controlled values in the template will cause Cross Site Scripting (XSS)
18155 * security bugs!
18156 * </div>
18157 *
18158 * @example
18159<example name="custom-interpolation-markup" module="customInterpolationApp">
18160<file name="index.html">
18161<script>
18162  var customInterpolationApp = angular.module('customInterpolationApp', []);
18163
18164  customInterpolationApp.config(function($interpolateProvider) {
18165    $interpolateProvider.startSymbol('//');
18166    $interpolateProvider.endSymbol('//');
18167  });
18168
18169
18170  customInterpolationApp.controller('DemoController', function() {
18171      this.label = "This binding is brought you by // interpolation symbols.";
18172  });
18173</script>
18174<div ng-controller="DemoController as demo">
18175    //demo.label//
18176</div>
18177</file>
18178<file name="protractor.js" type="protractor">
18179  it('should interpolate binding with custom symbols', function() {
18180    expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
18181  });
18182</file>
18183</example>
18184 */
18185function $InterpolateProvider() {
18186  var startSymbol = '{{';
18187  var endSymbol = '}}';
18188
18189  /**
18190   * @ngdoc method
18191   * @name $interpolateProvider#startSymbol
18192   * @description
18193   * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
18194   *
18195   * @param {string=} value new value to set the starting symbol to.
18196   * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
18197   */
18198  this.startSymbol = function(value) {
18199    if (value) {
18200      startSymbol = value;
18201      return this;
18202    } else {
18203      return startSymbol;
18204    }
18205  };
18206
18207  /**
18208   * @ngdoc method
18209   * @name $interpolateProvider#endSymbol
18210   * @description
18211   * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
18212   *
18213   * @param {string=} value new value to set the ending symbol to.
18214   * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
18215   */
18216  this.endSymbol = function(value) {
18217    if (value) {
18218      endSymbol = value;
18219      return this;
18220    } else {
18221      return endSymbol;
18222    }
18223  };
18224
18225
18226  this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
18227    var startSymbolLength = startSymbol.length,
18228        endSymbolLength = endSymbol.length,
18229        escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'),
18230        escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g');
18231
18232    function escape(ch) {
18233      return '\\\\\\' + ch;
18234    }
18235
18236    function unescapeText(text) {
18237      return text.replace(escapedStartRegexp, startSymbol).
18238        replace(escapedEndRegexp, endSymbol);
18239    }
18240
18241    function stringify(value) {
18242      if (value == null) { // null || undefined
18243        return '';
18244      }
18245      switch (typeof value) {
18246        case 'string':
18247          break;
18248        case 'number':
18249          value = '' + value;
18250          break;
18251        default:
18252          value = toJson(value);
18253      }
18254
18255      return value;
18256    }
18257
18258    // TODO: this is the same as the constantWatchDelegate in parse.js
18259    function constantWatchDelegate(scope, listener, objectEquality, constantInterp) {
18260      var unwatch = scope.$watch(function constantInterpolateWatch(scope) {
18261        unwatch();
18262        return constantInterp(scope);
18263      }, listener, objectEquality);
18264      return unwatch;
18265    }
18266
18267    /**
18268     * @ngdoc service
18269     * @name $interpolate
18270     * @kind function
18271     *
18272     * @requires $parse
18273     * @requires $sce
18274     *
18275     * @description
18276     *
18277     * Compiles a string with markup into an interpolation function. This service is used by the
18278     * HTML {@link ng.$compile $compile} service for data binding. See
18279     * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
18280     * interpolation markup.
18281     *
18282     *
18283     * ```js
18284     *   var $interpolate = ...; // injected
18285     *   var exp = $interpolate('Hello {{name | uppercase}}!');
18286     *   expect(exp({name:'Angular'})).toEqual('Hello ANGULAR!');
18287     * ```
18288     *
18289     * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
18290     * `true`, the interpolation function will return `undefined` unless all embedded expressions
18291     * evaluate to a value other than `undefined`.
18292     *
18293     * ```js
18294     *   var $interpolate = ...; // injected
18295     *   var context = {greeting: 'Hello', name: undefined };
18296     *
18297     *   // default "forgiving" mode
18298     *   var exp = $interpolate('{{greeting}} {{name}}!');
18299     *   expect(exp(context)).toEqual('Hello !');
18300     *
18301     *   // "allOrNothing" mode
18302     *   exp = $interpolate('{{greeting}} {{name}}!', false, null, true);
18303     *   expect(exp(context)).toBeUndefined();
18304     *   context.name = 'Angular';
18305     *   expect(exp(context)).toEqual('Hello Angular!');
18306     * ```
18307     *
18308     * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
18309     *
18310     * #### Escaped Interpolation
18311     * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
18312     * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
18313     * It will be rendered as a regular start/end marker, and will not be interpreted as an expression
18314     * or binding.
18315     *
18316     * This enables web-servers to prevent script injection attacks and defacing attacks, to some
18317     * degree, while also enabling code examples to work without relying on the
18318     * {@link ng.directive:ngNonBindable ngNonBindable} directive.
18319     *
18320     * **For security purposes, it is strongly encouraged that web servers escape user-supplied data,
18321     * replacing angle brackets (&lt;, &gt;) with &amp;lt; and &amp;gt; respectively, and replacing all
18322     * interpolation start/end markers with their escaped counterparts.**
18323     *
18324     * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered
18325     * output when the $interpolate service processes the text. So, for HTML elements interpolated
18326     * by {@link ng.$compile $compile}
18326, or otherwise interpolated with the `mustHaveExpression` parameter
18327     * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such,
18328     * this is typically useful only when user-data is used in rendering a template from the server, or
18329     * when otherwise untrusted data is used by a directive.
18330     *
18331     * <example name="interpolation">
18332     *  <file name="index.html">
18333     *    <div ng-init="username='A user'">
18334     *      <p ng-init="apptitle='Escaping demo'">{{apptitle}}: \{\{ username = "defaced value"; \}\}
18335     *        </p>
18336     *      <p><strong>{{username}}</strong> attempts to inject code which will deface the
18337     *        application, but fails to accomplish their task, because the server has correctly
18338     *        escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash)
18339     *        characters.</p>
18340     *      <p>Instead, the result of the attempted script injection is visible, and can be removed
18341     *        from the database by an administrator.</p>
18342     *    </div>
18343     *  </file>
18344     * </example>
18345     *
18346     * @knownIssue
18347     * It is currently not possible for an interpolated expression to contain the interpolation end
18348     * symbol. For example, `{{ '}}' }}` will be incorrectly interpreted as `{{ ' }}` + `' }}`, i.e.
18349     * an interpolated expression consisting of a single-quote (`'`) and the `' }}` string.
18350     *
18351     * @knownIssue
18352     * All directives and components must use the standard `{{` `}}` interpolation symbols
18353     * in their templates. If you change the application interpolation symbols the {@link $compile}
18354     * service will attempt to denormalize the standard symbols to the custom symbols.
18355     * The denormalization process is not clever enough to know not to replace instances of the standard
18356     * symbols where they would not normally be treated as interpolation symbols. For example in the following
18357     * code snippet the closing braces of the literal object will get incorrectly denormalized:
18358     *
18359     * ```
18360     * <div data-context='{"context":{"id":3,"type":"page"}}">
18361     * ```
18362     *
18363     * The workaround is to ensure that such instances are separated by whitespace:
18364     * ```
18365     * <div data-context='{"context":{"id":3,"type":"page"} }">
18366     * ```
18367     *
18368     * See https://github.com/angular/angular.js/pull/14610#issuecomment-219401099 for more information.
18369     *
18370     * @param {string} text The text with markup to interpolate.
18371     * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
18372     *    embedded expression in order to return an interpolation function. Strings with no
18373     *    embedded expression will return null for the interpolation function.
18374     * @param {string=} trustedContext when provided, the returned function passes the interpolated
18375     *    result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
18376     *    trustedContext)} before returning it.  Refer to the {@link ng.$sce $sce} service that
18377     *    provides Strict Contextual Escaping for details.
18378     * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined
18379     *    unless all embedded expressions evaluate to a value other than `undefined`.
18380     * @returns {function(context)} an interpolation function which is used to compute the
18381     *    interpolated string. The function has these parameters:
18382     *
18383     * - `context`: evaluation context for all expressions embedded in the interpolated text
18384     */
18385    function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
18386      // Provide a quick exit and simplified result function for text with no interpolation
18387      if (!text.length || text.indexOf(startSymbol) === -1) {
18388        var constantInterp;
18389        if (!mustHaveExpression) {
18390          var unescapedText = unescapeText(text);
18391          constantInterp = valueFn(unescapedText);
18392          constantInterp.exp = text;
18393          constantInterp.expressions = [];
18394          constantInterp.$$watchDelegate = constantWatchDelegate;
18395        }
18396        return constantInterp;
18397      }
18398
18399      allOrNothing = !!allOrNothing;
18400      var startIndex,
18401          endIndex,
18402          index = 0,
18403          expressions = [],
18404          parseFns = [],
18405          textLength = text.length,
18406          exp,
18407          concat = [],
18408          expressionPositions = [];
18409
18410      while (index < textLength) {
18411        if (((startIndex = text.indexOf(startSymbol, index)) !== -1) &&
18412             ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) !== -1)) {
18413          if (index !== startIndex) {
18414            concat.push(unescapeText(text.substring(index, startIndex)));
18415          }
18416          exp = text.substring(startIndex + startSymbolLength, endIndex);
18417          expressions.push(exp);
18418          parseFns.push($parse(exp, parseStringifyInterceptor));
18419          index = endIndex + endSymbolLength;
18420          expressionPositions.push(concat.length);
18421          concat.push('');
18422        } else {
18423          // we did not find an interpolation, so we have to add the remainder to the separators array
18424          if (index !== textLength) {
18425            concat.push(unescapeText(text.substring(index)));
18426          }
18427          break;
18428        }
18429      }
18430
18431      // Concatenating expressions makes it hard to reason about whether some combination of
18432      // concatenated values are unsafe to use and could easily lead to XSS.  By requiring that a
18433      // single expression be used for iframe[src], object[src], etc., we ensure that the value
18434      // that's used is assigned or constructed by some JS code somewhere that is more testable or
18435      // make it obvious that you bound the value to some user controlled value.  This helps reduce
18436      // the load when auditing for XSS issues.
18437      if (trustedContext && concat.length > 1) {
18438          $interpolateMinErr.throwNoconcat(text);
18439      }
18440
18441      if (!mustHaveExpression || expressions.length) {
18442        var compute = function(values) {
18443          for (var i = 0, ii = expressions.length; i < ii; i++) {
18444            if (allOrNothing && isUndefined(values[i])) return;
18445            concat[expressionPositions[i]] = values[i];
18446          }
18447          return concat.join('');
18448        };
18449
18450        var getValue = function(value) {
18451          return trustedContext ?
18452            $sce.getTrusted(trustedContext, value) :
18453            $sce.valueOf(value);
18454        };
18455
18456        return extend(function interpolationFn(context) {
18457            var i = 0;
18458            var ii = expressions.length;
18459            var values = new Array(ii);
18460
18461            try {
18462              for (; i < ii; i++) {
18463                values[i] = parseFns[i](context);
18464              }
18465
18466              return compute(values);
18467            } catch (err) {
18468              $exceptionHandler($interpolateMinErr.interr(text, err));
18469            }
18470
18471          }, {
18472          // all of these properties are undocumented for now
18473          exp: text, //just for compatibility with regular watchers created via $watch
18474          expressions: expressions,
18475          $$watchDelegate: function(scope, listener) {
18476            var lastValue;
18477            return scope.$watchGroup(parseFns, /** @this */ function interpolateFnWatcher(values, oldValues) {
18478              var currValue = compute(values);
18479              if (isFunction(listener)) {
18480                listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
18481              }
18482              lastValue = currValue;
18483            });
18484          }
18485        });
18486      }
18487
18488      function parseStringifyInterceptor(value) {
18489        try {
18490          value = getValue(value);
18491          return allOrNothing && !isDefined(value) ? value : stringify(value);
18492        } catch (err) {
18493          $exceptionHandler($interpolateMinErr.interr(text, err));
18494        }
18495      }
18496    }
18497
18498
18499    /**
18500     * @ngdoc method
18501     * @name $interpolate#startSymbol
18502     * @description
18503     * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
18504     *
18505     * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
18506     * the symbol.
18507     *
18508     * @returns {string} start symbol.
18509     */
18510    $interpolate.startSymbol = function() {
18511      return startSymbol;
18512    };
18513
18514
18515    /**
18516     * @ngdoc method
18517     * @name $interpolate#endSymbol
18518     * @description
18519     * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
18520     *
18521     * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
18522     * the symbol.
18523     *
18524     * @returns {string} end symbol.
18525     */
18526    $interpolate.endSymbol = function() {
18527      return endSymbol;
18528    };
18529
18530    return $interpolate;
18531  }];
18532}
18533
18534/** @this */
18535function $IntervalProvider() {
18536  this.$get = ['$rootScope', '$window', '$q', '$$q', '$browser',
18537       function($rootScope,   $window,   $q,   $$q,   $browser) {
18538    var intervals = {};
18539
18540
18541     /**
18542      * @ngdoc service
18543      * @name $interval
18544      *
18545      * @description
18546      * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
18547      * milliseconds.
18548      *
18549      * The return value of registering an interval function is a promise. This promise will be
18550      * notified upon each tick of the interval, and will be resolved after `count` iterations, or
18551      * run indefinitely if `count` is not defined. The value of the notification will be the
18552      * number of iterations that have run.
18553      * To cancel an interval, call `$interval.cancel(promise)`.
18554      *
18555      * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
18556      * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
18557      * time.
18558      *
18559      * <div class="alert alert-warning">
18560      * **Note**: Intervals created by this service must be explicitly destroyed when you are finished
18561      * with them.  In particular they are not automatically destroyed when a controller's scope or a
18562      * directive's element are destroyed.
18563      * You should take this into consideration and make sure to always cancel the interval at the
18564      * appropriate moment.  See the example below for more details on how and when to do this.
18565      * </div>
18566      *
18567      * @param {function()} fn A function that should be called repeatedly. If no additional arguments
18568      *   are passed (see below), the function is called with the current iteration count.
18569      * @param {number} delay Number of milliseconds between each function call.
18570      * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
18571      *   indefinitely.
18572      * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
18573      *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
18574      * @param {...*=} Pass additional parameters to the executed function.
18575      * @returns {promise} A promise which will be notified on each iteration.
18576      *
18577      * @example
18578      * <example module="intervalExample" name="interval-service">
18579      * <file name="index.html">
18580      *   <script>
18581      *     angular.module('intervalExample', [])
18582      *       .controller('ExampleController', ['$scope', '$interval',
18583      *         function($scope, $interval) {
18584      *           $scope.format = 'M/d/yy h:mm:ss a';
18585      *           $scope.blood_1 = 100;
18586      *           $scope.blood_2 = 120;
18587      *
18588      *           var stop;
18589      *           $scope.fight = function() {
18590      *             // Don't start a new fight if we are already fighting
18591      *             if ( angular.isDefined(stop) ) return;
18592      *
18593      *             stop = $interval(function() {
18594      *               if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
18595      *                 $scope.blood_1 = $scope.blood_1 - 3;
18596      *                 $scope.blood_2 = $scope.blood_2 - 4;
18597      *               } else {
18598      *                 $scope.stopFight();
18599      *               }
18600      *             }, 100);
18601      *           };
18602      *
18603      *           $scope.stopFight = function() {
18604      *             if (angular.isDefined(stop)) {
18605      *               $interval.cancel(stop);
18606      *               stop = undefined;
18607      *             }
18608      *           };
18609      *
18610      *           $scope.resetFight = function() {
18611      *             $scope.blood_1 = 100;
18612      *             $scope.blood_2 = 120;
18613      *           };
18614      *
18615      *           $scope.$on('$destroy', function() {
18616      *             // Make sure that the interval is destroyed too
18617      *             $scope.stopFight();
18618      *           });
18619      *         }])
18620      *       // Register the 'myCurrentTime' directive factory method.
18621      *       // We inject $interval and dateFilter service since the factory method is DI.
18622      *       .directive('myCurrentTime', ['$interval', 'dateFilter',
18623      *         function($interval, dateFilter) {
18624      *           // return the directive link function. (compile function not needed)
18625      *           return function(scope, element, attrs) {
18626      *             var format,  // date format
18627      *                 stopTime; // so that we can cancel the time updates
18628      *
18629      *             // used to update the UI
18630      *             function updateTime() {
18631      *               element.text(dateFilter(new Date(), format));
18632      *             }
18633      *
18634      *             // watch the expression, and update the UI on change.
18635      *             scope.$watch(attrs.myCurrentTime, function(value) {
18636      *               format = value;
18637      *               updateTime();
18638      *             });
18639      *
18640      *             stopTime = $interval(updateTime, 1000);
18641      *
18642      *             // listen on DOM destroy (removal) event, and cancel the next UI update
18643      *             // to prevent updating time after the DOM element was removed.
18644      *             element.on('$destroy', function() {
18645      *               $interval.cancel(stopTime);
18646      *             });
18647      *           }
18648      *         }]);
18649      *   </script>
18650      *
18651      *   <div>
18652      *     <div ng-controller="ExampleController">
18653      *       <label>Date format: <input ng-model="format"></label> <hr/>
18654      *       Current time is: <span my-current-time="format"></span>
18655      *       <hr/>
18656      *       Blood 1 : <font color='red'>{{blood_1}}</font>
18657      *       Blood 2 : <font color='red'>{{blood_2}}</font>
18658      *       <button type="button" data-ng-click="fight()">Fight</button>
18659      *       <button type="button" data-ng-click="stopFight()">StopFight</button>
18660      *       <button type="button" data-ng-click="resetFight()">resetFight</button>
18661      *     </div>
18662      *   </div>
18663      *
18664      * </file>
18665      * </example>
18666      */
18667    function interval(fn, delay, count, invokeApply) {
18668      var hasParams = arguments.length > 4,
18669          args = hasParams ? sliceArgs(arguments, 4) : [],
18670          setInterval = $window.setInterval,
18671          clearInterval = $window.clearInterval,
18672          iteration = 0,
18673          skipApply = (isDefined(invokeApply) && !invokeApply),
18674          deferred = (skipApply ? $$q : $q).defer(),
18675          promise = deferred.promise;
18676
18677      count = isDefined(count) ? count : 0;
18678
18679      promise.$$intervalId = setInterval(function tick() {
18680        if (skipApply) {
18681          $browser.defer(callback);
18682        } else {
18683          $rootScope.$evalAsync(callback);
18684        }
18685        deferred.notify(iteration++);
18686
18687        if (count > 0 && iteration >= count) {
18688          deferred.resolve(iteration);
18689          clearInterval(promise.$$intervalId);
18690          delete intervals[promise.$$intervalId];
18691        }
18692
18693        if (!skipApply) $rootScope.$apply();
18694
18695      }, delay);
18696
18697      intervals[promise.$$intervalId] = deferred;
18698
18699      return promise;
18700
18701      function callback() {
18702        if (!hasParams) {
18703          fn(iteration);
18704        } else {
18705          fn.apply(null, args);
18706        }
18707      }
18708    }
18709
18710
18711     /**
18712      * @ngdoc method
18713      * @name $interval#cancel
18714      *
18715      * @description
18716      * Cancels a task associated with the `promise`.
18717      *
18718      * @param {Promise=} promise returned by the `$interval` function.
18719      * @returns {boolean} Returns `true` if the task was successfully canceled.
18720      */
18721    interval.cancel = function(promise) {
18722      if (promise && promise.$$intervalId in intervals) {
18723        intervals[promise.$$intervalId].reject('canceled');
18724        $window.clearInterval(promise.$$intervalId);
18725        delete intervals[promise.$$intervalId];
18726        return true;
18727      }
18728      return false;
18729    };
18730
18731    return interval;
18732  }];
18733}
18734
18735/**
18736 * @ngdoc service
18737 * @name $jsonpCallbacks
18738 * @requires $window
18739 * @description
18740 * This service handles the lifecycle of callbacks to handle JSONP requests.
18741 * Override this service if you wish to customise where the callbacks are stored and
18742 * how they vary compared to the requested url.
18743 */
18744var $jsonpCallbacksProvider = /** @this */ function() {
18745  this.$get = ['$window', function($window) {
18746    var callbacks = $window.angular.callbacks;
18747    var callbackMap = {};
18748
18749    function createCallback(callbackId) {
18750      var callback = function(data) {
18751        callback.data = data;
18752        callback.called = true;
18753      };
18754      callback.id = callbackId;
18755      return callback;
18756    }
18757
18758    return {
18759      /**
18760       * @ngdoc method
18761       * @name $jsonpCallbacks#createCallback
18762       * @param {string} url the url of the JSONP request
18763       * @returns {string} the callback path to send to the server as part of the JSONP request
18764       * @description
18765       * {@link $httpBackend} calls this method to create a callback and get hold of the path to the callback
18766       * to pass to the server, which will be used to call the callback with its payload in the JSONP response.
18767       */
18768      createCallback: function(url) {
18769        var callbackId = '_' + (callbacks.$$counter++).toString(36);
18770        var callbackPath = 'angular.callbacks.' + callbackId;
18771        var callback = createCallback(callbackId);
18772        callbackMap[callbackPath] = callbacks[callbackId] = callback;
18773        return callbackPath;
18774      },
18775      /**
18776       * @ngdoc method
18777       * @name $jsonpCallbacks#wasCalled
18778       * @param {string} callbackPath the path to the callback that was sent in the JSONP request
18779       * @returns {boolean} whether the callback has been called, as a result of the JSONP response
vendor: 9,221 bytes, lines 18780-19091
18780       * @description
18781       * {@link $httpBackend} calls this method to find out whether the JSONP response actually called the
18782       * callback that was passed in the request.
18783       */
18784      wasCalled: function(callbackPath) {
18785        return callbackMap[callbackPath].called;
18786      },
18787      /**
18788       * @ngdoc method
18789       * @name $jsonpCallbacks#getResponse
18790       * @param {string} callbackPath the path to the callback that was sent in the JSONP request
18791       * @returns {*} the data received from the response via the registered callback
18792       * @description
18793       * {@link $httpBackend} calls this method to get hold of the data that was provided to the callback
18794       * in the JSONP response.
18795       */
18796      getResponse: function(callbackPath) {
18797        return callbackMap[callbackPath].data;
18798      },
18799      /**
18800       * @ngdoc method
18801       * @name $jsonpCallbacks#removeCallback
18802       * @param {string} callbackPath the path to the callback that was sent in the JSONP request
18803       * @description
18804       * {@link $httpBackend} calls this method to remove the callback after the JSONP request has
18805       * completed or timed-out.
18806       */
18807      removeCallback: function(callbackPath) {
18808        var callback = callbackMap[callbackPath];
18809        delete callbacks[callback.id];
18810        delete callbackMap[callbackPath];
18811      }
18812    };
18813  }];
18814};
18815
18816/**
18817 * @ngdoc service
18818 * @name $locale
18819 *
18820 * @description
18821 * $locale service provides localization rules for various Angular components. As of right now the
18822 * only public api is:
18823 *
18824 * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
18825 */
18826
18827var PATH_MATCH = /^([^?#]*)(\?([^#]*))?(#(.*))?$/,
18828    DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
18829var $locationMinErr = minErr('$location');
18830
18831
18832/**
18833 * Encode path using encodeUriSegment, ignoring forward slashes
18834 *
18835 * @param {string} path Path to encode
18836 * @returns {string}
18837 */
18838function encodePath(path) {
18839  var segments = path.split('/'),
18840      i = segments.length;
18841
18842  while (i--) {
18843    segments[i] = encodeUriSegment(segments[i]);
18844  }
18845
18846  return segments.join('/');
18847}
18848
18849function parseAbsoluteUrl(absoluteUrl, locationObj) {
18850  var parsedUrl = urlResolve(absoluteUrl);
18851
18852  locationObj.$$protocol = parsedUrl.protocol;
18853  locationObj.$$host = parsedUrl.hostname;
18854  locationObj.$$port = toInt(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
18855}
18856
18857var DOUBLE_SLASH_REGEX = /^\s*[\\/]{2,}/;
18858function parseAppUrl(url, locationObj) {
18859
18860  if (DOUBLE_SLASH_REGEX.test(url)) {
18861    throw $locationMinErr('badpath', 'Invalid url "{0}".', url);
18862  }
18863
18864  var prefixed = (url.charAt(0) !== '/');
18865  if (prefixed) {
18866    url = '/' + url;
18867  }
18868  var match = urlResolve(url);
18869  locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
18870      match.pathname.substring(1) : match.pathname);
18871  locationObj.$$search = parseKeyValue(match.search);
18872  locationObj.$$hash = decodeURIComponent(match.hash);
18873
18874  // make sure path starts with '/';
18875  if (locationObj.$$path && locationObj.$$path.charAt(0) !== '/') {
18876    locationObj.$$path = '/' + locationObj.$$path;
18877  }
18878}
18879
18880function startsWith(str, search) {
18881  return str.slice(0, search.length) === search;
18882}
18883
18884/**
18885 *
18886 * @param {string} base
18887 * @param {string} url
18888 * @returns {string} returns text from `url` after `base` or `undefined` if it does not begin with
18889 *                   the expected string.
18890 */
18891function stripBaseUrl(base, url) {
18892  if (startsWith(url, base)) {
18893    return url.substr(base.length);
18894  }
18895}
18896
18897
18898function stripHash(url) {
18899  var index = url.indexOf('#');
18900  return index === -1 ? url : url.substr(0, index);
18901}
18902
18903function trimEmptyHash(url) {
18904  return url.replace(/(#.+)|#$/, '$1');
18905}
18906
18907
18908function stripFile(url) {
18909  return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
18910}
18911
18912/* return the server only (scheme://host:port) */
18913function serverBase(url) {
18914  return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
18915}
18916
18917
18918/**
18919 * LocationHtml5Url represents a URL
18920 * This object is exposed as $location service when HTML5 mode is enabled and supported
18921 *
18922 * @constructor
18923 * @param {string} appBase application base URL
18924 * @param {string} appBaseNoFile application base URL stripped of any filename
18925 * @param {string} basePrefix URL path prefix
18926 */
18927function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
18928  this.$$html5 = true;
18929  basePrefix = basePrefix || '';
18930  parseAbsoluteUrl(appBase, this);
18931
18932
18933  /**
18934   * Parse given HTML5 (regular) URL string into properties
18935   * @param {string} url HTML5 URL
18936   * @private
18937   */
18938  this.$$parse = function(url) {
18939    var pathUrl = stripBaseUrl(appBaseNoFile, url);
18940    if (!isString(pathUrl)) {
18941      throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url,
18942          appBaseNoFile);
18943    }
18944
18945    parseAppUrl(pathUrl, this);
18946
18947    if (!this.$$path) {
18948      this.$$path = '/';
18949    }
18950
18951    this.$$compose();
18952  };
18953
18954  /**
18955   * Compose url and update `absUrl` property
18956   * @private
18957   */
18958  this.$$compose = function() {
18959    var search = toKeyValue(this.$$search),
18960        hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
18961
18962    this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
18963    this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
18964  };
18965
18966  this.$$parseLinkUrl = function(url, relHref) {
18967    if (relHref && relHref[0] === '#') {
18968      // special case for links to hash fragments:
18969      // keep the old url and only replace the hash fragment
18970      this.hash(relHref.slice(1));
18971      return true;
18972    }
18973    var appUrl, prevAppUrl;
18974    var rewrittenUrl;
18975
18976
18977    if (isDefined(appUrl = stripBaseUrl(appBase, url))) {
18978      prevAppUrl = appUrl;
18979      if (basePrefix && isDefined(appUrl = stripBaseUrl(basePrefix, appUrl))) {
18980        rewrittenUrl = appBaseNoFile + (stripBaseUrl('/', appUrl) || appUrl);
18981      } else {
18982        rewrittenUrl = appBase + prevAppUrl;
18983      }
18984    } else if (isDefined(appUrl = stripBaseUrl(appBaseNoFile, url))) {
18985      rewrittenUrl = appBaseNoFile + appUrl;
18986    } else if (appBaseNoFile === url + '/') {
18987      rewrittenUrl = appBaseNoFile;
18988    }
18989    if (rewrittenUrl) {
18990      this.$$parse(rewrittenUrl);
18991    }
18992    return !!rewrittenUrl;
18993  };
18994}
18995
18996
18997/**
18998 * LocationHashbangUrl represents URL
18999 * This object is exposed as $location service when developer doesn't opt into html5 mode.
19000 * It also serves as the base class for html5 mode fallback on legacy browsers.
19001 *
19002 * @constructor
19003 * @param {string} appBase application base URL
19004 * @param {string} appBaseNoFile application base URL stripped of any filename
19005 * @param {string} hashPrefix hashbang prefix
19006 */
19007function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
19008
19009  parseAbsoluteUrl(appBase, this);
19010
19011
19012  /**
19013   * Parse given hashbang URL into properties
19014   * @param {string} url Hashbang URL
19015   * @private
19016   */
19017  this.$$parse = function(url) {
19018    var withoutBaseUrl = stripBaseUrl(appBase, url) || stripBaseUrl(appBaseNoFile, url);
19019    var withoutHashUrl;
19020
19021    if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === '#') {
19022
19023      // The rest of the URL starts with a hash so we have
19024      // got either a hashbang path or a plain hash fragment
19025      withoutHashUrl = stripBaseUrl(hashPrefix, withoutBaseUrl);
19026      if (isUndefined(withoutHashUrl)) {
19027        // There was no hashbang prefix so we just have a hash fragment
19028        withoutHashUrl = withoutBaseUrl;
19029      }
19030
19031    } else {
19032      // There was no hashbang path nor hash fragment:
19033      // If we are in HTML5 mode we use what is left as the path;
19034      // Otherwise we ignore what is left
19035      if (this.$$html5) {
19036        withoutHashUrl = withoutBaseUrl;
19037      } else {
19038        withoutHashUrl = '';
19039        if (isUndefined(withoutBaseUrl)) {
19040          appBase = url;
19041          this.replace();
19042        }
19043      }
19044    }
19045
19046    parseAppUrl(withoutHashUrl, this);
19047
19048    this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
19049
19050    this.$$compose();
19051
19052    /*
19053     * In Windows, on an anchor node on documents loaded from
19054     * the filesystem, the browser will return a pathname
19055     * prefixed with the drive name ('/C:/path') when a
19056     * pathname without a drive is set:
19057     *  * a.setAttribute('href', '/foo')
19058     *   * a.pathname === '/C:/foo' //true
19059     *
19060     * Inside of Angular, we're always using pathnames that
19061     * do not include drive names for routing.
19062     */
19063    function removeWindowsDriveName(path, url, base) {
19064      /*
19065      Matches paths for file protocol on windows,
19066      such as /C:/foo/bar, and captures only /foo/bar.
19067      */
19068      var windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
19069
19070      var firstPathSegmentMatch;
19071
19072      //Get the relative path from the input URL.
19073      if (startsWith(url, base)) {
19074        url = url.replace(base, '');
19075      }
19076
19077      // The input URL intentionally contains a first path segment that ends with a colon.
19078      if (windowsFilePathExp.exec(url)) {
19079        return path;
19080      }
19081
19082      firstPathSegmentMatch = windowsFilePathExp.exec(path);
19083      return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
19084    }
19085  };
19086
19087  /**
19088   * Compose hashbang URL and update `absUrl` property
19089   * @private
19090   */
19091  this.$$compose = function() {
19092    var search = toKeyValue(this.$$search),
19093        hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
19094
19095    this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
19096    this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
19097  };
19098
19099  this.$$parseLinkUrl = function(url, relHref) {
19100    if (stripHash(appBase) === stripHash(url)) {
19101      this.$$parse(url);
19102      return true;
19103    }
19104    return false;
19105  };
19106}
19107
19108
19109/**
19110 * LocationHashbangUrl represents URL
19111 * This object is exposed as $location service when html5 history api is enabled but the browser
19112 * does not support it.
19113 *
19114 * @constructor
19115 * @param {string} appBase application base URL
19116 * @param {string} appBaseNoFile application base URL stripped of any filename
19117 * @param {string} hashPrefix hashbang prefix
19118 */
19119function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
19120  this.$$html5 = true;
19121  LocationHashbangUrl.apply(this, arguments);
19122
19123  this.$$parseLinkUrl = function(url, relHref) {
19124    if (relHref && relHref[0] === '#') {
19125      // special case for links to hash fragments:
19126      // keep the old url and only replace the hash fragment
19127      this.hash(relHref.slice(1));
19128      return true;
19129    }
19130
19131    var rewrittenUrl;
19132    var appUrl;
19133
19134    if (appBase === stripHash(url)) {
19135      rewrittenUrl = url;
19136    } else if ((appUrl = stripBaseUrl(appBaseNoFile, url))) {
19137      rewrittenUrl = appBase + hashPrefix + appUrl;
19138    } else if (appBaseNoFile === url + '/') {
19139      rewrittenUrl = appBaseNoFile;
19140    }
19141    if (rewrittenUrl) {
19142      this.$$parse(rewrittenUrl);
19143    }
19144    return !!rewrittenUrl;
19145  };
19146
19147  this.$$compose = function() {
19148    var search = toKeyValue(this.$$search),
19149        hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
19150
19151    this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
19152    // include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#'
19153    this.$$absUrl = appBase + hashPrefix + this.$$url;
19154  };
19155
19156}
19157
19158
19159var locationPrototype = {
19160
19161  /**
19162   * Ensure absolute URL is initialized.
19163   * @private
19164   */
19165  $$absUrl:'',
19166
19167  /**
19168   * Are we in html5 mode?
19169   * @private
19170   */
19171  $$html5: false,
19172
19173  /**
19174   * Has any change been replacing?
19175   * @private
19176   */
19177  $$replace: false,
19178
19179  /**
19180   * @ngdoc method
19181   * @name $location#absUrl
19182   *
19183   * @description
19184   * This method is getter only.
19185   *
19186   * Return full URL representation with all segments encoded according to rules specified in
19187   * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
19188   *
19189   *
19190   * ```js
19191   * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
19192   * var absUrl = $location.absUrl();
19193   * // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
19194   * ```
19195   *
19196   * @return {string} full URL
19197   */
19198  absUrl: locationGetter('$$absUrl'),
19199
19200  /**
19201   * @ngdoc method
19202   * @name $location#url
19203   *
19204   * @description
19205   * This method is getter / setter.
19206   *
19207   * Return URL (e.g. `/path?a=b#hash`) when called without any parameter.
19208   *
19209   * Change path, search and hash, when called with parameter and return `$location`.
19210   *
19211   *
19212   * ```js
19213   * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
19214   * var url = $location.url();
19215   * // => "/some/path?foo=bar&baz=xoxo"
19216   * ```
19217   *
19218   * @param {string=} url New URL without base prefix (e.g. `/path?a=b#hash`)
19219   * @return {string} url
19220   */
19221  url: function(url) {
19222    if (isUndefined(url)) {
19223      return this.$$url;
19224    }
19225
19226    var match = PATH_MATCH.exec(url);
19227    if (match[1] || url === '') this.path(decodeURIComponent(match[1]));
19228    if (match[2] || match[1] || url === '') this.search(match[3] || '');
19229    this.hash(match[5] || '');
19230
19231    return this;
19232  },
19233
19234  /**
19235   * @ngdoc method
19236   * @name $location#protocol
19237   *
19238   * @description
19239   * This method is getter only.
19240   *
19241   * Return protocol of current URL.
19242   *
19243   *
19244   * ```js
19245   * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
19246   * var protocol = $location.protocol();
19247   * // => "http"
19248   * ```
19249   *
19250   * @return {string} protocol of current URL
19251   */
19252  protocol: locationGetter('$$protocol'),
19253
19254  /**
19255   * @ngdoc method
19256   * @name $location#host
19257   *
19258   * @description
19259   * This method is getter only.
19260   *
19261   * Return host of current URL.
19262   *
19263   * Note: compared to the non-angular version `location.host` which returns `hostname:port`, this returns the `hostname` portion only.
19264   *
19265   *
19266   * ```js
19267   * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
19268   * var host = $location.host();
19269   * // => "example.com"
19270   *
19271   * // given URL http://user:[email protected]:8080/#/some/path?foo=bar&baz=xoxo
19272   * host = $location.host();
19273   * // => "example.com"
19274   * host = location.host;
19275   * // => "example.com:8080"
19276   * ```
19277   *
19278   * @return {string} host of current URL.
19279   */
19280  host: locationGetter('$$host'),
19281
19282  /**
19283   * @ngdoc method
19284   * @name $location#port
19285   *
19286   * @description
19287   * This method is getter only.
19288   *
19289   * Return port of current URL.
19290   *
19291   *
19292   * ```js
19293   * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
19294   * var port = $location.port();
19295   * // => 80
19296   * ```
19297   *
19298   * @return {Number} port
19299   */
19300  port: locationGetter('$$port'),
19301
19302  /**
19303   * @ngdoc method
19304   * @name $location#path
19305   *
19306   * @description
19307   * This method is getter / setter.
19308   *
19309   * Return path of current URL when called without any parameter.
19310   *
19311   * Change path when called with parameter and return `$location`.
19312   *
19313   * Note: Path should always begin with forward slash (/), this method will add the forward slash
19314   * if it is missing.
19315   *
19316   *
19317   * ```js
19318   * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
19319   * var path = $location.path();
19320   * // => "/some/path"
19321   * ```
19322   *
19323   * @param {(string|number)=} path New path
19324   * @return {(string|object)} path if called with no parameters, or `$location` if called with a parameter
19325   */
19326  path: locationGetterSetter('$$path', function(path) {
19327    path = path !== null ? path.toString() : '';
19328    return path.charAt(0) === '/' ? path : '/' + path;
19329  }),
19330
19331  /**
19332   * @ngdoc method
19333   * @name $location#search
19334   *
19335   * @description
19336   * This method is getter / setter.
19337   *
19338   * Return search part (as object) of current URL when called without any parameter.
19339   *
19340   * Change search part when called with parameter and return `$location`.
19341   *
19342   *
19343   * ```js
19344   * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
19345   * var searchObject = $location.search();
19346   * // => {foo: 'bar', baz: 'xoxo'}
19347   *
19348   * // set foo to 'yipee'
19349   * $location.search('foo', 'yipee');
19350   * // $location.search() => {foo: 'yipee', baz: 'xoxo'}
19351   * ```
19352   *
19353   * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
19354   * hash object.
19355   *
19356   * When called with a single argument the method acts as a setter, setting the `search` component
19357   * of `$location` to the specified value.
19358   *
19359   * If the argument is a hash object containing an array of values, these values will be encoded
19360   * as duplicate search parameters in the URL.
19361   *
19362   * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`
19363   * will override only a single search property.
19364   *
19365   * If `paramValue` is an array, it will override the property of the `search` component of
19366   * `$location` specified via the first argument.
19367   *
19368   * If `paramValue` is `null`, the property specified via the first argument will be deleted.
19369   *
19370   * If `paramValue` is `true`, the property specified via the first argument will be added with no
19371   * value nor trailing equal sign.
19372   *
19373   * @return {Object} If called with no arguments returns the parsed `search` object. If called with
19374   * one or more arguments returns `$location` object itself.
19375   */
19376  search: function(search, paramValue) {
19377    switch (arguments.length) {
19378      case 0:
19379        return this.$$search;
19380      case 1:
19381        if (isString(search) || isNumber(search)) {
19382          search = search.toString();
19383          this.$$search = parseKeyValue(search);
19384        } else if (isObject(search)) {
19385          search = copy(search, {});
19386          // remove object undefined or null properties
19387          forEach(search, function(value, key) {
19388            if (value == null) delete search[key];
19389          });
19390
19391          this.$$search = search;
19392        } else {
19393          throw $locationMinErr('isrcharg',
19394              'The first argument of the `$location#search()` call must be a string or an object.');
19395        }
19396        break;
19397      default:
19398        if (isUndefined(paramValue) || paramValue === null) {
19399          delete this.$$search[search];
19400        } else {
19401          this.$$search[search] = paramValue;
19402        }
19403    }
19404
19405    this.$$compose();
19406    return this;
19407  },
19408
19409  /**
19410   * @ngdoc method
19411   * @name $location#hash
19412   *
19413   * @description
19414   * This method is getter / setter.
19415   *
19416   * Returns the hash fragment when called without any parameters.
19417   *
19418   * Changes the hash fragment when called with a parameter and returns `$location`.
19419   *
19420   *
19421   * ```js
19422   * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue
19423   * var hash = $location.hash();
19424   * // => "hashValue"
19425   * ```
19426   *
19427   * @param {(string|number)=} hash New hash fragment
19428   * @return {string} hash
19429   */
19430  hash: locationGetterSetter('$$hash', function(hash) {
19431    return hash !== null ? hash.toString() : '';
19432  }),
19433
19434  /**
19435   * @ngdoc method
19436   * @name $location#replace
19437   *
19438   * @description
19439   * If called, all changes to $location during the current `$digest` will replace the current history
19440   * record, instead of adding a new one.
19441   */
19442  replace: function() {
19443    this.$$replace = true;
19444    return this;
19445  }
19446};
19447
19448forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) {
19449  Location.prototype = Object.create(locationPrototype);
19450
19451  /**
19452   * @ngdoc method
19453   * @name $location#state
19454   *
19455   * @description
19456   * This method is getter / setter.
19457   *
19458   * Return the history state object when called without any parameter.
19459   *
19460   * Change the history state object when called with one parameter and return `$location`.
19461   * The state object is later passed to `pushState` or `replaceState`.
19462   *
19463   * NOTE: This method is supported only in HTML5 mode and only in browsers supporting
19464   * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support
19465   * older browsers (like IE9 or Android < 4.0), don't use this method.
19466   *
19467   * @param {object=} state State object for pushState or replaceState
19468   * @return {object} state
19469   */
19470  Location.prototype.state = function(state) {
19471    if (!arguments.length) {
19472      return this.$$state;
19473    }
19474
19475    if (Location !== LocationHtml5Url || !this.$$html5) {
19476      throw $locationMinErr('nostate', 'History API state support is available only ' +
19477        'in HTML5 mode and only in browsers supporting HTML5 History API');
19478    }
19479    // The user might modify `stateObject` after invoking `$location.state(stateObject)`
19480    // but we're changing the $$state reference to $browser.state() during the $digest
19481    // so the modification window is narrow.
19482    this.$$state = isUndefined(state) ? null : state;
19483
19484    return this;
19485  };
19486});
19487
19488
19489function locationGetter(property) {
19490  return /** @this */ function() {
19491    return this[property];
19492  };
19493}
19494
19495
19496function locationGetterSetter(property, preprocess) {
19497  return /** @this */ function(value) {
19498    if (isUndefined(value)) {
19499      return this[property];
19500    }
19501
19502    this[property] = preprocess(value);
19503    this.$$compose();
19504
19505    return this;
19506  };
19507}
19508
19509
19510/**
19511 * @ngdoc service
19512 * @name $location
19513 *
19514 * @requires $rootElement
19515 *
19516 * @description
19517 * The $location service parses the URL in the browser address bar (based on the
19518 * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL
19519 * available to your application. Changes to the URL in the address bar are reflected into
19520 * $location service and changes to $location are reflected into the browser address bar.
19521 *
19522 * **The $location service:**
19523 *
19524 * - Exposes the current URL in the browser address bar, so you can
19525 *   - Watch and observe the URL.
19526 *   - Change the URL.
19527 * - Synchronizes the URL with the browser when the user
19528 *   - Changes the address bar.
19529 *   - Clicks the back or forward button (or clicks a History link).
19530 *   - Clicks on a link.
19531 * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
19532 *
19533 * For more information see {@link guide/$location Developer Guide: Using $location}
19534 */
19535
19536/**
19537 * @ngdoc provider
19538 * @name $locationProvider
19539 * @this
19540 *
19541 * @description
19542 * Use the `$locationProvider` to configure how the application deep linking paths are stored.
19543 */
19544function $LocationProvider() {
19545  var hashPrefix = '',
19546      html5Mode = {
19547        enabled: false,
19548        requireBase: true,
19549        rewriteLinks: true
19550      };
19551
19552  /**
19553   * @ngdoc method
19554   * @name $locationProvider#hashPrefix
19555   * @description
19556   * The default value for the prefix is `''`.
19557   * @param {string=} prefix Prefix for hash part (containing path and search)
19558   * @returns {*} current value if used as getter or itself (chaining) if used as setter
19559   */
19560  this.hashPrefix = function(prefix) {
19561    if (isDefined(prefix)) {
19562      hashPrefix = prefix;
19563      return this;
19564    } else {
19565      return hashPrefix;
19566    }
19567  };
19568
19569  /**
19570   * @ngdoc method
19571   * @name $locationProvider#html5Mode
19572   * @description
19573   * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.
19574   *   If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported
19575   *   properties:
19576   *   - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to
19577   *     change urls where supported. Will fall back to hash-prefixed paths in browsers that do not
19578   *     support `pushState`.
19579   *   - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies
19580   *     whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are
19581   *     true, and a base tag is not present, an error will be thrown when `$location` is injected.
19582   *     See the {@link guide/$location $location guide for more information}
19583   *   - **rewriteLinks** - `{boolean|string}` - (default: `true`) When html5Mode is enabled,
19584   *     enables/disables URL rewriting for relative links. If set to a string, URL rewriting will
19585   *     only happen on links with an attribute that matches the given string. For example, if set
19586   *     to `'internal-link'`, then the URL will only be rewritten for `<a internal-link>` links.
19587   *     Note that [attribute name normalization](guide/directive#normalization) does not apply
19588   *     here, so `'internalLink'` will **not** match `'internal-link'`.
19589   *
19590   * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
19591   */
19592  this.html5Mode = function(mode) {
19593    if (isBoolean(mode)) {
19594      html5Mode.enabled = mode;
19595      return this;
19596    } else if (isObject(mode)) {
19597
19598      if (isBoolean(mode.enabled)) {
19599        html5Mode.enabled = mode.enabled;
19600      }
19601
19602      if (isBoolean(mode.requireBase)) {
19603        html5Mode.requireBase = mode.requireBase;
19604      }
19605
19606      if (isBoolean(mode.rewriteLinks) || isString(mode.rewriteLinks)) {
19607        html5Mode.rewriteLinks = mode.rewriteLinks;
19608      }
19609
19610      return this;
19611    } else {
19612      return html5Mode;
19613    }
19614  };
19615
19616  /**
19617   * @ngdoc event
19618   * @name $location#$locationChangeStart
19619   * @eventType broadcast on root scope
19620   * @description
19621   * Broadcasted before a URL will change.
19622   *
19623   * This change can be prevented by calling
19624   * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
19625   * details about event object. Upon successful change
19626   * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired.
19627   *
19628   * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
19629   * the browser supports the HTML5 History API.
19630   *
19631   * @param {Object} angularEvent Synthetic event object.
19632   * @param {string} newUrl New URL
19633   * @param {string=} oldUrl URL that was before it was changed.
19634   * @param {string=} newState New history state object
19635   * @param {string=} oldState History state object that was before it was changed.
19636   */
19637
19638  /**
19639   * @ngdoc event
19640   * @name $location#$locationChangeSuccess
19641   * @eventType broadcast on root scope
19642   * @description
19643   * Broadcasted after a URL was changed.
19644   *
19645   * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
19646   * the browser supports the HTML5 History API.
19647   *
19648   * @param {Object} angularEvent Synthetic event object.
19649   * @param {string} newUrl New URL
19650   * @param {string=} oldUrl URL that was before it was changed.
19651   * @param {string=} newState New history state object
19652   * @param {string=} oldState History state object that was before it was changed.
19653   */
19654
19655  this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window',
19656      function($rootScope, $browser, $sniffer, $rootElement, $window) {
19657    var $location,
19658        LocationMode,
19659        baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
19660        initialUrl = $browser.url(),
19661        appBase;
19662
19663    if (html5Mode.enabled) {
19664      if (!baseHref && html5Mode.requireBase) {
19665        throw $locationMinErr('nobase',
19666          '$location in HTML5 mode requires a <base> tag to be present!');
19667      }
19668      appBase = serverBase(initialUrl) + (baseHref || '/');
19669      LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
19670    } else {
19671      appBase = stripHash(initialUrl);
19672      LocationMode = LocationHashbangUrl;
19673    }
19674    var appBaseNoFile = stripFile(appBase);
19675
vendor: 4,316 bytes, lines 19676-19786
19676    $location = new LocationMode(appBase, appBaseNoFile, '#' + hashPrefix);
19677    $location.$$parseLinkUrl(initialUrl, initialUrl);
19678
19679    $location.$$state = $browser.state();
19680
19681    var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
19682
19683    function setBrowserUrlWithFallback(url, replace, state) {
19684      var oldUrl = $location.url();
19685      var oldState = $location.$$state;
19686      try {
19687        $browser.url(url, replace, state);
19688
19689        // Make sure $location.state() returns referentially identical (not just deeply equal)
19690        // state object; this makes possible quick checking if the state changed in the digest
19691        // loop. Checking deep equality would be too expensive.
19692        $location.$$state = $browser.state();
19693      } catch (e) {
19694        // Restore old values if pushState fails
19695        $location.url(oldUrl);
19696        $location.$$state = oldState;
19697
19698        throw e;
19699      }
19700    }
19701
19702    $rootElement.on('click', function(event) {
19703      var rewriteLinks = html5Mode.rewriteLinks;
19704      // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
19705      // currently we open nice url link and redirect then
19706
19707      if (!rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which === 2 || event.button === 2) return;
19708
19709      var elm = jqLite(event.target);
19710
19711      // traverse the DOM up to find first A tag
19712      while (nodeName_(elm[0]) !== 'a') {
19713        // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
19714        if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
19715      }
19716
19717      if (isString(rewriteLinks) && isUndefined(elm.attr(rewriteLinks))) return;
19718
19719      var absHref = elm.prop('href');
19720      // get the actual href attribute - see
19721      // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
19722      var relHref = elm.attr('href') || elm.attr('xlink:href');
19723
19724      if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
19725        // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
19726        // an animation.
19727        absHref = urlResolve(absHref.animVal).href;
19728      }
19729
19730      // Ignore when url is started with javascript: or mailto:
19731      if (IGNORE_URI_REGEXP.test(absHref)) return;
19732
19733      if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
19734        if ($location.$$parseLinkUrl(absHref, relHref)) {
19735          // We do a preventDefault for all urls that are part of the angular application,
19736          // in html5mode and also without, so that we are able to abort navigation without
19737          // getting double entries in the location history.
19738          event.preventDefault();
19739          // update location manually
19740          if ($location.absUrl() !== $browser.url()) {
19741            $rootScope.$apply();
19742            // hack to work around FF6 bug 684208 when scenario runner clicks on links
19743            $window.angular['ff-684208-preventDefault'] = true;
19744          }
19745        }
19746      }
19747    });
19748
19749
19750    // rewrite hashbang url <> html5 url
19751    if (trimEmptyHash($location.absUrl()) !== trimEmptyHash(initialUrl)) {
19752      $browser.url($location.absUrl(), true);
19753    }
19754
19755    var initializing = true;
19756
19757    // update $location when $browser url changes
19758    $browser.onUrlChange(function(newUrl, newState) {
19759
19760      if (isUndefined(stripBaseUrl(appBaseNoFile, newUrl))) {
19761        // If we are navigating outside of the app then force a reload
19762        $window.location.href = newUrl;
19763        return;
19764      }
19765
19766      $rootScope.$evalAsync(function() {
19767        var oldUrl = $location.absUrl();
19768        var oldState = $location.$$state;
19769        var defaultPrevented;
19770        newUrl = trimEmptyHash(newUrl);
19771        $location.$$parse(newUrl);
19772        $location.$$state = newState;
19773
19774        defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
19775            newState, oldState).defaultPrevented;
19776
19777        // if the location was changed by a `$locationChangeStart` handler then stop
19778        // processing this location change
19779        if ($location.absUrl() !== newUrl) return;
19780
19781        if (defaultPrevented) {
19782          $location.$$parse(oldUrl);
19783          $location.$$state = oldState;
19784          setBrowserUrlWithFallback(oldUrl, false, oldState);
19785        } else {
19786          initializing = false;
19787          afterLocationChange(oldUrl, oldState);
19788        }
19789      });
19790      if (!$rootScope.$$phase) $rootScope.$digest();
19791    });
19792
19793    // update browser
19794    $rootScope.$watch(function $locationWatch() {
19795      var oldUrl = trimEmptyHash($browser.url());
19796      var newUrl = trimEmptyHash($location.absUrl());
19797      var oldState = $browser.state();
19798      var currentReplace = $location.$$replace;
19799      var urlOrStateChanged = oldUrl !== newUrl ||
19800        ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
19801
19802      if (initializing || urlOrStateChanged) {
19803        initializing = false;
19804
19805        $rootScope.$evalAsync(function() {
19806          var newUrl = $location.absUrl();
19807          var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
19808              $location.$$state, oldState).defaultPrevented;
19809
19810          // if the location was changed by a `$locationChangeStart` handler then stop
19811          // processing this location change
19812          if ($location.absUrl() !== newUrl) return;
19813
19814          if (defaultPrevented) {
19815            $location.$$parse(oldUrl);
19816            $location.$$state = oldState;
19817          } else {
19818            if (urlOrStateChanged) {
19819              setBrowserUrlWithFallback(newUrl, currentReplace,
19820                                        oldState === $location.$$state ? null : $location.$$state);
19821            }
19822            afterLocationChange(oldUrl, oldState);
19823          }
19824        });
19825      }
19826
19827      $location.$$replace = false;
19828
19829      // we don't need to return anything because $evalAsync will make the digest loop dirty when
19830      // there is a change
19831    });
19832
19833    return $location;
19834
19835    function afterLocationChange(oldUrl, oldState) {
19836      $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl,
19837        $location.$$state, oldState);
19838    }
19839}];
19840}
19841
19842/**
19843 * @ngdoc service
19844 * @name $log
19845 * @requires $window
19846 *
19847 * @description
19848 * Simple service for logging. Default implementation safely writes the message
19849 * into the browser's console (if present).
19850 *
19851 * The main purpose of this service is to simplify debugging and troubleshooting.
19852 *
19853 * The default is to log `debug` messages. You can use
19854 * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
19855 *
19856 * @example
19857   <example module="logExample" name="log-service">
19858     <file name="script.js">
19859       angular.module('logExample', [])
19860         .controller('LogController', ['$scope', '$log', function($scope, $log) {
19861           $scope.$log = $log;
19862           $scope.message = 'Hello World!';
19863         }]);
19864     </file>
19865     <file name="index.html">
19866       <div ng-controller="LogController">
19867         <p>Reload this page with open console, enter text and hit the log button...</p>
19868         <label>Message:
19869         <input type="text" ng-model="message" /></label>
19870         <button ng-click="$log.log(message)">log</button>
19871         <button ng-click="$log.warn(message)">warn</button>
19872         <button ng-click="$log.info(message)">info</button>
19873         <button ng-click="$log.error(message)">error</button>
19874         <button ng-click="$log.debug(message)">debug</button>
19875       </div>
19876     </file>
19877   </example>
19878 */
19879
19880/**
19881 * @ngdoc provider
19882 * @name $logProvider
19883 * @this
19884 *
19885 * @description
19886 * Use the `$logProvider` to configure how the application logs messages
19887 */
19888function $LogProvider() {
19889  var debug = true,
19890      self = this;
19891
19892  /**
19893   * @ngdoc method
19894   * @name $logProvider#debugEnabled
19895   * @description
19896   * @param {boolean=} flag enable or disable debug level messages
19897   * @returns {*} current value if used as getter or itself (chaining) if used as setter
19898   */
19899  this.debugEnabled = function(flag) {
19900    if (isDefined(flag)) {
19901      debug = flag;
19902      return this;
19903    } else {
19904      return debug;
19905    }
19906  };
19907
19908  this.$get = ['$window', function($window) {
19909    return {
19910      /**
19911       * @ngdoc method
19912       * @name $log#log
19913       *
19914       * @description
19915       * Write a log message
19916       */
19917      log: consoleLog('log'),
19918
19919      /**
19920       * @ngdoc method
19921       * @name $log#info
19922       *
19923       * @description
19924       * Write an information message
19925       */
19926      info: consoleLog('info'),
19927
19928      /**
19929       * @ngdoc method
19930       * @name $log#warn
19931       *
19932       * @description
19933       * Write a warning message
19934       */
19935      warn: consoleLog('warn'),
19936
19937      /**
19938       * @ngdoc method
19939       * @name $log#error
19940       *
19941       * @description
19942       * Write an error message
19943       */
19944      error: consoleLog('error'),
19945
19946      /**
19947       * @ngdoc method
19948       * @name $log#debug
19949       *
19950       * @description
19951       * Write a debug message
19952       */
19953      debug: (function() {
19954        var fn = consoleLog('debug');
19955
19956        return function() {
19957          if (debug) {
19958            fn.apply(self, arguments);
19959          }
19960        };
19961      })()
19962    };
19963
19964    function formatError(arg) {
19965      if (arg instanceof Error) {
19966        if (arg.stack) {
19967          arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
19968              ? 'Error: ' + arg.message + '\n' + arg.stack
19969              : arg.stack;
19970        } else if (arg.sourceURL) {
19971          arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
19972        }
19973      }
19974      return arg;
19975    }
19976
19977    function consoleLog(type) {
19978      var console = $window.console || {},
19979          logFn = console[type] || console.log || noop,
19980          hasApply = false;
19981
19982      // Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
19983      // The reason behind this is that console.log has type "object" in IE8...
19984      try {
19985        hasApply = !!logFn.apply;
19986      } catch (e) { /* empty */ }
19987
19988      if (hasApply) {
19989        return function() {
19990          var args = [];
19991          forEach(arguments, function(arg) {
19992            args.push(formatError(arg));
19993          });
19994          return logFn.apply(console, args);
19995        };
19996      }
19997
19998      // we are IE which either doesn't have window.console => this is noop and we do nothing,
19999      // or we are IE where console.log doesn't have apply so we log at least first 2 args
20000      return function(arg1, arg2) {
20001        logFn(arg1, arg2 == null ? '' : arg2);
20002      };
20003    }
20004  }];
20005}
20006
20007/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
20008 *     Any commits to this file should be reviewed with security in mind.  *
20009 *   Changes to this file can potentially create security vulnerabilities. *
20010 *          An approval from 2 Core members with history of modifying      *
20011 *                         this file is required.                          *
20012 *                                                                         *
20013 *  Does the change somehow allow for arbitrary javascript to be executed? *
20014 *    Or allows for someone to change the prototype of built-in objects?   *
20015 *     Or gives undesired access to variables likes document or window?    *
20016 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
20017
20018var $parseMinErr = minErr('$parse');
20019
20020var ARRAY_CTOR = [].constructor;
20021var BOOLEAN_CTOR = (false).constructor;
20022var FUNCTION_CTOR = Function.constructor;
20023var NUMBER_CTOR = (0).constructor;
20024var OBJECT_CTOR = {}
20024.constructor;
20025var STRING_CTOR = ''.constructor;
20026var ARRAY_CTOR_PROTO = ARRAY_CTOR.prototype;
20027var BOOLEAN_CTOR_PROTO = BOOLEAN_CTOR.prototype;
20028var FUNCTION_CTOR_PROTO = FUNCTION_CTOR.prototype;
20029var NUMBER_CTOR_PROTO = NUMBER_CTOR.prototype;
20030var OBJECT_CTOR_PROTO = OBJECT_CTOR.prototype;
20031var STRING_CTOR_PROTO = STRING_CTOR.prototype;
20032
20033var CALL = FUNCTION_CTOR_PROTO.call;
20034var APPLY = FUNCTION_CTOR_PROTO.apply;
20035var BIND = FUNCTION_CTOR_PROTO.bind;
20036
20037var objectValueOf = OBJECT_CTOR_PROTO.valueOf;
20038
20039// Sandboxing Angular Expressions
20040// ------------------------------
20041// Angular expressions are generally considered safe because these expressions only have direct
20042// access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by
20043// obtaining a reference to native JS functions such as the Function constructor.
20044//
20045// As an example, consider the following Angular expression:
20046//
20047//   {}.toString.constructor('alert("evil JS code")')
20048//
20049// This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
20050// against the expression language, but not to prevent exploits that were enabled by exposing
20051// sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good
20052// practice and therefore we are not even trying to protect against interaction with an object
20053// explicitly exposed in this way.
20054//
20055// In general, it is not possible to access a Window object from an angular expression unless a
20056// window or some DOM object that has a reference to window is published onto a Scope.
20057// Similarly we prevent invocations of function known to be dangerous, as well as assignments to
20058// native objects.
20059//
20060// See https://docs.angularjs.org/guide/security
20061
20062
20063function ensureSafeMemberName(name, fullExpression) {
20064  if (name === '__defineGetter__' || name === '__defineSetter__'
20065      || name === '__lookupGetter__' || name === '__lookupSetter__'
20066      || name === '__proto__') {
20067    throw $parseMinErr('isecfld',
20068        'Attempting to access a disallowed field in Angular expressions! '
20069        + 'Expression: {0}', fullExpression);
20070  }
20071  return name;
20072}
20073
20074function getStringValue(name) {
20075  // Property names must be strings. This means that non-string objects cannot be used
20076  // as keys in an object. Any non-string object, including a number, is typecasted
20077  // into a string via the toString method.
20078  // -- MDN, https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Property_accessors#Property_names
20079  //
20080  // So, to ensure that we are checking the same `name` that JavaScript would use, we cast it
20081  // to a string. It's not always possible. If `name` is an object and its `toString` method is
20082  // 'broken' (doesn't return a string, isn't a function, etc.), an error will be thrown:
20083  //
20084  // TypeError: Cannot convert object to primitive value
20085  //
20086  // For performance reasons, we don't catch this error here and allow it to propagate up the call
20087  // stack. Note that you'll get the same error in JavaScript if you try to access a property using
20088  // such a 'broken' object as a key.
20089  return name + '';
20090}
20091
20092function ensureSafeObject(obj, fullExpression) {
20093  // nifty check if obj is Function that is fast and works across iframes and other contexts
20094  if (obj) {
20095    if (obj.constructor === obj) {
20096      throw $parseMinErr('isecfn',
20097          'Referencing Function in Angular expressions is disallowed! Expression: {0}',
20098          fullExpression);
20099    } else if (// isWindow(obj)
20100        obj.window === obj) {
20101      throw $parseMinErr('isecwindow',
20102          'Referencing the Window in Angular expressions is disallowed! Expression: {0}',
20103          fullExpression);
20104    } else if (// isElement(obj)
20105        obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) {
20106      throw $parseMinErr('isecdom',
20107          'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
20108          fullExpression);
20109    } else if (// block Object so that we can't get hold of dangerous Object.* methods
20110        obj === Object) {
20111      throw $parseMinErr('isecobj',
20112          'Referencing Object in Angular expressions is disallowed! Expression: {0}',
20113          fullExpression);
20114    }
20115  }
20116  return obj;
20117}
20118
20119function ensureSafeFunction(obj, fullExpression) {
20120  if (obj) {
20121    if (obj.constructor === obj) {
20122      throw $parseMinErr('isecfn',
20123        'Referencing Function in Angular expressions is disallowed! Expression: {0}',
20124        fullExpression);
20125    } else if (obj === CALL || obj === APPLY || obj === BIND) {
20126      throw $parseMinErr('isecff',
20127        'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}',
20128        fullExpression);
20129    }
20130  }
20131}
20132
20133function ensureSafeAssignContext(obj, fullExpression) {
20134  if (obj) {
20135    if (obj === ARRAY_CTOR ||
20136        obj === BOOLEAN_CTOR ||
20137        obj === FUNCTION_CTOR ||
20138        obj === NUMBER_CTOR ||
20139        obj === OBJECT_CTOR ||
20140        obj === STRING_CTOR ||
20141        obj === ARRAY_CTOR_PROTO ||
20142        obj === BOOLEAN_CTOR_PROTO ||
20143        obj === FUNCTION_CTOR_PROTO ||
20144        obj === NUMBER_CTOR_PROTO ||
20145        obj === OBJECT_CTOR_PROTO ||
20146        obj === STRING_CTOR_PROTO) {
20147      throw $parseMinErr('isecaf',
20148        'Assigning to a constructor or its prototype is disallowed! Expression: {0}',
20149        fullExpression);
20150    }
20151  }
20152}
vendor: 16,057 bytes, lines 20152-20726
20152
20153
20154var OPERATORS = createMap();
20155forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; });
20156var ESCAPE = {'n':'\n', 'f':'\f', 'r':'\r', 't':'\t', 'v':'\v', '\'':'\'', '"':'"'};
20157
20158
20159/////////////////////////////////////////
20160
20161
20162/**
20163 * @constructor
20164 */
20165var Lexer = function Lexer(options) {
20166  this.options = options;
20167};
20168
20169Lexer.prototype = {
20170  constructor: Lexer,
20171
20172  lex: function(text) {
20173    this.text = text;
20174    this.index = 0;
20175    this.tokens = [];
20176
20177    while (this.index < this.text.length) {
20178      var ch = this.text.charAt(this.index);
20179      if (ch === '"' || ch === '\'') {
20180        this.readString(ch);
20181      } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
20182        this.readNumber();
20183      } else if (this.isIdentifierStart(this.peekMultichar())) {
20184        this.readIdent();
20185      } else if (this.is(ch, '(){}[].,;:?')) {
20186        this.tokens.push({index: this.index, text: ch});
20187        this.index++;
20188      } else if (this.isWhitespace(ch)) {
20189        this.index++;
20190      } else {
20191        var ch2 = ch + this.peek();
20192        var ch3 = ch2 + this.peek(2);
20193        var op1 = OPERATORS[ch];
20194        var op2 = OPERATORS[ch2];
20195        var op3 = OPERATORS[ch3];
20196        if (op1 || op2 || op3) {
20197          var token = op3 ? ch3 : (op2 ? ch2 : ch);
20198          this.tokens.push({index: this.index, text: token, operator: true});
20199          this.index += token.length;
20200        } else {
20201          this.throwError('Unexpected next character ', this.index, this.index + 1);
20202        }
20203      }
20204    }
20205    return this.tokens;
20206  },
20207
20208  is: function(ch, chars) {
20209    return chars.indexOf(ch) !== -1;
20210  },
20211
20212  peek: function(i) {
20213    var num = i || 1;
20214    return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;
20215  },
20216
20217  isNumber: function(ch) {
20218    return ('0' <= ch && ch <= '9') && typeof ch === 'string';
20219  },
20220
20221  isWhitespace: function(ch) {
20222    // IE treats non-breaking space as \u00A0
20223    return (ch === ' ' || ch === '\r' || ch === '\t' ||
20224            ch === '\n' || ch === '\v' || ch === '\u00A0');
20225  },
20226
20227  isIdentifierStart: function(ch) {
20228    return this.options.isIdentifierStart ?
20229        this.options.isIdentifierStart(ch, this.codePointAt(ch)) :
20230        this.isValidIdentifierStart(ch);
20231  },
20232
20233  isValidIdentifierStart: function(ch) {
20234    return ('a' <= ch && ch <= 'z' ||
20235            'A' <= ch && ch <= 'Z' ||
20236            '_' === ch || ch === '$');
20237  },
20238
20239  isIdentifierContinue: function(ch) {
20240    return this.options.isIdentifierContinue ?
20241        this.options.isIdentifierContinue(ch, this.codePointAt(ch)) :
20242        this.isValidIdentifierContinue(ch);
20243  },
20244
20245  isValidIdentifierContinue: function(ch, cp) {
20246    return this.isValidIdentifierStart(ch, cp) || this.isNumber(ch);
20247  },
20248
20249  codePointAt: function(ch) {
20250    if (ch.length === 1) return ch.charCodeAt(0);
20251    // eslint-disable-next-line no-bitwise
20252    return (ch.charCodeAt(0) << 10) + ch.charCodeAt(1) - 0x35FDC00;
20253  },
20254
20255  peekMultichar: function() {
20256    var ch = this.text.charAt(this.index);
20257    var peek = this.peek();
20258    if (!peek) {
20259      return ch;
20260    }
20261    var cp1 = ch.charCodeAt(0);
20262    var cp2 = peek.charCodeAt(0);
20263    if (cp1 >= 0xD800 && cp1 <= 0xDBFF && cp2 >= 0xDC00 && cp2 <= 0xDFFF) {
20264      return ch + peek;
20265    }
20266    return ch;
20267  },
20268
20269  isExpOperator: function(ch) {
20270    return (ch === '-' || ch === '+' || this.isNumber(ch));
20271  },
20272
20273  throwError: function(error, start, end) {
20274    end = end || this.index;
20275    var colStr = (isDefined(start)
20276            ? 's ' + start +  '-' + this.index + ' [' + this.text.substring(start, end) + ']'
20277            : ' ' + end);
20278    throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',
20279        error, colStr, this.text);
20280  },
20281
20282  readNumber: function() {
20283    var number = '';
20284    var start = this.index;
20285    while (this.index < this.text.length) {
20286      var ch = lowercase(this.text.charAt(this.index));
20287      if (ch === '.' || this.isNumber(ch)) {
20288        number += ch;
20289      } else {
20290        var peekCh = this.peek();
20291        if (ch === 'e' && this.isExpOperator(peekCh)) {
20292          number += ch;
20293        } else if (this.isExpOperator(ch) &&
20294            peekCh && this.isNumber(peekCh) &&
20295            number.charAt(number.length - 1) === 'e') {
20296          number += ch;
20297        } else if (this.isExpOperator(ch) &&
20298            (!peekCh || !this.isNumber(peekCh)) &&
20299            number.charAt(number.length - 1) === 'e') {
20300          this.throwError('Invalid exponent');
20301        } else {
20302          break;
20303        }
20304      }
20305      this.index++;
20306    }
20307    this.tokens.push({
20308      index: start,
20309      text: number,
20310      constant: true,
20311      value: Number(number)
20312    });
20313  },
20314
20315  readIdent: function() {
20316    var start = this.index;
20317    this.index += this.peekMultichar().length;
20318    while (this.index < this.text.length) {
20319      var ch = this.peekMultichar();
20320      if (!this.isIdentifierContinue(ch)) {
20321        break;
20322      }
20323      this.index += ch.length;
20324    }
20325    this.tokens.push({
20326      index: start,
20327      text: this.text.slice(start, this.index),
20328      identifier: true
20329    });
20330  },
20331
20332  readString: function(quote) {
20333    var start = this.index;
20334    this.index++;
20335    var string = '';
20336    var rawString = quote;
20337    var escape = false;
20338    while (this.index < this.text.length) {
20339      var ch = this.text.charAt(this.index);
20340      rawString += ch;
20341      if (escape) {
20342        if (ch === 'u') {
20343          var hex = this.text.substring(this.index + 1, this.index + 5);
20344          if (!hex.match(/[\da-f]{4}/i)) {
20345            this.throwError('Invalid unicode escape [\\u' + hex + ']');
20346          }
20347          this.index += 4;
20348          string += String.fromCharCode(parseInt(hex, 16));
20349        } else {
20350          var rep = ESCAPE[ch];
20351          string = string + (rep || ch);
20352        }
20353        escape = false;
20354      } else if (ch === '\\') {
20355        escape = true;
20356      } else if (ch === quote) {
20357        this.index++;
20358        this.tokens.push({
20359          index: start,
20360          text: rawString,
20361          constant: true,
20362          value: string
20363        });
20364        return;
20365      } else {
20366        string += ch;
20367      }
20368      this.index++;
20369    }
20370    this.throwError('Unterminated quote', start);
20371  }
20372};
20373
20374var AST = function AST(lexer, options) {
20375  this.lexer = lexer;
20376  this.options = options;
20377};
20378
20379AST.Program = 'Program';
20380AST.ExpressionStatement = 'ExpressionStatement';
20381AST.AssignmentExpression = 'AssignmentExpression';
20382AST.ConditionalExpression = 'ConditionalExpression';
20383AST.LogicalExpression = 'LogicalExpression';
20384AST.BinaryExpression = 'BinaryExpression';
20385AST.UnaryExpression = 'UnaryExpression';
20386AST.CallExpression = 'CallExpression';
20387AST.MemberExpression = 'MemberExpression';
20388AST.Identifier = 'Identifier';
20389AST.Literal = 'Literal';
20390AST.ArrayExpression = 'ArrayExpression';
20391AST.Property = 'Property';
20392AST.ObjectExpression = 'ObjectExpression';
20393AST.ThisExpression = 'ThisExpression';
20394AST.LocalsExpression = 'LocalsExpression';
20395
20396// Internal use only
20397AST.NGValueParameter = 'NGValueParameter';
20398
20399AST.prototype = {
20400  ast: function(text) {
20401    this.text = text;
20402    this.tokens = this.lexer.lex(text);
20403
20404    var value = this.program();
20405
20406    if (this.tokens.length !== 0) {
20407      this.throwError('is an unexpected token', this.tokens[0]);
20408    }
20409
20410    return value;
20411  },
20412
20413  program: function() {
20414    var body = [];
20415    while (true) {
20416      if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
20417        body.push(this.expressionStatement());
20418      if (!this.expect(';')) {
20419        return { type: AST.Program, body: body};
20420      }
20421    }
20422  },
20423
20424  expressionStatement: function() {
20425    return { type: AST.ExpressionStatement, expression: this.filterChain() };
20426  },
20427
20428  filterChain: function() {
20429    var left = this.expression();
20430    while (this.expect('|')) {
20431      left = this.filter(left);
20432    }
20433    return left;
20434  },
20435
20436  expression: function() {
20437    return this.assignment();
20438  },
20439
20440  assignment: function() {
20441    var result = this.ternary();
20442    if (this.expect('=')) {
20443      if (!isAssignable(result)) {
20444        throw $parseMinErr('lval', 'Trying to assign a value to a non l-value');
20445      }
20446
20447      result = { type: AST.AssignmentExpression, left: result, right: this.assignment(), operator: '='};
20448    }
20449    return result;
20450  },
20451
20452  ternary: function() {
20453    var test = this.logicalOR();
20454    var alternate;
20455    var consequent;
20456    if (this.expect('?')) {
20457      alternate = this.expression();
20458      if (this.consume(':')) {
20459        consequent = this.expression();
20460        return { type: AST.ConditionalExpression, test: test, alternate: alternate, consequent: consequent};
20461      }
20462    }
20463    return test;
20464  },
20465
20466  logicalOR: function() {
20467    var left = this.logicalAND();
20468    while (this.expect('||')) {
20469      left = { type: AST.LogicalExpression, operator: '||', left: left, right: this.logicalAND() };
20470    }
20471    return left;
20472  },
20473
20474  logicalAND: function() {
20475    var left = this.equality();
20476    while (this.expect('&&')) {
20477      left = { type: AST.LogicalExpression, operator: '&&', left: left, right: this.equality()};
20478    }
20479    return left;
20480  },
20481
20482  equality: function() {
20483    var left = this.relational();
20484    var token;
20485    while ((token = this.expect('==','!=','===','!=='))) {
20486      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.relational() };
20487    }
20488    return left;
20489  },
20490
20491  relational: function() {
20492    var left = this.additive();
20493    var token;
20494    while ((token = this.expect('<', '>', '<=', '>='))) {
20495      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.additive() };
20496    }
20497    return left;
20498  },
20499
20500  additive: function() {
20501    var left = this.multiplicative();
20502    var token;
20503    while ((token = this.expect('+','-'))) {
20504      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.multiplicative() };
20505    }
20506    return left;
20507  },
20508
20509  multiplicative: function() {
20510    var left = this.unary();
20511    var token;
20512    while ((token = this.expect('*','/','%'))) {
20513      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.unary() };
20514    }
20515    return left;
20516  },
20517
20518  unary: function() {
20519    var token;
20520    if ((token = this.expect('+', '-', '!'))) {
20521      return { type: AST.UnaryExpression, operator: token.text, prefix: true, argument: this.unary() };
20522    } else {
20523      return this.primary();
20524    }
20525  },
20526
20527  primary: function() {
20528    var primary;
20529    if (this.expect('(')) {
20530      primary = this.filterChain();
20531      this.consume(')');
20532    } else if (this.expect('[')) {
20533      primary = this.arrayDeclaration();
20534    } else if (this.expect('{')) {
20535      primary = this.object();
20536    } else if (this.selfReferential.hasOwnProperty(this.peek().text)) {
20537      primary = copy(this.selfReferential[this.consume().text]);
20538    } else if (this.options.literals.hasOwnProperty(this.peek().text)) {
20539      primary = { type: AST.Literal, value: this.options.literals[this.consume().text]};
20540    } else if (this.peek().identifier) {
20541      primary = this.identifier();
20542    } else if (this.peek().constant) {
20543      primary = this.constant();
20544    } else {
20545      this.throwError('not a primary expression', this.peek());
20546    }
20547
20548    var next;
20549    while ((next = this.expect('(', '[', '.'))) {
20550      if (next.text === '(') {
20551        primary = {type: AST.CallExpression, callee: primary, arguments: this.parseArguments() };
20552        this.consume(')');
20553      } else if (next.text === '[') {
20554        primary = { type: AST.MemberExpression, object: primary, property: this.expression(), computed: true };
20555        this.consume(']');
20556      } else if (next.text === '.') {
20557        primary = { type: AST.MemberExpression, object: primary, property: this.identifier(), computed: false };
20558      } else {
20559        this.throwError('IMPOSSIBLE');
20560      }
20561    }
20562    return primary;
20563  },
20564
20565  filter: function(baseExpression) {
20566    var args = [baseExpression];
20567    var result = {type: AST.CallExpression, callee: this.identifier(), arguments: args, filter: true};
20568
20569    while (this.expect(':')) {
20570      args.push(this.expression());
20571    }
20572
20573    return result;
20574  },
20575
20576  parseArguments: function() {
20577    var args = [];
20578    if (this.peekToken().text !== ')') {
20579      do {
20580        args.push(this.filterChain());
20581      } while (this.expect(','));
20582    }
20583    return args;
20584  },
20585
20586  identifier: function() {
20587    var token = this.consume();
20588    if (!token.identifier) {
20589      this.throwError('is not a valid identifier', token);
20590    }
20591    return { type: AST.Identifier, name: token.text };
20592  },
20593
20594  constant: function() {
20595    // TODO check that it is a constant
20596    return { type: AST.Literal, value: this.consume().value };
20597  },
20598
20599  arrayDeclaration: function() {
20600    var elements = [];
20601    if (this.peekToken().text !== ']') {
20602      do {
20603        if (this.peek(']')) {
20604          // Support trailing commas per ES5.1.
20605          break;
20606        }
20607        elements.push(this.expression());
20608      } while (this.expect(','));
20609    }
20610    this.consume(']');
20611
20612    return { type: AST.ArrayExpression, elements: elements };
20613  },
20614
20615  object: function() {
20616    var properties = [], property;
20617    if (this.peekToken().text !== '}') {
20618      do {
20619        if (this.peek('}')) {
20620          // Support trailing commas per ES5.1.
20621          break;
20622        }
20623        property = {type: AST.Property, kind: 'init'};
20624        if (this.peek().constant) {
20625          property.key = this.constant();
20626          property.computed = false;
20627          this.consume(':');
20628          property.value = this.expression();
20629        } else if (this.peek().identifier) {
20630          property.key = this.identifier();
20631          property.computed = false;
20632          if (this.peek(':')) {
20633            this.consume(':');
20634            property.value = this.expression();
20635          } else {
20636            property.value = property.key;
20637          }
20638        } else if (this.peek('[')) {
20639          this.consume('[');
20640          property.key = this.expression();
20641          this.consume(']');
20642          property.computed = true;
20643          this.consume(':');
20644          property.value = this.expression();
20645        } else {
20646          this.throwError('invalid key', this.peek());
20647        }
20648        properties.push(property);
20649      } while (this.expect(','));
20650    }
20651    this.consume('}');
20652
20653    return {type: AST.ObjectExpression, properties: properties };
20654  },
20655
20656  throwError: function(msg, token) {
20657    throw $parseMinErr('syntax',
20658        'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].',
20659          token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));
20660  },
20661
20662  consume: function(e1) {
20663    if (this.tokens.length === 0) {
20664      throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
20665    }
20666
20667    var token = this.expect(e1);
20668    if (!token) {
20669      this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
20670    }
20671    return token;
20672  },
20673
20674  peekToken: function() {
20675    if (this.tokens.length === 0) {
20676      throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
20677    }
20678    return this.tokens[0];
20679  },
20680
20681  peek: function(e1, e2, e3, e4) {
20682    return this.peekAhead(0, e1, e2, e3, e4);
20683  },
20684
20685  peekAhead: function(i, e1, e2, e3, e4) {
20686    if (this.tokens.length > i) {
20687      var token = this.tokens[i];
20688      var t = token.text;
20689      if (t === e1 || t === e2 || t === e3 || t === e4 ||
20690          (!e1 && !e2 && !e3 && !e4)) {
20691        return token;
20692      }
20693    }
20694    return false;
20695  },
20696
20697  expect: function(e1, e2, e3, e4) {
20698    var token = this.peek(e1, e2, e3, e4);
20699    if (token) {
20700      this.tokens.shift();
20701      return token;
20702    }
20703    return false;
20704  },
20705
20706  selfReferential: {
20707    'this': {type: AST.ThisExpression },
20708    '$locals': {type: AST.LocalsExpression }
20709  }
20710};
20711
20712function ifDefined(v, d) {
20713  return typeof v !== 'undefined' ? v : d;
20714}
20715
20716function plusFn(l, r) {
20717  if (typeof l === 'undefined') return r;
20718  if (typeof r === 'undefined') return l;
20719  return l + r;
20720}
20721
20722function isStateless($filter, filterName) {
20723  var fn = $filter(filterName);
20724  return !fn.$stateful;
20725}
20726
20727function findConstantAndWatchExpressions(ast, $filter) {
20728  var allConstants;
20729  var argsToWatch;
20730  var isStatelessFilter;
20731  switch (ast.type) {
20732  case AST.Program:
20733    allConstants = true;
20734    forEach(ast.body, function(expr) {
20735      findConstantAndWatchExpressions(expr.expression, $filter);
20736      allConstants = allConstants && expr.expression.constant;
20737    });
20738    ast.constant = allConstants;
20739    break;
20740  case AST.Literal:
20741    ast.constant = true;
20742    ast.toWatch = [];
20743    break;
20744  case AST.UnaryExpression:
20745    findConstantAndWatchExpressions(ast.argument, $filter);
20746    ast.constant = ast.argument.constant;
20747    ast.toWatch = ast.argument.toWatch;
20748    break;
20749  case AST.BinaryExpression:
20750    findConstantAndWatchExpressions(ast.left, $filter);
20751    findConstantAndWatchExpressions(ast.right, $filter);
20752    ast.constant = ast.left.constant && ast.right.constant;
20753    ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch);
20754    break;
20755  case AST.LogicalExpression:
20756    findConstantAndWatchExpressions(ast.left, $filter);
20757    findConstantAndWatchExpressions(ast.right, $filter);
20758    ast.constant = ast.left.constant && ast.right.constant;
20759    ast.toWatch = ast.constant ? [] : [ast];
20760    break;
20761  case AST.ConditionalExpression:
20762    findConstantAndWatchExpressions(ast.test, $filter);
20763    findConstantAndWatchExpressions(ast.alternate, $filter);
20764    findConstantAndWatchExpressions(ast.consequent, $filter);
20765    ast.constant = ast.test.constant && ast.alternate.constant && ast.consequent.constant;
20766    ast.toWatch = ast.constant ? [] : [ast];
20767    break;
20768  case AST.Identifier:
20769    ast.constant = false;
20770    ast.toWatch = [ast];
20771    break;
20772  case AST.MemberExpression:
20773    findConstantAndWatchExpressions(ast.object, $filter);
20774    if (ast.computed) {
20775      findConstantAndWatchExpressions(ast.property, $filter);
20776    }
20777    ast.constant = ast.object.constant && (!ast.computed || ast.property.constant);
20778    ast.toWatch = [ast];
20779    break;
20780  case AST.CallExpression:
20781    isStatelessFilter = ast.filter ? isStateless($filter, ast.callee.name) : false;
20782    allConstants = isStatelessFilter;
20783    argsToWatch = [];
20784    forEach(ast.arguments, function(expr) {
20785      findConstantAndWatchExpressions(expr, $filter);
20786      allConstants = allConstants && expr.constant;
20787      if (!expr.constant) {
20788        argsToWatch.push.apply(argsToWatch, expr.toWatch);
20789      }
20790    });
20791    ast.constant = allConstants;
20792    ast.toWatch = isStatelessFilter ? argsToWatch : [ast];
20793    break;
20794  case AST.AssignmentExpression:
20795    findConstantAndWatchExpressions(ast.left, $filter);
20796    findConstantAndWatchExpressions(ast.right, $filter);
20797    ast.constant = ast.left.constant && ast.right.constant;
20798    ast.toWatch = [ast];
20799    break;
20800  case AST.ArrayExpression:
20801    allConstants = true;
20802    argsToWatch = [];
20803    forEach(ast.elements, function(expr) {
20804      findConstantAndWatchExpressions(expr, $filter);
20805      allConstants = allConstants && expr.constant;
20806      if (!expr.constant) {
20807        argsToWatch.push.apply(argsToWatch, expr.toWatch);
20808      }
20809    });
20810    ast.constant = allConstants;
20811    ast.toWatch = argsToWatch;
20812    break;
20813  case AST.ObjectExpression:
20814    allConstants = true;
20815    argsToWatch = [];
20816    forEach(ast.properties, function(property) {
20817      findConstantAndWatchExpressions(property.value, $filter);
20818      allConstants = allConstants && property.value.constant && !property.computed;
20819      if (!property.value.constant) {
20820        argsToWatch.push.apply(argsToWatch, property.value.toWatch);
20821      }
20822    });
20823    ast.constant = allConstants;
20824    ast.toWatch = argsToWatch;
20825    break;
20826  case AST.ThisExpression:
20827    ast.constant = false;
20828    ast.toWatch = [];
20829    break;
20830  case AST.LocalsExpression:
20831    ast.constant = false;
vendor: 32,119 bytes, lines 20832-21779
20832    ast.toWatch = [];
20833    break;
20834  }
20835}
20836
20837function getInputs(body) {
20838  if (body.length !== 1) return;
20839  var lastExpression = body[0].expression;
20840  var candidate = lastExpression.toWatch;
20841  if (candidate.length !== 1) return candidate;
20842  return candidate[0] !== lastExpression ? candidate : undefined;
20843}
20844
20845function isAssignable(ast) {
20846  return ast.type === AST.Identifier || ast.type === AST.MemberExpression;
20847}
20848
20849function assignableAST(ast) {
20850  if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) {
20851    return {type: AST.AssignmentExpression, left: ast.body[0].expression, right: {type: AST.NGValueParameter}, operator: '='};
20852  }
20853}
20854
20855function isLiteral(ast) {
20856  return ast.body.length === 0 ||
20857      ast.body.length === 1 && (
20858      ast.body[0].expression.type === AST.Literal ||
20859      ast.body[0].expression.type === AST.ArrayExpression ||
20860      ast.body[0].expression.type === AST.ObjectExpression);
20861}
20862
20863function isConstant(ast) {
20864  return ast.constant;
20865}
20866
20867function ASTCompiler(astBuilder, $filter) {
20868  this.astBuilder = astBuilder;
20869  this.$filter = $filter;
20870}
20871
20872ASTCompiler.prototype = {
20873  compile: function(expression, expensiveChecks) {
20874    var self = this;
20875    var ast = this.astBuilder.ast(expression);
20876    this.state = {
20877      nextId: 0,
20878      filters: {},
20879      expensiveChecks: expensiveChecks,
20880      fn: {vars: [], body: [], own: {}},
20881      assign: {vars: [], body: [], own: {}},
20882      inputs: []
20883    };
20884    findConstantAndWatchExpressions(ast, self.$filter);
20885    var extra = '';
20886    var assignable;
20887    this.stage = 'assign';
20888    if ((assignable = assignableAST(ast))) {
20889      this.state.computing = 'assign';
20890      var result = this.nextId();
20891      this.recurse(assignable, result);
20892      this.return_(result);
20893      extra = 'fn.assign=' + this.generateFunction('assign', 's,v,l');
20894    }
20895    var toWatch = getInputs(ast.body);
20896    self.stage = 'inputs';
20897    forEach(toWatch, function(watch, key) {
20898      var fnKey = 'fn' + key;
20899      self.state[fnKey] = {vars: [], body: [], own: {}};
20900      self.state.computing = fnKey;
20901      var intoId = self.nextId();
20902      self.recurse(watch, intoId);
20903      self.return_(intoId);
20904      self.state.inputs.push(fnKey);
20905      watch.watchId = key;
20906    });
20907    this.state.computing = 'fn';
20908    this.stage = 'main';
20909    this.recurse(ast);
20910    var fnString =
20911      // The build and minification steps remove the string "use strict" from the code, but this is done using a regex.
20912      // This is a workaround for this until we do a better job at only removing the prefix only when we should.
20913      '"' + this.USE + ' ' + this.STRICT + '";\n' +
20914      this.filterPrefix() +
20915      'var fn=' + this.generateFunction('fn', 's,l,a,i') +
20916      extra +
20917      this.watchFns() +
20918      'return fn;';
20919
20920    // eslint-disable-next-line no-new-func
20921    var fn = (new Function('$filter',
20922        'ensureSafeMemberName',
20923        'ensureSafeObject',
20924        'ensureSafeFunction',
20925        'getStringValue',
20926        'ensureSafeAssignContext',
20927        'ifDefined',
20928        'plus',
20929        'text',
20930        fnString))(
20931          this.$filter,
20932          ensureSafeMemberName,
20933          ensureSafeObject,
20934          ensureSafeFunction,
20935          getStringValue,
20936          ensureSafeAssignContext,
20937          ifDefined,
20938          plusFn,
20939          expression);
20940    this.state = this.stage = undefined;
20941    fn.literal = isLiteral(ast);
20942    fn.constant = isConstant(ast);
20943    return fn;
20944  },
20945
20946  USE: 'use',
20947
20948  STRICT: 'strict',
20949
20950  watchFns: function() {
20951    var result = [];
20952    var fns = this.state.inputs;
20953    var self = this;
20954    forEach(fns, function(name) {
20955      result.push('var ' + name + '=' + self.generateFunction(name, 's'));
20956    });
20957    if (fns.length) {
20958      result.push('fn.inputs=[' + fns.join(',') + '];');
20959    }
20960    return result.join('');
20961  },
20962
20963  generateFunction: function(name, params) {
20964    return 'function(' + params + '){' +
20965        this.varsPrefix(name) +
20966        this.body(name) +
20967        '};';
20968  },
20969
20970  filterPrefix: function() {
20971    var parts = [];
20972    var self = this;
20973    forEach(this.state.filters, function(id, filter) {
20974      parts.push(id + '=$filter(' + self.escape(filter) + ')');
20975    });
20976    if (parts.length) return 'var ' + parts.join(',') + ';';
20977    return '';
20978  },
20979
20980  varsPrefix: function(section) {
20981    return this.state[section].vars.length ? 'var ' + this.state[section].vars.join(',') + ';' : '';
20982  },
20983
20984  body: function(section) {
20985    return this.state[section].body.join('');
20986  },
20987
20988  recurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
20989    var left, right, self = this, args, expression, computed;
20990    recursionFn = recursionFn || noop;
20991    if (!skipWatchIdCheck && isDefined(ast.watchId)) {
20992      intoId = intoId || this.nextId();
20993      this.if_('i',
20994        this.lazyAssign(intoId, this.computedMember('i', ast.watchId)),
20995        this.lazyRecurse(ast, intoId, nameId, recursionFn, create, true)
20996      );
20997      return;
20998    }
20999    switch (ast.type) {
21000    case AST.Program:
21001      forEach(ast.body, function(expression, pos) {
21002        self.recurse(expression.expression, undefined, undefined, function(expr) { right = expr; });
21003        if (pos !== ast.body.length - 1) {
21004          self.current().body.push(right, ';');
21005        } else {
21006          self.return_(right);
21007        }
21008      });
21009      break;
21010    case AST.Literal:
21011      expression = this.escape(ast.value);
21012      this.assign(intoId, expression);
21013      recursionFn(expression);
21014      break;
21015    case AST.UnaryExpression:
21016      this.recurse(ast.argument, undefined, undefined, function(expr) { right = expr; });
21017      expression = ast.operator + '(' + this.ifDefined(right, 0) + ')';
21018      this.assign(intoId, expression);
21019      recursionFn(expression);
21020      break;
21021    case AST.BinaryExpression:
21022      this.recurse(ast.left, undefined, undefined, function(expr) { left = expr; });
21023      this.recurse(ast.right, undefined, undefined, function(expr) { right = expr; });
21024      if (ast.operator === '+') {
21025        expression = this.plus(left, right);
21026      } else if (ast.operator === '-') {
21027        expression = this.ifDefined(left, 0) + ast.operator + this.ifDefined(right, 0);
21028      } else {
21029        expression = '(' + left + ')' + ast.operator + '(' + right + ')';
21030      }
21031      this.assign(intoId, expression);
21032      recursionFn(expression);
21033      break;
21034    case AST.LogicalExpression:
21035      intoId = intoId || this.nextId();
21036      self.recurse(ast.left, intoId);
21037      self.if_(ast.operator === '&&' ? intoId : self.not(intoId), self.lazyRecurse(ast.right, intoId));
21038      recursionFn(intoId);
21039      break;
21040    case AST.ConditionalExpression:
21041      intoId = intoId || this.nextId();
21042      self.recurse(ast.test, intoId);
21043      self.if_(intoId, self.lazyRecurse(ast.alternate, intoId), self.lazyRecurse(ast.consequent, intoId));
21044      recursionFn(intoId);
21045      break;
21046    case AST.Identifier:
21047      intoId = intoId || this.nextId();
21048      if (nameId) {
21049        nameId.context = self.stage === 'inputs' ? 's' : this.assign(this.nextId(), this.getHasOwnProperty('l', ast.name) + '?l:s');
21050        nameId.computed = false;
21051        nameId.name = ast.name;
21052      }
21053      ensureSafeMemberName(ast.name);
21054      self.if_(self.stage === 'inputs' || self.not(self.getHasOwnProperty('l', ast.name)),
21055        function() {
21056          self.if_(self.stage === 'inputs' || 's', function() {
21057            if (create && create !== 1) {
21058              self.if_(
21059                self.not(self.nonComputedMember('s', ast.name)),
21060                self.lazyAssign(self.nonComputedMember('s', ast.name), '{}'));
21061            }
21062            self.assign(intoId, self.nonComputedMember('s', ast.name));
21063          });
21064        }, intoId && self.lazyAssign(intoId, self.nonComputedMember('l', ast.name))
21065        );
21066      if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.name)) {
21067        self.addEnsureSafeObject(intoId);
21068      }
21069      recursionFn(intoId);
21070      break;
21071    case AST.MemberExpression:
21072      left = nameId && (nameId.context = this.nextId()) || this.nextId();
21073      intoId = intoId || this.nextId();
21074      self.recurse(ast.object, left, undefined, function() {
21075        self.if_(self.notNull(left), function() {
21076          if (create && create !== 1) {
21077            self.addEnsureSafeAssignContext(left);
21078          }
21079          if (ast.computed) {
21080            right = self.nextId();
21081            self.recurse(ast.property, right);
21082            self.getStringValue(right);
21083            self.addEnsureSafeMemberName(right);
21084            if (create && create !== 1) {
21085              self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}'));
21086            }
21087            expression = self.ensureSafeObject(self.computedMember(left, right));
21088            self.assign(intoId, expression);
21089            if (nameId) {
21090              nameId.computed = true;
21091              nameId.name = right;
21092            }
21093          } else {
21094            ensureSafeMemberName(ast.property.name);
21095            if (create && create !== 1) {
21096              self.if_(self.not(self.nonComputedMember(left, ast.property.name)), self.lazyAssign(self.nonComputedMember(left, ast.property.name), '{}'));
21097            }
21098            expression = self.nonComputedMember(left, ast.property.name);
21099            if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.property.name)) {
21100              expression = self.ensureSafeObject(expression);
21101            }
21102            self.assign(intoId, expression);
21103            if (nameId) {
21104              nameId.computed = false;
21105              nameId.name = ast.property.name;
21106            }
21107          }
21108        }, function() {
21109          self.assign(intoId, 'undefined');
21110        });
21111        recursionFn(intoId);
21112      }, !!create);
21113      break;
21114    case AST.CallExpression:
21115      intoId = intoId || this.nextId();
21116      if (ast.filter) {
21117        right = self.filter(ast.callee.name);
21118        args = [];
21119        forEach(ast.arguments, function(expr) {
21120          var argument = self.nextId();
21121          self.recurse(expr, argument);
21122          args.push(argument);
21123        });
21124        expression = right + '(' + args.join(',') + ')';
21125        self.assign(intoId, expression);
21126        recursionFn(intoId);
21127      } else {
21128        right = self.nextId();
21129        left = {};
21130        args = [];
21131        self.recurse(ast.callee, right, left, function() {
21132          self.if_(self.notNull(right), function() {
21133            self.addEnsureSafeFunction(right);
21134            forEach(ast.arguments, function(expr) {
21135              self.recurse(expr, self.nextId(), undefined, function(argument) {
21136                args.push(self.ensureSafeObject(argument));
21137              });
21138            });
21139            if (left.name) {
21140              if (!self.state.expensiveChecks) {
21141                self.addEnsureSafeObject(left.context);
21142              }
21143              expression = self.member(left.context, left.name, left.computed) + '(' + args.join(',') + ')';
21144            } else {
21145              expression = right + '(' + args.join(',') + ')';
21146            }
21147            expression = self.ensureSafeObject(expression);
21148            self.assign(intoId, expression);
21149          }, function() {
21150            self.assign(intoId, 'undefined');
21151          });
21152          recursionFn(intoId);
21153        });
21154      }
21155      break;
21156    case AST.AssignmentExpression:
21157      right = this.nextId();
21158      left = {};
21159      this.recurse(ast.left, undefined, left, function() {
21160        self.if_(self.notNull(left.context), function() {
21161          self.recurse(ast.right, right);
21162          self.addEnsureSafeObject(self.member(left.context, left.name, left.computed));
21163          self.addEnsureSafeAssignContext(left.context);
21164          expression = self.member(left.context, left.name, left.computed) + ast.operator + right;
21165          self.assign(intoId, expression);
21166          recursionFn(intoId || expression);
21167        });
21168      }, 1);
21169      break;
21170    case AST.ArrayExpression:
21171      args = [];
21172      forEach(ast.elements, function(expr) {
21173        self.recurse(expr, self.nextId(), undefined, function(argument) {
21174          args.push(argument);
21175        });
21176      });
21177      expression = '[' + args.join(',') + ']';
21178      this.assign(intoId, expression);
21179      recursionFn(expression);
21180      break;
21181    case AST.ObjectExpression:
21182      args = [];
21183      computed = false;
21184      forEach(ast.properties, function(property) {
21185        if (property.computed) {
21186          computed = true;
21187        }
21188      });
21189      if (computed) {
21190        intoId = intoId || this.nextId();
21191        this.assign(intoId, '{}');
21192        forEach(ast.properties, function(property) {
21193          if (property.computed) {
21194            left = self.nextId();
21195            self.recurse(property.key, left);
21196          } else {
21197            left = property.key.type === AST.Identifier ?
21198                       property.key.name :
21199                       ('' + property.key.value);
21200          }
21201          right = self.nextId();
21202          self.recurse(property.value, right);
21203          self.assign(self.member(intoId, left, property.computed), right);
21204        });
21205      } else {
21206        forEach(ast.properties, function(property) {
21207          self.recurse(property.value, ast.constant ? undefined : self.nextId(), undefined, function(expr) {
21208            args.push(self.escape(
21209                property.key.type === AST.Identifier ? property.key.name :
21210                  ('' + property.key.value)) +
21211                ':' + expr);
21212          });
21213        });
21214        expression = '{' + args.join(',') + '}';
21215        this.assign(intoId, expression);
21216      }
21217      recursionFn(intoId || expression);
21218      break;
21219    case AST.ThisExpression:
21220      this.assign(intoId, 's');
21221      recursionFn('s');
21222      break;
21223    case AST.LocalsExpression:
21224      this.assign(intoId, 'l');
21225      recursionFn('l');
21226      break;
21227    case AST.NGValueParameter:
21228      this.assign(intoId, 'v');
21229      recursionFn('v');
21230      break;
21231    }
21232  },
21233
21234  getHasOwnProperty: function(element, property) {
21235    var key = element + '.' + property;
21236    var own = this.current().own;
21237    if (!own.hasOwnProperty(key)) {
21238      own[key] = this.nextId(false, element + '&&(' + this.escape(property) + ' in ' + element + ')');
21239    }
21240    return own[key];
21241  },
21242
21243  assign: function(id, value) {
21244    if (!id) return;
21245    this.current().body.push(id, '=', value, ';');
21246    return id;
21247  },
21248
21249  filter: function(filterName) {
21250    if (!this.state.filters.hasOwnProperty(filterName)) {
21251      this.state.filters[filterName] = this.nextId(true);
21252    }
21253    return this.state.filters[filterName];
21254  },
21255
21256  ifDefined: function(id, defaultValue) {
21257    return 'ifDefined(' + id + ',' + this.escape(defaultValue) + ')';
21258  },
21259
21260  plus: function(left, right) {
21261    return 'plus(' + left + ',' + right + ')';
21262  },
21263
21264  return_: function(id) {
21265    this.current().body.push('return ', id, ';');
21266  },
21267
21268  if_: function(test, alternate, consequent) {
21269    if (test === true) {
21270      alternate();
21271    } else {
21272      var body = this.current().body;
21273      body.push('if(', test, '){');
21274      alternate();
21275      body.push('}');
21276      if (consequent) {
21277        body.push('else{');
21278        consequent();
21279        body.push('}');
21280      }
21281    }
21282  },
21283
21284  not: function(expression) {
21285    return '!(' + expression + ')';
21286  },
21287
21288  notNull: function(expression) {
21289    return expression + '!=null';
21290  },
21291
21292  nonComputedMember: function(left, right) {
21293    var SAFE_IDENTIFIER = /^[$_a-zA-Z][$_a-zA-Z0-9]*$/;
21294    var UNSAFE_CHARACTERS = /[^$_a-zA-Z0-9]/g;
21295    if (SAFE_IDENTIFIER.test(right)) {
21296      return left + '.' + right;
21297    } else {
21298      return left  + '["' + right.replace(UNSAFE_CHARACTERS, this.stringEscapeFn) + '"]';
21299    }
21300  },
21301
21302  computedMember: function(left, right) {
21303    return left + '[' + right + ']';
21304  },
21305
21306  member: function(left, right, computed) {
21307    if (computed) return this.computedMember(left, right);
21308    return this.nonComputedMember(left, right);
21309  },
21310
21311  addEnsureSafeObject: function(item) {
21312    this.current().body.push(this.ensureSafeObject(item), ';');
21313  },
21314
21315  addEnsureSafeMemberName: function(item) {
21316    this.current().body.push(this.ensureSafeMemberName(item), ';');
21317  },
21318
21319  addEnsureSafeFunction: function(item) {
21320    this.current().body.push(this.ensureSafeFunction(item), ';');
21321  },
21322
21323  addEnsureSafeAssignContext: function(item) {
21324    this.current().body.push(this.ensureSafeAssignContext(item), ';');
21325  },
21326
21327  ensureSafeObject: function(item) {
21328    return 'ensureSafeObject(' + item + ',text)';
21329  },
21330
21331  ensureSafeMemberName: function(item) {
21332    return 'ensureSafeMemberName(' + item + ',text)';
21333  },
21334
21335  ensureSafeFunction: function(item) {
21336    return 'ensureSafeFunction(' + item + ',text)';
21337  },
21338
21339  getStringValue: function(item) {
21340    this.assign(item, 'getStringValue(' + item + ')');
21341  },
21342
21343  ensureSafeAssignContext: function(item) {
21344    return 'ensureSafeAssignContext(' + item + ',text)';
21345  },
21346
21347  lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
21348    var self = this;
21349    return function() {
21350      self.recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck);
21351    };
21352  },
21353
21354  lazyAssign: function(id, value) {
21355    var self = this;
21356    return function() {
21357      self.assign(id, value);
21358    };
21359  },
21360
21361  stringEscapeRegex: /[^ a-zA-Z0-9]/g,
21362
21363  stringEscapeFn: function(c) {
21364    return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4);
21365  },
21366
21367  escape: function(value) {
21368    if (isString(value)) return '\'' + value.replace(this.stringEscapeRegex, this.stringEscapeFn) + '\'';
21369    if (isNumber(value)) return value.toString();
21370    if (value === true) return 'true';
21371    if (value === false) return 'false';
21372    if (value === null) return 'null';
21373    if (typeof value === 'undefined') return 'undefined';
21374
21375    throw $parseMinErr('esc', 'IMPOSSIBLE');
21376  },
21377
21378  nextId: function(skip, init) {
21379    var id = 'v' + (this.state.nextId++);
21380    if (!skip) {
21381      this.current().vars.push(id + (init ? '=' + init : ''));
21382    }
21383    return id;
21384  },
21385
21386  current: function() {
21387    return this.state[this.state.computing];
21388  }
21389};
21390
21391
21392function ASTInterpreter(astBuilder, $filter) {
21393  this.astBuilder = astBuilder;
21394  this.$filter = $filter;
21395}
21396
21397ASTInterpreter.prototype = {
21398  compile: function(expression, expensiveChecks) {
21399    var self = this;
21400    var ast = this.astBuilder.ast(expression);
21401    this.expression = expression;
21402    this.expensiveChecks = expensiveChecks;
21403    findConstantAndWatchExpressions(ast, self.$filter);
21404    var assignable;
21405    var assign;
21406    if ((assignable = assignableAST(ast))) {
21407      assign = this.recurse(assignable);
21408    }
21409    var toWatch = getInputs(ast.body);
21410    var inputs;
21411    if (toWatch) {
21412      inputs = [];
21413      forEach(toWatch, function(watch, key) {
21414        var input = self.recurse(watch);
21415        watch.input = input;
21416        inputs.push(input);
21417        watch.watchId = key;
21418      });
21419    }
21420    var expressions = [];
21421    forEach(ast.body, function(expression) {
21422      expressions.push(self.recurse(expression.expression));
21423    });
21424    var fn = ast.body.length === 0 ? noop :
21425             ast.body.length === 1 ? expressions[0] :
21426             function(scope, locals) {
21427               var lastValue;
21428               forEach(expressions, function(exp) {
21429                 lastValue = exp(scope, locals);
21430               });
21431               return lastValue;
21432             };
21433    if (assign) {
21434      fn.assign = function(scope, value, locals) {
21435        return assign(scope, locals, value);
21436      };
21437    }
21438    if (inputs) {
21439      fn.inputs = inputs;
21440    }
21441    fn.literal = isLiteral(ast);
21442    fn.constant = isConstant(ast);
21443    return fn;
21444  },
21445
21446  recurse: function(ast, context, create) {
21447    var left, right, self = this, args;
21448    if (ast.input) {
21449      return this.inputs(ast.input, ast.watchId);
21450    }
21451    switch (ast.type) {
21452    case AST.Literal:
21453      return this.value(ast.value, context);
21454    case AST.UnaryExpression:
21455      right = this.recurse(ast.argument);
21456      return this['unary' + ast.operator](right, context);
21457    case AST.BinaryExpression:
21458      left = this.recurse(ast.left);
21459      right = this.recurse(ast.right);
21460      return this['binary' + ast.operator](left, right, context);
21461    case AST.LogicalExpression:
21462      left = this.recurse(ast.left);
21463      right = this.recurse(ast.right);
21464      return this['binary' + ast.operator](left, right, context);
21465    case AST.ConditionalExpression:
21466      return this['ternary?:'](
21467        this.recurse(ast.test),
21468        this.recurse(ast.alternate),
21469        this.recurse(ast.consequent),
21470        context
21471      );
21472    case AST.Identifier:
21473      ensureSafeMemberName(ast.name, self.expression);
21474      return self.identifier(ast.name,
21475                             self.expensiveChecks || isPossiblyDangerousMemberName(ast.name),
21476                             context, create, self.expression);
21477    case AST.MemberExpression:
21478      left = this.recurse(ast.object, false, !!create);
21479      if (!ast.computed) {
21480        ensureSafeMemberName(ast.property.name, self.expression);
21481        right = ast.property.name;
21482      }
21483      if (ast.computed) right = this.recurse(ast.property);
21484      return ast.computed ?
21485        this.computedMember(left, right, context, create, self.expression) :
21486        this.nonComputedMember(left, right, self.expensiveChecks, context, create, self.expression);
21487    case AST.CallExpression:
21488      args = [];
21489      forEach(ast.arguments, function(expr) {
21490        args.push(self.recurse(expr));
21491      });
21492      if (ast.filter) right = this.$filter(ast.callee.name);
21493      if (!ast.filter) right = this.recurse(ast.callee, true);
21494      return ast.filter ?
21495        function(scope, locals, assign, inputs) {
21496          var values = [];
21497          for (var i = 0; i < args.length; ++i) {
21498            values.push(args[i](scope, locals, assign, inputs));
21499          }
21500          var value = right.apply(undefined, values, inputs);
21501          return context ? {context: undefined, name: undefined, value: value} : value;
21502        } :
21503        function(scope, locals, assign, inputs) {
21504          var rhs = right(scope, locals, assign, inputs);
21505          var value;
21506          if (rhs.value != null) {
21507            ensureSafeObject(rhs.context, self.expression);
21508            ensureSafeFunction(rhs.value, self.expression);
21509            var values = [];
21510            for (var i = 0; i < args.length; ++i) {
21511              values.push(ensureSafeObject(args[i](scope, locals, assign, inputs), self.expression));
21512            }
21513            value = ensureSafeObject(rhs.value.apply(rhs.context, values), self.expression);
21514          }
21515          return context ? {value: value} : value;
21516        };
21517    case AST.AssignmentExpression:
21518      left = this.recurse(ast.left, true, 1);
21519      right = this.recurse(ast.right);
21520      return function(scope, locals, assign, inputs) {
21521        var lhs = left(scope, locals, assign, inputs);
21522        var rhs = right(scope, locals, assign, inputs);
21523        ensureSafeObject(lhs.value, self.expression);
21524        ensureSafeAssignContext(lhs.context);
21525        lhs.context[lhs.name] = rhs;
21526        return context ? {value: rhs} : rhs;
21527      };
21528    case AST.ArrayExpression:
21529      args = [];
21530      forEach(ast.elements, function(expr) {
21531        args.push(self.recurse(expr));
21532      });
21533      return function(scope, locals, assign, inputs) {
21534        var value = [];
21535        for (var i = 0; i < args.length; ++i) {
21536          value.push(args[i](scope, locals, assign, inputs));
21537        }
21538        return context ? {value: value} : value;
21539      };
21540    case AST.ObjectExpression:
21541      args = [];
21542      forEach(ast.properties, function(property) {
21543        if (property.computed) {
21544          args.push({key: self.recurse(property.key),
21545                     computed: true,
21546                     value: self.recurse(property.value)
21547          });
21548        } else {
21549          args.push({key: property.key.type === AST.Identifier ?
21550                          property.key.name :
21551                          ('' + property.key.value),
21552                     computed: false,
21553                     value: self.recurse(property.value)
21554          });
21555        }
21556      });
21557      return function(scope, locals, assign, inputs) {
21558        var value = {};
21559        for (var i = 0; i < args.length; ++i) {
21560          if (args[i].computed) {
21561            value[args[i].key(scope, locals, assign, inputs)] = args[i].value(scope, locals, assign, inputs);
21562          } else {
21563            value[args[i].key] = args[i].value(scope, locals, assign, inputs);
21564          }
21565        }
21566        return context ? {value: value} : value;
21567      };
21568    case AST.ThisExpression:
21569      return function(scope) {
21570        return context ? {value: scope} : scope;
21571      };
21572    case AST.LocalsExpression:
21573      return function(scope, locals) {
21574        return context ? {value: locals} : locals;
21575      };
21576    case AST.NGValueParameter:
21577      return function(scope, locals, assign) {
21578        return context ? {value: assign} : assign;
21579      };
21580    }
21581  },
21582
21583  'unary+': function(argument, context) {
21584    return function(scope, locals, assign, inputs) {
21585      var arg = argument(scope, locals, assign, inputs);
21586      if (isDefined(arg)) {
21587        arg = +arg;
21588      } else {
21589        arg = 0;
21590      }
21591      return context ? {value: arg} : arg;
21592    };
21593  },
21594  'unary-': function(argument, context) {
21595    return function(scope, locals, assign, inputs) {
21596      var arg = argument(scope, locals, assign, inputs);
21597      if (isDefined(arg)) {
21598        arg = -arg;
21599      } else {
21600        arg = 0;
21601      }
21602      return context ? {value: arg} : arg;
21603    };
21604  },
21605  'unary!': function(argument, context) {
21606    return function(scope, locals, assign, inputs) {
21607      var arg = !argument(scope, locals, assign, inputs);
21608      return context ? {value: arg} : arg;
21609    };
21610  },
21611  'binary+': function(left, right, context) {
21612    return function(scope, locals, assign, inputs) {
21613      var lhs = left(scope, locals, assign, inputs);
21614      var rhs = right(scope, locals, assign, inputs);
21615      var arg = plusFn(lhs, rhs);
21616      return context ? {value: arg} : arg;
21617    };
21618  },
21619  'binary-': function(left, right, context) {
21620    return function(scope, locals, assign, inputs) {
21621      var lhs = left(scope, locals, assign, inputs);
21622      var rhs = right(scope, locals, assign, inputs);
21623      var arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0);
21624      return context ? {value: arg} : arg;
21625    };
21626  },
21627  'binary*': function(left, right, context) {
21628    return function(scope, locals, assign, inputs) {
21629      var arg = left(scope, locals, assign, inputs) * right(scope, locals, assign, inputs);
21630      return context ? {value: arg} : arg;
21631    };
21632  },
21633  'binary/': function(left, right, context) {
21634    return function(scope, locals, assign, inputs) {
21635      var arg = left(scope, locals, assign, inputs) / right(scope, locals, assign, inputs);
21636      return context ? {value: arg} : arg;
21637    };
21638  },
21639  'binary%': function(left, right, context) {
21640    return function(scope, locals, assign, inputs) {
21641      var arg = left(scope, locals, assign, inputs) % right(scope, locals, assign, inputs);
21642      return context ? {value: arg} : arg;
21643    };
21644  },
21645  'binary===': function(left, right, context) {
21646    return function(scope, locals, assign, inputs) {
21647      var arg = left(scope, locals, assign, inputs) === right(scope, locals, assign, inputs);
21648      return context ? {value: arg} : arg;
21649    };
21650  },
21651  'binary!==': function(left, right, context) {
21652    return function(scope, locals, assign, inputs) {
21653      var arg = left(scope, locals, assign, inputs) !== right(scope, locals, assign, inputs);
21654      return context ? {value: arg} : arg;
21655    };
21656  },
21657  'binary==': function(left, right, context) {
21658    return function(scope, locals, assign, inputs) {
21659      // eslint-disable-next-line eqeqeq
21660      var arg = left(scope, locals, assign, inputs) == right(scope, locals, assign, inputs);
21661      return context ? {value: arg} : arg;
21662    };
21663  },
21664  'binary!=': function(left, right, context) {
21665    return function(scope, locals, assign, inputs) {
21666      // eslint-disable-next-line eqeqeq
21667      var arg = left(scope, locals, assign, inputs) != right(scope, locals, assign, inputs);
21668      return context ? {value: arg} : arg;
21669    };
21670  },
21671  'binary<': function(left, right, context) {
21672    return function(scope, locals, assign, inputs) {
21673      var arg = left(scope, locals, assign, inputs) < right(scope, locals, assign, inputs);
21674      return context ? {value: arg} : arg;
21675    };
21676  },
21677  'binary>': function(left, right, context) {
21678    return function(scope, locals, assign, inputs) {
21679      var arg = left(scope, locals, assign, inputs) > right(scope, locals, assign, inputs);
21680      return context ? {value: arg} : arg;
21681    };
21682  },
21683  'binary<=': function(left, right, context) {
21684    return function(scope, locals, assign, inputs) {
21685      var arg = left(scope, locals, assign, inputs) <= right(scope, locals, assign, inputs);
21686      return context ? {value: arg} : arg;
21687    };
21688  },
21689  'binary>=': function(left, right, context) {
21690    return function(scope, locals, assign, inputs) {
21691      var arg = left(scope, locals, assign, inputs) >= right(scope, locals, assign, inputs);
21692      return context ? {value: arg} : arg;
21693    };
21694  },
21695  'binary&&': function(left, right, context) {
21696    return function(scope, locals, assign, inputs) {
21697      var arg = left(scope, locals, assign, inputs) && right(scope, locals, assign, inputs);
21698      return context ? {value: arg} : arg;
21699    };
21700  },
21701  'binary||': function(left, right, context) {
21702    return function(scope, locals, assign, inputs) {
21703      var arg = left(scope, locals, assign, inputs) || right(scope, locals, assign, inputs);
21704      return context ? {value: arg} : arg;
21705    };
21706  },
21707  'ternary?:': function(test, alternate, consequent, context) {
21708    return function(scope, locals, assign, inputs) {
21709      var arg = test(scope, locals, assign, inputs) ? alternate(scope, locals, assign, inputs) : consequent(scope, locals, assign, inputs);
21710      return context ? {value: arg} : arg;
21711    };
21712  },
21713  value: function(value, context) {
21714    return function() { return context ? {context: undefined, name: undefined, value: value} : value; };
21715  },
21716  identifier: function(name, expensiveChecks, context, create, expression) {
21717    return function(scope, locals, assign, inputs) {
21718      var base = locals && (name in locals) ? locals : scope;
21719      if (create && create !== 1 && base && !(base[name])) {
21720        base[name] = {};
21721      }
21722      var value = base ? base[name] : undefined;
21723      if (expensiveChecks) {
21724        ensureSafeObject(value, expression);
21725      }
21726      if (context) {
21727        return {context: base, name: name, value: value};
21728      } else {
21729        return value;
21730      }
21731    };
21732  },
21733  computedMember: function(left, right, context, create, expression) {
21734    return function(scope, locals, assign, inputs) {
21735      var lhs = left(scope, locals, assign, inputs);
21736      var rhs;
21737      var value;
21738      if (lhs != null) {
21739        rhs = right(scope, locals, assign, inputs);
21740        rhs = getStringValue(rhs);
21741        ensureSafeMemberName(rhs, expression);
21742        if (create && create !== 1) {
21743          ensureSafeAssignContext(lhs);
21744          if (lhs && !(lhs[rhs])) {
21745            lhs[rhs] = {};
21746          }
21747        }
21748        value = lhs[rhs];
21749        ensureSafeObject(value, expression);
21750      }
21751      if (context) {
21752        return {context: lhs, name: rhs, value: value};
21753      } else {
21754        return value;
21755      }
21756    };
21757  },
21758  nonComputedMember: function(left, right, expensiveChecks, context, create, expression) {
21759    return function(scope, locals, assign, inputs) {
21760      var lhs = left(scope, locals, assign, inputs);
21761      if (create && create !== 1) {
21762        ensureSafeAssignContext(lhs);
21763        if (lhs && !(lhs[right])) {
21764          lhs[right] = {};
21765        }
21766      }
21767      var value = lhs != null ? lhs[right] : undefined;
21768      if (expensiveChecks || isPossiblyDangerousMemberName(right)) {
21769        ensureSafeObject(value, expression);
21770      }
21771      if (context) {
21772        return {context: lhs, name: right, value: value};
21773      } else {
21774        return value;
21775      }
21776    };
21777  },
21778  inputs: function(input, watchId) {
21779    return function(scope, value, locals, inputs) {
21780      if (inputs) return inputs[watchId];
21781      return input(scope, value, locals);
21782    };
21783  }
21784};
21785
21786/**
21787 * @constructor
21788 */
21789var Parser = function Parser(lexer, $filter, options) {
21790  this.lexer = lexer;
21791  this.$filter = $filter;
21792  this.options = options;
21793  this.ast = new AST(lexer, options);
21794  this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) :
21795                                   new ASTCompiler(this.ast, $filter);
21796};
21797
21798Parser.prototype = {
21799  constructor: Parser,
21800
21801  parse: function(text) {
21802    return this.astCompiler.compile(text, this.options.expensiveChecks);
21803  }
21804};
21805
21806function isPossiblyDangerousMemberName(name) {
21807  return name === 'constructor';
21808}
21809
21810function getValueOf(value) {
21811  return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value);
21812}
21813
21814///////////////////////////////////
21815
21816/**
21817 * @ngdoc service
21818 * @name $parse
21819 * @kind function
21820 *
21821 * @description
21822 *
21823 * Converts Angular {@link guide/expression expression} into a function.
21824 *
21825 * ```js
21826 *   var getter = $parse('user.name');
21827 *   var setter = getter.assign;
21828 *   var context = {user:{name:'angular'}};
21829 *   var locals = {user:{name:'local'}};
21830 *
21831 *   expect(getter(context)).toEqual('angular');
21832 *   setter(context, 'newValue');
21833 *   expect(context.user.name).toEqual('newValue');
21834 *   expect(getter(context, locals)).toEqual('local');
21835 * ```
21836 *
21837 *
21838 * @param {string} expression String expression to compile.
21839 * @returns {function(context, locals)} a function which represents the compiled expression:
21840 *
21841 *    * `context` – `{object}` – an object against which any expressions embedded in the strings
21842 *      are evaluated against (typically a scope object).
21843 *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
21844 *      `context`.
21845 *
21846 *    The returned function also has the following properties:
21847 *      * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
21848 *        literal.
21849 *      * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
21850 *        constant literals.
21851 *      * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
21852 *        set to a function to change its value on the given context.
21853 *
21854 */
21855
21856
21857/**
21858 * @ngdoc provider
21859 * @name $parseProvider
21860 * @this
21861 *
21862 * @description
21863 * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
21864 *  service.
21865 */
21866function $ParseProvider() {
21867  var cacheDefault = createMap();
21868  var cacheExpensive = createMap();
21869  var literals = {
21870    'true': true,
21871    'false': false,
21872    'null': null,
21873    'undefined': undefined
21874  };
21875  var identStart, identContinue;
21876
21877  /**
21878   * @ngdoc method
21879   * @name $parseProvider#addLiteral
21880   * @description
21881   *
21882   * Configure $parse service to add literal values that will be present as literal at expressions.
21883   *
21884   * @param {string} literalName Token for the literal value. The literal name value must be a valid literal name.
21885   * @param {*} literalValue Value for this literal. All literal values must be primitives or `undefined`.
21886   *
21887   **/
21888  this.addLiteral = function(literalName, literalValue) {
21889    literals[literalName] = literalValue;
21890  };
21891
21892 /**
21893  * @ngdoc method
21894  * @name $parseProvider#setIdentifierFns
21895  *
21896  * @description
21897  *
21898  * Allows defining the set of characters that are allowed in Angular expressions. The function
21899  * `identifierStart` will get called to know if a given character is a valid character to be the
21900  * first character for an identifier. The function `identifierContinue` will get called to know if
21901  * a given character is a valid character to be a follow-up identifier character. The functions
21902  * `identifierStart` and `identifierContinue` will receive as arguments the single character to be
21903  * identifier and the character code point. These arguments will be `string` and `numeric`. Keep in
21904  * mind that the `string` parameter can be two characters long depending on the character
21905  * representation. It is expected for the function to return `true` or `false`, whether that
21906  * character is allowed or not.
21907  *
21908  * Since this function will be called extensively, keep the implementation of these functions fast,
21909  * as the performance of these functions have a direct impact on the expressions parsing speed.
21910  *
21911  * @param {function=} identifierStart The function that will decide whether the given character is
21912  *   a valid identifier start character.
21913  * @param {function=} identifierContinue The function that will decide whether the given character is
21914  *   a valid identifier continue character.
21915  */
21916  this.setIdentifierFns = function(identifierStart, identifierContinue) {
21917    identStart = identifierStart;
21918    identContinue = identifierContinue;
21919    return this;
21920  };
21921
21922  this.$get = ['$filter', function($filter) {
21923    var noUnsafeEval = csp().noUnsafeEval;
21924    var $parseOptions = {
21925          csp: noUnsafeEval,
21926          expensiveChecks: false,
21927          literals: copy(literals),
21928          isIdentifierStart: isFunction(identStart) && identStart,
21929          isIdentifierContinue: isFunction(identContinue) && identContinue
21930        },
21931        $parseOptionsExpensive = {
21932          csp: noUnsafeEval,
21933          expensiveChecks: true,
21934          literals: copy(literals),
21935          isIdentifierStart: isFunction(identStart) && identStart,
21936          isIdentifierContinue: isFunction(identContinue) && identContinue
21937        };
21938    var runningChecksEnabled = false;
21939
21940    $parse.$$runningExpensiveChecks = function() {
21941      return runningChecksEnabled;
21942    };
21943
21944    return $parse;
21945
21946    function $parse(exp, interceptorFn, expensiveChecks) {
21947      var parsedExpression, oneTime, cacheKey;
21948
21949      expensiveChecks = expensiveChecks || runningChecksEnabled;
21950
21951      switch (typeof exp) {
21952        case 'string':
21953          exp = exp.trim();
21954          cacheKey = exp;
21955
21956          var cache = (expensiveChecks ? cacheExpensive : cacheDefault);
21957          parsedExpression = cache[cacheKey];
21958
21959          if (!parsedExpression) {
21960            if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
21961              oneTime = true;
21962              exp = exp.substring(2);
21963            }
21964            var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions;
21965            var lexer = new Lexer(parseOptions);
21966            var parser = new Parser(lexer, $filter, parseOptions);
21967            parsedExpression = parser.parse(exp);
21968            if (parsedExpression.constant) {
21969              parsedExpression.$$watchDelegate = constantWatchDelegate;
21970            } else if (oneTime) {
21971              parsedExpression.$$watchDelegate = parsedExpression.literal ?
21972                  oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
21973            } else if (parsedExpression.inputs) {
21974              parsedExpression.$$watchDelegate = inputsWatchDelegate;
21975            }
21976            if (expensiveChecks) {
21977              parsedExpression = expensiveChecksInterceptor(parsedExpression);
21978            }
21979            cache[cacheKey] = parsedExpression;
21980          }
21981          return addInterceptor(parsedExpression, interceptorFn);
21982
21983        case 'function':
21984          return addInterceptor(exp, interceptorFn);
21985
21986        default:
21987          return addInterceptor(noop, interceptorFn);
21988      }
21989    }
21990
21991    function expensiveChecksInterceptor(fn) {
21992      if (!fn) return fn;
21993      expensiveCheckFn.$$watchDelegate = fn.$$watchDelegate;
21994      expensiveCheckFn.assign = expensiveChecksInterceptor(fn.assign);
21995      expensiveCheckFn.constant = fn.constant;
21996      expensiveCheckFn.literal = fn.literal;
21997      for (var i = 0; fn.inputs && i < fn.inputs.length; ++i) {
21998        fn.inputs[i] = expensiveChecksInterceptor(fn.inputs[i]);
21999      }
22000      expensiveCheckFn.inputs = fn.inputs;
22001
22002      return expensiveCheckFn;
22003
22004      function expensiveCheckFn(scope, locals, assign, inputs) {
22005        var expensiveCheckOldValue = runningChecksEnabled;
22006        runningChecksEnabled = true;
22007        try {
22008          return fn(scope, locals, assign, inputs);
22009        } finally {
22010          runningChecksEnabled = expensiveCheckOldValue;
22011        }
22012      }
22013    }
22014
22015    function expressionInputDirtyCheck(newValue, oldValueOfValue) {
22016
22017      if (newValue == null || oldValueOfValue == null) { // null/undefined
22018        return newValue === oldValueOfValue;
22019      }
22020
22021      if (typeof newValue === 'object') {
22022
22023        // attempt to convert the value to a primitive type
22024        // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
22025        //             be cheaply dirty-checked
22026        newValue = getValueOf(newValue);
22027
22028        if (typeof newValue === 'object') {
22029          // objects/arrays are not supported - deep-watching them would be too expensive
22030          return false;
22031        }
22032
22033        // fall-through to the primitive equality check
22034      }
22035
22036      //Primitive or NaN
22037      // eslint-disable-next-line no-self-compare
22038      return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue);
22039    }
22040
22041    function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) {
22042      var inputExpressions = parsedExpression.inputs;
22043      var lastResult;
22044
22045      if (inputExpressions.length === 1) {
22046        var oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails
22047        inputExpressions = inputExpressions[0];
22048        return scope.$watch(function expressionInputWatch(scope) {
22049          var newInputValue = inputExpressions(scope);
22050          if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf)) {
22051            lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]);
22052            oldInputValueOf = newInputValue && getValueOf(newInputValue);
22053          }
22054          return lastResult;
22055        }, listener, objectEquality, prettyPrintExpression);
22056      }
22057
22058      var oldInputValueOfValues = [];
22059      var oldInputValues = [];
22060      for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
22061        oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails
22062        oldInputValues[i] = null;
22063      }
22064
22065      return scope.$watch(function expressionInputsWatch(scope) {
22066        var changed = false;
22067
22068        for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
22069          var newInputValue = inputExpressions[i](scope);
22070          if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) {
22071            oldInputValues[i] = newInputValue;
22072            oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
22073          }
22074        }
22075
22076        if (changed) {
22077          lastResult = parsedExpression(scope, undefined, undefined, oldInputValues);
22078        }
22079
22080        return lastResult;
22081      }, listener, objectEquality, prettyPrintExpression);
22082    }
22083
22084    function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression) {
22085      var unwatch, lastValue;
22086      unwatch = scope.$watch(function oneTimeWatch(scope) {
22087        return parsedExpression(scope);
22088      }, /** @this */ function oneTimeListener(value, old, scope) {
22089        lastValue = value;
22090        if (isFunction(listener)) {
22091          listener.apply(this, arguments);
22092        }
22093        if (isDefined(value)) {
22094          scope.$$postDigest(function() {
22095            if (isDefined(lastValue)) {
22096              unwatch();
22097            }
22098          });
22099        }
22100      }, objectEquality);
22101      return unwatch;
22102    }
22103
22104    function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) {
22105      var unwatch, lastValue;
22106      unwatch = scope.$watch(function oneTimeWatch(scope) {
22107        return parsedExpression(scope);
22108      }, /** @this */ function oneTimeListener(value, old, scope) {
22109        lastValue = value;
22110        if (isFunction(listener)) {
22111          listener.call(this, value, old, scope);
22112        }
22113        if (isAllDefined(value)) {
22114          scope.$$postDigest(function() {
22115            if (isAllDefined(lastValue)) unwatch();
22116          });
22117        }
22118      }, objectEquality);
22119
22120      return unwatch;
22121
22122      function isAllDefined(value) {
22123        var allDefined = true;
22124        forEach(value, function(val) {
22125          if (!isDefined(val)) allDefined = false;
22126        });
22127        return allDefined;
22128      }
22129    }
22130
22131    function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
22132      var unwatch = scope.$watch(function constantWatch(scope) {
22133        unwatch();
22134        return parsedExpression(scope);
22135      }, listener, objectEquality);
22136      return unwatch;
22137    }
22138
22139    function addInterceptor(parsedExpression, interceptorFn) {
22140      if (!interceptorFn) return parsedExpression;
22141      var watchDelegate = parsedExpression.$$watchDelegate;
22142      var useInputs = false;
22143
22144      var regularWatch =
22145          watchDelegate !== oneTimeLiteralWatchDelegate &&
22146          watchDelegate !== oneTimeWatchDelegate;
22147
22148      var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) {
22149        var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs);
22150        return interceptorFn(value, scope, locals);
22151      } : function oneTimeInterceptedExpression(scope, locals, assign, inputs) {
22152        var value = parsedExpression(scope, locals, assign, inputs);
22153        var result = interceptorFn(value, scope, locals);
22154        // we only return the interceptor's result if the
22155        // initial value is defined (for bind-once)
22156        return isDefined(value) ? result : value;
22157      };
22158
22159      // Propagate $$watchDelegates other then inputsWatchDelegate
22160      if (parsedExpression.$$watchDelegate &&
22161          parsedExpression.$$watchDelegate !== inputsWatchDelegate) {
22162        fn.$$watchDelegate = parsedExpression.$$watchDelegate;
22163      } else if (!interceptorFn.$stateful) {
22164        // If there is an interceptor, but no watchDelegate then treat the interceptor like
22165        // we treat filters - it is assumed to be a pure function unless flagged with $stateful
22166        fn.$$watchDelegate = inputsWatchDelegate;
22167        useInputs = !parsedExpression.inputs;
22168        fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression];
22169      }
22170
22171      return fn;
22172    }
22173  }];
22174}
22175
22176/**
22177 * @ngdoc service
22178 * @name $q
22179 * @requires $rootScope
22180 * @this
22181 *
22182 * @description
22183 * A service that helps you run functions asynchronously, and use their return values (or exceptions)
22184 * when they are done processing.
22185 *
22186 * This is an implementation of promises/deferred objects inspired by
22187 * [Kris Kowal's Q](https://github.com/kriskowal/q).
22188 *
22189 * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
22190 * implementations, and the other which resembles ES6 (ES2015) promises to 
22190some degree.
22191 *
22192 * # $q constructor
22193 *
22194 * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
22195 * function as the first argument. This is similar to the native Promise implementation from ES6,
22196 * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
22197 *
22198 * While the constructor-style use is supported, not all of the supporting methods from ES6 promises are
22199 * available yet.
22200 *
22201 * It can be used like so:
22202 *
22203 * ```js
22204 *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
22205 *   // are available in the current lexical scope (they could have been injected or passed in).
22206 *
22207 *   function asyncGreet(name) {
22208 *     // perform some asynchronous operation, resolve or reject the promise when appropriate.
22209 *     return $q(function(resolve, reject) {
22210 *       setTimeout(function() {
22211 *         if (okToGreet(name)) {
22212 *           resolve('Hello, ' + name + '!');
22213 *         } else {
22214 *           reject('Greeting ' + name + ' is not allowed.');
22215 *         }
22216 *       }, 1000);
22217 *     });
22218 *   }
22219 *
22220 *   var promise = asyncGreet('Robin Hood');
22221 *   promise.then(function(greeting) {
22222 *     alert('Success: ' + greeting);
22223 *   }, function(reason) {
22224 *     alert('Failed: ' + reason);
22225 *   });
22226 * ```
22227 *
22228 * Note: progress/notify callbacks are not currently supported via the ES6-style interface.
22229 *
22230 * Note: unlike ES6 behavior, an exception thrown in the constructor function will NOT implicitly reject the promise.
22231 *
22232 * However, the more traditional CommonJS-style usage is still available, and documented below.
22233 *
22234 * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
22235 * interface for interacting with an object that represents the result of an action that is
22236 * performed asynchronously, and may or may not be finished at any given point in time.
22237 *
22238 * From the perspective of dealing with error handling, deferred and promise APIs are to
22239 * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
22240 *
22241 * ```js
22242 *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
22243 *   // are available in the current lexical scope (they could have been injected or passed in).
22244 *
22245 *   function asyncGreet(name) {
22246 *     var deferred = $q.defer();
22247 *
22248 *     setTimeout(function() {
22249 *       deferred.notify('About to greet ' + name + '.');
22250 *
22251 *       if (okToGreet(name)) {
22252 *         deferred.resolve('Hello, ' + name + '!');
22253 *       } else {
22254 *         deferred.reject('Greeting ' + name + ' is not allowed.');
22255 *       }
22256 *     }, 1000);
22257 *
22258 *     return deferred.promise;
22259 *   }
22260 *
22261 *   var promise = asyncGreet('Robin Hood');
22262 *   promise.then(function(greeting) {
22263 *     alert('Success: ' + greeting);
22264 *   }, function(reason) {
22265 *     alert('Failed: ' + reason);
22266 *   }, function(update) {
22267 *     alert('Got notification: ' + update);
22268 *   });
22269 * ```
22270 *
22271 * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
22272 * comes in the way of guarantees that promise and deferred APIs make, see
22273 * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md.
22274 *
22275 * Additionally the promise api allows for composition that is very hard to do with the
22276 * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
22277 * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
22278 * section on serial or parallel joining of promises.
22279 *
22280 * # The Deferred API
22281 *
22282 * A new instance of deferred is constructed by calling `$q.defer()`.
22283 *
22284 * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
22285 * that can be used for signaling the successful or unsuccessful completion, as well as the status
22286 * of the task.
22287 *
22288 * **Methods**
22289 *
22290 * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection
22291 *   constructed via `$q.reject`, the promise will be rejected instead.
22292 * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
22293 *   resolving it with a rejection constructed via `$q.reject`.
22294 * - `notify(value)` - provides updates on the status of the promise's execution. This may be called
22295 *   multiple times before the promise is either resolved or rejected.
22296 *
22297 * **Properties**
22298 *
22299 * - promise – `{Promise}` – promise object associated with this deferred.
22300 *
22301 *
22302 * # The Promise API
22303 *
22304 * A new promise instance is created when a deferred instance is created and can be retrieved by
22305 * calling `deferred.promise`.
22306 *
22307 * The purpose of the promise object is to allow for interested parties to get access to the result
22308 * of the deferred task when it completes.
22309 *
22310 * **Methods**
22311 *
22312 * - `then(successCallback, [errorCallback], [notifyCallback])` – regardless of when the promise was or
22313 *   will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
22314 *   as soon as the result is available. The callbacks are called with a single argument: the result
22315 *   or rejection reason. Additionally, the notify callback may be called zero or more times to
22316 *   provide a progress indication, before the promise is resolved or rejected.
22317 *
22318 *   This method *returns a new promise* which is resolved or rejected via the return value of the
22319 *   `successCallback`, `errorCallback` (unless that value is a promise, in which case it is resolved
22320 *   with the value which is resolved in that promise using
22321 *   [promise chaining](http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promises-queues)).
22322 *   It also notifies via the return value of the `notifyCallback` method. The promise cannot be
22323 *   resolved or rejected from the notifyCallback method. The errorCallback and notifyCallback
22324 *   arguments are optional.
22325 *
22326 * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
22327 *
22328 * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise,
22329 *   but to do so without modifying the final value. This is useful to release resources or do some
22330 *   clean-up that needs to be done whether the promise was rejected or resolved. See the [full
22331 *   specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
22332 *   more information.
22333 *
22334 * # Chaining promises
22335 *
22336 * Because calling the `then` method of a promise returns a new derived promise, it is easily
22337 * possible to create a chain of promises:
22338 *
22339 * ```js
22340 *   promiseB = promiseA.then(function(result) {
22341 *     return result + 1;
22342 *   });
22343 *
22344 *   // promiseB will be resolved immediately after promiseA is resolved and its value
22345 *   // will be the result of promiseA incremented by 1
22346 * ```
22347 *
22348 * It is possible to create chains of any length and since a promise can be resolved with another
22349 * promise (which will defer its resolution further), it is possible to pause/defer resolution of
22350 * the promises at any point in the chain. This makes it possible to implement powerful APIs like
22351 * $http's response interceptors.
22352 *
22353 *
22354 * # Differences between Kris Kowal's Q and $q
22355 *
22356 *  There are two main differences:
22357 *
22358 * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
22359 *   mechanism in angular, which means faster propagation of resolution or rejection into your
22360 *   models and avoiding unnecessary browser repaints, which would result in flickering UI.
22361 * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
22362 *   all the important functionality needed for common async tasks.
22363 *
22364 * # Testing
22365 *
22366 *  ```js
22367 *    it('should simulate promise', inject(function($q, $rootScope) {
22368 *      var deferred = $q.defer();
22369 *      var promise = deferred.promise;
22370 *      var resolvedValue;
22371 *
22372 *      promise.then(function(value) { resolvedValue = value; });
22373 *      expect(resolvedValue).toBeUndefined();
22374 *
22375 *      // Simulate resolving of promise
22376 *      deferred.resolve(123);
22377 *      // Note that the 'then' function does not get called synchronously.
22378 *      // This is because we want the promise API to always be async, whether or not
22379 *      // it got called synchronously or asynchronously.
22380 *      expect(resolvedValue).toBeUndefined();
22381 *
22382 *      // Propagate promise resolution to 'then' functions using $apply().
22383 *      $rootScope.$apply();
22384 *      expect(resolvedValue).toEqual(123);
22385 *    }));
22386 *  ```
22387 *
22388 * @param {function(function, function)} resolver Function which is responsible for resolving or
22389 *   rejecting the newly created promise. The first parameter is a function which resolves the
22390 *   promise, the second parameter is a function which rejects the promise.
22391 *
22392 * @returns {Promise} The newly created promise.
22393 */
22394function $QProvider() {
22395
22396  this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
22397    return qFactory(function(callback) {
22398      $rootScope.$evalAsync(callback);
22399    }, $exceptionHandler);
22400  }];
22401}
22402
22403/** @this */
22404function $$QProvider() {
22405  this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) {
22406    return qFactory(function(callback) {
22407      $browser.defer(callback);
22408    }, $exceptionHandler);
22409  }];
22410}
22411
22412/**
22413 * Constructs a promise manager.
22414 *
22415 * @param {function(function)} nextTick Function for executing functions in the next turn.
22416 * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
22417 *     debugging purposes.
22418 * @returns {object} Promise manager.
22419 */
22420function qFactory(nextTick, exceptionHandler) {
22421  var $qMinErr = minErr('$q', TypeError);
22422
22423  /**
22424   * @ngdoc method
22425   * @name ng.$q#defer
22426   * @kind function
22427   *
22428   * @description
22429   * Creates a `Deferred` object which represents a task which will finish in the future.
22430   *
22431   * @returns {Deferred} Returns a new instance of deferred.
22432   */
22433  function defer() {
22434    var d = new Deferred();
22435    //Necessary to support unbound execution :/
22436    d.resolve = simpleBind(d, d.resolve);
22437    d.reject = simpleBind(d, d.reject);
22438    d.notify = simpleBind(d, d.notify);
22439    return d;
22440  }
22441
22442  function Promise() {
22443    this.$$state = { status: 0 };
22444  }
22445
22446  extend(Promise.prototype, {
22447    then: function(onFulfilled, onRejected, progressBack) {
22448      if (isUndefined(onFulfilled) && isUndefined(onRejected) && isUndefined(progressBack)) {
22449        return this;
22450      }
22451      var result = new Deferred();
22452
22453      this.$$state.pending = this.$$state.pending || [];
22454      this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
22455      if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);
22456
22457      return result.promise;
22458    },
22459
22460    'catch': function(callback) {
22461      return this.then(null, callback);
22462    },
22463
22464    'finally': function(callback, progressBack) {
22465      return this.then(function(value) {
22466        return handleCallback(value, resolve, callback);
22467      }, function(error) {
22468        return handleCallback(error, reject, callback);
22469      }, progressBack);
22470    }
22471  });
22472
22473  //Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native
22474  function simpleBind(context, fn) {
22475    return function(value) {
22476      fn.call(context, value);
22477    };
22478  }
22479
22480  function processQueue(state) {
22481    var fn, deferred, pending;
22482
22483    pending = state.pending;
22484    state.processScheduled = false;
22485    state.pending = undefined;
22486    for (var i = 0, ii = pending.length; i < ii; ++i) {
22487      deferred = pending[i][0];
22488      fn = pending[i][state.status];
22489      try {
22490        if (isFunction(fn)) {
22491          deferred.resolve(fn(state.value));
22492        } else if (state.status === 1) {
22493          deferred.resolve(state.value);
22494        } else {
22495          deferred.reject(state.value);
22496        }
22497      } catch (e) {
22498        deferred.reject(e);
22499        exceptionHandler(e);
22500      }
22501    }
22502  }
22503
22504  function scheduleProcessQueue(state) {
22505    if (state.processScheduled || !state.pending) return;
22506    state.processScheduled = true;
22507    nextTick(function() { processQueue(state); });
22508  }
22509
22510  function Deferred() {
22511    this.promise = new Promise();
22512  }
22513
22514  extend(Deferred.prototype, {
22515    resolve: function(val) {
22516      if (this.promise.$$state.status) return;
22517      if (val === this.promise) {
22518        this.$$reject($qMinErr(
22519          'qcycle',
22520          'Expected promise to be resolved with value other than 
22520itself \'{0}\'',
22521          val));
22522      } else {
22523        this.$$resolve(val);
22524      }
22525
22526    },
22527
22528    $$resolve: function(val) {
22529      var then;
22530      var that = this;
22531      var done = false;
22532      try {
22533        if ((isObject(val) || isFunction(val))) then = val && val.then;
22534        if (isFunction(then)) {
22535          this.promise.$$state.status = -1;
22536          then.call(val, resolvePromise, rejectPromise, simpleBind(this, this.notify));
22537        } else {
22538          this.promise.$$state.value = val;
22539          this.promise.$$state.status = 1;
22540          scheduleProcessQueue(this.promise.$$state);
22541        }
22542      } catch (e) {
22543        rejectPromise(e);
22544        exceptionHandler(e);
22545      }
22546
22547      function resolvePromise(val) {
22548        if (done) return;
22549        done = true;
22550        that.$$resolve(val);
22551      }
22552      function rejectPromise(val) {
22553        if (done) return;
22554        done = true;
22555        that.$$reject(val);
22556      }
22557    },
22558
22559    reject: function(reason) {
22560      if (this.promise.$$state.status) return;
22561      this.$$reject(reason);
22562    },
22563
22564    $$reject: function(reason) {
22565      this.promise.$$state.value = reason;
22566      this.promise.$$state.status = 2;
22567      scheduleProcessQueue(this.promise.$$state);
22568    },
22569
22570    notify: function(progress) {
22571      var callbacks = this.promise.$$state.pending;
22572
22573      if ((this.promise.$$state.status <= 0) && callbacks && callbacks.length) {
22574        nextTick(function() {
22575          var callback, result;
22576          for (var i = 0, ii = callbacks.length; i < ii; i++) {
22577            result = callbacks[i][0];
22578            callback = callbacks[i][3];
22579            try {
22580              result.notify(isFunction(callback) ? callback(progress) : progress);
22581            } catch (e) {
22582              exceptionHandler(e);
22583            }
22584          }
22585        });
22586      }
22587    }
22588  });
22589
22590  /**
22591   * @ngdoc method
22592   * @name $q#reject
22593   * @kind function
22594   *
22595   * @description
22596   * Creates a promise that is resolved as rejected with the specified `reason`. This api should be
22597   * used to forward rejection in a chain of promises. If you are dealing with the last promise in
22598   * a promise chain, you don't need to worry about it.
22599   *
22600   * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
22601   * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
22602   * a promise error callback and you want to forward the error to the promise derived from the
22603   * current promise, you have to "rethrow" the error by returning a rejection constructed via
22604   * `reject`.
22605   *
22606   * ```js
22607   *   promiseB = promiseA.then(function(result) {
22608   *     // success: do something and resolve promiseB
22609   *     //          with the old or a new result
22610   *     return result;
22611   *   }, function(reason) {
22612   *     // error: handle the error if possible and
22613   *     //        resolve promiseB with newPromiseOrValue,
22614   *     //        otherwise forward the rejection to promiseB
22615   *     if (canHandle(reason)) {
22616   *      // handle the error and recover
22617   *      return newPromiseOrValue;
22618   *     }
22619   *     return $q.reject(reason);
22620   *   });
22621   * ```
22622   *
22623   * @param {*} reason Constant, message, exception or an object representing the rejection reason.
22624   * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
22625   */
22626  function reject(reason) {
22627    var result = new Deferred();
22628    result.reject(reason);
22629    return result.promise;
22630  }
22631
22632  function handleCallback(value, resolver, callback) {
22633    var callbackOutput = null;
22634    try {
22635      if (isFunction(callback)) callbackOutput = callback();
22636    } catch (e) {
22637      return reject(e);
22638    }
22639    if (isPromiseLike(callbackOutput)) {
22640      return callbackOutput.then(function() {
22641        return resolver(value);
22642      }, reject);
22643    } else {
22644      return resolver(value);
22645    }
22646  }
22647
22648  /**
22649   * @ngdoc method
22650   * @name $q#when
22651   * @kind function
22652   *
22653   * @description
22654   * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
22655   * This is useful when you are dealing with an object that might or might not be a promise, or if
22656   * the promise comes from a source that can't be trusted.
22657   *
22658   * @param {*} value Value or a promise
22659   * @param {Function=} successCallback
22660   * @param {Function=} errorCallback
22661   * @param {Function=} progressCallback
22662   * @returns {Promise} Returns a promise of the passed value or promise
22663   */
22664
22665
22666  function when(value, callback, errback, progressBack) {
22667    var result = new Deferred();
22668    result.resolve(value);
22669    return result.promise.then(callback, errback, progressBack);
22670  }
22671
22672  /**
22673   * @ngdoc method
22674   * @name $q#resolve
22675   * @kind function
22676   *
22677   * @description
22678   * Alias of {@link ng.$q#when when} to maintain naming consistency with ES6.
22679   *
22680   * @param {*} value Value or a promise
22681   * @param {Function=} successCallback
22682   * @param {Function=} errorCallback
22683   * @param {Function=} progressCallback
22684   * @returns {Promise} Returns a promise of the passed value or promise
22685   */
22686  var resolve = when;
22687
22688  /**
22689   * @ngdoc method
22690   * @name $q#all
22691   * @kind function
22692   *
22693   * @description
22694   * Combines multiple promises into a single promise that is resolved when all of the input
22695   * promises are resolved.
22696   *
22697   * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
22698   * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
22699   *   each value corresponding to the promise at the same index/key in the `promises` array/hash.
22700   *   If any of the promises is resolved with a rejection, this resulting promise will be rejected
22701   *   with the same rejection value.
22702   */
22703
22704  function all(promises) {
22705    var deferred = new Deferred(),
22706        counter = 0,
22707        results = isArray(promises) ? [] : {};
22708
22709    forEach(promises, function(promise, key) {
22710      counter++;
22711      when(promise).then(function(value) {
22712        results[key] = value;
22713        if (!(--counter)) deferred.resolve(results);
22714      }, function(reason) {
22715        deferred.reject(reason);
22716      });
22717    });
22718
22719    if (counter === 0) {
22720      deferred.resolve(results);
22721    }
22722
22723    return deferred.promise;
22724  }
22725
22726  /**
22727   * @ngdoc method
22728   * @name $q#race
22729   * @kind function
22730   *
22731   * @description
22732   * Returns a promise that resolves or rejects as soon as one of those promises
22733   * resolves or rejects, with the value or reason from that promise.
22734   *
22735   * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
22736   * @returns {Promise} a promise that resolves or rejects as soon as one of the `promises`
22737   * resolves or rejects, with the value or reason from that promise.
22738   */
22739
22740  function race(promises) {
22741    var deferred = defer();
22742
22743    forEach(promises, function(promise) {
22744      when(promise).then(deferred.resolve, deferred.reject);
22745    });
22746
22747    return deferred.promise;
22748  }
22749
22750  function $Q(resolver) {
22751    if (!isFunction(resolver)) {
22752      throw $qMinErr('norslvr', 'Expected resolverFn, got \'{0}\'', resolver);
22753    }
22754
22755    var deferred = new Deferred();
22756
22757    function resolveFn(value) {
22758      deferred.resolve(value);
22759    }
22760
22761    function rejectFn(reason) {
22762      deferred.reject(reason);
22763    }
22764
22765    resolver(resolveFn, rejectFn);
22766
22767    return deferred.promise;
22768  }
22769
22770  // Let's make the instanceof operator work for promises, so that
22771  // `new $q(fn) instanceof $q` would evaluate to true.
22772  $Q.prototype = Promise.prototype;
22773
22774  $Q.defer = defer;
22775  $Q.reject = reject;
22776  $Q.when = when;
22777  $Q.resolve = resolve;
22778  $Q.all = all;
22779  $Q.race = race;
22780
22781  return $Q;
22782}
22783
22784/** @this */
22785function $$RAFProvider() { //rAF
22786  this.$get = ['$window', '$timeout', function($window, $timeout) {
22787    var requestAnimationFrame = $window.requestAnimationFrame ||
22788                                $window.webkitRequestAnimationFrame;
22789
22790    var cancelAnimationFrame = $window.cancelAnimationFrame ||
22791                               $window.webkitCancelAnimationFrame ||
22792                               $window.webkitCancelRequestAnimationFrame;
22793
22794    var rafSupported = !!requestAnimationFrame;
22795    var raf = rafSupported
22796      ? function(fn) {
22797          var id = requestAnimationFrame(fn);
22798          return function() {
22799            cancelAnimationFrame(id);
22800          };
22801        }
22802      : function(fn) {
22803          var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
22804          return function() {
22805            $timeout.cancel(timer);
22806          };
22807        };
22808
22809    raf.supported = rafSupported;
22810
22811    return raf;
22812  }];
22813}
22814
22815/**
22816 * DESIGN NOTES
22817 *
22818 * The design decisions behind the scope are heavily favored for speed and memory consumption.
22819 *
22820 * The typical use of scope is to watch the expressions, which most of the time return the same
22821 * value as last time so we optimize the operation.
22822 *
22823 * Closures construction is expensive in terms of speed as well as memory:
22824 *   - No closures, instead use prototypical inheritance for API
22825 *   - Internal state needs to be stored on scope directly, which means that private state is
22826 *     exposed as $$____ properties
22827 *
22828 * Loop operations are optimized by using while(count--) { ... }
22829 *   - This means that in order to keep the same order of execution as addition we have to add
22830 *     items to the array at the beginning (unshift) instead of at the end (push)
22831 *
22832 * Child scopes are created and removed often
22833 *   - Using an array would be slow since inserts in the middle are expensive; so we use linked lists
22834 *
22835 * There are fewer watches than observers. This is why you don't want the observer to be implemented
22836 * in the same way as watch. Watch requires return of the initialization function which is expensive
22837 * to construct.
22838 */
22839
22840
22841/**
22842 * @ngdoc provider
22843 * @name $rootScopeProvider
22844 * @description
22845 *
22846 * Provider for the $rootScope service.
22847 */
22848
22849/**
22850 * @ngdoc method
22851 * @name $rootScopeProvider#digestTtl
22852 * @description
22853 *
22854 * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and
22855 * assuming that the model is unstable.
22856 *
22857 * The current default is 10 iterations.
22858 *
22859 * In complex applications it's possible that the dependencies between `$watch`s will result in
22860 * several digest iterations. However if an application needs more than the default 10 digest
22861 * iterations for its model to stabilize then you should investigate what is causing the model to
22862 * continuously change during the digest.
22863 *
22864 * Increasing the TTL could have performance implications, so you should not change it without
22865 * proper justification.
22866 *
22867 * @param {number} limit The number of digest iterations.
22868 */
22869
22870
22871/**
22872 * @ngdoc service
22873 * @name $rootScope
22874 * @this
22875 *
22876 * @description
22877 *
22878 * Every application has a single root {@link ng.$rootScope.Scope scope}.
22879 * All other scopes are descendant scopes of the root scope. Scopes provide separation
22880 * between the model and the view, via a mechanism for watching the model for changes.
22881 * They also provide event emission/broadcast and subscription facility. See the
22882 * {@link guide/scope developer guide on scopes}.
22883 */
22884function $RootScopeProvider() {
22885  var TTL = 10;
22886  var $rootScopeMinErr = minErr('$rootScope');
22887  var lastDirtyWatch = null;
22888  var applyAsyncId = null;
22889
22890  this.digestTtl = function(value) {
22891    if (arguments.length) {
22892      TTL = value;
22893    }
22894    return TTL;
22895  };
22896
22897  function createChildScopeClass(parent) {
22898    function ChildScope() {
vendor: 10,821 bytes, lines 22899-23156
22899      this.$$watchers = this.$$nextSibling =
22900          this.$$childHead = this.$$childTail = null;
22901      this.$$listeners = {};
22902      this.$$listenerCount = {};
22903      this.$$watchersCount = 0;
22904      this.$id = nextUid();
22905      this.$$ChildScope = null;
22906    }
22907    ChildScope.prototype = parent;
22908    return ChildScope;
22909  }
22910
22911  this.$get = ['$exceptionHandler', '$parse', '$browser',
22912      function($exceptionHandler, $parse, $browser) {
22913
22914    function destroyChildScope($event) {
22915        $event.currentScope.$$destroyed = true;
22916    }
22917
22918    function cleanUpScope($scope) {
22919
22920      if (msie === 9) {
22921        // There is a memory leak in IE9 if all child scopes are not disconnected
22922        // completely when a scope is destroyed. So this code will recurse up through
22923        // all this scopes children
22924        //
22925        // See issue https://github.com/angular/angular.js/issues/10706
22926        if ($scope.$$childHead) {
22927          cleanUpScope($scope.$$childHead);
22928        }
22929        if ($scope.$$nextSibling) {
22930          cleanUpScope($scope.$$nextSibling);
22931        }
22932      }
22933
22934      // The code below works around IE9 and V8's memory leaks
22935      //
22936      // See:
22937      // - https://code.google.com/p/v8/issues/detail?id=2073#c26
22938      // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
22939      // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
22940
22941      $scope.$parent = $scope.$$nextSibling = $scope.$$prevSibling = $scope.$$childHead =
22942          $scope.$$childTail = $scope.$root = $scope.$$watchers = null;
22943    }
22944
22945    /**
22946     * @ngdoc type
22947     * @name $rootScope.Scope
22948     *
22949     * @description
22950     * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
22951     * {@link auto.$injector $injector}. Child scopes are created using the
22952     * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
22953     * compiled HTML template is executed.) See also the {@link guide/scope Scopes guide} for
22954     * an in-depth introduction and usage examples.
22955     *
22956     *
22957     * # Inheritance
22958     * A scope can inherit from a parent scope, as in this example:
22959     * ```js
22960         var parent = $rootScope;
22961         var child = parent.$new();
22962
22963         parent.salutation = "Hello";
22964         expect(child.salutation).toEqual('Hello');
22965
22966         child.salutation = "Welcome";
22967         expect(child.salutation).toEqual('Welcome');
22968         expect(parent.salutation).toEqual('Hello');
22969     * ```
22970     *
22971     * When interacting with `Scope` in tests, additional helper methods are available on the
22972     * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional
22973     * details.
22974     *
22975     *
22976     * @param {Object.<string, function()>=} providers Map of service factory which need to be
22977     *                                       provided for the current scope. Defaults to {@link ng}.
22978     * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
22979     *                              append/override services provided by `providers`. This is handy
22980     *                              when unit-testing and having the need to override a default
22981     *                              service.
22982     * @returns {Object} Newly created scope.
22983     *
22984     */
22985    function Scope() {
22986      this.$id = nextUid();
22987      this.$$phase = this.$parent = this.$$watchers =
22988                     this.$$nextSibling = this.$$prevSibling =
22989                     this.$$childHead = this.$$childTail = null;
22990      this.$root = this;
22991      this.$$destroyed = false;
22992      this.$$listeners = {};
22993      this.$$listenerCount = {};
22994      this.$$watchersCount = 0;
22995      this.$$isolateBindings = null;
22996    }
22997
22998    /**
22999     * @ngdoc property
23000     * @name $rootScope.Scope#$id
23001     *
23002     * @description
23003     * Unique scope ID (monotonically increasing) useful for debugging.
23004     */
23005
23006     /**
23007      * @ngdoc property
23008      * @name $rootScope.Scope#$parent
23009      *
23010      * @description
23011      * Reference to the parent scope.
23012      */
23013
23014      /**
23015       * @ngdoc property
23016       * @name $rootScope.Scope#$root
23017       *
23018       * @description
23019       * Reference to the root scope.
23020       */
23021
23022    Scope.prototype = {
23023      constructor: Scope,
23024      /**
23025       * @ngdoc method
23026       * @name $rootScope.Scope#$new
23027       * @kind function
23028       *
23029       * @description
23030       * Creates a new child {@link ng.$rootScope.Scope scope}.
23031       *
23032       * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event.
23033       * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
23034       *
23035       * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
23036       * desired for the scope and its child scopes to be permanently detached from the parent and
23037       * thus stop participating in model change detection and listener notification by invoking.
23038       *
23039       * @param {boolean} isolate If true, then the scope does not prototypically inherit from the
23040       *         parent scope. The scope is isolated, as it can not see parent scope properties.
23041       *         When creating widgets, it is useful for the widget to not accidentally read parent
23042       *         state.
23043       *
23044       * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`
23045       *                              of the newly created scope. Defaults to `this` scope if not provided.
23046       *                              This is used when creating a transclude scope to correctly place it
23047       *                              in the scope hierarchy while maintaining the correct prototypical
23048       *                              inheritance.
23049       *
23050       * @returns {Object} The newly created child scope.
23051       *
23052       */
23053      $new: function(isolate, parent) {
23054        var child;
23055
23056        parent = parent || this;
23057
23058        if (isolate) {
23059          child = new Scope();
23060          child.$root = this.$root;
23061        } else {
23062          // Only create a child scope class if somebody asks for one,
23063          // but cache it to allow the VM to optimize lookups.
23064          if (!this.$$ChildScope) {
23065            this.$$ChildScope = createChildScopeClass(this);
23066          }
23067          child = new this.$$ChildScope();
23068        }
23069        child.$parent = parent;
23070        child.$$prevSibling = parent.$$childTail;
23071        if (parent.$$childHead) {
23072          parent.$$childTail.$$nextSibling = child;
23073          parent.$$childTail = child;
23074        } else {
23075          parent.$$childHead = parent.$$childTail = child;
23076        }
23077
23078        // When the new scope is not isolated or we inherit from `this`, and
23079        // the parent scope is destroyed, the property `$$destroyed` is inherited
23080        // prototypically. In all other cases, this property needs to be set
23081        // when the parent scope is destroyed.
23082        // The listener needs to be added after the parent is set
23083        if (isolate || parent !== this) child.$on('$destroy', destroyChildScope);
23084
23085        return child;
23086      },
23087
23088      /**
23089       * @ngdoc method
23090       * @name $rootScope.Scope#$watch
23091       * @kind function
23092       *
23093       * @description
23094       * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
23095       *
23096       * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
23097       *   $digest()} and should return the value that will be watched. (`watchExpression` should not change
23098       *   its value when executed multiple times with the same input because it may be executed multiple
23099       *   times by {@link ng.$rootScope.Scope#$digest $digest()}. That is, `watchExpression` should be
23100       *   [idempotent](http://en.wikipedia.org/wiki/Idempotence).)
23101       * - The `listener` is called only when the value from the current `watchExpression` and the
23102       *   previous call to `watchExpression` are not equal (with the exception of the initial run,
23103       *   see below). Inequality is determined according to reference inequality,
23104       *   [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators)
23105       *    via the `!==` Javascript operator, unless `objectEquality == true`
23106       *   (see next point)
23107       * - When `objectEquality == true`, inequality of the `watchExpression` is determined
23108       *   according to the {@link angular.equals} function. To save the value of the object for
23109       *   later comparison, the {@link angular.copy} function is used. This therefore means that
23110       *   watching complex objects will have adverse memory and performance implications.
23111       * - The watch `listener` may change the model, which may trigger other `listener`s to fire.
23112       *   This is achieved by rerunning the watchers until no changes are detected. The rerun
23113       *   iteration limit is 10 to prevent an infinite loop deadlock.
23114       *
23115       *
23116       * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
23117       * you can register a `watchExpression` function with no `listener`. (Be prepared for
23118       * multiple calls to your `watchExpression` because it will execute multiple times in a
23119       * single {@link ng.$rootScope.Scope#$digest $digest} cycle if a change is detected.)
23120       *
23121       * After a watcher is registered with the scope, the `listener` fn is called asynchronously
23122       * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
23123       * watcher. In rare cases, this is undesirable because the listener is called when the result
23124       * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
23125       * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
23126       * listener was called due to initialization.
23127       *
23128       *
23129       *
23130       * # Example
23131       * ```js
23132           // let's assume that scope was dependency injected as the $rootScope
23133           var scope = $rootScope;
23134           scope.name = 'misko';
23135           scope.counter = 0;
23136
23137           expect(scope.counter).toEqual(0);
23138           scope.$watch('name', function(newValue, oldValue) {
23139             scope.counter = scope.counter + 1;
23140           });
23141           expect(scope.counter).toEqual(0);
23142
23143           scope.$digest();
23144           // the listener is always called during the first $digest loop after it was registered
23145           expect(scope.counter).toEqual(1);
23146
23147           scope.$digest();
23148           // but now it will not be called unless the value changes
23149           expect(scope.counter).toEqual(1);
23150
23151           scope.name = 'adam';
23152           scope.$digest();
23153           expect(scope.counter).toEqual(2);
23154
23155
23156
23157           // Using a function as a watchExpression
23158           var food;
23159           scope.foodCounter = 0;
23160           expect(scope.foodCounter).toEqual(0);
23161           scope.$watch(
23162             // This function returns the value being watched. It is called for each turn of the $digest loop
23163             function() { return food; },
23164             // This is the change listener, called when the value returned from the above function changes
23165             function(newValue, oldValue) {
23166               if ( newValue !== oldValue ) {
23167                 // Only increment the counter if the value changed
23168                 scope.foodCounter = scope.foodCounter + 1;
23169               }
23170             }
23171           );
23172           // No digest has been run so the counter will be zero
23173           expect(scope.foodCounter).toEqual(0);
23174
23175           // Run the digest but since food has not changed count will still be zero
23176           scope.$digest();
23177           expect(scope.foodCounter).toEqual(0);
23178
23179           // Update food and run digest.  Now the counter will increment
23180           food = 'cheeseburger';
23181           scope.$digest();
23182           expect(scope.foodCounter).toEqual(1);
23183
23184       * ```
23185       *
23186       *
23187       *
23188       * @param {(function()|string)} watchExpression Expression that is evaluated on each
23189       *    {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
23190       *    a call to the `listener`.
23191       *
23192       *    - `string`: Evaluated as {@link guide/expression expression}
23193       *    - `function(scope)`: called with current `scope` as a parameter.
23194       * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value
23195       *    of `watchExpression` changes.
23196       *
23197       *    - `newVal` contains the current value of the `watchExpression`
23198       *    - `oldVal` contains the previous value of the `watchExpression`
23199       *    - `scope` refers to the current scope
23200       * @param {boolean=} [objectEquality=false] Compare for object equality using {@link angular.equals} instead of
23201       *     comparing for reference equality.
23202       * @returns {function()} Returns a deregistration function for this listener.
23203       */
23204      $watch: function(watchExp, listener, objectEquality, prettyPrintExpression) {
23205        var get = $parse(watchExp);
23206
23207        if (get.$$watchDelegate) {
23208          return get.$$watchDelegate(this, listener, objectEquality, get, watchExp);
23209        }
23210        var scope = this,
23211            array = scope.$$watchers,
23212            watcher = {
23213              fn: listener,
23214              last: initWatchVal,
23215              get: get,
23216              exp: prettyPrintExpression || watchExp,
23217              eq: !!objectEquality
23218            };
23219
23220        lastDirtyWatch = null;
23221
23222        if (!isFunction(listener)) {
23223          watcher.fn = noop;
23224        }
23225
23226        if (!array) {
23227          array = scope.$$watchers = [];
23228          array.$$digestWatchIndex = -1;
23229        }
23230        // we use unshift since we use a while loop in $digest for speed.
23231        // the while loop reads in reverse order.
23232        array.unshift(watcher);
23233        array.$$digestWatchIndex++;
23234        incrementWatchersCount(this, 1);
23235
23236        return function deregisterWatch() {
23237          var index = arrayRemove(array, watcher);
23238          if (index >= 0) {
23239            incrementWatchersCount(scope, -1);
23240            if (index < array.$$digestWatchIndex) {
23241              array.$$digestWatchIndex--;
23242            }
23243          }
23244          lastDirtyWatch = null;
23245        };
23246      },
23247
23248      /**
23249       * @ngdoc method
23250       * @name $rootScope.Scope#$watchGroup
23251       * @kind function
23252       *
23253       * @description
23254       * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`.
23255       * If any one expression in the collection changes the `listener` is executed.
23256       *
23257       * - The items in the `watchExpressions` array are observed via the standard `$watch` operation. Their return
23258       *   values are examined for changes on every call to `$digest`.
23259       * - The `listener` is called whenever any expression in the `watchExpressions` array changes.
23260       *
23261       * @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
23262       * watched using {@link ng.$rootScope.Scope#$watch $watch()}
23263       *
23264       * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any
23265       *    expression in `watchExpressions` changes
23266       *    The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
23267       *    those of `watchExpression`
23268       *    and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
23269       *    those of `watchExpression`
23270       *    The `scope` refers to the current scope.
23271       * @returns {function()} Returns a de-registration function for all listeners.
23272       */
23273      $watchGroup: function(watchExpressions, listener) {
23274        var oldValues = new Array(watchExpressions.length);
23275        var newValues = new Array(watchExpressions.length);
23276        var deregisterFns = [];
23277        var self = this;
23278        var changeReactionScheduled = false;
vendor: 4,272 bytes, lines 23279-23388
23279        var firstRun = true;
23280
23281        if (!watchExpressions.length) {
23282          // No expressions means we call the listener ASAP
23283          var shouldCall = true;
23284          self.$evalAsync(function() {
23285            if (shouldCall) listener(newValues, newValues, self);
23286          });
23287          return function deregisterWatchGroup() {
23288            shouldCall = false;
23289          };
23290        }
23291
23292        if (watchExpressions.length === 1) {
23293          // Special case size of one
23294          return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) {
23295            newValues[0] = value;
23296            oldValues[0] = oldValue;
23297            listener(newValues, (value === oldValue) ? newValues : oldValues, scope);
23298          });
23299        }
23300
23301        forEach(watchExpressions, function(expr, i) {
23302          var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
23303            newValues[i] = value;
23304            oldValues[i] = oldValue;
23305            if (!changeReactionScheduled) {
23306              changeReactionScheduled = true;
23307              self.$evalAsync(watchGroupAction);
23308            }
23309          });
23310          deregisterFns.push(unwatchFn);
23311        });
23312
23313        function watchGroupAction() {
23314          changeReactionScheduled = false;
23315
23316          if (firstRun) {
23317            firstRun = false;
23318            listener(newValues, newValues, self);
23319          } else {
23320            listener(newValues, oldValues, self);
23321          }
23322        }
23323
23324        return function deregisterWatchGroup() {
23325          while (deregisterFns.length) {
23326            deregisterFns.shift()();
23327          }
23328        };
23329      },
23330
23331
23332      /**
23333       * @ngdoc method
23334       * @name $rootScope.Scope#$watchCollection
23335       * @kind function
23336       *
23337       * @description
23338       * Shallow watches the properties of an object and fires whenever any of the properties change
23339       * (for arrays, this implies watching the array items; for object maps, this implies watching
23340       * the properties). If a change is detected, the `listener` callback is fired.
23341       *
23342       * - The `obj` collection is observed via standard $watch operation and is examined on every
23343       *   call to $digest() to see if any items have been added, removed, or moved.
23344       * - The `listener` is called whenever anything within the `obj` has changed. Examples include
23345       *   adding, removing, and moving items belonging to an object or array.
23346       *
23347       *
23348       * # Example
23349       * ```js
23350          $scope.names = ['igor', 'matias', 'misko', 'james'];
23351          $scope.dataCount = 4;
23352
23353          $scope.$watchCollection('names', function(newNames, oldNames) {
23354            $scope.dataCount = newNames.length;
23355          });
23356
23357          expect($scope.dataCount).toEqual(4);
23358          $scope.$digest();
23359
23360          //still at 4 ... no changes
23361          expect($scope.dataCount).toEqual(4);
23362
23363          $scope.names.pop();
23364          $scope.$digest();
23365
23366          //now there's been a change
23367          expect($scope.dataCount).toEqual(3);
23368       * ```
23369       *
23370       *
23371       * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The
23372       *    expression value should evaluate to an object or an array which is observed on each
23373       *    {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
23374       *    collection will trigger a call to the `listener`.
23375       *
23376       * @param {function(newCollection, oldCollection, scope)} listener a callback function called
23377       *    when a change is detected.
23378       *    - The `newCollection` object is the newly modified data obtained from the `obj` expression
23379       *    - The `oldCollection` object is a copy of the former collection data.
23380       *      Due to performance considerations, the`oldCollection` value is computed only if the
23381       *      `listener` function declares two or more arguments.
23382       *    - The `scope` argument refers to the current scope.
23383       *
23384       * @returns {function()} Returns a de-registration function for this listener. When the
23385       *    de-registration function is executed, the internal watch operation is terminated.
23386       */
23387      $watchCollection: function(obj, listener) {
23388        $watchCollectionInterceptor.$stateful = tru
vendor: 6,149 bytes, lines 23388-23546
23388e;
23389
23390        var self = this;
23391        // the current value, updated on each dirty-check run
23392        var newValue;
23393        // a shallow copy of the newValue from the last dirty-check run,
23394        // updated to match newValue during dirty-check run
23395        var oldValue;
23396        // a shallow copy of the newValue from when the last change happened
23397        var veryOldValue;
23398        // only track veryOldValue if the listener is asking for it
23399        var trackVeryOldValue = (listener.length > 1);
23400        var changeDetected = 0;
23401        var changeDetector = $parse(obj, $watchCollectionInterceptor);
23402        var internalArray = [];
23403        var internalObject = {};
23404        var initRun = true;
23405        var oldLength = 0;
23406
23407        function $watchCollectionInterceptor(_value) {
23408          newValue = _value;
23409          var newLength, key, bothNaN, newItem, oldItem;
23410
23411          // If the new value is undefined, then return undefined as the watch may be a one-time watch
23412          if (isUndefined(newValue)) return;
23413
23414          if (!isObject(newValue)) { // if primitive
23415            if (oldValue !== newValue) {
23416              oldValue = newValue;
23417              changeDetected++;
23418            }
23419          } else if (isArrayLike(newValue)) {
23420            if (oldValue !== internalArray) {
23421              // we are transitioning from something which was not an array into array.
23422              oldValue = internalArray;
23423              oldLength = oldValue.length = 0;
23424              changeDetected++;
23425            }
23426
23427            newLength = newValue.length;
23428
23429            if (oldLength !== newLength) {
23430              // if lengths do not match we need to trigger change notification
23431              changeDetected++;
23432              oldValue.length = oldLength = newLength;
23433            }
23434            // copy the items to oldValue and look for changes.
23435            for (var i = 0; i < newLength; i++) {
23436              oldItem = oldValue[i];
23437              newItem = newValue[i];
23438
23439              // eslint-disable-next-line no-self-compare
23440              bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
23441              if (!bothNaN && (oldItem !== newItem)) {
23442                changeDetected++;
23443                oldValue[i] = newItem;
23444              }
23445            }
23446          } else {
23447            if (oldValue !== internalObject) {
23448              // we are transitioning from something which was not an object into object.
23449              oldValue = internalObject = {};
23450              oldLength = 0;
23451              changeDetected++;
23452            }
23453            // copy the items to oldValue and look for changes.
23454            newLength = 0;
23455            for (key in newValue) {
23456              if (hasOwnProperty.call(newValue, key)) {
23457                newLength++;
23458                newItem = newValue[key];
23459                oldItem = oldValue[key];
23460
23461                if (key in oldValue) {
23462                  // eslint-disable-next-line no-self-compare
23463                  bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
23464                  if (!bothNaN && (oldItem !== newItem)) {
23465                    changeDetected++;
23466                    oldValue[key] = newItem;
23467                  }
23468                } else {
23469                  oldLength++;
23470                  oldValue[key] = newItem;
23471                  changeDetected++;
23472                }
23473              }
23474            }
23475            if (oldLength > newLength) {
23476              // we used to have more keys, need to find them and destroy them.
23477              changeDetected++;
23478              for (key in oldValue) {
23479                if (!hasOwnProperty.call(newValue, key)) {
23480                  oldLength--;
23481                  delete oldValue[key];
23482                }
23483              }
23484            }
23485          }
23486          return changeDetected;
23487        }
23488
23489        function $watchCollectionAction() {
23490          if (initRun) {
23491            initRun = false;
23492            listener(newValue, newValue, self);
23493          } else {
23494            listener(newValue, veryOldValue, self);
23495          }
23496
23497          // make a copy for the next time a collection is changed
23498          if (trackVeryOldValue) {
23499            if (!isObject(newValue)) {
23500              //primitive
23501              veryOldValue = newValue;
23502            } else if (isArrayLike(newValue)) {
23503              veryOldValue = new Array(newValue.length);
23504              for (var i = 0; i < newValue.length; i++) {
23505                veryOldValue[i] = newValue[i];
23506              }
23507            } else { // if object
23508              veryOldValue = {};
23509              for (var key in newValue) {
23510                if (hasOwnProperty.call(newValue, key)) {
23511                  veryOldValue[key] = newValue[key];
23512                }
23513              }
23514            }
23515          }
23516        }
23517
23518        return this.$watch(changeDetector, $watchCollectionAction);
23519      },
23520
23521      /**
23522       * @ngdoc method
23523       * @name $rootScope.Scope#$digest
23524       * @kind function
23525       *
23526       * @description
23527       * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
23528       * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
23529       * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
23530       * until no more listeners are firing. This means that it is possible to get into an infinite
23531       * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
23532       * iterations exceeds 10.
23533       *
23534       * Usually, you don't call `$digest()` directly in
23535       * {@link ng.directive:ngController controllers} or in
23536       * {@link ng.$compileProvider#directive directives}.
23537       * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
23538       * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`.
23539       *
23540       * If you want to be notified whenever `$digest()` is called,
23541       * you can register a `watchExpression` function with
23542       * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
23543       *
23544       * In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
23545       *
23546       * 
vendor: 8,924 bytes, lines 23546-23764
23546# Example
23547       * ```js
23548           var scope = ...;
23549           scope.name = 'misko';
23550           scope.counter = 0;
23551
23552           expect(scope.counter).toEqual(0);
23553           scope.$watch('name', function(newValue, oldValue) {
23554             scope.counter = scope.counter + 1;
23555           });
23556           expect(scope.counter).toEqual(0);
23557
23558           scope.$digest();
23559           // the listener is always called during the first $digest loop after it was registered
23560           expect(scope.counter).toEqual(1);
23561
23562           scope.$digest();
23563           // but now it will not be called unless the value changes
23564           expect(scope.counter).toEqual(1);
23565
23566           scope.name = 'adam';
23567           scope.$digest();
23568           expect(scope.counter).toEqual(2);
23569       * ```
23570       *
23571       */
23572      $digest: function() {
23573        var watch, value, last, fn, get,
23574            watchers,
23575            dirty, ttl = TTL,
23576            next, current, target = this,
23577            watchLog = [],
23578            logIdx, asyncTask;
23579
23580        beginPhase('$digest');
23581        // Check for changes to browser url that happened in sync before the call to $digest
23582        $browser.$$checkUrlChange();
23583
23584        if (this === $rootScope && applyAsyncId !== null) {
23585          // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then
23586          // cancel the scheduled $apply and flush the queue of expressions to be evaluated.
23587          $browser.defer.cancel(applyAsyncId);
23588          flushApplyAsync();
23589        }
23590
23591        lastDirtyWatch = null;
23592
23593        do { // "while dirty" loop
23594          dirty = false;
23595          current = target;
23596
23597          // It's safe for asyncQueuePosition to be a local variable here because this loop can't
23598          // be reentered recursively. Calling $digest from a function passed to $applyAsync would
23599          // lead to a '$digest already in progress' error.
23600          for (var asyncQueuePosition = 0; asyncQueuePosition < asyncQueue.length; asyncQueuePosition++) {
23601            try {
23602              asyncTask = asyncQueue[asyncQueuePosition];
23603              asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals);
23604            } catch (e) {
23605              $exceptionHandler(e);
23606            }
23607            lastDirtyWatch = null;
23608          }
23609          asyncQueue.length = 0;
23610
23611          traverseScopesLoop:
23612          do { // "traverse the scopes" loop
23613            if ((watchers = current.$$watchers)) {
23614              // process our watches
23615              watchers.$$digestWatchIndex = watchers.length;
23616              while (watchers.$$digestWatchIndex--) {
23617                try {
23618                  watch = watchers[watchers.$$digestWatchIndex];
23619                  // Most common watches are on primitives, in which case we can short
23620                  // circuit it with === operator, only when === fails do we use .equals
23621                  if (watch) {
23622                    get = watch.get;
23623                    if ((value = get(current)) !== (last = watch.last) &&
23624                        !(watch.eq
23625                            ? equals(value, last)
23626                            : (isNumberNaN(value) && isNumberNaN(last)))) {
23627                      dirty = true;
23628                      lastDirtyWatch = watch;
23629                      watch.last = watch.eq ? copy(value, null) : value;
23630                      fn = watch.fn;
23631                      fn(value, ((last === initWatchVal) ? value : last), current);
23632                      if (ttl < 5) {
23633                        logIdx = 4 - ttl;
23634                        if (!watchLog[logIdx]) watchLog[logIdx] = [];
23635                        watchLog[logIdx].push({
23636                          msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp,
23637                          newVal: value,
23638                          oldVal: last
23639                        });
23640                      }
23641                    } else if (watch === lastDirtyWatch) {
23642                      // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
23643                      // have already been tested.
23644                      dirty = false;
23645                      break traverseScopesLoop;
23646                    }
23647                  }
23648                } catch (e) {
23649                  $exceptionHandler(e);
23650                }
23651              }
23652            }
23653
23654            // Insanity Warning: scope depth-first traversal
23655            // yes, this code is a bit crazy, but it works and we have tests to prove it!
23656            // this piece should be kept in sync with the traversal in $broadcast
23657            if (!(next = ((current.$$watchersCount && current.$$childHead) ||
23658                (current !== target && current.$$nextSibling)))) {
23659              while (current !== target && !(next = current.$$nextSibling)) {
23660                current = current.$parent;
23661              }
23662            }
23663          } while ((current = next));
23664
23665          // `break traverseScopesLoop;` takes us to here
23666
23667          if ((dirty || asyncQueue.length) && !(ttl--)) {
23668            clearPhase();
23669            throw $rootScopeMinErr('infdig',
23670                '{0} $digest() iterations reached. Aborting!\n' +
23671                'Watchers fired in the last 5 iterations: {1}',
23672                TTL, watchLog);
23673          }
23674
23675        } while (dirty || asyncQueue.length);
23676
23677        clearPhase();
23678
23679        // postDigestQueuePosition isn't local here because this loop can be reentered recursively.
23680        while (postDigestQueuePosition < postDigestQueue.length) {
23681          try {
23682            postDigestQueue[postDigestQueuePosition++]();
23683          } catch (e) {
23684            $exceptionHandler(e);
23685          }
23686        }
23687        postDigestQueue.length = postDigestQueuePosition = 0;
23688      },
23689
23690
23691      /**
23692       * @ngdoc event
23693       * @name $rootScope.Scope#$destroy
23694       * @eventType broadcast on scope being destroyed
23695       *
23696       * @description
23697       * Broadcasted when a scope and its children are being destroyed.
23698       *
23699       * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
23700       * clean up DOM bindings before an element is removed from the DOM.
23701       */
23702
23703      /**
23704       * @ngdoc method
23705       * @name $rootScope.Scope#$destroy
23706       * @kind function
23707       *
23708       * @description
23709       * Removes the current scope (and all of its children) from the parent scope. Removal implies
23710       * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
23711       * propagate to the current scope and its children. Removal also implies that the current
23712       * scope is eligible for garbage collection.
23713       *
23714       * The `$destroy()` is usually used by directives such as
23715       * {@link ng.directive:ngRepeat ngRepeat} for managing the
23716       * unrolling of the loop.
23717       *
23718       * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope.
23719       * Application code can register a `$destroy` event handler that will give it a chance to
23720       * perform any necessary cleanup.
23721       *
23722       * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
23723       * clean up DOM bindings before an element is removed from the DOM.
23724       */
23725      $destroy: function() {
23726        // We can't destroy a scope that has been already destroyed.
23727        if (this.$$destroyed) return;
23728        var parent = this.$parent;
23729
23730        this.$broadcast('$destroy');
23731        this.$$destroyed = true;
23732
23733        if (this === $rootScope) {
23734          //Remove handlers attached to window when $rootScope is removed
23735          $browser.$$applicationDestroyed();
23736        }
23737
23738        incrementWatchersCount(this, -this.$$watchersCount);
23739        for (var eventName in this.$$listenerCount) {
23740          decrementListenerCount(this, this.$$listenerCount[eventName], eventName);
23741        }
23742
23743        // sever all the references to parent scopes (after this cleanup, the current scope should
23744        // not be retained by any of our references and should be eligible for garbage collection)
23745        if (parent && parent.$$childHead === this) parent.$$childHead = this.$$nextSibling;
23746        if (parent && parent.$$childTail === this) parent.$$childTail = this.$$prevSibling;
23747        if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
23748        if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
23749
23750        // Disable listeners, watchers and apply/digest methods
23751        this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
23752        this.$on = this.$watch = this.$watchGroup = function() { return noop; };
23753        this.$$listeners = {};
23754
23755        // Disconnect the next sibling to prevent `cleanUpScope` destroying those too
23756        this.$$nextSibling = null;
23757        cleanUpScope(this);
23758      },
23759
23760      /**
23761       * @ngdoc method
23762       * @name $rootScope.Scope#$eval
23763       * @kind function
23764       *
23765       * @description
23766       * Executes the `expression` on the current scope and returns the result. Any exceptions in
23767       * the expression are propagated (uncaught). This is useful when evaluating Angular
23768       * expressions.
23769       *
23770       * # Example
23771       * ```js
23772           var scope = ng.$rootScope.Scope();
23773           scope.a = 1;
23774           scope.b = 2;
23775
23776           expect(scope.$eval('a+b')).toEqual(3);
23777           expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
23778       * ```
23779       *
23780       * @param {(string|function())=} expression An angular expression to be executed.
23781       *
23782       *    - `string`: execute using the rules as defined in  {@link guide/expression expression}.
23783       *    - `function(scope)`: execute the function with the current `scope` parameter.
23784       *
23785       * @param {(object)=} locals Local variables object, useful for overriding values in scope.
23786       * @returns {*} The result of evaluating the expression.
23787       */
23788      $eval: function(expr, locals) {
23789        return $parse(expr)(this, locals);
23790      },
23791
23792      /**
23793       * @ngdoc method
23794       * @name $rootScope.Scope#$evalAsync
23795       * @kind function
23796       *
23797       * @description
23798       * Executes the expression on the current scope at a later point in time.
23799       *
23800       * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only
23801       * that:
23802       *
23803       *   - it will execute after the function that scheduled the evaluation (preferably before DOM
23804       *     rendering).
23805       *   - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
23806       *     `expression` execution.
23807       *
23808       * Any exceptions from the execution of the expression are forwarded to the
23809       * {@link ng.$exceptionHandler $exceptionHandler} service.
23810       *
23811       * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle
23812       * will be scheduled. However, it is encouraged to always call code that changes the model
23813       * from within an `$apply` call. That includes code evaluated via `$evalAsync`.
23814       *
23815       * @param {(string|function())=} expression An angular expression to be executed.
23816       *
23817       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
23818       *    - `function(scope)`: execute the function with the current `scope` parameter.
23819       *
23820       * @param {(object)=} locals Local variables object, useful for overriding values in scope.
23821       */
23822      $evalAsync: function(expr, locals) {
23823        // if we are outside of an $digest loop and this is the first time we are scheduling async
23824        // task also schedule async auto-flush
23825        if (!$rootScope.$$phase && !asyncQueue.length) {
23826          $browser.defer(function() {
23827            if (asyncQueue.length) {
23828              $rootScope.$digest();
23829            }
23830          });
23831        }
23832
23833        asyncQueue.push({scope: this, expression: $parse(expr), locals: locals});
23834      },
23835
23836      $$postDigest: function(fn) {
23837        postDigestQueue.push(fn);
23838      },
23839
23840      /**
23841       * @ngdoc method
23842       * @name $rootScope.Scope#$apply
23843       * @kind function
23844       *
23845       * @description
23846       * `$apply()` is used to execute an expression in angular from outside of the angular
23847       * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
23848       * Because we are calling into the angular framework we need to perform proper scope life
23849       * cycle of {@link ng.$exceptionHandler exception handling},
23850       * {@link ng.$rootScope.Scope#$digest executing watches}.
23851       *
23852       * ## Life cycle
23853       *
23854       * # Pseudo-Code of `$apply()`
23855       * ```js
23856           function $apply(expr) {
23857             try {
23858               return $eval(expr);
23859             } catch (e) {
23860               $exceptionHandler(e);
23861             } finally {
23862               $root.$digest();
23863             }
23864           }
23865       * ```
23866       *
23867       *
23868       * Scope's `$apply()` method transitions through the following stages:
23869       *
23870       * 1. The {@link guide/expression expression} is executed using the
23871       *    {@link ng.$rootScope.Scope#$eval $eval()} method.
23872       * 2. Any exceptions from the execution of the expression are forwarded to the
23873       *    {@link ng.$exceptionHandler $exceptionHandler} service.
23874       * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
23875       *    expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
23876       *
23877       *
23878       * @param {(string|function())=} exp An angular expression to be executed.
23879       *
23880       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
23881       *    - `function(scope)`: execute the function with current `scope` parameter.
23882       *
23883       * @returns {*} The result of evaluating the expression.
23884       */
23885      $apply: function(expr) {
23886        try {
23887          beginPhase('$apply');
23888          try {
23889            return this.$eval(expr);
23890          } finally {
23891            clearPhase();
23892          }
23893        } catch (e) {
23894          $exceptionHandler(e);
23895        } finally {
23896          try {
23897            $rootScope.$digest();
23898          } catch (e) {
23899            $exceptionHandler(e);
23900            // eslint-disable-next-line no-unsafe-finally
23901            throw e;
23902          }
23903        }
23904      },
23905
23906      /**
23907       * @ngdoc method
23908       * @name $rootScope.Scope#$applyAsync
23909       * @kind function
23910       *
23911       * @description
23912       * Schedule the invocation of $apply to occur at a later time. The actual time difference
23913       * varies across browsers, but is typically around ~10 milliseconds.
23914       *
23915       * This can be used to queue up multiple expressions which need to be evaluated in the same
23916       * digest.
23917       *
23918       * @param {(string|function())=} exp An angular expression to be executed.
23919       *
23920       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
23921       *    - `function(scope)`: execute the function with current `scope` parameter.
23922       */
23923      $applyAsync: function(expr) {
23924        var scope = this;
23925        if (expr) {
23926          applyAsyncQueue.push($applyAsyncExpression);
23927        }
23928        expr = $parse(expr);
23929        scheduleApplyAsync();
23930
23931        function $applyAsyncExpression() {
23932          scope.$eval(expr);
23933        }
23934      },
23935
23936      /**
23937       * @ngdoc method
23938       * @name $rootScope.Scope#$on
23939       * @kind function
23940       *
23941       * @description
23942       * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
23943       * discussion of event life cycle.
23944       *
23945       * The event listener function format is: `function(event, args...)`. The `event` object
23946       * passed into the listener has the following attributes:
23947       *
23948       *   - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or
23949       *     `$broadcast`-ed.
23950       *   - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the
23951       *     event propagates through the scope hierarchy, this property is set to null.
23952       *   - `name` - `{string}`: name of the event.
23953       *   - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
23954       *     further event propagation (available only for events that were `$emit`-ed).
23955       *   - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
23956       *     to true.
23957       *   - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
23958       *
23959       * @param {string} name Event name to listen on.
23960       * @param {function(event, ...args)} listener Function to call when the event is emitted.
23961       * @returns {function()} Returns a deregistration function for this listener.
23962       */
23963      $on: function(name, listener) {
23964        var namedListeners = this.$$listeners[name];
23965        if (!namedListeners) {
23966          this.$$listeners[name] = namedListeners = [];
23967        }
23968        namedListeners.push(listener);
23969
23970        var current = this;
23971        do {
23972          if (!current.$$listenerCount[name]) {
23973            current.$$listenerCount[name] = 0;
23974          }
23975          current.$$listenerCount[name]++;
23976        } while ((current = current.$parent));
23977
23978        var self = this;
23979        return function() {
23980          var indexOfListener = namedListeners.indexOf(listener);
23981          if (indexOfListener !== -1) {
23982            namedListeners[indexOfListener] = null;
23983            decrementListenerCount(self, 1, name);
23984          }
23985        };
23986      },
23987
23988
23989      /**
23990       * @ngdoc method
23991       * @name $rootScope.Scope#$emit
23992       * @kind function
23993       *
23994       * @description
23995       * Dispatches an event `name` upwards through the scope hierarchy notifying the
23996       * registered {@link ng.$rootScope.Scope#$on} listeners.
23997       *
23998       * The event life cycle starts at the scope on which `$emit` was called. All
23999       * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
24000       * notified. Afterwards, the event traverses upwards toward the root scope and calls all
24001       * registered listeners along the way. The event will stop propagating if one of the listeners
24002       * cancels it.
24003       *
24004       * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
24005       * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
24006       *
24007       * @param {string} name Event name to emit.
24008       * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
24009       * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
24010       */
24011      $emit: function(name, args) {
24012        var empty = [],
24013            namedListeners,
24014            scope = this,
24015            stopPropagation = false,
24016            event = {
24017              name: name,
24018              targetScope: scope,
24019              stopPropagation: function() {stopPropagation = true;},
24020              preventDefault: function() {
24021                event.defaultPrevented = true;
24022              },
24023              defaultPrevented: false
24024            },
24025            listenerArgs = concat([event], arguments, 1),
24026            i, length;
24027
24028        do {
24029          namedListeners = scope.$$listeners[name] || empty;
24030          event.currentScope = scope;
24031          for (i = 0, length = namedListeners.length; i < length; i++) {
24032
24033            // if listeners were deregistered, defragment the array
24034            if (!namedListeners[i]) {
24035              namedListeners.splice(i, 1);
24036              i--;
24037              length--;
24038              continue;
24039            }
24040            try {
24041              //allow all listeners attached to the current scope to run
24042              namedListeners[i].apply(null, listenerArgs);
24043            } catch (e) {
24044              $exceptionHandler(e);
24045            }
24046          }
24047          //if any listener on the current scope stops propagation, prevent bubbling
24048          if (stopPropagation) {
24049            event.currentScope = null;
24050            return event;
24051          }
24052          //traverse upwards
24053          scope = scope.$parent;
24054        } while (scope);
24055
24056        event.currentScope = null;
24057
24058        return event;
24059      },
24060
24061
24062      /**
24063       * @ngdoc method
24064       * @name $rootScope.Scope#$broadcast
24065       * @kind function
24066       *
24067       * @description
24068       * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
24069       * registered {@link ng.$rootScope.Scope#$on} listeners.
24070       *
24071       * The event life cycle starts at the scope on which `$broadcast` was called. All
24072       * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
24073       * notified. Afterwards, the event propagates to all direct and indirect scopes of the current
24074       * scope and calls all registered listeners along the way. The event cannot be canceled.
24075       *
24076       * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
24077       * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
24078       *
24079       * @param {string} name Event name to broadcast.
24080       * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
24081       * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
24082       */
24083      $broadcast: function(name, args) {
24084        var target = this,
24085            current = target,
24086            next = target,
24087            event = {
24088              name: name,
24089              targetScope: target,
24090              preventDefault: function() {
24091                event.defaultPrevented = true;
24092              },
24093              defaultPrevented: false
24094            };
24095
24096        if (!target.$$listenerCount[name]) return event;
24097
24098        var listenerArgs = concat([event], arguments, 1),
24099            listeners, i, length;
24100
24101        //down while you can, then up and next sibling or up and next sibling until back at root
24102        while ((current = next)) {
24103          event.currentScope = current;
24104          listeners = current.$$listeners[name] || [];
24105          for (i = 0, length = listeners.length; i < length; i++) {
24106            // if listeners were deregistered, defragment the array
24107            if (!listeners[i]) {
24108              listeners.splice(i, 1);
24109              i--;
24110              length--;
24111              continue;
24112            }
24113
24114            try {
24115              listeners[i].apply(null, listenerArgs);
24116            } catch (e) {
24117              $exceptionHandler(e);
24118            }
24119          }
24120
24121          // Insanity Warning: scope depth-first traversal
24122          // yes, this code is a bit crazy, but it works and we have tests to prove it!
24123          // this piece should be kept in sync with the traversal in $digest
24124          // (though it differs due to having the extra check for $$listenerCount)
24125          if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
24126              (current !== target && current.$$nextSibling)))) {
24127            while (current !== target && !(next = current.$$nextSibling)) {
24128              current = current.$parent;
24129            }
24130          }
24131        }
24132
24133        event.currentScope = null;
24134        return event;
24135      }
24136    };
24137
24138    var $rootScope = new Scope();
24139
24140    //The internal queues. Expose them on the $rootScope for debugging/testing purposes.
24141    var asyncQueue = $rootScope.$$asyncQueue = [];
24142    var postDigestQueue = $rootScope.$$postDigestQueue = [];
24143    var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];
24144
24145    var postDigestQueuePosition = 0;
24146
24147    return $rootScope;
24148
24149
24150    function beginPhase(phase) {
24151      if ($rootScope.$$phase) {
24152        throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase);
24153      }
24154
24155      $rootScope.$$phase = phase;
24156    }
24157
24158    function clearPhase() {
24159      $rootScope.$$phase = null;
24160    }
24161
24162    function incrementWatchersCount(current, count) {
24163      do {
24164        current.$$watchersCount += count;
24165      } while ((current = current.$parent));
24166    }
24167
24168    function decrementListenerCount(current, count, name) {
24169      do {
24170        current.$$listenerCount[name] -= count;
24171
24172        if (current.$$listenerCount[name] === 0) {
24173          delete current.$$listenerCount[name];
24174        }
24175      } while ((current = current.$parent));
24176    }
24177
24178    /**
24179     * function used as an initial value for watchers.
24180     * because it's unique we can easily tell it apart from other values
24181     */
24182    function initWatchVal() {}
24183
24184    function flushApplyAsync() {
vendor: 9,063 bytes, lines 24185-24417
24185      while (applyAsyncQueue.length) {
24186        try {
24187          applyAsyncQueue.shift()();
24188        } catch (e) {
24189          $exceptionHandler(e);
24190        }
24191      }
24192      applyAsyncId = null;
24193    }
24194
24195    function scheduleApplyAsync() {
24196      if (applyAsyncId === null) {
24197        applyAsyncId = $browser.defer(function() {
24198          $rootScope.$apply(flushApplyAsync);
24199        });
24200      }
24201    }
24202  }];
24203}
24204
24205/**
24206 * @ngdoc service
24207 * @name $rootElement
24208 *
24209 * @description
24210 * The root element of Angular application. This is either the element where {@link
24211 * ng.directive:ngApp ngApp} was declared or the element passed into
24212 * {@link angular.bootstrap}. The element represents the root element of application. It is also the
24213 * location where the application's {@link auto.$injector $injector} service gets
24214 * published, and can be retrieved using `$rootElement.injector()`.
24215 */
24216
24217
24218// the implementation is in angular.bootstrap
24219
24220/**
24221 * @this
24222 * @description
24223 * Private service to sanitize uris for links and images. Used by $compile and $sanitize.
24224 */
24225function $$SanitizeUriProvider() {
24226  var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
24227    imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/;
24228
24229  /**
24230   * @description
24231   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
24232   * urls during a[href] sanitization.
24233   *
24234   * The sanitization is a security measure aimed at prevent XSS attacks via html links.
24235   *
24236   * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
24237   * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
24238   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
24239   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
24240   *
24241   * @param {RegExp=} regexp New regexp to whitelist urls with.
24242   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
24243   *    chaining otherwise.
24244   */
24245  this.aHrefSanitizationWhitelist = function(regexp) {
24246    if (isDefined(regexp)) {
24247      aHrefSanitizationWhitelist = regexp;
24248      return this;
24249    }
24250    return aHrefSanitizationWhitelist;
24251  };
24252
24253
24254  /**
24255   * @description
24256   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
24257   * urls during img[src] sanitization.
24258   *
24259   * The sanitization is a security measure aimed at prevent XSS attacks via html links.
24260   *
24261   * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
24262   * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
24263   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
24264   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
24265   *
24266   * @param {RegExp=} regexp New regexp to whitelist urls with.
24267   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
24268   *    chaining otherwise.
24269   */
24270  this.imgSrcSanitizationWhitelist = function(regexp) {
24271    if (isDefined(regexp)) {
24272      imgSrcSanitizationWhitelist = regexp;
24273      return this;
24274    }
24275    return imgSrcSanitizationWhitelist;
24276  };
24277
24278  this.$get = function() {
24279    return function sanitizeUri(uri, isImage) {
24280      var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
24281      var normalizedVal;
24282      normalizedVal = urlResolve(uri).href;
24283      if (normalizedVal !== '' && !normalizedVal.match(regex)) {
24284        return 'unsafe:' + normalizedVal;
24285      }
24286      return uri;
24287    };
24288  };
24289}
24290
24291/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
24292 *     Any commits to this file should be reviewed with security in mind.  *
24293 *   Changes to this file can potentially create security vulnerabilities. *
24294 *          An approval from 2 Core members with history of modifying      *
24295 *                         this file is required.                          *
24296 *                                                                         *
24297 *  Does the change somehow allow for arbitrary javascript to be executed? *
24298 *    Or allows for someone to change the prototype of built-in objects?   *
24299 *     Or gives undesired access to variables likes document or window?    *
24300 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
24301
24302/* exported $SceProvider, $SceDelegateProvider */
24303
24304var $sceMinErr = minErr('$sce');
24305
24306var SCE_CONTEXTS = {
24307  HTML: 'html',
24308  CSS: 'css',
24309  URL: 'url',
24310  // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
24311  // url.  (e.g. ng-include, script src, templateUrl)
24312  RESOURCE_URL: 'resourceUrl',
24313  JS: 'js'
24314};
24315
24316// Helper functions follow.
24317
24318function adjustMatcher(matcher) {
24319  if (matcher === 'self') {
24320    return matcher;
24321  } else if (isString(matcher)) {
24322    // Strings match exactly except for 2 wildcards - '*' and '**'.
24323    // '*' matches any character except those from the set ':/.?&'.
24324    // '**' matches any character (like .* in a RegExp).
24325    // More than 2 *'s raises an error as it's ill defined.
24326    if (matcher.indexOf('***') > -1) {
24327      throw $sceMinErr('iwcard',
24328          'Illegal sequence *** in string matcher.  String: {0}', matcher);
24329    }
24330    matcher = escapeForRegexp(matcher).
24331                  replace(/\\\*\\\*/g, '.*').
24332                  replace(/\\\*/g, '[^:/.?&;]*');
24333    return new RegExp('^' + matcher + '$');
24334  } else if (isRegExp(matcher)) {
24335    // The only other type of matcher allowed is a Regexp.
24336    // Match entire URL / disallow partial matches.
24337    // Flags are reset (i.e. no global, ignoreCase or multiline)
24338    return new RegExp('^' + matcher.source + '$');
24339  } else {
24340    throw $sceMinErr('imatcher',
24341        'Matchers may only be "self", string patterns or RegExp objects');
24342  }
24343}
24344
24345
24346function adjustMatchers(matchers) {
24347  var adjustedMatchers = [];
24348  if (isDefined(matchers)) {
24349    forEach(matchers, function(matcher) {
24350      adjustedMatchers.push(adjustMatcher(matcher));
24351    });
24352  }
24353  return adjustedMatchers;
24354}
24355
24356
24357/**
24358 * @ngdoc service
24359 * @name $sceDelegate
24360 * @kind function
24361 *
24362 * @description
24363 *
24364 * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
24365 * Contextual Escaping (SCE)} services to AngularJS.
24366 *
24367 * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
24368 * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS.  This is
24369 * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
24370 * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things
24371 * work because `$sce` delegates to `$sceDelegate` for these operations.
24372 *
24373 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service.
24374 *
24375 * The default instance of `$sceDelegate` should work out of the box with little pain.  While you
24376 * can override it completely to change the behavior of `$sce`, the common case would
24377 * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting
24378 * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as
24379 * templates.  Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist
24380 * $sceDelegateProvider.resourceUrlWhitelist} and {@link
24381 * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
24382 */
24383
24384/**
24385 * @ngdoc provider
24386 * @name $sceDelegateProvider
24387 * @this
24388 *
24389 * @description
24390 *
24391 * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
24392 * $sceDelegate} service.  This allows one to get/set the whitelists and blacklists used to ensure
24393 * that the URLs used for sourcing Angular templates are safe.  Refer {@link
24394 * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
24395 * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
24396 *
24397 * For the general details about this service in Angular, read the main page for {@link ng.$sce
24398 * Strict Contextual Escaping (SCE)}.
24399 *
24400 * **Example**:  Consider the following case. <a name="example"></a>
24401 *
24402 * - your app is hosted at url `http://myapp.example.com/`
24403 * - but some of your templates are hosted on other domains you control such as
24404 *   `http://srv01.assets.example.com/`, `http://srv02.assets.example.com/`, etc.
24405 * - and you have an open redirect at `http://myapp.example.com/clickThru?...`.
24406 *
24407 * Here is what a secure configuration for this scenario might look like:
24408 *
24409 * ```
24410 *  angular.module('myApp', []).config(function($sceDelegateProvider) {
24411 *    $sceDelegateProvider.resourceUrlWhitelist([
24412 *      // Allow same origin resource loads.
24413 *      'self',
24414 *      // Allow loading from our assets domain.  Notice the difference between * and **.
24415 *      'http://srv*.assets.example.com/**'
24416 *    ]);
24417 *
24418 *    // The blacklist overrides the whitelist so the open redirect here is blocked.
24419 *    $sceDelegateProvider.resourceUrlBlacklist([
24420 *      'http://myapp.example.com/clickThru**'
24421 *    ]);
24422 *  });
24423 * ```
24424 */
24425
24426function $SceDelegateProvider() {
24427  this.SCE_CONTEXTS = SCE_CONTEXTS;
24428
24429  // Resource URLs can also be trusted by policy.
24430  var resourceUrlWhitelist = ['self'],
24431      resourceUrlBlacklist = [];
24432
24433  /**
24434   * @ngdoc method
24435   * @name $sceDelegateProvider#resourceUrlWhitelist
24436   * @kind function
24437   *
24438   * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
24439   *    provided.  This must be an array or null.  A snapshot of this array is used so further
24440   *    changes to the array are ignored.
24441   *
24442   *    Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
24443   *    allowed in this array.
24444   *
24445   *    <div class="alert alert-warning">
24446   *    **Note:** an empty whitelist array will block all URLs!
24447   *    </div>
24448   *
24449   * @return {Array} the currently set whitelist array.
24450   *
24451   * The **default value** when no whitelist has been explicitly set is `['self']` allowing only
24452   * same origin resource requests.
24453   *
24454   * @description
24455   * Sets/Gets the whitelist of trusted resource URLs.
24456   */
24457  this.resourceUrlWhitelist = function(value) {
24458    if (arguments.length) {
24459      resourceUrlWhitelist = adjustMatchers(value);
24460    }
24461    return resourceUrlWhitelist;
24462  };
24463
24464  /**
24465   * @ngdoc method
24466   * @name $sceDelegateProvider#resourceUrlBlacklist
24467   * @kind function
24468   *
24469   * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
24470   *    provided.  This must be an array or null.  A snapshot of this array is used so further
24471   *    changes to the array are ignored.
24472   *
24473   *    Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
24474   *    allowed in this array.
24475   *
24476   *    The typical usage for the blacklist is to **block
24477   *    [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
24478   *    these would otherwise be trusted but actually return content from the redirected domain.
24479   *
24480   *    Finally, **the blacklist overrides the whitelist** and has the final say.
24481   *
24482   * @return {Array} the currently set blacklist array.
24483   *
24484   * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
24485   * is no blacklist.)
24486   *
24487   * @description
24488   * Sets/Gets the blacklist of trusted resource URLs.
24489   */
24490
24491  this.resourceUrlBlacklist = function(value) {
24492    if (arguments.length) {
24493      resourceUrlBlacklist = adjustMatchers(value);
24494    }
24495    return resourceUrlBlacklist;
24496  };
24497
24498  this.$get = ['$injector', function($injector) {
24499
24500    var htmlSanitizer = function htmlSanitizer(html) {
24501      throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
24502    };
24503
24504    if ($injector.has('$sanitize')) {
24505      htmlSanitizer = $injector.get('$sanitize');
24506    }
24507
24508
24509    function matchUrl(matcher, parsedUrl) {
24510      if (matcher === 'self') {
24511        return urlIsSameOrigin(parsedUrl);
24512      } else {
24513        // definitely a regex.  See adjustMatchers()
24514        return !!matcher.exec(parsedUrl.href);
24515      }
24516    }
24517
24518    function isResourceUrlAllowedByPolicy(url) {
24519      var parsedUrl = urlResolve(url.toString());
24520      var i, n, allowed = false;
24521      // Ensure that at least one item from the whitelist allows this url.
24522      for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {
24523        if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {
24524          allowed = true;
24525          break;
24526        }
24527      }
24528      if (allowed) {
24529        // Ensure that no item from the blacklist blocked this url.
24530        for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {
24531          if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {
24532            allowed = false;
24533            break;
24534          }
24535        }
24536      }
24537      return allowed;
24538    }
24539
24540    function generateHolderType(Base) {
24541      var holderType = function TrustedValueHolderType(trustedValue) {
24542        this.$$unwrapTrustedValue = function() {
24543          return trustedValue;
24544        };
24545      };
24546      if (Base) {
24547        holderType.prototype = new Base();
24548      }
24549      holderType.prototype.valueOf = function sceValueOf() {
24550        return this.$$unwrapTrustedValue();
24551      };
24552      holderType.prototype.toString = function sceToString() {
24553        return this.$$unwrapTrustedValue().toString();
24554      };
24555      return holderType;
24556    }
24557
24558    var trustedValueHolderBase = generateHolderType(),
24559        byType = {};
24560
24561    byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
24562    byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
24563    byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);
24564    byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
24565    byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);
24566
24567    /**
24568     * @ngdoc method
24569     * @name $sceDelegate#trustAs
24570     *
24571     * @description
24572     * Returns an object that is trusted by angular for use in specified strict
24573     * contextual escaping contexts (such as ng-bind-html, ng-include, any src
24574     * attribute interpolation, any dom event binding attribute interpolation
24575     * such as for onclick,  etc.) that uses the provided value.
24576     * See {@link ng.$sce $sce} for enabling strict contextual escaping.
24577     *
24578     * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
24579     *   resourceUrl, html, js and css.
24580     * @param {*} value The value that that should be considered trusted/safe.
24581     * @returns {*} A value that can be used to stand in for the provided `value` in places
24582     * where Angular expects a $sce.trustAs() return value.
24583     */
24584    function trustAs(type, trustedValue) {
24585      var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
24586      if (!Constructor) {
24587        throw $sceMinErr('icontext',
24588            'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
24589            type, trustedValue);
24590      }
24591      if (trustedValue === null || isUndefined(trustedValue) || trustedValue === '') {
24592        return trustedValue;
24593      }
24594      // All the current contexts in SCE_CONTEXTS happen to be strings.  In order to avoid trusting
24595      // mutable objects, we ensure here that the value passed in is actually a string.
24596      if (typeof trustedValue !== 'string') {
24597        throw $sceMinErr('itype',
24598            'Attempted to trust a non-string value in a content requiring a string: Context: {0}',
24599            type);
24600      }
24601      return new Constructor(trustedValue);
24602    }
24603
24604    /**
24605     * @ngdoc method
24606     * @name $sceDelegate#valueOf
24607     *
24608     * @description
24609     * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs
24610     * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link
24611     * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
24612     *
24613     * If the passed parameter is not a value that had been returned by {@link
24614     * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.
24615     *
24616     * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
24617     *      call or anything else.
24618     * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
24619     *     `$sceDelegate.trustAs`} if `value` is the result of such a call.  Otherwise, returns
24620     *     `value` unchanged.
24621     */
24622    function valueOf(maybeTrusted) {
24623      if (maybeTrusted instanceof trustedValueHolderBase) {
24624        return maybeTrusted.$$unwrapTrustedValue();
24625      } else {
24626        return maybeTrusted;
24627      }
24628    }
24629
24630    /**
24631     * @ngdoc method
24632     * @name $sceDelegate#getTrusted
24633     *
24634     * @description
24635     * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and
24636     * returns the originally supplied value if the queried context type is a supertype of the
24637     * created type.  If this condition isn't satisfied, throws an exception.
24638     *
24639     * <div class="alert alert-danger">
24640     * Disabling auto-escaping is extremely dangerous, it usually creates a Cross Site Scripting
24641     * (XSS) vulnerability in your application.
24642     * </div>
24643     *
24644     * @param {string} type The kind of context in which this value is to be used.
24645     * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
24646     *     `$sceDelegate.trustAs`} call.
24647     * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
24648     *     `$sceDelegate.trustAs`} if valid in this context.  Otherwise, throws an exception.
24649     */
24650    function getTrusted(type, maybeTrusted) {
24651      if (maybeTrusted === null || isUndefined(maybeTrusted) || maybeTrusted === '') {
24652        return maybeTrusted;
24653      }
24654      var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
24655      if (constructor && maybeTrusted instanceof constructor) {
24656        return maybeTrusted.$$unwrapTrustedValue();
24657      }
24658      // If we get here, then we may only take one of two actions.
24659      // 1. sanitize the value for the requested type, or
24660      // 2. throw an exception.
24661      if (type === SCE_CONTEXTS.RESOURCE_URL) {
24662        if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
24663          return maybeTrusted;
24664        } else {
24665          throw $sceMinErr('insecurl',
24666              'Blocked loading resource from url not allowed by $sceDelegate policy.  URL: {0}',
24667              maybeTrusted.toString());
24668        }
24669      } else if (type === SCE_CONTEXTS.HTML) {
24670        return htmlSanitizer(maybeTrusted);
24671      }
24672      throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
24673    }
24674
24675    return { trustAs: trustAs,
24676             getTrusted: getTrusted,
24677             valueOf: valueOf };
24678  }];
24679}
24680
24681
24682/**
24683 * @ngdoc provider
24684 * @name $sceProvider
24685 * @this
24686 *
24687 * @description
24688 *
24689 * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service.
24690 * -   enable/disable Strict Contextual Escaping (SCE) in a module
24691 * -   override the default implementation with a custom delegate
24692 *
24693 * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.
24694 */
24695
24696/**
24697 * @ngdoc service
24698 * @name $sce
24699 * @kind function
24700 *
24701 * @description
24702 *
24703 * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.
24704 *
24705 * # Strict Contextual Escaping
24706 *
24707 * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
24708 * contexts to result in a value that is marked as safe to use for that context.  One example of
24709 * such a context is binding arbitrary html controlled by the user via `ng-bind-html`.  We refer
24710 * to these contexts as privileged or SCE contexts.
24711 *
24712 * As of version 1.2, Angular ships with SCE enabled by default.
24713 *
24714 * Note:  When enabled (the default), IE<11 in quirks mode is not supported.  In this mode, IE<11 allow
24715 * one to execute arbitrary javascript by the use of the expression() syntax.  Refer
24716 * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
24717 * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
24718 * to the top of your HTML document.
24719 *
24720 * SCE assists in writing code in a way that (a) is secure by default and (b) makes auditing for
24721 * security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
24722 *
24723 * Here's an example of a binding in a privileged context:
24724 *
24725 * ```
24726 * <input ng-model="userHtml" aria-label="User input">
24727 * <div ng-bind-html="userHtml"></div>
24728 * ```
24729 *
24730 * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user.  With SCE
24731 * disabled, this application allows the user to render arbitrary HTML into the DIV.
24732 * In a more realistic example, one may be rendering user comments, blog articles, etc. via
24733 * bindings.  (HTML is just one example of a context where rendering user controlled input creates
24734 * security vulnerabilities.)
24735 *
24736 * For the case of HTML, you might use a library, either on the client side, or on the server side,
24737 * to sanitize unsafe HTML before binding to the value and rendering it in the document.
24738 *
24739 * How would you ensure that every place that used these types of bindings was bound to a value that
24740 * was sanitized by your library (or returned as safe for rendering by your server?)  How can you
24741 * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
24742 * properties/fields and forgot to update the binding to the sanitized value?
24743 *
24744 * To be secure by default, you want to ensure that any such bindings are disallowed unless you can
24745 * determine that something explicitly says it's safe to use a value for binding in that
24746 * context.  You can then audit your code (a simple grep would do) to ensure that this is only done
24747 * for those values that you can easily tell are safe - because they were received from your server,
24748 * sanitized by your library, etc.  You can organize your codebase to help with this - perhaps
24749 * allowing only the files in a specific directory to do this.  Ensuring that the internal API
24750 * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
24751 *
24752 * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}
24753 * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to
24754 * obtain values that will be accepted by SCE / privileged contexts.
24755 *
24756 *
24757 * ## How does it work?
24758 *
24759 * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
24760 * $sce.getTrusted(context, value)} rather than to the value directly.  Directives use {@link
24761 * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
24762 * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
24763 *
24764 * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
24765 * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}.  Here's the actual code (slightly
24766 * simplified):
24767 *
24768 * ```
24769 * var ngBindHtmlDirective = ['$sce', function($sce) {
24770 *   return function(scope, element, attr) {
24771 *     scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
24772 *       element.html(value || '');
24773 *     });
24774 *   };
24775 * }];
24776 * ```
24777 *
24778 * ## Impact on loading templates
24779 *
24780 * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as
24781 * `templateUrl`'s specified by {@link guide/directive directives}.
24782 *
24783 * By default, Angular only loads templates from the same domain and protocol as the application
24784 * document.  This is done by calling {@link ng.$sce#getTrustedResourceUrl
24785 * $sce.getTrustedResourceUrl} on the template URL.  To load templates from other domains and/or
24786 * protocols, you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
24787 * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
24788 *
24789 * *Please note*:
24790 * The browser's
24791 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
24792 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
24793 * policy apply in addition to this and may further restrict whether the template is successfully
24794 * loaded.  This means that without the right CORS policy, loading templates from a different domain
24795 * won't work on all browsers.  Also, loading templates from `file://` URL does not work on some
24796 * browsers.
24797 *
24798 * ## This feels like too much overhead
24799 *
24800 * It's important to remember that SCE only applies to interpolation expressions.
24801 *
24802 * If your expressions are constant literals, they're automatically trusted and you don't need to
24803 * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
24804 * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
24805 *
24806 * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
24807 * through {@link ng.$sce#getTrusted $sce.getTrusted}.  SCE doesn't play a role here.
24808 *
24809 * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
24810 * templates in `ng-include` from your application's domain without having to even know about SCE.
24811 * It blocks loading templates from other domains or loading templates over http from an https
24812 * served document.  You can change these by setting your own custom {@link
24813 * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link
24814 * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs.
24815 *
24816 * This significantly reduces the overhead.  It is far easier to pay the small overhead and have an
24817 * application that's secure and can be audited to verify that with much more ease than bolting
24818 * security onto an application later.
24819 *
24820 * <a name="contexts"></a>
24821 * ## What trusted context types are supported?
24822 *
24823 * | Context             | Notes          |
24824 * |---------------------|----------------|
24825 * | `$sce.HTML`         | For HTML that's safe to source into the application.  The {@link ng.directive:ngBindHtml ngBindHtml} directive uses this context for bindings. If an unsafe value is encountered and the {@link ngSanitize $sanitize} module is present this will sanitize the value instead of throwing an error. |
24826 * | `$sce.CSS`          | For CSS that's safe to source into the application.  Currently unused.  Feel free to use it in your own directives. |
24827 * | `$sce.URL`          | For URLs that are safe to follow as links.  Currently unused (`<a href=` and `<img src=` sanitize their urls and don't constitute an SCE context. |
24828 * | `$sce.RESOURCE_URL` | For URLs that are not only safe to follow as links, but whose contents are also safe to include in your application.  Examples include `ng-include`, `src` / `ngSrc` bindings for tags other than `IMG`, `VIDEO`, `AUDIO`, `SOURCE`, and `TRACK` (e.g. `IFRAME`, `OBJECT`, etc.)  <br><br>Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. |
24829 * | `$sce.JS`           | For JavaScript that is safe to execute in your application's context.  Currently unused.  Feel free to use it in your own directives. |
24830 *
24831 * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
24832 *
24833 *  Each element in these arrays must be one of the following:
24834 *
24835 *  - **'self'**
24836 *    - The special **string**, `'self'`, can be used to match against all URLs of the **same
24837 *      domain** as the application document using the **same protocol**.
24838 *  - **String** (except the special value `'self'`)
24839 *    - The string is matched against the full *normalized / absolute URL* of the resource
24840 *      being tested (substring matches are not good enough.)
24841 *    - There are exactly **two wildcard sequences** - `*` and `**`.  All other characters
24842 *      match themselves.
24843 *    - `*`: matches zero or more occurrences of any character other than one of the following 6
24844 *      characters: '`:`', '`/`', '`.`', '`?`', '`&`' and '`;`'.  It's a useful wildcard for use
24845 *      in a whitelist.
24846 *    - `**`: matches zero or more occurrences of *any* character.  As such, it's not
24847 *      appropriate for use in a scheme, domain, etc. as it would match too much.  (e.g.
24848 *      http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
24849 *      not have been the intention.)  Its usage at the very end of the path is ok.  (e.g.
24850 *      http://foo.example.com/templates/**).
24851 *  - **RegExp** (*see caveat below*)
24852 *    - *Caveat*:  While regular expressions are powerful and offer great flexibility,  their syntax
24853 *      (and all the inevitable escaping) makes them *harder to maintain*.  It's easy to
24854 *      accidentally introduce a bug when one updates a complex expression (imho, all regexes should
24855 *      have good test coverage).  For instance, the use of `.` in the regex is correct only in a
24856 *      small number of cases.  A `.` character in the regex used when matching the scheme or a
24857 *      subdomain could be matched against a `:` or literal `.` that was likely not intended.   It
24858 *      is highly recommended to use the string patterns and only fall back to regular expressions
24859 *      as a last resort.
24860 *    - The regular expression must be an instance of RegExp (i.e. not a string.)  It is
24861 *      matched against the **entire** *normalized / absolute URL* of the resource being tested
24862 *      (even when the RegExp did not have the `^` and `$` codes.)  In addition, any flags
24863 *      present on the RegExp (such as multiline, global, ignoreCase) are ignored.
24864 *    - If you are generating your JavaScript from some other templating engine (not
24865 *      recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
24866 *      remember to escape your regular expression (and be aware that you might need more than
24867 *      one level of escaping depending on your templating engine and the way you interpolated
24868 *      the value.)  Do make use of your platform's escaping mechanism as it might be good
24869 *      enough before coding your own.  E.g. Ruby has
24870 *      [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)
24871 *      and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).
24872 *      Javascript lacks a similar built in function for escaping.  Take a look at Google
24873 *      Closure library's [goog.string.regExpEscape(s)](
24874 *      http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962).
24875 *
24876 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example.
24877 *
24878 * ## Show me an example using SCE.
24879 *
24880 * <example module="mySceApp" deps="angular-sanitize.js" name="sce-service">
24881 * <file name="index.html">
24882 *   <div ng-controller="AppController as myCtrl">
24883 *     <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
24884 *     <b>User comments</b><br>
24885 *     By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when
24886 *     $sanitize is available.  If $sanitize isn't available, this results in an error instead of an
24887 *     exploit.
24888 *     <div class="well">
24889 *       <div ng-repeat="userComment in myCtrl.userComments">
24890 *         <b>{{userComment.name}}</b>:
24891 *         <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
24892 *         <br>
24893 *       </div>
24894 *     </div>
24895 *   </div>
24896 * </file>
24897 *
24898 * <file name="script.js">
24899 *   angular.module('mySceApp', ['ngSanitize'])
24900 *     .controller('AppController', ['$http', '$templateCache', '$sce',
24901 *       function AppController($http, $templateCache, $sce) {
24902 *         var self = this;
24903 *         $http.get('test_data.json', {cache: $templateCache}).success(function(userComments) {
24904 *           self.userComments = userComments;
24905 *         });
24906 *         self.explicitlyTrustedHtml = $sce.trustAsHtml(
24907 *             '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
24908 *             'sanitization.&quot;">Hover over this text.</span>');
24909 *       }]);
24910 * </file>
24911 *
24912 * <file name="test_data.json">
24913 * [
24914 *   { "name": "Alice",
24915 *     "htmlComment":
24916 *         "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
24917 *   },
24918 *   { "name": "Bob",
24919 *     "htmlComment": "<i>Yes!</i>  Am I the only other one?"
24920 *   }
24921 * ]
24922 * </file>
24923 *
24924 * <file name="protractor.js" type="protractor">
24925 *   describe('SCE doc demo', function() {
24926 *     it('should sanitize untrusted values', function() {
24927 *       expect(element.all(by.css('.htmlComment')).first().getAttribute('innerHTML'))
24928 *           .toBe('<span>Is <i>anyone</i> reading this?</span>');
24929 *     });
24930 *
24931 *     it('should NOT sanitize explicitly trusted values', function() {
24932 *       expect(element(by.id('explicitlyTrustedHtml')).getAttribute('innerHTML')).toBe(
24933 *           '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
24934 *           'sanitization.&quot;">Hover over this text.</span>');
24935 *     });
24936 *   });
24937 * </file>
24938 * </example>
24939 *
24940 *
24941 *
24942 * ## Can I disable SCE completely?
24943 *
24944 * Yes, you can.  However, this is strongly discouraged.  SCE gives you a lot of security benefits
24945 * for little coding overhead.  It will be much harder to take an SCE disabled application and
24946 * either secure it on your own or enable SCE at a later stage.  It might make sense to disable SCE
24947 * for cases where you have a lot of existing code that was written before SCE was introduced and
24948 * you're migrating them a module at a time.
24949 *
24950 * That said, here's how you can completely disable SCE:
24951 *
24952 * ```
24953 * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
24954 *   // Completely disable SCE.  For demonstration purposes only!
24955 *   // Do not use in new projects.
24956 *   $sceProvider.enabled(false);
24957 * });
24958 * ```
24959 *
24960 */
24961
24962function $SceProvider() {
24963  var enabled = true;
24964
24965  /**
24966   * @ngdoc method
24967   * @name $sceProvider#enabled
24968   * @kind function
24969   *
24970   * @param {boolean=} value If provided, then enables/disables SCE.
24971   * @return {boolean} true if SCE is enabled, false otherwise.
24972   *
24973   * @description
24974   * Enables/disables SCE and returns the current value.
24975   */
24976  this.enabled = function(value) {
24977    if (arguments.length) {
24978      enabled = !!value;
24979    }
24980    return enabled;
24981  };
24982
24983
24984  /* Design notes on the default implementation for SCE.
24985   *
24986   * The API contract for the SCE delegate
24987   * -------------------------------------
24988   * The SCE delegate object must provide the following 3 methods:
24989   *
24990   * - trustAs(contextEnum, value)
24991   *     This method is used to tell the SCE service that the provided value is OK to use in the
24992   *     contexts specified by contextEnum.  It must return an object that will be accepted by
24993   *     getTrusted() for a compatible contextEnum and return this value.
24994   *
24995   * - valueOf(value)
24996   *     For values that were not produced by trustAs(), return them as is.  For values that were
24997   *     produced by trustAs(), return the corresponding input value to trustAs.  Basically, if
24998   *     trustAs is wrapping the given values into some type, this operation unwraps it when given
24999   *     such a value.
25000   *
25001   * - getTrusted(contextEnum, value)
25002   *     This function should return the a value that is safe to use in the context specified by
25003   *     contextEnum or throw and exception otherwise.
25004   *
25005   * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be
25006   * opaque or wrapped in some holder object.  That happens to be an implementation detail.  For
25007   * instance, an implementation could maintain a registry of all trusted objects by context.  In
25008   * such a case, trustAs() would return the same object that was passed in.  getTrusted() would
25009   * return the same object passed in if it was found in the registry under a compatible context or
25010   * throw an exception otherwise.  An implementation might only wrap values some of the time based
25011   * on some criteria.  getTrusted() might return a value and not throw an exception for special
25012   * constants or objects even if not wrapped.  All such implementations fulfill this contract.
25013   *
25014   *
25015   * A note on the inheritance model for SCE contexts
25016   * ------------------------------------------------
25017   * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types.  This
25018   * is purely an implementation details.
25019   *
25020   * The contract is simply this:
25021   *
25022   *     getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
25023   *     will also succeed.
25024   *
25025   * Inheritance happens to capture this in a natural way.  In some future, we
25026   * may not use inheritance anymore.  That is OK because no code outside of
25027   * sce.js and sceSpecs.js would need to be aware of this detail.
25028   */
25029
25030  this.$get = ['$parse', '$sceDelegate', function(
25031                $parse,   $sceDelegate) {
25032    // Prereq: Ensure that we're not running in IE<11 quirks mode.  In that mode, IE < 11 allow
25033    // the "expression(javascript expression)" syntax which is insecure.
25034    if (enabled && msie < 8) {
25035      throw $sceMinErr('iequirks',
25036        'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' +
25037        'mode.  You can fix this by adding the text <!doctype html> to the top of your HTML ' +
25038        'document.  See http://docs.angularjs.org/api/ng.$sce for more information.');
25039    }
25040
25041    var sce = shallowCopy(SCE_CONTEXTS);
25042
25043    /**
25044     * @ngdoc method
25045     * @name $sce#isEnabled
25046     * @kind function
25047     *
25048     * @return {Boolean} true if SCE is enabled, false otherwise.  If you want to set the value, you
25049     * have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
25050     *
25051     * @description
25052     * Returns a boolean indicating if SCE is enabled.
25053     */
25054    sce.isEnabled = function() {
25055      return enabled;
25056    };
25057    sce.trustAs = $sceDelegate.trustAs;
25058    sce.getTrusted = $sceDelegate.getTrusted;
25059    sce.valueOf = $sceDelegate.valueOf;
25060
25061    if (!enabled) {
25062      sce.trustAs = sce.getTrusted = function(type, value) { return value; };
25063      sce.valueOf = identity;
25064    }
25065
25066    /**
25067     * @ngdoc method
25068     * @name $sce#parseAs
25069     *
25070     * @description
25071     * Converts Angular {@link guide/expression expression} into a function.  This is like {@link
25072     * ng.$parse $parse} and is identical when the expression is a literal constant.  Otherwise, it
25073     * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
25074     * *result*)}
25075     *
25076     * @param {string} type The kind of SCE context in which this result will be used.
25077     * @param {string} expression String expression to compile.
25078     * @returns {function(context, locals)} a function which represents the compiled expression:
25079     *
25080     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
25081     *      are evaluated against (typically a scope object).
25082     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
25083     *      `context`.
25084     */
25085    sce.parseAs = function sceParseAs(type, expr) {
25086      var parsed = $parse(expr);
25087      if (parsed.literal && parsed.constant) {
25088        return parsed;
25089      } else {
25090        return $parse(expr, function(value) {
25091          return sce.getTrusted(type, value);
25092        });
25093      }
25094    };
25095
25096    /**
25097     * @ngdoc method
25098     * @name $sce#trustAs
25099     *
25100     * @description
25101     * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.  As such,
25102     * returns an object that is trusted by angular for use in specified strict contextual
25103     * escaping contexts (such as ng-bind-html, ng-include, any src attribute
25104     * interpolation, any dom event binding attribute interpolation such as for onclick,  etc.)
25105     * that uses the provided value.  See * {@link ng.$sce $sce} for enabling strict contextual
25106     * escaping.
25107     *
25108     * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
25109     *   resourceUrl, html, js and css.
25110     * @param {*} value The value that that should be considered trusted/safe.
25111     * @returns {*} A value that can be used to stand in for the provided `value` in places
25112     * where Angular expects a $sce.trustAs() return value.
25113     */
25114
25115    /**
25116     * @ngdoc method
25117     * @name $sce#trustAsHtml
25118     *
25119     * @description
25120     * Shorthand method.  `$sce.trustAsHtml(value)` →
25121     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
25122     *
25123     * @param {*} value The value to trustAs.
25124     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml
25125     *     $sce.getTrustedHtml(value)} to obtain the original value.  (privileged directives
25126     *     only accept expressions that are either literal constants or are the
25127     *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
25128     */
25129
25130    /**
25131     * @ngdoc method
25132     * @name $sce#trustAsUrl
25133     *
25134     * @description
25135     * Shorthand method.  `$sce.trustAsUrl(value)` →
25136     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
25137     *
25138     * @param {*} value The value to trustAs.
25139     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl
25140     *     $sce.getTrustedUrl(value)} to obtain the original value.  (privileged directives
25141     *     only accept expressions that are either literal constants or are the
25142     *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
25143     */
25144
25145    /**
25146     * @ngdoc method
25147     * @name $sce#trustAsResourceUrl
25148     *
25149     * @description
25150     * Shorthand method.  `$sce.trustAsResourceUrl(value)` →
25151     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
25152     *
25153     * @param {*} value The value to trustAs.
25154     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl
25155     *     $sce.getTrustedResourceUrl(value)} to obtain the original value.  (privileged directives
25156     *     only accept expressions that are either literal constants or are the return
25157     *     value of {@link ng.$sce#trustAs $sce.trustAs}.)
25158     */
25159
25160    /**
25161     * @ngdoc method
25162     * @name $sce#trustAsJs
25163     *
25164     * @description
25165     * Shorthand method.  `$sce.trustAsJs(value)` →
25166     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
25167     *
25168     * @param {*} value The value to trustAs.
25169     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs
25170     *     $sce.getTrustedJs(value)} to obtain the original value.  (privileged directives
25171     *     only accept expressions that are either literal constants or are the
25172     *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
25173     */
25174
25175    /**
25176     * @ngdoc method
25177     * @name $sce#getTrusted
25178     *
25179     * @description
25180     * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}.  As such,
25181     * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the
25182     * originally supplied value if the queried context type is a supertype of the created type.
25183     * If this condition isn't satisfied, throws an exception.
25184     *
25185     * @param {string} type The kind of context in which this value is to be used.
25186     * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`}
25187     *                         call.
25188     * @returns {*} The value the was originally provided to
25189     *              {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context.
25190     *              Otherwise, throws an exception.
25191     */
25192
25193    /**
25194     * @ngdoc method
25195     * @name $sce#getTrustedHtml
25196     *
25197     * @description
25198     * Shorthand method.  `$sce.getTrustedHtml(value)` →
25199     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
25200     *
25201     * @param {*} value The value to pass to `$sce.getTrusted`.
25202     * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
25203     */
25204
25205    /**
25206     * @ngdoc method
25207     * @name $sce#getTrustedCss
25208     *
25209     * @description
25210     * Shorthand method.  `$sce.getTrustedCss(value)` →
25211     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
25212     *
25213     * @param {*} value The value to pass to `$sce.getTrusted`.
25214     * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
25215     */
25216
25217    /**
25218     * @ngdoc method
25219     * @name $sce#getTrustedUrl
25220     *
25221     * @description
25222     * Shorthand method.  `$sce.getTrustedUrl(value)` →
25223     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
25224     *
25225     * @param {*} value The value to pass to `$sce.getTrusted`.
25226     * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
25227     */
25228
25229    /**
25230     * @ngdoc method
25231     * @name $sce#getTrustedResourceUrl
25232     *
25233     * @description
25234     * Shorthand method.  `$sce.getTrustedResourceUrl(value)` →
25235     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
25236     *
25237     * @param {*} value The value to pass to `$sceDelegate.getTrusted`.
25238     * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
25239     */
25240
25241    /**
25242     * @ngdoc method
25243     * @name $sce#getTrustedJs
25244     *
25245     * @description
25246     * Shorthand method.  `$sce.getTrustedJs(value)` →
25247     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
25248     *
25249     * @param {*} value The value to pass to `$sce.getTrusted`.
25250     * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
25251     */
25252
25253    /**
25254     * @ngdoc method
25255     * @name $sce#parseAsHtml
25256     *
25257     * @description
25258     * Shorthand method.  `$sce.parseAsHtml(expression string)` →
25259     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`}
25260     *
25261     * @param {string} expression String expression to compile.
25262     * @returns {function(context, locals)} a function which represents the compiled expression:
25263     *
25264     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
25265     *      are evaluated against (typically a scope object).
25266     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
25267     *      `context`.
25268     */
25269
25270    /**
25271     * @ngdoc method
25272     * @name $sce#parseAsCss
25273     *
25274     * @description
25275     * Shorthand method.  `$sce.parseAsCss(value)` →
25276     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`}
25277     *
25278     * @param {string} expression String expression to compile.
25279     * @returns {function(context, locals)} a function which represents the compiled expression:
25280     *
25281     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
25282     *      are evaluated against (typically a scope object).
25283     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
25284     *      `context`.
25285     */
25286
25287    /**
25288     * @ngdoc method
25289     * @name $sce#parseAsUrl
25290     *
25291     * @description
25292     * Shorthand method.  `$sce.parseAsUrl(value)` →
25293     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`}
25294     *
25295     * @param {string} expression String expression to compile.
25296     * @returns {function(context, locals)} a function which represents the compiled expression:
25297     *
25298     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
25299     *      are evaluated against (typically a scope object).
25300     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
25301     *      `context`.
25302     */
25303
25304    /**
25305     * @ngdoc method
25306     * @name $sce#parseAsResourceUrl
25307     *
25308     * @description
25309     * Shorthand method.  `$sce.parseAsResourceUrl(value)` →
25310     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`}
25311     *
25312     * @param {string} expression String expression to compile.
25313     * @returns {function(context, locals)} a function which represents the compiled expression:
25314     *
25315     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
25316     *      are evaluated against (typically a scope object).
25317     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
25318     *      `context`.
25319     */
25320
25321    /**
25322     * @ngdoc method
25323     * @name $sce#parseAsJs
25324     *
25325     * @description
25326     * Shorthand method.  `$sce.parseAsJs(value)` →
25327     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`}
25328     *
25329     * @param {string} expression String expression to compile.
25330     * @returns {function(context, locals)} a function which represents the compiled expression:
25331     *
25332     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
25333     *      are evaluated against (typically a scope object).
25334     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
25335     *      `context`.
25336     */
25337
25338    // Shorthand delegations.
25339    var parse = sce.parseAs,
25340        getTrusted = sce.getTrusted,
25341        trustAs = sce.trustAs;
25342
25343    forEach(SCE_CONTEXTS, function(enumValue, name) {
25344      var lName = lowercase(name);
25345      sce[camelCase('parse_as_' + lName)] = function(expr) {
25346        return parse(enumValue, expr);
25347      };
25348      sce[camelCase('get_trusted_' + lName)] = function(value) {
25349        return getTrusted(enumValue, value);
25350      };
25351      sce[camelCase('trust_as_' + lName)] = function(value) {
25352        return trustAs(enumValue, value);
25353      };
25354    });
25355
25356    return sce;
25357  }];
25358}
25359
25360/* exported $SnifferProvider */
25361
25362/**
25363 * !!! This is an undocumented "private" service !!!
25364 *
25365 * @name $sniffer
25366 * @requires $window
25367 * @requires $document
25368 * @this
25369 *
25370 * @property {boolean} history Does the browser support html5 history api ?
25371 * @property {boolean} transitions Does the browser support CSS transition events ?
25372 * @property {boolean} animations Does the browser support CSS animation events ?
25373 *
25374 * @description
25375 * This is very simple implementation of testing browser's features.
25376 */
25377function $SnifferProvider() {
25378  this.$get = ['$window', '$document', function($window, $document) {
25379    var eventSupport = {},
25380        // Chrome Packaged Apps are not allowed to access `history.pushState`.
25381        // If not sandboxed, they can be detected by the presence of `chrome.app.runtime`
25382        // (see https://developer.chrome.com/apps/api_index). If sandboxed, they can be detected by
25383        // the presence of an extension runtime ID and the absence of other Chrome runtime APIs
25384        // (see https://developer.chrome.com/apps/manifest/sandbox).
25385        isChromePackagedApp =
25386            $window.chrome &&
25387            ($window.chrome.app && $window.chrome.app.runtime ||
25388                !$window.chrome.app && $window.chrome.runtime && $window.chrome.runtime.id),
25389        hasHistoryPushState = !isChromePackagedApp && $window.history && $window.history.pushState,
25390        android =
25391          toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
25392        boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
25393        document = $document[0] || {},
25394        vendorPrefix,
25395        vendorRegex = /^(Moz|webkit|ms)(?=[A-Z])/,
25396        bodyStyle = document.body && document.body.style,
25397        transitions = false,
25398        animations = false,
25399        match;
25400
25401    if (bodyStyle) {
25402      for (var prop in bodyStyle) {
25403        if ((match = vendorRegex.exec(prop))) {
25404          vendorPrefix = match[0];
25405          vendorPrefix = vendorPrefix[0].toUpperCase() + vendorPrefix.substr(1);
25406          break;
25407        }
25408      }
25409
25410      if (!vendorPrefix) {
25411        vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit';
25412      }
25413
25414      transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
25415      animations  = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
25416
25417      if (android && (!transitions ||  !animations)) {
25418        transitions = isString(bodyStyle.webkitTransition);
25419        animations = isString(bodyStyle.webkitAnimation);
25420      }
25421    }
25422
25423
25424    return {
25425      // Android has history.pushState, but it does not update location correctly
25426      // so let's not use the history API at all.
25427      // http://code.google.com/p/android/issues/detail?id=17471
25428      // https://github.com/angular/angular.js/issues/904
25429
25430      // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
25431      // so let's not use the history API also
25432      // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
25433      history: !!(hasHistoryPushState && !(android < 4) && !boxee),
25434      hasEvent: function(event) {
25435        // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
25436        // it. In particular the event is not fired when backspace or delete key are pressed or
25437        // when cut operation is performed.
25438        // IE10+ implements 'input' event but it erroneously fires under various situations,
25439        // e.g. when placeholder changes, or a form is focused.
25440        if (event === 'input' && msie <= 11) return false;
25441
25442        if (isUndefined(eventSupport[event])) {
25443          var divElm = document.createElement('div');
25444          eventSupport[event] = 'on' + event in divElm;
25445        }
25446
25447        return eventSupport[event];
25448      },
25449      csp: csp(),
25450      vendorPrefix: vendorPrefix,
25451      transitions: transitions,
25452      animations: animations,
25453      android: android
25454    };
25455  }];
25456}
25457
25458var $templateRequestMinErr = minErr('$compile');
25459
25460/**
25461 * @ngdoc provider
25462 * @name $templateRequestProvider
25463 * @this
25464 *
25465 * @description
25466 * Used to configure the options passed to the {@link $http} service when making a template request.
25467 *
25468 * For example, it can be used for specifying the "Accept" header that is sent to the server, when
25469 * requesting a template.
25470 */
25471function $TemplateRequestProvider() {
25472
25473  var httpOptions;
25474
25475  /**
25476   * @ngdoc method
25477   * @name $templateRequestProvider#httpOptions
25478   * @description
25479   * The options to be passed to the {@link $http} service when making the request.
25480   * You can use this to override options such as the "Accept" header for template requests.
25481   *
25482   * The {@link $templateRequest} will set the `cache` and the `transformResponse` properties of the
25483   * options if not overridden here.
25484   *
25485   * @param {string=} value new value for the {@link $http} options.
25486   * @returns {string|self} Returns the {@link $http} options when used as getter and self if used as setter.
25487   */
25488  this.httpOptions = function(val) {
25489    if (val) {
25490      httpOptions = val;
25491      return this;
25492    }
25493    return httpOptions;
25494  };
25495
25496  /**
25497   * @ngdoc service
25498   * @name $templateRequest
25499   *
25500   * @description
25501   * The `$templateRequest` service runs security checks then downloads the provided template using
25502   * `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request
25503   * fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the
25504   * exception can be thwarted by setting the 2nd parameter of the function to true). Note that the
25505   * contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted
25506   * when `tpl` is of type string and `$templateCache` has the matching entry.
25507   *
25508   * If you want to pass custom options to the `$http` service, such as setting the Accept header you
25509   * can configure this via {@link $templateRequestProvider#httpOptions}.
25510   *
25511   * @param {string|TrustedResourceUrl} tpl The HTTP request template URL
25512   * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
25513   *
25514   * @return {Promise} a promise for the HTTP response data of the given URL.
25515   *
25516   * @property {number} totalPendingRequests total amount of pending template requests being downloaded.
vendor: 4,448 bytes, lines 25517-25644
25517   */
25518  this.$get = ['$templateCache', '$http', '$q', '$sce', function($templateCache, $http, $q, $sce) {
25519
25520    function handleRequestFn(tpl, ignoreRequestError) {
25521      handleRequestFn.totalPendingRequests++;
25522
25523      // We consider the template cache holds only trusted templates, so
25524      // there's no need to go through whitelisting again for keys that already
25525      // are included in there. This also makes Angular accept any script
25526      // directive, no matter its name. However, we still need to unwrap trusted
25527      // types.
25528      if (!isString(tpl) || isUndefined($templateCache.get(tpl))) {
25529        tpl = $sce.getTrustedResourceUrl(tpl);
25530      }
25531
25532      var transformResponse = $http.defaults && $http.defaults.transformResponse;
25533
25534      if (isArray(transformResponse)) {
25535        transformResponse = transformResponse.filter(function(transformer) {
25536          return transformer !== defaultHttpResponseTransform;
25537        });
25538      } else if (transformResponse === defaultHttpResponseTransform) {
25539        transformResponse = null;
25540      }
25541
25542      return $http.get(tpl, extend({
25543          cache: $templateCache,
25544          transformResponse: transformResponse
25545        }, httpOptions)
25546        )['finally'](function() {
25547          handleRequestFn.totalPendingRequests--;
25548        })
25549        .then(function(response) {
25550          $templateCache.put(tpl, response.data);
25551          return response.data;
25552        }, handleError);
25553
25554      function handleError(resp) {
25555        if (!ignoreRequestError) {
25556          throw $templateRequestMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})',
25557            tpl, resp.status, resp.statusText);
25558        }
25559        return $q.reject(resp);
25560      }
25561    }
25562
25563    handleRequestFn.totalPendingRequests = 0;
25564
25565    return handleRequestFn;
25566  }];
25567}
25568
25569/** @this */
25570function $$TestabilityProvider() {
25571  this.$get = ['$rootScope', '$browser', '$location',
25572       function($rootScope,   $browser,   $location) {
25573
25574    /**
25575     * @name $testability
25576     *
25577     * @description
25578     * The private $$testability service provides a collection of methods for use when debugging
25579     * or by automated test and debugging tools.
25580     */
25581    var testability = {};
25582
25583    /**
25584     * @name $$testability#findBindings
25585     *
25586     * @description
25587     * Returns an array of elements that are bound (via ng-bind or {{}})
25588     * to expressions matching the input.
25589     *
25590     * @param {Element} element The element root to search from.
25591     * @param {string} expression The binding expression to match.
25592     * @param {boolean} opt_exactMatch If true, only returns exact matches
25593     *     for the expression. Filters and whitespace are ignored.
25594     */
25595    testability.findBindings = function(element, expression, opt_exactMatch) {
25596      var bindings = element.getElementsByClassName('ng-binding');
25597      var matches = [];
25598      forEach(bindings, function(binding) {
25599        var dataBinding = angular.element(binding).data('$binding');
25600        if (dataBinding) {
25601          forEach(dataBinding, function(bindingName) {
25602            if (opt_exactMatch) {
25603              var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)');
25604              if (matcher.test(bindingName)) {
25605                matches.push(binding);
25606              }
25607            } else {
25608              if (bindingName.indexOf(expression) !== -1) {
25609                matches.push(binding);
25610              }
25611            }
25612          });
25613        }
25614      });
25615      return matches;
25616    };
25617
25618    /**
25619     * @name $$testability#findModels
25620     *
25621     * @description
25622     * Returns an array of elements that are two-way found via ng-model to
25623     * expressions matching the input.
25624     *
25625     * @param {Element} element The element root to search from.
25626     * @param {string} expression The model expression to match.
25627     * @param {boolean} opt_exactMatch If true, only returns exact matches
25628     *     for the expression.
25629     */
25630    testability.findModels = function(element, expression, opt_exactMatch) {
25631      var prefixes = ['ng-', 'data-ng-', 'ng\\:'];
25632      for (var p = 0; p < prefixes.length; ++p) {
25633        var attributeEquals = opt_exactMatch ? '=' : '*=';
25634        var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]';
25635        var elements = element.querySelectorAll(selector);
25636        if (elements.length) {
25637          return elements;
25638        }
25639      }
25640    };
25641
25642    /**
25643     * @name $$testability#getLocation
25644     *
25645     * @description
25646     * Shortcut for getting the location in a browser agnostic way. Returns
25647     *     the path, search, and hash. (e.g. /path?a=b#hash)
25648     */
25649    testability.getLocation = function() {
25650      return $location.url();
25651    };
25652
25653    /**
25654     * @name $$testability#setLocation
25655     *
25656     * @description
25657     * Shortcut for navigating to a location without doing a full page reload.
25658     *
25659     * @param {string} url The location url (path, search and hash,
25660     *     e.g. /path?a=b#hash) to go to.
25661     */
25662    testability.setLocation = function(url) {
25663      if (url !== $location.url()) {
25664        $location.url(url);
25665        $rootScope.$digest();
25666      }
25667    };
25668
25669    /**
25670     * @name $$testability#whenStable
25671     *
25672     * @description
25673     * Calls the callback when $timeout and $http requests are completed.
25674     *
25675     * @param {function} callback
25676     */
25677    testability.whenStable = function(callback) {
25678      $browser.notifyWhenNoOutstandingRequests(callback);
25679    };
25680
25681    return testability;
25682  }];
25683}
25684
25685/** @this */
25686function $TimeoutProvider() {
25687  this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler',
25688       function($rootScope,   $browser,   $q,   $$q,   $exceptionHandler) {
25689
25690    var deferreds = {};
25691
25692
25693     /**
25694      * @ngdoc service
25695      * @name $timeout
25696      *
25697      * @description
25698      * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
25699      * block and delegates any exceptions to
25700      * {@link ng.$exceptionHandler $exceptionHandler} service.
25701      *
25702      * The return value of calling `$timeout` is a promise, which will be resolved when
25703      * the delay has passed and the timeout function, if provided, is executed.
25704      *
25705      * To cancel a timeout request, call `$timeout.cancel(promise)`.
25706      *
25707      * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
25708      * synchronously flush the queue of deferred functions.
25709      *
25710      * If you only want a promise that will be resolved after some specified delay
25711      * then you can call `$timeout` without the `fn` function.
25712      *
25713      * @param {function()=} fn A function, whose execution should be delayed.
25714      * @param {number=} [delay=0] Delay in milliseconds.
25715      * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
25716      *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
25717      * @param {...*=} Pass additional parameters to the executed function.
25718      * @returns {Promise} Promise that will be resolved when the timeout is reached. The promise
25719      *   will be resolved with the return value of the `fn` function.
25720      *
25721      */
25722    function timeout(fn, delay, invokeApply) {
25723      if (!isFunction(fn)) {
25724        invokeApply = delay;
25725        delay = fn;
25726        fn = noop;
25727      }
25728
25729      var args = sliceArgs(arguments, 3),
25730          skipApply = (isDefined(invokeApply) && !invokeApply),
25731          deferred = (skipApply ? $$q : $q).defer(),
25732          promise = deferred.promise,
25733          timeoutId;
25734
25735      timeoutId = $browser.defer(function() {
25736        try {
25737          deferred.resolve(fn.apply(null, args));
25738        } catch (e) {
25739          deferred.reject(e);
25740          $exceptionHandler(e);
25741        } finally {
25742          delete deferreds[promise.$$timeoutId];
25743        }
25744
25745        if (!skipApply) $rootScope.$apply();
25746      }, delay);
25747
25748      promise.$$timeoutId = timeoutId;
25749      deferreds[timeoutId] = deferred;
25750
25751      return promise;
25752    }
25753
25754
25755     /**
25756      * @ngdoc method
25757      * @name $timeout#cancel
25758      *
25759      * @description
25760      * Cancels a task associated with the `promise`. As a result of this, the promise will be
25761      * resolved with a rejection.
25762      *
25763      * @param {Promise=} promise Promise returned by the `$timeout` function.
25764      * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
25765      *   canceled.
25766      */
25767    timeout.cancel = function(promise) {
25768      if (promise && promise.$$timeoutId in deferreds) {
25769        deferreds[promise.$$timeoutId].reject('canceled');
25770        delete deferreds[promise.$$timeoutId];
25771        return $browser.defer.cancel(promise.$$timeoutId);
25772      }
25773      return false;
25774    };
25775
25776    return timeout;
25777  }];
25778}
25779
25780// NOTE:  The usage of window and document instead of $window and $document here is
25781// deliberate.  This service depends on the specific behavior of anchor nodes created by the
25782// browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and
25783// cause us to break tests.  In addition, when the browser resolves a URL for XHR, it
25784// doesn't know about mocked locations and resolves URLs to the real document - which is
25785// exactly the behavior needed here.  There is little value is mocking these out for this
25786// service.
25787var urlParsingNode = window.document.createElement('a');
25788var originUrl = urlResolve(window.location.href);
25789
25790
25791/**
25792 *
25793 * Implementation Notes for non-IE browsers
25794 * ----------------------------------------
25795 * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM,
25796 * results both in the normalizing and parsing of the URL.  Normalizing means that a relative
25797 * URL will be resolved into an absolute URL in the context of the application document.
25798 * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
25799 * properties are all populated to reflect the normalized URL.  This approach has wide
25800 * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc.  See
25801 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
25802 *
25803 * Implementation Notes for IE
25804 * ---------------------------
25805 * IE <= 10 normalizes the URL when assigned to the anchor node similar to the other
25806 * browsers.  However, the parsed components will not be set if the URL assigned did not specify
25807 * them.  (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.)  We
25808 * work around that by performing the parsing in a 2nd step by taking a previously normalized
25809 * URL (e.g. by assigning to a.href) and assigning it a.href again.  This correctly populates the
25810 * properties such as protocol, hostname, port, etc.
25811 *
25812 * References:
25813 *   http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
25814 *   http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
25815 *   http://url.spec.whatwg.org/#urlutils
25816 *   https://github.com/angular/angular.js/pull/2902
25817 *   http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
25818 *
25819 * @kind function
25820 * @param {string} url The URL to be parsed.
25821 * @description Normalizes and parses a URL.
25822 * @returns {object} Returns the normalized URL as a dictionary.
25823 *
25824 *   | member name   | Description    |
25825 *   |---------------|----------------|
25826 *   | href          | A normalized version of the provided URL if it was not an absolute URL |
25827 *   | protocol      | The protocol including the trailing colon                              |
25828 *   | host          | The host and port (if the port is non-default) of the normalizedUrl    |
25829 *   | search        | The search params, minus the question mark                             |
25830 *   | hash          | The hash string, minus the hash symbol
25831 *   | hostname      | The hostname
25832 *   | port          | The port, without ":"
25833 *   | pathname      | The pathname, beginning with "/"
25834 *
25835 */
25836function urlResolve(url) {
25837  var href = url;
25838
25839  if (msie) {
25840    // Normalize before parse.  Refer Implementation Notes on why this is
25841    // done in two steps on IE.
25842    urlParsingNode.setAttribute('href', href);
25843    href = urlParsingNode.href;
25844  }
25845
25846  urlParsingNode.setAttribute('href', href);
25847
25848  // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
25849  return {
25850    href: urlParsingNode.href,
25851    protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
25852    host: urlParsingNode.host,
25853    search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
25854    hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
25855    hostname: urlParsingNode.hostname,
25856    port: urlParsingNode.port,
25857    pathname: (urlParsingNode.pathname.charAt(0) === '/')
25858      ? urlParsingNode.pathname
25859      : '/' + urlParsingNode.pathname
25860  };
25861}
25862
25863/**
25864 * Parse a request URL and determine whether this is a same-origin request as the application document.
25865 *
25866 * @param {string|object} requestUrl The url of the request as a string that will be resolved
25867 * or a parsed URL object.
25868 * @returns {boolean} Whether the request is for the same origin as the application document.
25869 */
25870function urlIsSameOrigin(requestUrl) {
25871  var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl;
25872  return (parsed.protocol === originUrl.protocol &&
25873          parsed.host === originUrl.host);
25874}
25875
25876/**
25877 * @ngdoc service
25878 * @name $window
25879 * @this
25880 *
25881 * @description
25882 * A reference to the browser's `window` object. While `window`
25883 * is globally available in JavaScript, it causes testability problems, because
25884 * it is a global variable. In angular we always refer to it through the
25885 * `$window` service, so it may be overridden, removed or mocked for testing.
25886 *
25887 * Expressions, like the one defined for the `ngClick` directive in the example
25888 * below, are evaluated with respect to the current scope.  Therefore, there is
25889 * no risk of inadvertently coding in a dependency on a global value in such an
25890 * expression.
25891 *
25892 * @example
25893   <example module="windowExample" name="window-service">
25894     <file name="index.html">
25895       <script>
25896         angular.module('windowExample', [])
25897           .controller('ExampleController', ['$scope', '$window', function($scope, $window) {
25898             $scope.greeting = 'Hello, World!';
25899             $scope.doGreeting = function(greeting) {
25900               $window.alert(greeting);
25901             };
25902           }]);
25903       </script>
25904       <div ng-controller="ExampleController">
25905         <input type="text" ng-model="greeting" aria-label="greeting" />
25906         <button ng-click="doGreeting(greeting)">ALERT</button>
25907       </div>
25908     </file>
25909     <file name="protractor.js" type="protractor">
25910      it('should display the greeting in the input box', function() {
25911       element(by.model('greeting')).sendKeys('Hello, E2E Tests');
25912       // If we click the button it will block the test runner
25913       // element(':button').click();
25914      });
25915     </file>
25916   </example>
25917 */
25918function $WindowProvider() {
25919  this.$get = valueFn(window);
25920}
25921
25922/**
25923 * @name $$cookieReader
25924 * @requires $document
25925 *
25926 * @description
25927 * This is a private service for reading cookies used by $http and ngCookies
25928 *
25929 * @return {Object} a key/value map of the current cookies
25930 */
25931function $$CookieReader($document) {
25932  var rawDocument = $document[0] || {};
25933  var lastCookies = {};
25934  var lastCookieString = '';
25935
25936  function safeGetCookie(rawDocument) {
25937    try {
25938      return rawDocument.cookie || '';
25939    } catch (e) {
25940      return '';
25941    }
25942  }
25943
25944  function safeDecodeURIComponent(str) {
25945    try {
25946      return decodeURIComponent(str);
25947    } catch (e) {
25948      return str;
25949    }
25950  }
25951
25952  return function() {
25953    var cookieArray, cookie, i, index, name;
25954    var currentCookieString = safeGetCookie(rawDocument);
25955
25956    if (currentCookieString !== lastCookieString) {
25957      lastCookieString = currentCookieString;
25958      cookieArray = lastCookieString.split('; ');
25959      lastCookies = {};
25960
25961      for (i = 0; i < cookieArray.length; i++) {
25962        cookie = cookieArray[i];
25963        index = cookie.indexOf('=');
25964        if (index > 0) { //ignore nameless cookies
25965          name = safeDecodeURIComponent(cookie.substring(0, index));
25966          // the first value that is seen for a cookie is the most
25967          // specific one.  values for the same cookie name that
25968          // follow are for less specific paths.
25969          if (isUndefined(lastCookies[name])) {
25970            lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1));
25971          }
25972        }
25973      }
25974    }
25975    return lastCookies;
25976  };
25977}
25978
25979$$CookieReader.$inject = ['$document'];
25980
25981/** @this */
25982function $$CookieReaderProvider() {
25983  this.$get = $$CookieReader;
25984}
25985
25986/* global currencyFilter: true,
25987 dateFilter: true,
25988 filterFilter: true,
25989 jsonFilter: true,
25990 limitToFilter: true,
25991 lowercaseFilter: true,
25992 numberFilter: true,
25993 orderByFilter: true,
25994 uppercaseFilter: true,
25995 */
25996
25997/**
25998 * @ngdoc provider
25999 * @name $filterProvider
26000 * @description
26001 *
26002 * Filters are just functions which transform input to an output. However filters need to be
26003 * Dependency Injected. To achieve this a filter definition consists of a factory function which is
26004 * annotated with dependencies and is responsible for creating a filter function.
26005 *
26006 * <div class="alert alert-warning">
26007 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
26008 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
26009 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
26010 * (`myapp_subsection_filterx`).
26011 * </div>
26012 *
26013 * ```js
26014 *   // Filter registration
26015 *   function MyModule($provide, $filterProvider) {
26016 *     // create a service to demonstrate injection (not always needed)
26017 *     $provide.value('greet', function(name){
26018 *       return 'Hello ' + name + '!';
26019 *     });
26020 *
26021 *     // register a filter factory which uses the
26022 *     // greet service to demonstrate DI.
26023 *     $filterProvider.register('greet', function(greet){
26024 *       // return the filter function which uses the greet service
26025 *       // to generate salutation
26026 *       return function(text) {
26027 *         // filters need to be forgiving so check input validity
26028 *         return text && greet(text) || text;
26029 *       };
26030 *     });
26031 *   }
26032 * ```
26033 *
26034 * The filter function is registered with the `$injector` under the filter name suffix with
26035 * `Filter`.
26036 *
26037 * ```js
26038 *   it('should be the same instance', inject(
26039 *     function($filterProvider) {
26040 *       $filterProvider.register('reverse', function(){
26041 *         return ...;
26042 *       });
26043 *     },
26044 *     function($filter, reverseFilter) {
26045 *       expect($filter('reverse')).toBe(reverseFilter);
26046 *     });
26047 * ```
26048 *
26049 *
26050 * For more information about how angular filters work, and how to create your own filters, see
26051 * {@link guide/filter Filters} in the Angular Developer Guide.
26052 */
26053
26054/**
26055 * @ngdoc service
26056 * @name $filter
26057 * @kind function
26058 * @description
26059 * Filters are used for formatting data displayed to the user.
26060 *
26061 * They can be used in view templates, controllers or services.Angular comes
26062 * with a collection of [built-in filters](api/ng/filter), but it is easy to
26063 * define your own as well.
26064 *
26065 * The general syntax in templates is as follows:
26066 *
26067 * ```html
26068 * {{ expression [| filter_name[:parameter_value] ... ] }}
26069 * ```
26070 *
26071 * @param {String} name Name of the filter function to retrieve
26072 * @return {Function} the filter function
26073 * @example
26074   <example name="$filter" module="filterExample">
26075     <file name="index.html">
26076       <div ng-controller="MainCtrl">
26077        <h3>{{ originalText }}</h3>
26078        <h3>{{ filteredText }}</h3>
26079       </div>
26080     </file>
26081
26082     <file name="script.js">
26083      angular.module('filterExample', [])
26084      .controller('MainCtrl', function($scope, $filter) {
26085        $scope.originalText = 'hello';
26086        $scope.filteredText = $filter('uppercase')($scope.originalText);
26087      });
26088     </file>
26089   </example>
26090  */
26091$FilterProvider.$inject = ['$provide'];
26092/** @this */
26093function $FilterProvider($provide) {
26094  var suffix = 'Filter';
26095
26096  /**
26097   * @ngdoc method
26098   * @name $filterProvider#register
26099   * @param {string|Object} name Name of the filter function, or an object map of filters where
26100   *    the keys are the filter names and the values are the filter factories.
26101   *
26102   *    <div class="alert alert-warning">
26103   *    **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
26104   *    Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
26105   *    your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
26106   *    (`myapp_subsection_filterx`).
26107   *    </div>
26108    * @param {Function} factory If the first argument was a string, a factory function for the filter to be registered.
26109   * @returns {Object} Registered filter instance, or if a map of filters was provided then a map
26110   *    of the registered filter instances.
26111   */
26112  function register(name, factory) {
26113    if (isObject(name)) {
26114      var filters = {};
26115      forEach(name, function(filter, key) {
26116        filters[key] = register(key, filter);
26117      });
26118      return filters;
26119    } else {
26120      return $provide.factory(name + suffix, factory);
26121    }
26122  }
26123  this.register = register;
26124
26125  this.$get = ['$injector', function($injector) {
26126    return function(name) {
26127      return $injector.get(name + suffix);
26128    };
26129  }];
26130
26131  ////////////////////////////////////////
26132
26133  /* global
26134    currencyFilter: false,
26135    dateFilter: false,
26136    filterFilter: false,
26137    jsonFilter: false,
26138    limitToFilter: false,
26139    lowercaseFilter: false,
26140    numberFilter: false,
26141    orderByFilter: false,
26142    uppercaseFilter: false
26143  */
26144
26145  register('currency', currencyFilter);
26146  register('date', dateFilter);
26147  register('filter', filterFilter);
26148  register('json', jsonFilter);
26149  register('limitTo', limitToFilter);
26150  register('lowercase', lowercaseFilter);
26151  register('number', numberFilter);
26152  register('orderBy', orderByFilter);
26153  register('uppercase', uppercaseFilter);
26154}
26155
26156/**
26157 * @ngdoc filter
26158 * @name filter
26159 * @kind function
26160 *
26161 * @description
26162 * Selects a subset of items from `array` and returns it as a new array.
26163 *
26164 * @param {Array} array The source array.
26165 * @param {string|Object|function()} expression The predicate to be used for selecting items from
26166 *   `array`.
26167 *
26168 *   Can be one of:
26169 *
26170 *   - `string`: The string is used for matching against the contents of the `array`. All strings or
26171 *     objects with string properties in `array` that match this string will be returned. This also
26172 *     applies to nested object properties.
26173 *     The predicate can be negated by prefixing the string with `!`.
26174 *
26175 *   - `Object`: A pattern object can be used to filter specific properties on objects contained
26176 *     by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
26177 *     which have property `name` containing "M" and property `phone` containing "1". A special
26178 *     property name (`$` by default) can be used (e.g. as in `{$: "text"}`) to accept a match
26179 *     against any property of the object or its nested object properties. That's equivalent to the
26180 *     simple substring match with a `string` as described above. The special property name can be
26181 *     overwritten, using the `anyPropertyKey` parameter.
26182 *     The predicate can be negated by prefixing the string with `!`.
26183 *     For example `{name: "!M"}` predicate will return an array of items which have property `name`
26184 *     not containing "M".
26185 *
26186 *     Note that a named property will match properties on the same level only, while the special
26187 *     `$` property will match properties on the same level or deeper. E.g. an array item like
26188 *     `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but
26189 *     **will** be matched by `{$: 'John'}`.
26190 *
26191 *   - `function(value, index, array)`: A predicate function can be used to write arbitrary filters.
26192 *     The function is called for each element of the array, with the element, its index, and
26193 *     the entire array itself as arguments.
26194 *
26195 *     The final result is an array of those elements that the predicate returned true for.
26196 *
26197 * @param {function(actual, expected)|true|false} [comparator] Comparator which is used in
26198 *     determining if the expected value (from the filter expression) and actual value (from
26199 *     the object in the array) should be considered a match.
26200 *
26201 *   Can be one of:
26202 *
26203 *   - `function(actual, expected)`:
26204 *     The function will be given the object value and the predicate value to compare and
26205 *     should return true if both values should be considered equal.
26206 *
26207 *   - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`.
26208 *     This is essentially strict comparison of expected and actual.
26209 *
26210 *   - `false`: A short hand for a function which will look for a substring match in a case
26211 *     insensitive way. Primitive values are converted to strings. Objects are not compared against
26212 *     primitives, unless they have a custom `toString` method (e.g. `Date` objects).
26213 *
26214 *
26215 *   Defaults to `false`.
26216 *
26217 * @param {string} [anyPropertyKey] The special property name that matches against any property.
26218 *     By default `$`.
26219 *
26220 * @example
26221   <example name="filter-filter">
26222     <file name="index.html">
26223       <div ng-init="friends = [{name:'John', phone:'555-1276'},
26224                                {name:'Mary', phone:'800-BIG-MARY'},
26225                                {name:'Mike', phone:'555-4321'},
26226                                {name:'Adam', phone:'555-5678'},
26227                                {name:'Julie', phone:'555-8765'},
26228                                {name:'Juliette', phone:'555-5678'}]"></div>
26229
26230       <label>Search: <input ng-model="searchText"></label>
26231       <table id="searchTextResults">
26232         <tr><th>
26232Name</th><th>Phone</th></tr>
26233         <tr ng-repeat="friend in friends | filter:searchText">
26234           <td>{{friend.name}}</td>
26235           <td>{{friend.phone}}</td>
26236         </tr>
26237       </table>
26238       <hr>
26239       <label>Any: <input ng-model="search.$"></label> <br>
26240       <label>Name only <input ng-model="search.name"></label><br>
26241       <label>Phone only <input ng-model="search.phone"></label><br>
26242       <label>Equality <input type="checkbox" ng-model="strict"></label><br>
26243       <table id="searchObjResults">
26244         <tr><th>Name</th><th>Phone</th></tr>
26245         <tr ng-repeat="friendObj in friends | filter:search:strict">
26246           <td>{{friendObj.name}}</td>
26247           <td>{{friendObj.phone}}</td>
26248         </tr>
26249       </table>
26250     </file>
26251     <file name="protractor.js" type="protractor">
26252       var expectFriendNames = function(expectedNames, key) {
26253         element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) {
26254           arr.forEach(function(wd, i) {
26255             expect(wd.getText()).toMatch(expectedNames[i]);
26256           });
26257         });
26258       };
26259
26260       it('should search across all fields when filtering with a string', function() {
26261         var searchText = element(by.model('searchText'));
26262         searchText.clear();
26263         searchText.sendKeys('m');
26264         expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend');
26265
26266         searchText.clear();
26267         searchText.sendKeys('76');
26268         expectFriendNames(['John', 'Julie'], 'friend');
26269       });
26270
26271       it('should search in specific fields when filtering with a predicate object', function() {
26272         var searchAny = element(by.model('search.$'));
26273         searchAny.clear();
26274         searchAny.sendKeys('i');
26275         expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj');
26276       });
26277       it('should use a equal comparison when comparator is true', function() {
26278         var searchName = element(by.model('search.name'));
26279         var strict = element(by.model('strict'));
26280         searchName.clear();
26281         searchName.sendKeys('Julie');
26282         strict.click();
26283         expectFriendNames(['Julie'], 'friendObj');
26284       });
26285     </file>
26286   </example>
26287 */
26288
26289function filterFilter() {
26290  return function(array, expression, comparator, anyPropertyKey) {
26291    if (!isArrayLike(array)) {
26292      if (array == null) {
26293        return array;
26294      } else {
26295        throw minErr('filter')('notarray', 'Expected array but received: {0}', array);
26296      }
26297    }
26298
26299    anyPropertyKey = anyPropertyKey || '$';
26300    var expressionType = getTypeForFilter(expression);
26301    var predicateFn;
26302    var matchAgainstAnyProp;
26303
26304    switch (expressionType) {
26305      case 'function':
26306        predicateFn = expression;
26307        break;
26308      case 'boolean':
26309      case 'null':
26310      case 'number':
26311      case 'string':
26312        matchAgainstAnyProp = true;
26313        // falls through
26314      case 'object':
26315        predicateFn = createPredicateFn(expression, comparator, anyPropertyKey, matchAgainstAnyProp);
26316        break;
26317      default:
26318        return array;
26319    }
26320
26321    return Array.prototype.filter.call(array, predicateFn);
26322  };
26323}
26324
26325// Helper functions for `filterFilter`
26326function createPredicateFn(expression, comparator, anyPropertyKey, matchAgainstAnyProp) {
26327  var shouldMatchPrimitives = isObject(expression) && (anyPropertyKey in expression);
26328  var predicateFn;
26329
26330  if (comparator === true) {
26331    comparator = equals;
26332  } else if (!isFunction(comparator)) {
26333    comparator = function(actual, expected) {
26334      if (isUndefined(actual)) {
26335        // No substring matching against `undefined`
26336        return false;
26337      }
26338      if ((actual === null) || (expected === null)) {
26339        // No substring matching against `null`; only match against `null`
26340        return actual === expected;
26341      }
26342      if (isObject(expected) || (isObject(actual) && !hasCustomToString(actual))) {
26343        // Should not compare primitives against objects, unless they have custom `toString` method
26344        return false;
26345      }
26346
26347      actual = lowercase('' + actual);
26348      expected = lowercase('' + expected);
26349      return actual.indexOf(expected) !== -1;
26350    };
26351  }
26352
26353  predicateFn = function(item) {
26354    if (shouldMatchPrimitives && !isObject(item)) {
26355      return deepCompare(item, expression[anyPropertyKey], comparator, anyPropertyKey, false);
26356    }
26357    return deepCompare(item, expression, comparator, anyPropertyKey, matchAgainstAnyProp);
26358  };
26359
26360  return predicateFn;
26361}
26362
26363function deepCompare(actual, expected, comparator, anyPropertyKey, matchAgainstAnyProp, dontMatchWholeObject) {
26364  var actualType = getTypeForFilter(actual);
26365  var expectedType = getTypeForFilter(expected);
26366
26367  if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
26368    return !deepCompare(actual, expected.substring(1), comparator, anyPropertyKey, matchAgainstAnyProp);
26369  } else if (isArray(actual)) {
26370    // In case `actual` is an array, consider it a match
26371    // if ANY of it's items matches `expected`
26372    return actual.some(function(item) {
26373      return deepCompare(item, expected, comparator, anyPropertyKey, matchAgainstAnyProp);
26374    });
26375  }
26376
26377  switch (actualType) {
26378    case 'object':
26379      var key;
26380      if (matchAgainstAnyProp) {
26381        for (key in actual) {
26382          if ((key.charAt(0) !== '$') &&
vendor: 2,309 bytes, lines 26382-26447
26382 deepCompare(actual[key], expected, comparator, anyPropertyKey, true)) {
26383            return true;
26384          }
26385        }
26386        return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, anyPropertyKey, false);
26387      } else if (expectedType === 'object') {
26388        for (key in expected) {
26389          var expectedVal = expected[key];
26390          if (isFunction(expectedVal) || isUndefined(expectedVal)) {
26391            continue;
26392          }
26393
26394          var matchAnyProperty = key === anyPropertyKey;
26395          var actualVal = matchAnyProperty ? actual : actual[key];
26396          if (!deepCompare(actualVal, expectedVal, comparator, anyPropertyKey, matchAnyProperty, matchAnyProperty)) {
26397            return false;
26398          }
26399        }
26400        return true;
26401      } else {
26402        return comparator(actual, expected);
26403      }
26404    case 'function':
26405      return false;
26406    default:
26407      return comparator(actual, expected);
26408  }
26409}
26410
26411// Used for easily differentiating between `null` and actual `object`
26412function getTypeForFilter(val) {
26413  return (val === null) ? 'null' : typeof val;
26414}
26415
26416var MAX_DIGITS = 22;
26417var DECIMAL_SEP = '.';
26418var ZERO_CHAR = '0';
26419
26420/**
26421 * @ngdoc filter
26422 * @name currency
26423 * @kind function
26424 *
26425 * @description
26426 * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
26427 * symbol for current locale is used.
26428 *
26429 * @param {number} amount Input to filter.
26430 * @param {string=} symbol Currency symbol or identifier to be displayed.
26431 * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale
26432 * @returns {string} Formatted number.
26433 *
26434 *
26435 * @example
26436   <example module="currencyExample" name="currency-filter">
26437     <file name="index.html">
26438       <script>
26439         angular.module('currencyExample', [])
26440           .controller('ExampleController', ['$scope', function($scope) {
26441             $scope.amount = 1234.56;
26442           }]);
26443       </script>
26444       <div ng-controller="ExampleController">
26445         <input type="number" ng-model="amount" aria-label="amount"> <br>
26446         default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br>
26447         custom currency identifier (USD$): <span id="currency-custom">{{amount | currency:"USD$"}}</span>
26447
26448         no fractions (0): <span id="currency-no-fractions">{{amount | currency:"USD$":0}}</span>
26449       </div>
26450     </file>
26451     <file name="protractor.js" type="protractor">
26452       it('should init with 1234.56', function() {
26453         expect(element(by.id('currency-default')).getText()).toBe('$1,234.56');
26454         expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56');
26455         expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235');
26456       });
26457       it('should update', function() {
26458         if (browser.params.browser === 'safari') {
26459           // Safari does not understand the minus key. See
26460           // https://github.com/angular/protractor/issues/481
26461           return;
26462         }
26463         element(by.model('amount')).clear();
26464         element(by.model('amount')).sendKeys('-1234');
26465         expect(element(by.id('currency-default')).getText()).toBe('-$1,234.00');
26466         expect(element(by.id('currency-custom')).getText()).toBe('-USD$1,234.00');
26467         expect(element(by.id('currency-no-fractions')).getText()).toBe('-USD$1,234');
26468       });
26469     </file>
26470   </example>
26471 */
26472currencyFilter.$inject = ['$locale'];
26473function currencyFilter($locale) {
26474  var formats = $locale.NUMBER_FORMATS;
26475  return function(amount, currencySymbol, fractionSize) {
26476    if (isUndefined(currencySymbol)) {
26477      currencySymbol = formats.CURRENCY_SYM;
26478    }
26479
26480    if (isUndefined(fractionSize)) {
26481      fractionSize = formats.PATTERNS[1].maxFrac;
26482    }
26483
26484    // if null or undefined pass it through
26485    return (amount == null)
26486        ? amount
26487        : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize).
26488            replace(/\u00A4/g
vendor: 13,487 bytes, lines 26488-26891
26488, currencySymbol);
26489  };
26490}
26491
26492/**
26493 * @ngdoc filter
26494 * @name number
26495 * @kind function
26496 *
26497 * @description
26498 * Formats a number as text.
26499 *
26500 * If the input is null or undefined, it will just be returned.
26501 * If the input is infinite (Infinity or -Infinity), the Infinity symbol '∞' or '-∞' is returned, respectively.
26502 * If the input is not a number an empty string is returned.
26503 *
26504 *
26505 * @param {number|string} number Number to format.
26506 * @param {(number|string)=} fractionSize Number of decimal places to round the number to.
26507 * If this is not provided then the fraction size is computed from the current locale's number
26508 * formatting pattern. In the case of the default locale, it will be 3.
26509 * @returns {string} Number rounded to `fractionSize` appropriately formatted based on the current
26510 *                   locale (e.g., in the en_US locale it will have "." as the decimal separator and
26511 *                   include "," group separators after each third digit).
26512 *
26513 * @example
26514   <example module="numberFilterExample" name="number-filter">
26515     <file name="index.html">
26516       <script>
26517         angular.module('numberFilterExample', [])
26518           .controller('ExampleController', ['$scope', function($scope) {
26519             $scope.val = 1234.56789;
26520           }]);
26521       </script>
26522       <div ng-controller="ExampleController">
26523         <label>Enter number: <input ng-model='val'></label><br>
26524         Default formatting: <span id='number-default'>{{val | number}}</span><br>
26525         No fractions: <span>{{val | number:0}}</span><br>
26526         Negative number: <span>{{-val | number:4}}</span>
26527       </div>
26528     </file>
26529     <file name="protractor.js" type="protractor">
26530       it('should format numbers', function() {
26531         expect(element(by.id('number-default')).getText()).toBe('1,234.568');
26532         expect(element(by.binding('val | number:0')).getText()).toBe('1,235');
26533         expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679');
26534       });
26535
26536       it('should update', function() {
26537         element(by.model('val')).clear();
26538         element(by.model('val')).sendKeys('3374.333');
26539         expect(element(by.id('number-default')).getText()).toBe('3,374.333');
26540         expect(element(by.binding('val | number:0')).getText()).toBe('3,374');
26541         expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330');
26542      });
26543     </file>
26544   </example>
26545 */
26546numberFilter.$inject = ['$locale'];
26547function numberFilter($locale) {
26548  var formats = $locale.NUMBER_FORMATS;
26549  return function(number, fractionSize) {
26550
26551    // if null or undefined pass it through
26552    return (number == null)
26553        ? number
26554        : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP,
26555                       fractionSize);
26556  };
26557}
26558
26559/**
26560 * Parse a number (as a string) into three components that can be used
26561 * for formatting the number.
26562 *
26563 * (Significant bits of this parse algorithm came from https://github.com/MikeMcl/big.js/)
26564 *
26565 * @param  {string} numStr The number to parse
26566 * @return {object} An object describing this number, containing the following keys:
26567 *  - d : an array of digits containing leading zeros as necessary
26568 *  - i : the number of the digits in `d` that are to the left of the decimal point
26569 *  - e : the exponent for numbers that would need more than `MAX_DIGITS` digits in `d`
26570 *
26571 */
26572function parse(numStr) {
26573  var exponent = 0, digits, numberOfIntegerDigits;
26574  var i, j, zeros;
26575
26576  // Decimal point?
26577  if ((numberOfIntegerDigits = numStr.indexOf(DECIMAL_SEP)) > -1) {
26578    numStr = numStr.replace(DECIMAL_SEP, '');
26579  }
26580
26581  // Exponential form?
26582  if ((i = numStr.search(/e/i)) > 0) {
26583    // Work out the exponent.
26584    if (numberOfIntegerDigits < 0) numberOfIntegerDigits = i;
26585    numberOfIntegerDigits += +numStr.slice(i + 1);
26586    numStr = numStr.substring(0, i);
26587  } else if (numberOfIntegerDigits < 0) {
26588    // There was no decimal point or exponent so it is an integer.
26589    numberOfIntegerDigits = numStr.length;
26590  }
26591
26592  // Count the number of leading zeros.
26593  for (i = 0; numStr.charAt(i) === ZERO_CHAR; i++) { /* empty */ }
26594
26595  if (i === (zeros = numStr.length)) {
26596    // The digits are all zero.
26597    digits = [0];
26598    numberOfIntegerDigits = 1;
26599  } else {
26600    // Count the number of trailing zeros
26601    zeros--;
26602    while (numStr.charAt(zeros) === ZERO_CHAR) zeros--;
26603
26604    // Trailing zeros are insignificant so ignore them
26605    numberOfIntegerDigits -= i;
26606    digits = [];
26607    // Convert string to array of digits without leading/trailing zeros.
26608    for (j = 0; i <= zeros; i++, j++) {
26609      digits[j] = +numStr.charAt(i);
26610    }
26611  }
26612
26613  // If the number overflows the maximum allowed digits then use an exponent.
26614  if (numberOfIntegerDigits > MAX_DIGITS) {
26615    digits = digits.splice(0, MAX_DIGITS - 1);
26616    exponent = numberOfIntegerDigits - 1;
26617    numberOfIntegerDigits = 1;
26618  }
26619
26620  return { d: digits, e: exponent, i: numberOfIntegerDigits };
26621}
26622
26623/**
26624 * Round the parsed number to the specified number of decimal places
26625 * This function changed the parsedNumber in-place
26626 */
26627function roundNumber(parsedNumber, fractionSize, minFrac, maxFrac) {
26628    var digits = parsedNumber.d;
26629    var fractionLen = digits.length - parsedNumber.i;
26630
26631    // determine fractionSize if it is not specified; `+fractionSize` converts it to a number
26632    fractionSize = (isUndefined(fractionSize)) ? Math.min(Math.max(minFrac, fractionLen), maxFrac) : +fractionSize;
26633
26634    // The index of the digit to where rounding is to occur
26635    var roundAt = fractionSize + parsedNumber.i;
26636    var digit = digits[roundAt];
26637
26638    if (roundAt > 0) {
26639      // Drop fractional digits beyond `roundAt`
26640      digits.splice(Math.max(parsedNumber.i, roundAt));
26641
26642      // Set non-fractional digits beyond `roundAt` to 0
26643      for (var j = roundAt; j < digits.length; j++) {
26644        digits[j] = 0;
26645      }
26646    } else {
26647      // We rounded to zero so reset the parsedNumber
26648      fractionLen = Math.max(0, fractionLen);
26649      parsedNumber.i = 1;
26650      digits.length = Math.max(1, roundAt = fractionSize + 1);
26651      digits[0] = 0;
26652      for (var i = 1; i < roundAt; i++) digits[i] = 0;
26653    }
26654
26655    if (digit >= 5) {
26656      if (roundAt - 1 < 0) {
26657        for (var k = 0; k > roundAt; k--) {
26658          digits.unshift(0);
26659          parsedNumber.i++;
26660        }
26661        digits.unshift(1);
26662        parsedNumber.i++;
26663      } else {
26664        digits[roundAt - 1]++;
26665      }
26666    }
26667
26668    // Pad out with zeros to get the required fraction length
26669    for (; fractionLen < Math.max(0, fractionSize); fractionLen++) digits.push(0);
26670
26671
26672    // Do any carrying, e.g. a digit was rounded up to 10
26673    var carry = digits.reduceRight(function(carry, d, i, digits) {
26674      d = d + carry;
26675      digits[i] = d % 10;
26676      return Math.floor(d / 10);
26677    }, 0);
26678    if (carry) {
26679      digits.unshift(carry);
26680      parsedNumber.i++;
26681    }
26682}
26683
26684/**
26685 * Format a number into a string
26686 * @param  {number} number       The number to format
26687 * @param  {{
26688 *           minFrac, // the minimum number of digits required in the fraction part of the number
26689 *           maxFrac, // the maximum number of digits required in the fraction part of the number
26690 *           gSize,   // number of digits in each group of separated digits
26691 *           lgSize,  // number of digits in the last group of digits before the decimal separator
26692 *           negPre,  // the string to go in front of a negative number (e.g. `-` or `(`))
26693 *           posPre,  // the string to go in front of a positive number
26694 *           negSuf,  // the string to go after a negative number (e.g. `)`)
26695 *           posSuf   // the string to go after a positive number
26696 *         }} pattern
26697 * @param  {string} groupSep     The string to separate groups of number (e.g. `,`)
26698 * @param  {string} decimalSep   The string to act as the decimal separator (e.g. `.`)
26699 * @param  {[type]} fractionSize The size of the fractional part of the number
26700 * @return {string}              The number formatted as a string
26701 */
26702function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
26703
26704  if (!(isString(number) || isNumber(number)) || isNaN(number)) return '';
26705
26706  var isInfinity = !isFinite(number);
26707  var isZero = false;
26708  var numStr = Math.abs(number) + '',
26709      formattedText = '',
26710      parsedNumber;
26711
26712  if (isInfinity) {
26713    formattedText = '\u221e';
26714  } else {
26715    parsedNumber = parse(numStr);
26716
26717    roundNumber(parsedNumber, fractionSize, pattern.minFrac, pattern.maxFrac);
26718
26719    var digits = parsedNumber.d;
26720    var integerLen = parsedNumber.i;
26721    var exponent = parsedNumber.e;
26722    var decimals = [];
26723    isZero = digits.reduce(function(isZero, d) { return isZero && !d; }, true);
26724
26725    // pad zeros for small numbers
26726    while (integerLen < 0) {
26727      digits.unshift(0);
26728      integerLen++;
26729    }
26730
26731    // extract decimals digits
26732    if (integerLen > 0) {
26733      decimals = digits.splice(integerLen, digits.length);
26734    } else {
26735      decimals = digits;
26736      digits = [0];
26737    }
26738
26739    // format the integer digits with grouping separators
26740    var groups = [];
26741    if (digits.length >= pattern.lgSize) {
26742      groups.unshift(digits.splice(-pattern.lgSize, digits.length).join(''));
26743    }
26744    while (digits.length > pattern.gSize) {
26745      groups.unshift(digits.splice(-pattern.gSize, digits.length).join(''));
26746    }
26747    if (digits.length) {
26748      groups.unshift(digits.join(''));
26749    }
26750    formattedText = groups.join(groupSep);
26751
26752    // append the decimal digits
26753    if (decimals.length) {
26754      formattedText += decimalSep + decimals.join('');
26755    }
26756
26757    if (exponent) {
26758      formattedText += 'e+' + exponent;
26759    }
26760  }
26761  if (number < 0 && !isZero) {
26762    return pattern.negPre + formattedText + pattern.negSuf;
26763  } else {
26764    return pattern.posPre + formattedText + pattern.posSuf;
26765  }
26766}
26767
26768function padNumber(num, digits, trim, negWrap) {
26769  var neg = '';
26770  if (num < 0 || (negWrap && num <= 0)) {
26771    if (negWrap) {
26772      num = -num + 1;
26773    } else {
26774      num = -num;
26775      neg = '-';
26776    }
26777  }
26778  num = '' + num;
26779  while (num.length < digits) num = ZERO_CHAR + num;
26780  if (trim) {
26781    num = num.substr(num.length - digits);
26782  }
26783  return neg + num;
26784}
26785
26786
26787function dateGetter(name, size, offset, trim, negWrap) {
26788  offset = offset || 0;
26789  return function(date) {
26790    var value = date['get' + name]();
26791    if (offset > 0 || value > -offset) {
26792      value += offset;
26793    }
26794    if (value === 0 && offset === -12) value = 12;
26795    return padNumber(value, size, trim, negWrap);
26796  };
26797}
26798
26799function dateStrGetter(name, shortForm, standAlone) {
26800  return function(date, formats) {
26801    var value = date['get' + name]();
26802    var propPrefix = (standAlone ? 'STANDALONE' : '') + (shortForm ? 'SHORT' : '');
26803    var get = uppercase(propPrefix + name);
26804
26805    return formats[get][value];
26806  };
26807}
26808
26809function timeZoneGetter(date, formats, offset) {
26810  var zone = -1 * offset;
26811  var paddedZone = (zone >= 0) ? '+' : '';
26812
26813  paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
26814                padNumber(Math.abs(zone % 60), 2);
26815
26816  return paddedZone;
26817}
26818
26819function getFirstThursdayOfYear(year) {
26820    // 0 = index of January
26821    var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay();
26822    // 4 = index of Thursday (+1 to account for 1st = 5)
26823    // 11 = index of *next* Thursday (+1 account for 1st = 12)
26824    return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst);
26825}
26826
26827function getThursdayThisWeek(datetime) {
26828    return new Date(datetime.getFullYear(), datetime.getMonth(),
26829      // 4 = index of Thursday
26830      datetime.getDate() + (4 - datetime.getDay()));
26831}
26832
26833function weekGetter(size) {
26834   return function(date) {
26835      var firstThurs = getFirstThursdayOfYear(date.getFullYear()),
26836         thisThurs = getThursdayThisWeek(date);
26837
26838      var diff = +thisThurs - +firstThurs,
26839         result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week
26840
26841      return padNumber(result, size);
26842   };
26843}
26844
26845function ampmGetter(date, formats) {
26846  return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
26847}
26848
26849function eraGetter(date, formats) {
26850  return date.getFullYear() <= 0 ? formats.ERAS[0] : formats.ERAS[1];
26851}
26852
26853function longEraGetter(date, formats) {
26854  return date.getFullYear() <= 0 ? formats.ERANAMES[0] : formats.ERANAMES[1];
26855}
26856
26857var DATE_FORMATS = {
26858  yyyy: dateGetter('FullYear', 4, 0, false, true),
26859    yy: dateGetter('FullYear', 2, 0, true, true),
26860     y: dateGetter('FullYear', 1, 0, false, true),
26861  MMMM: dateStrGetter('Month'),
26862   MMM: dateStrGetter('Month', true),
26863    MM: dateGetter('Month', 2, 1),
26864     M: dateGetter('Month', 1, 1),
26865  LLLL: dateStrGetter('Month', false, true),
26866    dd: dateGetter('Date', 2),
26867     d: dateGetter('Date', 1),
26868    HH: dateGetter('Hours', 2),
26869     H: dateGetter('Hours', 1),
26870    hh: dateGetter('Hours', 2, -12),
26871     h: dateGetter('Hours', 1, -12),
26872    mm: dateGetter('Minutes', 2),
26873     m: dateGetter('Minutes', 1),
26874    ss: dateGetter('Seconds', 2),
26875     s: dateGetter('Seconds', 1),
26876     // while ISO 8601 requires fractions to be prefixed with `.` or `,`
26877     // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
26878   sss: dateGetter('Milliseconds', 3),
26879  EEEE: dateStrGetter('Day'),
26880   EEE: dateStrGetter('Day', true),
26881     a: ampmGetter,
26882     Z: timeZoneGetter,
26883    ww: weekGetter(2),
26884     w: weekGetter(1),
26885     G: eraGetter,
26886     GG: eraGetter,
26887     GGG: eraGetter,
26888     GGGG: longEraGetter
26889};
26890
26891var DATE_FORMATS_SPLIT = /((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|m+|s+|a|Z|G+|w+))(
26891.*)/,
26892    NUMBER_STRING = /^-?\d+$/;
26893
26894/**
26895 * @ngdoc filter
26896 * @name date
26897 * @kind function
26898 *
26899 * @description
26900 *   Formats `date` to a string based on the requested `format`.
26901 *
26902 *   `format` string can be composed of the following elements:
26903 *
26904 *   * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
26905 *   * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
26906 *   * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
26907 *   * `'MMMM'`: Month in year (January-December)
26908 *   * `'MMM'`: Month in year (Jan-Dec)
26909 *   * `'MM'`: Month in year, padded (01-12)
26910 *   * `'M'`: Month in year (1-12)
26911 *   * `'LLLL'`: Stand-alone month in year (January-December)
26912 *   * `'dd'`: Day in month, padded (01-31)
26913 *   * `'d'`: Day in month (1-31)
26914 *   * `'EEEE'`: Day in Week,(Sunday-Saturday)
26915 *   * `'EEE'`: Day in Week, (Sun-Sat)
26916 *   * `'HH'`: Hour in day, padded (00-23)
26917 *   * `'H'`: Hour in day (0-23)
26918 *   * `'hh'`: Hour in AM/PM, padded (01-12)
26919 *   * `'h'`: Hour in AM/PM, (1-12)
26920 *   * `'mm'`: Minute in hour, padded (00-59)
26921 *   * `'m'`: Minute in hour (0-59)
26922 *   * `'ss'`: Second in minute, padded (00-59)
26923 *   * `'s'`: Second in minute (0-59)
26924 *   * `'sss'`: Millisecond in second, padded (000-999)
26925 *   * `'a'`: AM/PM marker
26926 *   * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
26927 *   * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year
26928 *   * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year
26929 *   * `'G'`, `'GG'`, `'GGG'`: The abbreviated form of the era string (e.g. 'AD')
26930 *   * `'GGGG'`: The long form of the era string (e.g. 'Anno Domini')
26931 *
26932 *   `format` string can also be one of the following predefined
26933 *   {@link guide/i18n localizable formats}:
26934 *
26935 *   * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
26936 *     (e.g. Sep 3, 2010 12:05:08 PM)
26937 *   * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US  locale (e.g. 9/3/10 12:05 PM)
26938 *   * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US  locale
26939 *     (e.g. Friday, September 3, 2010)
26940 *   * `'longDate'`: equivalent to `'MMMM d, y'` for en_US  locale (e.g. September 3, 2010)
26941 *   * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US  locale (e.g. Sep 3, 2010)
26942 *   * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
26943 *   * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM)
26944 *   * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM)
26945 *
26946 *   `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g.
26947 *   `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
26948 *   (e.g. `"h 'o''clock'"`)
vendor: 6,066 bytes, lines 26948-27112
26948.
26949 *
26950 * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
26951 *    number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
26952 *    shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
26953 *    specified in the string input, the time is considered to be in the local timezone.
26954 * @param {string=} format Formatting rules (see Description). If not specified,
26955 *    `mediumDate` is used.
26956 * @param {string=} timezone Timezone to be used for formatting. It understands UTC/GMT and the
26957 *    continental US time zone abbreviations, but for general use, use a time zone offset, for
26958 *    example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
26959 *    If not specified, the timezone of the browser will be used.
26960 * @returns {string} Formatted string or the input if input is not recognized as date/millis.
26961 *
26962 * @example
26963   <example name="filter-date">
26964     <file name="index.html">
26965       <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
26966           <span>{{1288323623006 | date:'medium'}}</span><br>
26967       <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
26968          <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
26969       <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
26970          <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
26971       <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>:
26972          <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br>
26973     </file>
26974     <file name="protractor.js" type="protractor">
26975       it('should format date', function() {
26976         expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
26977            toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
26978         expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
26979            toMatch(/2010-10-2\d \d{2}:\d{2}:\d{2} (-|\+)?\d{4}/);
26980         expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
26981            toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
26982         expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()).
26983            toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/);
26984       });
26985     </file>
26986   </example>
26987 */
26988dateFilter.$inject = ['$locale'];
26989function dateFilter($locale) {
26990
26991
26992  var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
26993                     // 1        2       3         4          5          6          7          8  9     10      11
26994  function jsonStringToDate(string) {
26995    var match;
26996    if ((match = string.match(R_ISO8601_STR))) {
26997      var date = new Date(0),
26998          tzHour = 0,
26999          tzMin  = 0,
27000          dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
27001          timeSetter = match[8] ? date.setUTCHours : date.setHours;
27002
27003      if (match[9]) {
27004        tzHour = toInt(match[9] + match[10]);
27005        tzMin = toInt(match[9] + match[11]);
27006      }
27007      dateSetter.call(date, toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
27008      var h = toInt(match[4] || 0) - tzHour;
27009      var m = toInt(match[5] || 0) - tzMin;
27010      var s = toInt(match[6] || 0);
27011      var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
27012      timeSetter.call(date, h, m, s, ms);
27013      return date;
27014    }
27015    return string;
27016  }
27017
27018
27019  return function(date, format, timezone) {
27020    var text = '',
27021        parts = [],
27022        fn, match;
27023
27024    format = format || 'mediumDate';
27025    format = $locale.DATETIME_FORMATS[format] || format;
27026    if (isString(date)) {
27027      date = NUMBER_STRING.test(date) ? toInt(date) : jsonStringToDate(date);
27028    }
27029
27030    if (isNumber(date)) {
27031      date = new Date(date);
27032    }
27033
27034    if (!isDate(date) || !isFinite(date.getTime())) {
27035      return date;
27036    }
27037
27038    while (format) {
27039      match = DATE_FORMATS_SPLIT.exec(format);
27040      if (match) {
27041        parts = concat(parts, match, 1);
27042        format = parts.pop();
27043      } else {
27044        parts.push(format);
27045        format = null;
27046      }
27047    }
27048
27049    var dateTimezoneOffset = date.getTimezoneOffset();
27050    if (timezone) {
27051      dateTimezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
27052      date = convertTimezoneToLocal(date, timezone, true);
27053    }
27054    forEach(parts, function(value) {
27055      fn = DATE_FORMATS[value];
27056      text += fn ? fn(date, $locale.DATETIME_FORMATS, dateTimezoneOffset)
27057                 : value === '\'\'' ? '\'' : value.replace(/(^'|'$)/g, '').replace(/''/g, '\'');
27058    });
27059
27060    return text;
27061  };
27062}
27063
27064
27065/**
27066 * @ngdoc filter
27067 * @name json
27068 * @kind function
27069 *
27070 * @description
27071 *   Allows you to convert a JavaScript object into JSON string.
27072 *
27073 *   This filter is mostly useful for debugging. When using the double curly {{value}} notation
27074 *   the binding is automatically converted to JSON.
27075 *
27076 * @param {*} object Any JavaScript object (including arrays and primitive types) to filter.
27077 * @param {number=} spacing The number of spaces to use per indentation, defaults to 2.
27078 * @returns {string} JSON string.
27079 *
27080 *
27081 * @example
27082   <example name="filter-json">
27083     <file name="index.html">
27084       <pre id="default-spacing">{{ {'name':'value'} | json }}</pre>
27085       <pre id="custom-spacing">{{ {'name':'value'} | json:4 }}</pre>
27086     </file>
27087     <file name="protractor.js" type="protractor">
27088       it('should jsonify filtered objects', function() {
27089         expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n {2}"name": ?"value"\n}/);
27090         expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n {4}"name": ?"value"\n}/);
27091       });
27092     </file>
27093   </example>
27094 *
27095 */
27096function jsonFilter() {
27097  return function(object, spacing) {
27098    if (isUndefined(spacing)) {
27099        spacing = 2;
27100    }
27101    return toJson(object, spacing);
27102  };
27103}
27104
27105
27106/**
27107 * @ngdoc filter
27108 * @name lowercase
27109 * @kind function
27110 * @description
27111 * Converts string to lowercase.
27112 *
27112 @see angular.lowercase
27113 */
27114var lowercaseFilter = valueFn(lowercase);
27115
27116
27117/**
27118 * @ngdoc filter
27119 * @name uppercase
27120 * @kind function
27121 * @description
27122 * Converts string to uppercase.
27123 * @see angular.uppercase
vendor: 7,356 bytes, lines 27123-27299
27123
27124 */
27125var uppercaseFilter = valueFn(uppercase);
27126
27127/**
27128 * @ngdoc filter
27129 * @name limitTo
27130 * @kind function
27131 *
27132 * @description
27133 * Creates a new array or string containing only a specified number of elements. The elements are
27134 * taken from either the beginning or the end of the source array, string or number, as specified by
27135 * the value and sign (positive or negative) of `limit`. Other array-like objects are also supported
27136 * (e.g. array subclasses, NodeLists, jqLite/jQuery collections etc). If a number is used as input,
27137 * it is converted to a string.
27138 *
27139 * @param {Array|ArrayLike|string|number} input - Array/array-like, string or number to be limited.
27140 * @param {string|number} limit - The length of the returned array or string. If the `limit` number
27141 *     is positive, `limit` number of items from the beginning of the source array/string are copied.
27142 *     If the number is negative, `limit` number  of items from the end of the source array/string
27143 *     are copied. The `limit` will be trimmed if it exceeds `array.length`. If `limit` is undefined,
27144 *     the input will be returned unchanged.
27145 * @param {(string|number)=} begin - Index at which to begin limitation. As a negative index,
27146 *     `begin` indicates an offset from the end of `input`. Defaults to `0`.
27147 * @returns {Array|string} A new sub-array or substring of length `limit` or less if the input had
27148 *     less than `limit` elements.
27149 *
27150 * @example
27151   <example module="limitToExample" name="limit-to-filter">
27152     <file name="index.html">
27153       <script>
27154         angular.module('limitToExample', [])
27155           .controller('ExampleController', ['$scope', function($scope) {
27156             $scope.numbers = [1,2,3,4,5,6,7,8,9];
27157             $scope.letters = "abcdefghi";
27158             $scope.longNumber = 2345432342;
27159             $scope.numLimit = 3;
27160             $scope.letterLimit = 3;
27161             $scope.longNumberLimit = 3;
27162           }]);
27163       </script>
27164       <div ng-controller="ExampleController">
27165         <label>
27166            Limit {{numbers}} to:
27167            <input type="number" step="1" ng-model="numLimit">
27168         </label>
27169         <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
27170         <label>
27171            Limit {{letters}} to:
27172            <input type="number" step="1" ng-model="letterLimit">
27173         </label>
27174         <p>Output letters: {{ letters | limitTo:letterLimit }}</p>
27175         <label>
27176            Limit {{longNumber}} to:
27177            <input type="number" step="1" ng-model="longNumberLimit">
27178         </label>
27179         <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p>
27180       </div>
27181     </file>
27182     <file name="protractor.js" type="protractor">
27183       var numLimitInput = element(by.model('numLimit'));
27184       var letterLimitInput = element(by.model('letterLimit'));
27185       var longNumberLimitInput = element(by.model('longNumberLimit'));
27186       var limitedNumbers = element(by.binding('numbers | limitTo:numLimit'));
27187       var limitedLetters = element(by.binding('letters | limitTo:letterLimit'));
27188       var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit'));
27189
27190       it('should limit the number array to first three items', function() {
27191         expect(numLimitInput.getAttribute('value')).toBe('3');
27192         expect(letterLimitInput.getAttribute('value')).toBe('3');
27193         expect(longNumberLimitInput.getAttribute('value')).toBe('3');
27194         expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]');
27195         expect(limitedLetters.getText()).toEqual('Output letters: abc');
27196         expect(limitedLongNumber.getText()).toEqual('Output long number: 234');
27197       });
27198
27199       // There is a bug in safari and protractor that doesn't like the minus key
27200       // it('should update the output when -3 is entered', function() {
27201       //   numLimitInput.clear();
27202       //   numLimitInput.sendKeys('-3');
27203       //   letterLimitInput.clear();
27204       //   letterLimitInput.sendKeys('-3');
27205       //   longNumberLimitInput.clear();
27206       //   longNumberLimitInput.sendKeys('-3');
27207       //   expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
27208       //   expect(limitedLetters.getText()).toEqual('Output letters: ghi');
27209       //   expect(limitedLongNumber.getText()).toEqual('Output long number: 342');
27210       // });
27211
27212       it('should not exceed the maximum size of input array', function() {
27213         numLimitInput.clear();
27214         numLimitInput.sendKeys('100');
27215         letterLimitInput.clear();
27216         letterLimitInput.sendKeys('100');
27217         longNumberLimitInput.clear();
27218         longNumberLimitInput.sendKeys('100');
27219         expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]');
27220         expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi');
27221         expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342');
27222       });
27223     </file>
27224   </example>
27225*/
27226function limitToFilter() {
27227  return function(input, limit, begin) {
27228    if (Math.abs(Number(limit)) === Infinity) {
27229      limit = Number(limit);
27230    } else {
27231      limit = toInt(limit);
27232    }
27233    if (isNumberNaN(limit)) return input;
27234
27235    if (isNumber(input)) input = input.toString();
27236    if (!isArrayLike(input)) return input;
27237
27238    begin = (!begin || isNaN(begin)) ? 0 : toInt(begin);
27239    begin = (begin < 0) ? Math.max(0, input.length + begin) : begin;
27240
27241    if (limit >= 0) {
27242      return sliceFn(input, begin, begin + limit);
27243    } else {
27244      if (begin === 0) {
27245        return sliceFn(input, limit, input.length);
27246      } else {
27247        return sliceFn(input, Math.max(0, begin + limit), begin);
27248      }
27249    }
27250  };
27251}
27252
27253function sliceFn(input, begin, end) {
27254  if (isString(input)) return input.slice(begin, end);
27255
27256  return slice.call(input, begin, end);
27257}
27258
27259/**
27260 * @ngdoc filter
27261 * @name orderBy
27262 * @kind function
27263 *
27264 * @description
27265 * Returns an array containing the items from the specified `collection`, ordered by a `comparator`
27266 * function based on the values computed using the `expression` predicate.
27267 *
27268 * For example, `[{id: 'foo'}, {id: 'bar'}] | orderBy:'id'` would result in
27269 * `[{id: 'bar'}, {id: 'foo'}]`.
27270 *
27271 * The `collection` can be an Array or array-like object (e.g. NodeList, jQuery object, TypedArray,
27272 * String, etc).
27273 *
27274 * The `expression` can be a single predicate, or a list of predicates each serving as a tie-breaker
27275 * for the preceding one. The `expression` is evaluated against each item and the output is used
27276 * for comparing with other items.
27277 *
27278 * You can change the sorting order by setting `reverse` to `true`. By default, items are sorted in
27279 * ascending order.
27280 *
27281 * The comparison is done using the `comparator` function. If none is specified, a default, built-in
27282 * comparator is used (see below for details - in a nutshell, it compares numbers numerically and
27283 * strings alphabetically).
27284 *
27285 * ### Under the hood
27286 *
27287 * Ordering the specified `collection` happens in two phases:
27288 *
27289 * 1. All items are passed through the predicate (or predicates), and the returned values are saved
27290 *    along with their type (`string`, `number` etc). For example, an item `{label: 'foo'}`, passed
27291 *    through a predicate that extracts the value of the `label` property, would be transformed to:
27292 *    ```
27293 *    {
27294 *      value: 'foo',
27295 *      type: 'string',
27296 *      index: ...
27297 *    }
27298 *    ```
27299 * 
272992. The comparator function is used to sort the items, based on the derived values, types and
27300 *    indices.
27301 *
27302 * If you use a custom comparator, it will be called with pairs of objects of the form
27303 * `{value: ..., type: '...', index: ...}` and is expected to return `0` if the objects are equal
27304 * (as far as the comparator is concerned), `-1` if the 1st one should be ranked higher than the
27305 * second, or `1` otherwise.
27306 *
27307 * In order to ensure that the sorting will be deterministic across platforms, if none of the
27308 * specified predicates can distinguish between two items, `orderBy` will automatically introduce a
27309 * dummy predicate that returns the item's index as `value`.
27310 * (If you are using a custom comparator, make sure it can handle this predicate as well.)
27311 *
27312 * Finally, in an attempt to simplify things, if a predicate returns an object as the extracted
27313 * value for an item, `orderBy` will try to convert that object to a primitive value, before passing
27314 * it to the comparator. The following rules govern the conversion:
27315 *
27316 * 1. If the object has a `valueOf()` method that returns a primitive, its return value will be
27317 *    used instead.<br />
27318 *    (If the object has a `valueOf()` method that returns another object, then the returned object
27319 *    will be used in subsequent steps.)
27320 * 2. If the object has a custom `toString()` method (i.e. not the one inherited from `Object`) that
27321 *    returns a primitive, its return value will be used instead.<br />
27322 *    (If the object has a `toString()` method that returns another object, then the returned object
27323 *    will be used in subsequent steps.)
27324 * 3. No conversion; the object itself is used.
27325 *
27326 * ### The default comparator
27327 *
27328 * The default, built-in comparator should be sufficient for most usecases. In short, it compares
27329 * numbers numerically, strings alphabetically (and case-insensitively), for objects falls back to
27330 * using their index in the original collection, and sorts values of different types by type.
27331 *
27332 * More specifically, it follows these steps to determine the relative order of items:
27333 *
27334 * 1. If the compared values are of different types, compare the types themselves alphabetically.
27335 * 2. If both values are of type `string`, compare them alphabetically in a case- and
27336 *    locale-insensitive way.
27337 * 3. If both values are objects, compare their indices instead.
27338 * 4. Otherwise, return:
27339 *    -  `0`, if the values are equal (by strict equality comparison, i.e. using `===`).
27340 *    - `-1`, if the 1st value is "less than" the 2nd value (compared using the `<` operator).
27341 *    -  `1`, otherwise.
27342 *
27343 * **Note:** If you notice numbers not being sorted as expected, make sure they are actually being
27344 *           saved as numbers and not strings.
27345 * **Note:** For the purpose of sorting, `null` values are treated as the string `'null'` (i.e.
27346 *           `type: 'string'`, `value: 'null'`). This may cause unexpected sort order relative to
27347 *           other values.
27348 *
27349 * @param {Array|ArrayLike} collection - The collection (array or array-like object) to sort.
27350 * @param {(Function|string|Array.<Function|string>)=} expression - A predicate (or list of
27351 *    predicates) to be used by the comparator to determine the order of elements.
27352 *
27353 *    Can be one of:
27354 *
27355 *    - `Function`: A getter function. This function will be called with each item as argument and
27356 *      the return value will be used for sorting.
27357 *    - `string`: An Angular
vendor: 19,123 bytes, lines 27357-27858
27357 expression. This expression will be evaluated against each item and the
27358 *      result will be used for sorting. For example, use `'label'` to sort by a property called
27359 *      `label` or `'label.substring(0, 3)'` to sort by the first 3 characters of the `label`
27360 *      property.<br />
27361 *      (The result of a constant expression is interpreted as a property name to be used for
27362 *      comparison. For example, use `'"special name"'` (note the extra pair of quotes) to sort by a
27363 *      property called `special name`.)<br />
27364 *      An expression can be optionally prefixed with `+` or `-` to control the sorting direction,
27365 *      ascending or descending. For example, `'+label'` or `'-label'`. If no property is provided,
27366 *      (e.g. `'+'` or `'-'`), the collection element itself is used in comparisons.
27367 *    - `Array`: An array of function and/or string predicates. If a predicate cannot determine the
27368 *      relative order of two items, the next predicate is used as a tie-breaker.
27369 *
27370 * **Note:** If the predicate is missing or empty then it defaults to `'+'`.
27371 *
27372 * @param {boolean=} reverse - If `true`, reverse the sorting order.
27373 * @param {(Function)=} comparator - The comparator function used to determine the relative order of
27374 *    value pairs. If omitted, the built-in comparator will be used.
27375 *
27376 * @returns {Array} - The sorted array.
27377 *
27378 *
27379 * @example
27380 * ### Ordering a table with `ngRepeat`
27381 *
27382 * The example below demonstrates a simple {@link ngRepeat ngRepeat}, where the data is sorted by
27383 * age in descending order (expression is set to `'-age'`). The `comparator` is not set, which means
27384 * it defaults to the built-in comparator.
27385 *
27386   <example name="orderBy-static" module="orderByExample1">
27387     <file name="index.html">
27388       <div ng-controller="ExampleController">
27389         <table class="friends">
27390           <tr>
27391             <th>Name</th>
27392             <th>Phone Number</th>
27393             <th>Age</th>
27394           </tr>
27395           <tr ng-repeat="friend in friends | orderBy:'-age'">
27396             <td>{{friend.name}}</td>
27397             <td>{{friend.phone}}</td>
27398             <td>{{friend.age}}</td>
27399           </tr>
27400         </table>
27401       </div>
27402     </file>
27403     <file name="script.js">
27404       angular.module('orderByExample1', [])
27405         .controller('ExampleController', ['$scope', function($scope) {
27406           $scope.friends = [
27407             {name: 'John',   phone: '555-1212',  age: 10},
27408             {name: 'Mary',   phone: '555-9876',  age: 19},
27409             {name: 'Mike',   phone: '555-4321',  age: 21},
27410             {name: 'Adam',   phone: '555-5678',  age: 35},
27411             {name: 'Julie',  phone: '555-8765',  age: 29}
27412           ];
27413         }]);
27414     </file>
27415     <file name="style.css">
27416       .friends {
27417         border-collapse: collapse;
27418       }
27419
27420       .friends th {
27421         border-bottom: 1px solid;
27422       }
27423       .friends td, .friends th {
27424         border-left: 1px solid;
27425         padding: 5px 10px;
27426       }
27427       .friends td:first-child, .friends th:first-child {
27428         border-left: none;
27429       }
27430     </file>
27431     <file name="protractor.js" type="protractor">
27432       // Element locators
27433       var names = element.all(by.repeater('friends').column('friend.name'));
27434
27435       it('should sort friends by age in reverse order', function() {
27436         expect(names.get(0).getText()).toBe('Adam');
27437         expect(names.get(1).getText()).toBe('Julie');
27438         expect(names.get(2).getText()).toBe('Mike');
27439         expect(names.get(3).getText()).toBe('Mary');
27440         expect(names.get(4).getText()).toBe('John');
27441       });
27442     </file>
27443   </example>
27444 * <hr />
27445 *
27446 * @example
27447 * ### Changing parameters dynamically
27448 *
27449 * All parameters can be changed dynamically. The next example shows how you can make the columns of
27450 * a table sortable, by binding the `expression` and `reverse` parameters to scope properties.
27451 *
27452   <example name="orderBy-dynamic" module="orderByExample2">
27453     <file name="index.html">
27454       <div ng-controller="ExampleController">
27455         <pre>Sort by = {{propertyName}}; reverse = {{reverse}}</pre>
27456         <hr/>
27457         <button ng-click="propertyName = null; reverse = false">Set to unsorted</button>
27458         <hr/>
27459         <table class="friends">
27460           <tr>
27461             <th>
27462               <button ng-click="sortBy('name')">Name</button>
27463               <span class="sortorder" ng-show="propertyName === 'name'" ng-class="{reverse: reverse}"></span>
27464             </th>
27465             <th>
27466               <button ng-click="sortBy('phone')">Phone Number</button>
27467               <span class="sortorder" ng-show="propertyName === 'phone'" ng-class="{reverse: reverse}"></span>
27468             </th>
27469             <th>
27470               <button ng-click="sortBy('age')">Age</button>
27471               <span class="sortorder" ng-show="propertyName === 'age'" ng-class="{reverse: reverse}"></span>
27472             </th>
27473           </tr>
27474           <tr ng-repeat="friend in friends | orderBy:propertyName:reverse">
27475             <td>{{friend.name}}</td>
27476             <td>{{friend.phone}}</td>
27477             <td>{{friend.age}}</td>
27478           </tr>
27479         </table>
27480       </div>
27481     </file>
27482     <file name="script.js">
27483       angular.module('orderByExample2', [])
27484         .controller('ExampleController', ['$scope', function($scope) {
27485           var friends = [
27486             {name: 'John',   phone: '555-1212',  age: 10},
27487             {name: 'Mary',   phone: '555-9876',  age: 19},
27488             {name: 'Mike',   phone: '555-4321',  age: 21},
27489             {name: 'Adam',   phone: '555-5678',  age: 35},
27490             {name: 'Julie',  phone: '555-8765',  age: 29}
27491           ];
27492
27493           $scope.propertyName = 'age';
27494           $scope.reverse = true;
27495           $scope.friends = friends;
27496
27497           $scope.sortBy = function(propertyName) {
27498             $scope.reverse = ($scope.propertyName === propertyName) ? !$scope.reverse : false;
27499             $scope.propertyName = propertyName;
27500           };
27501         }]);
27502     </file>
27503     <file name="style.css">
27504       .friends {
27505         border-collapse: collapse;
27506       }
27507
27508       .friends th {
27509         border-bottom: 1px solid;
27510       }
27511       .friends td, .friends th {
27512         border-left: 1px solid;
27513         padding: 5px 10px;
27514       }
27515       .friends td:first-child, .friends th:first-child {
27516         border-left: none;
27517       }
27518
27519       .sortorder:after {
27520         content: '\25b2';   // BLACK UP-POINTING TRIANGLE
27521       }
27522       .sortorder.reverse:after {
27523         content: '\25bc';   // BLACK DOWN-POINTING TRIANGLE
27524       }
27525     </file>
27526     <file name="protractor.js" type="protractor">
27527       // Element locators
27528       var unsortButton = element(by.partialButtonText('unsorted'));
27529       var nameHeader = element(by.partialButtonText('Name'));
27530       var phoneHeader = element(by.partialButtonText('Phone'));
27531       var ageHeader = element(by.partialButtonText('Age'));
27532       var firstName = element(by.repeater('friends').column('friend.name').row(0));
27533       var lastName = element(by.repeater('friends').column('friend.name').row(4));
27534
27535       it('should sort friends by some property, when clicking on the column header', function() {
27536         expect(firstName.getText()).toBe('Adam');
27537         expect(lastName.getText()).toBe('John');
27538
27539         phoneHeader.click();
27540         expect(firstName.getText()).toBe('John');
27541         expect(lastName.getText()).toBe('Mary');
27542
27543         nameHeader.click();
27544         expect(firstName.getText()).toBe('Adam');
27545         expect(lastName.getText()).toBe('Mike');
27546
27547         ageHeader.click();
27548         expect(firstName.getText()).toBe('John');
27549         expect(lastName.getText()).toBe('Adam');
27550       });
27551
27552       it('should sort friends in reverse order, when clicking on the same column', function() {
27553         expect(firstName.getText()).toBe('Adam');
27554         expect(lastName.getText()).toBe('John');
27555
27556         ageHeader.click();
27557         expect(firstName.getText()).toBe('John');
27558         expect(lastName.getText()).toBe('Adam');
27559
27560         ageHeader.click();
27561         expect(firstName.getText()).toBe('Adam');
27562         expect(lastName.getText()).toBe('John');
27563       });
27564
27565       it('should restore the original order, when clicking "Set to unsorted"', function() {
27566         expect(firstName.getText()).toBe('Adam');
27567         expect(lastName.getText()).toBe('John');
27568
27569         unsortButton.click();
27570         expect(firstName.getText()).toBe('John');
27571         expect(lastName.getText()).toBe('Julie');
27572       });
27573     </file>
27574   </example>
27575 * <hr />
27576 *
27577 * @example
27578 * ### Using `orderBy` inside a controller
27579 *
27580 * It is also possible to call the `orderBy` filter manually, by injecting `orderByFilter`, and
27581 * calling it with the desired parameters. (Alternatively, you could inject the `$filter` factory
27582 * and retrieve the `orderBy` filter with `$filter('orderBy')`.)
27583 *
27584   <example name="orderBy-call-manually" module="orderByExample3">
27585     <file name="index.html">
27586       <div ng-controller="ExampleController">
27587         <pre>Sort by = {{propertyName}}; reverse = {{reverse}}</pre>
27588         <hr/>
27589         <button ng-click="sortBy(null)">Set to unsorted</button>
27590         <hr/>
27591         <table class="friends">
27592           <tr>
27593             <th>
27594               <button ng-click="sortBy('name')">Name</button>
27595               <span class="sortorder" ng-show="propertyName === 'name'" ng-class="{reverse: reverse}"></span>
27596             </th>
27597             <th>
27598               <button ng-click="sortBy('phone')">Phone Number</button>
27599               <span class="sortorder" ng-show="propertyName === 'phone'" ng-class="{reverse: reverse}"></span>
27600             </th>
27601             <th>
27602               <button ng-click="sortBy('age')">Age</button>
27603               <span class="sortorder" ng-show="propertyName === 'age'" ng-class="{reverse: reverse}"></span>
27604             </th>
27605           </tr>
27606           <tr ng-repeat="friend in friends">
27607             <td>{{friend.name}}</td>
27608             <td>{{friend.phone}}</td>
27609             <td>{{friend.age}}</td>
27610           </tr>
27611         </table>
27612       </div>
27613     </file>
27614     <file name="script.js">
27615       angular.module('orderByExample3', [])
27616         .controller('ExampleController', ['$scope', 'orderByFilter', function($scope, orderBy) {
27617           var friends = [
27618             {name: 'John',   phone: '555-1212',  age: 10},
27619             {name: 'Mary',   phone: '555-9876',  age: 19},
27620             {name: 'Mike',   phone: '555-4321',  age: 21},
27621             {name: 'Adam',   phone: '555-5678',  age: 35},
27622             {name: 'Julie',  phone: '555-8765',  age: 29}
27623           ];
27624
27625           $scope.propertyName = 'age';
27626           $scope.reverse = true;
27627           $scope.friends = orderBy(friends, $scope.propertyName, $scope.reverse);
27628
27629           $scope.sortBy = function(propertyName) {
27630             $scope.reverse = (propertyName !== null && $scope.propertyName === propertyName)
27631                 ? !$scope.reverse : false;
27632             $scope.propertyName = propertyName;
27633             $scope.friends = orderBy(friends, $scope.propertyName, $scope.reverse);
27634           };
27635         }]);
27636     </file>
27637     <file name="style.css">
27638       .friends {
27639         border-collapse: collapse;
27640       }
27641
27642       .friends th {
27643         border-bottom: 1px solid;
27644       }
27645       .friends td, .friends th {
27646         border-left: 1px solid;
27647         padding: 5px 10px;
27648       }
27649       .friends td:first-child, .friends th:first-child {
27650         border-left: none;
27651       }
27652
27653       .sortorder:after {
27654         content: '\25b2';   // BLACK UP-POINTING TRIANGLE
27655       }
27656       .sortorder.reverse:after {
27657         content: '\25bc';   // BLACK DOWN-POINTING TRIANGLE
27658       }
27659     </file>
27660     <file name="protractor.js" type="protractor">
27661       // Element locators
27662       var unsortButton = element(by.partialButtonText('unsorted'));
27663       var nameHeader = element(by.partialButtonText('Name'));
27664       var phoneHeader = element(by.partialButtonText('Phone'));
27665       var ageHeader = element(by.partialButtonText('Age'));
27666       var firstName = element(by.repeater('friends').column('friend.name').row(0));
27667       var lastName = element(by.repeater('friends').column('friend.name').row(4));
27668
27669       it('should sort friends by some property, when clicking on the column header', function() {
27670         expect(firstName.getText()).toBe('Adam');
27671         expect(lastName.getText()).toBe('John');
27672
27673         phoneHeader.click();
27674         expect(firstName.getText()).toBe('John');
27675         expect(lastName.getText()).toBe('Mary');
27676
27677         nameHeader.click();
27678         expect(firstName.getText()).toBe('Adam');
27679         expect(lastName.getText()).toBe('Mike');
27680
27681         ageHeader.click();
27682         expect(firstName.getText()).toBe('John');
27683         expect(lastName.getText()).toBe('Adam');
27684       });
27685
27686       it('should sort friends in reverse order, when clicking on the same column', function() {
27687         expect(firstName.getText()).toBe('Adam');
27688         expect(lastName.getText()).toBe('John');
27689
27690         ageHeader.click();
27691         expect(firstName.getText()).toBe('John');
27692         expect(lastName.getText()).toBe('Adam');
27693
27694         ageHeader.click();
27695         expect(firstName.getText()).toBe('Adam');
27696         expect(lastName.getText()).toBe('John');
27697       });
27698
27699       it('should restore the original order, when clicking "Set to unsorted"', function() {
27700         expect(firstName.getText()).toBe('Adam');
27701         expect(lastName.getText()).toBe('John');
27702
27703         unsortButton.click();
27704         expect(firstName.getText()).toBe('John');
27705         expect(lastName.getText()).toBe('Julie');
27706       });
27707     </file>
27708   </example>
27709 * <hr />
27710 *
27711 * @example
27712 * ### Using a custom comparator
27713 *
27714 * If you have very specific requirements about the way items are sorted, you can pass your own
27715 * comparator function. For example, you might need to compare some strings in a locale-sensitive
27716 * way. (When specifying a custom comparator, you also need to pass a value for the `reverse`
27717 * argument - passing `false` retains the default sorting order, i.e. ascending.)
27718 *
27719   <example name="orderBy-custom-comparator" module="orderByExample4">
27720     <file name="index.html">
27721       <div ng-controller="ExampleController">
27722         <div class="friends-container custom-comparator">
27723           <h3>Locale-sensitive Comparator</h3>
27724           <table class="friends">
27725             <tr>
27726               <th>Name</th>
27727               <th>Favorite Letter</th>
27728             </tr>
27729             <tr ng-repeat="friend in friends | orderBy:'favoriteLetter':false:localeSensitiveComparator">
27730               <td>{{friend.name}}</td>
27731               <td>{{friend.favoriteLetter}}</td>
27732             </tr>
27733           </table>
27734         </div>
27735         <div class="friends-container default-comparator">
27736           <h3>Default Comparator</h3>
27737           <table class="friends">
27738             <tr>
27739               <th>Name</th>
27740               <th>Favorite Letter</th>
27741             </tr>
27742             <tr ng-repeat="friend in friends | orderBy:'favoriteLetter'">
27743               <td>{{friend.name}}</td>
27744               <td>{{friend.favoriteLetter}}</td>
27745             </tr>
27746           </table>
27747         </div>
27748       </div>
27749     </file>
27750     <file name="script.js">
27751       angular.module('orderByExample4', [])
27752         .controller('ExampleController', ['$scope', function($scope) {
27753           $scope.friends = [
27754             {name: 'John',   favoriteLetter: 'Ä'},
27755             {name: 'Mary',   favoriteLetter: 'Ü'},
27756             {name: 'Mike',   favoriteLetter: 'Ö'},
27757             {name: 'Adam',   favoriteLetter: 'H'},
27758             {name: 'Julie',  favoriteLetter: 'Z'}
27759           ];
27760
27761           $scope.localeSensitiveComparator = function(v1, v2) {
27762             // If we don't get strings, just compare by index
27763             if (v1.type !== 'string' || v2.type !== 'string') {
27764               return (v1.index < v2.index) ? -1 : 1;
27765             }
27766
27767             // Compare strings alphabetically, taking locale into account
27768             return v1.value.localeCompare(v2.value);
27769           };
27770         }]);
27771     </file>
27772     <file name="style.css">
27773       .friends-container {
27774         display: inline-block;
27775         margin: 0 30px;
27776       }
27777
27778       .friends {
27779         border-collapse: collapse;
27780       }
27781
27782       .friends th {
27783         border-bottom: 1px solid;
27784       }
27785       .friends td, .friends th {
27786         border-left: 1px solid;
27787         padding: 5px 10px;
27788       }
27789       .friends td:first-child, .friends th:first-child {
27790         border-left: none;
27791       }
27792     </file>
27793     <file name="protractor.js" type="protractor">
27794       // Element locators
27795       var container = element(by.css('.custom-comparator'));
27796       var names = container.all(by.repeater('friends').column('friend.name'));
27797
27798       it('should sort friends by favorite letter (in correct alphabetical order)', function() {
27799         expect(names.get(0).getText()).toBe('John');
27800         expect(names.get(1).getText()).toBe('Adam');
27801         expect(names.get(2).getText()).toBe('Mike');
27802         expect(names.get(3).getText()).toBe('Mary');
27803         expect(names.get(4).getText()).toBe('Julie');
27804       });
27805     </file>
27806   </example>
27807 *
27808 */
27809orderByFilter.$inject = ['$parse'];
27810function orderByFilter($parse) {
27811  return function(array, sortPredicate, reverseOrder, compareFn) {
27812
27813    if (array == null) return array;
27814    if (!isArrayLike(array)) {
27815      throw minErr('orderBy')('notarray', 'Expected array but received: {0}', array);
27816    }
27817
27818    if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; }
27819    if (sortPredicate.length === 0) { sortPredicate = ['+']; }
27820
27821    var predicates = processPredicates(sortPredicate);
27822
27823    var descending = reverseOrder ? -1 : 1;
27824
27825    // Define the `compare()` function. Use a default comparator if none is specified.
27826    var compare = isFunction(compareFn) ? compareFn : defaultCompare;
27827
27828    // The next three lines are a version of a Swartzian Transform idiom from Perl
27829    // (sometimes called the Decorate-Sort-Undecorate idiom)
27830    // See https://en.wikipedia.org/wiki/Schwartzian_transform
27831    var compareValues = Array.prototype.map.call(array, getComparisonObject);
27832    compareValues.sort(doComparison);
27833    array = compareValues.map(function(item) { return item.value; });
27834
27835    return array;
27836
27837    function getComparisonObject(value, index) {
27838      // NOTE: We are adding an extra `tieBreaker` value based on the element's index.
27839      // This will be used to keep the sort stable when none of the input predicates can
27840      // distinguish between two elements.
27841      return {
27842        value: value,
27843        tieBreaker: {value: index, type: 'number', index: index},
27844        predicateValues: predicates.map(function(predicate) {
27845          return getPredicateValue(predicate.get(value), index);
27846        })
27847      };
27848    }
27849
27850    function doComparison(v1, v2) {
27851      for (var i = 0, ii = predicates.length; i < ii; i++) {
27852        var result = compare(v1.predicateValues[i], v2.predicateValues[i]);
27853        if (result) {
27854          return result * predicates[i].descending * descending;
27855        }
27856      }
27857
27858      return 
vendor: 5,453 bytes, lines 27858-28037
27858compare(v1.tieBreaker, v2.tieBreaker) * descending;
27859    }
27860  };
27861
27862  function processPredicates(sortPredicates) {
27863    return sortPredicates.map(function(predicate) {
27864      var descending = 1, get = identity;
27865
27866      if (isFunction(predicate)) {
27867        get = predicate;
27868      } else if (isString(predicate)) {
27869        if ((predicate.charAt(0) === '+' || predicate.charAt(0) === '-')) {
27870          descending = predicate.charAt(0) === '-' ? -1 : 1;
27871          predicate = predicate.substring(1);
27872        }
27873        if (predicate !== '') {
27874          get = $parse(predicate);
27875          if (get.constant) {
27876            var key = get();
27877            get = function(value) { return value[key]; };
27878          }
27879        }
27880      }
27881      return {get: get, descending: descending};
27882    });
27883  }
27884
27885  function isPrimitive(value) {
27886    switch (typeof value) {
27887      case 'number': /* falls through */
27888      case 'boolean': /* falls through */
27889      case 'string':
27890        return true;
27891      default:
27892        return false;
27893    }
27894  }
27895
27896  function objectValue(value) {
27897    // If `valueOf` is a valid function use that
27898    if (isFunction(value.valueOf)) {
27899      value = value.valueOf();
27900      if (isPrimitive(value)) return value;
27901    }
27902    // If `toString` is a valid function and not the one from `Object.prototype` use that
27903    if (hasCustomToString(value)) {
27904      value = value.toString();
27905      if (isPrimitive(value)) return value;
27906    }
27907
27908    return value;
27909  }
27910
27911  function getPredicateValue(value, index) {
27912    var type = typeof value;
27913    if (value === null) {
27914      type = 'string';
27915      value = 'null';
27916    } else if (type === 'object') {
27917      value = objectValue(value);
27918    }
27919    return {value: value, type: type, index: index};
27920  }
27921
27922  function defaultCompare(v1, v2) {
27923    var result = 0;
27924    var type1 = v1.type;
27925    var type2 = v2.type;
27926
27927    if (type1 === type2) {
27928      var value1 = v1.value;
27929      var value2 = v2.value;
27930
27931      if (type1 === 'string') {
27932        // Compare strings case-insensitively
27933        value1 = value1.toLowerCase();
27934        value2 = value2.toLowerCase();
27935      } else if (type1 === 'object') {
27936        // For basic objects, use the position of the object
27937        // in the collection instead of the value
27938        if (isObject(value1)) value1 = v1.index;
27939        if (isObject(value2)) value2 = v2.index;
27940      }
27941
27942      if (value1 !== value2) {
27943        result = value1 < value2 ? -1 : 1;
27944      }
27945    } else {
27946      result = type1 < type2 ? -1 : 1;
27947    }
27948
27949    return result;
27950  }
27951}
27952
27953function ngDirective(directive) {
27954  if (isFunction(directive)) {
27955    directive = {
27956      link: directive
27957    };
27958  }
27959  directive.restrict = directive.restrict || 'AC';
27960  return valueFn(directive);
27961}
27962
27963/**
27964 * @ngdoc directive
27965 * @name a
27966 * @restrict E
27967 *
27968 * @description
27969 * Modifies the default behavior of the html a tag so that the default action is prevented when
27970 * the href attribute is empty.
27971 *
27972 * For dynamically creating `href` attributes for a tags, see the {@link ng.ngHref `ngHref`} directive.
27973 */
27974var htmlAnchorDirective = valueFn({
27975  restrict: 'E',
27976  compile: function(element, attr) {
27977    if (!attr.href && !attr.xlinkHref) {
27978      return function(scope, element) {
27979        // If the linked element is not an anchor tag anymore, do nothing
27980        if (element[0].nodeName.toLowerCase() !== 'a') return;
27981
27982        // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
27983        var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
27984                   'xlink:href' : 'href';
27985        element.on('click', function(event) {
27986          // if we have no href url, then don't navigate anywhere.
27987          if (!element.attr(href)) {
27988            event.preventDefault();
27989          }
27990        });
27991      };
27992    }
27993  }
27994});
27995
27996/**
27997 * @ngdoc directive
27998 * @name ngHref
27999 * @restrict A
28000 * @priority 99
28001 *
28002 * @description
28003 * Using Angular markup like `{{hash}}` in an href attribute will
28004 * make the link go to the wrong URL if the user clicks it before
28005 * Angular has a chance to replace the `{{hash}}` markup with its
28006 * value. Until Angular replaces the markup the link will be broken
28007 * and will most likely return a 404 error. The `ngHref` directive
28008 * solves this problem.
28009 *
28010 * The wrong way to write it:
28011 * ```html
28012 * <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
28013 * ```
28014 *
28015 * The correct way to write it:
28016 * ```html
28017 * <a ng-href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
28018 * ```
28019 *
28020 * @element A
28021 * @param {template} ngHref any string which can contain `{{}}` markup.
28022 *
28023 * @example
28024 * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes
28025 * in links and their different behaviors:
28026    <example name="ng-href">
28027      <file name="index.html">
28028        <input ng-model="value" /><br />
28029        <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
28030        <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
28031        <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br />
28032        <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
28033        <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
28034        <a id="link-6" ng-href="{{value}}">link</a> (link, change location)
28035      </file>
28036      <file name="protractor.js" type="protractor">
28037        it('should execute ng-click but not reload when href without value', function() {
28038          element(by.id('link-1')).click();
28039          expect(element(by.model('value')).getAttribute('value')).toEqual('1');
28040          expect(element(by.id('link-1')).getAttribute('href')).toBe('');
28041        });
28042
28043        it('should execute ng-click but not reload when href empty string', function() {
28044          element(by.id('link-2')).click();
28045          expect(element(by.model('value')).getAttribute('value')).toEqual('2');
28046          expect(element(by.id('link-2')).getAttribute('href')).toBe('');
28047        });
28048
28049        it('should execute ng-click and change url when ng-href specified', function() {
28050          expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
28051
28052          element(by.id('link-3')).click();
28053
28054          // At this point, we navigate away from an Angular page, so we need
28055          // to use browser.driver to get the base webdriver.
28056
28057          browser.wait(function() {
28058            return browser.driver.getCurrentUrl().then(function(url) {
28059              return url.match(/\/123$/);
28060            });
28061          }, 5000, 'page should navigate to /123');
28062        });
28063
28064        it('should execute ng-click but not reload when href empty string and name specified', function() {
28065          element(by.id('link-4')).click();
28066          expect(element(by.model('value')).getAttribute('value')).toEqual('4');
28067          expect(element(by.id('link-4')).getAttribute('href')).toBe('');
28068        });
28069
28070        it('should execute ng-click but not reload when no href but name specified', function() {
28071          element(by.id('link-5')).click();
28072          expect(element(by.model('value')).getAttribute('value')).toEqual('5');
28073          expect(element(by.id('link-5')).getAttribute('href')).toBe(null);
28074        });
28075
28076        it('should only change url when only ng-href', function() {
28077          element(by.model('value')).clear();
28078          element(by.model('value')).sendKeys('6');
28079          expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
28080
28081          element(by.id('link-6')).click();
28082
28083          // At this point, we navigate away from an Angular page, so we need
28084          // to use browser.driver to get the base webdriver.
28085          browser.wait(function() {
28086            return browser.driver.getCurrentUrl().then(function(url) {
28087              return url.match(/\/6$/);
28088            });
28089          }, 5000, 'page should navigate to /6');
28090        });
28091      </file>
28092    </example>
28093 */
28094
28095/**
28096 * @ngdoc directive
28097 * @name ngSrc
28098 * @restrict A
28099 * @priority 99
28100 *
28101 * @description
28102 * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
28103 * work right: The browser will fetch from the URL with the literal
28104 * text `{{hash}}` until Angular replaces the expression inside
28105 * `{{hash}}`. The `ngSrc` directive solves this problem.
28106 *
28107 * The buggy way to write it:
28108 * ```html
28109 * <img src="http://www.gravatar.com/avatar/{{hash}}" alt="Description"/>
28110 * ```
28111 *
28112 * The correct way to write it:
28113 * ```html
28114 * <img ng-src="http://www.gravatar.com/avatar/{{hash}}" alt="Description" />
28115 * ```
28116 *
28117 * @element IMG
28118 * @param {template} ngSrc any string which can contain `{{}}` markup.
28119 */
28120
28121/**
28122 * @ngdoc directive
28123 * @name ngSrcset
28124 * @restrict A
28125 * @priority 99
28126 *
28127 * @description
28128 * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
28129 * work right: The browser will fetch from the URL with the literal
28130 * text `{{hash}}` until Angular replaces the expression inside
28131 * `{{hash}}`. The `ngSrcset` directive solves this problem.
28132 *
28133 * The buggy way to write it:
28134 * ```html
28135 * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description"/>
28136 * ```
28137 *
28138 * The correct way to write it:
28139 * ```html
28140 * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description" />
28141 * ```
28142 *
28143 * @element IMG
28144 * @param {template} ngSrcset any string which can contain `{{}}` markup.
28145 */
28146
28147/**
28148 * @ngdoc directive
28149 * @name ngDisabled
28150 * @restrict A
28151 * @priority 100
28152 *
28153 * @description
28154 *
28155 * This directive sets the `disabled` attribute on the element if the
28156 * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy.
28157 *
28158 * A special directive is necessary because we cannot use interpolation inside the `disabled`
28159 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
28160 *
28161 * @example
28162    <example name="ng-disabled">
28163      <file name="index.html">
28164        <label>Click me to toggle: <input type="checkbox" ng-model="checked"></label><br/>
28165        <button ng-model="button" ng-disabled="checked">Button</button>
28166      </file>
28167      <file name="protractor.js" type="protractor">
28168        it('should toggle button', function() {
28169          expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy();
28170          element(by.model('checked')).click();
28171          expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy();
28172        });
28173      </file>
28174    </example>
28175 *
28176 * @element INPUT
28177 * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
28178 *     then the `disabled` attribute will be set on the element
28179 */
28180
28181
28182/**
28183 * @ngdoc directive
28184 * @name ngChecked
28185 * @restrict A
28186 * @priority 100
28187 *
28188 * @description
28189 * Sets the `checked` attribute on the element, if the expression inside `ngChecked` is truthy.
28190 *
28191 * Note that this directive should not be used together with {@link ngModel `ngModel`},
28192 * as this can lead to unexpected behavior.
28193 *
28194 * A special directive is necessary because we cannot use interpolation inside the `checked`
28195 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
28196 *
28197 * @example
28198    <example name="ng-checked">
28199      <file name="index.html">
28200        <label>Check me to check both: <input type="checkbox" ng-model="master"></label><br/>
28201        <input id="checkSlave" type="checkbox" ng-checked="master" aria-label="Slave input">
28202      </file>
28203      <file name="protractor.js" type="protractor">
28204        it('should check both checkBoxes', function() {
28205          expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
28206          element(by.model('master')).click();
28207          expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
28208        });
28209      </file>
28210    </example>
28211 *
28212 * @element INPUT
28213 * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
28214 *     then the `checked` attribute will be set on the element
28215 */
28216
28217
28218/**
28219 * @ngdoc directive
28220 * @name ngReadonly
28221 * @restrict A
28222 * @priority 100
28223 *
28224 * @description
28225 *
28226 * Sets the `readonly` attribute on the element, if the expression inside `ngReadonly` is truthy.
28227 * Note that `readonly` applies only to `input` elements with specific types. [See the input docs on
28228 * MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-readonly) for more information.
28229 *
28230 * A special directive is necessary because we cannot use interpolation inside the `readonly`
28231 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
28232 *
28233 * @example
28234    <example name="ng-readonly">
28235      <file name="index.html">
28236        <label>Check me to make text readonly: <input type="checkbox" ng-model="checked"></label><br/>
28237        <input type="text" ng-readonly="checked" value="I'm Angular" aria-label="Readonly field" />
28238      </file>
28239      <file name="protractor.js" type="protractor">
28240        it('should toggle readonly attr', function() {
28241          expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy();
28242          element(by.model('checked')).click();
28243          expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy();
28244        });
28245      </file>
28246    </example>
28247 *
28248 * @element INPUT
28249 * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
28250 *     then special attribute "readonly" will be set on the element
28251 */
28252
28253
28254/**
28255 * @ngdoc directive
28256 * @name ngSelected
28257 * @restrict A
28258 * @priority 100
28259 *
28260 * @description
28261 *
28262 * Sets the `selected` attribute on the element, if the expression inside `ngSelected` is truthy.
28263 *
28264 * A special directive is necessary because we cannot use interpolation inside the `selected`
28265 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
28266 *
28267 * <div class="alert alert-warning">
28268 *   **Note:** `ngSelected` does not interact with the `select` and `ngModel` directives, it only
28269 *   sets the `selected` attribute on the element. If you are using `ngModel` on the select, you
28270 *   should not use `ngSelected` on the options, as `ngModel` will set the select value and
28271 *   selected options.
28272 * </div>
28273 *
28274 * @example
28275    <example name="ng-selected">
28276      <file name="index.html">
28277        <label>Check me to select: <input type="checkbox" ng-model="selected"></label><br/>
28278        <select aria-label="ngSelected demo">
28279          <option>Hello!</option>
28280          <option id="greet" ng-selected="selected">Greetings!</option>
28281        </select>
28282      </file>
28283      <file name="protractor.js" type="protractor">
28284        it('should select Greetings!', function() {
28285          expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
28286          element(by.model('selected')).click();
28287          expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
28288        });
28289      </file>
28290    </example>
28291 *
28292 * @element OPTION
28293 * @param {expression} ngSelected If the {@link guide/expression expression} is truthy,
28294 *     then special attribute "selected" will be set on the element
28295 */
28296
28297/**
28298 * @ngdoc directive
28299 * @name ngOpen
28300 * @restrict A
28301 * @priority 100
28302 *
28303 * @description
28304 *
28305 * Sets the `open` attribute on the element, if the expression inside `ngOpen` is truthy.
28306 *
28307 * A special directive is necessary because we cannot use interpolation inside the `open`
28308 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
28309 *
28310 * ## A note about browser compatibility
28311 *
28312 * Edge, Firefox, and Internet Explorer do not support the `details` element, it is
28313 * recommended to use {@link ng.ngShow} and {@link ng.ngHide} instead.
28314 *
28315 * @example
28316     <example name="ng-open">
28317       <file name="index.html">
28318         <label>Check me check multiple: <input type="checkbox" ng-model="open"></label><br/>
28319         <details id="details" ng-open="open">
28320            <summary>Show/Hide me</summary>
28321         </details>
28322       </file>
28323       <file name="protractor.js" type="protractor">
28324         it('should toggle open', function() {
28325           expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
28326           element(by.model('open')).click();
28327           expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
28328         });
28329       </file>
28330     </example>
28331 *
28332 * @element DETAILS
28333 * @param {expression} ngOpen If the {@link guide/expression expression} is truthy,
28334 *     then special attribute "open" will be set on the element
28335 */
28336
28337var ngAttributeAliasDirectives = {};
28338
28339// boolean attrs are evaluated
28340forEach(BOOLEAN_ATTR, function(propName, attrName) {
28341  // binding to multiple is not supported
28342  if (propName === 'multiple') return;
28343
28344  function defaultLinkFn(scope, element, attr) {
28345    scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
28346      attr.$set(attrName, !!value);
28347    });
28348  }
28349
28350  var normalized = directiveNormalize('ng-' + attrName);
28351  var linkFn = defaultLinkFn;
28352
28353  if (propName === 'checked') {
28354    linkFn = function(scope, element, attr) {
28355      // ensuring ngChecked doesn't interfere with ngModel when both are set on the same input
28356      if (attr.ngModel !== attr[normalized]) {
28357        defaultLinkFn(scope, element, attr);
28358      }
28359    };
28360  }
28361
28362  ngAttributeAliasDirectives[normalized] = function() {
28363    return {
28364      restrict: 'A',
28365      priority: 100,
28366      link: linkFn
28367    };
28368  };
28369});
28370
28371// aliased input attrs are evaluated
28372forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) {
28373  ngAttributeAliasDirectives[ngAttr] = function() {
28374    return {
28375      priority: 100,
28376      link: function(scope, element, attr) {
28377        //special case ngPattern when a literal regular expression value
28378        //is used as the expression (this way we don't have to watch anything).
28379        if (ngAttr === 'ngPattern' && attr.ngPattern.charAt(0) === '/') {
28380          var match = attr.ngPattern.match(REGEX_STRING_REGEXP);
28381          if (match) {
28382            attr.$set('ngPattern', new RegExp(match[1], match[2]));
28383            return;
28384          }
28385        }
28386
28387        scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) {
28388          attr.$set(ngAttr, value);
28389        });
28390      }
28391    };
28392  };
28393});
28394
28395// ng-src, ng-srcset, ng-href are interpolated
28396forEach(['src', 'srcset', 'href'], function(attrName) {
28397  var normalized = directiveNormalize('ng-' + attrName);
28398  ngAttributeAliasDirectives[normalized] = function() {
28399    return {
28400      priority: 99, // it needs to run after the attributes are interpolated
28401      link: function(scope, element, attr) {
28402        var propName = attrName,
28403            name = attrName;
28404
28405        if (attrName === 'href' &&
28406            toString.call(element.prop('href')) === '[object SVGAnimatedString]') {
28407          name = 'xlinkHref';
28408          attr.$attr[name] = 'xlink:href';
28409          propName = null;
28410        }
28411
28412        attr.$observe(normalized, function(value) {
28413          if (!value) {
28414            if (attrName === 'href') {
28415              attr.$set(name, null);
28416            }
28417            return;
28418          }
28419
28420          attr.$set(name, value);
28421
28422          // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
28423          // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
28424          // to set the property as well to achieve the desired effect.
28425          // we use attr[attrName] value since $set can sanitize the url.
28426          if (msie && propName) element.prop(propName, attr[name]);
28427        });
28428      }
28429    };
28430  };
28431});
28432
28433/* global -nullFormCtrl, -SUBMITTED_CLASS, addSetValidityMethod: true
28434 */
28435var nullFormCtrl = {
28436  $addControl: noop,
28437  $$renameControl: nullFormRenameControl,
28438  $removeControl: noop,
28439  $setValidity: noop,
28440  $setDirty: noop,
28441  $setPristine: noop,
28442  $setSubmitted: noop
28443},
28444SUBMITTED_CLASS = 'ng-submitted';
28445
28446function nullFormRenameControl(control, name) {
28447  control.$name = name;
28448}
28449
28450/**
28451 * @ngdoc type
28452 * @name form.FormController
28453 *
28454 * @property {boolean} $pristine True if user has not interacted with the form yet.
28455 * @property {boolean} $dirty True if user has already interacted with the form.
28456 * @property {boolean} $valid True if all of the containing forms and controls are valid.
28457 * @property {boolean} $invalid True if at least one containing control or form is invalid.
28458 * @property {boolean} $pending True if at least one containing control or form is pending.
28459 * @property {boolean} $submitted True if user has submitted the form even if its invalid.
28460 *
28461 * @property {Object} $error Is an object hash, containing references to controls or
28462 *  forms with failing validators, where:
28463 *
28464 *  - keys are validation tokens (error names),
28465 *  - values are arrays of controls or forms that have a failing validator for given error name.
28466 *
28467 *  Built-in validation tokens:
28468 *
28469 *  - `email`
28470 *  - `max`
28471 *  - `maxlength`
28472 *  - `min`
28473 *  - `minlength`
28474 *  - `number`
28475 *  - `pattern`
28476 *  - `required`
28477 *  - `url`
28478 *  - `date`
28479 *  - `datetimelocal`
28480 *  - `time`
28481 *  - `week`
28482 *  - `month`
28483 *
28484 * @description
28485 * `FormController` keeps track of all its controls and nested forms as well as the state of them,
28486 * such as being valid/invalid or dirty/pristine.
28487 *
28488 * Each {@link ng.directive:form form} directive creates an instance
28489 * of `FormController`.
28490 *
28491 */
28492//asks for $scope to fool the BC controller module
28493FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate'];
28494function FormController(element, attrs, $scope, $animate, $interpolate) {
28495  var form = this,
28496      controls = [];
28497
28498  // init state
28499  form.$error = {};
28500  form.$$success = {};
28501  form.$pending = undefined;
28502  form.$name = $interpolate(attrs.name || attrs.ngForm || '')($scope);
28503  form.$dirty = false;
28504  form.$pristine = true;
28505  form.$valid = true;
28506  form.$invalid = false;
28507  form.$submitted = false;
28508  form.$$parentForm = nullFormCtrl;
28509
28510  /**
28511   * @ngdoc method
28512   * @name form.FormController#$rollbackViewValue
28513   *
28514   * @description
28515   * Rollback all form controls pending updates to the `$modelValue`.
28516   *
28517   * Updates may be pending by a debounced event or because the input is waiting for a some future
28518   * event defined in `ng-model-options`. This method is typically needed by the reset button of
28519   * a form that uses `ng-model-options` to pend updates.
28520   */
28521  form.$rollbackViewValue = function() {
28522    forEach(controls, function(control) {
28523      control.$rollbackViewValue();
28524    });
28525  };
28526
28527  /**
28528   * @ngdoc method
28529   * @name form.FormController#$commitViewValue
28530   *
28531   * @description
28532   * Commit all form controls pending updates to the `$modelValue`.
28533   *
28534   * Updates may be pending by a debounced event or because the input is waiting for a some future
28535   * event defined in `ng-model-options`. This method is rarely needed as `NgModelController`
28536   * usually handles calling this in response to input events.
28537   */
28538  form.$commitViewValue = function() {
28539    forEach(controls, function(control) {
28540      control.$commitViewValue();
28541    });
28542  };
28543
28544  /**
28545   * @ngdoc method
28546   * @name form.FormController#$addControl
28547   * @param {object} control control object, either a {@link form.FormController} or an
28548   * {@link ngModel.NgModelController}
28549   *
28550   * @description
28551   * Register a control with the form. Input elements using ngModelController do this automatically
28552   * when they are linked.
28553   *
28554   * Note that the current state of the control will not be reflected on the new parent form. This
28555   * is not an issue with normal use, as freshly compiled and linked controls are in a `$pristine`
28556   * state.
28557   *
28558   * However, if the method is used programmatically, for example by adding dynamically created controls,
28559   * or controls that have been previously removed without destroying their corresponding DOM element,
28560   * it's the developers responsibility to make sure the current state propagates to the parent form.
28561   *
28562   * For example, if an input control is added that is already `$dirty` and has `$error` properties,
28563   * calling `$setDirty()` and `$validate()` afterwards will propagate the state to the parent form.
28564   */
28565  form.$addControl = function(control) {
28566    // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
28567    // and not added to the scope.  Now we throw an error.
28568    assertNotHasOwnProperty(control.$name, 'input');
28569    controls.push(control);
28570
28571    if (control.$name) {
28572      form[control.$name] = control;
28573    }
28574
28575    control.$$parentForm = form;
28576  };
28577
28578  // Private API: rename a form control
28579  form.$$renameControl = function(control, newName) {
28580    var oldName = control.$name;
28581
28582    if (form[oldName] === control) {
28583      delete form[oldName];
28584    }
28585    form[newName] = control;
28586    control.$name = newName;
28587  };
28588
28589  /**
28590   * @ngdoc method
28591   * @name form.FormController#$removeControl
28592   * @param {object} control control object, either a {@link form.FormController} or an
28593   * {@link ngModel.NgModelController}
28594   *
28595   * @description
28596   * Deregister a control from the form.
28597   *
28598   * Input elements using ngModelController do this automatically when they are destroyed.
28599   *
28600   * Note that only the removed control's validation state (`$errors`etc.) will be removed from the
28601   * form. `$dirty`, `$submitted` states will not be changed, because the expected behavior can be
28602   * different from case to case. For example, removing the only `$dirty` control from a form may or
28603   * may not mean that the form is still `$dirty`.
28604   */
28605  form.$removeControl = function(control) {
28606    if (control.$name && form[control.$name] === control) {
28607      delete form[control.$name];
28608    }
28609    forEach(form.$pending, function(value, name) {
28610      form.$setValidity(name, null, control);
28611    });
28612    forEach(form.$error, function(value, name) {
28613      form.$setValidity(name, null, control);
28614    });
28615    forEach(form.$$success, function(value, name) {
28616      form.$setValidity(name, null, control);
28617    });
28618
28619    arrayRemove(controls, control);
28620    control.$$parentForm = nullFormCtrl;
28621  };
28622
28623
28624  /**
28625   * @ngdoc method
28626   * @name form.FormController#$setValidity
28627   *
28628   * @description
28629   * Sets the validity of a form control.
28630   *
28631   * This method will also propagate to parent forms.
28632   */
28633  addSetValidityMethod({
28634    ctrl: this,
28635    $element: element,
28636    set: function(object, property, controller) {
28637      var list = object[property];
28638      if (!list) {
28639        object[property] = [controller];
28640      } else {
28641        var index = list.indexOf(controller);
28642        if (index === -1) {
28643          list.push(controller);
28644        }
28645      }
28646    },
28647    unset: function(object, property, controller) {
28648      var list = object[property];
28649      if (!list) {
28650        return;
28651      }
28652      arrayRemove(list, controller);
28653      if (list.length === 0) {
28654        delete object[property];
28655      }
28656    },
28657    $animate: $animate
28658  });
28659
28660  /**
28661   * @ngdoc method
28662   * @name form.FormController#$setDirty
28663   *
28664   * @description
28665   * Sets the form to a dirty state.
28666   *
28667   * This method can be called to add the 'ng-dirty' class and set the form to a dirty
28668   * state (ng-dirty class). This method will also propagate to parent forms.
28669   */
28670  form.$setDirty = function() {
28671    $animate.removeClass(element, PRISTINE_CLASS);
28672    $animate.addClass(element, DIRTY_CLASS);
28673    form.$dirty = true;
28674    form.$pristine = false;
28675    form.$$parentForm.$setDirty();
28676  };
28677
28678  /**
28679   * @ngdoc method
28680   * @name form.FormController#$setPristine
28681   *
28682   * @description
28683   * Sets the form to its pristine state.
28684   *
28685   * This method sets the form's `$pristine` state to true, the `$dirty` state to false, removes
28686   * the `ng-dirty` class and adds the `ng-pristine` class. Additionally, it sets the `$submitted`
28687   * state to false.
28688   *
28689   * This method will also propagate to all the controls contained in this form.
28690   *
28691   * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
28692   * saving or resetting it.
28693   */
28694  form.$setPristine = function() {
28695    $animate.setClass(element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS);
28696    form.$dirty = false;
28697    form.$pristine = true;
28698    form.$submitted = false;
28699    forEach(controls, function(control) {
28700      control.$setPristine();
28701    });
28702  };
28703
28704  /**
28705   * @ngdoc method
28706   * @name form.FormController#$setUntouched
28707   *
28708   * @description
28709   * Sets the form to its untouched state.
28710   *
28711   * This method can be called to remove the 'ng-touched' class and set the form controls to their
28712   * untouched state (ng-untouched class).
28713   *
28714   * Setting a form controls back to their untouched state is often useful when setting the form
28715   * back to its pristine state.
28716   */
28717  form.$setUntouched = function() {
28718    forEach(controls, function(control) {
28719      control.$setUntouched();
28720    });
28721  };
28722
28723  /**
28724   * @ngdoc method
28725   * @name form.FormController#$setSubmitted
28726   *
28727   * @description
28728   * Sets the form to its submitted state.
28729   */
28730  form.$setSubmitted = function() {
28731    $animate.addClass(element, SUBMITTED_CLASS);
28732    form.$submitted = true;
28733    form.$$parentForm.$setSubmitted();
28734  };
28735}
28736
28737/**
28738 * @ngdoc directive
28739 * @name ngForm
28740 * @restrict EAC
28741 *
28742 * @description
28743 * Nestable alias of {@link ng.directive:form `form`} directive. HTML
28744 * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
28745 * sub-group of controls needs to be determined.
28746 *
28747 * Note: the purpose of `ngForm` is to group controls,
28748 * but not to be a replacement for the `<form>` tag with all of its capabilities
28749 * (e.g. posting to the server, ...).
28750 *
28751 * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
28752 *                       related scope, under this name.
28753 *
28754 */
28755
28756 /**
28757 * @ngdoc directive
28758 * @name form
28759 * @restrict E
28760 *
28761 * @description
28762 * Directive that instantiates
28763 * {@link form.FormController FormController}.
28764 *
28765 * If the `name` attribute is specified, the form controller is published onto the current scope under
28766 * this name.
28767 *
28768 * # Alias: {@link ng.directive:ngForm `ngForm`}
28769 *
28770 * In Angular, forms can be nested. This means that the outer form is valid when all of the child
28771 * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
28772 * Angular provides the {@link ng.directive:ngForm `ngForm`} directive, which behaves identically to
28773 * `form` but can be nested. Nested forms can be useful, for example, if the validity of a sub-group
28774 * of controls needs to be determined.
28775 *
28776 * # CSS classes
28777 *  - `ng-valid` is set if the form is valid.
28778 *  - `ng-invalid` is set if the form is invalid.
28779 *  - `ng-pending` is set if the form is pending.
28780 *  - `ng-pristine` is set if the form is pristine.
28781 *  - `ng-dirty` is set if the form is dirty.
28782 *  - `ng-submitted` is set if the form was submitted.
28783 *
28784 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
28785 *
28786 *
28787 * # Submitting a form and preventing the default action
28788 *
28789 * Since the role of forms in client-side Angular applications is different than in classical
28790 * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
28791 * page reload that sends the data to the server. Instead some javascript logic should be triggered
28792 * to handle the form submission in an application-specific way.
28793 *
28794 * For this reason, Angular prevents the default action (form submission to the server) unless the
28795 * `<form>` element has an `action` attribute specified.
28796 *
28797 * You can use one of the following two ways to specify what javascript method should be called when
28798 * a form is submitted:
28799 *
28800 * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
28801 * - {@link ng.directive:ngClick ngClick} directive on the first
28802  *  button or input field of type submit (input[type=submit])
28803 *
28804 * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
28805 * or {@link ng.directive:ngClick ngClick} directives.
28806 * This is because of the following form submission rules in the HTML specification:
28807 *
28808 * - If a form has only one input field then hitting enter in this field triggers form submit
28809 * (`ngSubmit`)
28810 * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
28811 * doesn't trigger submit
28812 * - if a form has one or more input fields and one or more buttons or input[type=submit] then
28813 * hitting enter in any of the input fields will trigger the click handler on the *first* button or
28814 * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
28815 *
28816 * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is
28817 * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
28818 * to have access to the updated model.
28819 *
28820 * ## Animation Hooks
28821 *
28822 * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
28823 * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
28824 * other validations that are performed within the form. Animations in ngForm are similar to how
28825 * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
28826 * as JS animations.
28827 *
28828 * The following example shows a simple way to utilize CSS transitions to style a form element
28829 * that has been rendered as invalid after it has been validated:
28830 *
28831 * <pre>
28832 * //be sure to include ngAnimate as a module to hook into more
28833 * //advanced animations
28834 * .my-form {
28835 *   transition:0.5s linear all;
28836 *   background: white;
28837 * }
28838 * .my-form.ng-invalid {
28839 *   background: red;
28840 *   color:white;
28841 * }
28842 * </pre>
28843 *
28844 * @example
28845    <example name="ng-form" deps="angular-animate.js" animations="true" fixBase="true" module="formExample">
28846      <file name="index.html">
28847       <script>
28848         angular.module('formExample', [])
28849           .controller('FormController', ['$scope', function($scope) {
28850             $scope.userType = 'guest';
28851           }]);
28852       </script>
28853       <style>
28854        .my-form {
28855          transition:all linear 0.5s;
28856          background: transparent;
28857        }
28858        .my-form.ng-invalid {
28859          background: red;
28860        }
28861       </style>
28862       <form name="myForm" ng-controller="FormController" class="my-form">
28863         userType: <input name="input" ng-model="userType" required>
28864         <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
28865         <code>userType = {{userType}}</code><br>
28866         <code>myForm.input.$valid = {{myForm.input.$valid}}</code><br>
28867         <code>myForm.input.$error = {{myForm.input.$error}}</code><br>
28868         <code>myForm.$valid = {{myForm.$valid}}</code><br>
28869         <code>myForm.$error.required = {{!!myForm.$error.required}}</code><br>
28870        </form>
28871      </file>
28872      <file name="protractor.js" type="protractor">
28873        it('should initialize to model', function() {
28874          var userType = element(by.binding('userType'));
28875          var valid = element(by.binding('myForm.input.$valid'));
28876
28877          expect(userType.getText()).toContain('guest');
28878          expect(valid.getText()).toContain('true');
28879        });
28880
28881        it('should be invalid if empty', function() {
28882          var userType = element(by.binding('userType'));
28883          var valid = element(by.binding('myForm.input.$valid'));
28884          var userInput = element(by.model('userType'));
28885
28886          userInput.clear();
28887          userInput.sendKeys('');
28888
28889          expect(userType.getText()).toEqual('userType =');
28890          expect(valid.getText()).toContain('false');
28891        });
28892      </file>
28893    </example>
28894 *
28895 * @param {string=} name Name of the form. If specified, the form controller will be published into
28896 *                       related scope, under this name.
28897 */
28898var formDirectiveFactory = function(isNgForm) {
28899  return ['$timeout', '$parse', function($timeout, $parse) {
28900    var formDirective = {
28901      name: 'form',
28902      restrict: isNgForm ? 'EAC' : 'E',
28903      require: ['form', '^^?form'], //first is the form's own ctrl, second is an optional parent form
28904      controller: FormController,
28905      compile: function ngFormCompile(formElement, attr) {
28906        // Setup initial state of the control
28907        formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS);
28908
28909        var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false);
28910
28911        return {
28912          pre: function ngFormPreLink(scope, formElement, attr, ctrls) {
28913            var controller = ctrls[0];
28914
28915            // if `action` attr is not present on the form, prevent the default action (submission)
28916            if (!('action' in attr)) {
28917              // we can't use jq events because if a form is destroyed during submission the default
28918              // action is not prevented. see #1238
28919              //
28920              // IE 9 is not affected because it doesn't fire a submit event and try to do a full
28921              // page reload if the form was destroyed by submission of the form via a click handler
28922              // on a button in the form. Looks like an IE9 specific bug.
28923              var handleFormSubmission = function(event) {
28924                scope.$apply(function() {
28925                  controller.$commitViewValue();
28926                  controller.$setSubmitted();
28927                });
28928
28929                event.preventDefault();
28930              };
28931
28932              addEventListenerFn(formElement[0], 'submit', handleFormSubmission);
28933
28934              // unregister the preventDefault listener so that we don't not leak memory but in a
28935              // way that will achieve the prevention of the default action.
28936              formElement.on('$destroy', function() {
28937                $timeout(function() {
28938                  removeEventListenerFn(formElement[0], 'submit', handleFormSubmission);
28939                }, 0, false);
28940              });
28941            }
28942
28943            var parentFormCtrl = ctrls[1] || controller.$$parentForm;
28944            parentFormCtrl.$addControl(controller);
28945
28946            var setter = nameAttr ? getSetter(controller.$name) : noop;
28947
28948            if (nameAttr) {
28949              setter(scope, controller);
28950              attr.$observe(nameAttr, function(newValue) {
28951                if (controller.$name === newValue) return;
28952                setter(scope, undefined);
28953                controller.$$parentForm.$$renameControl(controller, newValue);
28954                setter = getSetter(controller.$name);
28955                setter(scope, controller);
28956              });
28957            }
28958            formElement.on('$destroy', function() {
28959              controller.$$parentForm.$removeControl(controller);
28960              setter(scope, undefined);
28961              extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
28962            });
28963          }
28964        };
28965      }
28966    };
28967
28968    return formDirective;
28969
28970    function getSetter(expression) {
28971      if (expression === '') {
28972        //create an assignable expression, so forms with an empty name can be renamed later
28973        return $parse('this[""]').assign;
28974      }
28975      return $parse(expression).assign || noop;
28976    }
28977  }];
28978};
28979
28980var formDirective = formDirectiveFactory();
28981var ngFormDirective = formDirectiveFactory(true);
28982
28983/* global
28984  VALID_CLASS: false,
28985  INVALID_CLASS: false,
28986  PRISTINE_CLASS: false,
28987  DIRTY_CLASS: false,
28988  ngModelMinErr: false
28989*/
28990
28991// Regex code was initially obtained from SO prior to modification: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
28992var ISO_DATE_REGEXP = /^\d{4,}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+(?:[+-][0-2]\d:[0-5]\d|Z)$/;
28993// See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987)
28994// Note: We are being more lenient, because browsers are too.
28995//   1. Scheme
28996//   2. Slashes
28997//   3. Username
28998//   4. Password
28999//   5. Hostname
29000//   6. Port
29001//   7. Path
29002//   8. Query
29003//   9. Fragment
29004//                 1111111111111111 222   333333    44444        55555555555555555555555     666     77777777     8888888     999
29005var URL_REGEXP = /^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i;
29006// eslint-disable-next-line max-len
29007var EMAIL_REGEXP = /^(?=.{1,254}$)(?=.{1,64}@)[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+(\.[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+)*@[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?(\.[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*$/;
29008var NUMBER_REGEXP = /^\s*(-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
29009var DATE_REGEXP = /^(\d{4,})-(\d{2})-(\d{2})$/;
29010var DATETIMELOCAL_REGEXP = /^(\d{4,})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
29011var WEEK_REGEXP = /^(\d{4,})-W(\d\d)$/;
29012var MONTH_REGEXP = /^(\d{4,})-(\d\d)$/;
29013var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
29014
29015var PARTIAL_VALIDATION_EVENTS = 'keydown wheel mousedown';
29016var PARTIAL_VALIDATION_TYPES = createMap();
29017forEach('date,datetime-local,month,time,week'.split(','), function(type) {
29018  PARTIAL_VALIDATION_TYPES[type] = true;
29019});
29020
29021var inputType = {
29022
29023  /**
29024   * @ngdoc input
29025   * @name input[text]
29026   *
29027   * @description
29028   * Standard HTML text input with angular data binding, inherited by most of the `input` elements.
29029   *
29030   *
29031   * @param {string} ngModel Assignable angular expression to data-bind to.
29032   * @param {string=} name Property name of the form under which the control is published.
29033   * @param {string=} required Adds `required` validation error key if the value is not entered.
29034   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
29035   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
29036   *    `required` when you want to data-bind to the `required` attribute.
29037   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
29038   *    minlength.
29039   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
29040   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
29041   *    any length.
29042   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
29043   *    that contains the regular expression body that will be converted to a regular expression
29044   *    as in the ngPattern directive.
29045   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
29046   *    does not match a RegExp found by evaluating the Angular expression given in the attribute value.
29047   *    If the expression evaluates to a RegExp object, then this is used directly.
29048   *    If the expression evaluates to a string, then it will be converted to a RegExp
29049   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
29050   *    `new RegExp('^abc$')`.<br />
29051   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
29052   *    start at the index of the last search's match, thus not taking the whole input value into
29053   *    account.
29054   * @param {string=} ngChange Angular expression to be executed when input changes due to user
29055   *    interaction with the input element.
29056   * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
29057   *    This parameter is ignored for input[type=password] controls, which will never trim the
29058   *    input.
29059   *
29060   * @example
29061      <example name="text-input-directive" module="textInputExample">
29062        <file name="index.html">
29063         <script>
29064           angular.module('textInputExample', [])
29065             .controller('ExampleController', ['$scope', function($scope) {
29066               $scope.example = {
29067                 text: 'guest',
29068                 word: /^\s*\w*\s*$/
29069               };
29070             }]);
29071         </script>
29072         <form name="myForm" ng-controller="ExampleController">
29073           <label>Single word:
29074             <input type="text" name="input" ng-model="example.text"
29075                    ng-pattern="example.word" required ng-trim="false">
29076           </label>
29077           <div role="alert">
29078             <span class="error" ng-show="myForm.input.$error.required">
29079               Required!</span>
29080             <span class="error" ng-show="myForm.input.$error.pattern">
29081               Single word only!</span>
29082           </div>
29083           <code>text = {{example.text}}</code><br/>
29084           <code>myForm.input.$valid = {{myForm.input.$valid}}</code><br/>
29085           <code>myForm.input.$error = {{myForm.input.$error}}</code><br/>
29086           <code>myForm.$valid = {{myForm.$valid}}</code><br/>
29087           <code>myForm.$error.required = {{!!myForm.$error.required}}</code><br/>
29088          </form>
29089        </file>
29090        <file name="protractor.js" type="protractor">
29091          var text = element(by.binding('example.text'));
29092          var valid = element(by.binding('myForm.input.$valid'));
29093          var input = element(by.model('example.text'));
29094
29095          it('should initialize to model', function() {
29096            expect(text.getText()).toContain('guest');
29097            expect(valid.getText()).toContain('true');
29098          });
29099
29100          it('should be invalid if empty', function() {
29101            input.clear();
29102            input.sendKeys('');
29103
29104            expect(text.getText()).toEqual('text =');
29105            expect(valid.getText()).toContain('false');
29106          });
29107
29108          it('should be invalid if multi word', function() {
29109            input.clear();
29110            input.sendKeys('hello world');
29111
29112            expect(valid.getText()).toContain('false');
29113          });
29114        </file>
29115      </example>
29116   */
29117  'text': textInputType,
29118
29119    /**
29120     * @ngdoc input
29121     * @name input[date]
29122     *
29123     * @description
29124     * Input with date validation and transformation. In browsers that do not yet support
29125     * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601
29126     * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many
29127     * modern browsers do not yet support this input type, it is important to provide cues to users on the
29128     * expected input format via a placeholder or label.
29129     *
29130     * The model must always be a Date object, otherwise Angular will throw an error.
29131     * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
29132     *
29133     * The timezone to be used to read/write the `Date` instance in the model can be defined using
29134     * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
29135     *
29136     * @param {string} ngModel Assignable angular expression to data-bind to.
29137     * @param {string=} name Property name of the form under which the control is published.
29138     * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
29139     *   valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
29140     *   (e.g. `min="{{minDate | date:'yyyy-MM-dd'}}"`). Note that `min` will also add native HTML5
29141     *   constraint validation.
29142     * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
29143     *   a valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
29144     *   (e.g. `max="{{maxDate | date:'yyyy-MM-dd'}}"`). Note that `max` will also add native HTML5
29145     *   constraint validation.
29146     * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO date string
29147     *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
29148     * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO date string
29149     *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
29150     * @param {string=} required Sets `required` validation error key if the value is not entered.
29151     * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
29152     *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
29153     *    `required` when you want to data-bind to the `required` attribute.
29154     * @param {string=} ngChange Angular expression to be executed when input changes due to user
29155     *    interaction with the input element.
29156     *
29157     * @example
29158     <example name="date-input-directive" module="dateInputExample">
29159     <file name="index.html">
29160       <script>
29161          angular.module('dateInputExample', [])
29162            .controller('DateController', ['$scope', function($scope) {
29163              $scope.example = {
29164                value: new Date(2013, 9, 22)
29165              };
29166            }]);
29167       </script>
29168       <form name="myForm" ng-controller="DateController as dateCtrl">
29169          <label for="exampleInput">Pick a date in 2013:</label>
29170          <input type="date" id="exampleInput" name="input" ng-model="example.value"
29171              placeholder="yyyy-MM-dd" min="2013-01-01" max="2013-12-31" required />
29172          <div role="alert">
29173            <span class="error" ng-show="myForm.input.$error.required">
29174                Required!</span>
29175            <span class="error" ng-show="myForm.input.$error.date">
29176                Not a valid date!</span>
29177           </div>
29178           <tt>value = {{example.value | date: "yyyy-MM-dd"}}</tt><br/>
29179           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
29180           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
29181           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
29182           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
29183       </form>
29184     </file>
29185     <file name="protractor.js" type="protractor">
29186        var value = element(by.binding('example.value | date: "yyyy-MM-dd"'));
29187        var valid = element(by.binding('myForm.input.$valid'));
29188
29189        // currently protractor/webdriver does not support
29190        // sending keys to all known HTML5 input controls
29191        // for various browsers (see https://github.com/angular/protractor/issues/562).
29192        function setInput(val) {
29193          // set the value of the element and force validation.
29194          var scr = "var ipt = document.getElementById('exampleInput'); " +
29195          "ipt.value = '" + val + "';" +
29196          "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
29197          browser.executeScript(scr);
29198        }
29199
29200        it('should initialize to model', function() {
29201          expect(value.getText()).toContain('2013-10-22');
29202          expect(valid.getText()).toContain('myForm.input.$valid = true');
29203        });
29204
29205        it('should be invalid if empty', function() {
29206          setInput('');
29207          expect(value.getText()).toEqual('value =');
29208          expect(valid.getText()).toContain('myForm.input.$valid = false');
29209        });
29210
29211        it('should be invalid if over max', function() {
29212          setInput('2015-01-01');
29213          expect(value.getText()).toContain('');
29214          expect(valid.getText()).toContain('myForm.input.$valid = false');
29215        });
29216     </file>
29217     </example>
29218     */
29219  'date': createDateInputType('date', DATE_REGEXP,
29220         createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']),
29221         'yyyy-MM-dd'),
29222
29223   /**
29224    * @ngdoc input
29225    * @name input[datetime-local]
29226    *
29227    * @description
29228    * Input with datetime validation and transformation. In browsers that do not yet support
29229    * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
29230    * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`.
29231    *
29232    * The model must always be a Date object, otherwise Angular will throw an error.
29233    * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
29234    *
29235    * The timezone to be used to read/write the `Date` instance in the model can be defined using
29236    * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
29237    *
29238    * @param {string} ngModel Assignable angular expression to data-bind to.
29239    * @param {string=} name Property name of the form under which the control is published.
29240    * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
29241    *   This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
29242    *   inside this attribute (e.g. `min="{{minDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
29243    *   Note that `min` will also add native HTML5 constraint validation.
29244    * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
29245    *   This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
29246    *   inside this attribute (e.g. `max="{{maxDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
29247    *   Note that `max` will also add native HTML5 constraint validation.
29248    * @param {(date|string)=} ngMin Sets the `min` validation error key to the Date / ISO datetime string
29249    *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
29250    * @param {(date|string)=} ngMax Sets the `max` validation error key to the Date / ISO datetime string
29251    *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
29252    * @param {string=} required Sets `required` validation error key if the value is not entered.
29253    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
29254    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
29255    *    `required` when you want to data-bind to the `required` attribute.
29256    * @param {string=} ngChange Angular expression to be executed when input changes due to user
29257    *    interaction with the input element.
29258    *
29259    * @example
29260    <example name="datetimelocal-input-directive" module="dateExample">
29261    <file name="index.html">
29262      <script>
29263        angular.module('dateExample', [])
29264          .controller('DateController', ['$scope', function($scope) {
29265            $scope.example = {
29266              value: new Date(2010, 11, 28, 14, 57)
29267            };
29268          }]);
29269      </script>
29270      <form name="myForm" ng-controller="DateController as dateCtrl">
29271        <label for="exampleInput">Pick a date between in 2013:</label>
29272        <input type="datetime-local" id="exampleInput" name="input" ng-model="example.value"
29273            placeholder="yyyy-MM-ddTHH:mm:ss" min="2001-01-01T00:00:00" max="2013-12-31T00:00:00" required />
29274        <div role="alert">
29275          <span class="error" ng-show="myForm.input.$error.required">
29276              Required!</span>
29277          <span class="error" ng-show="myForm.input.$error.datetimelocal">
29278              Not a valid date!</span>
29279        </div>
29280        <tt>value = {{example.value | date: "yyyy-MM-ddTHH:mm:ss"}}</tt><br/>
29281        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
29282        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
29283        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
29284        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
29285      </form>
29286    </file>
29287    <file name="protractor.js" type="protractor">
29288      var value = element(by.binding('example.value | date: "yyyy-MM-ddTHH:mm:ss"'));
29289      var valid = element(by.binding('myForm.input.$valid'));
29290
29291      // currently protractor/webdriver does not support
29292      // sending keys to all known HTML5 input controls
29293      // for various browsers (https://github.com/angular/protractor/issues/562).
29294      function setInput(val) {
29295        // set the value of the element and force validation.
29296        var scr = "var ipt = document.getElementById('exampleInput'); " +
29297        "ipt.value = '" + val + "';" +
29298        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
29299        browser.executeScript(scr);
29300      }
29301
29302      it('should initialize to model', function() {
29303        expect(value.getText()).toContain('2010-12-28T14:57:00');
29304        expect(valid.getText()).toContain('myForm.input.$valid = true');
29305      });
29306
29307      it('should be invalid if empty', function() {
29308        setInput('');
29309        expect(value.getText()).toEqual('value =');
29310        expect(valid.getText()).toContain('myForm.input.$valid = false');
29311      });
29312
29313      it('should be invalid if over max', function() {
29314        setInput('2015-01-01T23:59:00');
29315        expect(value.getText()).toContain('');
29316        expect(valid.getText()).toContain('myForm.input.$valid = false');
29317      });
29318    </file>
29319    </example>
29320    */
29321  'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP,
29322      createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']),
29323      'yyyy-MM-ddTHH:mm:ss.sss'),
29324
29325  /**
29326   * @ngdoc input
29327   * @name input[time]
29328   *
29329   * @description
29330   * Input with time validation and transformation. In browsers that do not yet support
29331   * the HTML5 time input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
29332   * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a
29333   * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`.
29334   *
29335   * The model must always be a Date object, otherwise Angular will throw an error.
29336   * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
29337   *
29338   * The timezone to be used to read/write the `Date` instance in the model can be defined using
29339   * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
29340   *
29341   * @param {string} ngModel Assignable angular expression to data-bind to.
29342   * @param {string=} name Property name of the form under which the control is published.
29343   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
29344   *   This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
29345   *   attribute (e.g. `min="{{minTime | date:'HH:mm:ss'}}"`). Note that `min` will also add
29346   *   native HTML5 constraint validation.
29347   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
29348   *   This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
29349   *   attribute (e.g. `max="{{maxTime | date:'HH:mm:ss'}}"`). Note that `max` will also add
29350   *   native HTML5 constraint validation.
29351   * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO time string the
29352   *   `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
29353   * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO time string the
29354   *   `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
29355   * @param {string=} required Sets `required` validation error key if the value is not entered.
29356   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
29357   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
29358   *    `required` when you want to data-bind to the `required` attribute.
29359   * @param {string=} ngChange Angular expression to be executed when input changes due to user
29360   *    interaction with the input element.
29361   *
29362   * @example
29363   <example name="time-input-directive" module="timeExample">
29364   <file name="index.html">
29365     <script>
29366      angular.module('timeExample', [])
29367        .controller('DateController', ['$scope', function($scope) {
29368          $scope.example = {
29369            value: new Date(1970, 0, 1, 14, 57, 0)
29370          };
29371        }]);
29372     </script>
29373     <form name="myForm" ng-controller="DateController as dateCtrl">
29374        <label for="exampleInput">Pick a time between 8am and 5pm:</label>
29375        <input type="time" id="exampleInput" name="input" ng-model="example.value"
29376            placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
29377        <div role="alert">
29378          <span class="error" ng-show="myForm.input.$error.required">
29379              Required!</span>
29380          <span class="error" ng-show="myForm.input.$error.time">
29381              Not a valid date!</span>
29382        </div>
29383        <tt>value = {{example.value | date: "HH:mm:ss"}}</tt><br/>
29384        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
29385        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
29386        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
29387        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
29388     </form>
29389   </file>
29390   <file name="protractor.js" type="protractor">
29391      var value = element(by.binding('example.value | date: "HH:mm:ss"'));
29392      var valid = element(by.binding('myForm.input.$valid'));
29393
29394      // currently protractor/webdriver does not support
29395      // sending keys to all known HTML5 input controls
29396      // for various browsers (https://github.com/angular/protractor/issues/562).
29397      function setInput(val) {
29398        // set the value of the element and force validation.
29399        var scr = "var ipt = document.getElementById('exampleInput'); " +
29400        "ipt.value = '" + val + "';" +
29401        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
29402        browser.executeScript(scr);
29403      }
29404
29405      it('should initialize to model', function() {
29406        expect(value.getText()).toContain('14:57:00');
29407        expect(valid.getText()).toContain('myForm.input.$valid = true');
29408      });
29409
29410      it('should be invalid if empty', function() {
29411        setInput('');
29412        expect(value.getText()).toEqual('value =');
29413        expect(valid.getText()).toContain('myForm.input.$valid = false');
29414      });
29415
29416      it('should be invalid if over max', function() {
29417        setInput('23:59:00');
29418        expect(value.getText()).toContain('');
29419        expect(valid.getText()).toContain('myForm.input.$valid = false');
29420      });
29421   </file>
29422   </example>
29423   */
29424  'time': createDateInputType('time', TIME_REGEXP,
29425      createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']),
29426     'HH:mm:ss.sss'),
29427
29428   /**
29429    * @ngdoc input
29430    * @name input[week]
29431    *
29432    * @description
29433    * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support
29434    * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
29435    * week format (yyyy-W##), for example: `2013-W02`.
29436    *
29437    * The model must always be a Date object, otherwise Angular will throw an error.
29438    * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
29439    *
29440    * The timezone to be used to read/write the `Date` instance in the model can be defined using
29441    * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
29442    *
29443    * @param {string} ngModel Assignable angular expression to data-bind to.
29444    * @param {string=} name Property name of the form under which the control is published.
29445    * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
29446    *   This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
29447    *   attribute (e.g. `min="{{minWeek | date:'yyyy-Www'}}"`). Note that `min` will also add
29448    *   native HTML5 constraint validation.
29449    * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
29450    *   This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
29451    *   attribute (e.g. `max="{{maxWeek | date:'yyyy-Www'}}"`). Note that `max` will also add
29452    *   native HTML5 constraint validation.
29453    * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
29454    *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
29455    * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
29456    *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
29457    * @param {string=} required Sets `required` validation error key if the value is not entered.
29458    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
29459    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
29460    *    `required` when you want to data-bind to the `required` attribute.
29461    * @param {string=} ngChange Angular expression to be executed when input changes due to user
29462    *    interaction with the input element.
29463    *
29464    * @example
29465    <example name="week-input-directive" module="weekExample">
29466    <file name="index.html">
29467      <script>
29468      angular.module('weekExample', [])
29469        .controller('DateController', ['$scope', function($scope) {
29470          $scope.example = {
29471            value: new Date(2013, 0, 3)
29472          };
29473        }]);
29474      </script>
29475      <form name="myForm" ng-controller="DateController as dateCtrl">
29476        <label>Pick a date between in 2013:
29477          <input id="exampleInput" type="week" name="input" ng-model="example.value"
29478                 placeholder="YYYY-W##" min="2012-W32"
29479                 max="2013-W52" required />
29480        </label>
29481        <div role="alert">
29482          <span class="error" ng-show="myForm.input.$error.required">
29483              Required!</span>
29484          <span class="error" ng-show="myForm.input.$error.week">
29485              Not a valid date!</span>
29486        </div>
29487        <tt>value = {{example.value | date: "yyyy-Www"}}</tt><br/>
29488        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
29489        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
29490        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
29491        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
29492      </form>
29493    </file>
29494    <file name="protractor.js" type="protractor">
29495      var value = element(by.binding('example.value | date: "yyyy-Www"'));
29496      var valid = element(by.binding('myForm.input.$valid'));
29497
29498      // currently protractor/webdriver does not support
29499      // sending keys to all known HTML5 input controls
29500      // for various browsers (https://github.com/angular/protractor/issues/562).
29501      function setInput(val) {
29502        // set the value of the element and force validation.
29503        var scr = "var ipt = document.getElementById('exampleInput'); " +
29504        "ipt.value = '" + val + "';" +
29505        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
29506        browser.executeScript(scr);
29507      }
29508
29509      it('should initialize to model', function() {
29510        expect(value.getText()).toContain('2013-W01');
29511        expect(valid.getText()).toContain('myForm.input.$valid = true');
29512      });
29513
29514      it('should be invalid if empty', function() {
29515        setInput('');
29516        expect(value.getText()).toEqual('value =');
29517        expect(valid.getText()).toContain('myForm.input.$valid = false');
29518      });
29519
29520      it('should be invalid if over max', function() {
29521        setInput('2015-W01');
29522        expect(value.getText()).toContain('');
29523        expect(valid.getText()).toContain('myForm.input.$valid = false');
29524      });
29525    </file>
29526    </example>
29527    */
29528  'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'),
29529
29530  /**
29531   * @ngdoc input
29532   * @name input[month]
29533   *
29534   * @description
29535   * Input with month validation and transformation. In browsers that do not yet support
29536   * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
29537   * month format (yyyy-MM), for example: `2009-01`.
29538   *
29539   * The model must always be a Date object, otherwise Angular will throw an error.
29540   * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
29541   * If the model is not set to the first of the month, the next view to model update will set it
29542   * to the first of the month.
29543   *
29544   * The timezone to be used to read/write the `Date` instance in the model can be defined using
29545   * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
29546   *
29547   * @param {string} ngModel Assignable angular expression to data-bind to.
29548   * @param {string=} name Property name of the form under which the control is published.
29549   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
29550   *   This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
29551   *   attribute (e.g. `min="{{minMonth | date:'yyyy-MM'}}"`). Note that `min` will also add
29552   *   native HTML5 constraint validation.
29553   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
29554   *   This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
29555   *   attribute (e.g. `max="{{maxMonth | date:'yyyy-MM'}}"`). Note that `max` will also add
29556   *   native HTML5 constraint validation.
29557   * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
29558   *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
29559   * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
29560   *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
29561
29562   * @param {string=} required Sets `required` validation error key if the value is not entered.
29563   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
29564   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
29565   *    `required` when you want to data-bind to the `required` attribute.
29566   * @param {string=} ngChange Angular expression to be executed when input changes due to user
29567   *    interaction with the input element.
29568   *
29569   * @example
29570   <example name="month-input-directive" module="monthExample">
29571   <file name="index.html">
29572     <script>
29573      angular.module('monthExample', [])
29574        .controller('DateController', ['$scope', function($scope) {
29575          $scope.example = {
29576            value: new Date(2013, 9, 1)
29577          };
29578        }]);
29579     </script>
29580     <form name="myForm" ng-controller="DateController as dateCtrl">
29581       <label for="exampleInput">Pick a month in 2013:</label>
29582       <input id="exampleInput" type="month" name="input" ng-model="example.value"
29583          placeholder="yyyy-MM" min="2013-01" max="2013-12" required />
29584       <div role="alert">
29585         <span class="error" ng-show="myForm.input.$error.required">
29586            Required!</span>
29587         <span class="error" ng-show="myForm.input.$error.month">
29588            Not a valid month!</span>
29589       </div>
29590       <tt>value = {{example.value | date: "yyyy-MM"}}</tt><br/>
29591       <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
29592       <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
29593       <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
29594       <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
29595     </form>
29596   </file>
29597   <file name="protractor.js" type="protractor">
29598      var value = element(by.binding('example.value | date: "yyyy-MM"'));
29599      var valid = element(by.binding('myForm.input.$valid'));
29600
29601      // currently protractor/webdriver does not support
29602      // sending keys to all known HTML5 input controls
29603      // for various browsers (https://github.com/angular/protractor/issues/562).
29604      function setInput(val) {
29605        // set the value of the element and force validation.
29606        var scr = "var ipt = document.getElementById('exampleInput'); " +
29607        "ipt.value = '" + val + "';" +
29608        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
29609        browser.executeScript(scr);
29610      }
29611
29612      it('should initialize to model', function() {
29613        expect(value.getText()).toContain('2013-10');
29614        expect(valid.getText()).toContain('myForm.input.$valid = true');
29615      });
29616
29617      it('should be invalid if empty', function() {
29618        setInput('');
29619        expect(value.getText()).toEqual('value =');
29620        expect(valid.getText()).toContain('myForm.input.$valid = false');
29621      });
29622
29623      it('should be invalid if over max', function() {
29624        setInput('2015-01');
29625        expect(value.getText()).toContain('');
29626        expect(valid.getText()).toContain('myForm.input.$valid = false');
29627      });
29628   </file>
29629   </example>
29630   */
29631  'month': createDateInputType('month', MONTH_REGEXP,
29632     createDateParser(MONTH_REGEXP, ['yyyy', 'MM']),
29633     'yyyy-MM'),
29634
29635  /**
29636   * @ngdoc input
29637   * @name input[number]
29638   *
29639   * @description
29640   * Text input with number validation and transformation. Sets the `number` validation
29641   * error if not a valid number.
29642   *
29643   * <div class="alert alert-warning">
29644   * The model must always be of type `number` otherwise Angular will throw an error.
29645   * Be aware that a string containing a number is not enough. See the {@link ngModel:numfmt}
29646   * error docs for more information and an example of how to convert your model if necessary.
29647   * </div>
29648   *
29649   * ## Issues with HTML5 constraint validation
29650   *
29651   * In browsers that follow the
29652   * [HTML5 specification](https://html.spec.whatwg.org/multipage/forms.html#number-state-%28type=number%29),
29653   * `input[number]` does not work as expected with {@link ngModelOptions `ngModelOptions.allowInvalid`}.
29654   * If a non-number is entered in the input, the browser will report the value as an empty string,
29655   * which means the view / model values in `ngModel` and subsequently the scope value
29656   * will also be an empty string.
29657   *
29658   *
29659   * @param {string} ngModel Assignable angular expression to data-bind to.
29660   * @param {string=} name Property name of the form under which the control is published.
29661   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
29662   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
29663   * @param {string=} required Sets `required` validation error key if the value is not entered.
29664   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
29665   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
29666   *    `required` when you want to data-bind to the `required` attribute.
29667   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
29668   *    minlength.
29669   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
29670   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
29671   *    any length.
29672   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
29673   *    that contains the regular expression body that will be converted to a regular expression
29674   *    as in the ngPattern directive.
29675   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
29676   *    does not match a RegExp found by evaluating the Angular expression given in the attribute value.
29677   *    If the expression evaluates to a RegExp object, then this is used directly.
29678   *    If the expression evaluates to a string, then it will be converted to a RegExp
29679   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
29680   *    `new RegExp('^abc$')`.<br />
29681   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
29682   *    start at the index of the last search's match, thus not taking the whole input value into
29683   *    account.
29684   * @param {string=} ngChange Angular expression to be executed when input changes due to user
29685   *    interaction with the input element.
29686   *
29687   * @example
29688      <example name="number-input-directive" module="numberExample">
29689        <file name="index.html">
29690         <script>
29691           angular.module('numberExample', [])
29692             .controller('ExampleController', ['$scope', function($scope) {
29693               $scope.example = {
29694                 value: 12
29695               };
29696             }]);
29697         </script>
29698         <form name="myForm" ng-controller="ExampleController">
29699           <label>Number:
29700             <input type="number" name="input" ng-model="example.value"
29701                    min="0" max="99" required>
29702          </label>
29703           <div role="alert">
29704             <span class="error" ng-show="myForm.input.$error.required">
29705               Required!</span>
29706             <span class="error" ng-show="myForm.input.$error.number">
29707               Not valid number!</span>
29708           </div>
29709           <tt>value = {{example.value}}</tt><br/>
29710           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
29711           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
29712           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
29713           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
29714          </form>
29715        </file>
29716        <file name="protractor.js" type="protractor">
29717          var value = element(by.binding('example.value'));
29718          var valid = element(by.binding('myForm.input.$valid'));
29719          var input = element(by.model('example.value'));
29720
29721          it('should initialize to model', function() {
29722            expect(value.getText()).toContain('12');
29723            expect(valid.getText()).toContain('true');
29724          });
29725
29726          it('should be invalid if empty', function() {
29727            input.clear();
29728            input.sendKeys('');
29729            expect(value.getText()).toEqual('value =');
29730            expect(valid.getText()).toContain('false');
29731          });
29732
29733          it('should be invalid if over max', function() {
29734            input.clear();
29735            input.sendKeys('123');
29736            expect(value.getText()).toEqual('value =');
29737            expect(valid.getText()).toContain('false');
29738          });
29739        </file>
29740      </example>
29741   */
29742  'number': numberInputType,
29743
29744
29745  /**
29746   * @ngdoc input
29747   * @name input[url]
29748   *
29749   * @description
29750   * Text input with URL validation. Sets the `url` validation error key if the content is not a
29751   * valid URL.
29752   *
29753   * <div class="alert alert-warning">
29754   * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex
29755   * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify
29756   * the built-in validators (see the {@link guide/forms Forms guide})
29757   * </div>
29758   *
29759   * @param {string} ngModel Assignable angular expression to data-bind to.
29760   * @param {string=} name Property name of the form under which the control is published.
29761   * @param {string=} required Sets `required` validation error key if the value is not entered.
29762   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
29763   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
29764   *    `required` when you want to data-bind to the `required` attribute.
29765   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
29766   *    minlength.
29767   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
29768   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
29769   *    any length.
29770   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
29771   *    that contains the regular expression body that will be converted to a regular expression
29772   *    as in the ngPattern directive.
29773   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
29774   *    does not match a RegExp found by evaluating the Angular expression given in the attribute value.
29775   *    If the expression evaluates to a RegExp object, then this is used directly.
29776   *    If the expression evaluates to a string, then it will be converted to a RegExp
29777   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
29778   *    `new RegExp('^abc$')`.<br />
29779   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
29780   *    start at the index of the last search's match, thus not taking the whole input value into
29781   *    account.
29782   * @param {string=} ngChange Angular expression to be executed when input changes due to user
29783   *    interaction with the input element.
29784   *
29785   * @example
29786      <example name="url-input-directive" module="urlExample">
29787        <file name="index.html">
29788         <script>
29789           angular.module('urlExample', [])
29790             .controller('ExampleController', ['$scope', function($scope) {
29791               $scope.url = {
29792                 text: 'http://google.com'
29793               };
29794             }]);
29795         </script>
29796         <form name="myForm" ng-controller="ExampleController">
29797           <label>URL:
29798             <input type="url" name="input" ng-model="url.text" required>
29799           <label>
29800           <div role="alert">
29801             <span class="error" ng-show="myForm.input.$error.required">
29802               Required!</span>
29803             <span class="error" ng-show="myForm.input.$error.url">
29804               Not valid url!</span>
29805           </div>
29806           <tt>text = {{url.text}}</tt><br/>
29807           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
29808           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
29809           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
29810           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
29811           <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
29812          </form>
29813        </file>
29814        <file name="protractor.js" type="protractor">
29815          var text = element(by.binding('url.text'));
29816          var valid = element(by.binding('myForm.input.$valid'));
29817          var input = element(by.model('url.text'));
29818
29819          it('should initialize to model', function() {
29820            expect(text.getText()).toContain('http://google.com');
29821            expect(valid.getText()).toContain('true');
29822          });
29823
29824          it('should be invalid if empty', function() {
29825            input.clear();
29826            input.sendKeys('');
29827
29828            expect(text.getText()).toEqual('text =');
29829            expect(valid.getText()).toContain('false');
29830          });
29831
29832          it('should be invalid if not url', function() {
29833            input.clear();
29834            input.sendKeys('box');
29835
29836            expect(valid.getText()).toContain('false');
29837          });
29838        </file>
29839      </example>
29840   */
29841  'url': urlInputType,
29842
29843
29844  /**
29845   * @ngdoc input
29846   * @name input[email]
29847   *
29848   * @description
29849   * Text input with email validation. Sets the `email` validation error key if not a valid email
29850   * address.
29851   *
29852   * <div class="alert alert-warning">
29853   * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex
29854   * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can
29855   * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide})
29856   * </div>
29857   *
29858   * @param {string} ngModel Assignable angular expression to data-bind to.
29859   * @param {string=} name Property name of the form under which the control is published.
29860   * @param {string=} required Sets `required` validation error key if the value is not entered.
29861   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
29862   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
29863   *    `required` when you want to data-bind to the `required` attribute.
29864   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
29865   *    minlength.
29866   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
29867   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
29868   *    any length.
29869   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
29870   *    that contains the regular expression body that will be converted to a regular expression
29871   *    as in the ngPattern directive.
29872   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
29873   *    does not match a RegExp found by evaluating the Angular expression given in the attribute value.
29874   *    If the expression evaluates to a RegExp object, then this is used directly.
29875   *    If the expression evaluates to a string, then it will be converted to a RegExp
29876   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
29877   *    `new RegExp('^abc$')`.<br />
29878   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
29879   *    start at the index of the last search's match, thus not taking the whole input value into
29880   *    account.
29881   * @param {string=} ngChange Angular expression to be executed when input changes due to user
29882   *    interaction with the input element.
29883   *
29884   * @example
29885      <example name="email-input-directive" module="emailExample">
29886        <file name="index.html">
29887         <script>
29888           angular.module('emailExample', [])
29889             .controller('ExampleController', ['$scope', function($scope) {
29890               $scope.email = {
29891                 text: '[email protected]'
29892               };
29893             }]);
29894         </script>
29895           <form name="myForm" ng-controller="ExampleController">
29896             <label>Email:
29897               <input type="email" name="input" ng-model="email.text" required>
29898             </label>
29899             <div role="alert">
29900               <span class="error" ng-show="myForm.input.$error.required">
29901                 Required!</span>
29902               <span class="error" ng-show="myForm.input.$error.email">
29903                 Not valid email!</span>
29904             </div>
29905             <tt>text = {{email.text}}</tt><br/>
29906             <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
29907             <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
29908             <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
29909             <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
29910             <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
29911           </form>
29912         </file>
29913        <file name="protractor.js" type="protractor">
29914          var text = element(by.binding('email.text'));
29915          var valid = element(by.binding('myForm.input.$valid'));
29916          var input = element(by.model('email.text'));
29917
29918          it('should initialize to model', function() {
29919            expect(text.getText()).toContain('[email protected]');
29920            expect(valid.getText()).toContain('true');
29921          });
29922
29923          it('should be invalid if empty', function() {
29924            input.clear();
29925            input.sendKeys('');
29926            expect(text.getText()).toEqual('text =');
29927            expect(valid.getText()).toContain('false');
29928          });
29929
29930          it('should be invalid if not email', function() {
29931            input.clear();
29932            input.sendKeys('xxx');
29933
29934            expect(valid.getText()).toContain('false');
29935          });
29936        </file>
29937      </example>
29938   */
29939  'email': emailInputType,
29940
29941
29942  /**
29943   * @ngdoc input
29944   * @name input[radio]
29945   *
29946   * @description
29947   * HTML radio button.
29948   *
29949   * @param {string} ngModel Assignable angular expression to data-bind to.
29950   * @param {string} value The value to which the `ngModel` expression should be set when selected.
29951   *    Note that `value` only supports `string` values, i.e. the scope model needs to be a string,
29952   *    too. Use `ngValue` if you need complex models (`number`, `object`, ...).
29953   * @param {string=} name Property name of the form under which the control is published.
29954   * @param {string=} ngChange Angular expression to be executed when input changes due to user
29955   *    interaction with the input element.
29956   * @param {string} ngValue Angular expression to which `ngModel` will be be set when the radio
29957   *    is selected. Should be used instead of the `value` attribute if you need
29958   *    a non-string `ngModel` (`boolean`, `array`, ...).
29959   *
29960   * @example
29961      <example name="radio-input-directive" module="radioExample">
29962        <file name="index.html">
29963         <script>
29964           angular.module('radioExample', [])
29965             .controller('ExampleController', ['$scope', function($scope) {
29966               $scope.color = {
29967                 name: 'blue'
29968               };
29969               $scope.specialValue = {
29970                 "id": "12345",
29971                 "value": "green"
29972               };
29973             }]);
29974         </script>
29975         <form name="myForm" ng-controller="ExampleController">
29976           <label>
29977             <input type="radio" ng-model="color.name" value="red">
29978             Red
29979           </label><br/>
29980           <label>
29981             <input type="radio" ng-model="color.name" ng-value="specialValue">
29982             Green
29983           </label><br/>
29984           <label>
29985             <input type="radio" ng-model="color.name" value="blue">
29986             Blue
29987           </label><br/>
29988           <tt>color = {{color.name | json}}</tt><br/>
29989          </form>
29990          Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
29991        </file>
29992        <file name="protractor.js" type="protractor">
29993          it('should change state', function() {
29994            var inputs = element.all(by.model('color.name'));
29995            var color = element(by.binding('color.name'));
29996
29997            expect(color.getText()).toContain('blue');
29998
29999            inputs.get(0).click();
30000            expect(color.getText()).toContain('red');
30001
30002            inputs.get(1).click();
30003            expect(color.getText()).toContain('green');
30004          });
30005        </file>
30006      </example>
30007   */
30008  'radio': radioInputType,
30009
30010  /**
30011   * @ngdoc input
30012   * @name input[range]
30013   *
30014   * @description
30015   * Native range input with validation and transformation.
30016   *
30017   * <div class="alert alert-warning">
30018   *   <p>
30019   *     In v1.5.9+, in order to avoid interfering with already existing, custom directives for
30020   *     `input[range]`, you need to let Angular know that you want to enable its built-in support.
30021   *     You can do this by adding the `ng-input-range` attribute to the input element. E.g.:
30022   *     `<input type="range" ng-input-range ... />`
30023   *   </p><br />
30024   *   <p>
30025   *     Input elements without the `ng-input-range` attibute will continue to be treated the same
30026   *     as in previous versions (e.g. their model value will be a string not a number and Angular
30027   *     will not take `min`/`max`/`step` attributes and properties into account).
30028   *   </p><br />
30029   *   <p>
30030   *     **Note:** From v1.6.x onwards, the support for `input[range]` will be always enabled and
30031   *     the `ng-input-range` attribute will have no effect.
30032   *   </p><br />
30033   *   <p>
30034   *     This documentation page refers to elements which have the built-in support enabled; i.e.
30035   *     elements _with_ the `ng-input-range` attribute.
30036   *   </p>
30037   * </div>
30038   *
30039   * The model for the range input must always be a `Number`.
30040   *
30041   * IE9 and other browsers that do not support the `range` type fall back
30042   * to a text input without any default values for `min`, `max` and `step`. Model binding,
30043   * validation and number parsing are nevertheless supported.
30044   *
30045   * Browsers that support range (latest Chrome, Safari, Firefox, Edge) treat `input[range]`
30046   * in a way that never allows the input to hold an invalid value. That means:
30047   * - any non-numerical value is set to `(max + min) / 2`.
30048   * - any numerical value that is less than the current min val, or greater than the current max val
30049   * is set to the min / max val respectively.
30050   * - additionally, the current `step` is respected, so the nearest value that satisfies a step
30051   * is used.
30052   *
30053   * See the [HTML Spec on input[type=range]](https://www.w3.org/TR/html5/forms.html#range-state-(type=range))
30054   * for more info.
30055   *
30056   * This has the following consequences for Angular:
30057   *
30058   * Since the element value should always reflect the current model value, a range input
30059   * will set the bound ngModel expression to the value that the browser has set for the
30060   * input element. For example, in the following input `<input type="range" ng-input-range ng-model="model.value">`,
30061   * if the application sets `model.value = null`, the browser will set the input to `'50'`.
30062   * Angular will then set the model to `50`, to prevent input and model value being out of sync.
30063   *
30064   * That means the model for range will immediately be set to `50` after `ngModel` has been
30065   * initialized. It also means a range input can never have the required error.
30066   *
30067   * This does not only affect changes to the model value, but also to the values of the `min`,
30068   * `max`, and `step` attributes. When these change in a way that will cause the browser to modify
30069   * the input value, Angular will also update the model value.
30070   *
30071   * Automatic value adjustment also means that a range input element can never have the `required`,
30072   * `min`, or `max` errors.
30073   *
30074   * However, `step` is currently only fully implemented by Firefox. Other browsers have problems
30075   * when the step value changes dynamically - they do not adjust the element value correctly, but
30076   * instead may set the `stepMismatch` error. If that's the case, the Angular will set the `step`
30077   * error on the input, and set the model to `undefined`.
30078   *
30079   * Note that `input[range]` is not compatible with `ngMax`, `ngMin`, and `ngStep`, because they do
30080   * not set the `min` and `max` attributes, which means that the browser won't automatically adjust
30081   * the input value based on their values, and will always assume min = 0, max = 100, and step = 1.
30082   *
30083   * @param           ngInputRange The presense of this attribute enables the built-in support for
30084   *                  `input[range]`.
30085   * @param {string}  ngModel Assignable angular expression to data-bind to.
30086   * @param {string=} name Property name of the form under which the control is published.
30087   * @param {string=} min Sets the `min` validation to ensure that the value entered is greater
30088   *                  than `min`. Can be interpolated.
30089   * @param {string=} max Sets the `max` validation to ensure that the value entered is less than `max`.
30090   *                  Can be interpolated.
30091   * @param {string=} step Sets the `step` validation to ensure that the value entered matches the `step`
30092   *                  Can be interpolated.
30093   * @param {string=} ngChange Angular expression to be executed when the ngModel value changes due
30094   *                  to user interaction with the input element.
30095   * @param {expression=} ngChecked If the expression is truthy, then the `checked` attribute will be set on the
30096   *                      element. **Note** : `ngChecked` should not be used alongside `ngModel`.
30097   *                      Checkout {@link ng.directive:ngChecked ngChecked} for usage.
30098   *
30099   * @example
30100      <example name="range-input-directive" module="rangeExample">
30101        <file name="index.html">
30102          <script>
30103            angular.module('rangeExample', [])
30104              .controller('ExampleController', ['$scope', function($scope) {
30105                $scope.value = 75;
30106                $scope.min = 10;
30107                $scope.max = 90;
30108              }]);
30109          </script>
30110          <form name="myForm" ng-controller="ExampleController">
30111
30112            Model as range: <input type="range" ng-input-range name="range" ng-model="value" min="{{min}}"  max="{{max}}">
30113            <hr>
30114            Model as number: <input type="number" ng-model="value"><br>
30115            Min: <input type="number" ng-model="min"><br>
30116            Max: <input type="number" ng-model="max"><br>
30117            value = <code>{{value}}</code><br/>
30118            myForm.range.$valid = <code>{{myForm.range.$valid}}</code><br/>
30119            myForm.range.$error = <code>{{myForm.range.$error}}</code>
30120          </form>
30121        </file>
30122      </example>
30123
30124   * ## Range Input with ngMin & ngMax attributes
30125
30126   * @example
30127      <example name="range-input-directive-ng" module="rangeExample">
30128        <file name="index.html">
30129          <script>
30130            angular.module('rangeExample', [])
30131              .controller('ExampleController', ['$scope', function($scope) {
30132                $scope.value = 75;
30133                $scope.min = 10;
30134                $scope.max = 90;
30135              }]);
30136          </script>
30137          <form name="myForm" ng-controller="ExampleController">
30138            Model as range: <input type="range" ng-input-range name="range" ng-model="value" ng-min="min" ng-max="max">
30139            <hr>
30140            Model as number: <input type="number" ng-model="value"><br>
30141            Min: <input type="number" ng-model="min"><br>
30142            Max: <input type="number" ng-model="max"><br>
30143            value = <code>{{value}}</code><br/>
30144            myForm.range.$valid = <code>{{myForm.range.$valid}}</code><br/>
30145            myForm.range.$error = <code>{{myForm.range.$error}}</code>
30146          </form>
30147        </file>
30148      </example>
30149
30150   */
30151  'range': rangeInputType,
30152
30153  /**
30154   * @ngdoc input
30155   * @name input[checkbox]
30156   *
30157   * @description
30158   * HTML checkbox.
30159   *
30160   * @param {string} ngModel Assignable angular expression to data-bind to.
30161   * @param {string=} name Property name of the form under which the control is published.
30162   * @param {expression=} ngTrueValue The value to which the expression should be set when selected.
30163   * @param {expression=} ngFalseValue The value to which the expression should be set when not selected.
30164   * @param {string=} ngChange Angular expression to be executed when input changes due to user
30165   *    interaction with the input element.
30166   *
30167   * @example
30168      <example name="checkbox-input-directive" module="checkboxExample">
30169        <file name="index.html">
30170         <script>
30171           angular.module('checkboxExample', [])
30172             .controller('ExampleController', ['$scope', function($scope) {
30173               $scope.checkboxModel = {
30174                value1 : true,
30175                value2 : 'YES'
30176              };
30177             }]);
30178         </script>
30179         <form name="myForm" ng-controller="ExampleController">
30180           <label>Value1:
30181             <input type="checkbox" ng-model="checkboxModel.value1">
30182           </label><br/>
30183           <label>Value2:
30184             <input type="checkbox" ng-model="checkboxModel.value2"
30185                    ng-true-value="'YES'" ng-false-value="'NO'">
30186            </label><br/>
30187           <tt>value1 = {{checkboxModel.value1}}</tt><br/>
30188           <tt>value2 = {{checkboxModel.value2}}</tt><br/>
30189          </form>
30190        </file>
30191        <file name="protractor.js" type="protractor">
30192          it('should change state', function() {
30193            var value1 = element(by.binding('checkboxModel.value1'));
30194            var value2 = element(by.binding('checkboxModel.value2'));
30195
30196            expect(value1.getText()).toContain('true');
30197            expect(value2.getText()).toContain('YES');
30198
30199            element(by.model('checkboxModel.value1')).click();
30200            element(by.model('checkboxModel.value2')).click();
30201
30202            expect(value1.getText()).toContain('false');
30203            expect(value2.getText()).toContain('NO');
30204          });
30205        </file>
30206      </example>
30207   */
30208  'checkbox': checkboxInputType,
30209
30210  'hidden': noop,
30211  'button': noop,
30212  'submit': noop,
30213  'reset': noop,
30214  'file': noop
30215};
30216
30217function stringBasedInputType(ctrl) {
30218  ctrl.$formatters.push(function(value) {
30219    return ctrl.$isEmpty(value) ? value : value.toString();
30220  });
30221}
30222
30223function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
30224  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
30225  stringBasedInputType(ctrl);
30226}
30227
30228function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
30229  var type = lowercase(element[0].type);
30230
30231  // In composition mode, users are still inputting intermediate text buffer,
30232  // hold the listener until composition is done.
30233  // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
30234  if (!$sniffer.android) {
30235    var composing = false;
30236
30237    element.on('compositionstart', function() {
30238      composing = true;
30239    });
30240
30241    element.on('compositionend', function() {
30242      composing = false;
30243      listener();
30244    });
30245  }
30246
30247  var timeout;
30248
30249  var listener = function(ev) {
30250    if (timeout) {
30251      $browser.defer.cancel(timeout);
30252      timeout = null;
30253    }
30254    if (composing) return;
30255    var value = element.val(),
30256        event = ev && ev.type;
30257
30258    // By default we will trim the value
30259    // If the attribute ng-trim exists we will avoid trimming
30260    // If input type is 'password', the value is never trimmed
30261    if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) {
30262      value = trim(value);
30263    }
30264
30265    // If a control is suffering from bad input (due to native validators), browsers discard its
30266    // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the
30267    // control's value is the same empty value twice in a row.
30268    if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) {
30269      ctrl.$setViewValue(value, event);
30270    }
30271  };
30272
30273  // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the
30274  // input event on backspace, delete or cut
30275  if ($sniffer.hasEvent('input')) {
30276    element.on('input', listener);
30277  } else {
30278    var deferListener = function(ev, input, origValue) {
30279      if (!timeout) {
30280        timeout = $browser.defer(function() {
30281          timeout = null;
30282          if (!input || input.value !== origValue) {
30283            listener(ev);
30284          }
30285        });
30286      }
30287    };
30288
30289    element.on('keydown', /** @this */ function(event) {
30290      var key = event.keyCode;
30291
30292      // ignore
30293      //    command            modifiers                   arrows
30294      if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
30295
30296      deferListener(event, this, this.value);
30297    });
30298
30299    // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
30300    if ($sniffer.hasEvent('paste')) {
30301      element.on('paste cut', deferListener);
30302    }
30303  }
30304
30305  // if user paste into input using mouse on older browser
30306  // or form autocomplete on newer browser, we need "change" event to catch it
30307  element.on('change', listener);
30308
30309  // Some native input types (date-family) have the ability to change validity without
30310  // firing any input/change events.
30311  // For these event types, when native validators are present and the browser supports the type,
30312  // check for validity changes on various DOM events.
30313  if (PARTIAL_VALIDATION_TYPES[type] && ctrl.$$hasNativeValidators && type === attr.type) {
30314    element.on(PARTIAL_VALIDATION_EVENTS, /** @this */ function(ev) {
30315      if (!timeout) {
30316        var validity = this[VALIDITY_STATE_PROPERTY];
30317        var origBadInput = validity.badInput;
30318        var origTypeMismatch = validity.typeMismatch;
30319        timeout = $browser.defer(function() {
30320          timeout = null;
30321          if (validity.badInput !== origBadInput || validity.typeMismatch !== origTypeMismatch) {
30322            listener(ev);
30323          }
30324        });
30325      }
30326    });
30327  }
30328
30329  ctrl.$render = function() {
30330    // Workaround for Firefox validation #12102.
30331    var value = ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue;
30332    if (element.val() !== value) {
30333      element.val(value);
30334    }
30335  };
30336}
30337
30338function weekParser(isoWeek, existingDate) {
30339  if (isDate(isoWeek)) {
30340    return isoWeek;
30341  }
30342
30343  if (isString(isoWeek)) {
30344    WEEK_REGEXP.lastIndex = 0;
30345    var parts = WEEK_REGEXP.exec(isoWeek);
30346    if (parts) {
30347      var year = +parts[1],
30348          week = +parts[2],
30349          hours = 0,
30350          minutes = 0,
30351          seconds = 0,
30352          milliseconds = 0,
30353          firstThurs = getFirstThursdayOfYear(year),
30354          addDays = (week - 1) * 7;
30355
30356      if (existingDate) {
30357        hours = existingDate.getHours();
30358        minutes = existingDate.getMinutes();
30359        seconds = existingDate.getSeconds();
30360        milliseconds = existingDate.getMilliseconds();
30361      }
30362
30363      return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds);
30364    }
30365  }
30366
30367  return NaN;
30368}
30369
30370function createDateParser(regexp, mapping) {
30371  return function(iso, date) {
30372    var parts, map;
30373
30374    if (isDate(iso)) {
30375      return iso;
30376    }
30377
30378    if (isString(iso)) {
30379      // When a date is JSON'ified to wraps itself inside of an extra
30380      // set of double quotes. This makes the date parsing code unable
30381      // to match the date string and parse it as a date.
30382      if (iso.charAt(0) === '"' && iso.charAt(iso.length - 1) === '"') {
30383        iso = iso.substring(1, iso.length - 1);
30384      }
30385      if (ISO_DATE_REGEXP.test(iso)) {
30386        return new Date(iso);
30387      }
30388      regexp.lastIndex = 0;
30389      parts = regexp.exec(iso);
30390
30391      if (parts) {
30392        parts.shift();
30393        if (date) {
30394          map = {
30395            yyyy: date.getFullYear(),
30396            MM: date.getMonth() + 1,
30397            dd: date.getDate(),
30398            HH: date.getHours(),
30399            mm: date.getMinutes(),
30400            ss: date.getSeconds(),
30401            sss: date.getMilliseconds() / 1000
30402          };
30403        } else {
30404          map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 };
30405        }
30406
30407        forEach(parts, function(part, index) {
30408          if (index < mapping.length) {
30409            map[mapping[index]] = +part;
30410          }
30411        });
30412        return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0);
30413      }
30414    }
30415
30416    return NaN;
30417  };
30418}
30419
30420function createDateInputType(type, regexp, parseDate, format) {
30421  return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
30422    badInputChecker(scope, element, attr, ctrl);
30423    baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
30424    var timezone = ctrl && ctrl.$options && ctrl.$options.timezone;
30425    var previousDate;
30426
30427    ctrl.$$parserName = type;
30428    ctrl.$parsers.push(function(value) {
30429      if (ctrl.$isEmpty(value)) return null;
30430      if (regexp.test(value)) {
30431        // Note: We cannot read ctrl.$modelValue, as there might be a different
30432        // parser/formatter in the processing chain so that the model
30433        // contains some different data format!
30434        var parsedDate = parseDate(value, previousDate);
30435        if (timezone) {
30436          parsedDate = convertTimezoneToLocal(parsedDate, timezone);
30437        }
30438        return parsedDate;
30439      }
30440      return undefined;
30441    });
30442
30443    ctrl.$formatters.push(function(value) {
30444      if (value && !isDate(value)) {
30445        throw ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
30446      }
30447      if (isValidDate(value)) {
30448        previousDate = value;
30449        if (previousDate && timezone) {
30450          previousDate = convertTimezoneToLocal(previousDate, timezone, true);
30451        }
30452        return $filter('date')(value, format, timezone);
30453      } else {
30454        previousDate = null;
30455        return '';
30456      }
30457    });
30458
30459    if (isDefined(attr.min) || attr.ngMin) {
30460      var minVal;
30461      ctrl.$validators.min = function(value) {
30462        return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal;
30463      };
30464      attr.$observe('min', function(val) {
30465        minVal = parseObservedDateValue(val);
30466        ctrl.$validate();
30467      });
30468    }
30469
30470    if (isDefined(attr.max) || attr.ngMax) {
30471      var maxVal;
30472      ctrl.$validators.max = function(value) {
30473        return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;
30474      };
30475      attr.$observe('max', function(val) {
30476        maxVal = parseObservedDateValue(val);
30477        ctrl.$validate();
30478      });
30479    }
30480
30481    function isValidDate(value) {
30482      // Invalid Date: getTime() returns NaN
30483      return value && !(value.getTime && value.getTime() !== value.getTime());
30484    }
30485
30486    function parseObservedDateValue(val) {
30487      return isDefined(val) && !isDate(val) ? parseDate(val) || undefined : val;
30488    }
30489  };
30490}
30491
30492function badInputChecker(scope, element, attr, ctrl) {
30493  var node = element[0];
30494  var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity);
30495  if (nativeValidation) {
30496    ctrl.$parsers.push(function(value) {
30497      var validity = element.prop(VALIDITY_STATE_PROPERTY) || {};
30498      return validity.badInput || validity.typeMismatch ? undefined : value;
30499    });
30500  }
30501}
30502
30503function numberFormatterParser(ctrl) {
30504  ctrl.$$parserName = 'number';
30505  ctrl.$parsers.push(function(value) {
30506    if (ctrl.$isEmpty(value))      return null;
30507    if (NUMBER_REGEXP.test(value)) return parseFloat(value);
30508    return undefined;
30509  });
30510
30511  ctrl.$formatters.push(function(value) {
30512    if (!ctrl.$isEmpty(value)) {
30513      if (!isNumber(value)) {
30514        throw ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value);
30515      }
30516      value = value.toString();
30517    }
30518    return value;
30519  });
30520}
30521
30522function parseNumberAttrVal(val) {
30523  if (isDefined(val) && !isNumber(val)) {
30524    val = parseFloat(val);
30525  }
30526  return !isNumberNaN(val) ? val : undefined;
30527}
30528
30529function isNumberInteger(num) {
30530  // See http://stackoverflow.com/questions/14636536/how-to-check-if-a-variable-is-an-integer-in-javascript#14794066
30531  // (minus the assumption that `num` is a number)
30532
30533  // eslint-disable-next-line no-bitwise
30534  return (num | 0) === num;
30535}
30536
30537function countDecimals(num) {
30538  var numString = num.toString();
30539  var decimalSymbolIndex = numString.indexOf('.');
30540
30541  if (decimalSymbolIndex === -1) {
30542    if (-1 < num && num < 1) {
30543      // It may be in the exponential notation format (`1e-X`)
30544      var match = /e-(\d+)$/.exec(numString);
30545
30546      if (match) {
30547        return Number(match[1]);
30548      }
30549    }
30550
30551    return 0;
30552  }
30553
30554  return numString.length - decimalSymbolIndex - 1;
30555}
30556
30557function isValidForStep(viewValue, stepBase, step) {
30558  // At this point `stepBase` and `step` are expected to be non-NaN values
30559  // and `viewValue` is expected to be a valid stringified number.
30560  var value = Number(viewValue);
30561
30562  // Due to limitations in Floating Point Arithmetic (e.g. `0.3 - 0.2 !== 0.1` or
30563  // `0.5 % 0.1 !== 0`), we need to convert all numbers to integers.
30564  if (!isNumberInteger(value) || !isNumberInteger(stepBase) || !isNumberInteger(step)) {
30565    var decimalCount = Math.max(countDecimals(value), countDecimals(stepBase), countDecimals(step));
30566    var multiplier = Math.pow(10, decimalCount);
30567
30568    value = value * multiplier;
30569    stepBase = stepBase * multiplier;
30570    step = step * multiplier;
30571  }
30572
30573  return (value - stepBase) % step === 0;
30574}
30575
30576function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
30577  badInputChecker(scope, element, attr, ctrl);
30578  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
30579  numberFormatterParser(ctrl);
30580
30581  var minVal;
30582  var maxVal;
30583
30584  if (isDefined(attr.min) || attr.ngMin) {
30585    ctrl.$validators.min = function(value) {
30586      return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal;
30587    };
30588
30589    attr.$observe('min', function(val) {
30590      minVal = parseNumberAttrVal(val);
30591      // TODO(matsko): implement validateLater to reduce number of validations
30592      ctrl.$validate();
30593    });
30594  }
30595
30596  if (isDefined(attr.max) || attr.ngMax) {
30597    ctrl.$validators.max = function(value) {
30598      return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal;
30599    };
30600
30601    attr.$observe('max', function(val) {
30602      maxVal = parseNumberAttrVal(val);
30603      // TODO(matsko): implement validateLater to reduce number of validations
30604      ctrl.$validate();
30605    });
30606  }
30607}
30608
30609function rangeInputType(scope, element, attr, ctrl, $sniffer, $browser) {
30610  badInputChecker(scope, element, attr, ctrl);
30611  numberFormatterParser(ctrl);
30612  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
30613
30614  var supportsRange = ctrl.$$hasNativeValidators && element[0].type === 'range',
30615      minVal = supportsRange ? 0 : undefined,
30616      maxVal = supportsRange ? 100 : undefined,
30617      stepVal = supportsRange ? 1 : undefined,
30618      validity = element[0].validity,
30619      hasMinAttr = isDefined(attr.min),
30620      hasMaxAttr = isDefined(attr.max),
30621      hasStepAttr = isDefined(attr.step);
30622
30623  var originalRender = ctrl.$render;
30624
30625  ctrl.$render = supportsRange && isDefined(validity.rangeUnderflow) && isDefined(validity.rangeOverflow) ?
30626    //Browsers that implement range will set these values automatically, but reading the adjusted values after
30627    //$render would cause the min / max validators to be applied with the wrong value
30628    function rangeRender() {
30629      originalRender();
30630      ctrl.$setViewValue(element.val());
30631    } :
30632    originalRender;
30633
30634  if (hasMinAttr) {
30635    ctrl.$validators.min = supportsRange ?
30636      // Since all browsers set the input to a valid value, we don't need to check validity
30637      function noopMinValidator() { return true; } :
30638      // non-support browsers validate the min val
30639      function minValidator(modelValue, viewValue) {
30640        return ctrl.$isEmpty(viewValue) || isUndefined(minVal) || viewValue >= minVal;
30641      };
30642
30643    setInitialValueAndObserver('min', minChange);
30644  }
30645
30646  if (hasMaxAttr) {
30647    ctrl.$validators.max = supportsRange ?
30648      // Since all browsers set the input to a valid value, we don't need to check validity
30649      function noopMaxValidator() { return true; } :
30650      // non-support browsers validate the max val
30651      function maxValidator(modelValue, viewValue) {
30652        return ctrl.$isEmpty(viewValue) || isUndefined(maxVal) || viewValue <= maxVal;
30653      };
30654
30655    setInitialValueAndObserver('max', maxChange);
30656  }
30657
30658  if (hasStepAttr) {
30659    ctrl.$validators.step = supportsRange ?
30660      function nativeStepValidator() {
30661        // Currently, only FF implements the spec on step change correctly (i.e. adjusting the
30662        // input element value to a valid value). It's possible that other browsers set the stepMismatch
30663        // validity error instead, so we can at least report an error in that case.
30664        return !validity.stepMismatch;
30665      } :
30666      // ngStep doesn't set the setp attr, so the browser doesn't adjust the input value as setting step would
30667      function stepValidator(modelValue, viewValue) {
30668        return ctrl.$isEmpty(viewValue) || isUndefined(stepVal) ||
30669               isValidForStep(viewValue, minVal || 0, stepVal);
30670      };
30671
30672    setInitialValueAndObserver('step', stepChange);
30673  }
30674
30675  function setInitialValueAndObserver(htmlAttrName, changeFn) {
30676    // interpolated attributes set the attribute value only after a digest, but we need the
30677    // attribute value when the input is first rendered, so that the browser can adjust the
30678    // input value based on the min/max value
30679    element.attr(htmlAttrName, attr[htmlAttrName]);
30680    attr.$observe(htmlAttrName, changeFn);
30681  }
30682
30683  function minChange(val) {
30684    minVal = parseNumberAttrVal(val);
30685    // ignore changes before model is initialized
30686    if (isNumberNaN(ctrl.$modelValue)) {
30687      return;
30688    }
30689
30690    if (supportsRange) {
30691      var elVal = element.val();
30692      // IE11 doesn't set the el val correctly if the minVal is greater than the element value
30693      if (minVal > elVal) {
30694        elVal = minVal;
30695        element.val(elVal);
30696      }
30697      ctrl.$setViewValue(elVal);
30698    } else {
30699      // TODO(matsko): implement validateLater to reduce number of validations
30700      ctrl.$validate();
30701    }
30702  }
30703
30704  function maxChange(val) {
30705    maxVal = parseNumberAttrVal(val);
30706    // ignore changes before model is initialized
30707    if (isNumberNaN(ctrl.$modelValue)) {
30708      return;
30709    }
30710
30711    if (supportsRange) {
30712      var elVal = element.val();
30713      // IE11 doesn't set the el val correctly if the maxVal is less than the element value
30714      if (maxVal < elVal) {
30715        element.val(maxVal);
30716        // IE11 and Chrome don't set the value to the minVal when max < min
30717        elVal = maxVal < minVal ? minVal : maxVal;
30718      }
30719      ctrl.$setViewValue(elVal);
30720    } else {
30721      // TODO(matsko): implement validateLater to reduce number of validations
30722      ctrl.$validate();
30723    }
30724  }
30725
30726  function stepChange(val) {
30727    stepVal = parseNumberAttrVal(val);
30728    // ignore changes before model is initialized
30729    if (isNumberNaN(ctrl.$modelValue)) {
30730      return;
30731    }
30732
30733    // Some browsers don't adjust the input value correctly, but set the stepMismatch error
30734    if (supportsRange && ctrl.$viewValue !== element.val()) {
30735      ctrl.$setViewValue(element.val());
30736    } else {
30737      // TODO(matsko): implement validateLater to reduce number of validations
30738      ctrl.$validate();
30739    }
30740  }
30741}
30742
30743function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
30744  // Note: no badInputChecker here by purpose as `url` is only a validation
30745  // in browsers, i.e. we can always read out input.value even if it is not valid!
30746  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
30747  stringBasedInputType(ctrl);
30748
30749  ctrl.$$parserName = 'url';
30750  ctrl.$validators.url = function(modelValue, viewValue) {
30751    var value = modelValue || viewValue;
30752    return ctrl.$isEmpty(value) || URL_REGEXP.test(value);
30753  };
30754}
30755
30756function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
30757  // Note: no badInputChecker here by purpose as `url` is only a validation
30758  // in browsers, i.e. we can always read out input.value even if it is not valid!
30759  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
30760  stringBasedInputType(ctrl);
30761
30762  ctrl.$$parserName = 'email';
30763  ctrl.$validators.email = function(modelValue, viewValue) {
30764    var value = modelValue || viewValue;
30765    return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value);
30766  };
30767}
30768
30769function radioInputType(scope, element, attr, ctrl) {
30770  // make the name unique, if not defined
30771  if (isUndefined(attr.name)) {
30772    element.attr('name', nextUid());
30773  }
30774
30775  var listener = function(ev) {
30776    if (element[0].checked) {
30777      ctrl.$setViewValue(attr.value, ev && ev.type);
30778    }
30779  };
30780
30781  element.on('click', listener);
30782
30783  ctrl.$render = function() {
30784    var value = attr.value;
30785    // We generally use strict comparison. This is behavior we cannot change without a BC.
30786    // eslint-disable-next-line eqeqeq
30787    element[0].checked = (value == ctrl.$viewValue);
30788  };
30789
30790  attr.$observe('value', ctrl.$render);
30791}
30792
30793function parseConstantExpr($parse, context, name, expression, fallback) {
30794  var parseFn;
30795  if (isDefined(expression)) {
30796    parseFn = $parse(expression);
30797    if (!parseFn.constant) {
30798      throw ngModelMinErr('constexpr', 'Expected constant expression for `{0}`, but saw ' +
30799                                   '`{1}`.', name, expression);
30800    }
30801    return parseFn(context);
30802  }
30803  return fallback;
30804}
30805
30806function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
30807  var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true);
30808  var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false);
30809
30810  var listener = function(ev) {
30811    ctrl.$setViewValue(element[0].checked, ev && ev.type);
30812  };
30813
30814  element.on('click', listener);
30815
30816  ctrl.$render = function() {
30817    element[0].checked = ctrl.$viewValue;
30818  };
30819
30820  // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false`
30821  // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert
30822  // it to a boolean.
30823  ctrl.$isEmpty = function(value) {
30824    return value === false;
30825  };
30826
30827  ctrl.$formatters.push(function(value) {
30828    return equals(value, trueValue);
30829  });
30830
30831  ctrl.$parsers.push(function(value) {
30832    return value ? trueValue : falseValue;
30833  });
30834}
30835
30836
30837/**
30838 * @ngdoc directive
30839 * @name textarea
30840 * @restrict E
30841 *
30842 * @description
30843 * HTML textarea element control with angular data-binding. The data-binding and validation
30844 * properties of this element are exactly the same as those of the
30845 * {@link ng.directive:input input element}.
30846 *
30847 * @param {string} ngModel Assignable angular expression to data-bind to.
30848 * @param {string=} name Property name of the form under which the control is published.
30849 * @param {string=} required Sets `required` validation error key if the value is not entered.
30850 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
30851 *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
30852 *    `required` when you want to data-bind to the `required` attribute.
30853 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
30854 *    minlength.
30855 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
30856 *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
30857 *    length.
30858 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
30859 *    does not match a RegExp found by evaluating the Angular expression given in the attribute value.
30860 *    If the expression evaluates to a RegExp object, then this is used directly.
30861 *    If the expression evaluates to a string, then it will be converted to a RegExp
30862 *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
30863 *    `new RegExp('^abc$')`.<br />
30864 *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
30865 *    start at the index of the last search's match, thus not taking the whole input value into
30866 *    account.
30867 * @param {string=} ngChange Angular expression to be executed when input changes due to user
30868 *    interaction with the input element.
30869 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
30870 *
30871 * @knownIssue
30872 *
30873 * When specifying the `placeholder` attribute of `<textarea>`, Internet Explorer will temporarily
30874 * insert the placeholder value as the textarea's content. If the placeholder value contains
30875 * interpolation (`{{ ... }}`), an error will be logged in the console when Angular tries to update
30876 * the value of the by-then-removed text node. This doesn't affect the functionality of the
30877 * textarea, but can be undesirable.
30878 *
30879 * You can work around this Internet Explorer issue by using `ng-attr-placeholder` instead of
30880 * `placeholder` on textareas, whenever you need interpolation in the placeholder value. You can
30881 * find more details on `ngAttr` in the
30882 * [Interpolation](guide/interpolation#-ngattr-for-binding-to-arbitrary-attributes) section of the
30883 * Developer Guide.
30884 */
30885
30886
30887/**
30888 * @ngdoc directive
30889 * @name input
30890 * @restrict E
30891 *
30892 * @description
30893 * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding,
30894 * input state control, and validation.
30895 * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers.
30896 *
30897 * <div class="alert alert-warning">
30898 * **Note:** Not every feature offered is available for all input types.
30899 * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`.
30900 * </div>
30901 *
30902 * @param {string} ngModel Assignable angular expression to data-bind to.
30903 * @param {string=} name Property name of the form under which the control is published.
30904 * @param {string=} required Sets `required` validation error key if the value is not entered.
30905 * @param {boolean=} ngRequired Sets `required` attribute if set to true
30906 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
30907 *    minlength.
30908 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
30909 *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
30910 *    length.
30911 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
30912 *    value does not match a RegExp found by evaluating the Angular expression given in the attribute value.
30913 *    If the expression evaluates to a RegExp object, then this is used directly.
30914 *    If the expression evaluates to a string, then it will be converted to a RegExp
30915 *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
30916 *    `new RegExp('^abc$')`.<br />
30917 *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
30918 *    start at the index of the last search's match, thus not taking the whole input value into
30919 *    account.
30920 * @param {string=} ngChange Angular expression to be executed when input changes due to user
30921 *    interaction with the input element.
30922 * @param {boolean=} [ngTrim=true] If set to false Angular
vendor: 4,623 bytes, lines 30922-31032
30922 will not automatically trim the input.
30923 *    This parameter is ignored for input[type=password] controls, which will never trim the
30924 *    input.
30925 *
30926 * @example
30927    <example name="input-directive" module="inputExample">
30928      <file name="index.html">
30929       <script>
30930          angular.module('inputExample', [])
30931            .controller('ExampleController', ['$scope', function($scope) {
30932              $scope.user = {name: 'guest', last: 'visitor'};
30933            }]);
30934       </script>
30935       <div ng-controller="ExampleController">
30936         <form name="myForm">
30937           <label>
30938              User name:
30939              <input type="text" name="userName" ng-model="user.name" required>
30940           </label>
30941           <div role="alert">
30942             <span class="error" ng-show="myForm.userName.$error.required">
30943              Required!</span>
30944           </div>
30945           <label>
30946              Last name:
30947              <input type="text" name="lastName" ng-model="user.last"
30948              ng-minlength="3" ng-maxlength="10">
30949           </label>
30950           <div role="alert">
30951             <span class="error" ng-show="myForm.lastName.$error.minlength">
30952               Too short!</span>
30953             <span class="error" ng-show="myForm.lastName.$error.maxlength">
30954               Too long!</span>
30955           </div>
30956         </form>
30957         <hr>
30958         <tt>user = {{user}}</tt><br/>
30959         <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br/>
30960         <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br/>
30961         <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br/>
30962         <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br/>
30963         <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
30964         <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
30965         <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br/>
30966         <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br/>
30967       </div>
30968      </file>
30969      <file name="protractor.js" type="protractor">
30970        var user = element(by.exactBinding('user'));
30971        var userNameValid = element(by.binding('myForm.userName.$valid'));
30972        var lastNameValid = element(by.binding('myForm.lastName.$valid'));
30973        var lastNameError = element(by.binding('myForm.lastName.$error'));
30974        var formValid = element(by.binding('myForm.$valid'));
30975        var userNameInput = element(by.model('user.name'));
30976        var userLastInput = element(by.model('user.last'));
30977
30978        it('should initialize to model', function() {
30979          expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
30980          expect(userNameValid.getText()).toContain('true');
30981          expect(formValid.getText()).toContain('true');
30982        });
30983
30984        it('should be invalid if empty when required', function() {
30985          userNameInput.clear();
30986          userNameInput.sendKeys('');
30987
30988          expect(user.getText()).toContain('{"last":"visitor"}');
30989          expect(userNameValid.getText()).toContain('false');
30990          expect(formValid.getText()).toContain('false');
30991        });
30992
30993        it('should be valid if empty when min length is set', function() {
30994          userLastInput.clear();
30995          userLastInput.sendKeys('');
30996
30997          expect(user.getText()).toContain('{"name":"guest","last":""}');
30998          expect(lastNameValid.getText()).toContain('true');
30999          expect(formValid.getText()).toContain('true');
31000        });
31001
31002        it('should be invalid if less than required min length', function() {
31003          userLastInput.clear();
31004          userLastInput.sendKeys('xx');
31005
31006          expect(user.getText()).toContain('{"name":"guest"}');
31007          expect(lastNameValid.getText()).toContain('false');
31008          expect(lastNameError.getText()).toContain('minlength');
31009          expect(formValid.getText()).toContain('false');
31010        });
31011
31012        it('should be invalid if longer than max length', function() {
31013          userLastInput.clear();
31014          userLastInput.sendKeys('some ridiculously long name');
31015
31016          expect(user.getText()).toContain('{"name":"guest"}');
31017          expect(lastNameValid.getText()).toContain('false');
31018          expect(lastNameError.getText()).toContain('maxlength');
31019          expect(formValid.getText()).toContain('false');
31020        });
31021      </file>
31022    </example>
31023 */
31024var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
31025    function($browser, $sniffer, $filter, $parse) {
31026  return {
31027    restrict: 'E',
31028    require: ['?ngModel'],
31029    link: {
31030      pre: function(scope, element, attr, ctrls) {
31031        if (ctrls[0]) {
31032          
31032var type = lowercase(attr.type);
31033          if ((type === 'range') && !attr.hasOwnProperty('ngInputRange')) {
31034            type = 'text';
31035          }
31036          (inputType[type] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
31037                                                              $browser, $filter, $parse);
31038        }
31039      }
31040    }
31041  };
31042}];
31043
31044
31045
31046var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
31047/**
31048 * @ngdoc directive
31049 * @name ngValue
31050 *
31051 * @description
31052 * Binds the given expression to the value of `<option>` or {@link input[radio] `input[radio]`},
31053 * so that when the element is selected, the {@link ngModel `ngModel`} of that element is set to
31054 * the bound value.
31055 *
31056 * `ngValue` is useful when dynamically generating lists of radio buttons using
31057 * {@link ngRepeat `ngRepeat`}, as shown below.
31058 *
31059 * Likewise, `ngValue` can be used to generate `<option>` elements for
31060 * the {@link select `select`} element. In that case however, only strings are supported
31061 * for the `value `attribute, so the resulting `ngModel` will always be a string.
31062 * Support for `select` models with non-string values is available via `ngOptions`.
31063 *
31064 * @element input
31065 * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute
31066 *   of the `input` element
31067 *
31068 * @example
31069    <example name="ngValue-directive" module="valueExample">
31070      <file name="index.html">
31071       <script>
31072          angular.module('valueExample', [])
31073            .controller('ExampleController', ['$scope', function($scope) {
31074              $scope.names = ['pizza', 'unicorns', 'robots'];
31075              $scope.my = { favorite: 'unicorns' };
31076            }]);
31077       </script>
31078        <form ng-controller="ExampleController">
31079          <h2>Which is your favorite?</h2>
31080            <label ng-repeat="name in names" for="{{name}}">
31081              {{name}}
31082              <input type="radio"
31083                     ng-model="my.favorite"
31084                     ng-value="name"
31085                     id="{{name}}"
31086                     name="favorite">
31087            </label>
31088          <div>You chose {{my.favorite}}</div>
31089        </form>
31090      </file>
31091      <file name="protractor.js" type="protractor">
31092        var favorite = element(by.binding('my.favorite'));
31093
31094        it('should initialize to model', function() {
31095          expect(favorite.getText()).toContain('unicorns');
31096        });
31097        it('should bind the values to the inputs', function() {
31098          element.all(by.model('my.favorite')).get(0).click();
31099          expect(favorite.getText()).toContain('pizza');
31100        });
31101      </file>
31102    </example>
31103 */
31104var ngValueDirective = function() {
31105  return {
31106    restrict: 'A',
31107    priority: 100,
31108    compile: function(tpl, tplAttr) {
31109      if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
31110        return function ngValueConstantLink(scope, elm, attr) {
31111          attr.$set('value', scope.$eval(attr.ngValue));
31112        };
31113      } else {
31114        return function ngValueLink(scope, elm, attr) {
31115          scope.$watch(attr.ngValue, function valueWatchAction(value) {
31116            attr.$set('value', value);
31117          });
31118        };
31119      }
31120    }
31121  };
31122};
31123
31124/**
31125 * @ngdoc directive
31126 * @name ngBind
31127 * @restrict AC
31128 *
31129 * @description
31130 * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element
31131 * with the value of a given expression, and to update the text content when the value of that
31132 * expression changes.
31133 *
31134 * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
31135 * `{{ expression }}` which is similar but less verbose.
31136 *
31137 * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily
31138 * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
31139 * element attribute, it makes the bindings invisible to the user while the page is loading.
31140 *
31141 * An alternative solution to this problem would be using the
31142 * {@link ng.directive:ngCloak ngCloak} directive.
31143 *
31144 *
31145 * @element ANY
31146 * @param {expression} ngBind {@link guide/expression Expression} to evaluate.
31147 *
31148 * @example
31149 * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
31150   <example module="bindExample" name="ng-bind">
31151     <file name="index.html">
31152       <script>
31153         angular.module('bindExample', [])
31154           .controller('ExampleController', ['$scope', function($scope) {
31155             $scope.name = 'Whirled';
31156           }]);
31157       </script>
31158       <div ng-controller="ExampleController">
31159         <label>Enter name: <input type="text" ng-model="name"></label><br>
31160         Hello <span ng-bind="name"></span>!
31161       </div>
31162     </file>
31163     <file name="protractor.js" type="protractor">
31164       it('should check ng-bind', function() {
31165         var nameInput = element(by.model('name'));
31166
31167         expect(element(by.binding('name')).getText()).toBe('Whirled');
31168         nameInput.clear();
31169         nameInput.sendKeys('world');
31170         expect(element(by.binding('name')).getText()).toBe('world');
31171       });
31172     </file>
31173   </example>
31174 */
31175var ngBindDirective = ['$compile', function($compile) {
31176  return {
31177    restrict: 'AC',
31178    compile: function ngBindCompile(templateElement) {
31179      $compile.$$addBindingClass(templateElement);
31180      return function ngBindLink(scope, element, attr) {
31181        $compile.$$addBindingInfo(element, attr.ngBind);
31182        element = element[0];
31183        scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
31184          element.textContent = isUndefined(value) ? '' : value;
31185        });
31186      };
31187    }
31188  };
31189}];
31190
31191
31192/**
31193 * @ngdoc directive
31194 * @name ngBindTemplate
31195 *
31196 * @description
31197 * The `ngBindTemplate` directive specifies that the element
31198 * text content should be replaced with the interpolation of the template
31199 * in the `ngBindTemplate` attribute.
31200 * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}`
31201 * expressions. This directive is needed since some HTML elements
31202 * (such as TITLE and OPTION) cannot contain SPAN elements.
31203 *
31204 * @element ANY
31205 * @param {string} ngBindTemplate template of form
31206 *   <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
31207 *
31208 * @example
31209 * Try it here: enter text in text box and watch the greeting change.
31210   <example module="bindExample" name="ng-bind-template">
31211     <file name="index.html">
31212       <script>
31213         angular.module('bindExample', [])
31214           .controller('ExampleController', ['$scope', function($scope) {
31215             $scope.salutation = 'Hello';
31216             $scope.name = 'World';
31217           }]);
31218       </script>
31219       <div ng-controller="ExampleController">
31220        <label>Salutation: <input type="text" ng-model="salutation"></label><br>
31221        <label>Name: <input type="text" ng-model="name"></label><br>
31222        <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
31223       </div>
31224     </file>
31225     <file name="protractor.js" type="protractor">
31226       it('should check ng-bind', function() {
31227         var salutationElem = element(by.binding('salutation'));
31228         var salutationInput = element(by.model('salutation'));
31229         var nameInput = element(by.model('name'));
31230
31231         expect(salutationElem.getText()).toBe('Hello World!');
31232
31233         salutationInput.clear();
31234         salutationInput.sendKeys('Greetings');
31235         nameInput.clear();
31236         nameInput.sendKeys('user');
31237
31238         expect(salutationElem.getText()).toBe('Greetings user!');
31239       });
31240     </file>
31241   </example>
31242 */
31243var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate, $compile) {
31244  return {
31245    compile: function ngBindTemplateCompile(templateElement) {
31246      $compile.$$addBindingClass(templateElement);
31247      return function ngBindTemplateLink(scope, element, attr) {
31248        var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
31249        $compile.$$addBindingInfo(element, interpolateFn.expressions);
31250        element = element[0];
31251        attr.$observe('ngBindTemplate', function(value) {
31252          element.textContent = isUndefined(value) ? '' : value;
31253        });
31254      };
31255    }
31256  };
31257}];
31258
31259
31260/**
31261 * @ngdoc directive
31262 * @name ngBindHtml
31263 *
31264 * @description
31265 * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default,
31266 * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service.
31267 * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link
31268 * ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize}
31269 * in your module's dependencies, you need to include "angular-sanitize.js" in your application.
31270 *
31271 * You may also bypass sanitization for values you know are safe. To do so, bind to
31272 * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}.  See the example
31273 * under {@link ng.$sce#show-me-an-example-using-sce- Strict Contextual Escaping (SCE)}.
31274 *
31275 * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you
31276 * will have an exception (instead of an exploit.)
31277 *
31278 * @element ANY
31279 * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
31280 *
31281 * @example
31282
31283   <example module="bindHtmlExample" deps="angular-sanitize.js" name="ng-bind-html">
31284     <file name="index.html">
31285       <div ng-controller="ExampleController">
31286        <p ng-bind-html="myHTML"></p>
31287       </div>
31288     </file>
31289
31290     <file name="script.js">
31291       angular.module('bindHtmlExample', ['ngSanitize'])
31292         .controller('ExampleController', ['$scope', function($scope) {
31293           $scope.myHTML =
31294              'I am an <code>HTML</code>string with ' +
31295              '<a href="#">links!</a> and other <em>stuff</em>';
31296         }]);
31297     </file>
31298
31299     <file name="protractor.js" type="protractor">
31300       it('should check ng-bind-html', function() {
31301         expect(element(by.binding('myHTML')).getText()).toBe(
31302             'I am an HTMLstring with links! and other stuff');
31303       });
31304     </file>
31305   </example>
31306 */
31307var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) {
31308  return {
31309    restrict: 'A',
31310    compile: function ngBindHtmlCompile(tElement, tAttrs) {
31311      var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
31312      var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function sceValueOf(val) {
31313        // Unwrap the value to compare the actual inner safe value, not the wrapper object.
31314        return $sce.valueOf(val);
31315      });
31316      $compile.$$addBindingClass(tElement);
31317
31318      return function ngBindHtmlLink(scope, element, attr) {
31319        $compile.$$addBindingInfo(element, attr.ngBindHtml);
31320
31321        scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
31322          // The watched value is the unwrapped value. To avoid re-escaping, use the direct getter.
31323          var value = ngBindHtmlGetter(scope);
31324          element.html($sce.getTrustedHtml(value) || '');
31325        });
31326      };
31327    }
31328  };
31329}];
31330
31331/**
31332 * @ngdoc directive
31333 * @name ngChange
31334 *
31335 * @description
31336 * Evaluate the given expression when the user changes the input.
31337 * The expression is evaluated immediately, unlike the JavaScript onchange event
31338 * which only triggers at the end of a change (usually, when the user leaves the
31339 * form element or presses the return key).
31340 *
31341 * The `ngChange` expression is only evaluated when a change in the input value causes
31342 * a new value to be committed to the model.
31343 *
31344 * It will not be evaluated:
31345 * * if the value returned from the `$parsers` transformation pipeline has not changed
31346 * * if the input has continued to be invalid since the model will stay `null`
31347 * * if the model is changed programmatically and not by a change to the input value
31348 *
31349 *
31350 * Note, this directive requires `ngModel` to be present.
31351 *
31352 * @element input
31353 * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change
31354 * in input value.
31355 *
31356 * @example
31357 * <example name="ngChange-directive" module="changeExample">
31358 *   <file name="index.html">
31359 *     <script>
31360 *       angular.module('changeExample', [])
31361 *         .controller('ExampleController', ['$scope', function($scope) {
31362 *           $scope.counter = 0;
31363 *           $scope.change = function() {
31364 *             $scope.counter++;
31365 *           };
31366 *         }]);
31367 *     </script>
31368 *     <div ng-controller="ExampleController">
31369 *       <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
31370 *       <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
31371 *       <label for="ng-change-example2">Confirmed</label><br />
31372 *       <tt>debug = {{confirmed}}</tt><br/>
31373 *       <tt>counter = {{counter}}</tt><br/>
31374 *     </div>
31375 *   </file>
31376 *   <file name="protractor.js" type="protractor">
31377 *     var counter = element(by.binding('counter'));
31378 *     var debug = element(by.binding('confirmed'));
31379 *
31380 *     it('should evaluate the expression if changing from view', function() {
31381 *       expect(counter.getText()).toContain('0');
31382 *
31383 *       element(by.id('ng-change-example1')).click();
31384 *
31385 *       expect(counter.getText()).toContain('1');
31386 *       expect(debug.getText()).toContain('true');
31387 *     });
31388 *
31389 *     it('should not evaluate the expression if changing from model', function() {
31390 *       element(by.id('ng-change-example2')).click();
31391
31392 *       expect(counter.getText()).toContain('0');
31393 *       expect(debug.getText()).toContain('true');
31394 *     });
31395 *   </file>
31396 * </example>
31397 */
31398var ngChangeDirective = valueFn({
31399  restrict: 'A',
31400  require: 'ngModel',
31401  link: function(scope, element, attr, ctrl) {
31402    ctrl.$viewChangeListeners.push(function() {
31403      scope.$eval(attr.ngChange);
31404    });
31405  }
31406});
31407
31408/* exported
31409  ngClassDirective,
31410  ngClassEvenDirective,
31411  ngClassOddDirective
31412*/
31413
31414function classDirective(name, selector) {
31415  name = 'ngClass' + name;
31416  return ['$animate', function($animate) {
31417    return {
31418      restrict: 'AC',
31419      link: function(scope, element, attr) {
31420        var oldVal;
31421
31422        attr.$observe('class', function(value) {
31423          ngClassWatchAction(scope.$eval(attr[name]));
31424        });
31425
31426
31427        if (name !== 'ngClass') {
31428          scope.$watch('$index', function($index, old$index) {
31429            /* eslint-disable no-bitwise */
31430            var mod = $index & 1;
31431            if (mod !== (old$index & 1)) {
31432              var classes = arrayClasses(oldVal);
31433              if (mod === selector) {
31434                addClasses(classes);
31435              } else {
31436                removeClasses(classes);
31437              }
31438            }
31439            /* eslint-enable */
31440          });
31441        }
31442
31443        scope.$watch(attr[name], ngClassWatchAction, true);
31444
31445        function addClasses(classes) {
31446          var newClasses = digestClassCounts(classes, 1);
31447          attr.$addClass(newClasses);
31448        }
31449
31450        function removeClasses(classes) {
31451          var newClasses = digestClassCounts(classes, -1);
31452          attr.$removeClass(newClasses);
31453        }
31454
31455        function digestClassCounts(classes, count) {
31456          // Use createMap() to prevent class assumptions involving property
31457          // names in Object.prototype
31458          var classCounts = element.data('$classCounts') || createMap();
31459          var classesToUpdate = [];
31460          forEach(classes, function(className) {
31461            if (count > 0 || classCounts[className]) {
31462              classCounts[className] = (classCounts[className] || 0) + count;
31463              if (classCounts[className] === +(count > 0)) {
31464                classesToUpdate.push(className);
31465              }
31466            }
31467          });
31468          element.data('$classCounts', classCounts);
31469          return classesToUpdate.join(' ');
31470        }
31471
31472        function updateClasses(oldClasses, newClasses) {
31473          var toAdd = arrayDifference(newClasses, oldClasses);
31474          var toRemove = arrayDifference(oldClasses, newClasses);
31475          toAdd = digestClassCounts(toAdd, 1);
31476          toRemove = digestClassCounts(toRemove, -1);
31477          if (toAdd && toAdd.length) {
31478            $animate.addClass(element, toAdd);
31479          }
31480          if (toRemove && toRemove.length) {
31481            $animate.removeClass(element, toRemove);
31482          }
31483        }
31484
31485        function ngClassWatchAction(newVal) {
31486          // eslint-disable-next-line no-bitwise
31487          if (selector === true || (scope.$index & 1) === selector) {
31488            var newClasses = arrayClasses(newVal || []);
31489            if (!oldVal) {
31490              addClasses(newClasses);
31491            } else if (!equals(newVal,oldVal)) {
31492              var oldClasses = arrayClasses(oldVal);
31493              updateClasses(oldClasses, newClasses);
31494            }
31495          }
31496          if (isArray(newVal)) {
31497            oldVal = newVal.map(function(v) { return shallowCopy(v); });
31498          } else {
31499            oldVal = shallowCopy(newVal);
31500          }
31501        }
31502      }
31503    };
31504
31505    function arrayDifference(tokens1, tokens2) {
31506      var values = [];
31507
31508      outer:
31509      for (var i = 0; i < tokens1.length; i++) {
31510        var token = tokens1[i];
31511        for (var j = 0; j < tokens2.length; j++) {
31512          if (token === tokens2[j]) continue outer;
31513        }
31514        values.push(token);
31515      }
31516      return values;
31517    }
31518
31519    function arrayClasses(classVal) {
31520      var classes = [];
31521      if (isArray(classVal)) {
31522        forEach(classVal, function(v) {
31523          classes = classes.concat(arrayClasses(v));
31524        });
31525        return classes;
31526      } else if (isString(classVal)) {
31527        return classVal.split(' ');
31528      } else if (isObject(classVal)) {
31529        forEach(classVal, function(v, k) {
31530          if (v) {
31531            classes = classes.concat(k.split(' '));
31532          }
31533        });
31534        return classes;
31535      }
31536      return classVal;
31537    }
31538  }];
31539}
31540
31541/**
31542 * @ngdoc directive
31543 * @name ngClass
31544 * @restrict AC
31545 *
31546 * @description
31547 * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding
31548 * an expression that represents all classes to be added.
31549 *
31550 * The directive operates in three different ways, depending on which of three types the expression
31551 * evaluates to:
31552 *
31553 * 1. If the expression evaluates to a string, the string should be one or more space-delimited class
31554 * names.
31555 *
31556 * 2. If the expression evaluates to an object, then for each key-value pair of the
31557 * object with a truthy value the corresponding key is used as a class name.
31558 *
31559 * 3. If the expression evaluates to an array, each element of the array should either be a string as in
31560 * type 1 or an object as in type 2. This means that you can mix strings and objects together in an array
31561 * to give you more control over what CSS classes appear. See the code below for an example of this.
31562 *
31563 *
31564 * The directive won't add duplicate classes if a particular class was already set.
31565 *
31566 * When the expression changes, the previously added classes are removed and only then are the
31567 * new classes added.
31568 *
31569 * @knownIssue
31570 * You should not use {@link guide/interpolation interpolation} in the value of the `class`
31571 * attribute, when using the `ngClass` directive on the same element.
31572 * See {@link guide/interpolation#known-issues here} for more info.
31573 *
31574 * @animations
31575 * | Animation                        | Occurs                              |
31576 * |----------------------------------|-------------------------------------|
31577 * | {@link ng.$animate#addClass addClass}       | just before the class is applied to the element   |
31578 * | {@link ng.$animate#removeClass removeClass} | just before the class is removed from the element |
31579 *
31580 * @element ANY
31581 * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
31582 *   of the evaluation can be a string representing space delimited class
31583 *   names, an array, or a map of class names to boolean values. In the case of a map, the
31584 *   names of the properties whose values are truthy will be added as css classes to the
31585 *   element.
31586 *
31587 * @example Example that demonstrates basic bindings via ngClass directive.
31588   <example name="ng-class">
31589     <file name="index.html">
31590       <p ng-class="{strike: deleted, bold: important, 'has-error': error}">Map Syntax Example</p>
31591       <label>
31592          <input type="checkbox" ng-model="deleted">
31593          deleted (apply "strike" class)
31594       </label><br>
31595       <label>
31596          <input type="checkbox" ng-model="important">
31597          important (apply "bold" class)
31598       </label><br>
31599       <label>
31600          <input type="checkbox" ng-model="error">
31601          error (apply "has-error" class)
31602       </label>
31603       <hr>
31604       <p ng-class="style">Using String Syntax</p>
31605       <input type="text" ng-model="style"
31606              placeholder="Type: bold strike red" aria-label="Type: bold strike red">
31607       <hr>
31608       <p ng-class="[style1, style2, style3]">Using Array Syntax</p>
31609       <input ng-model="style1"
31610              placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red"><br>
31611       <input ng-model="style2"
31612              placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 2"><br>
31613       <input ng-model="style3"
31614              placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 3"><br>
31615       <hr>
31616       <p ng-class="[style4, {orange: warning}]">Using Array and Map Syntax</p>
31617       <input ng-model="style4" placeholder="Type: bold, strike" aria-label="Type: bold, strike"><br>
31618       <label><input type="checkbox" ng-model="warning"> warning (apply "orange" class)</label>
31619     </file>
31620     <file name="style.css">
31621       .strike {
31622           text-decoration: line-through;
31623       }
31624       .bold {
31625           font-weight: bold;
31626       }
31627       .red {
31628           color: red;
31629       }
31630       .has-error {
31631           color: red;
31632           background-color: yellow;
31633       }
31634       .orange {
31635           color: orange;
31636       }
31637     </file>
31638     <file name="protractor.js" type="protractor">
31639       var ps = element.all(by.css('p'));
31640
31641       it('should let you toggle the class', function() {
31642
31643         expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
31644         expect(ps.first().getAttribute('class')).not.toMatch(/has-error/);
31645
31646         element(by.model('important')).click();
31647         expect(ps.first().getAttribute('class')).toMatch(/bold/);
31648
31649         element(by.model('error')).click();
31650         expect(ps.first().getAttribute('class')).toMatch(/has-error/);
31651       });
31652
31653       it('should let you toggle string example', function() {
31654         expect(ps.get(1).getAttribute('class')).toBe('');
31655         element(by.model('style')).clear();
31656         element(by.model('style')).sendKeys('red');
31657         expect(ps.get(1).getAttribute('class')).toBe('red');
31658       });
31659
31660       it('array example should have 3 classes', function() {
31661         expect(ps.get(2).getAttribute('class')).toBe('');
31662         element(by.model('style1')).sendKeys('bold');
31663         element(by.model('style2')).sendKeys('strike');
31664         element(by.model('style3')).sendKeys('red');
31665         expect(ps.get(2).getAttribute('class')).toBe('bold strike red');
31666       });
31667
31668       it('array with map example should have 2 classes', function() {
31669         expect(ps.last().getAttribute('class')).toBe('');
31670         element(by.model('style4')).sendKeys('bold');
31671         element(by.model('warning')).click();
31672         expect(ps.last().getAttribute('class')).toBe('bold orange');
31673       });
31674     </file>
31675   </example>
31676
31677   ## Animations
31678
31679   The example below demonstrates how to perform animations using ngClass.
31680
31681   <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-class">
31682     <file name="index.html">
31683      <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
31684      <input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
31685      <br>
31686      <span class="base-class" ng-class="myVar">Sample Text</span>
31687     </file>
31688     <file name="style.css">
31689       .base-class {
31690         transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
31691       }
31692
31693       .base-class.my-class {
31694         color: red;
31695         font-size:3em;
31696       }
31697     </file>
31698     <file name="protractor.js" type="protractor">
31699       it('should check ng-class', function() {
31700         expect(element(by.css('.base-class')).getAttribute('class')).not.
31701           toMatch(/my-class/);
31702
31703         element(by.id('setbtn')).click();
31704
31705         expect(element(by.css('.base-class')).getAttribute('class')).
31706           toMatch(/my-class/);
31707
31708         element(by.id('clearbtn')).click();
31709
31710         expect(element(by.css('.base-class')).getAttribute('class')).not.
31711           toMatch(/my-class/);
31712       });
31713     </file>
31714   </example>
31715
31716
31717   ## ngClass and pre-existing CSS3 Transitions/Animations
31718   The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
31719   Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder
31720   any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure
31721   to view the step by step details of {@link $animate#addClass $animate.addClass} and
31722   {@link $animate#removeClass $animate.removeClass}.
31723 */
31724var ngClassDirective = classDirective('', true);
31725
31726/**
31727 * @ngdoc directive
31728 * @name ngClassOdd
31729 * @restrict AC
31730 *
31731 * @description
31732 * The `ngClassOdd` and `ngClassEven` directives work exactly as
31733 * {@link ng.directive:ngClass ngClass}, except they work in
31734 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
31735 *
31736 * This directive can be applied only within the scope of an
31737 * {@link ng.directive:ngRepeat ngRepeat}.
31738 *
31739 * @element ANY
31740 * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
31741 *   of the evaluation can be a string representing space delimited class names or an array.
31742 *
31743 * @example
31744   <example name="ng-class-odd">
31745     <file name="index.html">
31746        <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
31747          <li ng-repeat="name in names">
31748           <span ng-class-odd="'odd'" ng-class-even="'even'">
31749             {{name}}
31750           </span>
31751          </li>
31752        </ol>
31753     </file>
31754     <file name="style.css">
31755       .odd {
31756         color: red;
31757       }
31758       .even {
31759         color: blue;
31760       }
31761     </file>
31762     <file name="protractor.js" type="protractor">
31763       it('should check ng-class-odd and ng-class-even', function() {
31764         expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
31765           toMatch(/odd/);
31766         expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
31767           toMatch(/even/);
31768       });
31769     </file>
31770   </example>
31771 */
31772var ngClassOddDirective = classDirective('Odd', 0);
31773
31774/**
31775 * @ngdoc directive
31776 * @name ngClassEven
31777 * @restrict AC
31778 *
31779 * @description
31780 * The `ngClassOdd` and `ngClassEven` directives work exactly as
31781 * {@link ng.directive:ngClass ngClass}, except they work in
31782 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
31783 *
31784 * This directive can be applied only within the scope of an
31785 * {@link ng.directive:ngRepeat ngRepeat}.
31786 *
31787 * @element ANY
31788 * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
31789 *   result of the evaluation can be a string representing space delimited class names or an array.
31790 *
31791 * @example
31792   <example name="ng-class-even">
31793     <file name="index.html">
31794        <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
31795          <li ng-repeat="name in names">
31796           <span ng-class-odd="'odd'" ng-class-even="'even'">
31797             {{name}} &nbsp; &nbsp; &nbsp;
31798           </span>
31799          </li>
31800        </ol>
31801     </file>
31802     <file name="style.css">
31803       .odd {
31804         color: red;
31805       }
31806       .even {
31807         color: blue;
31808       }
31809     </file>
31810     <file name="protractor.js" type="protractor">
31811       it('should check ng-class-odd and ng-class-even', function() {
31812         expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
31813           toMatch(/odd/);
31814         expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
31815           toMatch(/even/);
31816       });
31817     </file>
31818   </example>
31819 */
31820var ngClassEvenDirective = classDirective('Even', 1);
31821
31822/**
31823 * @ngdoc directive
31824 * @name ngCloak
31825 * @restrict AC
31826 *
31827 * @description
31828 * The `ngCloak` directive is used to prevent the Angular html template from being briefly
31829 * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
31830 * directive to avoid the undesirable flicker effect caused by the html template display.
31831 *
31832 * The directive can be applied to the `<body>` element, but the preferred usage is to apply
31833 * multiple `ngCloak` directives to small portions of the page to permit progressive rendering
31834 * of the browser view.
31835 *
31836 * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and
31837 * `angular.min.js`.
31838 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
31839 *
31840 * ```css
31841 * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
31842 *   display: none !important;
31843 * }
31844 * ```
31845 *
31846 * When this css rule is loaded by the browser, all html elements (including their children) that
31847 * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive
31848 * during the compilation of the template it deletes the `ngCloak` element attribute, making
31849 * the compiled element visible.
31850 *
31851 * For the best result, the `angular.js` script must be loaded in the head section of the html
31852 * document; alternatively, the css rule above must be included in the external stylesheet of the
31853 * application.
31854 *
31855 * @element ANY
31856 *
31857 * @example
31858   <example name="ng-cloak">
31859     <file name="index.html">
31860        <div id="template1" ng-cloak>{{ 'hello' }}</div>
31861        <div id="template2" class="ng-cloak">{{ 'world' }}</div>
31862     </file>
31863     <file name="protractor.js" type="protractor">
31864       it('should remove the template directive and css class', function() {
31865         expect($('#template1').getAttribute('ng-cloak')).
31866           toBeNull();
31867         expect($('#template2').getAttribute('ng-cloak')).
31868           toBeNull();
31869       });
31870     </file>
31871   </example>
31872 *
31873 */
31874var ngCloakDirective = ngDirective({
31875  compile: function(element, attr) {
31876    attr.$set('ngCloak', undefined);
31877    element.removeClass('ng-cloak');
31878  }
31879});
31880
31881/**
31882 * @ngdoc directive
31883 * @name ngController
31884 *
31885 * @description
31886 * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular
31887 * supports the principles behind the Model-View-Controller design pattern.
31888 *
31889 * MVC components in angular:
31890 *
31891 * * Model — Models are the properties of a scope; scopes are attached to the DOM where scope properties
31892 *   are accessed through bindings.
31893 * * View — The template (HTML with data bindings) that is rendered into the View.
31894 * * Controller — The `ngController` directive specifies a Controller class; the class contains business
31895 *   logic behind the application to decorate the scope with functions and values
31896 *
31897 * Note that you can also attach controllers to the DOM by declaring it in a route definition
31898 * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller
31899 * again using `ng-controller` in the template itself.  This will cause the controller to be attached
31900 * and executed twice.
31901 *
31902 * @element ANY
31903 * @scope
31904 * @priority 500
31905 * @param {expression} ngController Name of a constructor function registered with the current
31906 * {@link ng.$controllerProvider $controllerProvider} or an {@link guide/expression expression}
31907 * that on the current scope evaluates to a constructor function.
31908 *
31909 * The controller instance can be published into a scope property by specifying
31910 * `ng-controller="as propertyName"`.
31911 *
31912 * If the current `$controllerProvider` is configured to use globals (via
31913 * {@link ng.$controllerProvider#allowGlobals `$controllerProvider.allowGlobals()` }), this may
31914 * also be the name of a globally accessible constructor function (not recommended).
31915 *
31916 * @example
31917 * Here is a simple form for editing user contact information. Adding, remov
31917ing, clearing, and
31918 * greeting are methods declared on the controller (see source tab). These methods can
31919 * easily be called from the angular markup. Any changes to the data are automatically reflected
31920 * in the View without the need for a manual update.
31921 *
31922 * Two different declaration styles are included below:
31923 *
31924 * * one binds methods and properties directly onto the controller using `this`:
31925 * `ng-controller="SettingsController1 as settings"`
31926 * * one injects `$scope` into the controller:
31927 * `ng-controller="SettingsController2"`
31928 *
31929 * The second option is more common in the Angular community, and is generally used in boilerplates
31930 * and in this guide. However, there are advantages to binding properties directly to the controller
31931 * and avoiding scope.
31932 *
31933 * * Using `controller as` makes it obvious which controller you are accessing in the template when
31934 * multiple controllers apply to an element.
31935 * * If you are writing your controllers as classes you have easier access to the properties and
31936 * methods, which will appear on the scope, from inside the controller code.
31937 * * Since there is always a `.` in the bindings, you don't have to worry about prototypal
31938 * inheritance masking primitives.
31939 *
31940 * This example demonstrates the `controller as` syntax.
31941 *
31942 * <example name="ngControllerAs" module="controllerAsExample">
31943 *   <file name="index.html">
31944 *    <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings">
31945 *      <label>Name: <input type="text" ng-model="settings.name"/></label>
31946 *      <button ng-click="settings.greet()">greet</button><br/>
31947 *      Contact:
31948 *      <ul>
31949 *        <li ng-repeat="contact in settings.contacts">
31950 *          <select ng-model="contact.type" aria-label="Contact method" id="select_{{$index}}">
31951 *             <option>phone</option>
31952 *             <option>email</option>
31953 *          </select>
31954 *          <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
31955 *          <button ng-click="settings.clearContact(contact)">clear</button>
31956 *          <button ng-click="settings.removeContact(contact)" aria-label="Remove">X</button>
31957 *        </li>
31958 *        <li><button ng-click="settings.addContact()">add</button></li>
31959 *     </ul>
31960 *    </div>
31961 *   </file>
31962 *   <file name="app.js">
31963 *    angular.module('controllerAsExample', [])
31964 *      .controller('SettingsController1', SettingsController1);
31965 *
31966 *    function SettingsController1() {
31967 *      this.name = 'John Smith';
31968 *      this.contacts = [
31969 *        {type: 'phone', value: '408 555 1212'},
31970 *        {type: 'email', value: '[email protected]'}
31971 *      ];
31972 *    }
31973 *
31974 *    SettingsController1.prototype.greet = function() {
31975 *      alert(this.name);
31976 *    };
31977 *
31978 *    SettingsController1.prototype.addContact = function() {
31979 *      this.contacts.push({type: 'email', value: '[email protected]'});
31980 *    };
31981 *
31982 *    SettingsController1.prototype.removeContact = function(contactToRemove) {
31983 *     var index = this.contacts.indexOf(contactToRemove);
31984 *      this.contacts.splice(index, 1);
31985 *    };
31986 *
31987 *    SettingsController1.prototype.clearContact = function(contact) {
31988 *      contact.type = 'phone';
31989 *      contact.value = '';
31990 *    };
31991 *   </file>
31992 *   <file name="protractor.js" type="protractor">
31993 *     it('should check controller as', function() {
31994 *       var container = element(by.id('ctrl-as-exmpl'));
31995 *         expect(container.element(by.model('settings.name'))
31996 *           .getAttribute('value')).toBe('John Smith');
31997 *
31998 *       var firstRepeat =
31999 *           container.element(by.repeater('contact in settings.contacts').row(0));
32000 *       var secondRepeat =
32001 *           container.element(by.repeater('contact in settings.contacts').row(1));
32002 *
32003 *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
32004 *           .toBe('408 555 1212');
32005 *
32006 *       expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
32007 *           .toBe('[email protected]');
32008 *
32009 *       firstRepeat.element(by.buttonText('clear')).click();
32010 *
32011 *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
32012 *           .toBe('');
32013 *
32014 *       container.element(by.buttonText('add')).click();
32015 *
32016 *       expect(container.element(by.repeater('contact in settings.contacts').row(2))
32017 *           .element(by.model('contact.value'))
32018 *           .getAttribute('value'))
32019 *           .toBe('[email protected]');
32020 *     });
32021 *   </file>
32022 * </example>
32023 *
32024 * This example demonstrates the "attach to `$scope`" style of controller.
32025 *
32026 * <example name="ngController" module="controllerExample">
32027 *  <file name="index.html">
32028 *   <div id="ctrl-exmpl" ng-controller="SettingsController2">
32029 *     <label>Name: <input type="text" ng-model="name"/></label>
32030 *     <button ng-click="greet()">greet</button><br/>
32031 *     Contact:
32032 *     <ul>
32033 *       <li ng-repeat="contact in contacts">
32034 *         <select ng-model="contact.type" id="select_{{$index}}">
32035 *            <option>phone</option>
32036 *            <option>email</option>
32037 *         </select>
32038 *         <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
32039 *         <button ng-click="clearContact(contact)">clear</button>
32040 *         <button ng-click="removeContact(contact)">X</button>
32041 *       </li>
32042 *       <li>[ <button ng-click="addContact()">add</button> ]</li>
32043 *    </ul>
32044 *   </div>
32045 *  </file>
32046 *  <file name="app.js">
32047 *   angular.module('controllerExample', [])
32048 *     .controller('SettingsController2', ['$scope', SettingsController2]);
32049 *
32050 *   function SettingsController2($scope) {
32051 *     $scope.name = 'John Smith';
32052 *     $scope.contacts = [
32053 *       {type:'phone', value:'408 555 1212'},
32054 *       {type:'email', value:'[email protected]'}
32055 *     ];
32056 *
32057 *     $scope.greet = function() {
32058 *       alert($scope.name);
32059 *     };
32060 *
32061 *     $scope.addContact = function() {
32062 *       $scope.contacts.push({type:'email', value:'[email protected]'});
32063 *     };
32064 *
32065 *     $scope.removeContact = function(contactToRemove) {
32066 *       var index = $scope.contacts.indexOf(contactToRemove);
32067 *       $scope.contacts.splice(index, 1);
32068 *     };
32069 *
32070 *     $scope.clearContact = function(contact) {
32071 *       contact.type = 'phone';
32072 *       contact.value = '';
32073 *     };
32074 *   }
32075 *  </file>
32076 *  <file name="protractor.js" type="protractor">
32077 *    it('should check controller', function() {
32078 *      var container = element(by.id('ctrl-exmpl'));
32079 *
32080 *      expect(container.element(by.model('name'))
32081 *          .getAttribute('value')).toBe('John Smith');
32082 *
32083 *      var firstRepeat =
32084 *          container.element(by.repeater('contact in contacts').row(0));
32085 *      var secondRepeat =
32086 *          container.element(by.repeater('contact in contacts').row(1));
32087 *
32088 *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
32089 *          .toBe('408 555 1212');
32090 *      expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
32091 *          .toBe('[email protected]');
32092 *
32093 *      firstRepeat.element(by.buttonText('clear')).click();
32094 *
32095 *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
32096 *          .toBe('');
32097 *
32098 *      container.element(by.buttonText('add')).click();
32099 *
32100 *      expect(container.element(by.repeater('contact in contacts').row(2))
32101 *          .element(by.model('contact.value'))
32102 *          .getAttribute('value'))
32103 *          .toBe('[email protected]');
32104 *    });
32105 *  </file>
32106 *</example>
32107
32108 */
32109var ngControllerDirective = [function() {
32110  return {
32111    restrict: 'A',
32112    scope: true,
32113    controller: '@',
32114    priority: 500
32115  };
32116}];
32117
32118/**
32119 * @ngdoc directive
32120 * @name ngCsp
32121 *
32122 * @restrict A
32123 * @element ANY
32124 * @description
32125 *
32126 * Angular has some features that can conflict with certain restrictions that are applied when using
32127 * [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) rules.
32128 *
32129 * If you intend to implement CSP with these rules then you must tell Angular not to use these
32130 * features.
32131 *
32132 * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps.
32133 *
32134 *
32135 * The following default rules in CSP affect Angular:
32136 *
32137 * * The use of `eval()`, `Function(string)` and similar functions to dynamically create and execute
32138 * code from strings is forbidden. Angular makes use of this in the {@link $parse} service to
32139 * provide a 30% increase in the speed of evaluating Angular expressions. (This CSP rule can be
32140 * disabled with the CSP keyword `unsafe-eval`, but it is generally not recommended as it would
32141 * weaken the protections offered by CSP.)
32142 *
32143 * * The use of inline resources, such as inline `<script>` and `<style>` elements, are forbidden.
32144 * This prevents apps from injecting custom styles directly into the document. Angular makes use of
32145 * this to include some CSS rules (e.g. {@link ngCloak} and {@link ngHide}). To make these
32146 * directives work when a CSP rule is blocking inline styles, you must link to the `angular-csp.css`
32147 * in your HTML manually. (This CSP rule can be disabled with the CSP keyword `unsafe-inline`, but
32148 * it is generally not recommended as it would weaken the protections offered by CSP.)
32149 *
32150 * If you do not provide `ngCsp` then Angular tries to autodetect if CSP is blocking dynamic code
32151 * creation from strings (e.g., `unsafe-eval` not specified in CSP header) and automatically
32152 * deactivates this feature in the {@link $parse} service. This autodetection, however, triggers a
32153 * CSP error to be logged in the console:
32154 *
32155 * ```
32156 * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
32157 * script in the following Content Security Policy directive: "default-src 'self'". Note that
32158 * 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
32159 * ```
32160 *
32161 * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`
32162 * directive on an element of the HTML document that appears before the `<script>` tag that loads
32163 * the `angular.js` file.
32164 *
32165 * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
32166 *
32167 * You can specify which of the CSP related Angular features should be deactivated by providing
32168 * a value for the `ng-csp` attribute. The options are as follows:
32169 *
32170 * * no-inline-style: this stops Angular from injecting CSS styles into the DOM
32171 *
32172 * * no-unsafe-eval: this stops Angular from optimizing $parse with unsafe eval of strings
32173 *
32174 * You can use these values in the following combinations:
32175 *
32176 *
32177 * * No declaration means that Angular will assume that you can do inline styles, but it will do
32178 * a runtime check for unsafe-eval. E.g. `<body>`. This is backwardly compatible with previous
32179 * versions of Angular.
32180 *
32181 * * A simple `ng-csp` (or `data-ng-csp`) attribute will tell Angular to deactivate both inline
32182 * styles and unsafe eval. E.g. `<body ng-csp>`. This is backwardly compatible with previous
32183 * versions of Angular.
32184 *
32185 * * Specifying only `no-unsafe-eval` tells Angular that we must not use eval, but that we can
32186 * inject inline styles. E.g. `<body ng-csp="no-unsafe-eval">`.
32187 *
32188 * * Specifying only `no-inline-style` tells Angular that we must not inject styles, but that we can
32189 * run eval - no automatic check for unsafe eval will occur. E.g. `<body ng-csp="no-inline-style">`
32190 *
32191 * * Specifying both `no-unsafe-eval` and `no-inline-style` tells Angular that we must not inject
32192 * styles nor use eval, which is the same as an empty: ng-csp.
32193 * E.g.`<body ng-csp="no-inline-style;no-unsafe-eval">`
32194 *
32195 * @example
32196 * This example shows how to apply the `ngCsp` directive to the `html` tag.
32197   ```html
32198     <!doctype html>
32199     <html ng-app ng-csp>
32200     ...
32201     ...
32202     </html>
32203   ```
32204  * @example
32205      <!-- Note: the `.csp` suffix in the example name triggers CSP mode in our http server! -->
32206      <example name="example.csp" module="cspExample" ng-csp="true">
32207        <file name="index.html">
32208          <div ng-controller="MainController as ctrl">
32209            <div>
32210              <button ng-click="ctrl.inc()" id="inc">Increment</button>
32211              <span id="counter">
32212                {{ctrl.counter}}
32213              </span>
32214            </div>
32215
32216            <div>
32217              <button ng-click="ctrl.evil()" id="evil">Evil</button>
32218              <span id="evilError">
32219                {{ctrl.evilError}}
32220              </span>
32221            </div>
32222          </div>
32223        </file>
32224        <file name="script.js">
32225           angular.module('cspExample', [])
32226             .controller('MainController', function MainController() {
32227                this.counter = 0;
32228                this.inc = function() {
32229                  this.counter++;
32230                };
32231                this.evil = function() {
32232                  try {
32233                    eval('1+2'); // eslint-disable-line no-eval
32234                  } catch (e) {
32235                    this.evilError = e.message;
32236                  }
32237                };
32238              });
32239        </file>
32240        <file name="protractor.js" type="protractor">
32241          var util, webdriver;
32242
32243          var incBtn = element(by.id('inc'));
32244          var counter = element(by.id('counter'));
32245          var evilBtn = element(by.id('evil'));
32246          var evilError = element(by.id('evilError'));
32247
32248          function getAndClearSevereErrors() {
32249            return browser.manage().logs().get('browser').then(function(browserLog) {
32250              return browserLog.filter(function(logEntry) {
32251                return logEntry.level.value > webdriver.logging.Level.WARNING.value;
32252              });
32253            });
32254          }
32255
32256          function clearErrors() {
32257            getAndClearSevereErrors();
32258          }
32259
32260          function expectNoErrors() {
32261            getAndClearSevereErrors().then(function(filteredLog) {
32262              expect(filteredLog.length).toEqual(0);
32263              if (filteredLog.length) {
32264                console.log('browser console errors: ' + util.inspect(filteredLog));
32265              }
32266            });
32267          }
32268
32269          function expectError(regex) {
32270            getAndClearSevereErrors().then(function(filteredLog) {
32271              var found = false;
32272              filteredLog.forEach(function(log) {
32273                if (log.message.match(regex)) {
32274                  found = true;
32275                }
32276              });
32277              if (!found) {
32278                throw new Error('expected an error that matches ' + regex);
32279              }
32280            });
32281          }
32282
32283          beforeEach(function() {
32284            util = require('util');
32285            webdriver = require('selenium-webdriver');
32286          });
32287
32288          // For now, we only test on Chrome,
32289          // as Safari does not load the page with Protractor's injected scripts,
32290          // and Firefox webdriver always disables content security policy (#6358)
32291          if (browser.params.browser !== 'chrome') {
32292            return;
32293          }
32294
32295          it('should not report errors when the page is loaded', function() {
32296            // clear errors so we are not dependent on previous tests
32297            clearErrors();
32298            // Need to reload the page as the page is already loaded when
32299            // we come here
32300            browser.driver.getCurrentUrl().then(function(url) {
32301              browser.get(url);
32302            });
32303            expectNoErrors();
32304          });
32305
32306          it('should evaluate expressions', function() {
32307            expect(counter.getText()).toEqual('0');
32308            incBtn.click();
32309            expect(counter.getText()).toEqual('1');
32310            expectNoErrors();
32311          });
32312
32313          it('should throw and report an error when using "eval"', function() {
32314            evilBtn.click();
32315            expect(evilError.getText()).toMatch(/Content Security Policy/);
32316            expectError(/Content Security Policy/);
32317          });
32318        </file>
32319      </example>
32320  */
32321
32322// `ngCsp` is not implemented as a proper directive any more, because we need it be processed while
32323// we bootstrap the app (before `$parse` is instantiated). For this reason, we just have the `csp()`
32324// fn that looks for the `ng-csp` attribute anywhere in the current doc.
32325
32326/**
32327 * @ngdoc directive
32328 * @name ngClick
32329 *
32330 * @description
32331 * The ngClick directive allows you to specify custom behavior when
32332 * an element is clicked.
32333 *
32334 * @element ANY
32335 * @priority 0
32336 * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
32337 * click. ({@link guide/expression#-event- Event object is available as `$event`})
32338 *
32339 * @example
32340   <example name="ng-click">
32341     <file name="index.html">
32342      <button ng-click="count = count + 1" ng-init="count=0">
32343        Increment
32344      </button>
32345      <span>
32346        count: {{count}}
32347      </span>
32348     </file>
32349     <file name="protractor.js" type="protractor">
32350       it('should check ng-click', function() {
32351         expect(element(by.binding('count')).getText()).toMatch('0');
32352         element(by.css('button')).click();
32353         expect(element(by.binding('count')).getText()).toMatch('1');
32354       });
32355     </file>
32356   </example>
32357 */
32358/*
32359 * A collection of directives that allows creation of custom event handlers that are defined as
32360 * angular expressions and are compiled and executed within the current scope.
32361 */
32362var ngEventDirectives = {};
32363
32364// For events that might fire synchronously during DOM manipulation
32365// we need to execute their event handlers asynchronously using $evalAsync,
32366// so that they are not executed in an inconsistent state.
32367var forceAsyncEvents = {
32368  'blur': true,
32369  'focus': true
32370};
32371forEach(
32372  'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
32373  function(eventName) {
32374    var directiveName = directiveNormalize('ng-' + eventName);
32375    ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
32376      return {
32377        restrict: 'A',
32378        compile: function($element, attr) {
32379          // We expose the powerful $event object on the scope that provides access to the Window,
32380          // etc. that isn't protected by the fast paths in $parse.  We explicitly request better
32381          // checks at the cost of speed since event handler expressions are not executed as
32382          // frequently as regular change detection.
32383          var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true);
32384          return function ngEventHandler(scope, element) {
32385            element.on(eventName, function(event) {
32386              var callback = function() {
32387                fn(scope, {$event:event});
32388              };
32389              if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
32390                scope.$evalAsync(callback);
32391              } else {
32392                scope.$apply(callback);
32393              }
32394            });
32395          };
32396        }
32397      };
32398    }];
32399  }
32400);
32401
32402/**
32403 * @ngdoc directive
32404 * @name ngDblclick
32405 *
32406 * @description
32407 * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
32408 *
32409 * @element ANY
32410 * @priority 0
32411 * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
32412 * a dblclick. (The Event object is available as `$event`)
32413 *
32414 * @example
32415   <example name="ng-dblclick">
32416     <file name="index.html">
32417      <button ng-dblclick="count = count + 1" ng-init="count=0">
32418        Increment (on double click)
32419      </button>
32420      count: {{count}}
32421     </file>
32422   </example>
32423 */
32424
32425
32426/**
32427 * @ngdoc directive
32428 * @name ngMousedown
32429 *
32430 * @description
32431 * The ngMousedown directive allows you to specify custom behavior on mousedown event.
32432 *
32433 * @element ANY
32434 * @priority 0
32435 * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
32436 * mousedown. ({@link guide/expression#-event- Event object is available as `$event`})
32437 *
32438 * @example
32439   <example name="ng-mousedown">
32440     <file name="index.html">
32441      <button ng-mousedown="count = count + 1" ng-init="count=0">
32442        Increment (on mouse down)
32443      </button>
32444      count: {{count}}
32445     </file>
32446   </example>
32447 */
32448
32449
32450/**
32451 * @ngdoc directive
32452 * @name ngMouseup
32453 *
32454 * @description
32455 * Specify custom behavior on mouseup event.
32456 *
32457 * @element ANY
32458 * @priority 0
32459 * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
32460 * mouseup. ({@link guide/expression#-event- Event object is available as `$event`})
32461 *
32462 * @example
32463   <example name="ng-mouseup">
32464     <file name="index.html">
32465      <button ng-mouseup="count = count + 1" ng-init="count=0">
32466        Increment (on mouse up)
32467      </button>
32468      count: {{count}}
32469     </file>
32470   </example>
32471 */
32472
32473/**
32474 * @ngdoc directive
32475 * @name ngMouseover
32476 *
32477 * @description
32478 * Specify custom behavior on mouseover event.
32479 *
32480 * @element ANY
32481 * @priority 0
32482 * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
32483 * mouseover. ({@link guide/expression#-event- Event object is available as `$event`})
32484 *
32485 * @example
32486   <example name="ng-mouseover">
32487     <file name="index.html">
32488      <button ng-mouseover="count = count + 1" ng-init="count=0">
32489        Increment (when mouse is over)
32490      </button>
32491      count: {{count}}
32492     </file>
32493   </example>
32494 */
32495
32496
32497/**
32498 * @ngdoc directive
32499 * @name ngMouseenter
32500 *
32501 * @description
32502 * Specify custom behavior on mouseenter event.
32503 *
32504 * @element ANY
32505 * @priority 0
32506 * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
32507 * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})
32508 *
32509 * @example
32510   <example name="ng-mouseenter">
32511     <file name="index.html">
32512      <button ng-mouseenter="count = count + 1" ng-init="count=0">
32513        Increment (when mouse enters)
32514      </button>
32515      count: {{count}}
32516     </file>
32517   </example>
32518 */
32519
32520
32521/**
32522 * @ngdoc directive
32523 * @name ngMouseleave
32524 *
32525 * @description
32526 * Specify custom behavior on mouseleave event.
32527 *
32528 * @element ANY
32529 * @priority 0
32530 * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
32531 * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})
32532 *
32533 * @example
32534   <example name="ng-mouseleave">
32535     <file name="index.html">
32536      <button ng-mouseleave="count = count + 1" ng-init="count=0">
32537        Increment (when mouse leaves)
32538      </button>
32539      count: {{count}}
32540     </file>
32541   </example>
32542 */
32543
32544
32545/**
32546 * @ngdoc directive
32547 * @name ngMousemove
32548 *
32549 * @description
32550 * Specify custom behavior on mousemove event.
32551 *
32552 * @element ANY
32553 * @priority 0
32554 * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
32555 * mousemove. ({@link guide/expression#-event- Event object is available as `$event`})
32556 *
32557 * @example
32558   <example name="ng-mousemove">
32559     <file name="index.html">
32560      <button ng-mousemove="count = count + 1" ng-init="count=0">
32561        Increment (when mouse moves)
32562      </button>
32563      count: {{count}}
32564     </file>
32565   </example>
32566 */
32567
32568
32569/**
32570 * @ngdoc directive
32571 * @name ngKeydown
32572 *
32573 * @description
32574 * Specify custom behavior on keydown event.
32575 *
32576 * @element ANY
32577 * @priority 0
32578 * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
32579 * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
32580 *
32581 * @example
32582   <example name="ng-keydown">
32583     <file name="index.html">
32584      <input ng-keydown="count = count + 1" ng-init="count=0">
32585      key down count: {{count}}
32586     </file>
32587   </example>
32588 */
32589
32590
32591/**
32592 * @ngdoc directive
32593 * @name ngKeyup
32594 *
32595 * @description
32596 * Specify custom behavior on keyup event.
32597 *
32598 * @element ANY
32599 * @priority 0
32600 * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
32601 * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
32602 *
32603 * @example
32604   <example name="ng-keyup">
32605     <file name="index.html">
32606       <p>Typing in the input box below updates the key count</p>
32607       <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}}
32608
32609       <p>Typing in the input box below updates the keycode</p>
32610       <input ng-keyup="event=$event">
32611       <p>event keyCode: {{ event.keyCode }}</p>
32612       <p>event altKey: {{ event.altKey }}</p>
32613     </file>
32614   </example>
32615 */
32616
32617
32618/**
32619 * @ngdoc directive
32620 * @name ngKeypress
32621 *
32622 * @description
32623 * Specify custom behavior on keypress event.
32624 *
32625 * @element ANY
32626 * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
32627 * keypress. ({@link guide/expression#-event- Event object is available as `$event`}
32628 * and can be interrogated for keyCode, altKey, etc.)
32629 *
32630 * @example
32631   <example name="ng-keypress">
32632     <file name="index.html">
32633      <input ng-keypress="count = count + 1" ng-init="count=0">
32634      key press count: {{count}}
32635     </file>
32636   </example>
32637 */
32638
32639
32640/**
32641 * @ngdoc directive
32642 * @name ngSubmit
32643 *
32644 * @description
32645 * Enables binding angular expressions to onsubmit events.
32646 *
32647 * Additionally it prevents the default action (which for form means 
32647sending the request to the
32648 * server and reloading the current page), but only if the form does not contain `action`,
32649 * `data-action`, or `x-action` attributes.
32650 *
32651 * <div class="alert alert-warning">
32652 * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and
32653 * `ngSubmit` handlers together. See the
32654 * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}
32655 * for a detailed discussion of when `ngSubmit` may be triggered.
32656 * </div>
32657 *
32658 * @element form
32659 * @priority 0
32660 * @param {expression} ngSubmit {@link guide/expression Expression} to eval.
32661 * ({@link guide/expression#-event- Event object is available as `$event`})
32662 *
32663 * @example
32664   <example module="submitExample" name="ng-submit">
32665     <file name="index.html">
32666      <script>
32667        angular.module('submitExample', [])
32668          .controller('ExampleController', ['$scope', function($scope) {
32669            $scope.list = [];
32670            $scope.text = 'hello';
32671            $scope.submit = function() {
32672              if ($scope.text) {
32673                $scope.list.push(this.text);
32674                $scope.text = '';
32675              }
32676            };
32677          }]);
32678      </script>
32679      <form ng-submit="submit()" ng-controller="ExampleController">
32680        Enter text and hit enter:
32681        <input type="text" ng-model="text" name="text" />
32682        <input type="submit" id="submit" value="Submit" />
32683        <pre>list={{list}}</pre>
32684      </form>
32685     </file>
32686     <file name="protractor.js" type="protractor">
32687       it('should check ng-submit', function() {
32688         expect(element(by.binding('list')).getText()).toBe('list=[]');
32689         element(by.css('#submit')).click();
32690         expect(element(by.binding('list')).getText()).toContain('hello');
32691         expect(element(by.model('text')).getAttribute('value')).toBe('');
32692       });
32693       it('should ignore empty strings', function() {
32694         expect(element(by.binding('list')).getText()).toBe('list=[]');
32695         element(by.css('#submit')).click();
32696         element(by.css('#submit')).click();
32697         expect(element(by.binding('list')).getText()).toContain('hello');
32698        });
32699     </file>
32700   </example>
32701 */
32702
32703/**
32704 * @ngdoc directive
32705 * @name ngFocus
32706 *
32707 * @description
32708 * Specify custom behavior on focus event.
32709 *
32710 * Note: As the `focus` event is executed synchronously when calling `input.focus()`
32711 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
32712 * during an `$apply` to ensure a consistent state.
32713 *
32714 * @element window, input, select, textarea, a
32715 * @priority 0
32716 * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
32717 * focus. ({@link guide/expression#-event- Event object is available as `$event`})
32718 *
32719 * @example
32720 * See {@link ng.directive:ngClick ngClick}
32721 */
32722
32723/**
32724 * @ngdoc directive
32725 * @name ngBlur
32726 *
32727 * @description
32728 * Specify custom behavior on blur event.
32729 *
32730 * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when
32731 * an element has lost focus.
32732 *
32733 * Note: As the `blur` event is executed synchronously also during DOM manipulations
32734 * (e.g. removing a focussed input),
32735 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
32736 * during an `$apply` to ensure a consistent state.
32737 *
32738 * @element window, input, select, textarea, a
32739 * @priority 0
32740 * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
32741 * blur. ({@link guide/expression#-event- Event object is available as `$event`})
32742 *
32743 * @example
32744 * See {@link ng.directive:ngClick ngClick}
32745 */
32746
32747/**
32748 * @ngdoc directive
32749 * @name ngCopy
32750 *
32751 * @description
32752 * Specify custom behavior on copy event.
32753 *
32754 * @element window, input, select, textarea, a
32755 * @priority 0
32756 * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
32757 * copy. ({@link guide/expression#-event- Event object is available as `$event`})
32758 *
32759 * @example
32760   <example name="ng-copy">
32761     <file name="index.html">
32762      <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
32763      copied: {{copied}}
32764     </file>
32765   </example>
32766 */
32767
32768/**
32769 * @ngdoc directive
32770 * @name ngCut
32771 *
32772 * @description
32773 * Specify custom behavior on cut event.
32774 *
32775 * @element window, input, select, textarea, a
32776 * @priority 0
32777 * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
32778 * cut. ({@link guide/expression#-event- Event object is available as `$event`})
32779 *
32780 * @example
32781   <example name="ng-cut">
32782     <file name="index.html">
32783      <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
32784      cut: {{cut}}
32785     </file>
32786   </example>
32787 */
32788
32789/**
32790 * @ngdoc directive
32791 * @name ngPaste
32792 *
32793 * @description
32794 * Specify custom behavior on paste event.
32795 *
32796 * @element window, input, select, textarea, a
32797 * @priority 0
32798 * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
32799 * paste. ({@link guide/expression#-event- Event object is available as `$event`})
32800 *
32801 * @example
32802   <example name="ng-paste">
32803     <file name="index.html">
32804      <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
32805      pasted: {{paste}}
32806     </file>
32807   </example>
32808 */
32809
32810/**
32811 * @ngdoc directive
32812 * @name ngIf
32813 * @restrict A
32814 * @multiElement
32815 *
32816 * @description
32817 * The `ngIf` directive removes or recreates a portion of the DOM tree based on an
32818 * {expression}. If the expression assigned to `ngIf` evaluates to a false
32819 * value then the element is removed from the DOM, otherwise a clone of the
32820 * element is reinserted into the DOM.
32821 *
32822 * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the
32823 * element in the DOM rather than changing its visibility via the `display` css property.  A common
32824 * case when this difference is significant is when using css selectors that rely on an element's
32825 * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classe
32825s.
32826 *
32827 * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
32828 * is created when the element is restored.  The scope created within `ngIf` inherits from
32829 * its parent scope using
32830 * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance).
32831 * An important implication of this is if `ngModel` is used within `ngIf` to bind to
32832 * a javascript primitive defined in the parent scope. In this case any modifications made to the
32833 * variable within the child scope will override (hide) the value in the parent scope.
32834 *
32835 * Also, `ngIf` recreates elements using their compiled state. An example of this behavior
32836 * is if an element's class attribute is directly modified after it's compiled, using something like
32837 * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
32838 * the added class will be lost because the original compiled state is used to regenerate the element.
32839 *
32840 * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter`
32841 * and `leave` effects.
32842 *
32843 * @animations
32844 * | Animation                        | Occurs                               |
32845 * |----------------------------------|-------------------------------------|
32846 * | {@link ng.$animate#enter enter}  | just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container |
32847 * | {@link ng.$animate#leave leave}  | just before the `ngIf` contents are removed from the DOM |
32848 *
32849 * @element ANY
32850 * @scope
32851 * @priority 600
32852 * @param {expression} ngIf If the {@link guide/expression expression} is falsy then
32853 *     the element is removed from the DOM tree. If it is truthy a copy of the compiled
32854 *     element is added to the DOM tree.
32855 *
32856 * @example
32857  <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-if">
32858    <file name="index.html">
32859      <label>Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /></label><br/>
32860      Show when checked:
32861      <span ng-if="checked" class="animate-if">
32862        This is removed when the checkbox is unchecked.
32863      </span>
32864    </file>
32865    <file name="animations.css">
32866      .animate-if {
32867        background:white;
32868        border:1px solid black;
32869        padding:10px;
32870      }
32871
32872      .animate-if.ng-enter, .animate-if.ng-leave {
32873        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
32874      }
32875
32876      .animate-if.ng-enter,
32877      .animate-if.ng-leave.ng-leave-active {
32878        opacity:0;
32879      }
32880
32881      .animate-if.ng-leave,
32882      .animate-if.ng-enter.ng-enter-active {
32883        opacity:1;
32884      }
32885    </file>
32886  </example>
32887 */
32888var ngIfDirective = ['$animate', '$compile', function($animate, $compile) {
32889  return {
32890    multiElement: true,
32891    transclude: 'element',
32892    priority: 600,
32893    terminal: true,
32894    restrict: 'A',
32895    $$tlb: true,
32896    link: function($scope, $element, $attr, ctrl, $transclude) {
32897        var block, childScope, previousElements;
32898        $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
32899
32900          if (value) {
32901            if (!childScope) {
32902              $transclude(function(clone, newScope) {
32903                childScope = newScope;
32904                clone[clone.length++] = $compile.$$createComment('end ngIf', $attr.ngIf);
32905                // Note: We only need the first/last node of the cloned nodes.
32906                // However, we need to keep the reference to the jqlite wrapper as it might be changed later
32907                // by a directive with templateUrl when its template arrives.
32908                block = {
32909                  clone: clone
32910                };
32911                $animate.enter(clone, $element.parent(), $element);
32912              });
32913            }
32914          } else {
32915            if (previousElements) {
32916              previousElements.remove();
32917              previousElements = null;
32918            }
32919            if (childScope) {
32920              childScope.$destroy();
32921              childScope = null;
32922            }
32923            if (block) {
32924              previousElements = getBlockNodes(block.clone);
32925              $animate.leave(previousElements).done(function(response) {
32926                if (response !== false) previousElements = null;
32927              });
32928              block = null;
32929            }
32930          }
32931        });
32932    }
32933  };
32934}];
32935
32936/**
32937 * @ngdoc directive
32938 * @name ngInclude
32939 * @restrict ECA
32940 *
32941 * @description
32942 * Fetches, compiles and includes an external HTML fragment.
32943 *
32944 * By default, the template URL is restricted to the same domain and protocol as the
32945 * application document. This is done by calling {@link $sce#getTrustedResourceUrl
32946 * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols
32947 * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or
32948 * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link
32949 * ng.$sce Strict Contextual Escaping}.
32950 *
32951 * In addition, the browser's
32952 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
32953 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
32954 * policy may further restrict whether the template is successfully loaded.
32955 * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://`
32956 * access on some browsers.
32957 *
32958 * @animations
32959 * | Animation                        | Occurs                              |
32960 * |----------------------------------|-------------------------------------|
32961 * | {@link ng.$animate#enter enter}  | when the expression changes, on the new include |
32962 * | {@link ng.$animate#leave leave}  | when the expression changes, on the old include |
32963 *
32964 * The enter and leave animation occur concurrently.
32965 *
32966 * @scope
32967 * @priority 400
32968 *
32969 * @param {string} ngInclude|src angular
vendor: 9,710 bytes, lines 32969-33246
32969 expression evaluating to URL. If the source is a string constant,
32970 *                 make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
32971 * @param {string=} onload Expression to evaluate when a new partial is loaded.
32972 *                  <div class="alert alert-warning">
32973 *                  **Note:** When using onload on SVG elements in IE11, the browser will try to call
32974 *                  a function with the name on the window element, which will usually throw a
32975 *                  "function is undefined" error. To fix this, you can instead use `data-onload` or a
32976 *                  different form that {@link guide/directive#normalization matches} `onload`.
32977 *                  </div>
32978   *
32979 * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
32980 *                  $anchorScroll} to scroll the viewport after the content is loaded.
32981 *
32982 *                  - If the attribute is not set, disable scrolling.
32983 *                  - If the attribute is set without value, enable scrolling.
32984 *                  - Otherwise enable scrolling only if the expression evaluates to truthy value.
32985 *
32986 * @example
32987  <example module="includeExample" deps="angular-animate.js" animations="true" name="ng-include">
32988    <file name="index.html">
32989     <div ng-controller="ExampleController">
32990       <select ng-model="template" ng-options="t.name for t in templates">
32991        <option value="">(blank)</option>
32992       </select>
32993       url of the template: <code>{{template.url}}</code>
32994       <hr/>
32995       <div class="slide-animate-container">
32996         <div class="slide-animate" ng-include="template.url"></div>
32997       </div>
32998     </div>
32999    </file>
33000    <file name="script.js">
33001      angular.module('includeExample', ['ngAnimate'])
33002        .controller('ExampleController', ['$scope', function($scope) {
33003          $scope.templates =
33004            [{ name: 'template1.html', url: 'template1.html'},
33005             { name: 'template2.html', url: 'template2.html'}];
33006          $scope.template = $scope.templates[0];
33007        }]);
33008     </file>
33009    <file name="template1.html">
33010      Content of template1.html
33011    </file>
33012    <file name="template2.html">
33013      Content of template2.html
33014    </file>
33015    <file name="animations.css">
33016      .slide-animate-container {
33017        position:relative;
33018        background:white;
33019        border:1px solid black;
33020        height:40px;
33021        overflow:hidden;
33022      }
33023
33024      .slide-animate {
33025        padding:10px;
33026      }
33027
33028      .slide-animate.ng-enter, .slide-animate.ng-leave {
33029        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
33030
33031        position:absolute;
33032        top:0;
33033        left:0;
33034        right:0;
33035        bottom:0;
33036        display:block;
33037        padding:10px;
33038      }
33039
33040      .slide-animate.ng-enter {
33041        top:-50px;
33042      }
33043      .slide-animate.ng-enter.ng-enter-active {
33044        top:0;
33045      }
33046
33047      .slide-animate.ng-leave {
33048        top:0;
33049      }
33050      .slide-animate.ng-leave.ng-leave-active {
33051        top:50px;
33052      }
33053    </file>
33054    <file name="protractor.js" type="protractor">
33055      var templateSelect = element(by.model('template'));
33056      var includeElem = element(by.css('[ng-include]'));
33057
33058      it('should load template1.html', function() {
33059        expect(includeElem.getText()).toMatch(/Content of template1.html/);
33060      });
33061
33062      it('should load template2.html', function() {
33063        if (browser.params.browser === 'firefox') {
33064          // Firefox can't handle using selects
33065          // See https://github.com/angular/protractor/issues/480
33066          return;
33067        }
33068        templateSelect.click();
33069        templateSelect.all(by.css('option')).get(2).click();
33070        expect(includeElem.getText()).toMatch(/Content of template2.html/);
33071      });
33072
33073      it('should change to blank', function() {
33074        if (browser.params.browser === 'firefox') {
33075          // Firefox can't handle using selects
33076          return;
33077        }
33078        templateSelect.click();
33079        templateSelect.all(by.css('option')).get(0).click();
33080        expect(includeElem.isPresent()).toBe(false);
33081      });
33082    </file>
33083  </example>
33084 */
33085
33086
33087/**
33088 * @ngdoc event
33089 * @name ngInclude#$includeContentRequested
33090 * @eventType emit on the scope ngInclude was declared in
33091 * @description
33092 * Emitted every time the ngInclude content is requested.
33093 *
33094 * @param {Object} angularEvent Synthetic event object.
33095 * @param {String} src URL of content to load.
33096 */
33097
33098
33099/**
33100 * @ngdoc event
33101 * @name ngInclude#$includeContentLoaded
33102 * @eventType emit on the current ngInclude scope
33103 * @description
33104 * Emitted every time the ngInclude content is reloaded.
33105 *
33106 * @param {Object} angularEvent Synthetic event object.
33107 * @param {String} src URL of content to load.
33108 */
33109
33110
33111/**
33112 * @ngdoc event
33113 * @name ngInclude#$includeContentError
33114 * @eventType emit on the scope ngInclude was declared in
33115 * @description
33116 * Emitted when a template HTTP request yields an erroneous response (status < 200 || status > 299)
33117 *
33118 * @param {Object} angularEvent Synthetic event object.
33119 * @param {String} src URL of content to load.
33120 */
33121var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate',
33122                  function($templateRequest,   $anchorScroll,   $animate) {
33123  return {
33124    restrict: 'ECA',
33125    priority: 400,
33126    terminal: true,
33127    transclude: 'element',
33128    controller: angular.noop,
33129    compile: function(element, attr) {
33130      var srcExp = attr.ngInclude || attr.src,
33131          onloadExp = attr.onload || '',
33132          autoScrollExp = attr.autoscroll;
33133
33134      return function(scope, $element, $attr, ctrl, $transclude) {
33135        var changeCounter = 0,
33136            currentScope,
33137            previousElement,
33138            currentElement;
33139
33140        var cleanupLastIncludeContent = function() {
33141          if (previousElement) {
33142            previousElement.remove();
33143            previousElement = null;
33144          }
33145          if (currentScope) {
33146            currentScope.$destroy();
33147            currentScope = null;
33148          }
33149          if (currentElement) {
33150            $animate.leave(currentElement).done(function(response) {
33151              if (response !== false) previousElement = null;
33152            });
33153            previousElement = currentElement;
33154            currentElement = null;
33155          }
33156        };
33157
33158        scope.$watch(srcExp, function ngIncludeWatchAction(src) {
33159          var afterAnimation = function(response) {
33160            if (response !== false && isDefined(autoScrollExp) &&
33161              (!autoScrollExp || scope.$eval(autoScrollExp))) {
33162                $anchorScroll();
33163            }
33164          };
33165          var thisChangeId = ++changeCounter;
33166
33167          if (src) {
33168            //set the 2nd param to true to ignore the template request error so that the inner
33169            //contents and scope can be cleaned up.
33170            $templateRequest(src, true).then(function(response) {
33171              if (scope.$$destroyed) return;
33172
33173              if (thisChangeId !== changeCounter) return;
33174              var newScope = scope.$new();
33175              ctrl.template = response;
33176
33177              // Note: This will also link all children of ng-include that were contained in the original
33178              // html. If that content contains controllers, ... they could pollute/change the scope.
33179              // However, using ng-include on an element with additional content does not make sense...
33180              // Note: We can't remove them in the cloneAttchFn of $transclude as that
33181              // function is called before linking the content, which would apply child
33182              // directives to non existing elements.
33183              var clone = $transclude(newScope, function(clone) {
33184                cleanupLastIncludeContent();
33185                $animate.enter(clone, null, $element).done(afterAnimation);
33186              });
33187
33188              currentScope = newScope;
33189              currentElement = clone;
33190
33191              currentScope.$emit('$includeContentLoaded', src);
33192              scope.$eval(onloadExp);
33193            }, function() {
33194              if (scope.$$destroyed) return;
33195
33196              if (thisChangeId === changeCounter) {
33197                cleanupLastIncludeContent();
33198                scope.$emit('$includeContentError', src);
33199              }
33200            });
33201            scope.$emit('$includeContentRequested', src);
33202          } else {
33203            cleanupLastIncludeContent();
33204            ctrl.template = null;
33205          }
33206        });
33207      };
33208    }
33209  };
33210}];
33211
33212// This directive is called during the $transclude call of the first `ngInclude` directive.
33213// It will replace and compile the content of the element with the loaded template.
33214// We need this directive so that the element content is already filled when
33215// the link function of another directive on the same element as ngInclude
33216// is called.
33217var ngIncludeFillContentDirective = ['$compile',
33218  function($compile) {
33219    return {
33220      restrict: 'ECA',
33221      priority: -400,
33222      require: 'ngInclude',
33223      link: function(scope, $element, $attr, ctrl) {
33224        if (toString.call($element[0]).match(/SVG/)) {
33225          // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not
33226          // support innerHTML, so detect this here and try to generate the contents
33227          // specially.
33228          $element.empty();
33229          $compile(jqLiteBuildFragment(ctrl.template, window.document).childNodes)(scope,
33230              function namespaceAdaptedClone(clone) {
33231            $element.append(clone);
33232          }, {futureParentElement: $element});
33233          return;
33234        }
33235
33236        $element.html(ctrl.template);
33237        $compile($element.contents())(scope);
33238      }
33239    };
33240  }];
33241
33242/**
33243 * @ngdoc directive
33244 * @name ngInit
33245 * @restrict AC
33246 *
33246
33247 * @description
33248 * The `ngInit` directive allows you to evaluate an expression in the
33249 * current scope.
33250 *
33251 * <div class="alert alert-danger">
33252 * This directive can be abused to add unnecessary amounts of logic into your templates.
33253 * There are only a few appropriate uses of `ngInit`, such as for aliasing special properties of
33254 * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below; and for injecting data via
33255 * server side scripting. Besides these few cases, you should use {@link guide/controller controllers}
33256 * rather than `ngInit` to initialize values on a scope.
33257 * </div>
33258 *
33259 * <div class="alert alert-warning">
33260 * **Note**: If you have assignment in `ngInit` along with a {@link ng.$filter `filter`}, make
33261 * sure you have parentheses to ensure correct operator precedence:
33262 * <pre class="prettyprint">
33263 * `<div ng-init="test1 = ($index | toString)"></div>`
33264 * </pre>
33265 * </div>
33266 *
33267 * @priority 450
33268 *
33269 * @element ANY
33270 * @param {expression} ngInit {@link guide/expression Expression} to eval.
33271 *
33272 * @example
33273   <example module="initExample" name="ng-init">
33274     <file name="index.html">
33275   <script>
33276     angular.module('initExample', [])
33277       .controller('ExampleController', ['$scope', function($scope) {
33278         $scope.list = [['a', 'b'], ['c', 'd']];
33279       }]);
33280   </script>
33281   <div ng-controller="ExampleController">
33282     <div ng-repeat="innerList in list" ng-init="outerIndex = $index">
33283       <div ng-repeat="value in innerList" ng-init="innerIndex = $index">
33284          <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>
33285       </div>
33286     </div>
33287   </div>
33288     </file>
33289     <file name="protractor.js" type="protractor">
33290       it('should alias index positions', function() {
33291         var elements = element.all(by.css('.example-init'));
33292         expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');
33293         expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');
33294         expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');
33295         expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');
33296       });
33297     </file>
33298   </example>
33299 */
33300var ngInitDirective = ngDirective({
33301  priority: 450,
33302  compile: function() {
33303    return {
33304      pre: function(scope, element, attrs) {
33305        scope.$eval(attrs.ngInit);
33306      }
33307    };
33308  }
33309});
33310
33311/**
33312 * @ngdoc directive
33313 * @name ngList
33314 *
33315 * @description
33316 * Text input that converts between a delimited string and an array of strings. The default
33317 * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom
33318 * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`.
33319 *
33320 * The behaviour of the directive is affected by the use of the `ngTrim` attribute.
33321 * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each
33322 *   list item is respected. This implies that the user of the directive is responsible for
33323 *   dealing with whitespace but also allows you to use whitespace as a delimiter, such as a
33324 *   tab or newline character.
33325 * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected
33326 *   when joining the list items back together) and whitespace around each list item is stripped
33327 *   before it is added to the model.
33328 *
33329 * ### Example with Validation
33330 *
33331 * <example name="ngList-directive" module="listExample">
33332 *   <file name="app.js">
33333 *      angular.module('listExample', [])
33334 *        .controller('ExampleController', ['$scope', function($scope) {
33335 *          $scope.names = ['morpheus', 'neo', 'trinity'];
33336 *        }]);
33337 *   </file>
33338 *   <file name="index.html">
33339 *    <form name="myForm" ng-controller="ExampleController">
33340 *      <label>List: <input name="namesInput" ng-model="names" ng-list required></label>
33341 *      <span role="alert">
33342 *        <span class="error" ng-show="myForm.namesInput.$error.required">
33343 *        Required!</span>
33344 *      </span>
33345 *      <br>
33346 *      <tt>names = {{names}}</tt><br/>
33347 *      <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
33348 *      <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
33349 *      <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
33350 *      <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
33351 *     </form>
33352 *   </file>
33353 *   <file name="protractor.js" type="protractor">
33354 *     var listInput = element(by.model('names'));
33355 *     var names = element(by.exactBinding('names'));
33356 *     var valid = element(by.binding('myForm.namesInput.$valid'));
33357 *     var error = element(by.css('span.error'));
33358 *
33359 *     it('should initialize to model', function() {
33360 *       expect(names.getText()).toContain('["morpheus","neo","trinity"]');
33361 *       expect(valid.getText()).toContain('true');
33362 *       expect(error.getCssValue('display')).toBe('none');
33363 *     });
33364 *
33365 *     it('should be invalid if empty', function() {
33366 *       listInput.clear();
33367 *       listInput.sendKeys('');
33368 *
33369 *       expect(names.getText()).toContain('');
33370 *       expect(valid.getText()).toContain('false');
33371 *       expect(error.getCssValue('display')).not.toBe('none');
33372 *     });
33373 *   </file>
33374 * </example>
33375 *
33376 * ### Example - splitting on newline
33377 * <example name="ngList-directive-newlines">
33378 *   <file name="index.html">
33379 *    <textarea ng-model="list" ng-list="&#10;" ng-trim="false"></textarea>
33380 *    <pre>{{ list | json }}</pre>
33381 *   </file>
33382 *   <file name="protractor.js" type="protractor">
33383 *     it("should split the text by newlines", function() {
33384 *       var listInput = element(by.model('list'));
33385 *       var output = element(by.binding('list | json'));
33386 *       listInput.sendKeys('abc\ndef\nghi');
33387 *       expect(output.getText()).toContain('[\n  "abc",\n  "def",\n  "ghi"\n]');
33388 *     });
33389 *   </file>
33390 * </example>
33391 *
33392 * @element input
33393 * @param {string=} ngList optional delimiter that should be used to split the value.
33394 */
33395var ngListDirective = function() {
33396  return {
33397    restrict: 'A',
33398    priority: 100,
33399    require: 'ngModel',
33400    link: function(scope, element, attr, ctrl) {
33401      // We want to control whitespace trimming so we use this convoluted approach
33402      // to access the ngList attribute, which doesn't pre-trim the attribute
33403      var ngList = element.attr(attr.$attr.ngList) || ', ';
33404      var trimValues = attr.ngTrim !== 'false';
33405      var separator = trimValues ? trim(ngList) : ngList;
33406
33407      var parse = function(viewValue) {
33408        // If the viewValue is invalid (say required but empty) it will be `undefined`
33409        if (isUndefined(viewValue)) return;
33410
33411        var list = [];
33412
33413        if (viewValue) {
33414          forEach(viewValue.split(separator), function(value) {
33415            if (value) list.push(trimValues ? trim(value) : value);
33416          });
33417        }
33418
33419        return list;
33420      };
33421
33422      ctrl.$parsers.push(parse);
33423      ctrl.$formatters.push(function(value) {
33424        if (isArray(value)) {
33425          return value.join(ngList);
33426        }
33427
33428        return undefined;
33429      });
33430
33431      // Override the standard $isEmpty because an empty array means the input is empty.
33432      ctrl.$isEmpty = function(value) {
33433        return !value || !value.length;
33434      };
33435    }
33436  };
33437};
33438
33439/* global VALID_CLASS: true,
33440  INVALID_CLASS: true,
33441  PRISTINE_CLASS: true,
33442  DIRTY_CLASS: true,
33443  UNTOUCHED_CLASS: true,
33444  TOUCHED_CLASS: true
33445*/
33446
33447var VALID_CLASS = 'ng-valid',
33448    INVALID_CLASS = 'ng-invalid',
33449    PRISTINE_CLASS = 'ng-pristine',
33450    DIRTY_CLASS = 'ng-dirty',
33451    UNTOUCHED_CLASS = 'ng-untouched',
33452    TOUCHED_CLASS = 'ng-touched',
33453    PENDING_CLASS = 'ng-pending',
33454    EMPTY_CLASS = 'ng-empty',
33455    NOT_EMPTY_CLASS = 'ng-not-empty';
33456
33457var ngModelMinErr = minErr('ngModel');
33458
33459/**
33460 * @ngdoc type
33461 * @name ngModel.NgModelController
33462 *
33463 * @property {*} $viewValue The actual value from the control's view. For `input` elements, this is a
33464 * String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue
33465 * is set.
33466 * @property {*} $modelValue The value in the model that the control is bound to.
33467 * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
33468       the control reads value from the DOM. The functions are called in array order, each passing
33469       its return value through to the next. The last return value is forwarded to the
33470       {@link ngModel.NgModelController#$validators `$validators`} collection.
33471
33472Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
33473`$viewValue`}.
33474
33475Returning `undefined` from a parser means a parse error occurred. In that case,
33476no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
33477will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
33478is set to `true`. The parse error is stored in `ngModel.$error.parse`.
33479
33480 *
33481 * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
33482       the model value changes. The functions are called in reverse array order, each passing the value through to the
33483       next. The last return value is used as the actual DOM value.
33484       Used to format / convert values for display in the control.
33485 * ```js
33486 * function formatter(value) {
33487 *   if (value) {
33488 *     return value.toUpperCase();
33489 *   }
33490 * }
33491 * ngModel.$formatters.push(formatter);
33492 * ```
33493 *
33494 * @property {Object.<string, function>} $validators A collection of validators that are applied
33495 *      whenever the model value changes. The key value within the object refers to the name of the
33496 *      validator while the function refers to the validation operation. The validation operation is
33497 *      provided with the model value as an argument and must return a true or false value depending
33498 *      on the response of that validation.
33499 *
33500 * ```js
33501 * ngModel.$validators.validCharacters = function(modelValue, viewValue) {
33502 *   var value = modelValue || viewValue;
33503 *   return /[0-9]+/.test(value) &&
33504 *          /[a-z]+/.test(value) &&
33505 *          /[A-Z]+/.test(value) &&
33506 *          /\W+/.test(value);
33507 * };
33508 * ```
33509 *
33510 * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to
33511 *      perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided
33512 *      is expected to return a promise when it is run during the model validation process. Once the promise
33513 *      is delivered then the validation status will be set to true when fulfilled and false when rejected.
33514 *      When the asynchronous validators are triggered, each of the validators will run in parallel and the model
33515 *      value will only be updated once all validators have been fulfilled. As long as an asynchronous validator
33516 *      is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators
33517 *      will only run once all synchronous validators have passed.
33518 *
33519 * Please note that if $http is used then it is important that the server returns a success HTTP response code
33520 * in order to fulfill the validation and a status level of `4xx` in order to reject the validation.
33521 *
33522 * ```js
33523 * ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
33524 *   var value = modelValue || viewValue;
33525 *
33526 *   // Lookup user by username
33527 *   return $http.get('/api/users/' + value).
33528 *      then(function resolved() {
33529 *        //username exists, this means validation fails
33530 *        return $q.reject('exists');
33531 *      }, function rejected() {
33532 *        //username does not exist, therefore this validation passes
33533 *        return true;
33534 *      });
33535 * };
33536 * ```
33537 *
33538 * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
33539 *     view value has changed. It is called with no arguments, and its return value is ignored.
33540 *     This can be used in place of additional $watches against the model value.
33541 *
33542 * @property {Object} $error An object hash with all failing validator ids as keys.
33543 * @property {Object} $pending An object hash with all pending validator ids as keys.
33544 *
33545 * @property {boolean} $untouched True if control has not lost focus yet.
33546 * @property {boolean} $touched True if control has lost focus.
33547 * @property {boolean} $pristine True if user has not interacted with the control yet.
33548 * @property {boolean} $dirty True if user has already interacted with the control.
33549 * @property {boolean} $valid True if there is no error.
33550 * @property {boolean}
33550 $invalid True if at least one error on the control.
33551 * @property {string} $name The name attribute of the control.
33552 *
33553 * @description
33554 *
33555 * `NgModelController` provides API for the {@link ngModel `ngModel`} directive.
33556 * The controller contains services for data-binding, validation, CSS updates, and value formatting
33557 * and parsing. It purposefully does not contain any logic which deals with DOM rendering or
33558 * listening to DOM events.
33559 * Such DOM related logic should be provided by other directives which make use of
33560 * `NgModelController` for data-binding to control elements.
33561 * Angular provides this DOM logic for most {@link input `input`} elements.
33562 * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example
33563 * custom control example} that uses `ngModelController` to bind to `contenteditable` elements.
33564 *
33565 * @example
33566 * ### Custom Control Example
33567 * This example shows how to use `NgModelController` with a custom control to achieve
33568 * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
33569 * collaborate together to achieve the desired result.
33570 *
33571 * `contenteditable` is an HTML5 attribute, which tells the browser to let the element
33572 * contents be edited in place by the user.
33573 *
33574 * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}
33575 * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`).
33576 * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks
33577 * that content using the `$sce` service.
33578 *
33579 * <example name="NgModelController" module="customControl" deps="angular-sanitize.js">
33580    <file name="style.css">
33581      [contenteditable] {
33582        border: 1px solid black;
33583        background-color: white;
33584        min-height: 20px;
33585      }
33586
33587      .ng-invalid {
33588        border: 1px solid red;
33589      }
33590
33591    </file>
33592    <file name="script.js">
33593      angular.module('customControl', ['ngSanitize']).
33594        directive('contenteditable', ['$sce', function($sce) {
33595          return {
33596            restrict: 'A', // only activate on element attribute
33597            require: '?ngModel', // get a hold of NgModelController
33598            link: function(scope, element, attrs, ngModel) {
33599              if (!ngModel) return; // do nothing if no ng-model
33600
33601              // Specify how UI should be updated
33602              ngModel.$render = function() {
33603                element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
33604              };
33605
33606              // Listen for change events to enable binding
33607              element.on('blur keyup change', function() {
33608                scope.$evalAsync(read);
33609              });
33610              read(); // initialize
33611
33612              // Write data to the model
33613              function read() {
33614                var html = element.html();
33615                // When we clear the content editable the browser leaves a <br> behind
33616                // If strip-br attribute is provided then we strip this out
33617                if (attrs.stripBr && html === '<br>') {
33618                  html = '';
33619                }
33620                ngModel.$setViewValue(html);
33621              }
33622            }
33623          };
33624        }]);
33625    </file>
33626    <file name="index.html">
33627      <form name="myForm">
33628       <div contenteditable
33629            name="myWidget" ng-model="userContent"
33630            strip-br="true"
33631            required>Change me!</div>
33632        <span ng-show="myForm.myWidget.$error.required">Required!</span>
33633       <hr>
33634       <textarea ng-model="userContent" aria-label="Dynamic textarea"></textarea>
33635      </form>
33636    </file>
33637    <file name="protractor.js" type="protractor">
33638    it('should data-bind and become invalid', function() {
33639      if (browser.params.browser === 'safari' || browser.params.browser === 'firefox') {
33640        // SafariDriver can't handle contenteditable
33641        // and Firefox driver can't clear contenteditables very well
33642        return;
33643      }
33644      var contentEditable = element(by.css('[contenteditable]'));
33645      var content = 'Change me!';
33646
33647      expect(contentEditable.getText()).toEqual(content);
33648
33649      contentEditable.clear();
33650      contentEditable.sendKeys(protractor.Key.BACK_SPACE);
33651      expect(contentEditable.getText()).toEqual('');
33652      expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
33653    });
33654    </file>
33655 * </example>
33656 *
33657 *
33658 */
33659var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q', '$interpolate',
33660    /** @this */ function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) {
33661  this.$viewValue = Number.NaN;
33662  this.$modelValue = Number.NaN;
33663  this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity.
33664  this.$validators = {};
33665  this.$asyncValidators = {};
33666  this.$parsers = [];
33667  this.$formatters = [];
33668  this.$viewChangeListeners = [];
33669  this.$untouched = true;
33670  this.$touched = false;
33671  this.$pristine = true;
33672  this.$dirty = false;
33673  this.$valid = true;
33674  this.$invalid = false;
33675  this.$error = {}; // keep invalid keys here
33676  this.$$success = {}; // keep valid keys here
33677  this.$pending = undefined; // keep pending keys here
33678  this.$name = $interpolate($attr.name || '', false)($scope);
33679  this.$$parentForm = nullFormCtrl;
33680
33681  var parsedNgModel = $parse($attr.ngModel),
33682      parsedNgModelAssign = parsedNgModel.assign,
33683      ngModelGet = parsedNgModel,
33684      ngModelSet = parsedNgModelAssign,
33685      pendingDebounce = null,
33686      parserValid,
33687      ctrl = this;
33688
33689  this.$$setOptions = function(options) {
33690    ctrl.$options = options;
33691    if (options && options.getterSetter) {
33692      var invokeModelGetter = $parse($attr.ngModel + '()'),
33693          invokeModelSetter = $parse($attr.ngModel + '($$$p)');
33694
33695      ngModelGet = function($scope) {
33696        var modelValue = parsedNgModel($scope);
33697        if (isFunction(modelValue)) {
33698          modelValue = invokeModelGetter($scope);
33699        }
33700        return modelValue;
33701      };
33702      ngModelSet = function($scope, newValue) {
33703        if (isFunction(parsedNgModel($scope))) {
33704          invokeModelSetter($scope, {$$$p: newValue});
33705        } else {
33706          parsedNgModelAssign($scope, newValue);
33707        }
33708      };
33709    } else if (!parsedNgModel.assign) {
33710      throw ngModelMinErr('nonassign', 'Expression \'{0}\' is non-assignable. Element: {1}',
33711          $attr.ngModel, startingTag($element));
33712    }
33713  };
33714
33715  /**
33716   * @ngdoc method
33717   * @name ngModel.NgModelController#$render
33718   *
33719   * @description
33720   * Called when the view needs to be updated. It is expected that the user of the ng-model
33721   * directive will implement this method.
33722   *
33723   * The `$render()` method is invoked in the following situations:
33724   *
33725   * * `$rollbackViewValue()` is called.  If we are rolling back the view value to the last
33726   *   committed value then `$render()` is called to update the input control.
33727   * * The value referenced by `ng-model` is changed programmatically and both the `$modelValue` and
33728   *   the `$viewValue` are different from last time.
33729   *
33730   * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of
33731   * `$modelValue` and `$viewValue` are actually different from their previous values. If `$modelValue`
33732   * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be
33733   * invoked if you only change a property on the objects.
33734   */
33735  this.$render = noop;
33736
33737  /**
33738   * @ngdoc method
33739   * @name ngModel.NgModelController#$isEmpty
33740   *
33741   * @description
33742   * This is called when we need to determine if the value of an input is empty.
33743   *
33744   * For instance, the required directive does this to work out if the input has data or not.
33745   *
33746   * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.
33747   *
33748   * You can override this for input directives whose concept of being empty is different from the
33749   * default. The `checkboxInputType` directive does this because in its case a value of `false`
33750   * implies empty.
33751   *
33752   * @param {*} value The value of the input to check for emptiness.
33753   * @returns {boolean} True if `value` is "empty".
33754   */
33755  this.$isEmpty = function(value) {
33756    // eslint-disable-next-line no-self-compare
33757    return isUndefined(value) || value === '' || value === null || value !== value;
33758  };
33759
33760  this.$$updateEmptyClasses = function(value) {
33761    if (ctrl.$isEmpty(value)) {
33762      $animate.removeClass($element, NOT_EMPTY_CLASS);
33763      $animate.addClass($element, EMPTY_CLASS);
33764    } else {
33765      $animate.removeClass($element, EMPTY_CLASS);
33766      $animate.addClass($element, NOT_EMPTY_CLASS);
33767    }
33768  };
33769
33770
33771  var currentValidationRunId = 0;
33772
33773  /**
33774   * @ngdoc method
33775   * @name ngModel.NgModelController#$setValidity
33776   *
33777   * @description
33778   * Change the validity state, and notify the form.
33779   *
33780   * This method can be called within $parsers/$formatters or a custom validation implementation.
33781   * However, in most cases it should be sufficient to use the `ngModel.$validators` and
33782   * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically.
33783   *
33784   * @param {string}
33784 validationErrorKey Name of the validator. The `validationErrorKey` will be assigned
33785   *        to either `$error[validationErrorKey]` or `$pending[validationErrorKey]`
33786   *        (for unfulfilled `$asyncValidators`), so that it is available for data-binding.
33787   *        The `validationErrorKey` should be in camelCase and will get converted into dash-case
33788   *        for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
33789   *        class and can be bound to as  `{{someForm.someControl.$error.myError}}` .
33790   * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined),
33791   *                          or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
33792   *                          Skipped is used by Angular when validators do not run because of parse errors and
33793   *                          when `$asyncValidators` do not run because any of the `$validators` failed.
33794   */
33795  addSetValidityMethod({
33796    ctrl: this,
33797    $element: $element,
33798    set: function(object, property) {
33799      object[property] = true;
33800    },
33801    unset: function(object, property) {
33802      delete object[property];
33803    },
33804    $animate: $animate
33805  });
33806
33807  /**
33808   * @ngdoc method
33809   * @name ngModel.NgModelController#$setPristine
33810   *
33811   * @description
33812   * Sets the control to its pristine state.
33813   *
33814   * This method can be called to remove the `ng-dirty` class and set the control to its pristine
33815   * state (`ng-pristine` class). A model is considered to be pristine when the control
33816   * has not been changed from when first compiled.
33817   */
33818  this.$setPristine = function() {
33819    ctrl.$dirty = false;
33820    ctrl.$pristine = true;
33821    $animate.removeClass($element, DIRTY_CLASS);
33822    $animate.addClass($element, PRISTINE_CLASS);
33823  };
33824
33825  /**
33826   * @ngdoc method
33827   * @name ngModel.NgModelController#$setDirty
33828   *
33829   * @description
33830   * Sets the control to its dirty state.
33831   *
33832   * This method can be called to remove the `ng-pristine` class and set the control to its dirty
33833   * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed
33834   * from when first compiled.
33835   */
33836  this.$setDirty = function() {
33837    ctrl.$dirty = true;
33838    ctrl.$pristine = false;
33839    $animate.removeClass($element, PRISTINE_CLASS);
33840    $animate.addClass($element, DIRTY_CLASS);
33841    ctrl.$$parentForm.$setDirty();
33842  };
33843
33844  /**
33845   * @ngdoc method
33846   * @name ngModel.NgModelController#$setUntouched
33847   *
33848   * @description
33849   * Sets the control to its untouched state.
33850   *
33851   * This method can be called to remove the `ng-touched` class and set the control to its
33852   * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched
33853   * by default, however this function can be used to restore that state if the model has
33854   * already been touched by the user.
33855   */
33856  this.$setUntouched = function() {
33857    ctrl.$touched = false;
33858    ctrl.$untouched = true;
33859    $animate.setClass($element, UNTOUCHED_CLASS, TOUCHED_CLASS);
33860  };
33861
33862  /**
33863   * @ngdoc method
33864   * @name ngModel.NgModelController#$setTouched
33865   *
33866   * @description
33867   * Sets the control to its touched state.
33868   *
33869   * This method can be called to remove the `ng-untouched` class and set the control to its
33870   * touched state (`ng-touched` class). A model is considered to be touched when the user has
33871   * first focused the control element and then shifted focus away from the control (blur event).
33872   */
33873  this.$setTouched = function() {
33874    ctrl.$touched = true;
33875    ctrl.$untouched = false;
33876    $animate.setClass($element, TOUCHED_CLASS, UNTOUCHED_CLASS);
33877  };
33878
33879  /**
33880   * @ngdoc method
33881   * @name ngModel.NgModelController#$rollbackViewValue
33882   *
33883   * @description
33884   * Cancel an update and reset the input element's value to prevent an update to the `$modelValue`,
33885   * which may be caused by a pending debounced event or because the input is waiting for some
33886   * future event.
33887   *
33888   * If you have an input that uses `ng-model-options` to set up debounced updates or updates that
33889   * depend on special events such as `blur`, there can be a period when the `$viewValue` is out of
33890   * sync with the ngModel's `$modelValue`.
33891   *
33892   * In this case, you can use `$rollbackViewValue()` to manually cancel the debounced / future update
33893   * and reset the input to the last committed view value.
33894   *
33895   * It is also possible that you run into difficulties if you try to update the ngModel's `$modelValue`
33896   * programmatically before these debounced/future events have resolved/occurred, because Angular's
33897   * dirty checking mechanism is not able to tell whether the model has actually changed or not.
33898   *
33899   * The `$rollbackViewValue()` method should be called before programmatically changing the model of an
33900   * input which may have such events pending. This is important in order to make sure that the
33901   * input field will be updated with the new model value and any pending operations are cancelled.
33902   *
33903   * <example name="ng-model-cancel-update" module="cancel-update-example">
33904   *   <file name="app.js">
33905   *     angular.module('cancel-update-example', [])
33906   *
33907   *     .controller('CancelUpdateController', ['$scope', function($scope) {
33908   *       $scope.model = {value1: '', value2: ''};
33909   *
33910   *       $scope.setEmpty = function(e, value, rollback) {
33911   *         if (e.keyCode === 27) {
33912   *           e.preventDefault();
33913   *           if (rollback) {
33914   *             $scope.myForm[value].$rollbackViewValue();
33915   *           }
33916   *           $scope.model[value] = '';
33917   *         }
33918   *       };
33919   *     }]);
33920   *   </file>
33921   *   <file name="index.html">
33922   *     <div ng-controller="CancelUpdateController">
33923   *       <p>Both of these inputs are only updated if they are blurred. Hitting escape should
33924   *       empty them. Follow these steps and observe the difference:</p>
33925   *       <ol>
33926   *         <li>Type something in the input. You will see that the model is not yet updated</li>
33927   *         <li>Press the Escape key.
33928   *           <ol>
33929   *             <li> In the first example, nothing happens, because the model is already '', and no
33930   *             update is detected. If you blur the input, the model will be set to the current view.
33931   *             </li>
33932   *             <li> In the second example, the pending update is cancelled, and the input is set back
33933   *             to the last committed view value (''). Blurring the input does nothing.
33934   *             </li>
33935   *           </ol>
33936   *         </li>
33937   *       </ol>
33938   *
33939   *       <form name="myForm" ng-model-options="{ updateOn: 'blur' }">
33940   *         <div>
33941   *           <p id="inputDescription1">Without $rollbackViewValue():</p>
33942   *           <input name="value1" aria-describedby="inputDescription1" ng-model="model.value1"
33943   *                  ng-keydown="setEmpty($event, 'value1')">
33944   *           value1: "{{ model.value1 }}"
33945   *         </div>
33946   *
33947   *         <div>
33948   *           <p id="inputDescription2">With $rollbackViewValue():</p>
33949   *           <input name="value2" aria-describedby="inputDescription2" ng-model="model.value2"
33950   *                  ng-keydown="setEmpty($event, 'value2', true)">
33951   *           value2: "{{ model.value2 }}"
33952   *         </div>
33953   *       </form>
33954   *     </div>
33955   *   </file>
33956       <file name="style.css">
33957          div {
33958            display: table-cell;
33959          }
33960          div:nth-child(1) {
33961            padding-right: 30px;
33962          }
33963
33964        </file>
33965   * </example>
33966   */
33967  this.$rollbackViewValue = function() {
33968    $timeout.cancel(pendingDebounce);
33969    ctrl.$viewValue = ctrl.$$lastCommittedViewValue;
33970    ctrl.$render();
33971  };
33972
33973  /**
33974   * @ngdoc method
33975   * @name ngModel.NgModelController#$validate
33976   *
33977   * @description
33978   * Runs each of the registered validators (first synchronous validators and then
33979   * asynchronous validators).
33980   * If the validity changes to invalid, the model will be set to `undefined`,
33981   * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`.
33982   * If the validity changes to valid, it will set the model to the last available valid
33983   * `$modelValue`, i.e. either the last parsed value or the last value set from the scope.
33984   */
33985  this.$validate = function() {
33986    // ignore $validate before model is initialized
33987    if (isNumberNaN(ctrl.$modelValue)) {
33988      return;
33989    }
33990
33991    var viewValue = ctrl.$$lastCommittedViewValue;
33992    // Note: we use the $$rawModelValue as $modelValue might have been
33993    // set to undefined during a view -> model update that found validation
33994    // errors. We can't parse the view here, since that could change
33995    // the model although neither viewValue nor the model on the scope changed
33996    var modelValue = ctrl.$$rawModelValue;
33997
33998    var prevValid = ctrl.$valid;
33999    var prevModelValue = ctrl.$modelValue;
34000
34001    var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
34002
34003    ctrl.$$runValidators(modelValue, viewValue, function(allValid) {
34004      // If there was no change in validity, don't update the model
34005      // This prevents changing an invalid modelValue to undefined
34006      if (!allowInvalid && prevValid !== allValid) {
34007        // Note: Don't check ctrl.$valid here, as we could have
34008        // external validators (e.g. calculated on the server),
34009        // that just call $setValidity and need the model value
34010        // to calculate their validity.
34011        ctrl.$modelValue = allValid ? modelValue : undefined;
34012
34013        if (ctrl.$modelValue !== prevModelValue) {
34014          ctrl.$$writeModelToScope();
34015        }
34016      }
34017    });
34018
34019  };
34020
34021  this.$$runValidators = function(modelValue, viewValue, doneCallback) {
34022    currentValidationRunId++;
34023    var localValidationRunId = currentValidationRunId;
34024
34025    // check parser error
34026    if (!processParseErrors()) {
34027      validationDone(false);
34028      return;
34029    }
34030    if (!processSyncValidators()) {
34031      validationDone(false);
34032      return;
34033    }
34034    processAsyncValidators();
34035
34036    function processParseErrors() {
34037      var errorKey = ctrl.$$parserName || 'parse';
34038      if (isUndefined(parserValid)) {
34039        setValidity(errorKey, null);
34040      } else {
34041        if (!parserValid) {
34042          forEach(ctrl.$validators, function(v, name) {
34043            setValidity(name, null);
34044          });
34045          forEach(ctrl.$asyncValidators, function(v, name) {
34046            setValidity(name, null);
34047          });
34048        }
34049        // Set the parse error last, to prevent unsetting it, should a $validators key == parserName
34050        setValidity(errorKey, parserValid);
34051        return parserValid;
34052      }
34053      return true;
34054    }
34055
34056    function processSyncValidators() {
34057      var syncValidatorsValid = true;
34058      forEach(ctrl.$validators, function(validator, name) {
34059        var result = validator(modelValue, viewValue);
34060        syncValidatorsValid = syncValidatorsValid && result;
34061        setValidity(name, result);
34062      });
34063      if (!syncValidatorsValid) {
34064        forEach(ctrl.$asyncValidators, function(v, name) {
34065          setValidity(name, null);
34066        });
34067        return false;
34068      }
34069      return true;
34070    }
34071
34072    function processAsyncValidators() {
34073      var validatorPromises = [];
34074      var allValid = true;
34075      forEach(ctrl.$asyncValidators, function(validator, name) {
34076        var promise = validator(modelValue, viewValue);
34077        if (!isPromiseLike(promise)) {
34078          throw ngModelMinErr('nopromise',
34079            'Expected asynchronous validator to return a promise but got \'{0}\' instead.', promise);
34080        }
34081        setValidity(name, undefined);
34082        validatorPromises.push(promise.then(function() {
34083          setValidity(name, true);
34084        }, function() {
34085          allValid = false;
34086          setValidity(name, false);
34087        }));
34088      });
34089      if (!validatorPromises.length) {
34090        validationDone(true);
34091      } else {
34092        $q.all(validatorPromises).then(function() {
34093          validationDone(allValid);
34094        }, noop);
34095      }
34096    }
34097
34098    function setValidity(name, isValid) {
34099      if (localValidationRunId === currentValidationRunId) {
34100        ctrl.$setValidity(name, isValid);
34101      }
34102    }
34103
34104    function validationDone(allValid) {
34105      if (localValidationRunId === currentValidationRunId) {
34106
34107        doneCallback(allValid);
34108      }
34109    }
34110  };
34111
34112  /**
34113   * @ngdoc method
34114   * @name ngModel.NgModelController#$commitViewValue
34115   *
34116   * @description
34117   * Commit a pending update to the `$modelValue`.
34118   *
34119   * Updates may be pending by a debounced event or because the input is waiting for a some future
34120   * event defined in `ng-model-options`. this method is rarely needed as `NgModelController`
34121   * usually handles calling this in response to input events.
34122   */
34123  this.$commitViewValue = function() {
34124    var viewValue = ctrl.$viewValue;
34125
34126    $timeout.cancel(pendingDebounce);
34127
34128    // If the view value has not changed then we should just exit, except in the case where there is
34129    // a native validator on the element. In this case the validation state may have changed even though
34130    // the viewValue has stayed empty.
34131    if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) {
34132      return;
34133    }
34134    ctrl.$$updateEmptyClasses(viewValue);
34135    ctrl.$$lastCommittedViewValue = viewValue;
34136
34137    // change to dirty
34138    if (ctrl.$pristine) {
34139      this.$setDirty();
34140    }
34141    this.$$parseAndValidate();
34142  };
34143
34144  this.$$parseAndValidate = function() {
34145    var viewValue = ctrl.$$lastCommittedViewValue;
34146    var modelValue = viewValue;
34147    parserValid = isUndefined(modelValue) ? undefined : true;
34148
34149    if (parserValid) {
34150      for (var i = 0; i < ctrl.$parsers.length; i++) {
34151        modelValue = ctrl.$parsers[i](modelValue);
34152        if (isUndefined(modelValue)) {
34153          parserValid = false;
34154          break;
34155        }
34156      }
34157    }
34158    if (isNumberNaN(ctrl.$modelValue)) {
34159      // ctrl.$modelValue has not been touched yet...
34160      ctrl.$modelValue = ngModelGet($scope);
34161    }
34162    var prevModelValue = ctrl.$modelValue;
34163    var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
34164    ctrl.$$rawModelValue = modelValue;
34165
34166    if (allowInvalid) {
34167      ctrl.$modelValue = modelValue;
34168      writeToModelIfNeeded();
34169    }
34170
34171    // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date.
34172    // This can happen if e.g. $setViewValue is called from inside a parser
34173    ctrl.$$runValidators(modelValue, ctrl.$$lastCommittedViewValue, function(allValid) {
34174      if (!allowInvalid) {
34175        // Note: Don't check ctrl.$valid here, as we could have
34176        // external validators (e.g. calculated on the server),
34177        // that just call $setValidity and need the model value
34178        // to calculate their validity.
34179        ctrl.$modelValue = allValid ? modelValue : undefined;
34180        writeToModelIfNeeded();
34181      }
34182    });
34183
34184    function writeToModelIfNeeded() {
34185      if (ctrl.$modelValue !== prevModelValue) {
34186        ctrl.$$writeModelToScope();
34187      }
34188    }
34189  };
34190
34191  this.$$writeModelToScope = function() {
34192    ngModelSet($scope, ctrl.$modelValue);
34193    forEach(ctrl.$viewChangeListeners, function(listener) {
34194      try {
34195        listener();
34196      } catch (e) {
34197        $exceptionHandler(e);
34198      }
34199    });
34200  };
34201
34202  /**
34203   * @ngdoc method
34204   * @name ngModel.NgModelController#$setViewValue
34205   *
34206   * @description
34207   * Update the view value.
34208   *
34209   * This method should be called when a control wants to change the view value; typically,
34210   * this is done from within a DOM event handler. For example, the {@link ng.directive:input input}
34211   * directive calls it when the value of the input changes and {@link ng.directive:select select}
34212   * calls it when an option is selected.
34213   *
34214   * When `$setViewValue` is called, the new `value` will be staged for committing through the `$parsers`
34215   * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
34216   * value sent directly for processing, finally to be applied to `$modelValue` and then the
34217   * **expression** specified in the `ng-model` attribute. Lastly, all the registered change listeners,
34218   * in the `$viewChangeListeners` list, are called.
34219   *
34220   * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
34221   * and the `default` trigger is not listed, all those actions will remain pending until one of the
34222   * `updateOn` events is triggered on the DOM element.
34223   * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions}
34224   * directive is used with a custom debounce for this particular event.
34225   * Note that a `$digest` is only triggered once the `updateOn` events are fired, or if `debounce`
34226   * is specified, once the timer runs out.
34227   *
34228   * When used with standard inputs, the view value will always be a string (which is in some cases
34229   * parsed into another type, such as a `Date` object for `input[date]`.)
34230   * However, custom controls might also pass objects to this method. In this case, we should make
34231   * a copy of the object before passing it to `$setViewValue`. This is because `ngModel` does not
34232   * perform a deep watch of objects, it only looks for a change of identity. If you only change
34233   * the property of the object then ngModel will not realize that the object has changed and
34234   * will not invoke the `$parsers` and `$validators` pipelines. For this reason, you should
34235   * not change properties of the copy once it has been passed to `$setViewValue`.
34236   * Otherwise you may cause the model value on the scope to change incorrectly.
34237   *
34238   * <div class="alert alert-info">
34239   * In any case, the value passed to the method should always reflect the current value
34240   * of the control. For example, if you are calling `$setViewValue` for an input element,
34241   * you should pass the input DOM value. Otherwise, the control and the scope model become
34242   * out of sync. It's also important to note that `$setViewValue` does not call `$render` or change
34243   * the control's DOM value in any way. If we want to change the control's DOM value
34244   * programmatically, we should update the `ngModel` scope expression. Its new value will be
34245   * picked up by the model controller, which will run it through the `$formatters`, `$render` it
34246   * to update the DOM, and finally call `$validate` on it.
34247   * </div>
34248   *
34249   * @param {*} value value from the view.
34250   * @param {string} trigger Event that triggered the update.
34251   */
34252  this.$setViewValue = function(value, trigger) {
34253    ctrl.$viewValue = value;
34254    if (!ctrl.$options || ctrl.$options.updateOnDefault) {
34255      ctrl.$$debounceViewValueCommit(trigger);
34256    }
34257  };
34258
34259  this.$$debounceViewValueCommit = function(trigger) {
34260    var debounceDelay = 0,
34261        options = ctrl.$options,
34262        debounce;
34263
34264    if (options && isDefined(options.debounce)) {
34265      debounce = options.debounce;
34266      if (isNumber(debounce)) {
34267        debounceDelay = debounce;
34268      } else if (isNumber(debounce[trigger])) {
34269        debounceDelay = debounce[trigger];
34270      } else if (isNumber(debounce['default'])) {
34271        debounceDelay = debounce['default'];
34272      }
34273    }
34274
34275    $timeout.cancel(pendingDebounce);
34276    if (debounceDelay) {
34277      pendingDebounce = $timeout(function() {
34278        ctrl.$commitViewValue();
34279      }, debounceDelay);
34280    } else if ($rootScope.$$phase) {
34281      ctrl.$commitViewValue();
34282    } else {
34283      $scope.$apply(function() {
34284        ctrl.$commitViewValue();
34285      });
34286    }
34287  };
34288
34289  // model -> value
34290  // Note: we cannot use a normal scope.$watch as we want to detect the following:
34291  // 1. scope value is 'a'
34292  // 2. user enters 'b'
34293  // 3. ng-change kicks in and reverts scope value to 'a'
34294  //    ->
34294 scope value did not change since the last digest as
34295  //       ng-change executes in apply phase
34296  // 4. view should be changed back to 'a'
34297  $scope.$watch(function ngModelWatch() {
34298    var modelValue = ngModelGet($scope);
34299
34300    // if scope model value and ngModel value are out of sync
34301    // TODO(perf): why not move this to the action fn?
34302    if (modelValue !== ctrl.$modelValue &&
34303       // checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator
34304        // eslint-disable-next-line no-self-compare
34305       (ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue)
34306    ) {
34307      ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
34308      parserValid = undefined;
34309
34310      var formatters = ctrl.$formatters,
34311          idx = formatters.length;
34312
34313      var viewValue = modelValue;
34314      while (idx--) {
34315        viewValue = formatters[idx](viewValue);
34316      }
34317      if (ctrl.$viewValue !== viewValue) {
34318        ctrl.$$updateEmptyClasses(viewValue);
34319        ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
34320        ctrl.$render();
34321
34322        // It is possible that model and view value have been updated during render
34323        ctrl.$$runValidators(ctrl.$modelValue, ctrl.$viewValue, noop);
34324      }
34325    }
34326
34327    return modelValue;
34328  });
34329}];
34330
34331
34332/**
34333 * @ngdoc directive
34334 * @name ngModel
34335 *
34336 * @element input
34337 * @priority 1
34338 *
34339 * @description
34340 * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a
34341 * property on the scope using {@link ngModel.NgModelController NgModelController},
34342 * which is created and exposed by this directive.
34343 *
34344 * `ngModel` is responsible for:
34345 *
34346 * - Binding the view into the model, which other directives such as `input`, `textarea` or `select`
34347 *   require.
34348 * - Providing validation behavior (i.e. required, number, email, url).
34349 * - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).
34350 * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`,
34351 *   `ng-untouched`, `ng-empty`, `ng-not-empty`) including animations.
34352 * - Registering the control with its parent {@link ng.directive:form form}.
34353 *
34354 * Note: `ngModel` will try to bind to the property given by evaluating the expression on the
34355 * current scope. If the property doesn't already exist on this scope, it will be created
34356 * implicitly and added to the scope.
34357 *
34358 * For best practices on using `ngModel`, see:
34359 *
34360 *  - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes)
34361 *
34362 * For basic examples, how to use `ngModel`, see:
34363 *
34364 *  - {@link ng.directive:input input}
34365 *    - {@link input[text] text}
34366 *    - {@link input[checkbox] checkbox}
34367 *    - {@link input[radio] radio}
34368 *    - {@link input[number] number}
34369 *    - {@link input[email] email}
34370 *    - {@link input[url] url}
34371 *    - {@link input[date] date}
34372 *    - {@link input[datetime-local] datetime-local}
34373 *    - {@link input[time] time}
34374 *    - {@link input[month] month}
34375 *    - {@link input[week] week}
34376 *  - {@link ng.directive:select select}
34377 *  - {@link ng.directive:textarea textarea}
34378 *
34379 * # Complex Models (objects or collections)
34380 *
34381 * By default, `ngModel` watches the model by reference, not value. This is important to know when
34382 * binding inputs to models that are objects (e.g. `Date`) or collections (e.g. arrays). If only properties of the
34383 * object or collection change, `ngModel` will not be notified and so the input will not be  re-rendered.
34384 *
34385 * The model must be assigned an entirely new object or collection before a re-rendering will occur.
34386 *
34387 * Some directives have options that will cause them to use a custom `$watchCollection` on the model expression
34388 * - for example, `ngOptions` will do so when a `track by` clause is included in the comprehension expression or
34389 * if the select is given the `multiple` attribute.
34390 *
34391 * The `$watchCollection()` method only does a shallow comparison, meaning that changing properties deeper than the
34392 * first level of the object (or only changing the properties of an item in the collection if it's an array) will still
34393 * not trigger a re-rendering of the model.
34394 *
34395 * # CSS classes
34396 * The following CSS classes are added and removed on the associated input/select/textarea element
34397 * depending on the validity of the model.
34398 *
34399 *  - `ng-valid`: the model is valid
34400 *  - `ng-invalid`: the model is invalid
34401 *  - `ng-valid-[key]`: for each valid key added by `$setValidity`
34402 *  - `ng-invalid-[key]`: for each invalid key added by `$setValidity`
34403 *  - `ng-pristine`: the control hasn't been interacted with yet
34404 *  - `ng-dirty`: the control has been interacted with
34405 *  - `ng-touched`: the control has been blurred
34406 *  - `ng-untouched`: the control hasn't been blurred
34407 *  - `ng-pending`: any `$asyncValidators` are unfulfilled
34408 *  - `ng-empty`: the view does not contain a value or the value is deemed "empty", as defined
34409 *     by the {@link ngModel.NgModelController#$isEmpty} method
34410 *  - `ng-not-empty`: the view contains a non-empty value
34411 *
34412 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
34413 *
34414 * ## Animation Hooks
34415 *
34416 * Animations within models are triggered when any of the associated CSS classes are added and removed
34417 * on the input element which is attached to the model. These classes include: `.ng-pristine`, `.ng-dirty`,
34418 * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
34419 * The animations that are triggered within ngModel are similar to how they work in ngClass and
34420 * animations can be hooked into using CSS transitions, keyframes as well as JS animations.
34421 *
34422 * The following example shows a simple way to utilize CSS transitions to style an input element
34423 * that has been rendered as invalid after it has been validated:
34424 *
34425 * <pre>
34426 * //be sure to include ngAnimate as a module to hook into more
34427 * //advanced animations
34428 * .my-input {
34429 *   transition:0.5s linear all;
34430 *   background: white;
34431 * }
34432 * .my-input.ng-invalid {
34433 *   background: red;
34434 *   color:white;
34435 * }
34436 * </pre>
34437 *
34438 * @example
34439 * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample" name="ng-model">
34440     <file name="index.html">
34441       <script>
34442        angular.module('inputExample', [])
34443          .controller('ExampleController', ['$scope', function($scope) {
34444            $scope.val = '1';
34445          }]);
34446       </script>
34447       <style>
34448         .my-input {
34449           transition:all linear 0.5s;
34450           background: transparent;
34451         }
34452         .my-input.ng-invalid {
34453           color:white;
34454           background: red;
34455         }
34456       </style>
34457       <p id="inputDescription">
34458        Update input to see transitions when valid/invalid.
34459        Integer is a valid value.
34460       </p>
34461       <form name="testForm" ng-controller="ExampleController">
34462         <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input"
34463                aria-describedby="inputDescription" />
34464       </form>
34465     </file>
34466 * </example>
34467 *
34468 * ## Binding to a getter/setter
34469 *
34470 * Sometimes it's helpful to bind `ngModel` to a getter/setter function.  A getter/setter is a
34471 * function that returns a representation of the model when called with zero arguments, and sets
34472 * the internal state of a model when called with an argument. It's sometimes useful to use this
34473 * for models that have an internal representation that's different from what the model exposes
34474 * to the view.
34475 *
34476 * <div class="alert alert-success">
34477 * **Best Practice:** It's best to keep getters fast because Angular is likely to call them more
34478 * frequently than other parts of your code.
34479 * </div>
34480 *
34481 * You use this behavior by adding `ng-model-options="{ getterSetter: true }"` to an element that
34482 * has `ng-model` attached to it. You can also add `ng-model-options="{ getterSetter: true }"` to
34483 * a `<form>`, which will enable this behavior for all `<input>`s within it. See
34484 * {@link ng.directive:ngModelOptions `ngModelOptions`} for more.
34485 *
34486 * The following example shows how to use `ngModel` with a getter/setter:
34487 *
34488 * @example
34489 * <example name="ngModel-getter-setter" module="getterSetterExample">
34490     <file name="index.html">
34491       <div ng-controller="ExampleController">
34492         <form name="userForm">
34493           <label>Name:
34494             <input type="text" name="userName"
34495                    ng-model="user.name"
34496                    ng-model-options="{ getterSetter: true }" />
34497           </label>
34498         </form>
34499         <pre>user.name = <span ng-bind="user.name()"></span></pre>
34500       </div>
34501     </file>
34502     <file name="app.js">
34503       angular.module('getterSetterExample', [])
34504         .controller('ExampleController', ['$scope', function($scope) {
34505           var _name = 'Brian';
34506           $scope.user = {
34507             name: function(newName) {
34508              // Note that newName can be undefined for two reasons:
34509              // 1. Because it is called as a getter and thus called with no arguments
34510              // 2. Because the property should actually be set to undefined. This happens e.g. if the
34511              //    input is invalid
34512              return arguments.length ? (_name = newName) : _name;
34513             }
34514           };
34515         }]);
34516     </file>
34517 * </example>
34518 */
34519var ngModelDirective = ['$rootScope', function($rootScope) {
34520  return {
34521    restrict: 'A',
34522    require: ['ngModel', '^?form', '^?ngModelOptions'],
34523    controller: NgModelController,
34524    // Prelink needs to run before any input directive
34525    // so that we can set the NgModelOptions in NgModelController
34526    // before anyone else uses it.
34527    priority: 1,
34528    compile: function ngModelCompile(element) {
34529      // Setup initial state of the control
34530      element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS);
34531
34532      return {
34533        pre: function ngModelPreLink(scope, element, attr, ctrls) {
34534          var modelCtrl = ctrls[0],
34535              formCtrl = ctrls[1] || modelCtrl.$$parentForm;
34536
34537          modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);
34538
34539          // notify others, especially parent forms
34540          formCtrl.$addControl(modelCtrl);
34541
34542          attr.$observe('name', function(newValue) {
34543            if (modelCtrl.$name !== newValue) {
34544              modelCtrl.$$parentForm.$$renameControl(modelCtrl, newValue);
34545            }
34546          });
34547
34548          scope.$on('$destroy', function() {
34549            modelCtrl.$$parentForm.$removeControl(modelCtrl);
34550          });
34551        },
34552        post: function ngModelPostLink(scope, element, attr, ctrls) {
34553          var modelCtrl = ctrls[0];
34554          if (modelCtrl.$options && modelCtrl.$options.updateOn) {
34555            element.on(modelCtrl.$options.updateOn, function(ev) {
34556              modelCtrl.$$debounceViewValueCommit(ev && ev.type);
34557            });
34558          }
34559
34560          element.on('blur', function() {
34561            if (modelCtrl.$touched) return;
34562
34563            if ($rootScope.$$phase) {
34564              scope.$evalAsync(modelCtrl.$setTouched);
34565            } else {
34566              scope.$apply(modelCtrl.$setTouched);
34567            }
34568          });
34569        }
34570      };
34571    }
34572  };
34573}];
34574
34575
34576
34577var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
34578
34579/**
34580 * @ngdoc directive
34581 * @name ngModelOptions
34582 *
34583 * @description
34584 * Allows tuning how model updates are done. Using `ngModelOptions` you can specify a custom list of
34585 * events that will trigger a model update and/or a debouncing delay so that the actual update only
34586 * takes place when a timer expires; this timer will be reset after another change takes place.
34587 *
34588 * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might
34589 * be different from the value in the actual model. This means that if you update the model you
34590 * should also invoke {@link ngModel.NgModelController `$rollbackViewValue`} on the relevant input field in
34591 * order to make sure it is synchronized with the model and that any debounced action is canceled.
34592 *
34593 * The easiest way to reference the control's {@link ngModel.NgModelController `$rollbackViewValue`}
34594 * method is by making sure the input is placed inside a form that has a `name` attribute. This is
34595 * important because `form` controllers are published to the related scope under the name in their
34596 * `name` attribute.
34597 *
34598 * Any pending changes will take place immediately when an enclosing form is submitted via the
34599 * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
34600 * to have access to the updated model.
34601 *
34602 * `ngModelOptions` has an effect on the element it's declared on and its desce
34602ndants.
34603 *
34604 * @param {Object} ngModelOptions options to apply to the current model. Valid keys are:
34605 *   - `updateOn`: string specifying which event should the input be bound to. You can set several
34606 *     events using an space delimited list. There is a special event called `default` that
34607 *     matches the default events belonging of the control.
34608 *   - `debounce`: integer value which contains the debounce model update value in milliseconds. A
34609 *     value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
34610 *     custom value for each event. For example:
34611 *     `ng-model-options="{ updateOn: 'default blur', debounce: { 'default': 500, 'blur': 0 } }"`
34612 *   - `allowInvalid`: boolean value which indicates that the model can be set with values that did
34613 *     not validate correctly instead of the default behavior of setting the model to undefined.
34614 *   - `getterSetter`: boolean value which determines whether or not to treat functions bound to
34615       `ngModel` as getters/setters.
34616 *   - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for
34617 *     `<input type="date">`, `<input type="time">`, ... . It understands UTC/GMT and the
34618 *     continental US time zone abbreviations, but for general use, use a time zone offset, for
34619 *     example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
34620 *     If not specified, the timezone of the browser will be used.
34621 *
34622 * @example
34623
34624  The following example shows how to override immediate updates. Changes on the inputs within the
34625  form will update the model only when the control loses focus (blur event). If `escape` key is
34626  pressed while the input field is focused, the value is reset to the value in the current model.
34627
34628  <example name="ngModelOptions-directive-blur" module="optionsExample">
34629    <file name="index.html">
34630      <div ng-controller="ExampleController">
34631        <form name="userForm">
34632          <label>Name:
34633            <input type="text" name="userName"
34634                   ng-model="user.name"
34635                   ng-model-options="{ updateOn: 'blur' }"
34636                   ng-keyup="cancel($event)" />
34637          </label><br />
34638          <label>Other data:
34639            <input type="text" ng-model="user.data" />
34640          </label><br />
34641        </form>
34642        <pre>user.name = <span ng-bind="user.name"></span></pre>
34643        <pre>user.data = <span ng-bind="user.data"></span></pre>
34644      </div>
34645    </file>
34646    <file name="app.js">
34647      angular.module('optionsExample', [])
34648        .controller('ExampleController', ['$scope', function($scope) {
34649          $scope.user = { name: 'John', data: '' };
34650
34651          $scope.cancel = function(e) {
34652            if (e.keyCode === 27) {
34653              $scope.userForm.userName.$rollbackViewValue();
34654            }
34655          };
34656        }]);
34657    </file>
34658    <file name="protractor.js" type="protractor">
34659      var model = element(by.binding('user.name'));
34660      var input = element(by.model('user.name'));
34661      var other = element(by.model('user.data'));
34662
34663      it('should allow custom events', function() {
34664        input.sendKeys(' Doe');
34665        input.click();
34666        expect(model.getText()).toEqual('John');
34667        other.click();
34668        expect(model.getText()).toEqual('John Doe');
34669      });
34670
34671      it('should $rollbackViewValue when model changes', function() {
34672        input.sendKeys(' Doe');
34673        expect(input.getAttribute('value')).toEqual('John Doe');
34674        input.sendKeys(protractor.Key.ESCAPE);
34675        expect(input.getAttribute('value')).toEqual('John');
34676        other.click();
34677        expect(model.getText()).toEqual('John');
34678      });
34679    </file>
34680  </example>
34681
34682  This one shows how to debounce model changes. Model will be updated only 1 sec after last change.
34683  If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty.
34684
34685  <example name="ngModelOptions-directive-debounce" module="optionsExample">
34686    <file name="index.html">
34687      <div ng-controller="ExampleController">
34688        <form name="userForm">
34689          <label>Name:
34690            <input type="text" name="userName"
34691                   ng-model="user.name"
34692                   ng-model-options="{ debounce: 1000 }" />
34693          </label>
34694          <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button>
34695          <br />
34696        </form>
34697        <pre>user.name = <span ng-bind="user.name"></span></pre>
34698      </div>
34699    </file>
34700    <file name="app.js">
34701      angular.module('optionsExample', [])
34702        .controller('ExampleController', ['$scope', function($scope) {
34703          $scope.user = { name: 'Igor' };
34704        }]);
34705    </file>
34706  </example>
34707
34708  This one shows how to bind to getter/setters:
34709
34710  <example name="ngModelOptions-directive-getter-setter" module="getterSetterExample">
34711    <file name="index.html">
34712      <div ng-controller="ExampleController">
34713        <form name="userForm">
34714          <label>Name:
34715            <input type="text" name="userName"
34716                   ng-model="user.name"
34717                   ng-model-options="{ getterSetter: true }" />
34718          </label>
34719        </form>
34720        <pre>user.name = <span ng-bind="user.name()"></span></pre>
34721      </div>
34722    </file>
34723    <file name="app.js">
34724      angular.module('getterSetterExample', [])
34725        .controller('ExampleController', ['$scope', function($scope) {
34726          var _name = 'Brian';
34727          $scope.user = {
34728            name: function(newName) {
34729              // Note that newName can be undefined for two reasons:
34730              // 1. Because it is called as a getter and thus called with no arguments
34731              // 2. Because the property should actually be set to undefined. This happens e.g. if the
34732              //    input is invalid
34733              return arguments.length ? (_name = newName) : _name;
34734            }
34735          };
34736        }]);
34737    </file>
34738  </example>
34739 */
34740var ngModelOptionsDirective = function() {
34741  return {
34742    restrict: 'A',
34743    controller: ['$scope', '$attrs', function NgModelOptionsController($scope, $attrs) {
34744      var that = this;
34745      this.$options = copy($scope.$eval($attrs.ngModelOptions));
34746      // Allow adding/overriding bound events
34747      if (isDefined(this.$options.updateOn)) {
34748        this.$options.updateOnDefault = false;
34749        // extract "default" pseudo-event from list of events that can trigger a model update
34750        this.$options.updateOn = trim(this.$options.updateOn.replace(DEFAULT_REGEXP, function() {
34751          that.$options.updateOnDefault = true;
34752          return ' ';
34753        }));
34754      } else {
34755        this.$options.updateOnDefault = true;
34756      }
34757    }]
34758  };
34759};
34760
34761
34762
34763// helper methods
34764function addSetValidityMethod(context) {
34765  var ctrl = context.ctrl,
34766      $element = context.$element,
34767      classCache = {},
34768      set = context.set,
34769      unset = context.unset,
34770      $animate = context.$animate;
34771
34772  classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS));
34773
34774  ctrl.$setValidity = setValidity;
34775
34776  function setValidity(validationErrorKey, state, controller) {
34777    if (isUndefined(state)) {
34778      createAndSet('$pending', validationErrorKey, controller);
34779    } else {
34780      unsetAndCleanup('$pending', validationErrorKey, controller);
34781    }
34782    if (!isBoolean(state)) {
34783      unset(ctrl.$error, validationErrorKey, controller);
34784      unset(ctrl.$$success, validationErrorKey, controller);
34785    } else {
34786      if (state) {
34787        unset(ctrl.$error, validationErrorKey, controller);
34788        set(ctrl.$$success, validationErrorKey, controller);
34789      } else {
34790        set(ctrl.$error, validationErrorKey, controller);
34791        unset(ctrl.$$success, validationErrorKey, controller);
34792      }
34793    }
34794    if (ctrl.$pending) {
34795      cachedToggleClass(PENDING_CLASS, true);
34796      ctrl.$valid = ctrl.$invalid = undefined;
34797      toggleValidationCss('', null);
34798    } else {
34799      cachedToggleClass(PENDING_CLASS, false);
34800      ctrl.$valid = isObjectEmpty(ctrl.$error);
34801      ctrl.$invalid = !ctrl.$valid;
34802      toggleValidationCss('', ctrl.$valid);
34803    }
34804
34805    // re-read the state as the set/unset methods could have
34806    // combined state in ctrl.$error[validationError] (used for forms),
34807    // where setting/unsetting only increments/decrements the value,
34808    // and does not replace it.
34809    var combinedState;
34810    if (ctrl.$pending && ctrl.$pending[validationErrorKey]) {
34811      combinedState = undefined;
34812    } else if (ctrl.$error[validationErrorKey]) {
34813      combinedState = false;
34814    } else if (ctrl.$$success[validationErrorKey]) {
34815      combinedState = true;
34816    } else {
34817      combinedState = null;
34818    }
34819
34820    toggleValidationCss(validationErrorKey, combinedState);
34821    ctrl.$$parentForm.$setValidity(validationErrorKey, combinedState, ctrl);
34822  }
34823
34824  function createAndSet(name, value, controller) {
34825    if (!ctrl[name]) {
34826      ctrl[name] = {};
34827    }
34828    set(ctrl[name], value, controller);
34829  }
34830
34831  function unsetAndCleanup(name, value, controller) {
34832    if (ctrl[name]) {
34833      unset(ctrl[name], value, controller);
34834    }
34835    if (isObjectEmpty(ctrl[name])) {
34836      ctrl[name] = undefined;
34837    }
34838  }
34839
34840  function cachedToggleClass(className, switchValue) {
34841    if (switchValue && !classCache[className]) {
34842      $animate.addClass($element, className);
34843      classCache[className] = true;
34844    } else if (!switchValue && classCache[className]) {
34845      $animate.removeClass($element, className);
34846      classCache[className] = false;
34847    }
34848  }
34849
34850  function toggleValidationCss(validationErrorKey, isValid) {
34851    validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
34852
34853    cachedToggleClass(VALID_CLASS + validationErrorKey, isValid === true);
34854    cachedToggleClass(INVALID_CLASS + validationErrorKey, isValid === false);
34855  }
34856}
34857
34858function isObjectEmpty(obj) {
34859  if (obj) {
34860    for (var prop in obj) {
34861      if (obj.hasOwnProperty(prop)) {
34862        return false;
34863      }
34864    }
34865  }
34866  return true;
34867}
34868
34869/**
34870 * @ngdoc directive
34871 * @name ngNonBindable
34872 * @restrict AC
34873 * @priority 1000
34874 *
34875 * @description
34876 * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current
34877 * DOM element. This is useful if the element contains what appears to be Angular directives and
34878 * bindings but which should be ignored by Angular. This could be the case if you have a site that
34879 * displays snippets of code, for instance.
34880 *
34881 * @element ANY
34882 *
34883 * @example
34884 * In this example there are two locations where a simple interpolation binding (`{{}}`) is present,
34885 * but the one wrapped in `ngNonBindable` is left alone.
34886 *
34887 * @example
34888    <example name="ng-non-bindable">
34889      <file name="index.html">
34890        <div>Normal: {{1 + 2}}</div>
34891        <div ng-non-bindable>Ignored: {{1 + 2}}</div>
34892      </file>
34893      <file name="protractor.js" type="protractor">
34894       it('should check ng-non-bindable', function() {
34895         expect(element(by.binding('1 + 2')).getText()).toContain('3');
34896         expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/);
34897       });
34898      </file>
34899    </example>
34900 */
34901var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
34902
34903/* exported ngOptionsDirective */
34904
34905/* global jqLiteRemove */
34906
34907var ngOptionsMinErr = minErr('ngOptions');
34908
34909/**
34910 * @ngdoc directive
34911 * @name ngOptions
34912 * @restrict A
34913 *
34914 * @description
34915 *
34916 * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
34917 * elements for the `<select>` element using the array or object obtained by evaluating the
34918 * `ngOptions` comprehension expression.
34919 *
34920 * In many cases, {@link ng.directive:ngRepeat ngRepeat} can be used on `<option>` elements
34921 * instead of `ngOptions` to achieve a similar result.
34922 * However, `ngOptions` provides some benefits such as reducing memory and
34923 * increasing speed by not creating a new scope for each repeated instance, as well as providing
34924 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
34925 * comprehension expression. `ngOptions` should be used when the `<select>` model needs to be bound
34926 *  to a non-string value. This is because an option element can only be bound to string values at
34927 * present.
34928 *
34929 * When an item in the `<select>` menu is selected, the array element or object property
34930 * represented by the selected option will be bound to the model identified by the `ngModel`
34931 * directive.
34932 *
34933 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
34934 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
34935 * option. See example below for demonstration.
34936 *
34937 * ## Complex Models (objects or collections)
34938 *
34939 * By default, `ngModel` watches the model by reference, not value. This is important to know when
34940 * binding the select to a model that is an object or a collection.
34941 *
34942 * One issue occurs if you want to preselect an option. For example, if you set
34943 * the model to an object that is equal to an object in your collection, `ngOptions` won't be able to set the selection,
34944 * because the objects are not identical. So by default, you should always reference the item in y
34944our collection
34945 * for preselections, e.g.: `$scope.selected = $scope.collection[3]`.
34946 *
34947 * Another solution is to use a `track by` clause, because then `ngOptions` will track the identity
34948 * of the item not by reference, but by the result of the `track by` expression. For example, if your
34949 * collection items have an id property, you would `track by item.id`.
34950 *
34951 * A different issue with objects or collections is that ngModel won't detect if an object property or
34952 * a collection item changes. For that reason, `ngOptions` additionally watches the model using
34953 * `$watchCollection`, when the expression contains a `track by` clause or the the select has the `multiple` attribute.
34954 * This allows ngOptions to trigger a re-rendering of the options even if the actual object/collection
34955 * has not changed identity, but only a property on the object or an item in the collection changes.
34956 *
34957 * Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection
34958 * if the model is an array). This means that changing a property deeper than the first level inside the
34959 * object/collection will not trigger a re-rendering.
34960 *
34961 * ## `select` **`as`**
34962 *
34963 * Using `select` **`as`** will bind the result of the `select` expression to the model, but
34964 * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
34965 * or property name (for object data sources) of the value within the collection. If a **`track by`** expression
34966 * is used, the result of that expression will be set as the value of the `option` and `select` elements.
34967 *
34968 *
34969 * ### `select` **`as`** and **`track by`**
34970 *
34971 * <div class="alert alert-warning">
34972 * Be careful when using `select` **`as`** and **`track by`** in the same expression.
34973 * </div>
34974 *
34975 * Given this array of items on the $scope:
34976 *
34977 * ```js
34978 * $scope.items = [{
34979 *   id: 1,
34980 *   label: 'aLabel',
34981 *   subItem: { name: 'aSubItem' }
34982 * }, {
34983 *   id: 2,
34984 *   label: 'bLabel',
34985 *   subItem: { name: 'bSubItem' }
34986 * }];
34987 * ```
34988 *
34989 * This will work:
34990 *
34991 * ```html
34992 * <select ng-options="item as item.label for item in items track by item.id" ng-model="selected"></select>
34993 * ```
34994 * ```js
34995 * $scope.selected = $scope.items[0];
34996 * ```
34997 *
34998 * but this will not work:
34999 *
35000 * ```html
35001 * <select ng-options="item.subItem as item.label for item in items track by item.id" ng-model="selected"></select>
35002 * ```
35003 * ```js
35004 * $scope.selected = $scope.items[0].subItem;
35005 * ```
35006 *
35007 * In both examples, the **`track by`** expression is applied successfully to each `item` in the
35008 * `items` array. Because the selected option has been set programmatically in the controller, the
35009 * **`track by`** expression is also applied to the `ngModel` value. In the first example, the
35010 * `ngModel` value is `items[0]` and the **`track by`** expression evaluates to `items[0].id` with
35011 * no issue. In the second example, the `ngModel` value is `items[0].subItem` and the **`track by`**
35012 * expression evaluates to `items[0].subItem.id` (which is undefined). As a result, the model value
35013 * is not matched against any `<option>` and the `<select>` appears as having no selected value.
35014 *
35015 *
35016 * @param {string} ngModel Assignable angular expression to data-bind to.
35017 * @param {string=} name Property name of the form under which the control is published.
35018 * @param {string=} required The control is considered valid only if value is entered.
35019 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
35020 *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
35021 *    `required` when you want to data-bind to the `required` attribute.
35022 * @param {comprehension_expression=} ngOptions in one of the following forms:
35023 *
35024 *   * for array data sources:
35025 *     * `label` **`for`** `value` **`in`** `array`
35026 *     * `select` **`as`** `label` **`for`** `value` **`in`** `array`
35027 *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
35028 *     * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array`
35029 *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
35030 *     * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
35031 *     * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr`
35032 *        (for including a filter with `track by`)
35033 *   * for object data sources:
35034 *     * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
35035 *     * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
35036 *     * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
35037 *     * `label` **`disable when`** `disable` **`for (`**`key`**`,`** `value`**`) in`** `object`
35038 *     * `select` **`as`** `label` **`group by`** `group`
35039 *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
35040 *     * `select` **`as`** `label` **`disable when`** `disable`
35041 *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
35042 *
35043 * Where:
35044 *
35045 *   * `array` / `object`: an expression which evaluates to an array / object to iterate over.
35046 *   * `value`: local variable which will refer to each item in the `array` or each property value
35047 *      of `object` during iteration.
35048 *   * `key`: local variable which will refer to a property name in `object` during iteration.
35049 *   * `label`: The result of this expression will be the label for `<option>` element. The
35050 *     `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
35051 *   * `select`: The result of this expression will be bound to the model of the parent `<select>`
35052 *      element. If not specified, `select` expression will default to `value`.
35053 *   * `group`: The result of this expression will be used to group options using the `<optgroup>`
35054 *      DOM element.
35055 *   * `disable`: The result of this expression will be used to disable the rendered `<option>`
35056 *      element. Return `true` to disable.
35057 *   * `trackexpr`: Used when working with an array of objects. The result of this expression will be
35058 *      used to identify the objects in the array. The `trackexpr` will most likely refer to the
35059 *     `value` variable (e.g. `value.propertyName`). With this the selection is preserved
35060 *      even when the options are recreated (e.g. reloaded from the server)
vendor: 10,367 bytes, lines 35060-35310
35060.
35061 *
35062 * @example
35063    <example module="selectExample" name="select">
35064      <file name="index.html">
35065        <script>
35066        angular.module('selectExample', [])
35067          .controller('ExampleController', ['$scope', function($scope) {
35068            $scope.colors = [
35069              {name:'black', shade:'dark'},
35070              {name:'white', shade:'light', notAnOption: true},
35071              {name:'red', shade:'dark'},
35072              {name:'blue', shade:'dark', notAnOption: true},
35073              {name:'yellow', shade:'light', notAnOption: false}
35074            ];
35075            $scope.myColor = $scope.colors[2]; // red
35076          }]);
35077        </script>
35078        <div ng-controller="ExampleController">
35079          <ul>
35080            <li ng-repeat="color in colors">
35081              <label>Name: <input ng-model="color.name"></label>
35082              <label><input type="checkbox" ng-model="color.notAnOption"> Disabled?</label>
35083              <button ng-click="colors.splice($index, 1)" aria-label="Remove">X</button>
35084            </li>
35085            <li>
35086              <button ng-click="colors.push({})">add</button>
35087            </li>
35088          </ul>
35089          <hr/>
35090          <label>Color (null not allowed):
35091            <select ng-model="myColor" ng-options="color.name for color in colors"></select>
35092          </label><br/>
35093          <label>Color (null allowed):
35094          <span  class="nullable">
35095            <select ng-model="myColor" ng-options="color.name for color in colors">
35096              <option value="">-- choose color --</option>
35097            </select>
35098          </span></label><br/>
35099
35100          <label>Color grouped by shade:
35101            <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors">
35102            </select>
35103          </label><br/>
35104
35105          <label>Color grouped by shade, with some disabled:
35106            <select ng-model="myColor"
35107                  ng-options="color.name group by color.shade disable when color.notAnOption for color in colors">
35108            </select>
35109          </label><br/>
35110
35111
35112
35113          Select <button ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</button>.
35114          <br/>
35115          <hr/>
35116          Currently selected: {{ {selected_color:myColor} }}
35117          <div style="border:solid 1px black; height:20px"
35118               ng-style="{'background-color':myColor.name}">
35119          </div>
35120        </div>
35121      </file>
35122      <file name="protractor.js" type="protractor">
35123         it('should check ng-options', function() {
35124           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red');
35125           element.all(by.model('myColor')).first().click();
35126           element.all(by.css('select[ng-model="myColor"] option')).first().click();
35127           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black');
35128           element(by.css('.nullable select[ng-model="myColor"]')).click();
35129           element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click();
35130           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null');
35131         });
35132      </file>
35133    </example>
35134 */
35135
35136/* eslint-disable max-len */
35137//                     //00001111111111000000000002222222222000000000000000000000333333333300000000000000000000000004444444444400000000000005555555555555000000000666666666666600000007777777777777000000000000000888888888800000000000000000009999999999
35138var NG_OPTIONS_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?(?:\s+disable\s+when\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]+?))?$/;
35139                        // 1: value expression (valueFn)
35140                        // 2: label expression (displayFn)
35141                        // 3: group by expression (groupByFn)
35142                        // 4: disable when expression (disableWhenFn)
35143                        // 5: array item variable name
35144                        // 6: object item key variable name
35145                        // 7: object item value variable name
35146                        // 8: collection expression
35147                        // 9: track by expression
35148/* eslint-enable */
35149
35150
35151var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile, $document, $parse) {
35152
35153  function parseOptionsExpression(optionsExp, selectElement, scope) {
35154
35155    var match = optionsExp.match(NG_OPTIONS_REGEXP);
35156    if (!(match)) {
35157      throw ngOptionsMinErr('iexp',
35158        'Expected expression in form of ' +
35159        '\'_select_ (as _label_)? for (_key_,)?_value_ in _collection_\'' +
35160        ' but got \'{0}\'. Element: {1}',
35161        optionsExp, startingTag(selectElement));
35162    }
35163
35164    // Extract the parts from the ngOptions expression
35165
35166    // The variable name for the value of the item in the collection
35167    var valueName = match[5] || match[7];
35168    // The variable name for the key of the item in the collection
35169    var keyName = match[6];
35170
35171    // An expression that generates the viewValue for an option if there is a label expression
35172    var selectAs = / as /.test(match[0]) && match[1];
35173    // An expression that is used to track the id of each object in the options collection
35174    var trackBy = match[9];
35175    // An expression that generates the viewValue for an option if there is no label expression
35176    var valueFn = $parse(match[2] ? match[1] : valueName);
35177    var selectAsFn = selectAs && $parse(selectAs);
35178    var viewValueFn = selectAsFn || valueFn;
35179    var trackByFn = trackBy && $parse(trackBy);
35180
35181    // Get the value by which we are going to track the option
35182    // if we have a trackFn then use that (passing scope and locals)
35183    // otherwise just hash the given viewValue
35184    var getTrackByValueFn = trackBy ?
35185                              function(value, locals) { return trackByFn(scope, locals); } :
35186                              function getHashOfValue(value) { return hashKey(value); };
35187    var getTrackByValue = function(value, key) {
35188      return getTrackByValueFn(value, getLocals(value, key));
35189    };
35190
35191    var displayFn = $parse(match[2] || match[1]);
35192    var groupByFn = $parse(match[3] || '');
35193    var disableWhenFn = $parse(match[4] || '');
35194    var valuesFn = $parse(match[8]);
35195
35196    var locals = {};
35197    var getLocals = keyName ? function(value, key) {
35198      locals[keyName] = key;
35199      locals[valueName] = value;
35200      return locals;
35201    } : function(value) {
35202      locals[valueName] = value;
35203      return locals;
35204    };
35205
35206
35207    function Option(selectValue, viewValue, label, group, disabled) {
35208      this.selectValue = selectValue;
35209      this.viewValue = viewValue;
35210      this.label = label;
35211      this.group = group;
35212      this.disabled = disabled;
35213    }
35214
35215    function getOptionValuesKeys(optionValues) {
35216      var optionValuesKeys;
35217
35218      if (!keyName && isArrayLike(optionValues)) {
35219        optionValuesKeys = optionValues;
35220      } else {
35221        // if object, extract keys, in enumeration order, unsorted
35222        optionValuesKeys = [];
35223        for (var itemKey in optionValues) {
35224          if (optionValues.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') {
35225            optionValuesKeys.push(itemKey);
35226          }
35227        }
35228      }
35229      return optionValuesKeys;
35230    }
35231
35232    return {
35233      trackBy: trackBy,
35234      getTrackByValue: getTrackByValue,
35235      getWatchables: $parse(valuesFn, function(optionValues) {
35236        // Create a collection of things that we would like to watch (watchedArray)
35237        // so that they can all be watched using a single $watchCollection
35238        // that only runs the handler once if anything changes
35239        var watchedArray = [];
35240        optionValues = optionValues || [];
35241
35242        var optionValuesKeys = getOptionValuesKeys(optionValues);
35243        var optionValuesLength = optionValuesKeys.length;
35244        for (var index = 0; index < optionValuesLength; index++) {
35245          var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
35246          var value = optionValues[key];
35247
35248          var locals = getLocals(value, key);
35249          var selectValue = getTrackByValueFn(value, locals);
35250          watchedArray.push(selectValue);
35251
35252          // Only need to watch the displayFn if there is a specific label expression
35253          if (match[2] || match[1]) {
35254            var label = displayFn(scope, locals);
35255            watchedArray.push(label);
35256          }
35257
35258          // Only need to watch the disableWhenFn if there is a specific disable expression
35259          if (match[4]) {
35260            var disableWhen = disableWhenFn(scope, locals);
35261            watchedArray.push(disableWhen);
35262          }
35263        }
35264        return watchedArray;
35265      }),
35266
35267      getOptions: function() {
35268
35269        var optionItems = [];
35270        var selectValueMap = {};
35271
35272        // The option values were already computed in the `getWatchables` fn,
35273        // which must have been called to trigger `getOptions`
35274        var optionValues = valuesFn(scope) || [];
35275        var optionValuesKeys = getOptionValuesKeys(optionValues);
35276        var optionValuesLength = optionValuesKeys.length;
35277
35278        for (var index = 0; index < optionValuesLength; index++) {
35279          var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
35280          var value = optionValues[key];
35281          var locals = getLocals(value, key);
35282          var viewValue = viewValueFn(scope, locals);
35283          var selectValue = getTrackByValueFn(viewValue, locals);
35284          var label = displayFn(scope, locals);
35285          var group = groupByFn(scope, locals);
35286          var disabled = disableWhenFn(scope, locals);
35287          var optionItem = new Option(selectValue, viewValue, label, group, disabled);
35288
35289          optionItems.push(optionItem);
35290          selectValueMap[selectValue] = optionItem;
35291        }
35292
35293        return {
35294          items: optionItems,
35295          selectValueMap: selectValueMap,
35296          getOptionFromViewValue: function(value) {
35297            return selectValueMap[getTrackByValue(value)];
35298          },
35299          getViewValueFromOption: function(option) {
35300            // If the viewValue could be an object that may be mutated by the application,
35301            // we need to make a copy and not return the reference to the value on the option.
35302            return trackBy ? copy(option.viewValue) : option.viewValue;
35303          }
35304        };
35305      }
35306    };
35307  }
35308
35309
35310  // 
35310we can't just jqLite('<option>') since jqLite is not smart enough
35311  // to create it in <select> and IE barfs otherwise.
35312  var optionTemplate = window.document.createElement('option'),
35313      optGroupTemplate = window.document.createElement('optgroup');
35314
35315    function ngOptionsPostLink(scope, selectElement, attr, ctrls) {
35316
35317      var selectCtrl = ctrls[0];
35318      var ngModelCtrl = ctrls[1];
35319      var multiple = attr.multiple;
35320
35321      // The emptyOption allows the application developer to provide their own custom "empty"
35322      // option when the viewValue does not match any of the option values.
35323      var emptyOption;
35324      for (var i = 0, children = selectElement.children(), ii = children.length; i < ii; i++) {
35325        if (children[i].value === '') {
35326          emptyOption = children.eq(i);
35327          break;
35328        }
35329      }
35330
35331      var providedEmptyOption = !!emptyOption;
35332      var emptyOptionRendered = false;
35333
35334      var unknownOption = jqLite(optionTemplate.cloneNode(false));
35335      unknownOption.val('?');
35336
35337      var options;
35338      var ngOptions = parseOptionsExpression(attr.ngOptions, selectElement, scope);
35339      // This stores the newly created options before they are appended to the select.
35340      // Since the contents are removed from the fragment when it is appended,
35341      // we only need to create it once.
35342      var listFragment = $document[0].createDocumentFragment();
35343
35344      var renderEmptyOption = function() {
35345        if (!providedEmptyOption) {
35346          selectElement.prepend(emptyOption);
35347        }
35348        selectElement.val('');
35349        if (emptyOptionRendered) {
35350          emptyOption.prop('selected', true); // needed for IE
35351          emptyOption.attr('selected', true);
35352        }
35353      };
35354
35355      var removeEmptyOption = function() {
35356        if (!providedEmptyOption) {
35357          emptyOption.remove();
35358        } else if (emptyOptionRendered) {
35359          emptyOption.removeAttr('selected');
35360        }
35361      };
35362
35363      var renderUnknownOption = function() {
35364        selectElement.prepend(unknownOption);
35365        selectElement.val('?');
35366        unknownOption.prop('selected', true); // needed for IE
35367        unknownOption.attr('selected', true);
35368      };
35369
35370      var removeUnknownOption = function() {
35371        unknownOption.remove();
35372      };
35373
35374      // Update the controller methods for multiple selectable options
35375      if (!multiple) {
35376
35377        selectCtrl.writeValue = function writeNgOptionsValue(value) {
35378          var selectedOption = options.selectValueMap[selectElement.val()];
35379          var option = options.getOptionFromViewValue(value);
35380
35381          // Make sure to remove the selected attribute from the previously selected option
35382          // Otherwise, screen readers might get confused
35383          if (selectedOption) selectedOption.element.removeAttribute('selected');
35384
35385          if (option) {
35386            // Don't update the option when it is already selected.
35387            // For example, the browser will select the first option by default. In that case,
35388            // most properties are set automatically - except the `selected` attribute, which we
35389            // set always
35390
35391            if (selectElement[0].value !== option.selectValue) {
35392              removeUnknownOption();
35393              removeEmptyOption();
35394
35395              selectElement[0].value = option.selectValue;
35396              option.element.selected = true;
35397            }
35398
35399            option.element.setAttribute('selected', 'selected');
35400          } else {
35401            if (value === null || providedEmptyOption) {
35402              removeUnknownOption();
35403              renderEmptyOption();
35404            } else {
35405              removeEmptyOption();
35406              renderUnknownOption();
35407            }
35408          }
35409        };
35410
35411        selectCtrl.readValue = function readNgOptionsValue() {
35412
35413          var selectedOption = options.selectValueMap[selectElement.val()];
35414
35415          if (selectedOption && !selectedOption.disabled) {
35416            removeEmptyOption();
35417            removeUnknownOption();
35418            return options.getViewValueFromOption(selectedOption);
35419          }
35420          return null;
35421        };
35422
35423        // If we are using `track by` then we must watch the tracked value on the model
35424        // since ngModel only watches for object identity change
35425        // FIXME: When a user selects an option, this watch will fire needlessly
35426        if (ngOptions.trackBy) {
35427          scope.$watch(
35428            function() { return ngOptions.getTrackByValue(ngModelCtrl.$viewValue); },
35429            function() { ngModelCtrl.$render(); }
35430          );
35431        }
35432
35433      } else {
35434
35435        ngModelCtrl.$isEmpty = function(value) {
35436          return !value || value.length === 0;
35437        };
35438
35439
35440        selectCtrl.writeValue = function writeNgOptionsMultiple(value) {
35441          options.items.forEach(function(option) {
35442            option.element.selected = false;
35443          });
35444
35445          if (value) {
35446            value.forEach(function(item) {
35447              var option = options.getOptionFromViewValue(item);
35448              if (option) option.element.selected = true;
35449            });
35450          }
35451        };
35452
35453
35454        selectCtrl.readValue = function readNgOptionsMultiple() {
35455          var selectedValues = selectElement.val() || [],
35456              selections = [];
35457
35458          forEach(selectedValues, function(value) {
35459            var option = options.selectValueMap[value];
35460            if (option && !option.disabled) selections.push(options.getViewValueFromOption(option));
35461          });
35462
35463          return selections;
35464        };
35465
35466        // If we are using `track by` then we must watch these tracked values on the model
35467        // since ngModel only watches for object identity change
35468        if (ngOptions.trackBy) {
35469
35470          scope.$watchCollection(function() {
35471            if (isArray(ngModelCtrl.$viewValue)) {
35472              return ngModelCtrl.$viewValue.map(function(value) {
35473                return ngOptions.getTrackByValue(value);
35474              });
35475            }
35476          }, function() {
35477            ngModelCtrl.$render();
35478          });
35479
35480        }
35481      }
35482
35483
35484      if (providedEmptyOption) {
35485
35486        // we need to remove it before calling selectElement.empty() because otherwise IE will
35487        // remove the label from the element. wtf?
35488        emptyOption.remove();
35489
35490        // compile the element since there might be bindings in it
35491        $compile(emptyOption)(scope);
35492
35493        if (emptyOption[0].nodeType === NODE_TYPE_COMMENT) {
35494          // This means the empty option has currently no actual DOM node, probably because
35495          // it has been modified by a transclusion directive.
35496
35497          emptyOptionRendered = false;
35498
35499          // Redefine the registerOption function, which will catch
35500          // options that are added by ngIf etc. (rendering of the node is async because of
35501          // lazy transclusion)
35502          selectCtrl.registerOption = function(optionScope, optionEl) {
35503            if (optionEl.val() === '') {
35504              emptyOptionRendered = true;
35505              emptyOption = optionEl;
35506              emptyOption.removeClass('ng-scope');
35507              // This ensures the new empty option is selected if previously no option was selected
35508              ngModelCtrl.$render();
35509
35510              optionEl.on('$destroy', function() {
35511                emptyOption = undefined;
35512                emptyOptionRendered = false;
35513              });
35514            }
35515          };
35516
35517        } else {
35518          emptyOption.removeClass('ng-scope');
35519          emptyOptionRendered = true;
35520        }
35521
35522      } else {
35523        emptyOption = jqLite(optionTemplate.cloneNode(false));
35524      }
35525
35526      selectElement.empty();
35527
35528      // We need to do this here to ensure that the options object is defined
35529      // when we first hit it in writeNgOptionsValue
35530      updateOptions();
35531
35532      // We will re-render the option elements if the option values or labels change
35533      scope.$watchCollection(ngOptions.getWatchables, updateOptions);
35534
35535      // ------------------------------------------------------------------ //
35536
35537      function addOptionElement(option, parent) {
35538        var optionElement = optionTemplate.cloneNode(false);
35539        parent.appendChild(optionElement);
35540        updateOptionElement(option, optionElement);
35541      }
35542
35543
35544      function updateOptionElement(option, element) {
35545        option.element = element;
35546        element.disabled = option.disabled;
35547        // NOTE: The label must be set before the value, otherwise IE10/11/EDGE create unresponsive
35548        // selects in certain circumstances when multiple selects are next to each other and display
35549        // the option list in listbox style, i.e. the select is [multiple], or specifies a [size].
35550        // See https://github.com/angular/angular.js/issues/11314 for more info.
35551        // This is unfortunately untestable with unit / e2e tests
35552        if (option.label !== element.label) {
35553          element.label = option.label;
35554          element.textContent = option.label;
35555        }
35556        element.value = option.selectValue;
35557      }
35558
35559      function updateOptions() {
35560        var previousValue = options && selectCtrl.readValue();
35561
35562        // We must remove all current options, but cannot simply set innerHTML = null
35563        // since the providedEmptyOption might have an ngIf on it that inserts comments which we
35564        // must preserve.
35565        // Instead, iterate over the current option elements and remove them or their optgroup
35566        // parents
35567        if (options) {
35568
35569          for (var i = options.items.length - 1; i >= 0; i--) {
35570            var option = options.items[i];
35571            if (isDefined(option.group)) {
35572              jqLiteRemove(option.element.parentNode);
35573            } else {
35574              jqLiteRemove(option.element);
35575            }
35576          }
35577        }
35578
35579        options = ngOptions.getOptions();
35580
35581        var groupElementMap = {};
35582
35583        // Ensure that the empty option is always there if it was explicitly provided
35584        if (providedEmptyOption) {
vendor: 4,592 bytes, lines 35585-35703
35585          selectElement.prepend(emptyOption);
35586        }
35587
35588        options.items.forEach(function addOption(option) {
35589          var groupElement;
35590
35591          if (isDefined(option.group)) {
35592
35593            // This option is to live in a group
35594            // See if we have already created this group
35595            groupElement = groupElementMap[option.group];
35596
35597            if (!groupElement) {
35598
35599              groupElement = optGroupTemplate.cloneNode(false);
35600              listFragment.appendChild(groupElement);
35601
35602              // Update the label on the group element
35603              // "null" is special cased because of Safari
35604              groupElement.label = option.group === null ? 'null' : option.group;
35605
35606              // Store it for use later
35607              groupElementMap[option.group] = groupElement;
35608            }
35609
35610            addOptionElement(option, groupElement);
35611
35612          } else {
35613
35614            // This option is not in a group
35615            addOptionElement(option, listFragment);
35616          }
35617        });
35618
35619        selectElement[0].appendChild(listFragment);
35620
35621        ngModelCtrl.$render();
35622
35623        // Check to see if the value has changed due to the update to the options
35624        if (!ngModelCtrl.$isEmpty(previousValue)) {
35625          var nextValue = selectCtrl.readValue();
35626          var isNotPrimitive = ngOptions.trackBy || multiple;
35627          if (isNotPrimitive ? !equals(previousValue, nextValue) : previousValue !== nextValue) {
35628            ngModelCtrl.$setViewValue(nextValue);
35629            ngModelCtrl.$render();
35630          }
35631        }
35632
35633      }
35634  }
35635
35636  return {
35637    restrict: 'A',
35638    terminal: true,
35639    require: ['select', 'ngModel'],
35640    link: {
35641      pre: function ngOptionsPreLink(scope, selectElement, attr, ctrls) {
35642        // Deactivate the SelectController.register method to prevent
35643        // option directives from accidentally registering themselves
35644        // (and unwanted $destroy handlers etc.)
35645        ctrls[0].registerOption = noop;
35646      },
35647      post: ngOptionsPostLink
35648    }
35649  };
35650}];
35651
35652/**
35653 * @ngdoc directive
35654 * @name ngPluralize
35655 * @restrict EA
35656 *
35657 * @description
35658 * `ngPluralize` is a directive that displays messages according to en-US localization rules.
35659 * These rules are bundled with angular.js, but can be overridden
35660 * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive
35661 * by specifying the mappings between
35662 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
35663 * and the strings to be displayed.
35664 *
35665 * # Plural categories and explicit number rules
35666 * There are two
35667 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
35668 * in Angular's default en-US locale: "one" and "other".
35669 *
35670 * While a plural category may match many numbers (for example, in en-US locale, "other" can match
35671 * any number that is not 1), an explicit number rule can only match one number. For example, the
35672 * explicit number rule for "3" matches the number 3. There are examples of plural categories
35673 * and explicit number rules throughout the rest of this documentation.
35674 *
35675 * # Configuring ngPluralize
35676 * You configure ngPluralize by providing 2 attributes: `count` and `when`.
35677 * You can also provide an optional attribute, `offset`.
35678 *
35679 * The value of the `count` attribute can be either a string or an {@link guide/expression
35680 * Angular expression}; these are evaluated on the current scope for its bound value.
35681 *
35682 * The `when` attribute specifies the mappings between plural categories and the actual
35683 * string to be displayed. The value of the attribute should be a JSON object.
35684 *
35685 * The following example shows how to configure ngPluralize:
35686 *
35687 * ```html
35688 * <ng-pluralize count="personCount"
35689                 when="{'0': 'Nobody is viewing.',
35690 *                      'one': '1 person is viewing.',
35691 *                      'other': '{} people are viewing.'}">
35692 * </ng-pluralize>
35693 *```
35694 *
35695 * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
35696 * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
35697 * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
35698 * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
35699 * show "a dozen people are viewing".
35700 *
35701 * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
35702 * into pluralized strings. In the previous example, Angular will replace `{}` with
35703 * <span ng-non-bindable>`{{personCount}}
35703`</span>. The closed braces `{}` is a placeholder
35704 * for <span ng-non-bindable>{{numberExpression}}</span>.
35705 *
35706 * If no rule is defined for a category, then an empty string is displayed and a warning is generated.
35707 * Note that some locales define more categories than `one` and `other`. For example, fr-fr defines `few` and `many`.
35708 *
35709 * # Configuring ngPluralize with offset
35710 * The `offset` attribute allows further customization of pluralized text, which can result in
35711 * a better user experience. For example, instead of the message "4 people are viewing this document",
35712 * you might display "John, Kate and 2 others are viewing this document".
35713 * The offset attribute allows you to offset a number by any desired value.
35714 * Let's take a look at an example:
35715 *
35716 * ```html
35717 * <ng-pluralize count="personCount" offset=2
35718 *               when="{'0': 'Nobody is viewing.',
35719 *                      '1': '{{person1}} is viewing.',
35720 *                      '2': '{{person1}} and {{person2}} are viewing.',
35721 *                      'one': '{{person1}}, {{person2}} and one other person are viewing.',
35722 *                      'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
35723 * </ng-pluralize>
35724 * ```
35725 *
35726 * Notice that we are still using two plural categories(one, other), but we added
35727 * three explicit number rules 0, 1 and 2.
35728 * When one person, perhaps John, views the document, "John is viewing" will be shown.
35729 * When three people view the document, no explicit number rule is found, so
35730 * an offset of 2 is taken off 3, and Angular
vendor: 6,786 bytes, lines 35730-35891
35730 uses 1 to decide the plural category.
35731 * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing"
35732 * is shown.
35733 *
35734 * Note that when you specify offsets, you must provide explicit number rules for
35735 * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
35736 * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
35737 * plural categories "one" and "other".
35738 *
35739 * @param {string|expression} count The variable to be bound to.
35740 * @param {string} when The mapping between plural category to its corresponding strings.
35741 * @param {number=} offset Offset to deduct from the total number.
35742 *
35743 * @example
35744    <example module="pluralizeExample" name="ng-pluralize">
35745      <file name="index.html">
35746        <script>
35747          angular.module('pluralizeExample', [])
35748            .controller('ExampleController', ['$scope', function($scope) {
35749              $scope.person1 = 'Igor';
35750              $scope.person2 = 'Misko';
35751              $scope.personCount = 1;
35752            }]);
35753        </script>
35754        <div ng-controller="ExampleController">
35755          <label>Person 1:<input type="text" ng-model="person1" value="Igor" /></label><br/>
35756          <label>Person 2:<input type="text" ng-model="person2" value="Misko" /></label><br/>
35757          <label>Number of People:<input type="text" ng-model="personCount" value="1" /></label><br/>
35758
35759          <!--- Example with simple pluralization rules for en locale --->
35760          Without Offset:
35761          <ng-pluralize count="personCount"
35762                        when="{'0': 'Nobody is viewing.',
35763                               'one': '1 person is viewing.',
35764                               'other': '{} people are viewing.'}">
35765          </ng-pluralize><br>
35766
35767          <!--- Example with offset --->
35768          With Offset(2):
35769          <ng-pluralize count="personCount" offset=2
35770                        when="{'0': 'Nobody is viewing.',
35771                               '1': '{{person1}} is viewing.',
35772                               '2': '{{person1}} and {{person2}} are viewing.',
35773                               'one': '{{person1}}, {{person2}} and one other person are viewing.',
35774                               'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
35775          </ng-pluralize>
35776        </div>
35777      </file>
35778      <file name="protractor.js" type="protractor">
35779        it('should show correct pluralized string', function() {
35780          var withoutOffset = element.all(by.css('ng-pluralize')).get(0);
35781          var withOffset = element.all(by.css('ng-pluralize')).get(1);
35782          var countInput = element(by.model('personCount'));
35783
35784          expect(withoutOffset.getText()).toEqual('1 person is viewing.');
35785          expect(withOffset.getText()).toEqual('Igor is viewing.');
35786
35787          countInput.clear();
35788          countInput.sendKeys('0');
35789
35790          expect(withoutOffset.getText()).toEqual('Nobody is viewing.');
35791          expect(withOffset.getText()).toEqual('Nobody is viewing.');
35792
35793          countInput.clear();
35794          countInput.sendKeys('2');
35795
35796          expect(withoutOffset.getText()).toEqual('2 people are viewing.');
35797          expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');
35798
35799          countInput.clear();
35800          countInput.sendKeys('3');
35801
35802          expect(withoutOffset.getText()).toEqual('3 people are viewing.');
35803          expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');
35804
35805          countInput.clear();
35806          countInput.sendKeys('4');
35807
35808          expect(withoutOffset.getText()).toEqual('4 people are viewing.');
35809          expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');
35810        });
35811        it('should show data-bound names', function() {
35812          var withOffset = element.all(by.css('ng-pluralize')).get(1);
35813          var personCount = element(by.model('personCount'));
35814          var person1 = element(by.model('person1'));
35815          var person2 = element(by.model('person2'));
35816          personCount.clear();
35817          personCount.sendKeys('4');
35818          person1.clear();
35819          person1.sendKeys('Di');
35820          person2.clear();
35821          person2.sendKeys('Vojta');
35822          expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');
35823        });
35824      </file>
35825    </example>
35826 */
35827var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale, $interpolate, $log) {
35828  var BRACE = /{}/g,
35829      IS_WHEN = /^when(Minus)?(.+)$/;
35830
35831  return {
35832    link: function(scope, element, attr) {
35833      var numberExp = attr.count,
35834          whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs
35835          offset = attr.offset || 0,
35836          whens = scope.$eval(whenExp) || {},
35837          whensExpFns = {},
35838          startSymbol = $interpolate.startSymbol(),
35839          endSymbol = $interpolate.endSymbol(),
35840          braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol,
35841          watchRemover = angular.noop,
35842          lastCount;
35843
35844      forEach(attr, function(expression, attributeName) {
35845        var tmpMatch = IS_WHEN.exec(attributeName);
35846        if (tmpMatch) {
35847          var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]);
35848          whens[whenKey] = element.attr(attr.$attr[attributeName]);
35849        }
35850      });
35851      forEach(whens, function(expression, key) {
35852        whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement));
35853
35854      });
35855
35856      scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) {
35857        var count = parseFloat(newVal);
35858        var countIsNaN = isNumberNaN(count);
35859
35860        if (!countIsNaN && !(count in whens)) {
35861          // If an explicit number rule such as 1, 2, 3... is defined, just use it.
35862          // Otherwise, check it against pluralization rules in $locale service.
35863          count = $locale.pluralCat(count - offset);
35864        }
35865
35866        // If both `count` and `lastCount` are NaN, we don't need to re-register a watch.
35867        // In JS `NaN !== NaN`, so we have to explicitly check.
35868        if ((count !== lastCount) && !(countIsNaN && isNumberNaN(lastCount))) {
35869          watchRemover();
35870          var whenExpFn = whensExpFns[count];
35871          if (isUndefined(whenExpFn)) {
35872            if (newVal != null) {
35873              $log.debug('ngPluralize: no rule defined for \'' + count + '\' in ' + whenExp);
35874            }
35875            watchRemover = noop;
35876            updateElementText();
35877          } else {
35878            watchRemover = scope.$watch(whenExpFn, updateElementText);
35879          }
35880          lastCount = count;
35881        }
35882      });
35883
35884      function updateElementText(newText) {
35885        element.text(newText || '');
35886      }
35887    }
35888  };
35889}];
35890
35891/*
35891 exported ngRepeatDirective */
35892
35893/**
35894 * @ngdoc directive
35895 * @name ngRepeat
35896 * @multiElement
35897 *
35898 * @description
35899 * The `ngRepeat` directive instantiates a template once per item from a collection. Each template
35900 * instance gets its own scope, where the given loop variable is set to the current collection item,
35901 * and `$index` is set to the item index or key.
35902 *
35903 * Special properties are exposed on the local scope of each template instance, including:
35904 *
35905 * | Variable  | Type            | Details                                                                     |
35906 * |-----------|-----------------|-----------------------------------------------------------------------------|
35907 * | `$index`  | {@type number}  | iterator offset of the repeated element (0..length-1)                       |
35908 * | `$first`  | {@type boolean} | true if the repeated element is first in the iterator.                      |
35909 * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. |
35910 * | `$last`   | {@type boolean} | true if the repeated element is last in the iterator.                       |
35911 * | `$even`   | {@type boolean} | true if the iterator position `$index` is even (otherwise false).           |
35912 * | `$odd`    | {@type boolean} | true if the iterator position `$index` is odd (otherwise false).            |
35913 *
35914 * <div class="alert alert-info">
35915 *   Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
35916 *   This may be useful when, for instance, nesting ngRepeats.
35917 * </div>
35918 *
35919 *
35920 * # Iterating over object properties
35921 *
35922 * It is possible to get `ngRepeat` to iterate over the properties of an object using the following
35923 * syntax:
35924 *
35925 * ```js
35926 * <div ng-repeat="(key, value) in myObj"> ... </div>
35927 * ```
35928 *
35929 * However, there are a few limitations compared to array iteration:
35930 *
35931 * - The JavaScript specification does not define the order of keys
35932 *   returned for an object, so Angular relies on the order returned by the browser
35933 *   when running `for key in myObj`. Browsers generally follow the strategy of providing
35934 *   keys in the order in which they were defined, although there are exceptions when keys are deleted
35935 *   and reinstated. See the
35936 *   [MDN page on `delete` for more info](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_notes).
35937 *
35938 * - `ngRepeat` will silently *ignore* object keys starting with `$`, because
35939 *   it's a prefix used by Angular for public (`$`) and private (`$$`) properties.
35940 *
35941 * - The built-in filters {@link ng.orderBy orderBy} and {@link ng.filter filter} do not work with
35942 *   objects, and will throw an error if used with one.
35943 *
35944 * If you are hitting any of these limitations, the recommended workaround is to convert your object into an array
35945 * that is sorted into the order that you prefer before providing it to `ngRepeat`. You could
35946 * do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter)
35947 * or implement a `$watch` on the object yourself.
35948 *
35949 *
35950 * # Tracking and Duplicates
35951 *
35952 * `ngRepeat` uses {@link $rootScope.Scope#$watchCollection $watchCollection} to detect changes in
35953 * the collection. When a change happens, `ngRepeat` then makes the corresponding changes to the DOM:
35954 *
35955 * * When an item is added, a new instance of the template is added to the DOM.
35956 * * When an item is removed, its template instance is removed from the DOM.
35957 * * When items are reordered, their respective templates are reordered in the DOM.
35958 *
35959 * To minimize creation of DOM elements, `ngRepeat` uses a function
35960 * to "keep track" of all items in the collection and their corresponding DOM elements.
35961 * For example, if an item is added to the collection, `ngRepeat` will know that all other items
35962 * already have DOM elements, and will not re-render them.
35963 *
35964 * The default tracking function (which tracks items by their identity) does not allow
35965 * duplicate items in arrays. This is because when there are duplicates, it is not possible
35966 * to maintain a one-to-one mapping between collection items and DOM elements.
35967 *
35968 * If you do need to repeat duplicate items, you can substitute the default tracking behavior
35969 * with your own using the `track by` expression.
35970 *
35971 * For example, you may track items by the index of each item in the collection, using the
35972 * special scope property `$index`:
35973 * ```html
35974 *    <div ng-repeat="n in [42, 42, 43, 43] track by $index">
35975 *      {{n}}
35976 *    </div>
35977 * ```
35978 *
35979 * You may also use arbitrary expressions in `track by`, including references to custom functions
35980 * on the scope:
35981 * ```html
35982 *    <div ng-repeat="n in [42, 42, 43, 43] track by myTrackingFunction(n)">
35983 *      {{n}}
35984 *    </div>
35985 * ```
35986 *
35987 * <div class="alert alert-success">
35988 * If you are working with objects that have a unique identifier property, you should track
35989 * by this identifier instead of the object instance. Should you reload your data later, `ngRepeat`
35990 * will not have to rebuild the DOM elements for items it has already rendered, even if the
35991 * JavaScript objects in the collection have been substituted for new ones. For large collections,
35992 * this significantly improves rendering performance. If you don't have a unique identifier,
35993 * `track by $index` can also provide a performance boost.
35994 * </div>
35995 *
35996 * ```html
35997 *    <div ng-repeat="model in collection track by model.id">
35998 *      {{model.name}}
35999 *    </div>
36000 * ```
36001 *
36002 * <br />
36003 * <div class="alert alert-warning">
36004 * Avoid using `track by $index` when the repeated template contains
36005 * {@link guide/expression#one-time-binding one-time bindings}. In such cases, the `nth` DOM
36006 * element will always be matched with the `nth` item of the array, so the bindings on that element
36007 * will not be updated even when the corresponding item changes, essentially causing the view to get
36008 * out-of-sync with the underlying data.
36009 * </div>
36010 *
36011 * When no `track by` expression is provided, it is equivalent to tracking by the built-in
36012 * `$id` function, which tracks items by their identity:
36013 * ```html
36014 *    <div ng-repeat="obj in collection track by $id(obj)">
36015 *      {{obj.prop}}
36016 *    </div>
36017 * ```
36018 *
36019 * <br />
36020 * <div class="alert alert-warning">
36021 * **Note:** `track by` must always be the last expression:
36022 * </div>
36023 * ```
36024 *    <div ng-repeat="model in collection | orderBy: 'id' as filtered_result track by model.id">
36025 *      {{model.name}}
36026 *    </div>
36027 * ```
36028 *
36029 *
36030 * # Special repeat start and end points
36031 * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
36032 * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
36033 * The **ng-repeat-start** directive works the same as **ng-repeat**, but will repeat all the HTML code (including the tag it's defined on)
36034 * up to and including the ending HTML tag where **ng-repeat-end** is placed.
36035 *
36036 * The example below makes use of this feature:
36037 * ```html
36038 *   <header ng-repeat-start="item in items">
36039 *     Header {{ item }}
36040 *   </header>
36041 *   <div class="body">
36042 *     Body {{ item }}
36043 *   </div>
36044 *   <footer ng-repeat-end>
36045 *     Footer {{ item }}
36046 *   </footer>
36047 * ```
36048 *
36049 * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to:
36050 * ```html
36051 *   <header>
36052 *     Header A
36053 *   </header>
36054 *   <div class="body">
36055 *     Body A
36056 *   </div>
36057 *   <footer>
36058 *     Footer A
36059 *   </footer>
36060 *   <header>
36061 *     Header B
36062 *   </header>
36063 *   <div class="body">
36064 *     Body B
36065 *   </div>
36066 *   <footer>
36067 *     Footer B
36068 *   </footer>
36069 * ```
36070 *
36071 * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such
36072 * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
36073 *
36074 * @animations
36075 * | Animation                        | Occurs                              |
36076 * |----------------------------------|-------------------------------------|
36077 * | {@link ng.$animate#enter enter} | when a new item is added to the list or when an item is revealed after a filter |
36078 * | {@link ng.$animate#leave leave} | when an item is removed from the list or when an item is filtered out |
36079 * | {@link ng.$animate#move move } | when an adjacent item is filtered out causing a reorder or when the item contents are reordered |
36080 *
36081 * See the example below for defining CSS animations with ngRepeat.
36082 *
36083 * @element ANY
36084 * @scope
36085 * @priority 1000
36086 * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These
36087 *   formats are currently supported:
36088 *
36089 *   * `variable in expression` – where variable is the user defined loop variable and `expression`
36090 *     is a scope expression giving the collection to enumerate.
36091 *
36092 *     For example: `album in artist.albums`.
36093 *
36094 *   * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
36095 *     and `expression` is the scope expression giving the collection to enumerate.
36096 *
36097 *     For example: `(name, age) in {'adam':10, 'amalie':12}`.
36098 *
36099 *   * `variable in expression track by tracking_expression` – You can also provide an optional tracking expression
36100 *     which can be used to associate the objects in the collection with the DOM elements. If no tracking expression
36101 *     is specified, ng-repeat associates elements by identity. It is an error to have
36102 *     more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are
36103 *     mapped to the same DOM element, which is not possible.)
36104 *
36105 *     Note that the tracking expression must come last, after any filters, and the alias expression.
36106 *
36107 *     For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
36108 *     will be associated by item identity in the array.
36109 *
36110 *     For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
36111 *     `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
36112 *     with the corresponding item in the array by identity. Moving the same object in array would move the DOM
36113 *     element in the same way in the DOM.
36114 *
36115 *     For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
36116 *     case the object identity does not matter. Two objects are considered equivalent as long as their `id`
36117 *     property is same.
36118 *
36119 *     For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter
36120 *     to items in conjunction with a tracking expression.
36121 *
36122 *   * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the
36123 *     intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message
36124 *     when a filter is active on the repeater, but the filtered result set is empty.
36125 *
36126 *     For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after
36127 *     the items have been processed through the filter.
36128 *
36129 *     Please note that `as [variable name] is not an operator but rather a part of ngRepeat micro-syntax so it can be used only at the end
36130 *     (and not as operator, inside an expression).
36131 *
36132 *     For example: `item in items | filter : x | orderBy : order | limitTo : limit as results` .
36133 *
36134 * @example
36135 * This example uses `ngRepeat` to display a list of people. A filter is used to restrict the displayed
36136 * results by name or by age. New (entering) and removed (leaving) items are animated.
36137  <example module="ngRepeat" name="ngRepeat" deps="angular-animate.js" animations="true" name="ng-repeat">
36138    <file name="index.html">
36139      <div ng-controller="repeatController">
36140        I have {{friends.length}} friends. They are:
36141        <input type="search" ng-model="q" placeholder="filter friends..." aria-label="filter friends" />
36142        <ul class="example-animate-container">
36143          <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results">
36144            [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
36145          </li>
36146          <li class="animate-repeat" ng-if="results.length === 0">
36147            <strong>No results found...</strong>
36148          </li>
36149        </ul>
36150      </div>
36151    </file>
36152    <file name="script.js">
36153      angular.module('ngRepeat', ['ngAnimate']).controller('repeatController', function($scope) {
36154        $scope.friends = [
36155          {name:'John', age:25, gender:'boy'},
36156          {name:'Jessie', age:30, gender:'girl'},
36157          {name:'Johanna', age:28, gender:'girl'},
36158          {name:'Joy', age:15, gender:'girl'},
36159          {name:'Mary', age:28, gender:'girl'},
36160          {name:'Peter', age:95, gender:'boy'},
36161          {name:'Sebastian', age:50, gender:'boy'},
36162          {name:'Erika', age:27, gender:'girl'},
36163          {name:'Patrick', age:40, gender:'boy'},
36164          {name:'Samantha', age:60, gender:'girl'}
36165        ];
36166      });
36167    </file>
36168    <file name="animations.css">
36169      .example-animate-container {
36170        background:white;
36171        border:1px solid black;
36172        list-style:none;
36173        margin:0;
36174        padding:0 10px;
36175      }
36176
36177      .animate-repeat {
36178        line-height:30px;
36179        list-style:none;
36180        box-sizing:border-box;
36181      }
36182
36183      .animate-repeat.ng-move,
36184      .animate-repeat.ng-enter,
36185      .animate-repeat.ng-leave {
36186        transition:all linear 0.5s;
36187      }
36188
36189      .animate-repeat.ng-leave.ng-leave-active,
36190      .animate-repeat.ng-move,
36191      .animate-repeat.ng-enter {
36192        opacity:0;
36193        max-height:0;
36194      }
36195
36196      .animate-repeat.ng-leave,
36197      .animate-repeat.ng-move.ng-move-active,
36198      .animate-repeat.ng-enter.ng-enter-active {
36199        opacity:1;
36200        max-height:30px;
36201      }
36202    </file>
36203    <file name="protractor.js" type="protractor">
36204      var friends = element.all(by.repeater('friend in friends'));
36205
36206      it('should render initial data set', function() {
36207        expect(friends.count()).toBe(10);
36208        expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');
36209        expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');
36210        expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');
36211        expect(element(by.binding('friends.length')).getText())
36212            .toMatch("I have 10 friends. They are:");
36213      });
36214
36215       it('should update repeater when filter predicate changes', function() {
36216         expect(friends.count()).toBe(10);
36217
36218         element(by.model('q')).sendKeys('ma');
36219
36220         expect(friends.count()).toBe(2);
36221         expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');
36222         expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');
36223       });
36224      </file>
36225    </example>
36226 */
36227var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $animate, $compile) {
36228  var NG_REMOVED = '$$NG_REMOVED';
36229  var ngRepeatMinErr = minErr('ngRepeat');
36230
36231  var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength) {
36232    // TODO(perf): generate setters to shave off ~40ms or 1-1.5%
36233    scope[valueIdentifier] = value;
36234    if (keyIdentifier) scope[keyIdentifier] = key;
36235    scope.$index = index;
36236    scope.$first = (index === 0);
36237    scope.$last = (index === (arrayLength - 1));
36238    scope.$middle = !(scope.$first || scope.$last);
36239    // eslint-disable-next-line no-bitwise
36240    scope.$odd = !(scope.$even = (index & 1) === 0);
36241  };
36242
36243  var getBlockStart = function(block) {
36244    return block.clone[0];
36245  };
36246
36247  var getBlockEnd = function(block) {
36248    return block.clone[block.clone.length - 1];
36249  };
36250
36251
36252  return {
36253    restrict: 'A',
36254    multiElement: true,
36255    transclude: 'element',
36256    priority: 1000,
36257    terminal: true,
36258    $$tlb: true,
36259    compile: function ngRepeatCompile($element, $attr) {
36260      var expression = $attr.ngRepeat;
36261      var ngRepeatEndComment = $compile.$$createComment('end ngRepeat', expression);
36262
36263      var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);
36264
36265      if (!match) {
36266        throw ngRepeatMinErr('iexp', 'Expected expression in form of \'_item_ in _collection_[ track by _id_]\' but got \'{0}\'.',
36267            expression);
36268      }
36269
36270      var lhs = match[1];
36271      var rhs = match[2];
36272      var aliasAs = match[3];
36273      var trackByExp = match[4];
36274
36275      match = lhs.match(/^(?:(\s*[$\w]+)|\(\s*([$\w]+)\s*,\s*([$\w]+)\s*\))$/);
36276
36277      if (!match) {
36278        throw ngRepeatMinErr('iidexp', '\'_item_\' in \'_item_ in _collection_\' should be an identifier or \'(_key_, _value_)\' expression, but got \'{0}\'.',
36279            lhs);
36280      }
36281      var valueIdentifier = match[3] || match[1];
36282      var keyIdentifier = match[2];
36283
36284      if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) ||
36285          /^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(aliasAs))) {
36286        throw ngRepeatMinErr('badident', 'alias \'{0}\' is invalid --- must be a valid JS identifier which is not a reserved name.',
36287          aliasAs);
36288      }
36289
36290      var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn;
36291      var hashFnLocals = {$id: hashKey};
36292
36293      if (trackByExp) {
36294        trackByExpGetter = $parse(trackByExp);
36295      } else {
36296        trackByIdArrayFn = function(key, value) {
36297          return hashKey(value);
36298        };
36299        trackByIdObjFn = function(key) {
36300          return key;
36301        };
36302      }
36303
36304      return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) {
36305
36306        if (trackByExpGetter) {
36307          trackByIdExpFn = function(key, value, index) {
vendor: 14,350 bytes, lines 36308-36650
36308            // assign key, value, and $index to the locals so that they can be used in hash functions
36309            if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
36310            hashFnLocals[valueIdentifier] = value;
36311            hashFnLocals.$index = index;
36312            return trackByExpGetter($scope, hashFnLocals);
36313          };
36314        }
36315
36316        // Store a list of elements from previous run. This is a hash where key is the item from the
36317        // iterator, and the value is objects with following properties.
36318        //   - scope: bound scope
36319        //   - element: previous element.
36320        //   - index: position
36321        //
36322        // We are using no-proto object so that we don't need to guard against inherited props via
36323        // hasOwnProperty.
36324        var lastBlockMap = createMap();
36325
36326        //watch props
36327        $scope.$watchCollection(rhs, function ngRepeatAction(collection) {
36328          var index, length,
36329              previousNode = $element[0],     // node that cloned nodes should be inserted after
36330                                              // initialized to the comment node anchor
36331              nextNode,
36332              // Same as lastBlockMap but it has the current state. It will become the
36333              // lastBlockMap on the next iteration.
36334              nextBlockMap = createMap(),
36335              collectionLength,
36336              key, value, // key/value of iteration
36337              trackById,
36338              trackByIdFn,
36339              collectionKeys,
36340              block,       // last object information {scope, element, id}
36341              nextBlockOrder,
36342              elementsToRemove;
36343
36344          if (aliasAs) {
36345            $scope[aliasAs] = collection;
36346          }
36347
36348          if (isArrayLike(collection)) {
36349            collectionKeys = collection;
36350            trackByIdFn = trackByIdExpFn || trackByIdArrayFn;
36351          } else {
36352            trackByIdFn = trackByIdExpFn || trackByIdObjFn;
36353            // if object, extract keys, in enumeration order, unsorted
36354            collectionKeys = [];
36355            for (var itemKey in collection) {
36356              if (hasOwnProperty.call(collection, itemKey) && itemKey.charAt(0) !== '$') {
36357                collectionKeys.push(itemKey);
36358              }
36359            }
36360          }
36361
36362          collectionLength = collectionKeys.length;
36363          nextBlockOrder = new Array(collectionLength);
36364
36365          // locate existing items
36366          for (index = 0; index < collectionLength; index++) {
36367            key = (collection === collectionKeys) ? index : collectionKeys[index];
36368            value = collection[key];
36369            trackById = trackByIdFn(key, value, index);
36370            if (lastBlockMap[trackById]) {
36371              // found previously seen block
36372              block = lastBlockMap[trackById];
36373              delete lastBlockMap[trackById];
36374              nextBlockMap[trackById] = block;
36375              nextBlockOrder[index] = block;
36376            } else if (nextBlockMap[trackById]) {
36377              // if collision detected. restore lastBlockMap and throw an error
36378              forEach(nextBlockOrder, function(block) {
36379                if (block && block.scope) lastBlockMap[block.id] = block;
36380              });
36381              throw ngRepeatMinErr('dupes',
36382                  'Duplicates in a repeater are not allowed. Use \'track by\' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}',
36383                  expression, trackById, value);
36384            } else {
36385              // new never before seen block
36386              nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined};
36387              nextBlockMap[trackById] = true;
36388            }
36389          }
36390
36391          // remove leftover items
36392          for (var blockKey in lastBlockMap) {
36393            block = lastBlockMap[blockKey];
36394            elementsToRemove = getBlockNodes(block.clone);
36395            $animate.leave(elementsToRemove);
36396            if (elementsToRemove[0].parentNode) {
36397              // if the element was not removed yet because of pending animation, mark it as deleted
36398              // so that we can ignore it later
36399              for (index = 0, length = elementsToRemove.length; index < length; index++) {
36400                elementsToRemove[index][NG_REMOVED] = true;
36401              }
36402            }
36403            block.scope.$destroy();
36404          }
36405
36406          // we are not using forEach for perf reasons (trying to avoid #call)
36407          for (index = 0; index < collectionLength; index++) {
36408            key = (collection === collectionKeys) ? index : collectionKeys[index];
36409            value = collection[key];
36410            block = nextBlockOrder[index];
36411
36412            if (block.scope) {
36413              // if we have already seen this object, then we need to reuse the
36414              // associated scope/element
36415
36416              nextNode = previousNode;
36417
36418              // skip nodes that are already pending removal via leave animation
36419              do {
36420                nextNode = nextNode.nextSibling;
36421              } while (nextNode && nextNode[NG_REMOVED]);
36422
36423              if (getBlockStart(block) !== nextNode) {
36424                // existing item which got moved
36425                $animate.move(getBlockNodes(block.clone), null, previousNode);
36426              }
36427              previousNode = getBlockEnd(block);
36428              updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
36429            } else {
36430              // new item which we don't know about
36431              $transclude(function ngRepeatTransclude(clone, scope) {
36432                block.scope = scope;
36433                // http://jsperf.com/clone-vs-createcomment
36434                var endNode = ngRepeatEndComment.cloneNode(false);
36435                clone[clone.length++] = endNode;
36436
36437                $animate.enter(clone, null, previousNode);
36438                previousNode = endNode;
36439                // Note: We only need the first/last node of the cloned nodes.
36440                // However, we need to keep the reference to the jqlite wrapper as it might be changed later
36441                // by a directive with templateUrl when its template arrives.
36442                block.clone = clone;
36443                nextBlockMap[block.id] = block;
36444                updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
36445              });
36446            }
36447          }
36448          lastBlockMap = nextBlockMap;
36449        });
36450      };
36451    }
36452  };
36453}];
36454
36455var NG_HIDE_CLASS = 'ng-hide';
36456var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
36457/**
36458 * @ngdoc directive
36459 * @name ngShow
36460 * @multiElement
36461 *
36462 * @description
36463 * The `ngShow` directive shows or hides the given HTML element based on the expression provided to
36464 * the `ngShow` attribute.
36465 *
36466 * The element is shown or hidden by removing or adding the `.ng-hide` CSS class onto the element.
36467 * The `.ng-hide` CSS class is predefined in AngularJS and sets the display style to none (using an
36468 * `!important` flag). For CSP mode please add `angular-csp.css` to your HTML file (see
36469 * {@link ng.directive:ngCsp ngCsp}).
36470 *
36471 * ```html
36472 * <!-- when $scope.myValue is truthy (element is visible) -->
36473 * <div ng-show="myValue"></div>
36474 *
36475 * <!-- when $scope.myValue is falsy (element is hidden) -->
36476 * <div ng-show="myValue" class="ng-hide"></div>
36477 * ```
36478 *
36479 * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added
36480 * to the class attribute on the element causing it to become hidden. When truthy, the `.ng-hide`
36481 * CSS class is removed from the element causing the element not to appear hidden.
36482 *
36483 * ## Why is `!important` used?
36484 *
36485 * You may be wondering why `!important` is used for the `.ng-hide` CSS class. This is because the
36486 * `.ng-hide` selector can be easily overridden by heavier selectors. For example, something as
36487 * simple as changing the display style on a HTML list item would make hidden elements appear
36488 * visible. This also becomes a bigger issue when dealing with CSS frameworks.
36489 *
36490 * By using `!important`, the show and hide behavior will work as expected despite any clash between
36491 * CSS selector specificity (when `!important` isn't used with any conflicting styles). If a
36492 * developer chooses to override the styling to change how to hide an element then it is just a
36493 * matter of using `!important` in their own CSS code.
36494 *
36495 * ### Overriding `.ng-hide`
36496 *
36497 * By default, the `.ng-hide` class will style the element with `display: none !important`. If you
36498 * wish to change the hide behavior with `ngShow`/`ngHide`, you can simply overwrite the styles for
36499 * the `.ng-hide` CSS class. Note that the selector that needs to be used is actually
36500 * `.ng-hide:not(.ng-hide-animate)` to cope with extra animation classes that can be added.
36501 *
36502 * ```css
36503 * .ng-hide:not(.ng-hide-animate) {
36504 *   /&#42; These are just alternative ways of hiding an element &#42;/
36505 *   display: block!important;
36506 *   position: absolute;
36507 *   top: -9999px;
36508 *   left: -9999px;
36509 * }
36510 * ```
36511 *
36512 * By default you don't need to override anything in CSS and the animations will work around the
36513 * display style.
36514 *
36515 * ## A note about animations with `ngShow`
36516 *
36517 * Animations in `ngShow`/`ngHide` work with the show and hide events that are triggered when the
36518 * directive expression is true and false. This system works like the animation system present with
36519 * `ngClass` except that you must also include the `!important` flag to override the display
36520 * property so that the elements are not actually hidden during the animation.
36521 *
36522 * ```css
36523 * /&#42; A working example can be found at the bottom of this page. &#42;/
36524 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
36525 *   transition: all 0.5s linear;
36526 * }
36527 *
36528 * .my-element.ng-hide-add { ... }
36529 * .my-element.ng-hide-add.ng-hide-add-active { ... }
36530 * .my-element.ng-hide-remove { ... }
36531 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
36532 * ```
36533 *
36534 * Keep in mind that, as of AngularJS version 1.3, there is no need to change the display property
36535 * to block during animation states - ngAnimate will automatically handle the style toggling for you.
36536 *
36537 * @animations
36538 * | Animation                                           | Occurs                                                                                                        |
36539 * |-----------------------------------------------------|---------------------------------------------------------------------------------------------------------------|
36540 * | {@link $animate#addClass addClass} `.ng-hide`       | After the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden. |
36541 * | {@link $animate#removeClass removeClass} `.ng-hide` | After the `ngShow` expression evaluates to a truthy value and just before contents are set to visible.        |
36542 *
36543 * @element ANY
36544 * @param {expression} ngShow If the {@link guide/expression expression} is truthy/falsy then the
36545 *                            element is shown/hidden respectively.
36546 *
36547 * @example
36548 * A simple example, animating the element's opacity:
36549 *
36550  <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-show-simple">
36551    <file name="index.html">
36552      Show: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br />
36553      <div class="check-element animate-show-hide" ng-show="checked">
36554        I show up when your checkbox is checked.
36555      </div>
36556    </file>
36557    <file name="animations.css">
36558      .animate-show-hide.ng-hide {
36559        opacity: 0;
36560      }
36561
36562      .animate-show-hide.ng-hide-add,
36563      .animate-show-hide.ng-hide-remove {
36564        transition: all linear 0.5s;
36565      }
36566
36567      .check-element {
36568        border: 1px solid black;
36569        opacity: 1;
36570        padding: 10px;
36571      }
36572    </file>
36573    <file name="protractor.js" type="protractor">
36574      it('should check ngShow', function() {
36575        var checkbox = element(by.model('checked'));
36576        var checkElem = element(by.css('.check-element'));
36577
36578        expect(checkElem.isDisplayed()).toBe(false);
36579        checkbox.click();
36580        expect(checkElem.isDisplayed()).toBe(true);
36581      });
36582    </file>
36583  </example>
36584 *
36585 * <hr />
36586 * @example
36587 * A more complex example, featuring different show/hide animations:
36588 *
36589  <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-show-complex">
36590    <file name="index.html">
36591      Show: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br />
36592      <div class="check-element funky-show-hide" ng-show="checked">
36593        I show up when your checkbox is checked.
36594      </div>
36595    </file>
36596    <file name="animations.css">
36597      body {
36598        overflow: hidden;
36599        perspective: 1000px;
36600      }
36601
36602      .funky-show-hide.ng-hide-add {
36603        transform: rotateZ(0);
36604        transform-origin: right;
36605        transition: all 0.5s ease-in-out;
36606      }
36607
36608      .funky-show-hide.ng-hide-add.ng-hide-add-active {
36609        transform: rotateZ(-135deg);
36610      }
36611
36612      .funky-show-hide.ng-hide-remove {
36613        transform: rotateY(90deg);
36614        transform-origin: left;
36615        transition: all 0.5s ease;
36616      }
36617
36618      .funky-show-hide.ng-hide-remove.ng-hide-remove-active {
36619        transform: rotateY(0);
36620      }
36621
36622      .check-element {
36623        border: 1px solid black;
36624        opacity: 1;
36625        padding: 10px;
36626      }
36627    </file>
36628    <file name="protractor.js" type="protractor">
36629      it('should check ngShow', function() {
36630        var checkbox = element(by.model('checked'));
36631        var checkElem = element(by.css('.check-element'));
36632
36633        expect(checkElem.isDisplayed()).toBe(false);
36634        checkbox.click();
36635        expect(checkElem.isDisplayed()).toBe(true);
36636      });
36637    </file>
36638  </example>
36639 */
36640var ngShowDirective = ['$animate', function($animate) {
36641  return {
36642    restrict: 'A',
36643    multiElement: true,
36644    link: function(scope, element, attr) {
36645      scope.$watch(attr.ngShow, function ngShowWatchAction(value) {
36646        // we're adding a temporary, animation-specific class for ng-hide since this way
36647        // we can control when the element is actually displayed on screen without having
36648        // to have a global/greedy CSS selector that breaks when other animations are run.
36649        // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845
36650        $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, {
36651          tempClasses: NG_HIDE_IN_PROGRESS_CLASS
36652        });
36653      });
36654    }
36655  };
36656}];
36657
36658
36659/**
36660 * @ngdoc directive
36661 * @name ngHide
36662 * @multiElement
36663 *
36664 * @description
36665 * The `ngHide` directive shows or hides the given HTML element based on the expression provided to
36666 * the `ngHide` attribute.
36667 *
36668 * The element is shown or hidden by removing or adding the `.ng-hide` CSS class onto the element.
36669 * The `.ng-hide` CSS class is predefined in AngularJS and sets the display style to none (using an
36670 * `!important` flag). For CSP mode please add `angular-csp.css` to your HTML file (see
36671 * {@link ng.directive:ngCsp ngCsp}).
36672 *
36673 * ```html
36674 * <!-- when $scope.myValue is truthy (element is hidden) -->
36675 * <div ng-hide="myValue" class="ng-hide"></div>
36676 *
36677 * <!-- when $scope.myValue is falsy (element is visible) -->
36678 * <div ng-hide="myValue"></div>
36679 * ```
36680 *
36681 * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added
36682 * to the class attribute on the element causing it to become hidden. When falsy, the `.ng-hide`
36683 * CSS class is removed from the element causing the element not to appear hidden.
36684 *
36685 * ## Why is `!important` used?
36686 *
36687 * You may be wondering why `!important` is used for the `.ng-hide` CSS class. This is because the
36688 * `.ng-hide` selector can be easily overridden by heavier selectors. For example, something as
36689 * simple as changing the display style on a HTML list item would make hidden elements appear
36690 * visible. This also becomes a bigger issue when dealing with CSS frameworks.
36691 *
36692 * By using `!important`, the show and hide behavior will work as expected despite any clash between
36693 * CSS selector specificity (when `!important` isn't used with any conflicting styles). If a
36694 * developer chooses to override the styling to change how to hide an element then it is just a
36695 * matter of using `!important` in their own CSS code.
36696 *
36697 * ### Overriding `.ng-hide`
36698 *
36699 * By default, the `.ng-hide` class will style the element with `display: none !important`. If you
36700 * wish to change the hide behavior with `ngShow`/`ngHide`, you can simply overwrite the styles for
36701 * the `.ng-hide` CSS class. Note that the selector that needs to be used is actually
36702 * `.ng-hide:not(.ng-hide-animate)` to cope with extra animation classes that can be added.
36703 *
36704 * ```css
36705 * .ng-hide:not(.ng-hide-animate) {
36706 *   /&#42; These are just alternative ways of hiding an element &#42;/
36707 *   display: block!important;
36708 *   position: absolute;
36709 *   top: -9999px;
36710 *   left: -9999px;
36711 * }
36712 * ```
36713 *
36714 * By default you don't need to override in CSS anything and the animations will work around the
36715 * display style.
36716 *
36717 * ## A note about animations with `ngHide`
36718 *
36719 * Animations in `ngShow`/`ngHide` work with the show and hide events that are triggered when the
36720 * directive expression is true and false. This system works like the animation system present with
36721 * `ngClass` except that you must also include the `!important` flag to override the display
36722 * property so that the elements are not actually hidden during the animation.
36723 *
36724 * ```css
36725 * /&#42; A working example can be found at the bottom of this page. &#42;/
36726 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
36727 *   transition: all 0.5s linear;
36728 * }
36729 *
36730 * .my-element.ng-hide-add { ... }
36731 * .my-element.ng-hide-add.ng-hide-add-active { ... }
36732 * .my-element.ng-hide-remove { ... }
36733 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
36734 * ```
36735 *
36736 * Keep in mind that, as of AngularJS version 1.3, there is no need to change the display property
36737 * to block during animation states - ngAnimate will automatically handle the style toggling for you.
36738 *
36739 * @animations
36740 * | Animation                                           | Occurs                                                                                                     |
36741 * |-----------------------------------------------------|------------------------------------------------------------------------------------------------------------|
36742 * | {@link $animate#addClass addClass} `.ng-hide`       | After the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden.  |
36743 * | {@link $animate#removeClass removeClass} `.ng-hide` | After the `ngHide` expression evaluates to a non truthy value and just before contents are set to visible. |
36744 *
36745 *
36746 * @element ANY
36747 * @param {expression} ngHide If the {@link guide/expression expression} is truthy/falsy then the
36748 *                            element is hidden/shown respectively.
36749 *
36750 * @example
36751 * A simple example, animating the element's opacity:
36752 *
36753  <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-hide-simple">
36754    <file name="index.html">
36755      Hide: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br />
36756      <div class="check-element animate-show-hide" ng-hide="checked">
36757        I hide when your checkbox is checked.
36758      </div>
36759    </file>
36760    <file name="animations.css">
36761      .animate-show-hide.ng-hide {
36762        opacity: 0;
36763      }
36764
36765      .animate-show-hide.ng-hide-add,
36766      .animate-show-hide.ng-hide-remove {
36767        transition: all linear 0.5s;
36768      }
36769
36770      .check-element {
36771        border: 1px solid black;
36772        opacity: 1;
36773        padding: 10px;
36774      }
36775    </file>
36776    <file name="protractor.js" type="protractor">
36777      it('should check ngHide', function() {
36778        var checkbox = element(by.model('checked'));
36779        var checkElem = element(by.css('.check-element'));
36780
36781        expect(checkElem.isDisplayed()).toBe(true);
36782        checkbox.click();
36783        expect(checkElem.isDisplayed()).toBe(false);
36784      });
36785    </file>
36786  </example>
36787 *
36788 * <hr />
36789 * @example
36790 * A more complex example, featuring different show/hide animations:
36791 *
36792  <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-hide-complex">
36793    <file name="index.html">
36794      Hide: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br />
36795      <div class="check-element funky-show-hide" ng-hide="checked">
36796        I hide when your checkbox is checked.
36797      </div>
36798    </file>
36799    <file name="animations.css">
36800      body {
36801        overflow: hidden;
36802        perspective: 1000px;
36803      }
36804
36805      .funky-show-hide.ng-hide-add {
36806        transform: rotateZ(0);
36807        transform-origin: right;
36808        transition: all 0.5s ease-in-out;
36809      }
36810
36811      .funky-show-hide.ng-hide-add.ng-hide-add-active {
36812        transform: rotateZ(-135deg);
36813      }
36814
36815      .funky-show-hide.ng-hide-remove {
36816        transform: rotateY(90deg);
36817        transform-origin: left;
36818        transition: all 0.5s ease;
36819      }
36820
36821      .funky-show-hide.ng-hide-remove.ng-hide-remove-active {
36822        transform: rotateY(0);
36823      }
36824
36825      .check-element {
36826        border: 1px solid black;
36827        opacity: 1;
36828        padding: 10px;
36829      }
36830    </file>
36831    <file name="protractor.js" type="protractor">
36832      it('should check ngHide', function() {
36833        var checkbox = element(by.model('checked'));
36834        var checkElem = element(by.css('.check-element'));
36835
36836        expect(checkElem.isDisplayed()).toBe(true);
36837        checkbox.click();
36838        expect(checkElem.isDisplayed()).toBe(false);
36839      });
36840    </file>
36841  </example>
36842 */
36843var ngHideDirective = ['$animate', function($animate) {
36844  return {
36845    restrict: 'A',
36846    multiElement: true,
36847    link: function(scope, element, attr) {
36848      scope.$watch(attr.ngHide, function ngHideWatchAction(value) {
36849        // The comment inside of the ngShowDirective explains why we add and
36850        // remove a temporary class for the show/hide animation
36851        $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, {
36852          tempClasses: NG_HIDE_IN_PROGRESS_CLASS
36853        });
36854      });
36855    }
36856  };
36857}];
36858
36859/**
36860 * @ngdoc directive
36861 * @name ngStyle
36862 * @restrict AC
36863 *
36864 * @description
36865 * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
36866 *
36867 * @knownIssue
36868 * You should not use {@link guide/interpolation interpolation} in the value of the `style`
36869 * attribute, when using the `ngStyle` directive on the same element.
36870 * See {@link guide/interpolation#known-issues here} for more info.
36871 *
36872 * @element ANY
36873 * @param {expression} ngStyle
36874 *
36875 * {@link guide/expression Expression} which evals to an
36876 * object whose keys are CSS style names and values are corresponding values for those CSS
36877 * keys.
36878 *
36879 * Since some CSS style names are not valid keys for an object, they must be quoted.
36880 * See the 'background-color' style in the example below.
36881 *
36882 * @example
36883   <example name="ng-style">
36884     <file name="index.html">
36885        <input type="button" value="set color" ng-click="myStyle={color:'red'}">
36886        <input type="button" value="set background" ng-click="myStyle={'background-color':'blue'}">
36887        <input type="button" value="clear" ng-click="myStyle={}">
36888        <br/>
36889        <span ng-style="myStyle">Sample Text</span>
36890        <pre>myStyle={{myStyle}}</pre>
36891     </file>
36892     <file name="style.css">
36893       span {
36894         color: black;
36895       }
36896     </file>
36897     <file name="protractor.js" type="protractor">
36898       var colorSpan = element(by.css('span'));
36899
36900       it('should check ng-style', function() {
36901         expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
36902         element(by.css('input[value=\'set color\']')).click();
36903         expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');
36904         element(by.css('input[value=clear]')).click();
36905         expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
36906       });
36907     </file>
36908   </example>
36909 */
36910var ngStyleDirective = ngDirective(function(scope, element, attr) {
36911  scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
36912    if (oldStyles && (newStyles !== oldStyles)) {
36913      forEach(oldStyles, function(val, style) { element.css(style, '');});
36914    }
36915    if (newStyles) element.css(newStyles);
36916  }, true
vendor: 9,215 bytes, lines 36916-37156
36916);
36917});
36918
36919/**
36920 * @ngdoc directive
36921 * @name ngSwitch
36922 * @restrict EA
36923 *
36924 * @description
36925 * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression.
36926 * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location
36927 * as specified in the template.
36928 *
36929 * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
36930 * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element
36931 * matches the value obtained from the evaluated expression. In other words, you define a container element
36932 * (where you place the directive), place an expression on the **`on="..."` attribute**
36933 * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
36934 * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
36935 * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
36936 * attribute is displayed.
36937 *
36938 * <div class="alert alert-info">
36939 * Be aware that the attribute values to match against cannot be expressions. They are interpreted
36940 * as literal string values to match against.
36941 * For example, **`ng-switch-when="someVal"`** will match against the string `"someVal"` not against the
36942 * value of the expression `$scope.someVal`.
36943 * </div>
36944
36945 * @animations
36946 * | Animation                        | Occurs                              |
36947 * |----------------------------------|-------------------------------------|
36948 * | {@link ng.$animate#enter enter}  | after the ngSwitch contents change and the matched child element is placed inside the container |
36949 * | {@link ng.$animate#leave leave}  | after the ngSwitch contents change and just before the former contents are removed from the DOM |
36950 *
36951 * @usage
36952 *
36953 * ```
36954 * <ANY ng-switch="expression">
36955 *   <ANY ng-switch-when="matchValue1">...</ANY>
36956 *   <ANY ng-switch-when="matchValue2">...</ANY>
36957 *   <ANY ng-switch-default>...</ANY>
36958 * </ANY>
36959 * ```
36960 *
36961 *
36962 * @scope
36963 * @priority 1200
36964 * @param {*} ngSwitch|on expression to match against <code>ng-switch-when</code>.
36965 * On child elements add:
36966 *
36967 * * `ngSwitchWhen`: the case statement to match against. If match then this
36968 *   case will be displayed. If the same match appears multiple times, all the
36969 *   elements will be displayed. It is possible to associate multiple values to
36970 *   the same `ngSwitchWhen` by defining the optional attribute
36971 *   `ngSwitchWhenSeparator`. The separator will be used to split the value of
36972 *   the `ngSwitchWhen` attribute into multiple tokens, and the element will show
36973 *   if any of the `ngSwitch` evaluates to any of these tokens.
36974 * * `ngSwitchDefault`: the default case when no other case match. If there
36975 *   are multiple default cases, all of them will be displayed when no other
36976 *   case match.
36977 *
36978 *
36979 * @example
36980  <example module="switchExample" deps="angular-animate.js" animations="true" name="ng-switch">
36981    <file name="index.html">
36982      <div ng-controller="ExampleController">
36983        <select ng-model="selection" ng-options="item for item in items">
36984        </select>
36985        <code>selection={{selection}}</code>
36986        <hr/>
36987        <div class="animate-switch-container"
36988          ng-switch on="selection">
36989            <div class="animate-switch" ng-switch-when="settings|options" ng-switch-when-separator="|">Settings Div</div>
36990            <div class="animate-switch" ng-switch-when="home">Home Span</div>
36991            <div class="animate-switch" ng-switch-default>default</div>
36992        </div>
36993      </div>
36994    </file>
36995    <file name="script.js">
36996      angular.module('switchExample', ['ngAnimate'])
36997        .controller('ExampleController', ['$scope', function($scope) {
36998          $scope.items = ['settings', 'home', 'options', 'other'];
36999          $scope.selection = $scope.items[0];
37000        }]);
37001    </file>
37002    <file name="animations.css">
37003      .animate-switch-container {
37004        position:relative;
37005        background:white;
37006        border:1px solid black;
37007        height:40px;
37008        overflow:hidden;
37009      }
37010
37011      .animate-switch {
37012        padding:10px;
37013      }
37014
37015      .animate-switch.ng-animate {
37016        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
37017
37018        position:absolute;
37019        top:0;
37020        left:0;
37021        right:0;
37022        bottom:0;
37023      }
37024
37025      .animate-switch.ng-leave.ng-leave-active,
37026      .animate-switch.ng-enter {
37027        top:-50px;
37028      }
37029      .animate-switch.ng-leave,
37030      .animate-switch.ng-enter.ng-enter-active {
37031        top:0;
37032      }
37033    </file>
37034    <file name="protractor.js" type="protractor">
37035      var switchElem = element(by.css('[ng-switch]'));
37036      var select = element(by.model('selection'));
37037
37038      it('should start in settings', function() {
37039        expect(switchElem.getText()).toMatch(/Settings Div/);
37040      });
37041      it('should change to home', function() {
37042        select.all(by.css('option')).get(1).click();
37043        expect(switchElem.getText()).toMatch(/Home Span/);
37044      });
37045      it('should change to settings via "options"', function() {
37046        select.all(by.css('option')).get(2).click();
37047        expect(switchElem.getText()).toMatch(/Settings Div/);
37048      });
37049      it('should select default', function() {
37050        select.all(by.css('option')).get(3).click();
37051        expect(switchElem.getText()).toMatch(/default/);
37052      });
37053    </file>
37054  </example>
37055 */
37056var ngSwitchDirective = ['$animate', '$compile', function($animate, $compile) {
37057  return {
37058    require: 'ngSwitch',
37059
37060    // asks for $scope to fool the BC controller module
37061    controller: ['$scope', function NgSwitchController() {
37062     this.cases = {};
37063    }],
37064    link: function(scope, element, attr, ngSwitchController) {
37065      var watchExpr = attr.ngSwitch || attr.on,
37066          selectedTranscludes = [],
37067          selectedElements = [],
37068          previousLeaveAnimations = [],
37069          selectedScopes = [];
37070
37071      var spliceFactory = function(array, index) {
37072          return function(response) {
37073            if (response !== false) array.splice(index, 1);
37074          };
37075      };
37076
37077      scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
37078        var i, ii;
37079
37080        // Start with the last, in case the array is modified during the loop
37081        while (previousLeaveAnimations.length) {
37082          $animate.cancel(previousLeaveAnimations.pop());
37083        }
37084
37085        for (i = 0, ii = selectedScopes.length; i < ii; ++i) {
37086          var selected = getBlockNodes(selectedElements[i].clone);
37087          selectedScopes[i].$destroy();
37088          var runner = previousLeaveAnimations[i] = $animate.leave(selected);
37089          runner.done(spliceFactory(previousLeaveAnimations, i));
37090        }
37091
37092        selectedElements.length = 0;
37093        selectedScopes.length = 0;
37094
37095        if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {
37096          forEach(selectedTranscludes, function(selectedTransclude) {
37097            selectedTransclude.transclude(function(caseElement, selectedScope) {
37098              selectedScopes.push(selectedScope);
37099              var anchor = selectedTransclude.element;
37100              caseElement[caseElement.length++] = $compile.$$createComment('end ngSwitchWhen');
37101              var block = { clone: caseElement };
37102
37103              selectedElements.push(block);
37104              $animate.enter(caseElement, anchor.parent(), anchor);
37105            });
37106          });
37107        }
37108      });
37109    }
37110  };
37111}];
37112
37113var ngSwitchWhenDirective = ngDirective({
37114  transclude: 'element',
37115  priority: 1200,
37116  require: '^ngSwitch',
37117  multiElement: true,
37118  link: function(scope, element, attrs, ctrl, $transclude) {
37119
37120    var cases = attrs.ngSwitchWhen.split(attrs.ngSwitchWhenSeparator).sort().filter(
37121      // Filter duplicate cases
37122      function(element, index, array) { return array[index - 1] !== element; }
37123    );
37124
37125    forEach(cases, function(whenCase) {
37126      ctrl.cases['!' + whenCase] = (ctrl.cases['!' + whenCase] || []);
37127      ctrl.cases['!' + whenCase].push({ transclude: $transclude, element: element });
37128    });
37129  }
37130});
37131
37132var ngSwitchDefaultDirective = ngDirective({
37133  transclude: 'element',
37134  priority: 1200,
37135  require: '^ngSwitch',
37136  multiElement: true,
37137  link: function(scope, element, attr, ctrl, $transclude) {
37138    ctrl.cases['?'] = (ctrl.cases['?'] || []);
37139    ctrl.cases['?'].push({ transclude: $transclude, element: element });
37140   }
37141});
37142
37143/**
37144 * @ngdoc directive
37145 * @name ngTransclude
37146 * @restrict EAC
37147 *
37148 * @description
37149 * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
37150 *
37151 * You can specify that you want to insert a named transclusion slot, instead of the default slot, by providing the slot name
37152 * as the value of the `ng-transclude` or `ng-transclude-slot` attribute.
37153 *
37154 * If the transcluded content is not empty (i.e. contains one or more DOM nodes, including whitespace text nodes), any existing
37155 * content of this element will be removed before the transcluded content is inserted.
37156 * If the transcluded content is empty
37156, the existing content is left intact. This lets you provide fallback content in the case
37157 * that no transcluded content is provided.
37158 *
37159 * @element ANY
37160 *
37161 * @param {string} ngTransclude|ngTranscludeSlot the name of the slot to insert at this point. If this is not provided, is empty
37162 *                                               or its value is the same as the name of the attribute then the default slot is used.
37163 *
37164 * @example
37165 * ### Basic transclusion
37166 * This example demonstrates basic transclusion of content into a component directive.
37167 * <example name="simpleTranscludeExample" module="transcludeExample">
37168 *   <file name="index.html">
37169 *     <script>
37170 *       angular.module('transcludeExample', [])
37171 *        .directive('pane', function(){
37172 *           return {
37173 *             restrict: 'E',
37174 *             transclude: true,
37175 *             scope: { title:'@' },
37176 *             template: '<div style="border: 1px solid black;">' +
37177 *                         '<div style="background-color: gray">{{title}}</div>' +
37178 *                         '<ng-transclude></ng-transclude>' +
37179 *                       '</div>'
37180 *           };
37181 *       })
37182 *       .controller('ExampleController', ['$scope', function($scope) {
37183 *         $scope.title = 'Lorem Ipsum';
37184 *         $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
37185 *       }]);
37186 *     </script>
37187 *     <div ng-controller="ExampleController">
37188 *       <input ng-model="title" aria-label="title"> <br/>
37189 *       <textarea ng-model="text" aria-label="text"></textarea> <br/>
37190 *       <pane title="{{title}}">{{text}}</pane>
37191 *     </div>
37192 *   </file>
37193 *   <file name="protractor.js" type="protractor">
37194 *      it('should have transcluded', function() {
37195 *        var titleElement = element(by.model('title'));
37196 *        titleElement.clear();
37197 *        titleElement.sendKeys('TITLE');
37198 *        var textElement = element(by.model('text'));
37199 *        textElement.clear();
37200 *        textElement.sendKeys('TEXT');
37201 *        expect(element(by.binding('title')).getText()).toEqual('TITLE');
37202 *        expect(element(by.binding('text')).getText()).toEqual('TEXT');
37203 *      });
37204 *   </file>
37205 * </example>
37206 *
37207 * @example
37208 * ### Transclude fallback content
37209 * This example shows how to use `NgTransclude` with fallback content, that
37210 * is displayed if no transcluded content is provided.
37211 *
37212 * <example module="transcludeFallbackContentExample" name="ng-transclude">
37213 * <file name="index.html">
37214 * <script>
37215 * angular.module('transcludeFallbackContentExample', [])
37216 * .directive('myButton', function(){
37217 *             return {
37218 *               restrict: 'E',
37219 *               transclude: true,
37220 *               scope: true,
37221 *               template: '<button style="cursor: pointer;">' +
37222 *                           '<ng-transclude>' +
37223 *                             '<b style="color: red;">Button1</b>' +
37224 *                           '</ng-transclude>' +
37225 *                         '</button>'
37226 *             };
37227 *         });
37228 * </script>
37229 * <!-- fallback button content -->
37230 * <my-button id="fallback"></my-button>
37231 * <!-- modified button content -->
37232 * <my-button id="modified">
37233 *   <i style="color: green;">Button2</i>
37234 * </my-button>
37235 * </file>
37236 * <file name="protractor.js" type="protractor">
37237 * it('should have different transclude element content', function() {
37238 *          expect(element(by.id('fallback')).getText()).toBe('Button1');
37239 *          expect(element(by.id('modified')).getText()).toBe('Button2');
37240 *        });
37241 * </file>
37242 * </example>
37243 *
37244 * @example
37245 * ### Multi-slot transclusion
37246 * This example demonstrates using multi-slot transclusion in a component directive.
37247 * <example name="multiSlotTranscludeExample" module="multiSlotTranscludeExample">
37248 *   <file name="index.html">
37249 *    <style>
37250 *      .title, .footer {
37251 *        background-color: gray
37252 *      }
37253 *    </style>
37254 *    <div ng-controller="ExampleController">
37255 *      <input ng-model="title" aria-label="title"> <br/>
37256 *      <textarea ng-model="text" aria-label="text"></textarea> <br/>
37257 *      <pane>
37258 *        <pane-title><a ng-href="{{link}}">{{title}}</a></pane-title>
37259 *        <pane-body><p>{{text}}</p></pane-body>
37260 *      </pane>
37261 *    </div>
37262 *   </file>
37263 *   <file name="app.js">
37264 *    angular.module('multiSlotTranscludeExample', [])
37265 *     .directive('pane', function() {
37266 *        return {
37267 *          restrict: 'E',
37268 *          transclude: {
37269 *            'title': '?paneTitle',
37270 *            'body': 'paneBody',
37271 *            'footer': '?paneFooter'
37272 *          },
37273 *          template: '<div style="border: 1px solid black;">' +
37274 *                      '<div class="title" ng-transclude="title">Fallback Title</div>' +
37275 *                      '<div ng-transclude="body"></div>' +
37276 *                      '<div class="footer" ng-transclude="footer">Fallback Footer</div>' +
37277 *                    '</div>'
37278 *        };
37279 *    })
37280 *    .controller('ExampleController', ['$scope', function($scope) {
37281 *      $scope.title = 'Lorem Ipsum';
37282 *      $scope.link = 'https://google.com';
37283 *      $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
37284 *    }]);
37285 *   </file>
37286 *   <file name="protractor.js" type="protractor">
37287 *      it('should have transcluded the title and the body', function() {
37288 *        var titleElement = element(by.model('title'));
37289 *        titleElement.clear();
37290 *        titleElement.sendKeys('TITLE');
37291 *        var textElement = element(by.model('text'));
37292 *        textElement.clear();
37293 *        textElement.sendKeys('TEXT');
37294 *        expect(element(by.css('.title')).getText()).toEqual('TITLE');
37295 *        expect(element(by.binding('text')).getText()).toEqual('TEXT');
37296 *        expect(element(by.css('.footer')).getText()).toEqual('Fallback Footer');
37297 *      });
37298 *   </file>
37299 * </example>
37300 */
37301var ngTranscludeMinErr = minErr('ngTransclude');
37302var ngTranscludeDirective = ['$compile', function($compile) {
37303  return {
37304    restrict: 'EAC',
37305    terminal: true,
37306    compile: function ngTranscludeCompile(tElement) {
37307
37308      // Remove and cache any original content to act as a fallback
37309      var fallbackLinkFn = $compile(tElement.contents());
37310      tElement.empty();
37311
37312      return function ngTranscludePostLink($scope, $element, $attrs, controller, $transclude) {
37313
37314        if (!$transclude) {
37315          throw ngTranscludeMinErr('orphan',
37316          'Illegal use of ngTransclude directive in the template! ' +
37317          'No parent directive that requires a transclusion found. ' +
37318          'Element: {0}',
37319          startingTag($element));
37320        }
37321
37322
37323        // If the attribute is of the form: `ng-transclude="ng-transclude"` then treat it like the default
37324        if ($attrs.ngTransclude === $attrs.$attr.ngTransclude) {
37325          $attrs.ngTransclude = '';
37326        }
37327        var slotName = $attrs.ngTransclude || $attrs.ngTranscludeSlot;
37328
37329        // If the slot is required and no transclusion content is provided then this call will throw an error
37330        $transclude(ngTranscludeCloneAttachFn, null, slotName);
37331
37332        // If the slot is optional and no transclusion content is provided then use the fallback content
37333        if (slotName && !$transclude.isSlotFilled(slotName)) {
37334          useFallbackContent();
37335        }
37336
37337        function ngTranscludeCloneAttachFn(clone, transcludedScope) {
37338          if (clone.length) {
37339            $element.append(clone);
37340          } else {
37341            useFallbackContent();
37342            // There is nothing linked against the transcluded scope since no content was available,
37343            // so it should be safe to clean up the generated scope.
37344            transcludedScope.$destroy();
37345          }
37346        }
37347
37348        function useFallbackContent() {
37349          // Since this is the fallback content rather than the transcluded content,
37350          // we link against the scope of this directive rather than the transcluded scope
37351          fallbackLinkFn($scope, function(clone) {
37352            $element.append(clone);
37353          });
37354        }
37355      };
37356    }
37357  };
37358}];
37359
37360/**
37361 * @ngdoc directive
37362 * @name script
37363 * @restrict E
37364 *
37365 * @description
37366 * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the
37367 * template can be used by {@link ng.directive:ngInclude `ngInclude`},
37368 * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the
37369 * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be
37370 * assigned through the element's `id`, which can then be used as a directive's `templateUrl`.
37371 *
37372 * @param {string} type Must be set to `'text/ng-template'`.
37373 * @param {string} id Cache name of the template.
37374 *
37375 * @example
37376  <example  name="script-tag">
37377    <file name="index.html">
37378      <script type="text/ng-template" id="/tpl.html">
37379        Content of the template.
37380      </script>
37381
37382      <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
37383      <div id="tpl-content" ng-include src="currentTpl"></div>
37384    </file>
37385    <file name="protractor.js" type="protractor">
37386      it('should load template defined inside script tag', function() {
37387        element(by.css('#tpl-link')).click();
37388        expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/);
37389      });
37390    </file>
37391  </example>
37392 */
37393var scriptDirective = ['$templateCache', function($templateCache) {
37394  return {
37395    restrict: 'E',
37396    terminal: true,
37397    compile: function(element, attr) {
37398      if (attr.type === 'text/ng-template') {
37399        var templateUrl = attr.id,
37400            text = element[0].text;
37401
37402        $templateCache.put(templateUrl, text);
37403      }
37404    }
37405  };
37406}];
37407
37408/* exported selectDirective, optionDirective */
37409
37410var noopNgModelController = { $setViewValue: noop, $render: noop };
37411
37412function chromeHack(optionElement) {
37413  // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459
37414  // Adding an <option selected="selected"> element to a <select required="required"> should
37415  // automatically select the new element
37416  if (optionElement[0].hasAttribute('selected')) {
37417    optionElement[0].selected = true;
37418  }
37419}
37420
37421/**
37422 * @ngdoc type
37423 * @name  select.SelectController
37424 * @description
37425 * The controller for the `<select>` directive. This provides support for reading
37426 * and writing the selected value(s) of the control and also coordinates dynamically
37427 * added `<option>` elements, perhaps by an `ngRepeat` directive.
37428 */
37429var SelectController =
37430        ['$element', '$scope', /** @this */ function($element, $scope) {
37431
37432  var self = this,
37433      optionsMap = new HashMap();
37434
37435  // If the ngModel doesn't get provided then provide a dummy noop version to prevent errors
37436  self.ngModelCtrl = noopNgModelController;
37437
37438  // The "unknown" option is one that is prepended to the list if the viewValue
37439  // does not match any of the options. When it is rendered the value of the unknown
37440  // option is '? XXX ?' where XXX is the hashKey of the value that is not known.
37441  //
37442  // We can't just jqLite('<option>') since jqLite is not smart enough
37443  // to create it in <select> and IE barfs otherwise.
37444  self.unknownOption = jqLite(window.document.createElement('option'));
37445  self.renderUnknownOption = function(val) {
37446    var unknownVal = '? ' + hashKey(val) + ' ?';
37447    self.unknownOption.val(unknownVal);
37448    $element.prepend(self.unknownOption);
37449    $element.val(unknownVal);
37450  };
37451
37452  $scope.$on('$destroy', function() {
37453    // disable unknown option so that we don't do work when the whole select is being destroyed
37454    self.renderUnknownOption = noop;
37455  });
37456
37457  self.removeUnknownOption = function() {
37458    if (self.unknownOption.parent()) self.unknownOption.remove();
37459  };
37460
37461
37462  // Read the value of the select control, the implementation of this changes depending
37463  // upon whether the select can have multiple values and whether ngOptions is at work.
37464  self.readValue = function readSingleValue() {
37465    self.removeUnknownOption();
37466    return $element.val();
37467  };
37468
37469
37470  // Write the value to the select control, the implementation of this changes depending
37471  // upon whether the select can have multiple values and whether ngOptions is at work.
37472  self.writeValue = function writeSingleValue(value) {
37473    if (self.hasOption(value)) {
37474      self.removeUnknownOption();
37475      $element.val(value);
37476      if (value === '') self.emptyOption.prop('selected', true); // to make IE9 happy
37477    } else {
37478      if (value == null && self.emptyOption) {
37479        self.removeUnknownOption();
37480        $element.val('');
37481      } else {
37482        self.renderUnknownOption(value);
37483      }
37484    }
37485  };
37486
37487
37488  // Tell the select control that an option, with the given value, has been added
37489  self.addOption = function(value, element) {
37490    // Skip comment nodes, as they only pollute the `optionsMap`
37491    if (element[0].nodeType === NODE_TYPE_COMMENT) return;
37492
37493    assertNotHasOwnProperty(value, '"option value"');
37494    if (value === '') {
37495      self.emptyOption = element;
37496    }
37497    var count = optionsMap.get(value) || 0;
37498    optionsMap.put(value, count + 1);
37499    self.ngModelCtrl.$render();
37500    chromeHack(element);
37501  };
37502
37503  // Tell the select control that an option, with the given value, has been removed
37504  self.removeOption = function(value) {
37505    var count = optionsMap.get(value);
37506    if (count) {
37507      if (count === 1) {
37508        optionsMap.remove(value);
37509        if (value === '') {
37510          self.emptyOption = undefined;
37511        }
37512      } else {
37513        optionsMap.put(value, count - 1);
37514      }
37515    }
37516  };
37517
37518  // Check whether the select control has an option matching the given value
37519  self.hasOption = function(value) {
37520    return !!optionsMap.get(value);
37521  };
37522
37523
37524  self.registerOption = function(optionScope, optionElement, optionAttrs, hasDynamicValueAttr, interpolateTextFn) {
37525
37526    if (hasDynamicValueAttr) {
37527      // either "value" is interpolated directly, or set by ngValue
37528      var oldVal;
37529      optionAttrs.$observe('value', function valueAttributeObserveAction(newVal) {
37530        if (isDefined(oldVal)) {
37531          self.removeOption(oldVal);
37532        }
37533        oldVal = newVal;
37534        self.addOption(newVal, optionElement);
37535      });
37536    } else if (interpolateTextFn) {
37537      // The text content is interpolated
37538      optionScope.$watch(interpolateTextFn, function interpolateWatchAction(newVal, oldVal) {
37539        optionAttrs.$set('value', newVal);
37540        if (oldVal !== newVal) {
37541          self.removeOption(oldVal);
37542        }
37543        self.addOption(newVal, optionElement);
37544      });
37545    } else {
37546      // The value attribute is static
37547      self.addOption(optionAttrs.value, optionElement);
37548    }
37549
37550    optionElement.on('$destroy', function() {
37551      self.removeOption(optionAttrs.value);
37552      self.ngModelCtrl.$render();
37553    });
37554  };
37555}];
37556
37557/**
37558 * @ngdoc directive
37559 * @name select
37560 * @restrict E
37561 *
37562 * @description
37563 * HTML `SELECT` element with angular data-binding.
37564 *
37565 * The `select` directive is used together with {@link ngModel `ngModel`} to provide data-binding
37566 * between the scope and the `<select>` control (including setting default values).
37567 * It also handles dynamic `<option>` elements, which can be added using the {@link ngRepeat `ngRepeat}` or
37568 * {@link ngOptions `ngOptions`} directives.
37569 *
37570 * When an item in the `<select>` menu is selected, the value of the selected option will be bound
37571 * to the model identified by the `ngModel` directive. With static or repeated options, this is
37572 * the content of the `value` attribute or the textContent of the `<option>`, if the value attribute is missing.
37573 * For dynamic options, use interpolation inside the `value` attribute or the `textContent`. Using
37574 * {@link ngValue ngValue} is also possible (as it sets the `value` attribute), and will take
37575 * precedence over `value` and `textContent`.
37576 *
37577 * <div class="alert alert-warning">
37578 * Note that the value of a `select` directive used without `ngOptions` is always a string.
37579 * When the model needs to be bound to a non-string value, you must either explicitly convert it
37580 * using a directive (see example below) or use `ngOptions` to specify the set of options.
37581 * This is because an option element can only be bound to string values at present.
37582 * </div>
37583 *
37584 * If the viewValue of `ngModel` does not match any of the options, then the control
37585 * will automatically add an "unknown" option, which it then removes when the mismatch is resolved.
37586 *
37587 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
37588 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
37589 * option. See example below for demonstration.
37590 *
37591 * <div class="alert alert-info">
37592 * In many cases, `ngRepeat` can be used on `<option>` elements instead of {@link ng.directive:ngOptions
37593 * ngOptions} to achieve a similar result. However, `ngOptions` provides some benef
37593its, such as
37594 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
37595 * comprehension expression, and additionally in reducing memory and increasing speed by not creating
37596 * a new scope for each repeated instance.
37597 * </div>
37598 *
37599 *
37600 * @param {string} ngModel Assignable angular expression to data-bind to.
37601 * @param {string=} name Property name of the form under which the control is published.
37602 * @param {string=} multiple Allows multiple options to be selected. The selected values will be
37603 *     bound to the model as an array.
37604 * @param {string=} required Sets `required` validation error key if the value is not entered.
37605 * @param {string=} ngRequired Adds required attribute and required validation constraint to
37606 * the element when the ngRequired expression evaluates to true. Use ngRequired instead of required
37607 * when you want to data-bind to the required attribute.
37608 * @param {string=} ngChange Angular expression to be executed when selected option(s) changes due to user
37609 *    interaction with the select element.
37610 * @param {string=} ngOptions sets the options that the select is populated with and defines what is
37611 * set on the model on selection. See {@link ngOptions `ngOptions`}.
37612 *
37613 * @example
37614 * ### Simple `select` elements with static options
37615 *
37616 * <example name="static-select" module="staticSelect">
37617 * <file name="index.html">
37618 * <div ng-controller="ExampleController">
37619 *   <form name="myForm">
37620 *     <label for="singleSelect"> Single select: </label><br>
37621 *     <select name="singleSelect" ng-model="data.singleSelect">
37622 *       <option value="option-1">Option 1</option>
37623 *       <option value="option-2">Option 2</option>
37624 *     </select><br>
37625 *
37626 *     <label for="singleSelect"> Single select with "not selected" option and dynamic option values: </label><br>
37627 *     <select name="singleSelect" id="singleSelect" ng-model="data.singleSelect">
37628 *       <option value="">---Please select---</option> <!-- not selected / blank option -->
37629 *       <option value="{{data.option1}}">Option 1</option> <!-- interpolation -->
37630 *       <option value="option-2">Option 2</option>
37631 *     </select><br>
37632 *     <button ng-click="forceUnknownOption()">Force unknown option</button><br>
37633 *     <tt>singleSelect = {{data.singleSelect}}</tt>
37634 *
37635 *     <hr>
37636 *     <label for="multipleSelect"> Multiple select: </label><br>
37637 *     <select name="multipleSelect" id="multipleSelect" ng-model="data.multipleSelect" multiple>
37638 *       <option value="option-1">Option 1</option>
37639 *       <option value="option-2">Option 2</option>
37640 *       <option value="option-3">Option 3</option>
37641 *     </select><br>
37642 *     <tt>multipleSelect = {{data.multipleSelect}}</tt><br/>
37643 *   </form>
37644 * </div>
37645 * </file>
37646 * <file name="app.js">
37647 *  angular.module('staticSelect', [])
37648 *    .controller('ExampleController', ['$scope', function($scope) {
37649 *      $scope.data = {
37650 *       singleSelect: null,
37651 *       multipleSelect: [],
37652 *       option1: 'option-1'
37653 *      };
37654 *
37655 *      $scope.forceUnknownOption = function() {
37656 *        $scope.data.singleSelect = 'nonsense';
37657 *      };
37658 *   }]);
37659 * </file>
37660 *</example>
37661 *
37662 * ### Using `ngRepeat` to generate `select` options
37663 * <example name="ngrepeat-select" module="ngrepeatSelect">
37664 * <file name="index.html">
37665 * <div ng-controller="ExampleController">
37666 *   <form name="myForm">
37667 *     <label for="repeatSelect"> Repeat select: </label>
37668 *     <select name="repeatSelect" id="repeatSelect" ng-model="data.repeatSelect">
37669 *       <option ng-repeat="option in data.availableOptions" value="{{option.id}}">{{option.name}}</option>
37670 *     </select>
37671 *   </form>
37672 *   <hr>
37673 *   <tt>repeatSelect = {{data.repeatSelect}}</tt><br/>
37674 * </div>
37675 * </file>
37676 * <file name="app.js">
37677 *  angular.module('ngrepeatSelect', [])
37678 *    .controller('ExampleController', ['$scope', function($scope) {
37679 *      $scope.data = {
37680 *       repeatSelect: null,
37681 *       availableOptions: [
37682 *         {id: '1', name: 'Option A'},
37683 *         {id: '2', name: 'Option B'},
37684 *         {id: '3', name: 'Option C'}
37685 *       ]
37686 *      };
37687 *   }]);
37688 * </file>
37689 *</example>
37690 *
37691 *
37692 * ### Using `select` with `ngOptions` and setting a default value
37693 * See the {@link ngOptions ngOptions documentation} for more `ngOptions` usage examples.
37694 *
37695 * <example name="select-with-default-values" module="defaultValueSelect">
37696 * <file name="index.html">
37697 * <div ng-controller="ExampleController">
37698 *   <form name="myForm">
37699 *     <label for="mySelect">Make a choice:</label>
37700 *     <select name="mySelect" id="mySelect"
37701 *       ng-options="option.name for option in data.availableOptions track by option.id"
37702 *       ng-model="data.selectedOption"></select>
37703 *   </form>
37704 *   <hr>
37705 *   <tt>option = {{data.selectedOption}}</tt><br/>
37706 * </div>
37707 * </file>
37708 * <file name="app.js">
37709 *  angular.module('defaultValueSelect', [])
37710 *    .controller('ExampleController', ['$scope', function($scope) {
37711 *      $scope.data = {
37712 *       availableOptions: [
37713 *         {id: '1', name: 'Option A'},
37714 *         {id: '2', name: 'Option B'},
37715 *         {id: '3', name: 'Option C'}
37716 *       ],
37717 *       selectedOption: {id: '3', name: 'Option C'} //This sets the default value of the select in the ui
37718 *       };
37719 *   }]);
37720 * </file>
37721 *</example>
37722 *
37723 *
37724 * ### Binding `select` to a non-string value via `ngModel` parsing / formatting
37725 *
37726 * <example name="select-with-non-string-options" module="nonStringSelect">
37727 *   <file name="index.html">
37728 *     <select ng-model="model.id" convert-to-number>
37729 *       <option value="0">Zero</option>
37730 *       <option value="1">One</option>
37731 *       <option value="2">Two</option>
37732 *     </select>
37733 *     {{ model }}
37734 *   </file>
37735 *   <file name="app.js">
37736 *     angular.module('nonStringSelect', [])
37737 *       .run(function($rootScope) {
37738 *         $rootScope.model = { id: 2 };
37739 *       })
37740 *       .directive('convertToNumber', function() {
37741 *         return {
37742 *           require: 'ngModel',
37743 *           link: function(scope, element, attrs, ngModel) {
37744 *             ngModel.$parsers.push(function(val) {
37745 *               return parseInt(val, 10);
37746 *             });
37747 *             ngModel.$formatters.push(function(val) {
37748 *               return '' + val;
37749 *             });
37750 *           }
37751 *         };
37752 *       });
37753 *   </file>
37754 *   <file name="protractor.js" type="protractor">
37755 *     it('should initialize to model', function() {
37756 *       expect(element(by.model('model.id')).$('option:checked').getText()).toEqual('Two');
37757 *     });
37758 *   </file>
37759 * </example>
37760 *
37761 */
37762var selectDirective = function() {
37763
37764  return {
37765    restrict: 'E',
37766    require: ['select', '?ngModel'],
37767    controller: SelectController,
37768    priority: 1,
37769    link: {
37770      pre: selectPreLink,
37771      post: selectPostLink
37772    }
37773  };
37774
37775  function selectPreLink(scope, element, attr, ctrls) {
37776
37777      // if ngModel is not defined, we don't need to do anything
37778      var ngModelCtrl = ctrls[1];
37779      if (!ngModelCtrl) return;
37780
37781      var selectCtrl = ctrls[0];
37782
37783      selectCtrl.ngModelCtrl = ngModelCtrl;
37784
37785      // When the selected item(s) changes we delegate getting the value of the select control
37786      // to the `readValue` method, which can be changed if the select can have multiple
37787      // selected values or if the options are being generated by `ngOptions`
37788      element.on('change', function() {
37789        scope.$apply(function() {
37790          ngModelCtrl.$setViewValue(selectCtrl.readValue());
37791        });
37792      });
37793
37794      // If the select allows multiple values then we need to modify how we read and write
37795      // values from and to the control; also what it means for the value to be empty and
37796      // we have to add an extra watch since ngModel doesn't work well with arrays - it
37797      // doesn't trigger rendering if only an item in the array changes.
37798      if (attr.multiple) {
37799
37800        // Read value now needs to check each option to see if it is selected
37801        selectCtrl.readValue = function readMultipleValue() {
37802          var array = [];
37803          forEach(element.find('option'), function(option) {
37804            if (option.selected) {
37805              array.push(option.value);
37806            }
37807          });
37808          return array;
37809        };
37810
37811        // Write value now needs to set the selected property of each matching option
37812        selectCtrl.writeValue = function writeMultipleValue(value) {
37813          var items = new HashMap(value);
37814          forEach(element.find('option'), function(option) {
37815            option.selected = isDefined(items.get(option.value));
37816          });
37817        };
37818
37819        // we have to do it on each watch since ngModel watches reference, but
37820        // we need to work of an array, so we need to see if anything was inserted/removed
37821        var lastView, lastViewRef = NaN;
37822        scope.$watch(function selectMultipleWatch() {
37823          if (lastViewRef === ngModelCtrl.$viewValue && !equals(lastView, ngModelCtrl.$viewValue)) {
37824            lastView = shallowCopy(ngModelCtrl.$viewValue);
37825            ngModelCtrl.$render();
37826          }
37827          lastViewRef = ngModelCtrl.$viewValue;
37828        });
37829
37830        // If we are a multiple select then value is now a collection
37831        // so the meaning of $isEmpty changes
37832        ngModelCtrl.$isEmpty = function(value) {
37833          return !value || value.length === 0;
37834        };
37835
37836      }
37837    }
37838
37839    function selectPostLink(scope, element, attrs, ctrls) {
37840      // if ngModel is not defined, we don't need to do anything
37841      var ngModelCtrl = ctrls[1];
37842      if (!ngModelCtrl) return;
37843
37844      var selectCtrl = ctrls[0];
37845
37846      // We delegate rendering to the `writeValue` method, which can be changed
37847      // if the select can have multiple selected values or if the options are being
37848      // generated by `ngOptions`.
37849      // This must be done in the postLink fn to prevent $render to be called before
37850      // all nodes have been linked correctly.
37851      ngModelCtrl.$render = function() {
37852        selectCtrl.writeValue(ngModelCtrl.$viewValue);
37853      };
37854    }
37855};
37856
37857
37858// The option directive is purely designed to communicate the existence (or lack of)
37859// of dynamically created (and destroyed) option elements to their containing select
vendor: 3,069 bytes, lines 37860-37930
37860// directive via its controller.
37861var optionDirective = ['$interpolate', function($interpolate) {
37862  return {
37863    restrict: 'E',
37864    priority: 100,
37865    compile: function(element, attr) {
37866      var hasDynamicValueAttr, interpolateTextFn;
37867
37868      if (isDefined(attr.ngValue)) {
37869        // If ngValue is defined, then the value attr will be set to the result of the expression,
37870        // and the selectCtrl must set up an observer
37871        hasDynamicValueAttr = true;
37872      } else if (isDefined(attr.value)) {
37873        // If the value attr contains an interpolation, the selectCtrl must set up an observer
37874        hasDynamicValueAttr = $interpolate(attr.value, true);
37875      } else {
37876        // If the value attribute is not defined then we fall back to the
37877        // text content of the option element, which may be interpolated
37878        interpolateTextFn = $interpolate(element.text(), true);
37879        if (!interpolateTextFn) {
37880          attr.$set('value', element.text());
37881        }
37882      }
37883
37884      return function(scope, element, attr) {
37885        // This is an optimization over using ^^ since we don't want to have to search
37886        // all the way to the root of the DOM for every single option element
37887        var selectCtrlName = '$selectController',
37888            parent = element.parent(),
37889            selectCtrl = parent.data(selectCtrlName) ||
37890              parent.parent().data(selectCtrlName); // in case we are in optgroup
37891
37892        if (selectCtrl) {
37893          selectCtrl.registerOption(scope, element, attr, hasDynamicValueAttr, interpolateTextFn);
37894        }
37895      };
37896    }
37897  };
37898}];
37899
37900/**
37901 * @ngdoc directive
37902 * @name ngRequired
37903 * @restrict A
37904 *
37905 * @description
37906 *
37907 * ngRequired adds the required {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
37908 * It is most often used for {@link input `input`} and {@link select `select`} controls, but can also be
37909 * applied to custom controls.
37910 *
37911 * The directive sets the `required` attribute on the element if the Angular expression inside
37912 * `ngRequired` evaluates to true. A special directive for setting `required` is necessary because we
37913 * cannot use interpolation inside `required`. See the {@link guide/interpolation interpolation guide}
37914 * for more info.
37915 *
37916 * The validator will set the `required` error key to true if the `required` attribute is set and
37917 * calling {@link ngModel.NgModelController#$isEmpty `NgModelController.$isEmpty`} with the
37918 * {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`} returns `true`. For example, the
37919 * `$isEmpty()` implementation for `input[text]` checks the length of the `$viewValue`. When developing
37920 * custom controls, `$isEmpty()` can be overwritten to account for a $viewValue that is not string-based.
37921 *
37922 * @example
37923 * <example name="ngRequiredDirective" module="ngRequiredExample">
37924 *   <file name="index.html">
37925 *     <script>
37926 *       angular.module('ngRequiredExample', [])
37927 *         .controller('ExampleController', ['$scope', function($scope) {
37928 *           $scope.required = true;
37929 *         }]);
37930 *     </script>
37931 *     <div ng-controller="ExampleController">
37932 *       <form name="form">
37933 *         <label for="required">Toggle required: </label>
37934 *         <input type="checkbox" ng-model="required" id="required" />
37935 *         <br>
37936 *         <label for="input">This input must be filled if `required` is true: </label>
37937 *         <input type="text" ng-model="model" id="input" name="input" ng-required="required" /><br>
37938 *         <hr>
37939 *         required error set? = <code>{{form.input.$error.required}}</code><br>
37940 *         model = <code>{{model}}</code>
37941 *       </form>
37942 *     </div>
37943 *   </file>
37944 *   <file name="protractor.js" type="protractor">
37945       var required = element(by.binding('form.input.$error.required'));
37946       var model = element(by.binding('model'));
37947       var input = element(by.id('input'));
37948
37949       it('should set the required error', function() {
37950         expect(required.getText()).toContain('true');
37951
37952         input.sendKeys('123');
37953         expect(required.getText()).not.toContain('true');
37954         expect(model.getText()).toContain('123');
37955       });
37956 *   </file>
37957 * </example>
37958 */
37959var requiredDirective = function() {
37960  return {
37961    restrict: 'A',
37962    require: '?ngModel',
37963    link: function(scope, elm, attr, ctrl) {
37964      if (!ctrl) return;
37965      attr.required = true; // force truthy in case we are on non input element
37966
37967      ctrl.$validators.required = function(modelValue, viewValue) {
37968        return !attr.required || !ctrl.$isEmpty(viewValue);
37969      };
37970
37971      attr.$observe('required', function() {
37972        ctrl.$validate();
37973      });
37974    }
37975  };
37976};
37977
37978/**
37979 * @ngdoc directive
37980 * @name ngPattern
37981 *
37982 * @description
37983 *
37984 * ngPattern adds the pattern {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
37985 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
37986 *
37987 * The validator sets the `pattern` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
37988 * does not match a RegExp which is obtained by evaluating the Angular expression given in the
37989 * `ngPattern` attribute value:
37990 * * If the expression evaluates to a RegExp object, then this is used directly.
37991 * * If the expression evaluates to a string, then it will be converted to a RegExp after wrapping it
37992 * in `^` and `$` characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
37993 *
37994 * <div class="alert alert-info">
37995 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
37996 * start at the index of the last search's match, thus not taking the whole input value into
37997 * account.
37998 * </div>
37999 *
38000 * <div class="alert alert-info">
38001 * **Note:** This directive is also added when the plain `pattern` attribute is used, with two
38002 * differences:
38003 * <ol>
38004 *   <li>
38005 *     `ngPattern` does not set the `pattern` attribute and therefore HTML5 constraint validation is
38006 *     not available.
38007 *   </li>
38008 *   <li>
38009 *     The `ngPattern` attribute must be an expression, while the `pattern` value must be
38010 *     interpolated.
38011 *   </li>
38012 * </ol>
38013 * </div>
38014 *
38015 * @example
38016 * <example name="ngPatternDirective" module="ngPatternExample">
38017 *   <file name="index.html">
38018 *     <script>
38019 *       angular.module('ngPatternExample', [])
38020 *         .controller('ExampleController', ['$scope', function($scope) {
38021 *           $scope.regex = '\\d+';
38022 *         }]);
38023 *     </script>
38024 *     <div ng-controller="ExampleController">
38025 *       <form name="form">
38026 *         <label for="regex">Set a pattern (regex string): </label>
38027 *         <input type="text" ng-model="regex" id="regex" />
38028 *         <br>
38029 *         <label for="input">This input is restricted by the current pattern: </label>
38030 *         <input type="text" ng-model="model" id="input" name="input" ng-pattern="regex" /><br>
38031 *         <hr>
38032 *         input valid? = <code>{{form.input.$valid}}</code><br>
38033 *         model = <code>{{model}}</code>
38034 *       </form>
38035 *     </div>
38036 *   </file>
38037 *   <file name="protractor.js" type="protractor">
38038       var model = element(by.binding('model'));
38039       var input = element(by.id('input'));
38040
38041       it('should validate the input with the default pattern', function() {
38042         input.sendKeys('aaa');
38043         expect(model.getText()).not.toContain('aaa');
38044
38045         input.clear().then(function() {
38046           input.sendKeys('123');
38047           expect(model.getText()).toContain('123');
38048         });
38049       });
38050 *   </file>
38051 * </example>
38052 */
38053var patternDirective = function() {
38054  return {
38055    restrict: 'A',
38056    require: '?ngModel',
38057    link: function(scope, elm, attr, ctrl) {
38058      if (!ctrl) return;
38059
38060      var regexp, patternExp = attr.ngPattern || attr.pattern;
38061      attr.$observe('pattern', function(regex) {
38062        if (isString(regex) && regex.length > 0) {
38063          regex = new RegExp('^' + regex + '$');
38064        }
38065
38066        if (regex && !regex.test) {
38067          throw minErr('ngPattern')('noregexp',
38068            'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp,
38069            regex, startingTag(elm));
38070        }
38071
38072        regexp = regex || undefined;
38073        ctrl.$validate();
38074      });
38075
38076      ctrl.$validators.pattern = function(modelValue, viewValue) {
38077        // HTML5 pattern constraint validates the input value, so we validate the viewValue
38078        return ctrl.$isEmpty(viewValue) || isUndefined(regexp) || regexp.test(viewValue);
38079      };
38080    }
38081  };
38082};
38083
38084/**
38085 * @ngdoc directive
38086 * @name ngMaxlength
38087 *
38088 * @description
38089 *
38090 * ngMaxlength adds the maxlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
38091 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
38092 *
38093 * The validator sets the `maxlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
38094 * is longer than the integer obtained by evaluating the Angular expression given in the
38095 * `ngMaxlength` attribute value.
38096 *
38097 * <div class="alert alert-info">
38098 * **Note:** This directive is also added when the plain `maxlength` attribute is used, with two
38099 * differences:
38100 * <ol>
38101 *   <li>
38102 *     `ngMaxlength` does not set the `maxlength` attribute and therefore HTML5 constraint
38103 *     validation is not available.
38104 *   </li>
38105 *   <li>
38106 *     The `ngMaxlength` attribute must be an expression, while the `maxlength` value must be
38107 *     interpolated.
38108 *   </li>
38109 * </ol>
38110 * </div>
38111 *
38112 * @example
38113 * <example name="ngMaxlengthDirective" module="ngMaxlengthExample">
38114 *   <file name="index.html">
38115 *     <script>
38116 *       angular.module('ngMaxlengthExample', [])
38117 *         .controller('ExampleController', ['$scope', function($scope) {
38118 *           $scope.maxlength = 5;
38119 *         }]);
38120 *     </script>
38121 *     <div ng-controller="ExampleController">
38122 *       <form name="form">
38123 *         <label for="maxlength">Set a maxlength: </label>
38124 *         <input type="number" ng-model="maxlength" id="maxlength" />
38125 *         <br>
38126 *         <label for="input">This input is restricted by the current maxlength: </label>
38127 *         <input type="text" ng-model="model" id="input" name="input" ng-maxlength="maxlength" /><br>
38128 *         <hr>
38129 *         input valid? = <code>{{form.input.$valid}}</code><br>
38130 *         model = <code>{{model}}</code>
38131 *       </form>
38132 *     </div>
38133 *   </file>
38134 *   <file name="protractor.js" type="protractor">
38135       var model = element(by.binding('model'));
38136       var input = element(by.id('input'));
38137
38138       it('should validate the input with the default maxlength', function() {
38139         input.sendKeys('abcdef');
38140         expect(model.getText()).not.toContain('abcdef');
38141
38142         input.clear().then(function() {
38143           input.sendKeys('abcde');
38144           expect(model.getText()).toContain('abcde');
38145         });
38146       });
38147 *   </file>
38148 * </example>
38149 */
38150var maxlengthDirective = function() {
38151  return {
38152    restrict: 'A',
38153    require: '?ngModel',
38154    link: function(scope, elm, attr, ctrl) {
38155      if (!ctrl) return;
38156
38157      var maxlength = -1;
38158      attr.$observe('maxlength', function(value) {
38159        var intVal = toInt(value);
38160        maxlength = isNumberNaN(intVal) ? -1 : intVal;
38161        ctrl.$validate();
38162      });
38163      ctrl.$validators.maxlength = function(modelValue, viewValue) {
38164        return (maxlength < 0) || ctrl.$isEmpty(viewValue) || (viewValue.length <= maxlength);
38165      };
38166    }
38167  };
38168};
38169
38170/**
38171 * @ngdoc directive
38172 * @name ngMinlength
38173 *
38174 * @description
38175 *
38176 * ngMinlength adds the minlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
38177 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
38178 *
38179 * The validator sets the `minlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
38180 * is shorter than the integer obtained by evaluating the Angular expression given in the
38181 * `ngMinlength` attribute value.
38182 *
38183 * <div class="alert alert-info">
38184 * **Note:** This directive is also added when the plain `minlength` attribute is used, with two
38185 * differences:
38186 * <ol>
38187 *   <li>
38188 *     `ngMinlength` does not set the `minlength` attribute and therefore HTML5 constraint
38189 *     validation is not available.
38190 *   </li>
38191 *   <li>
38192 *     The `ngMinlength` value must be an expression, while the `minlength` value must be
38193 *     interpolated.
38194 *   </li>
38195 * </ol>
38196 * </div>
38197 *
38198 * @example
38199 * <example name="ngMinlengthDirective" module="ngMinlengthExample">
38200 *   <file name="index.html">
38201 *     <script>
38202 *       angular.module('ngMinlengthExample', [])
38203 *         .controller('ExampleController', ['$scope', function($scope) {
38204 *           $scope.minlength = 3;
38205 *         }]);
38206 *     </script>
38207 *     <div ng-controller="ExampleController">
38208 *       <form name="form">
38209 *         <label for="minlength">Set a minlength: </label>
38210 *         <input type="number" ng-model="minlength" id="minlength" />
38211 *         <br>
38212 *         <label for="input">This input is restricted by the current minlength: </label>
38213 *         <input type="text" ng-model="model" id="input" name="input" ng-minlength="minlength" /><br>
38214 *         <hr>
38215 *         input valid? = <code>{{form.input.$valid}}</code><br>
38216 *         model = <code>{{model}}</code>
38217 *       </form>
38218 *     </div>
38219 *   </file>
38220 *   <file name="protractor.js" type="protractor">
38221       var model = element(by.binding('model'));
38222       var input = element(by.id('input'));
38223
38224       it('should validate the input with the default minlength', function() {
38225         input.sendKeys('ab');
38226         expect(model.getText()).not.toContain('ab');
38227
38228         input.sendKeys('abc');
38229         expect(model.getText()).toContain('abc');
38230       });
38231 *   </file>
38232 * </example>
38233 */
38234var minlengthDirective = function() {
38235  return {
38236    restrict: 'A',
38237    require: '?ngModel',
38238    link: function(scope, elm, attr, ctrl) {
38239      if (!ctrl) return;
38240
38241      var minlength = 0;
38242      attr.$observe('minlength', function(value) {
38243        minlength = toInt(value) || 0;
38244        ctrl.$validate();
38245      });
38246      ctrl.$validators.minlength = function(modelValue, viewValue) {
38247        return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength;
38248      };
38249    }
38250  };
38251};
38252
38253if (window.angular.bootstrap) {
38254  //AngularJS is already loaded, so we can return here...
38255  if (window.console) {
38256    console.log('WARNING: Tried to load angular more than once.');
38257  }
38258  return;
38259}
38260
38261//try to bind to jquery now so that one can write jqLite(document).ready()
38262//but we will rebind on bootstrap again.
38263bindJQuery();
38264
38265publishExternalAPI(angular);
38266
38267angular.module("ngLocale", [], ["$provide", function($provide) {
38268var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
38269function getDecimals(n) {
38270  n = n + '';
38271  var i = n.indexOf('.');
38272  return (i == -1) ? 0 : n.length - i - 1;
38273}
38274
38275function getVF(n, opt_precision) {
38276  var v = opt_precision;
38277
38278  if (undefined === v) {
38279    v = Math.min(getDecimals(n), 3);
38280  }
38281
38282  var base = Math.pow(10, v);
38283  var f = ((n * base) | 0) % base;
38284  return {v: v, f: f};
38285}
38286
38287$provide.value("$locale", {
38288  "DATETIME_FORMATS": {
38289    "AMPMS": [
38290      "AM",
38291      "PM"
38292    ],
38293    "DAY": [
38294      "Sunday",
38295      "Monday",
38296      "Tuesday",
38297      "Wednesday",
38298      "Thursday",
38299      "Friday",
38300      "Saturday"
38301    ],
38302    "ERANAMES": [
38303      "Before Christ",
38304      "Anno Domini"
38305    ],
38306    "ERAS": [
38307      "BC",
38308      "AD"
38309    ],
38310    "FIRSTDAYOFWEEK": 6,
38311    "MONTH": [
38312      "January",
38313      "February",
38314      "March",
38315      "April",
38316      "May",
38317      "June",
38318      "July",
38319      "August",
38320      "September",
38321      "October",
38322      "November",
38323      "December"
38324    ],
38325    "SHORTDAY": [
38326      "Sun",
38327      "Mon",
38328      "Tue",
38329      "Wed",
38330      "Thu",
38331      "Fri",
38332      "Sat"
38333    ],
38334    "SHORTMONTH": [
38335      "Jan",
38336      "Feb",
38337      "Mar",
38338      "Apr",
38339      "May",
38340      "Jun",
38341      "Jul",
38342      "Aug",
38343      "Sep",
38344      "Oct",
38345      "Nov",
38346      "Dec"
38347    ],
38348    "STANDALONEMONTH": [
38349      "January",
38350      "February",
38351      "March",
38352      "April",
38353      "May",
38354      "June",
38355      "July",
38356      "August",
38357      "September",
38358      "October",
38359      "November",
38360      "December"
38361    ],
38362    "WEEKENDRANGE": [
38363      5,
38364      6
38365    ],
38366    "fullDate": "EEEE, MMMM d, y",
38367    "longDate": "MMMM d, y",
38368    "medium": "MMM d, y h:mm:ss a",
38369    "mediumDate": "MMM d, y",
38370    "mediumTime": "h:mm:ss a",
38371    "short": "M/d/yy h:mm a",
38372    "shortDate": "M/d/yy",
38373    "shortTime": "h:mm a"
38374  },
38375  "NUMBER_FORMATS": {
38376    "CURRENCY_SYM": "$",
38377    "DECIMAL_SEP": ".",
38378    "GROUP_SEP": ",",
38379    "PATTERNS": [
38380      {
38381        "gSize": 3,
38382        "lgSize": 3,
38383        "maxFrac": 3,
38384        "minFrac": 0,
38385        "minInt": 1,
38386        "negPre": "-",
38387        "negSuf": "",
38388        "posPre": "",
38389        "posSuf": ""
38390      },
38391      {
38392        "gSize": 3,
38393        "lgSize": 3,
38394        "maxFrac": 2,
38395        "minFrac": 2,
38396        "minInt": 1,
38397        "negPre": "-\u00a4",
38398        "negSuf": "",
38399        "posPre": "\u00a4",
38400        "posSuf": ""
38401      }
38402    ]
38403  },
38404  "id": "en-us",
38405  "localeID": "en_US",
38406  "pluralCat": function(n, opt_precision) {  var i = n | 0;  var vf = getVF(n, opt_precision);  if (i == 1 && vf.v == 0) {    return PLURAL_CATEGORY.ONE;  }  return PLURAL_CATEGORY.OTHER;}
38407});
38408}]);
38409
38410  jqLite(window.document).ready(function() {
38411    angularInit(window.document, bootstrap);
38412  });
38413
38414})(window);
38415
38416!window.angular.$$csp().noInlineStyle && window.angular.element(document.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:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}.ng-animate-shim{visibility:hidden;}.ng-anchor{position:absolute;}</style>');
38417/**
38418 * @license AngularJS v1.5.11
38419 * (c) 2010-2017 Google, Inc. http://angularjs.org
38420 * License: MIT
38421 */
38422(function(window, angular) {'use strict';
38423
38424var ELEMENT_NODE = 1;
38425var COMMENT_NODE = 8;
38426
38427var ADD_CLASS_SUFFIX = '-add';
38428var REMOVE_CLASS_SUFFIX = '-remove';
38429var EVENT_CLASS_PREFIX = 'ng-';
38430var ACTIVE_CLASS_SUFFIX = '-active';
38431var PREPARE_CLASS_SUFFIX = '-prepare';
38432
38433var NG_ANIMATE_CLASSNAME = 'ng-animate';
38434var NG_ANIMATE_CHILDREN_DATA = '$$ngAnimateChildren';
38435
38436// Detect proper transitionend/animationend event names.
38437var CSS_PREFIX = '', TRANSITION_PROP, TRANSITIONEND_EVENT, ANIMATION_PROP, ANIMATIONEND_EVENT;
38438
38439// If unprefixed events are not supported but webkit-prefixed are, use the latter.
vendor: 13,270 bytes, lines 38440-38902
38440// Otherwise, just use W3C names, browsers not supporting them at all will just ignore them.
38441// Note: Chrome implements `window.onwebkitanimationend` and doesn't implement `window.onanimationend`
38442// but at the same time dispatches the `animationend` event and not `webkitAnimationEnd`.
38443// Register both events in case `window.onanimationend` is not supported because of that,
38444// do the same for `transitionend` as Safari is likely to exhibit similar behavior.
38445// Also, the only modern browser that uses vendor prefixes for transitions/keyframes is webkit
38446// therefore there is no reason to test anymore for other vendor prefixes:
38447// http://caniuse.com/#search=transition
38448if ((window.ontransitionend === undefined) && (window.onwebkittransitionend !== undefined)) {
38449  CSS_PREFIX = '-webkit-';
38450  TRANSITION_PROP = 'WebkitTransition';
38451  TRANSITIONEND_EVENT = 'webkitTransitionEnd transitionend';
38452} else {
38453  TRANSITION_PROP = 'transition';
38454  TRANSITIONEND_EVENT = 'transitionend';
38455}
38456
38457if ((window.onanimationend === undefined) && (window.onwebkitanimationend !== undefined)) {
38458  CSS_PREFIX = '-webkit-';
38459  ANIMATION_PROP = 'WebkitAnimation';
38460  ANIMATIONEND_EVENT = 'webkitAnimationEnd animationend';
38461} else {
38462  ANIMATION_PROP = 'animation';
38463  ANIMATIONEND_EVENT = 'animationend';
38464}
38465
38466var DURATION_KEY = 'Duration';
38467var PROPERTY_KEY = 'Property';
38468var DELAY_KEY = 'Delay';
38469var TIMING_KEY = 'TimingFunction';
38470var ANIMATION_ITERATION_COUNT_KEY = 'IterationCount';
38471var ANIMATION_PLAYSTATE_KEY = 'PlayState';
38472var SAFE_FAST_FORWARD_DURATION_VALUE = 9999;
38473
38474var ANIMATION_DELAY_PROP = ANIMATION_PROP + DELAY_KEY;
38475var ANIMATION_DURATION_PROP = ANIMATION_PROP + DURATION_KEY;
38476var TRANSITION_DELAY_PROP = TRANSITION_PROP + DELAY_KEY;
38477var TRANSITION_DURATION_PROP = TRANSITION_PROP + DURATION_KEY;
38478
38479var ngMinErr = angular.$$minErr('ng');
38480function assertArg(arg, name, reason) {
38481  if (!arg) {
38482    throw ngMinErr('areq', 'Argument \'{0}\' is {1}', (name || '?'), (reason || 'required'));
38483  }
38484  return arg;
38485}
38486
38487function mergeClasses(a,b) {
38488  if (!a && !b) return '';
38489  if (!a) return b;
38490  if (!b) return a;
38491  if (isArray(a)) a = a.join(' ');
38492  if (isArray(b)) b = b.join(' ');
38493  return a + ' ' + b;
38494}
38495
38496function packageStyles(options) {
38497  var styles = {};
38498  if (options && (options.to || options.from)) {
38499    styles.to = options.to;
38500    styles.from = options.from;
38501  }
38502  return styles;
38503}
38504
38505function pendClasses(classes, fix, isPrefix) {
38506  var className = '';
38507  classes = isArray(classes)
38508      ? classes
38509      : classes && isString(classes) && classes.length
38510          ? classes.split(/\s+/)
38511          : [];
38512  forEach(classes, function(klass, i) {
38513    if (klass && klass.length > 0) {
38514      className += (i > 0) ? ' ' : '';
38515      className += isPrefix ? fix + klass
38516                            : klass + fix;
38517    }
38518  });
38519  return className;
38520}
38521
38522function removeFromArray(arr, val) {
38523  var index = arr.indexOf(val);
38524  if (val >= 0) {
38525    arr.splice(index, 1);
38526  }
38527}
38528
38529function stripCommentsFromElement(element) {
38530  if (element instanceof jqLite) {
38531    switch (element.length) {
38532      case 0:
38533        return element;
38534
38535      case 1:
38536        // there is no point of stripping anything if the element
38537        // is the only element within the jqLite wrapper.
38538        // (it's important that we retain the element instance.)
38539        if (element[0].nodeType === ELEMENT_NODE) {
38540          return element;
38541        }
38542        break;
38543
38544      default:
38545        return jqLite(extractElementNode(element));
38546    }
38547  }
38548
38549  if (element.nodeType === ELEMENT_NODE) {
38550    return jqLite(element);
38551  }
38552}
38553
38554function extractElementNode(element) {
38555  if (!element[0]) return element;
38556  for (var i = 0; i < element.length; i++) {
38557    var elm = element[i];
38558    if (elm.nodeType === ELEMENT_NODE) {
38559      return elm;
38560    }
38561  }
38562}
38563
38564function $$addClass($$jqLite, element, className) {
38565  forEach(element, function(elm) {
38566    $$jqLite.addClass(elm, className);
38567  });
38568}
38569
38570function $$removeClass($$jqLite, element, className) {
38571  forEach(element, function(elm) {
38572    $$jqLite.removeClass(elm, className);
38573  });
38574}
38575
38576function applyAnimationClassesFactory($$jqLite) {
38577  return function(element, options) {
38578    if (options.addClass) {
38579      $$addClass($$jqLite, element, options.addClass);
38580      options.addClass = null;
38581    }
38582    if (options.removeClass) {
38583      $$removeClass($$jqLite, element, options.removeClass);
38584      options.removeClass = null;
38585    }
38586  };
38587}
38588
38589function prepareAnimationOptions(options) {
38590  options = options || {};
38591  if (!options.$$prepared) {
38592    var domOperation = options.domOperation || noop;
38593    options.domOperation = function() {
38594      options.$$domOperationFired = true;
38595      domOperation();
38596      domOperation = noop;
38597    };
38598    options.$$prepared = true;
38599  }
38600  return options;
38601}
38602
38603function applyAnimationStyles(element, options) {
38604  applyAnimationFromStyles(element, options);
38605  applyAnimationToStyles(element, options);
38606}
38607
38608function applyAnimationFromStyles(element, options) {
38609  if (options.from) {
38610    element.css(options.from);
38611    options.from = null;
38612  }
38613}
38614
38615function applyAnimationToStyles(element, options) {
38616  if (options.to) {
38617    element.css(options.to);
38618    options.to = null;
38619  }
38620}
38621
38622function mergeAnimationDetails(element, oldAnimation, newAnimation) {
38623  var target = oldAnimation.options || {};
38624  var newOptions = newAnimation.options || {};
38625
38626  var toAdd = (target.addClass || '') + ' ' + (newOptions.addClass || '');
38627  var toRemove = (target.removeClass || '') + ' ' + (newOptions.removeClass || '');
38628  var classes = resolveElementClasses(element.attr('class'), toAdd, toRemove);
38629
38630  if (newOptions.preparationClasses) {
38631    target.preparationClasses = concatWithSpace(newOptions.preparationClasses, target.preparationClasses);
38632    delete newOptions.preparationClasses;
38633  }
38634
38635  // noop is basically when there is no callback; otherwise something has been set
38636  var realDomOperation = target.domOperation !== noop ? target.domOperation : null;
38637
38638  extend(target, newOptions);
38639
38640  // TODO(matsko or sreeramu): proper fix is to maintain all animation callback in array and call at last,but now only leave has the callback so no issue with this.
38641  if (realDomOperation) {
38642    target.domOperation = realDomOperation;
38643  }
38644
38645  if (classes.addClass) {
38646    target.addClass = classes.addClass;
38647  } else {
38648    target.addClass = null;
38649  }
38650
38651  if (classes.removeClass) {
38652    target.removeClass = classes.removeClass;
38653  } else {
38654    target.removeClass = null;
38655  }
38656
38657  oldAnimation.addClass = target.addClass;
38658  oldAnimation.removeClass = target.removeClass;
38659
38660  return target;
38661}
38662
38663function resolveElementClasses(existing, toAdd, toRemove) {
38664  var ADD_CLASS = 1;
38665  var REMOVE_CLASS = -1;
38666
38667  var flags = {};
38668  existing = splitClassesToLookup(existing);
38669
38670  toAdd = splitClassesToLookup(toAdd);
38671  forEach(toAdd, function(value, key) {
38672    flags[key] = ADD_CLASS;
38673  });
38674
38675  toRemove = splitClassesToLookup(toRemove);
38676  forEach(toRemove, function(value, key) {
38677    flags[key] = flags[key] === ADD_CLASS ? null : REMOVE_CLASS;
38678  });
38679
38680  var classes = {
38681    addClass: '',
38682    removeClass: ''
38683  };
38684
38685  forEach(flags, function(val, klass) {
38686    var prop, allow;
38687    if (val === ADD_CLASS) {
38688      prop = 'addClass';
38689      allow = !existing[klass] || existing[klass + REMOVE_CLASS_SUFFIX];
38690    } else if (val === REMOVE_CLASS) {
38691      prop = 'removeClass';
38692      allow = existing[klass] || existing[klass + ADD_CLASS_SUFFIX];
38693    }
38694    if (allow) {
38695      if (classes[prop].length) {
38696        classes[prop] += ' ';
38697      }
38698      classes[prop] += klass;
38699    }
38700  });
38701
38702  function splitClassesToLookup(classes) {
38703    if (isString(classes)) {
38704      classes = classes.split(' ');
38705    }
38706
38707    var obj = {};
38708    forEach(classes, function(klass) {
38709      // sometimes the split leaves empty string values
38710      // incase extra spaces were applied to the options
38711      if (klass.length) {
38712        obj[klass] = true;
38713      }
38714    });
38715    return obj;
38716  }
38717
38718  return classes;
38719}
38720
38721function getDomNode(element) {
38722  return (element instanceof jqLite) ? element[0] : element;
38723}
38724
38725function applyGeneratedPreparationClasses(element, event, options) {
38726  var classes = '';
38727  if (event) {
38728    classes = pendClasses(event, EVENT_CLASS_PREFIX, true);
38729  }
38730  if (options.addClass) {
38731    classes = concatWithSpace(classes, pendClasses(options.addClass, ADD_CLASS_SUFFIX));
38732  }
38733  if (options.removeClass) {
38734    classes = concatWithSpace(classes, pendClasses(options.removeClass, REMOVE_CLASS_SUFFIX));
38735  }
38736  if (classes.length) {
38737    options.preparationClasses = classes;
38738    element.addClass(classes);
38739  }
38740}
38741
38742function clearGeneratedClasses(element, options) {
38743  if (options.preparationClasses) {
38744    element.removeClass(options.preparationClasses);
38745    options.preparationClasses = null;
38746  }
38747  if (options.activeClasses) {
38748    element.removeClass(options.activeClasses);
38749    options.activeClasses = null;
38750  }
38751}
38752
38753function blockTransitions(node, duration) {
38754  // we use a negative delay value since it performs blocking
38755  // yet it doesn't kill any existing transitions running on the
38756  // same element which makes this safe for class-based animations
38757  var value = duration ? '-' + duration + 's' : '';
38758  applyInlineStyle(node, [TRANSITION_DELAY_PROP, value]);
38759  return [TRANSITION_DELAY_PROP, value];
38760}
38761
38762function blockKeyframeAnimations(node, applyBlock) {
38763  var value = applyBlock ? 'paused' : '';
38764  var key = ANIMATION_PROP + ANIMATION_PLAYSTATE_KEY;
38765  applyInlineStyle(node, [key, value]);
38766  return [key, value];
38767}
38768
38769function applyInlineStyle(node, styleTuple) {
38770  var prop = styleTuple[0];
38771  var value = styleTuple[1];
38772  node.style[prop] = value;
38773}
38774
38775function concatWithSpace(a,b) {
38776  if (!a) return b;
38777  if (!b) return a;
38778  return a + ' ' + b;
38779}
38780
38781var $$rAFSchedulerFactory = ['$$rAF', function($$rAF) {
38782  var queue, cancelFn;
38783
38784  function scheduler(tasks) {
38785    // we make a copy since RAFScheduler mutates the state
38786    // of the passed in array variable and this would be difficult
38787    // to track down on the outside code
38788    queue = queue.concat(tasks);
38789    nextTick();
38790  }
38791
38792  queue = scheduler.queue = [];
38793
38794  /* waitUntilQuiet does two things:
38795   * 1. It will run the FINAL `fn` value only when an uncanceled RAF has passed through
38796   * 2. It will delay the next wave of tasks from running until the quiet `fn` has run.
38797   *
38798   * The motivation here is that animation code can request more time from the scheduler
38799   * before the next wave runs. This allows for certain DOM properties such as classes to
38800   * be resolved in time for the next animation to run.
38801   */
38802  scheduler.waitUntilQuiet = function(fn) {
38803    if (cancelFn) cancelFn();
38804
38805    cancelFn = $$rAF(function() {
38806      cancelFn = null;
38807      fn();
38808      nextTick();
38809    });
38810  };
38811
38812  return scheduler;
38813
38814  function nextTick() {
38815    if (!queue.length) return;
38816
38817    var items = queue.shift();
38818    for (var i = 0; i < items.length; i++) {
38819      items[i]();
38820    }
38821
38822    if (!cancelFn) {
38823      $$rAF(function() {
38824        if (!cancelFn) nextTick();
38825      });
38826    }
38827  }
38828}];
38829
38830/**
38831 * @ngdoc directive
38832 * @name ngAnimateChildren
38833 * @restrict AE
38834 * @element ANY
38835 *
38836 * @description
38837 *
38838 * ngAnimateChildren allows you to specify that children of this element should animate even if any
38839 * of the children's parents are currently animating. By default, when an element has an active `enter`, `leave`, or `move`
38840 * (structural) animation, child elements that also have an active structural animation are not animated.
38841 *
38842 * Note that even if `ngAnimateChildren` is set, no child animations will run when the parent element is removed from the DOM (`leave` animation).
38843 *
38844 *
38845 * @param {string} ngAnimateChildren If the value is empty, `true` or `on`,
38846 *     then child animations are allowed. If the value is `false`, child animations are not allowed.
38847 *
38848 * @example
38849 * <example module="ngAnimateChildren" name="ngAnimateChildren" deps="angular-animate.js" animations="true">
38850     <file name="index.html">
38851       <div ng-controller="MainController as main">
38852         <label>Show container? <input type="checkbox" ng-model="main.enterElement" /></label>
38853         <label>Animate children? <input type="checkbox" ng-model="main.animateChildren" /></label>
38854         <hr>
38855         <div ng-animate-children="{{main.animateChildren}}">
38856           <div ng-if="main.enterElement" class="container">
38857             List of items:
38858             <div ng-repeat="item in [0, 1, 2, 3]" class="item">Item {{item}}</div>
38859           </div>
38860         </div>
38861       </div>
38862     </file>
38863     <file name="animations.css">
38864
38865      .container.ng-enter,
38866      .container.ng-leave {
38867        transition: all ease 1.5s;
38868      }
38869
38870      .container.ng-enter,
38871      .container.ng-leave-active {
38872        opacity: 0;
38873      }
38874
38875      .container.ng-leave,
38876      .container.ng-enter-active {
38877        opacity: 1;
38878      }
38879
38880      .item {
38881        background: firebrick;
38882        color: #FFF;
38883        margin-bottom: 10px;
38884      }
38885
38886      .item.ng-enter,
38887      .item.ng-leave {
38888        transition: transform 1.5s ease;
38889      }
38890
38891      .item.ng-enter {
38892        transform: translateX(50px);
38893      }
38894
38895      .item.ng-enter-active {
38896        transform: translateX(0);
38897      }
38898    </file>
38899    <file name="script.js">
38900      angular.module('ngAnimateChildren', ['ngAnimate'])
38901        .controller('MainController', function MainController() {
38902          this.animateChildren = false;
38903          this.enterElement = false;
38904        });
38905    </file>
38906  </example>
38907 */
38908var $$AnimateChildrenDirective = ['$interpolate', function($interpolate) {
38909  return {
38910    link: function(scope, element, attrs) {
38911      var val = attrs.ngAnimateChildren;
38912      if (isString(val) && val.length === 0) { //empty attribute
38913        element.data(NG_ANIMATE_CHILDREN_DATA, true);
38914      } else {
38915        // Interpolate and set the value, so that it is available to
38916        // animations that run right after compilation
38917        setData($interpolate(val)(scope));
38918        attrs.$observe('ngAnimateChildren', setData);
38919      }
38920
38921      function setData(value) {
38922        value = value === 'on' || value === 'true';
38923        element.data(NG_ANIMATE_CHILDREN_DATA, value);
38924      }
38925    }
38926  };
38927}];
38928
38929/* exported $AnimateCssProvider */
38930
38931var ANIMATE_TIMER_KEY = '$$animateCss';
38932
38933/**
38934 * @ngdoc service
38935 * @name $animateCss
38936 * @kind object
38937 *
38938 * @description
38939 * The `$animateCss` service is a useful utility to trigger customized CSS-based transitions/keyframes
38940 * from a JavaScript-based animation or directly from a directive. The purpose of `$animateCss` is NOT
38941 * to side-step how `$animate` and ngAnimate work, but the goal is to allow pre-existing animations or
38942 * directives to create more complex animations that can be purely driven using CSS code.
38943 *
38944 * Note that only browsers that support CSS transitions and/or keyframe animations are capable of
38945 * rendering animations triggered via `$animateCss` (bad news for IE9 and lower).
38946 *
38947 * ## Usage
38948 * Once again, `$animateCss` is designed to be used inside of a registered JavaScript animation that
38949 * is powered by ngAnimate. It is possible to use `$animateCss` directly inside of a directive, however,
38950 * any automatic control over cancelling animations and/or preventing animations from being run on
38951 * child elements will not be handled by Angular
vendor: 13,421 bytes, lines 38951-39232
38951. For this to work as expected, please use `$animate` to
38952 * trigger the animation and then setup a JavaScript animation that injects `$animateCss` to trigger
38953 * the CSS animation.
38954 *
38955 * The example below shows how we can create a folding animation on an element using `ng-if`:
38956 *
38957 * ```html
38958 * <!-- notice the `fold-animation` CSS class -->
38959 * <div ng-if="onOff" class="fold-animation">
38960 *   This element will go BOOM
38961 * </div>
38962 * <button ng-click="onOff=true">Fold In</button>
38963 * ```
38964 *
38965 * Now we create the **JavaScript animation** that will trigger the CSS transition:
38966 *
38967 * ```js
38968 * ngModule.animation('.fold-animation', ['$animateCss', function($animateCss) {
38969 *   return {
38970 *     enter: function(element, doneFn) {
38971 *       var height = element[0].offsetHeight;
38972 *       return $animateCss(element, {
38973 *         from: { height:'0px' },
38974 *         to: { height:height + 'px' },
38975 *         duration: 1 // one second
38976 *       });
38977 *     }
38978 *   }
38979 * }]);
38980 * ```
38981 *
38982 * ## More Advanced Uses
38983 *
38984 * `$animateCss` is the underlying code that ngAnimate uses to power **CSS-based animations** behind the scenes. Therefore CSS hooks
38985 * like `.ng-EVENT`, `.ng-EVENT-active`, `.ng-EVENT-stagger` are all features that can be triggered using `$animateCss` via JavaScript code.
38986 *
38987 * This also means that just about any combination of adding classes, removing classes, setting styles, dynamically setting a keyframe animation,
38988 * applying a hardcoded duration or delay value, changing the animation easing or applying a stagger animation are all options that work with
38989 * `$animateCss`. The service itself is smart enough to figure out the combination of options and examine the element styling properties in order
38990 * to provide a working animation that will run in CSS.
38991 *
38992 * The example below showcases a more advanced version of the `.fold-animation` from the example above:
38993 *
38994 * ```js
38995 * ngModule.animation('.fold-animation', ['$animateCss', function($animateCss) {
38996 *   return {
38997 *     enter: function(element, doneFn) {
38998 *       var height = element[0].offsetHeight;
38999 *       return $animateCss(element, {
39000 *         addClass: 'red large-text pulse-twice',
39001 *         easing: 'ease-out',
39002 *         from: { height:'0px' },
39003 *         to: { height:height + 'px' },
39004 *         duration: 1 // one second
39005 *       });
39006 *     }
39007 *   }
39008 * }]);
39009 * ```
39010 *
39011 * Since we're adding/removing CSS classes then the CSS transition will also pick those up:
39012 *
39013 * ```css
39014 * /&#42; since a hardcoded duration value of 1 was provided in the JavaScript animation code,
39015 * the CSS classes below will be transitioned despite them being defined as regular CSS classes &#42;/
39016 * .red { background:red; }
39017 * .large-text { font-size:20px; }
39018 *
39019 * /&#42; we can also use a keyframe animation and $animateCss will make it work alongside the transition &#42;/
39020 * .pulse-twice {
39021 *   animation: 0.5s pulse linear 2;
39022 *   -webkit-animation: 0.5s pulse linear 2;
39023 * }
39024 *
39025 * @keyframes pulse {
39026 *   from { transform: scale(0.5); }
39027 *   to { transform: scale(1.5); }
39028 * }
39029 *
39030 * @-webkit-keyframes pulse {
39031 *   from { -webkit-transform: scale(0.5); }
39032 *   to { -webkit-transform: scale(1.5); }
39033 * }
39034 * ```
39035 *
39036 * Given this complex combination of CSS classes, styles and options, `$animateCss` will figure everything out and make the animation happen.
39037 *
39038 * ## How the Options are handled
39039 *
39040 * `$animateCss` is very versatile and intelligent when it comes to figuring out what configurations to apply to the element to ensure the animation
39041 * works with the options provided. Say for example we were adding a class that contained a keyframe value and we wanted to also animate some inline
39042 * styles using the `from` and `to` properties.
39043 *
39044 * ```js
39045 * var animator = $animateCss(element, {
39046 *   from: { background:'red' },
39047 *   to: { background:'blue' }
39048 * });
39049 * animator.start();
39050 * ```
39051 *
39052 * ```css
39053 * .rotating-animation {
39054 *   animation:0.5s rotate linear;
39055 *   -webkit-animation:0.5s rotate linear;
39056 * }
39057 *
39058 * @keyframes rotate {
39059 *   from { transform: rotate(0deg); }
39060 *   to { transform: rotate(360deg); }
39061 * }
39062 *
39063 * @-webkit-keyframes rotate {
39064 *   from { -webkit-transform: rotate(0deg); }
39065 *   to { -webkit-transform: rotate(360deg); }
39066 * }
39067 * ```
39068 *
39069 * The missing pieces here are that we do not have a transition set (within the CSS code nor within the `$animateCss` options) and the duration of the animation is
39070 * going to be detected from what the keyframe styles on the CSS class are. In this event, `$animateCss` will automatically create an inline transition
39071 * style matching the duration detected from the keyframe style (which is present in the CSS class that is being added) and then prepare both the transition
39072 * and keyframe animations to run in parallel on the element. Then when the animation is underway the provided `from` and `to` CSS styles will be applied
39073 * and spread across the transition and keyframe animation.
39074 *
39075 * ## What is returned
39076 *
39077 * `$animateCss` works in two stages: a preparation phase and an animation phase. Therefore when `$animateCss` is first called it will NOT actually
39078 * start the animation. All that is going on here is that the element is being prepared for the animation (which means that the generated CSS classes are
39079 * added and removed on the element). Once `$animateCss` is called it will return an object with the following properties:
39080 *
39081 * ```js
39082 * var animator = $animateCss(element, { ... });
39083 * ```
39084 *
39085 * Now what do the contents of our `animator` variable look like:
39086 *
39087 * ```js
39088 * {
39089 *   // starts the animation
39090 *   start: Function,
39091 *
39092 *   // ends (aborts) the animation
39093 *   end: Function
39094 * }
39095 * ```
39096 *
39097 * To actually start the animation we need to run `animation.start()` which will then return a promise that we can hook into to detect when the animation ends.
39098 * If we choose not to run the animation then we MUST run `animation.end()` to perform a cleanup on the element (since some CSS classes and styles may have been
39099 * applied to the element during the preparation phase). Note that all other properties such as duration, delay, transitions and keyframes are just properties
39100 * and that changing them will not reconfigure the parameters of the animation.
39101 *
39102 * ### runner.done() vs runner.then()
39103 * It is documented that `animation.start()` will return a promise object and this is true, however, there is also an additional method available on the
39104 * runner called `.done(callbackFn)`. The done method works the same as `.finally(callbackFn)`, however, it does **not trigger a digest to occur**.
39105 * Therefore, for performance reasons, it's always best to use `runner.done(callback)` instead of `runner.then()`, `runner.catch()` or `runner.finally()`
39106 * unless you really need a digest to kick off afterwards.
39107 *
39108 * Keep in mind that, to make this easier, ngAnimate has tweaked the JS animations API to recognize when a runner instance is returned from $animateCss
39109 * (so there is no need to call `runner.done(doneFn)` inside of your JavaScript animation code).
39110 * Check the {@link ngAnimate.$animateCss#usage animation code above} to see how this works.
39111 *
39112 * @param {DOMElement} element the element that will be animated
39113 * @param {object} options the animation-related options that will be applied during the animation
39114 *
39115 * * `event` - The DOM event (e.g. enter, leave, move). When used, a generated CSS class of `ng-EVENT` and `ng-EVENT-active` will be applied
39116 * to the element during the animation. Multiple events can be provided when spaces are used as a separator. (Note that this will not perform any DOM operation.)
39117 * * `structural` - Indicates that the `ng-` prefix will be added to the event class. Setting to `false` or omitting will turn `ng-EVENT` and
39118 * `ng-EVENT-active` in `EVENT` and `EVENT-active`. Unused if `event` is omitted.
39119 * * `easing` - The CSS easing value that will be applied to the transition or keyframe animation (or both).
39120 * * `transitionStyle` - The raw CSS transition style that will be used (e.g. `1s linear all`).
39121 * * `keyframeStyle` - The raw CSS keyframe animation style that will be used (e.g. `1s my_animation linear`).
39122 * * `from` - The starting CSS styles (a key/value object) that will be applied at the start of the animation.
39123 * * `to` - The ending CSS styles (a key/value object) that will be applied across the animation via a CSS transition.
39124 * * `addClass` - A space separated list of CSS classes that will be added to the element and spread across the animation.
39125 * * `removeClass` - A space separated list of CSS classes that will be removed from the element and spread across the animation.
39126 * * `duration` - A number value representing the total duration of the transition and/or keyframe (note that a value of 1 is 1000ms). If a value of `0`
39127 * is provided then the animation will be skipped entirely.
39128 * * `delay` - A number value representing the total delay of the transition and/or keyframe (note that a value of 1 is 1000ms). If a value of `true` is
39129 * used then whatever delay value is detected from the CSS classes will be mirrored on the elements styles (e.g. by setting delay true then the style value
39130 * of the element will be `transition-delay: DETECTED_VALUE`). Using `true` is useful when you want the CSS classes and inline styles to all share the same
39131 * CSS delay value.
39132 * * `stagger` - A numeric time value representing the delay between successively animated elements
39133 * ({@link ngAnimate#css-staggering-animations Click here to learn how CSS-based staggering works in ngAnimate.})
39134 * * `staggerIndex` - The numeric index representing the stagger item (e.g. a value of 5 is equal to the sixth item in the stagger; therefore when a
39135 *   `stagger` option value of `0.1` is used then there will be a stagger delay of `600ms`)
39136 * * `applyClassesEarly` - Whether or not the classes being added or removed will be used when detecting the animation. This is set by `$animate` when enter/leave/move animations are fired to ensure that the CSS classes are resolved in time. (Note that this will prevent any transitions from occurring on the classes being added and removed.)
39137 * * `cleanupStyles` - Whether or not the provided `from` and `to` styles will be removed once
39138 *    the animation is closed. This is useful for when the styles are used purely for the sake of
39139 *    the animation and do not have a lasting visual effect on the element (e.g. a collapse and open animation).
39140 *    By default this value is set to `false`.
39141 *
39142 * @return {object} an object with start and end methods and details about the animation.
39143 *
39144 * * `start` - The method to start the animation. This will return a `Promise` when called.
39145 * * `end` - This method will cancel the animation and remove all applied CSS classes and styles.
39146 */
39147var ONE_SECOND = 1000;
39148
39149var ELAPSED_TIME_MAX_DECIMAL_PLACES = 3;
39150var CLOSING_TIME_BUFFER = 1.5;
39151
39152var DETECT_CSS_PROPERTIES = {
39153  transitionDuration:      TRANSITION_DURATION_PROP,
39154  transitionDelay:         TRANSITION_DELAY_PROP,
39155  transitionProperty:      TRANSITION_PROP + PROPERTY_KEY,
39156  animationDuration:       ANIMATION_DURATION_PROP,
39157  animationDelay:          ANIMATION_DELAY_PROP,
39158  animationIterationCount: ANIMATION_PROP + ANIMATION_ITERATION_COUNT_KEY
39159};
39160
39161var DETECT_STAGGER_CSS_PROPERTIES = {
39162  transitionDuration:      TRANSITION_DURATION_PROP,
39163  transitionDelay:         TRANSITION_DELAY_PROP,
39164  animationDuration:       ANIMATION_DURATION_PROP,
39165  animationDelay:          ANIMATION_DELAY_PROP
39166};
39167
39168function getCssKeyframeDurationStyle(duration) {
39169  return [ANIMATION_DURATION_PROP, duration + 's'];
39170}
39171
39172function getCssDelayStyle(delay, isKeyframeAnimation) {
39173  var prop = isKeyframeAnimation ? ANIMATION_DELAY_PROP : TRANSITION_DELAY_PROP;
39174  return [prop, delay + 's'];
39175}
39176
39177function computeCssStyles($window, element, properties) {
39178  var styles = Object.create(null);
39179  var detectedStyles = $window.getComputedStyle(element) || {};
39180  forEach(properties, function(formalStyleName, actualStyleName) {
39181    var val = detectedStyles[formalStyleName];
39182    if (val) {
39183      var c = val.charAt(0);
39184
39185      // only numerical-based values have a negative sign or digit as the first value
39186      if (c === '-' || c === '+' || c >= 0) {
39187        val = parseMaxTime(val);
39188      }
39189
39190      // by setting this to null in the event that the delay is not set or is set directly as 0
39191      // then we can still allow for negative values to be used later on and not mistake this
39192      // value for being greater than any other negative value.
39193      if (val === 0) {
39194        val = null;
39195      }
39196      styles[actualStyleName] = val;
39197    }
39198  });
39199
39200  return styles;
39201}
39202
39203function parseMaxTime(str) {
39204  var maxValue = 0;
39205  var values = str.split(/\s*,\s*/);
39206  forEach(values, function(value) {
39207    // it's always safe to consider only second values and omit `ms` values since
39208    // getComputedStyle will always handle the conversion for us
39209    if (value.charAt(value.length - 1) === 's') {
39210      value = value.substring(0, value.length - 1);
39211    }
39212    value = parseFloat(value) || 0;
39213    maxValue = maxValue ? Math.max(value, maxValue) : value;
39214  });
39215  return maxValue;
39216}
39217
39218function truthyTimingValue(val) {
39219  return val === 0 || val != null;
39220}
39221
39222function getCssTransitionDurationStyle(duration, applyOnlyDuration) {
39223  var style = TRANSITION_PROP;
39224  var value = duration + 's';
39225  if (applyOnlyDuration) {
39226    style += DURATION_KEY;
39227  } else {
39228    value += ' linear all';
39229  }
39230  return [style, value];
39231}
39232
39233function createLocalCacheLookup() {
39234  var cache = Object.create(null);
39235  return {
39236    flush: function() {
39237      cache = Object.create(null);
39238    },
39239
39240    count: function(key) {
39241      var entry = cache[key];
39242      return entry ? entry.total : 0;
39243    },
39244
39245    get: function(key) {
39246      var entry = cache[key];
39247      return entry && entry.value;
39248    },
39249
39250    put: function(key, value) {
39251      if (!cache[key]) {
39252        cache[key] = { total: 1, value: value };
39253      } else {
39254        cache[key].total++;
39255      }
39256    }
39257  };
39258}
39259
39260// we do not reassign an already present style value since
39261// if we detect the style property value again we may be
39262// detecting styles that were added via the `from` styles.
39263// We make use of `isDefined` here since an empty string
39264// or null value (which is what getPropertyValue will return
39265// for a non-existing style) will still be marked as a valid
39266// value for the style (a falsy value implies that the style
39267// is to be removed at the end of the animation). If we had a simple
39268// "OR" statement then it would not be enough to catch that.
39269function registerRestorableStyles(backup, node, properties) {
39270  forEach(properties, function(prop) {
39271    backup[prop] = isDefined(backup[prop])
39272        ? backup[prop]
39273        : node.style.getPropertyValue(prop);
39274  });
39275}
39276
39277var $AnimateCssProvider = ['$animateProvider', /** @this */ function($animateProvider) {
39278  var gcsLookup = createLocalCacheLookup();
39279  var gcsStaggerLookup = createLocalCacheLookup();
39280
39281  this.$get = ['$window', '$$jqLite', '$$AnimateRunner', '$timeout',
39282               '$$forceReflow', '$sniffer', '$$rAFScheduler', '$$animateQueue',
39283       function($window,   $$jqLite,   $$AnimateRunner,   $timeout,
39284                $$forceReflow,   $sniffer,   $$rAFScheduler, $$animateQueue) {
39285
39286    var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
39287
39288    var parentCounter = 0;
vendor: 20,666 bytes, lines 39289-39829
39289    function gcsHashFn(node, extraClasses) {
39290      var KEY = '$$ngAnimateParentKey';
39291      var parentNode = node.parentNode;
39292      var parentID = parentNode[KEY] || (parentNode[KEY] = ++parentCounter);
39293      return parentID + '-' + node.getAttribute('class') + '-' + extraClasses;
39294    }
39295
39296    function computeCachedCssStyles(node, className, cacheKey, properties) {
39297      var timings = gcsLookup.get(cacheKey);
39298
39299      if (!timings) {
39300        timings = computeCssStyles($window, node, properties);
39301        if (timings.animationIterationCount === 'infinite') {
39302          timings.animationIterationCount = 1;
39303        }
39304      }
39305
39306      // we keep putting this in multiple times even though the value and the cacheKey are the same
39307      // because we're keeping an internal tally of how many duplicate animations are detected.
39308      gcsLookup.put(cacheKey, timings);
39309      return timings;
39310    }
39311
39312    function computeCachedCssStaggerStyles(node, className, cacheKey, properties) {
39313      var stagger;
39314
39315      // if we have one or more existing matches of matching elements
39316      // containing the same parent + CSS styles (which is how cacheKey works)
39317      // then staggering is possible
39318      if (gcsLookup.count(cacheKey) > 0) {
39319        stagger = gcsStaggerLookup.get(cacheKey);
39320
39321        if (!stagger) {
39322          var staggerClassName = pendClasses(className, '-stagger');
39323
39324          $$jqLite.addClass(node, staggerClassName);
39325
39326          stagger = computeCssStyles($window, node, properties);
39327
39328          // force the conversion of a null value to zero incase not set
39329          stagger.animationDuration = Math.max(stagger.animationDuration, 0);
39330          stagger.transitionDuration = Math.max(stagger.transitionDuration, 0);
39331
39332          $$jqLite.removeClass(node, staggerClassName);
39333
39334          gcsStaggerLookup.put(cacheKey, stagger);
39335        }
39336      }
39337
39338      return stagger || {};
39339    }
39340
39341    var rafWaitQueue = [];
39342    function waitUntilQuiet(callback) {
39343      rafWaitQueue.push(callback);
39344      $$rAFScheduler.waitUntilQuiet(function() {
39345        gcsLookup.flush();
39346        gcsStaggerLookup.flush();
39347
39348        // DO NOT REMOVE THIS LINE OR REFACTOR OUT THE `pageWidth` variable.
39349        // PLEASE EXAMINE THE `$$forceReflow` service to understand why.
39350        var pageWidth = $$forceReflow();
39351
39352        // we use a for loop to ensure that if the queue is changed
39353        // during this looping then it will consider new requests
39354        for (var i = 0; i < rafWaitQueue.length; i++) {
39355          rafWaitQueue[i](pageWidth);
39356        }
39357        rafWaitQueue.length = 0;
39358      });
39359    }
39360
39361    function computeTimings(node, className, cacheKey) {
39362      var timings = computeCachedCssStyles(node, className, cacheKey, DETECT_CSS_PROPERTIES);
39363      var aD = timings.animationDelay;
39364      var tD = timings.transitionDelay;
39365      timings.maxDelay = aD && tD
39366          ? Math.max(aD, tD)
39367          : (aD || tD);
39368      timings.maxDuration = Math.max(
39369          timings.animationDuration * timings.animationIterationCount,
39370          timings.transitionDuration);
39371
39372      return timings;
39373    }
39374
39375    return function init(element, initialOptions) {
39376      // all of the animation functions should create
39377      // a copy of the options data, however, if a
39378      // parent service has already created a copy then
39379      // we should stick to using that
39380      var options = initialOptions || {};
39381      if (!options.$$prepared) {
39382        options = prepareAnimationOptions(copy(options));
39383      }
39384
39385      var restoreStyles = {};
39386      var node = getDomNode(element);
39387      if (!node
39388          || !node.parentNode
39389          || !$$animateQueue.enabled()) {
39390        return closeAndReturnNoopAnimator();
39391      }
39392
39393      var temporaryStyles = [];
39394      var classes = element.attr('class');
39395      var styles = packageStyles(options);
39396      var animationClosed;
39397      var animationPaused;
39398      var animationCompleted;
39399      var runner;
39400      var runnerHost;
39401      var maxDelay;
39402      var maxDelayTime;
39403      var maxDuration;
39404      var maxDurationTime;
39405      var startTime;
39406      var events = [];
39407
39408      if (options.duration === 0 || (!$sniffer.animations && !$sniffer.transitions)) {
39409        return closeAndReturnNoopAnimator();
39410      }
39411
39412      var method = options.event && isArray(options.event)
39413            ? options.event.join(' ')
39414            : options.event;
39415
39416      var isStructural = method && options.structural;
39417      var structuralClassName = '';
39418      var addRemoveClassName = '';
39419
39420      if (isStructural) {
39421        structuralClassName = pendClasses(method, EVENT_CLASS_PREFIX, true);
39422      } else if (method) {
39423        structuralClassName = method;
39424      }
39425
39426      if (options.addClass) {
39427        addRemoveClassName += pendClasses(options.addClass, ADD_CLASS_SUFFIX);
39428      }
39429
39430      if (options.removeClass) {
39431        if (addRemoveClassName.length) {
39432          addRemoveClassName += ' ';
39433        }
39434        addRemoveClassName += pendClasses(options.removeClass, REMOVE_CLASS_SUFFIX);
39435      }
39436
39437      // there may be a situation where a structural animation is combined together
39438      // with CSS classes that need to resolve before the animation is computed.
39439      // However this means that there is no explicit CSS code to block the animation
39440      // from happening (by setting 0s none in the class name). If this is the case
39441      // we need to apply the classes before the first rAF so we know to continue if
39442      // there actually is a detected transition or keyframe animation
39443      if (options.applyClassesEarly && addRemoveClassName.length) {
39444        applyAnimationClasses(element, options);
39445      }
39446
39447      var preparationClasses = [structuralClassName, addRemoveClassName].join(' ').trim();
39448      var fullClassName = classes + ' ' + preparationClasses;
39449      var activeClasses = pendClasses(preparationClasses, ACTIVE_CLASS_SUFFIX);
39450      var hasToStyles = styles.to && Object.keys(styles.to).length > 0;
39451      var containsKeyframeAnimation = (options.keyframeStyle || '').length > 0;
39452
39453      // there is no way we can trigger an animation if no styles and
39454      // no classes are being applied which would then trigger a transition,
39455      // unless there a is raw keyframe value that is applied to the element.
39456      if (!containsKeyframeAnimation
39457           && !hasToStyles
39458           && !preparationClasses) {
39459        return closeAndReturnNoopAnimator();
39460      }
39461
39462      var cacheKey, stagger;
39463      if (options.stagger > 0) {
39464        var staggerVal = parseFloat(options.stagger);
39465        stagger = {
39466          transitionDelay: staggerVal,
39467          animationDelay: staggerVal,
39468          transitionDuration: 0,
39469          animationDuration: 0
39470        };
39471      } else {
39472        cacheKey = gcsHashFn(node, fullClassName);
39473        stagger = computeCachedCssStaggerStyles(node, preparationClasses, cacheKey, DETECT_STAGGER_CSS_PROPERTIES);
39474      }
39475
39476      if (!options.$$skipPreparationClasses) {
39477        $$jqLite.addClass(element, preparationClasses);
39478      }
39479
39480      var applyOnlyDuration;
39481
39482      if (options.transitionStyle) {
39483        var transitionStyle = [TRANSITION_PROP, options.transitionStyle];
39484        applyInlineStyle(node, transitionStyle);
39485        temporaryStyles.push(transitionStyle);
39486      }
39487
39488      if (options.duration >= 0) {
39489        applyOnlyDuration = node.style[TRANSITION_PROP].length > 0;
39490        var durationStyle = getCssTransitionDurationStyle(options.duration, applyOnlyDuration);
39491
39492        // we set the duration so that it will be picked up by getComputedStyle later
39493        applyInlineStyle(node, durationStyle);
39494        temporaryStyles.push(durationStyle);
39495      }
39496
39497      if (options.keyframeStyle) {
39498        var keyframeStyle = [ANIMATION_PROP, options.keyframeStyle];
39499        applyInlineStyle(node, keyframeStyle);
39500        temporaryStyles.push(keyframeStyle);
39501      }
39502
39503      var itemIndex = stagger
39504          ? options.staggerIndex >= 0
39505              ? options.staggerIndex
39506              : gcsLookup.count(cacheKey)
39507          : 0;
39508
39509      var isFirst = itemIndex === 0;
39510
39511      // this is a pre-emptive way of forcing the setup classes to be added and applied INSTANTLY
39512      // without causing any combination of transitions to kick in. By adding a negative delay value
39513      // it forces the setup class' transition to end immediately. We later then remove the negative
39514      // transition delay to allow for the transition to naturally do it's thing. The beauty here is
39515      // that if there is no transition defined then nothing will happen and this will also allow
39516      // other transitions to be stacked on top of each other without any chopping them out.
39517      if (isFirst && !options.skipBlocking) {
39518        blockTransitions(node, SAFE_FAST_FORWARD_DURATION_VALUE);
39519      }
39520
39521      var timings = computeTimings(node, fullClassName, cacheKey);
39522      var relativeDelay = timings.maxDelay;
39523      maxDelay = Math.max(relativeDelay, 0);
39524      maxDuration = timings.maxDuration;
39525
39526      var flags = {};
39527      flags.hasTransitions          = timings.transitionDuration > 0;
39528      flags.hasAnimations           = timings.animationDuration > 0;
39529      flags.hasTransitionAll        = flags.hasTransitions && timings.transitionProperty === 'all';
39530      flags.applyTransitionDuration = hasToStyles && (
39531                                        (flags.hasTransitions && !flags.hasTransitionAll)
39532                                         || (flags.hasAnimations && !flags.hasTransitions));
39533      flags.applyAnimationDuration  = options.duration && flags.hasAnimations;
39534      flags.applyTransitionDelay    = truthyTimingValue(options.delay) && (flags.applyTransitionDuration || flags.hasTransitions);
39535      flags.applyAnimationDelay     = truthyTimingValue(options.delay) && flags.hasAnimations;
39536      flags.recalculateTimingStyles = addRemoveClassName.length > 0;
39537
39538      if (flags.applyTransitionDuration || flags.applyAnimationDuration) {
39539        maxDuration = options.duration ? parseFloat(options.duration) : maxDuration;
39540
39541        if (flags.applyTransitionDuration) {
39542          flags.hasTransitions = true;
39543          timings.transitionDuration = maxDuration;
39544          applyOnlyDuration = node.style[TRANSITION_PROP + PROPERTY_KEY].length > 0;
39545          temporaryStyles.push(getCssTransitionDurationStyle(maxDuration, applyOnlyDuration));
39546        }
39547
39548        if (flags.applyAnimationDuration) {
39549          flags.hasAnimations = true;
39550          timings.animationDuration = maxDuration;
39551          temporaryStyles.push(getCssKeyframeDurationStyle(maxDuration));
39552        }
39553      }
39554
39555      if (maxDuration === 0 && !flags.recalculateTimingStyles) {
39556        return closeAndReturnNoopAnimator();
39557      }
39558
39559      if (options.delay != null) {
39560        var delayStyle;
39561        if (typeof options.delay !== 'boolean') {
39562          delayStyle = parseFloat(options.delay);
39563          // number in options.delay means we have to recalculate the delay for the closing timeout
39564          maxDelay = Math.max(delayStyle, 0);
39565        }
39566
39567        if (flags.applyTransitionDelay) {
39568          temporaryStyles.push(getCssDelayStyle(delayStyle));
39569        }
39570
39571        if (flags.applyAnimationDelay) {
39572          temporaryStyles.push(getCssDelayStyle(delayStyle, true));
39573        }
39574      }
39575
39576      // we need to recalculate the delay value since we used a pre-emptive negative
39577      // delay value and the delay value is required for the final event checking. This
39578      // property will ensure that this will happen after the RAF phase has passed.
39579      if (options.duration == null && timings.transitionDuration > 0) {
39580        flags.recalculateTimingStyles = flags.recalculateTimingStyles || isFirst;
39581      }
39582
39583      maxDelayTime = maxDelay * ONE_SECOND;
39584      maxDurationTime = maxDuration * ONE_SECOND;
39585      if (!options.skipBlocking) {
39586        flags.blockTransition = timings.transitionDuration > 0;
39587        flags.blockKeyframeAnimation = timings.animationDuration > 0 &&
39588                                       stagger.animationDelay > 0 &&
39589                                       stagger.animationDuration === 0;
39590      }
39591
39592      if (options.from) {
39593        if (options.cleanupStyles) {
39594          registerRestorableStyles(restoreStyles, node, Object.keys(options.from));
39595        }
39596        applyAnimationFromStyles(element, options);
39597      }
39598
39599      if (flags.blockTransition || flags.blockKeyframeAnimation) {
39600        applyBlocking(maxDuration);
39601      } else if (!options.skipBlocking) {
39602        blockTransitions(node, false);
39603      }
39604
39605      // TODO(matsko): for 1.5 change this code to have an animator object for better debugging
39606      return {
39607        $$willAnimate: true,
39608        end: endFn,
39609        start: function() {
39610          if (animationClosed) return;
39611
39612          runnerHost = {
39613            end: endFn,
39614            cancel: cancelFn,
39615            resume: null, //this will be set during the start() phase
39616            pause: null
39617          };
39618
39619          runner = new $$AnimateRunner(runnerHost);
39620
39621          waitUntilQuiet(start);
39622
39623          // we don't have access to pause/resume the animation
39624          // since it hasn't run yet. AnimateRunner will therefore
39625          // set noop functions for resume and pause and they will
39626          // later be overridden once the animation is triggered
39627          return runner;
39628        }
39629      };
39630
39631      function endFn() {
39632        close();
39633      }
39634
39635      function cancelFn() {
39636        close(true);
39637      }
39638
39639      function close(rejected) {
39640        // if the promise has been called already then we shouldn't close
39641        // the animation again
39642        if (animationClosed || (animationCompleted && animationPaused)) return;
39643        animationClosed = true;
39644        animationPaused = false;
39645
39646        if (!options.$$skipPreparationClasses) {
39647          $$jqLite.removeClass(element, preparationClasses);
39648        }
39649        $$jqLite.removeClass(element, activeClasses);
39650
39651        blockKeyframeAnimations(node, false);
39652        blockTransitions(node, false);
39653
39654        forEach(temporaryStyles, function(entry) {
39655          // There is only one way to remove inline style properties entirely from elements.
39656          // By using `removeProperty` this works, but we need to convert camel-cased CSS
39657          // styles down to hyphenated values.
39658          node.style[entry[0]] = '';
39659        });
39660
39661        applyAnimationClasses(element, options);
39662        applyAnimationStyles(element, options);
39663
39664        if (Object.keys(restoreStyles).length) {
39665          forEach(restoreStyles, function(value, prop) {
39666            if (value) {
39667              node.style.setProperty(prop, value);
39668            } else {
39669              node.style.removeProperty(prop);
39670            }
39671          });
39672        }
39673
39674        // the reason why we have this option is to allow a synchronous closing callback
39675        // that is fired as SOON as the animation ends (when the CSS is removed) or if
39676        // the animation never takes off at all. A good example is a leave animation since
39677        // the element must be removed just after the animation is over or else the element
39678        // will appear on screen for one animation frame causing an overbearing flicker.
39679        if (options.onDone) {
39680          options.onDone();
39681        }
39682
39683        if (events && events.length) {
39684          // Remove the transitionend / animationend listener(s)
39685          element.off(events.join(' '), onAnimationProgress);
39686        }
39687
39688        //Cancel the fallback closing timeout and remove the timer data
39689        var animationTimerData = element.data(ANIMATE_TIMER_KEY);
39690        if (animationTimerData) {
39691          $timeout.cancel(animationTimerData[0].timer);
39692          element.removeData(ANIMATE_TIMER_KEY);
39693        }
39694
39695        // if the preparation function fails then the promise is not setup
39696        if (runner) {
39697          runner.complete(!rejected);
39698        }
39699      }
39700
39701      function applyBlocking(duration) {
39702        if (flags.blockTransition) {
39703          blockTransitions(node, duration);
39704        }
39705
39706        if (flags.blockKeyframeAnimation) {
39707          blockKeyframeAnimations(node, !!duration);
39708        }
39709      }
39710
39711      function closeAndReturnNoopAnimator() {
39712        runner = new $$AnimateRunner({
39713          end: endFn,
39714          cancel: cancelFn
39715        });
39716
39717        // should flush the cache animation
39718        waitUntilQuiet(noop);
39719        close();
39720
39721        return {
39722          $$willAnimate: false,
39723          start: function() {
39724            return runner;
39725          },
39726          end: endFn
39727        };
39728      }
39729
39730      function onAnimationProgress(event) {
39731        event.stopPropagation();
39732        var ev = event.originalEvent || event;
39733
39734        // we now always use `Date.now()` due to the recent changes with
39735        // event.timeStamp in Firefox, Webkit and Chrome (see #13494 for more info)
39736        var timeStamp = ev.$manualTimeStamp || Date.now();
39737
39738        /* Firefox (or possibly just Gecko) likes to not round values up
39739         * when a ms measurement is used for the animation */
39740        var elapsedTime = parseFloat(ev.elapsedTime.toFixed(ELAPSED_TIME_MAX_DECIMAL_PLACES));
39741
39742        /* $manualTimeStamp is a mocked timeStamp value which is set
39743         * within browserTrigger(). This is only here so that tests can
39744         * mock animations properly. Real events fallback to event.timeStamp,
39745         * or, if they don't, then a timeStamp is automatically created for them.
39746         * We're checking to see if the timeStamp surpasses the expected delay,
39747         * but we're using elapsedTime instead of the timeStamp on the 2nd
39748         * pre-condition since animationPauseds sometimes close off early */
39749        if (Math.max(timeStamp - startTime, 0) >= maxDelayTime && elapsedTime >= maxDuration) {
39750          // we set this flag to ensure that if the transition is paused then, when resumed,
39751          // the animation will automatically close itself since transitions cannot be paused.
39752          animationCompleted = true;
39753          close();
39754        }
39755      }
39756
39757      function start() {
39758        if (animationClosed) return;
39759        if (!node.parentNode) {
39760          close();
39761          return;
39762        }
39763
39764        // even though we only pause keyframe animations here the pause flag
39765        // will still happen when transitions are used. Only the transition will
39766        // not be paused since that is not possible. If the animation ends when
39767        // paused then it will not complete until unpaused or cancelled.
39768        var playPause = function(playAnimation) {
39769          if (!animationCompleted) {
39770            animationPaused = !playAnimation;
39771            if (timings.animationDuration) {
39772              var value = blockKeyframeAnimations(node, animationPaused);
39773              if (animationPaused) {
39774                temporaryStyles.push(value);
39775              } else {
39776                removeFromArray(temporaryStyles, value);
39777              }
39778            }
39779          } else if (animationPaused && playAnimation) {
39780            animationPaused = false;
39781            close();
39782          }
39783        };
39784
39785        // checking the stagger duration prevents an accidentally cascade of the CSS delay style
39786        // being inherited from the parent. If the transition duration is zero then we can safely
39787        // rely that the delay value is an intentional stagger delay style.
39788        var maxStagger = itemIndex > 0
39789                         && ((timings.transitionDuration && stagger.transitionDuration === 0) ||
39790                            (timings.animationDuration && stagger.animationDuration === 0))
39791                         && Math.max(stagger.animationDelay, stagger.transitionDelay);
39792        if (maxStagger) {
39793          $timeout(triggerAnimationStart,
39794                   Math.floor(maxStagger * itemIndex * ONE_SECOND),
39795                   false);
39796        } else {
39797          triggerAnimationStart();
39798        }
39799
39800        // this will decorate the existing promise runner with pause/resume methods
39801        runnerHost.resume = function() {
39802          playPause(true);
39803        };
39804
39805        runnerHost.pause = function() {
39806          playPause(false);
39807        };
39808
39809        function triggerAnimationStart() {
39810          // just incase a stagger animation kicks in when the animation
39811          // itself was cancelled entirely
39812          if (animationClosed) return;
39813
39814          applyBlocking(false);
39815
39816          forEach(temporaryStyles, function(entry) {
39817            var key = entry[0];
39818            var value = entry[1];
39819            node.style[key] = value;
39820          });
39821
39822          applyAnimationClasses(element, options);
39823          $$jqLite.addClass(element, activeClasses);
39824
39825          if (flags.recalculateTimingStyles) {
39826            fullClassName = node.className + ' ' + preparationClasses;
39827            cacheKey = gcsHashFn(node, fullClassName);
39828
39829            timings = computeTimings(node, fullClassName, cacheKey
vendor: 23,535 bytes, lines 39829-40566
39829);
39830            relativeDelay = timings.maxDelay;
39831            maxDelay = Math.max(relativeDelay, 0);
39832            maxDuration = timings.maxDuration;
39833
39834            if (maxDuration === 0) {
39835              close();
39836              return;
39837            }
39838
39839            flags.hasTransitions = timings.transitionDuration > 0;
39840            flags.hasAnimations = timings.animationDuration > 0;
39841          }
39842
39843          if (flags.applyAnimationDelay) {
39844            relativeDelay = typeof options.delay !== 'boolean' && truthyTimingValue(options.delay)
39845                  ? parseFloat(options.delay)
39846                  : relativeDelay;
39847
39848            maxDelay = Math.max(relativeDelay, 0);
39849            timings.animationDelay = relativeDelay;
39850            delayStyle = getCssDelayStyle(relativeDelay, true);
39851            temporaryStyles.push(delayStyle);
39852            node.style[delayStyle[0]] = delayStyle[1];
39853          }
39854
39855          maxDelayTime = maxDelay * ONE_SECOND;
39856          maxDurationTime = maxDuration * ONE_SECOND;
39857
39858          if (options.easing) {
39859            var easeProp, easeVal = options.easing;
39860            if (flags.hasTransitions) {
39861              easeProp = TRANSITION_PROP + TIMING_KEY;
39862              temporaryStyles.push([easeProp, easeVal]);
39863              node.style[easeProp] = easeVal;
39864            }
39865            if (flags.hasAnimations) {
39866              easeProp = ANIMATION_PROP + TIMING_KEY;
39867              temporaryStyles.push([easeProp, easeVal]);
39868              node.style[easeProp] = easeVal;
39869            }
39870          }
39871
39872          if (timings.transitionDuration) {
39873            events.push(TRANSITIONEND_EVENT);
39874          }
39875
39876          if (timings.animationDuration) {
39877            events.push(ANIMATIONEND_EVENT);
39878          }
39879
39880          startTime = Date.now();
39881          var timerTime = maxDelayTime + CLOSING_TIME_BUFFER * maxDurationTime;
39882          var endTime = startTime + timerTime;
39883
39884          var animationsData = element.data(ANIMATE_TIMER_KEY) || [];
39885          var setupFallbackTimer = true;
39886          if (animationsData.length) {
39887            var currentTimerData = animationsData[0];
39888            setupFallbackTimer = endTime > currentTimerData.expectedEndTime;
39889            if (setupFallbackTimer) {
39890              $timeout.cancel(currentTimerData.timer);
39891            } else {
39892              animationsData.push(close);
39893            }
39894          }
39895
39896          if (setupFallbackTimer) {
39897            var timer = $timeout(onAnimationExpired, timerTime, false);
39898            animationsData[0] = {
39899              timer: timer,
39900              expectedEndTime: endTime
39901            };
39902            animationsData.push(close);
39903            element.data(ANIMATE_TIMER_KEY, animationsData);
39904          }
39905
39906          if (events.length) {
39907            element.on(events.join(' '), onAnimationProgress);
39908          }
39909
39910          if (options.to) {
39911            if (options.cleanupStyles) {
39912              registerRestorableStyles(restoreStyles, node, Object.keys(options.to));
39913            }
39914            applyAnimationToStyles(element, options);
39915          }
39916        }
39917
39918        function onAnimationExpired() {
39919          var animationsData = element.data(ANIMATE_TIMER_KEY);
39920
39921          // this will be false in the event that the element was
39922          // removed from the DOM (via a leave animation or something
39923          // similar)
39924          if (animationsData) {
39925            for (var i = 1; i < animationsData.length; i++) {
39926              animationsData[i]();
39927            }
39928            element.removeData(ANIMATE_TIMER_KEY);
39929          }
39930        }
39931      }
39932    };
39933  }];
39934}];
39935
39936var $$AnimateCssDriverProvider = ['$$animationProvider', /** @this */ function($$animationProvider) {
39937  $$animationProvider.drivers.push('$$animateCssDriver');
39938
39939  var NG_ANIMATE_SHIM_CLASS_NAME = 'ng-animate-shim';
39940  var NG_ANIMATE_ANCHOR_CLASS_NAME = 'ng-anchor';
39941
39942  var NG_OUT_ANCHOR_CLASS_NAME = 'ng-anchor-out';
39943  var NG_IN_ANCHOR_CLASS_NAME = 'ng-anchor-in';
39944
39945  function isDocumentFragment(node) {
39946    return node.parentNode && node.parentNode.nodeType === 11;
39947  }
39948
39949  this.$get = ['$animateCss', '$rootScope', '$$AnimateRunner', '$rootElement', '$sniffer', '$$jqLite', '$document',
39950       function($animateCss,   $rootScope,   $$AnimateRunner,   $rootElement,   $sniffer,   $$jqLite,   $document) {
39951
39952    // only browsers that support these properties can render animations
39953    if (!$sniffer.animations && !$sniffer.transitions) return noop;
39954
39955    var bodyNode = $document[0].body;
39956    var rootNode = getDomNode($rootElement);
39957
39958    var rootBodyElement = jqLite(
39959      // this is to avoid using something that exists outside of the body
39960      // we also special case the doc fragment case because our unit test code
39961      // appends the $rootElement to the body after the app has been bootstrapped
39962      isDocumentFragment(rootNode) || bodyNode.contains(rootNode) ? rootNode : bodyNode
39963    );
39964
39965    return function initDriverFn(animationDetails) {
39966      return animationDetails.from && animationDetails.to
39967          ? prepareFromToAnchorAnimation(animationDetails.from,
39968                                         animationDetails.to,
39969                                         animationDetails.classes,
39970                                         animationDetails.anchors)
39971          : prepareRegularAnimation(animationDetails);
39972    };
39973
39974    function filterCssClasses(classes) {
39975      //remove all the `ng-` stuff
39976      return classes.replace(/\bng-\S+\b/g, '');
39977    }
39978
39979    function getUniqueValues(a, b) {
39980      if (isString(a)) a = a.split(' ');
39981      if (isString(b)) b = b.split(' ');
39982      return a.filter(function(val) {
39983        return b.indexOf(val) === -1;
39984      }).join(' ');
39985    }
39986
39987    function prepareAnchoredAnimation(classes, outAnchor, inAnchor) {
39988      var clone = jqLite(getDomNode(outAnchor).cloneNode(true));
39989      var startingClasses = filterCssClasses(getClassVal(clone));
39990
39991      outAnchor.addClass(NG_ANIMATE_SHIM_CLASS_NAME);
39992      inAnchor.addClass(NG_ANIMATE_SHIM_CLASS_NAME);
39993
39994      clone.addClass(NG_ANIMATE_ANCHOR_CLASS_NAME);
39995
39996      rootBodyElement.append(clone);
39997
39998      var animatorIn, animatorOut = prepareOutAnimation();
39999
40000      // the user may not end up using the `out` animation and
40001      // only making use of the `in` animation or vice-versa.
40002      // In either case we should allow this and not assume the
40003      // animation is over unless both animations are not used.
40004      if (!animatorOut) {
40005        animatorIn = prepareInAnimation();
40006        if (!animatorIn) {
40007          return end();
40008        }
40009      }
40010
40011      var startingAnimator = animatorOut || animatorIn;
40012
40013      return {
40014        start: function() {
40015          var runner;
40016
40017          var currentAnimation = startingAnimator.start();
40018          currentAnimation.done(function() {
40019            currentAnimation = null;
40020            if (!animatorIn) {
40021              animatorIn = prepareInAnimation();
40022              if (animatorIn) {
40023                currentAnimation = animatorIn.start();
40024                currentAnimation.done(function() {
40025                  currentAnimation = null;
40026                  end();
40027                  runner.complete();
40028                });
40029                return currentAnimation;
40030              }
40031            }
40032            // in the event that there is no `in` animation
40033            end();
40034            runner.complete();
40035          });
40036
40037          runner = new $$AnimateRunner({
40038            end: endFn,
40039            cancel: endFn
40040          });
40041
40042          return runner;
40043
40044          function endFn() {
40045            if (currentAnimation) {
40046              currentAnimation.end();
40047            }
40048          }
40049        }
40050      };
40051
40052      function calculateAnchorStyles(anchor) {
40053        var styles = {};
40054
40055        var coords = getDomNode(anchor).getBoundingClientRect();
40056
40057        // we iterate directly since safari messes up and doesn't return
40058        // all the keys for the coords object when iterated
40059        forEach(['width','height','top','left'], function(key) {
40060          var value = coords[key];
40061          switch (key) {
40062            case 'top':
40063              value += bodyNode.scrollTop;
40064              break;
40065            case 'left':
40066              value += bodyNode.scrollLeft;
40067              break;
40068          }
40069          styles[key] = Math.floor(value) + 'px';
40070        });
40071        return styles;
40072      }
40073
40074      function prepareOutAnimation() {
40075        var animator = $animateCss(clone, {
40076          addClass: NG_OUT_ANCHOR_CLASS_NAME,
40077          delay: true,
40078          from: calculateAnchorStyles(outAnchor)
40079        });
40080
40081        // read the comment within `prepareRegularAnimation` to understand
40082        // why this check is necessary
40083        return animator.$$willAnimate ? animator : null;
40084      }
40085
40086      function getClassVal(element) {
40087        return element.attr('class') || '';
40088      }
40089
40090      function prepareInAnimation() {
40091        var endingClasses = filterCssClasses(getClassVal(inAnchor));
40092        var toAdd = getUniqueValues(endingClasses, startingClasses);
40093        var toRemove = getUniqueValues(startingClasses, endingClasses);
40094
40095        var animator = $animateCss(clone, {
40096          to: calculateAnchorStyles(inAnchor),
40097          addClass: NG_IN_ANCHOR_CLASS_NAME + ' ' + toAdd,
40098          removeClass: NG_OUT_ANCHOR_CLASS_NAME + ' ' + toRemove,
40099          delay: true
40100        });
40101
40102        // read the comment within `prepareRegularAnimation` to understand
40103        // why this check is necessary
40104        return animator.$$willAnimate ? animator : null;
40105      }
40106
40107      function end() {
40108        clone.remove();
40109        outAnchor.removeClass(NG_ANIMATE_SHIM_CLASS_NAME);
40110        inAnchor.removeClass(NG_ANIMATE_SHIM_CLASS_NAME);
40111      }
40112    }
40113
40114    function prepareFromToAnchorAnimation(from, to, classes, anchors) {
40115      var fromAnimation = prepareRegularAnimation(from, noop);
40116      var toAnimation = prepareRegularAnimation(to, noop);
40117
40118      var anchorAnimations = [];
40119      forEach(anchors, function(anchor) {
40120        var outElement = anchor['out'];
40121        var inElement = anchor['in'];
40122        var animator = prepareAnchoredAnimation(classes, outElement, inElement);
40123        if (animator) {
40124          anchorAnimations.push(animator);
40125        }
40126      });
40127
40128      // no point in doing anything when there are no elements to animate
40129      if (!fromAnimation && !toAnimation && anchorAnimations.length === 0) return;
40130
40131      return {
40132        start: function() {
40133          var animationRunners = [];
40134
40135          if (fromAnimation) {
40136            animationRunners.push(fromAnimation.start());
40137          }
40138
40139          if (toAnimation) {
40140            animationRunners.push(toAnimation.start());
40141          }
40142
40143          forEach(anchorAnimations, function(animation) {
40144            animationRunners.push(animation.start());
40145          });
40146
40147          var runner = new $$AnimateRunner({
40148            end: endFn,
40149            cancel: endFn // CSS-driven animations cannot be cancelled, only ended
40150          });
40151
40152          $$AnimateRunner.all(animationRunners, function(status) {
40153            runner.complete(status);
40154          });
40155
40156          return runner;
40157
40158          function endFn() {
40159            forEach(animationRunners, function(runner) {
40160              runner.end();
40161            });
40162          }
40163        }
40164      };
40165    }
40166
40167    function prepareRegularAnimation(animationDetails) {
40168      var element = animationDetails.element;
40169      var options = animationDetails.options || {};
40170
40171      if (animationDetails.structural) {
40172        options.event = animationDetails.event;
40173        options.structural = true;
40174        options.applyClassesEarly = true;
40175
40176        // we special case the leave animation since we want to ensure that
40177        // the element is removed as soon as the animation is over. Otherwise
40178        // a flicker might appear or the element may not be removed at all
40179        if (animationDetails.event === 'leave') {
40180          options.onDone = options.domOperation;
40181        }
40182      }
40183
40184      // We assign the preparationClasses as the actual animation event since
40185      // the internals of $animateCss will just suffix the event token values
40186      // with `-active` to trigger the animation.
40187      if (options.preparationClasses) {
40188        options.event = concatWithSpace(options.event, options.preparationClasses);
40189      }
40190
40191      var animator = $animateCss(element, options);
40192
40193      // the driver lookup code inside of $$animation attempts to spawn a
40194      // driver one by one until a driver returns a.$$willAnimate animator object.
40195      // $animateCss will always return an object, however, it will pass in
40196      // a flag as a hint as to whether an animation was detected or not
40197      return animator.$$willAnimate ? animator : null;
40198    }
40199  }];
40200}];
40201
40202// TODO(matsko): use caching here to speed things up for detection
40203// TODO(matsko): add documentation
40204//  by the time...
40205
40206var $$AnimateJsProvider = ['$animateProvider', /** @this */ function($animateProvider) {
40207  this.$get = ['$injector', '$$AnimateRunner', '$$jqLite',
40208       function($injector,   $$AnimateRunner,   $$jqLite) {
40209
40210    var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
40211         // $animateJs(element, 'enter');
40212    return function(element, event, classes, options) {
40213      var animationClosed = false;
40214
40215      // the `classes` argument is optional and if it is not used
40216      // then the classes will be resolved from the element's className
40217      // property as well as options.addClass/options.removeClass.
40218      if (arguments.length === 3 && isObject(classes)) {
40219        options = classes;
40220        classes = null;
40221      }
40222
40223      options = prepareAnimationOptions(options);
40224      if (!classes) {
40225        classes = element.attr('class') || '';
40226        if (options.addClass) {
40227          classes += ' ' + options.addClass;
40228        }
40229        if (options.removeClass) {
40230          classes += ' ' + options.removeClass;
40231        }
40232      }
40233
40234      var classesToAdd = options.addClass;
40235      var classesToRemove = options.removeClass;
40236
40237      // the lookupAnimations function returns a series of animation objects that are
40238      // matched up with one or more of the CSS classes. These animation objects are
40239      // defined via the module.animation factory function. If nothing is detected then
40240      // we don't return anything which then makes $animation query the next driver.
40241      var animations = lookupAnimations(classes);
40242      var before, after;
40243      if (animations.length) {
40244        var afterFn, beforeFn;
40245        if (event === 'leave') {
40246          beforeFn = 'leave';
40247          afterFn = 'afterLeave'; // TODO(matsko): get rid of this
40248        } else {
40249          beforeFn = 'before' + event.charAt(0).toUpperCase() + event.substr(1);
40250          afterFn = event;
40251        }
40252
40253        if (event !== 'enter' && event !== 'move') {
40254          before = packageAnimations(element, event, options, animations, beforeFn);
40255        }
40256        after  = packageAnimations(element, event, options, animations, afterFn);
40257      }
40258
40259      // no matching animations
40260      if (!before && !after) return;
40261
40262      function applyOptions() {
40263        options.domOperation();
40264        applyAnimationClasses(element, options);
40265      }
40266
40267      function close() {
40268        animationClosed = true;
40269        applyOptions();
40270        applyAnimationStyles(element, options);
40271      }
40272
40273      var runner;
40274
40275      return {
40276        $$willAnimate: true,
40277        end: function() {
40278          if (runner) {
40279            runner.end();
40280          } else {
40281            close();
40282            runner = new $$AnimateRunner();
40283            runner.complete(true);
40284          }
40285          return runner;
40286        },
40287        start: function() {
40288          if (runner) {
40289            return runner;
40290          }
40291
40292          runner = new $$AnimateRunner();
40293          var closeActiveAnimations;
40294          var chain = [];
40295
40296          if (before) {
40297            chain.push(function(fn) {
40298              closeActiveAnimations = before(fn);
40299            });
40300          }
40301
40302          if (chain.length) {
40303            chain.push(function(fn) {
40304              applyOptions();
40305              fn(true);
40306            });
40307          } else {
40308            applyOptions();
40309          }
40310
40311          if (after) {
40312            chain.push(function(fn) {
40313              closeActiveAnimations = after(fn);
40314            });
40315          }
40316
40317          runner.setHost({
40318            end: function() {
40319              endAnimations();
40320            },
40321            cancel: function() {
40322              endAnimations(true);
40323            }
40324          });
40325
40326          $$AnimateRunner.chain(chain, onComplete);
40327          return runner;
40328
40329          function onComplete(success) {
40330            close(success);
40331            runner.complete(success);
40332          }
40333
40334          function endAnimations(cancelled) {
40335            if (!animationClosed) {
40336              (closeActiveAnimations || noop)(cancelled);
40337              onComplete(cancelled);
40338            }
40339          }
40340        }
40341      };
40342
40343      function executeAnimationFn(fn, element, event, options, onDone) {
40344        var args;
40345        switch (event) {
40346          case 'animate':
40347            args = [element, options.from, options.to, onDone];
40348            break;
40349
40350          case 'setClass':
40351            args = [element, classesToAdd, classesToRemove, onDone];
40352            break;
40353
40354          case 'addClass':
40355            args = [element, classesToAdd, onDone];
40356            break;
40357
40358          case 'removeClass':
40359            args = [element, classesToRemove, onDone];
40360            break;
40361
40362          default:
40363            args = [element, onDone];
40364            break;
40365        }
40366
40367        args.push(options);
40368
40369        var value = fn.apply(fn, args);
40370        if (value) {
40371          if (isFunction(value.start)) {
40372            value = value.start();
40373          }
40374
40375          if (value instanceof $$AnimateRunner) {
40376            value.done(onDone);
40377          } else if (isFunction(value)) {
40378            // optional onEnd / onCancel callback
40379            return value;
40380          }
40381        }
40382
40383        return noop;
40384      }
40385
40386      function groupEventedAnimations(element, event, options, animations, fnName) {
40387        var operations = [];
40388        forEach(animations, function(ani) {
40389          var animation = ani[fnName];
40390          if (!animation) return;
40391
40392          // note that all of these animations will run in parallel
40393          operations.push(function() {
40394            var runner;
40395            var endProgressCb;
40396
40397            var resolved = false;
40398            var onAnimationComplete = function(rejected) {
40399              if (!resolved) {
40400                resolved = true;
40401                (endProgressCb || noop)(rejected);
40402                runner.complete(!rejected);
40403              }
40404            };
40405
40406            runner = new $$AnimateRunner({
40407              end: function() {
40408                onAnimationComplete();
40409              },
40410              cancel: function() {
40411                onAnimationComplete(true);
40412              }
40413            });
40414
40415            endProgressCb = executeAnimationFn(animation, element, event, options, function(result) {
40416              var cancelled = result === false;
40417              onAnimationComplete(cancelled);
40418            });
40419
40420            return runner;
40421          });
40422        });
40423
40424        return operations;
40425      }
40426
40427      function packageAnimations(element, event, options, animations, fnName) {
40428        var operations = groupEventedAnimations(element, event, options, animations, fnName);
40429        if (operations.length === 0) {
40430          var a, b;
40431          if (fnName === 'beforeSetClass') {
40432            a = groupEventedAnimations(element, 'removeClass', options, animations, 'beforeRemoveClass');
40433            b = groupEventedAnimations(element, 'addClass', options, animations, 'beforeAddClass');
40434          } else if (fnName === 'setClass') {
40435            a = groupEventedAnimations(element, 'removeClass', options, animations, 'removeClass');
40436            b = groupEventedAnimations(element, 'addClass', options, animations, 'addClass');
40437          }
40438
40439          if (a) {
40440            operations = operations.concat(a);
40441          }
40442          if (b) {
40443            operations = operations.concat(b);
40444          }
40445        }
40446
40447        if (operations.length === 0) return;
40448
40449        // TODO(matsko): add documentation
40450        return function startAnimation(callback) {
40451          var runners = [];
40452          if (operations.length) {
40453            forEach(operations, function(animateFn) {
40454              runners.push(animateFn());
40455            });
40456          }
40457
40458          if (runners.length) {
40459            $$AnimateRunner.all(runners, callback);
40460          }  else {
40461            callback();
40462          }
40463
40464          return function endFn(reject) {
40465            forEach(runners, function(runner) {
40466              if (reject) {
40467                runner.cancel();
40468              } else {
40469                runner.end();
40470              }
40471            });
40472          };
40473        };
40474      }
40475    };
40476
40477    function lookupAnimations(classes) {
40478      classes = isArray(classes) ? classes : classes.split(' ');
40479      var matches = [], flagMap = {};
40480      for (var i = 0; i < classes.length; i++) {
40481        var klass = classes[i],
40482            animationFactory = $animateProvider.$$registeredAnimations[klass];
40483        if (animationFactory && !flagMap[klass]) {
40484          matches.push($injector.get(animationFactory));
40485          flagMap[klass] = true;
40486        }
40487      }
40488      return matches;
40489    }
40490  }];
40491}];
40492
40493var $$AnimateJsDriverProvider = ['$$animationProvider', /** @this */ function($$animationProvider) {
40494  $$animationProvider.drivers.push('$$animateJsDriver');
40495  this.$get = ['$$animateJs', '$$AnimateRunner', function($$animateJs, $$AnimateRunner) {
40496    return function initDriverFn(animationDetails) {
40497      if (animationDetails.from && animationDetails.to) {
40498        var fromAnimation = prepareAnimation(animationDetails.from);
40499        var toAnimation = prepareAnimation(animationDetails.to);
40500        if (!fromAnimation && !toAnimation) return;
40501
40502        return {
40503          start: function() {
40504            var animationRunners = [];
40505
40506            if (fromAnimation) {
40507              animationRunners.push(fromAnimation.start());
40508            }
40509
40510            if (toAnimation) {
40511              animationRunners.push(toAnimation.start());
40512            }
40513
40514            $$AnimateRunner.all(animationRunners, done);
40515
40516            var runner = new $$AnimateRunner({
40517              end: endFnFactory(),
40518              cancel: endFnFactory()
40519            });
40520
40521            return runner;
40522
40523            function endFnFactory() {
40524              return function() {
40525                forEach(animationRunners, function(runner) {
40526                  // at this point we cannot cancel animations for groups just yet. 1.5+
40527                  runner.end();
40528                });
40529              };
40530            }
40531
40532            function done(status) {
40533              runner.complete(status);
40534            }
40535          }
40536        };
40537      } else {
40538        return prepareAnimation(animationDetails);
40539      }
40540    };
40541
40542    function prepareAnimation(animationDetails) {
40543      // TODO(matsko): make sure to check for grouped animations and delegate down to normal animations
40544      var element = animationDetails.element;
40545      var event = animationDetails.event;
40546      var options = animationDetails.options;
40547      var classes = animationDetails.classes;
40548      return $$animateJs(element, event, classes, options);
40549    }
40550  }];
40551}];
40552
40553var NG_ANIMATE_ATTR_NAME = 'data-ng-animate';
40554var NG_ANIMATE_PIN_DATA = '$ngAnimatePin';
40555var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animateProvider) {
40556  var PRE_DIGEST_STATE = 1;
40557  var RUNNING_STATE = 2;
40558  var ONE_SPACE = ' ';
40559
40560  var rules = this.rules = {
40561    skip: [],
40562    cancel: [],
40563    join: []
40564  };
40565
40566  function 
40566makeTruthyCssClassMap(classString) {
40567    if (!classString) {
40568      return null;
40569    }
40570
40571    var keys = classString.split(ONE_SPACE);
40572    var map = Object.create(null);
40573
40574    forEach(keys, function(key) {
40575      map[key] = true;
40576    });
40577    return map;
40578  }
40579
40580  function hasMatchingClasses(newClassString, currentClassString) {
40581    if (newClassString && currentClassString) {
40582      var currentClassMap = makeTruthyCssClassMap(currentClassString);
40583      return newClassString.split(ONE_SPACE).some(function(className) {
40584        return currentClassMap[className];
40585      });
40586    }
40587  }
40588
40589  function isAllowed(ruleType, element, currentAnimation, previousAnimation) {
40590    return rules[ruleType].some(function(fn) {
40591      return fn(element, currentAnimation, previousAnimation);
40592    });
40593  }
40594
40595  function hasAnimationClasses(animation, and) {
40596    var a = (animation.addClass || '').length > 0;
40597    var b = (animation.removeClass || '').length > 0;
40598    return and ? a && b : a || b;
40599  }
40600
40601  rules.join.push(function(element, newAnimation, currentAnimation) {
40602    // if the new animation is class-based then we can just tack that on
vendor: 11,131 bytes, lines 40603-40902
40603    return !newAnimation.structural && hasAnimationClasses(newAnimation);
40604  });
40605
40606  rules.skip.push(function(element, newAnimation, currentAnimation) {
40607    // there is no need to animate anything if no classes are being added and
40608    // there is no structural animation that will be triggered
40609    return !newAnimation.structural && !hasAnimationClasses(newAnimation);
40610  });
40611
40612  rules.skip.push(function(element, newAnimation, currentAnimation) {
40613    // why should we trigger a new structural animation if the element will
40614    // be removed from the DOM anyway?
40615    return currentAnimation.event === 'leave' && newAnimation.structural;
40616  });
40617
40618  rules.skip.push(function(element, newAnimation, currentAnimation) {
40619    // if there is an ongoing current animation then don't even bother running the class-based animation
40620    return currentAnimation.structural && currentAnimation.state === RUNNING_STATE && !newAnimation.structural;
40621  });
40622
40623  rules.cancel.push(function(element, newAnimation, currentAnimation) {
40624    // there can never be two structural animations running at the same time
40625    return currentAnimation.structural && newAnimation.structural;
40626  });
40627
40628  rules.cancel.push(function(element, newAnimation, currentAnimation) {
40629    // if the previous animation is already running, but the new animation will
40630    // be triggered, but the new animation is structural
40631    return currentAnimation.state === RUNNING_STATE && newAnimation.structural;
40632  });
40633
40634  rules.cancel.push(function(element, newAnimation, currentAnimation) {
40635    // cancel the animation if classes added / removed in both animation cancel each other out,
40636    // but only if the current animation isn't structural
40637
40638    if (currentAnimation.structural) return false;
40639
40640    var nA = newAnimation.addClass;
40641    var nR = newAnimation.removeClass;
40642    var cA = currentAnimation.addClass;
40643    var cR = currentAnimation.removeClass;
40644
40645    // early detection to save the global CPU shortage :)
40646    if ((isUndefined(nA) && isUndefined(nR)) || (isUndefined(cA) && isUndefined(cR))) {
40647      return false;
40648    }
40649
40650    return hasMatchingClasses(nA, cR) || hasMatchingClasses(nR, cA);
40651  });
40652
40653  this.$get = ['$$rAF', '$rootScope', '$rootElement', '$document', '$$HashMap',
40654               '$$animation', '$$AnimateRunner', '$templateRequest', '$$jqLite', '$$forceReflow',
40655       function($$rAF,   $rootScope,   $rootElement,   $document,   $$HashMap,
40656                $$animation,   $$AnimateRunner,   $templateRequest,   $$jqLite,   $$forceReflow) {
40657
40658    var activeAnimationsLookup = new $$HashMap();
40659    var disabledElementsLookup = new $$HashMap();
40660    var animationsEnabled = null;
40661
40662    function postDigestTaskFactory() {
40663      var postDigestCalled = false;
40664      return function(fn) {
40665        // we only issue a call to postDigest before
40666        // it has first passed. This prevents any callbacks
40667        // from not firing once the animation has completed
40668        // since it will be out of the digest cycle.
40669        if (postDigestCalled) {
40670          fn();
40671        } else {
40672          $rootScope.$$postDigest(function() {
40673            postDigestCalled = true;
40674            fn();
40675          });
40676        }
40677      };
40678    }
40679
40680    // Wait until all directive and route-related templates are downloaded and
40681    // compiled. The $templateRequest.totalPendingRequests variable keeps track of
40682    // all of the remote templates being currently downloaded. If there are no
40683    // templates currently downloading then the watcher will still fire anyway.
40684    var deregisterWatch = $rootScope.$watch(
40685      function() { return $templateRequest.totalPendingRequests === 0; },
40686      function(isEmpty) {
40687        if (!isEmpty) return;
40688        deregisterWatch();
40689
40690        // Now that all templates have been downloaded, $animate will wait until
40691        // the post digest queue is empty before enabling animations. By having two
40692        // calls to $postDigest calls we can ensure that the flag is enabled at the
40693        // very end of the post digest queue. Since all of the animations in $animate
40694        // use $postDigest, it's important that the code below executes at the end.
40695        // This basically means that the page is fully downloaded and compiled before
40696        // any animations are triggered.
40697        $rootScope.$$postDigest(function() {
40698          $rootScope.$$postDigest(function() {
40699            // we check for null directly in the event that the application already called
40700            // .enabled() with whatever arguments that it provided it with
40701            if (animationsEnabled === null) {
40702              animationsEnabled = true;
40703            }
40704          });
40705        });
40706      }
40707    );
40708
40709    var callbackRegistry = Object.create(null);
40710
40711    // remember that the classNameFilter is set during the provider/config
40712    // stage therefore we can optimize here and setup a helper function
40713    var classNameFilter = $animateProvider.classNameFilter();
40714    var isAnimatableClassName = !classNameFilter
40715              ? function() { return true; }
40716              : function(className) {
40717                return classNameFilter.test(className);
40718              };
40719
40720    var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
40721
40722    function normalizeAnimationDetails(element, animation) {
40723      return mergeAnimationDetails(element, animation, {});
40724    }
40725
40726    // IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
40727    var contains = window.Node.prototype.contains || /** @this */ function(arg) {
40728      // eslint-disable-next-line no-bitwise
40729      return this === arg || !!(this.compareDocumentPosition(arg) & 16);
40730    };
40731
40732    function findCallbacks(parent, element, event) {
40733      var targetNode = getDomNode(element);
40734      var targetParentNode = getDomNode(parent);
40735
40736      var matches = [];
40737      var entries = callbackRegistry[event];
40738      if (entries) {
40739        forEach(entries, function(entry) {
40740          if (contains.call(entry.node, targetNode)) {
40741            matches.push(entry.callback);
40742          } else if (event === 'leave' && contains.call(entry.node, targetParentNode)) {
40743            matches.push(entry.callback);
40744          }
40745        });
40746      }
40747
40748      return matches;
40749    }
40750
40751    function filterFromRegistry(list, matchContainer, matchCallback) {
40752      var containerNode = extractElementNode(matchContainer);
40753      return list.filter(function(entry) {
40754        var isMatch = entry.node === containerNode &&
40755                        (!matchCallback || entry.callback === matchCallback);
40756        return !isMatch;
40757      });
40758    }
40759
40760    function cleanupEventListeners(phase, element) {
40761      if (phase === 'close' && !element[0].parentNode) {
40762        // If the element is not attached to a parentNode, it has been removed by
40763        // the domOperation, and we can safely remove the event callbacks
40764        $animate.off(element);
40765      }
40766    }
40767
40768    var $animate = {
40769      on: function(event, container, callback) {
40770        var node = extractElementNode(container);
40771        callbackRegistry[event] = callbackRegistry[event] || [];
40772        callbackRegistry[event].push({
40773          node: node,
40774          callback: callback
40775        });
40776
40777        // Remove the callback when the element is removed from the DOM
40778        jqLite(container).on('$destroy', function() {
40779          var animationDetails = activeAnimationsLookup.get(node);
40780
40781          if (!animationDetails) {
40782            // If there's an animation ongoing, the callback calling code will remove
40783            // the event listeners. If we'd remove here, the callbacks would be removed
40784            // before the animation ends
40785            $animate.off(event, container, callback);
40786          }
40787        });
40788      },
40789
40790      off: function(event, container, callback) {
40791        if (arguments.length === 1 && !isString(arguments[0])) {
40792          container = arguments[0];
40793          for (var eventType in callbackRegistry) {
40794            callbackRegistry[eventType] = filterFromRegistry(callbackRegistry[eventType], container);
40795          }
40796
40797          return;
40798        }
40799
40800        var entries = callbackRegistry[event];
40801        if (!entries) return;
40802
40803        callbackRegistry[event] = arguments.length === 1
40804            ? null
40805            : filterFromRegistry(entries, container, callback);
40806      },
40807
40808      pin: function(element, parentElement) {
40809        assertArg(isElement(element), 'element', 'not an element');
40810        assertArg(isElement(parentElement), 'parentElement', 'not an element');
40811        element.data(NG_ANIMATE_PIN_DATA, parentElement);
40812      },
40813
40814      push: function(element, event, options, domOperation) {
40815        options = options || {};
40816        options.domOperation = domOperation;
40817        return queueAnimation(element, event, options);
40818      },
40819
40820      // this method has four signatures:
40821      //  () - global getter
40822      //  (bool) - global setter
40823      //  (element) - element getter
40824      //  (element, bool) - element setter<F37>
40825      enabled: function(element, bool) {
40826        var argCount = arguments.length;
40827
40828        if (argCount === 0) {
40829          // () - Global getter
40830          bool = !!animationsEnabled;
40831        } else {
40832          var hasElement = isElement(element);
40833
40834          if (!hasElement) {
40835            // (bool) - Global setter
40836            bool = animationsEnabled = !!element;
40837          } else {
40838            var node = getDomNode(element);
40839
40840            if (argCount === 1) {
40841              // (element) - Element getter
40842              bool = !disabledElementsLookup.get(node);
40843            } else {
40844              // (element, bool) - Element setter
40845              disabledElementsLookup.put(node, !bool);
40846            }
40847          }
40848        }
40849
40850        return bool;
40851      }
40852    };
40853
40854    return $animate;
40855
40856    function queueAnimation(element, event, initialOptions) {
40857      // we always make a copy of the options since
40858      // there should never be any side effects on
40859      // the input data when running `$animateCss`.
40860      var options = copy(initialOptions);
40861
40862      var node, parent;
40863      element = stripCommentsFromElement(element);
40864      if (element) {
40865        node = getDomNode(element);
40866        parent = element.parent();
40867      }
40868
40869      options = prepareAnimationOptions(options);
40870
40871      // we create a fake runner with a working promise.
40872      // These methods will become available after the digest has passed
40873      var runner = new $$AnimateRunner();
40874
40875      // this is used to trigger callbacks in postDigest mode
40876      var runInNextPostDigestOrNow = postDigestTaskFactory();
40877
40878      if (isArray(options.addClass)) {
40879        options.addClass = options.addClass.join(' ');
40880      }
40881
40882      if (options.addClass && !isString(options.addClass)) {
40883        options.addClass = null;
40884      }
40885
40886      if (isArray(options.removeClass)) {
40887        options.removeClass = options.removeClass.join(' ');
40888      }
40889
40890      if (options.removeClass && !isString(options.removeClass)) {
40891        options.removeClass = null;
40892      }
40893
40894      if (options.from && !isObject(options.from)) {
40895        options.from = null;
40896      }
40897
40898      if (options.to && !isObject(options.to)) {
40899        options.to = null;
40900      }
40901
40902      // there are situations where a directive issues an animation for
40903      // a jqLite wrapper that contains only comment nodes... If this
40904      // happens then there is no way we can perform an animation
40905      if (!node) {
40906        close();
40907        return runner;
40908      }
40909
40910      var className = [node.className, options.addClass, options.removeClass].join(' ');
40911      if (!isAnimatableClassName(className)) {
40912        close();
40913        return runner;
40914      }
40915
40916      var isStructural = ['enter', 'move', 'leave'].indexOf(event) >= 0;
40917
40918      var documentHidden = $document[0].hidden;
40919
40920      // this is a hard disable of all animations for the application or on
40921      // the element itself, therefore  there is no need to continue further
40922      // past this point if not enabled
40923      // Animations are also disabled if the document is currently hidden (page is not visible
40924      // to the user), because browsers slow down or do not flush calls to requestAnimationFrame
40925      var skipAnimations = !animationsEnabled || documentHidden || disabledElementsLookup.get(node);
40926      var existingAnimation = (!skipAnimations && activeAnimationsLookup.get(node)) || {};
40927      var hasExistingAnimation = !!existingAnimation.state;
40928
40929      // there is no point in traversing the same collection of parent ancestors if a followup
40930      // animation will be run on the same element that already did all that checking work
40931      if (!skipAnimations && (!hasExistingAnimation || existingAnimation.state !== PRE_DIGEST_STATE)) {
40932        skipAnimations = !areAnimationsAllowed(element, parent, event);
40933      }
40934
40935      if (skipAnimations) {
40936        // Callbacks should fire even if the document is hidden (regression fix for issue #14120)
40937        if (documentHidden) notifyProgress(runner, event, 'start');
40938        close();
40939        if (documentHidden) notifyProgress(runner, event, 'close');
40940        return runner;
40941      }
40942
40943      if (isStructural) {
40944        closeChildAnimations(element);
40945      }
40946
40947      var newAnimation = {
40948        structural: isStructural,
40949        element: element,
40950        event: event,
40951        addClass: options.addClass,
40952        removeClass: options.removeClass,
40953        close: close,
40954        options: options,
40955        runner: runner
40956      };
40957
40958      if (hasExistingAnimation) {
40959        var skipAnimationFlag = isAllowed('skip', element, newAnimation, existingAnimation);
40960        if (skipAnimationFlag) {
40961          if (existingAnimation.state === RUNNING_STATE) {
40962            close();
40963            return runner;
40964          } else {
40965            mergeAnimationDetails(element, existingAnimation, newAnimation);
40966            return existingAnimation.runner;
40967          }
40968        }
40969        var cancelAnimationFlag = isAllowed('cancel', element, newAnimation, existingAnimation);
40970        if (cancelAnimationFlag) {
40971          if (existingAnimation.state === RUNNING_STATE) {
40972            // this will end the animation right away and it is safe
40973            // to do so since the animation is already running and the
40974            // runner callback code will run in async
40975            existingAnimation.runner.end();
40976          } else if (existingAnimation.structural) {
40977            // this means that the animation is queued into a digest, but
40978            // hasn't started yet. Therefore it is safe to run the close
40979            // method which will call the runner methods in async.
40980            existingAnimation.close();
40981          } else {
40982            // this will merge the new animation options into existing animation options
40983            mergeAnimationDetails(element, existingAnimation, newAnimation);
40984
40985            return existingAnimation.runner;
40986          }
40987        } else {
40988          // a joined animation means that this animation will take over the existing one
40989          // so an example would involve a leave animation taking over an enter. Then when
40990          // the postDigest kicks in the enter will be ignored.
40991          var joinAnimationFlag = isAllowed('join', element, newAnimation, existingAnimation);
40992          if (joinAnimationFlag) {
40993            if (existingAnimation.state === RUNNING_STATE) {
40994              normalizeAnimationDetails(element, newAnimation);
40995            } else {
vendor: 4,089 bytes, lines 40996-41081
40996              applyGeneratedPreparationClasses(element, isStructural ? event : null, options);
40997
40998              event = newAnimation.event = existingAnimation.event;
40999              options = mergeAnimationDetails(element, existingAnimation, newAnimation);
41000
41001              //we return the same runner since only the option values of this animation will
41002              //be fed into the `existingAnimation`.
41003              return existingAnimation.runner;
41004            }
41005          }
41006        }
41007      } else {
41008        // normalization in this case means that it removes redundant CSS classes that
41009        // already exist (addClass) or do not exist (removeClass) on the element
41010        normalizeAnimationDetails(element, newAnimation);
41011      }
41012
41013      // when the options are merged and cleaned up we may end up not having to do
41014      // an animation at all, therefore we should check this before issuing a post
41015      // digest callback. Structural animations will always run no matter what.
41016      var isValidAnimation = newAnimation.structural;
41017      if (!isValidAnimation) {
41018        // animate (from/to) can be quickly checked first, otherwise we check if any classes are present
41019        isValidAnimation = (newAnimation.event === 'animate' && Object.keys(newAnimation.options.to || {}).length > 0)
41020                            || hasAnimationClasses(newAnimation);
41021      }
41022
41023      if (!isValidAnimation) {
41024        close();
41025        clearElementAnimationState(element);
41026        return runner;
41027      }
41028
41029      // the counter keeps track of cancelled animations
41030      var counter = (existingAnimation.counter || 0) + 1;
41031      newAnimation.counter = counter;
41032
41033      markElementAnimationState(element, PRE_DIGEST_STATE, newAnimation);
41034
41035      $rootScope.$$postDigest(function() {
41036        var animationDetails = activeAnimationsLookup.get(node);
41037        var animationCancelled = !animationDetails;
41038        animationDetails = animationDetails || {};
41039
41040        // if addClass/removeClass is called before something like enter then the
41041        // registered parent element may not be present. The code below will ensure
41042        // that a final value for parent element is obtained
41043        var parentElement = element.parent() || [];
41044
41045        // animate/structural/class-based animations all have requirements. Otherwise there
41046        // is no point in performing an animation. The parent node must also be set.
41047        var isValidAnimation = parentElement.length > 0
41048                                && (animationDetails.event === 'animate'
41049                                    || animationDetails.structural
41050                                    || hasAnimationClasses(animationDetails));
41051
41052        // this means that the previous animation was cancelled
41053        // even if the follow-up animation is the same event
41054        if (animationCancelled || animationDetails.counter !== counter || !isValidAnimation) {
41055          // if another animation did not take over then we need
41056          // to make sure that the domOperation and options are
41057          // handled accordingly
41058          if (animationCancelled) {
41059            applyAnimationClasses(element, options);
41060            applyAnimationStyles(element, options);
41061          }
41062
41063          // if the event changed from something like enter to leave then we do
41064          // it, otherwise if it's the same then the end result will be the same too
41065          if (animationCancelled || (isStructural && animationDetails.event !== event)) {
41066            options.domOperation();
41067            runner.end();
41068          }
41069
41070          // in the event that the element animation was not cancelled or a follow-up animation
41071          // isn't allowed to animate from here then we need to clear the state of the element
41072          // so that any future animations won't read the expired animation data.
41073          if (!isValidAnimation) {
41074            clearElementAnimationState(element);
41075          }
41076
41077          return;
41078        }
41079
41080        // this combined multiple class to addClass / removeClass into a setClass event
41081        // so long as a structural event did not take over the animation
41082        event = !animationDetails.structural && hasAnimationClasses(animationDetails, true)
41083            ? 'setClass'
41084            : animationDetails.event;
41085
41086        markElementAnimationState(element, RUNNING_STATE);
41087        var realRunner = $$animation(element, event, animationDetails.options);
41088
41089        // this will update the runner's flow-control events based on
41090        // the `realRunner` object.
41091        runner.setHost(realRunner);
41092        notifyProgress(runner, event, 'start', {});
41093
41094        realRunner.done(function(status) {
41095          close(!status);
41096          var animationDetails = activeAnimationsLookup.get(node);
41097          if (animationDetails && animationDetails.counter === counter) {
41098            clearElementAnimationState(getDomNode(element));
41099          }
41100          notifyProgress(runner, event, 'close', {});
41101        });
41102      });
41103
41104      return runner;
41105
41106      function notifyProgress(runner, event, phase, data) {
41107        runInNextPostDigestOrNow(function() {
41108          var callbacks = findCallbacks(parent, element, event);
41109          if (callbacks.length) {
41110            // do not optimize this call here to RAF because
41111            // we don't know how heavy the callback code here will
41112            // be and if this code is buffered then this can
41113            // lead to a performance regression.
41114            $$rAF(function() {
41115              forEach(callbacks, function(callback) {
41116                callback(element, phase, data);
41117              });
41118              cleanupEventListeners(phase, element);
41119            });
41120          } else {
41121            cleanupEventListeners(phase, element);
41122          }
41123        });
41124        runner.progress(event, phase, data);
41125      }
41126
41127      function close(reject) {
41128        clearGeneratedClasses(element, options);
41129        applyAnimationClasses(element, options);
41130        applyAnimationStyles(element, options);
41131        options.domOperation();
41132        runner.complete(!reject);
41133      }
41134    }
41135
41136    function closeChildAnimations(element) {
41137      var node = getDomNode(element);
41138      var children = node.querySelectorAll('[' + NG_ANIMATE_ATTR_NAME + ']');
41139      forEach(children, function(child) {
41140        var state = parseInt(child.getAttribute(NG_ANIMATE_ATTR_NAME), 10);
41141        var animationDetails = activeAnimationsLookup.get(child);
41142        if (animationDetails) {
41143          switch (state) {
41144            case RUNNING_STATE:
41145              animationDetails.runner.end();
41146              /* falls through */
41147            case PRE_DIGEST_STATE:
41148              activeAnimationsLookup.remove(child);
41149              break;
41150          }
41151        }
41152      });
41153    }
41154
41155    function clearElementAnimationState(element) {
41156      var node = getDomNode(element);
41157      node.removeAttribute(NG_ANIMATE_ATTR_NAME);
41158      activeAnimationsLookup.remove(node);
41159    }
41160
41161    function isMatchingElement(nodeOrElmA, nodeOrElmB) {
41162      return getDomNode(nodeOrElmA) === getDomNode(nodeOrElmB);
41163    }
41164
41165    /**
41166     * This fn returns false if any of the following is true:
41167     * a) animations on any parent element are disabled, and animations on the element aren't explicitly allowed
41168     * b) a parent element has an ongoing structural animation, and animateChildren is false
41169     * c) the element is not a child of the body
41170     * d) the element is not a child of the $rootElement
41171     */
41172    function areAnimationsAllowed(element, parentElement, event) {
41173      var bodyElement = jqLite($document[0].body);
41174      var bodyElementDetected = isMatchingElement(element, bodyElement) || element[0].nodeName === 'HTML';
41175      var rootElementDetected = isMatchingElement(element, $rootElement);
41176      var parentAnimationDetected = false;
41177      var animateChildren;
41178      var elementDisabled = disabledElementsLookup.get(getDomNode(element));
41179
41180      var parentHost = jqLite.data(element[0], NG_ANIMATE_PIN_DATA);
41181      if (parentHost) {
41182        parentElement = parentHost;
41183      }
41184
41185      parentElement = getDomNode(parentElement);
41186
41187      while (parentElement) {
41188        if (!rootElementDetected) {
41189          // angular doesn't want to attempt to animate elements outside of the application
41190          // therefore we need to ensure that the rootElement is an ancestor of the current element
41191          rootElementDetected = isMatchingElement(parentElement, $rootElement);
41192        }
41193
41194        if (parentElement.nodeType !== ELEMENT_NODE) {
41195          // no point in inspecting the #document element
41196          break;
41197        }
41198
41199        var details = activeAnimationsLookup.get(parentElement) || {};
41200        // either an enter, leave or move animation will commence
41201        // therefore we can't allow any animations to take place
41202        // but if a parent animation is class-based then that's ok
41203        if (!parentAnimationDetected) {
41204          var parentElementDisabled = disabledElementsLookup.get(parentElement);
41205
41206          if (parentElementDisabled === true && elementDisabled !== false) {
41207            // disable animations if the user hasn't explicitly enabled animations on the
41208            // current element
41209            elementDisabled = true;
41210            // element is disabled via parent element, no need to check anything else
41211            break;
41212          } else if (parentElementDisabled === false) {
41213            elementDisabled = false;
vendor: 4,789 bytes, lines 41214-41366
41214          }
41215          parentAnimationDetected = details.structural;
41216        }
41217
41218        if (isUndefined(animateChildren) || animateChildren === true) {
41219          var value = jqLite.data(parentElement, NG_ANIMATE_CHILDREN_DATA);
41220          if (isDefined(value)) {
41221            animateChildren = value;
41222          }
41223        }
41224
41225        // there is no need to continue traversing at this point
41226        if (parentAnimationDetected && animateChildren === false) break;
41227
41228        if (!bodyElementDetected) {
41229          // we also need to ensure that the element is or will be a part of the body element
41230          // otherwise it is pointless to even issue an animation to be rendered
41231          bodyElementDetected = isMatchingElement(parentElement, bodyElement);
41232        }
41233
41234        if (bodyElementDetected && rootElementDetected) {
41235          // If both body and root have been found, any other checks are pointless,
41236          // as no animation data should live outside the application
41237          break;
41238        }
41239
41240        if (!rootElementDetected) {
41241          // If no rootElement is detected, check if the parentElement is pinned to another element
41242          parentHost = jqLite.data(parentElement, NG_ANIMATE_PIN_DATA);
41243          if (parentHost) {
41244            // The pin target element becomes the next parent element
41245            parentElement = getDomNode(parentHost);
41246            continue;
41247          }
41248        }
41249
41250        parentElement = parentElement.parentNode;
41251      }
41252
41253      var allowAnimation = (!parentAnimationDetected || animateChildren) && elementDisabled !== true;
41254      return allowAnimation && rootElementDetected && bodyElementDetected;
41255    }
41256
41257    function markElementAnimationState(element, state, details) {
41258      details = details || {};
41259      details.state = state;
41260
41261      var node = getDomNode(element);
41262      node.setAttribute(NG_ANIMATE_ATTR_NAME, state);
41263
41264      var oldValue = activeAnimationsLookup.get(node);
41265      var newValue = oldValue
41266          ? extend(oldValue, details)
41267          : details;
41268      activeAnimationsLookup.put(node, newValue);
41269    }
41270  }];
41271}];
41272
41273/* exported $$AnimationProvider */
41274
41275var $$AnimationProvider = ['$animateProvider', /** @this */ function($animateProvider) {
41276  var NG_ANIMATE_REF_ATTR = 'ng-animate-ref';
41277
41278  var drivers = this.drivers = [];
41279
41280  var RUNNER_STORAGE_KEY = '$$animationRunner';
41281
41282  function setRunner(element, runner) {
41283    element.data(RUNNER_STORAGE_KEY, runner);
41284  }
41285
41286  function removeRunner(element) {
41287    element.removeData(RUNNER_STORAGE_KEY);
41288  }
41289
41290  function getRunner(element) {
41291    return element.data(RUNNER_STORAGE_KEY);
41292  }
41293
41294  this.$get = ['$$jqLite', '$rootScope', '$injector', '$$AnimateRunner', '$$HashMap', '$$rAFScheduler',
41295       function($$jqLite,   $rootScope,   $injector,   $$AnimateRunner,   $$HashMap,   $$rAFScheduler) {
41296
41297    var animationQueue = [];
41298    var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
41299
41300    function sortAnimations(animations) {
41301      var tree = { children: [] };
41302      var i, lookup = new $$HashMap();
41303
41304      // this is done first beforehand so that the hashmap
41305      // is filled with a list of the elements that will be animated
41306      for (i = 0; i < animations.length; i++) {
41307        var animation = animations[i];
41308        lookup.put(animation.domNode, animations[i] = {
41309          domNode: animation.domNode,
41310          fn: animation.fn,
41311          children: []
41312        });
41313      }
41314
41315      for (i = 0; i < animations.length; i++) {
41316        processNode(animations[i]);
41317      }
41318
41319      return flatten(tree);
41320
41321      function processNode(entry) {
41322        if (entry.processed) return entry;
41323        entry.processed = true;
41324
41325        var elementNode = entry.domNode;
41326        var parentNode = elementNode.parentNode;
41327        lookup.put(elementNode, entry);
41328
41329        var parentEntry;
41330        while (parentNode) {
41331          parentEntry = lookup.get(parentNode);
41332          if (parentEntry) {
41333            if (!parentEntry.processed) {
41334              parentEntry = processNode(parentEntry);
41335            }
41336            break;
41337          }
41338          parentNode = parentNode.parentNode;
41339        }
41340
41341        (parentEntry || tree).children.push(entry);
41342        return entry;
41343      }
41344
41345      function flatten(tree) {
41346        var result = [];
41347        var queue = [];
41348        var i;
41349
41350        for (i = 0; i < tree.children.length; i++) {
41351          queue.push(tree.children[i]);
41352        }
41353
41354        var remainingLevelEntries = queue.length;
41355        var nextLevelEntries = 0;
41356        var row = [];
41357
41358        for (i = 0; i < queue.length; i++) {
41359          var entry = queue[i];
41360          if (remainingLevelEntries <= 0) {
41361            remainingLevelEntries = nextLevelEntries;
41362            nextLevelEntries = 0;
41363            result.push(row);
41364            row = [];
41365          }
41366          row.push(entry.fn);
41367          entry.children.forEach(function(childEntry) {
41368            nextLevelEntries++;
41369            queue.push(childEntry);
41370          });
41371          remainingLevelEntries--;
41372        }
41373
41374        if (row.length) {
41375          result.push(row);
41376        }
41377
41378        return result;
41379      }
41380    }
41381
41382    // TODO(matsko): document the signature in a better way
41383    return function(element, event, options) {
41384      options = prepareAnimationOptions(options);
41385      var isStructural = ['enter', 'move', 'leave'].indexOf(event) >= 0;
41386
41387      // there is no animation at the current moment, however
41388      // these runner methods will get later updated with the
41389      // methods leading into the driver's end/cancel methods
41390      // for now they just stop the animation from starting
41391      var runner = new $$AnimateRunner({
41392        end: function() { close(); },
41393        cancel: function() { close(true); }
41394      });
41395
41396      if (!drivers.length) {
41397        close();
41398        return runner;
41399      }
41400
41401      setRunner(element, runner);
41402
41403      var classes = mergeClasses(element.attr('class'), mergeClasses(options.addClass, options.removeClass));
41404      var tempClasses = options.tempClasses;
41405      if (tempClasses) {
41406        classes += ' ' + tempClasses;
41407        options.tempClasses = null;
41408      }
41409
41410      var prepareClassName;
41411      if (isStructural) {
41412        prepareClassName = 'ng-' + event + PREPARE_CLASS_SUFFIX;
41413        $$jqLite.addClass(element, prepareClassName);
41414      }
41415
41416      animationQueue.push({
41417        // this data is used by the postDigest code and passed into
41418        // the driver step function
41419        element: element,
41420        classes: classes,
41421        event: event,
41422        structural: isStructural,
41423        options: options,
41424        beforeStart: beforeStart,
41425        close: close
41426      });
41427
41428      element.on('$destroy', handleDestroyedElement);
41429
41430      // we only want there to be one function called within the post digest
41431      // block. This way we can group animations for all the animations that
41432      // were apart of the same postDigest flush call.
41433      if (animationQueue.length > 1) return runner;
41434
41435      $rootScope.$$postDigest(function() {
41436        var animations = [];
41437        forEach(animationQueue, function(entry) {
41438          // the element was destroyed early on which removed the runner
41439          // form its storage. This means we can't animate this element
41440          // at all and it already has been closed due to destruction.
41441          if (getRunner(entry.element)) {
41442            animations.push(entry);
41443          } else {
41444            entry.close();
41445          }
41446        });
41447
41448        // now any future animations will be in another postDigest
41449        animationQueue.length = 0;
41450
41451        var groupedAnimations = groupAnimations(animations);
41452        var toBeSortedAnimations = [];
41453
41454        forEach(groupedAnimations, function(animationEntry) {
41455          toBeSortedAnimations.push({
41456            domNode: getDomNode(animationEntry.from ? animationEntry.from.element : animationEntry.element),
41457            fn: function triggerAnimationStart() {
41458              // it's important that we apply the `ng-animate` CSS class and the
41459              // temporary classes before we do any driver invoking since these
41460              // CSS classes may be required for proper CSS detection.
41461              animationEntry.beforeStart();
41462
41463              var startAnimationFn, closeFn = animationEntry.close;
41464
41465              // in the event that the element was removed before the digest runs or
41466              // during the RAF sequencing then we should not trigger the animation.
41467              var targetElement = animationEntry.anchors
41468                  ? (animationEntry.from.element || animationEntry.to.element)
41469                  : animationEntry.element;
41470
41471              if (getRunner(targetElement)) {
41472                var operation = invokeFirstDriver(animationEntry);
41473                if (operation) {
41474                  startAnimationFn = operation.start;
41475                }
41476              }
41477
41478              if (!startAnimationFn) {
41479                closeFn();
41480              } else {
41481                var animationRunner = startAnimationFn();
41482                animationRunner.done(function(status) {
41483                  closeFn(!status);
41484                });
41485                updateAnimationRunners(animationEntry, animationRunner);
41486              }
41487            }
41488          });
41489        });
41490
41491        // we need to sort each of the animations in order of parent to child
41492        // relationships. This ensures that the child classes are applied at the
41493        // right time.
41494        $$rAFScheduler(sortAnimations(toBeSortedAnimations)
vendor: 4,673 bytes, lines 41494-41632
41494);
41495      });
41496
41497      return runner;
41498
41499      // TODO(matsko): change to reference nodes
41500      function getAnchorNodes(node) {
41501        var SELECTOR = '[' + NG_ANIMATE_REF_ATTR + ']';
41502        var items = node.hasAttribute(NG_ANIMATE_REF_ATTR)
41503              ? [node]
41504              : node.querySelectorAll(SELECTOR);
41505        var anchors = [];
41506        forEach(items, function(node) {
41507          var attr = node.getAttribute(NG_ANIMATE_REF_ATTR);
41508          if (attr && attr.length) {
41509            anchors.push(node);
41510          }
41511        });
41512        return anchors;
41513      }
41514
41515      function groupAnimations(animations) {
41516        var preparedAnimations = [];
41517        var refLookup = {};
41518        forEach(animations, function(animation, index) {
41519          var element = animation.element;
41520          var node = getDomNode(element);
41521          var event = animation.event;
41522          var enterOrMove = ['enter', 'move'].indexOf(event) >= 0;
41523          var anchorNodes = animation.structural ? getAnchorNodes(node) : [];
41524
41525          if (anchorNodes.length) {
41526            var direction = enterOrMove ? 'to' : 'from';
41527
41528            forEach(anchorNodes, function(anchor) {
41529              var key = anchor.getAttribute(NG_ANIMATE_REF_ATTR);
41530              refLookup[key] = refLookup[key] || {};
41531              refLookup[key][direction] = {
41532                animationID: index,
41533                element: jqLite(anchor)
41534              };
41535            });
41536          } else {
41537            preparedAnimations.push(animation);
41538          }
41539        });
41540
41541        var usedIndicesLookup = {};
41542        var anchorGroups = {};
41543        forEach(refLookup, function(operations, key) {
41544          var from = operations.from;
41545          var to = operations.to;
41546
41547          if (!from || !to) {
41548            // only one of these is set therefore we can't have an
41549            // anchor animation since all three pieces are required
41550            var index = from ? from.animationID : to.animationID;
41551            var indexKey = index.toString();
41552            if (!usedIndicesLookup[indexKey]) {
41553              usedIndicesLookup[indexKey] = true;
41554              preparedAnimations.push(animations[index]);
41555            }
41556            return;
41557          }
41558
41559          var fromAnimation = animations[from.animationID];
41560          var toAnimation = animations[to.animationID];
41561          var lookupKey = from.animationID.toString();
41562          if (!anchorGroups[lookupKey]) {
41563            var group = anchorGroups[lookupKey] = {
41564              structural: true,
41565              beforeStart: function() {
41566                fromAnimation.beforeStart();
41567                toAnimation.beforeStart();
41568              },
41569              close: function() {
41570                fromAnimation.close();
41571                toAnimation.close();
41572              },
41573              classes: cssClassesIntersection(fromAnimation.classes, toAnimation.classes),
41574              from: fromAnimation,
41575              to: toAnimation,
41576              anchors: [] // TODO(matsko): change to reference nodes
41577            };
41578
41579            // the anchor animations require that the from and to elements both have at least
41580            // one shared CSS class which effectively marries the two elements together to use
41581            // the same animation driver and to properly sequence the anchor animation.
41582            if (group.classes.length) {
41583              preparedAnimations.push(group);
41584            } else {
41585              preparedAnimations.push(fromAnimation);
41586              preparedAnimations.push(toAnimation);
41587            }
41588          }
41589
41590          anchorGroups[lookupKey].anchors.push({
41591            'out': from.element, 'in': to.element
41592          });
41593        });
41594
41595        return preparedAnimations;
41596      }
41597
41598      function cssClassesIntersection(a,b) {
41599        a = a.split(' ');
41600        b = b.split(' ');
41601        var matches = [];
41602
41603        for (var i = 0; i < a.length; i++) {
41604          var aa = a[i];
41605          if (aa.substring(0,3) === 'ng-') continue;
41606
41607          for (var j = 0; j < b.length; j++) {
41608            if (aa === b[j]) {
41609              matches.push(aa);
41610              break;
41611            }
41612          }
41613        }
41614
41615        return matches.join(' ');
41616      }
41617
41618      function invokeFirstDriver(animationDetails) {
41619        // we loop in reverse order since the more general drivers (like CSS and JS)
41620        // may attempt more elements, but custom drivers are more particular
41621        for (var i = drivers.length - 1; i >= 0; i--) {
41622          var driverName = drivers[i];
41623          var factory = $injector.get(driverName);
41624          var driver = factory(animationDetails);
41625          if (driver) {
41626            return driver;
41627          }
41628        }
41629      }
41630
41631      function beforeStart() {
41632        
vendor: 7,723 bytes, lines 41632-41825
41632element.addClass(NG_ANIMATE_CLASSNAME);
41633        if (tempClasses) {
41634          $$jqLite.addClass(element, tempClasses);
41635        }
41636        if (prepareClassName) {
41637          $$jqLite.removeClass(element, prepareClassName);
41638          prepareClassName = null;
41639        }
41640      }
41641
41642      function updateAnimationRunners(animation, newRunner) {
41643        if (animation.from && animation.to) {
41644          update(animation.from.element);
41645          update(animation.to.element);
41646        } else {
41647          update(animation.element);
41648        }
41649
41650        function update(element) {
41651          var runner = getRunner(element);
41652          if (runner) runner.setHost(newRunner);
41653        }
41654      }
41655
41656      function handleDestroyedElement() {
41657        var runner = getRunner(element);
41658        if (runner && (event !== 'leave' || !options.$$domOperationFired)) {
41659          runner.end();
41660        }
41661      }
41662
41663      function close(rejected) {
41664        element.off('$destroy', handleDestroyedElement);
41665        removeRunner(element);
41666
41667        applyAnimationClasses(element, options);
41668        applyAnimationStyles(element, options);
41669        options.domOperation();
41670
41671        if (tempClasses) {
41672          $$jqLite.removeClass(element, tempClasses);
41673        }
41674
41675        element.removeClass(NG_ANIMATE_CLASSNAME);
41676        runner.complete(!rejected);
41677      }
41678    };
41679  }];
41680}];
41681
41682/**
41683 * @ngdoc directive
41684 * @name ngAnimateSwap
41685 * @restrict A
41686 * @scope
41687 *
41688 * @description
41689 *
41690 * ngAnimateSwap is a animation-oriented directive that allows for the container to
41691 * be removed and entered in whenever the associated expression changes. A
41692 * common usecase for this directive is a rotating banner or slider component which
41693 * contains one image being present at a time. When the active image changes
41694 * then the old image will perform a `leave` animation and the new element
41695 * will be inserted via an `enter` animation.
41696 *
41697 * @animations
41698 * | Animation                        | Occurs                               |
41699 * |----------------------------------|--------------------------------------|
41700 * | {@link ng.$animate#enter enter}  | when the new element is inserted to the DOM  |
41701 * | {@link ng.$animate#leave leave}  | when the old element is removed from the DOM |
41702 *
41703 * @example
41704 * <example name="ngAnimateSwap-directive" module="ngAnimateSwapExample"
41705 *          deps="angular-animate.js"
41706 *          animations="true" fixBase="true">
41707 *   <file name="index.html">
41708 *     <div class="container" ng-controller="AppCtrl">
41709 *       <div ng-animate-swap="number" class="cell swap-animation" ng-class="colorClass(number)">
41710 *         {{ number }}
41711 *       </div>
41712 *     </div>
41713 *   </file>
41714 *   <file name="script.js">
41715 *     angular.module('ngAnimateSwapExample', ['ngAnimate'])
41716 *       .controller('AppCtrl', ['$scope', '$interval', function($scope, $interval) {
41717 *         $scope.number = 0;
41718 *         $interval(function() {
41719 *           $scope.number++;
41720 *         }, 1000);
41721 *
41722 *         var colors = ['red','blue','green','yellow','orange'];
41723 *         $scope.colorClass = function(number) {
41724 *           return colors[number % colors.length];
41725 *         };
41726 *       }]);
41727 *   </file>
41728 *  <file name="animations.css">
41729 *  .container {
41730 *    height:250px;
41731 *    width:250px;
41732 *    position:relative;
41733 *    overflow:hidden;
41734 *    border:2px solid black;
41735 *  }
41736 *  .container .cell {
41737 *    font-size:150px;
41738 *    text-align:center;
41739 *    line-height:250px;
41740 *    position:absolute;
41741 *    top:0;
41742 *    left:0;
41743 *    right:0;
41744 *    border-bottom:2px solid black;
41745 *  }
41746 *  .swap-animation.ng-enter, .swap-animation.ng-leave {
41747 *    transition:0.5s linear all;
41748 *  }
41749 *  .swap-animation.ng-enter {
41750 *    top:-250px;
41751 *  }
41752 *  .swap-animation.ng-enter-active {
41753 *    top:0px;
41754 *  }
41755 *  .swap-animation.ng-leave {
41756 *    top:0px;
41757 *  }
41758 *  .swap-animation.ng-leave-active {
41759 *    top:250px;
41760 *  }
41761 *  .red { background:red; }
41762 *  .green { background:green; }
41763 *  .blue { background:blue; }
41764 *  .yellow { background:yellow; }
41765 *  .orange { background:orange; }
41766 *  </file>
41767 * </example>
41768 */
41769var ngAnimateSwapDirective = ['$animate', '$rootScope', function($animate, $rootScope) {
41770  return {
41771    restrict: 'A',
41772    transclude: 'element',
41773    terminal: true,
41774    priority: 600, // we use 600 here to ensure that the directive is caught before others
41775    link: function(scope, $element, attrs, ctrl, $transclude) {
41776      var previousElement, previousScope;
41777      scope.$watchCollection(attrs.ngAnimateSwap || attrs['for'], function(value) {
41778        if (previousElement) {
41779          $animate.leave(previousElement);
41780        }
41781        if (previousScope) {
41782          previousScope.$destroy();
41783          previousScope = null;
41784        }
41785        if (value || value === 0) {
41786          previousScope = scope.$new();
41787          $transclude(previousScope, function(element) {
41788            previousElement = element;
41789            $animate.enter(element, null, $element);
41790          });
41791        }
41792      });
41793    }
41794  };
41795}];
41796
41797/**
41798 * @ngdoc module
41799 * @name ngAnimate
41800 * @description
41801 *
41802 * The `ngAnimate` module provides support for CSS-based animations (keyframes and transitions) as well as JavaScript-based animations via
41803 * callback hooks. Animations are not enabled by default, however, by including `ngAnimate` the animation hooks are enabled for an Angular app.
41804 *
41805 * <div doc-module-components="ngAnimate"></div>
41806 *
41807 * # Usage
41808 * Simply put, there are two ways to make use of animations when ngAnimate is used: by using **CSS** and **JavaScript**. The former works purely based
41809 * using CSS (by using matching CSS selectors/styles) and the latter triggers animations that are registered via `module.animation()`. For
41810 * both CSS and JS animations the sole requirement is to have a matching `CSS class` that exists both in the registered animation and within
41811 * the HTML element that the animation will be triggered on.
41812 *
41813 * ## Directive Support
41814 * The following directives are "animation aware":
41815 *
41816 * | Directive                                                                                                | Supported Animations                                                     |
41817 * |----------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------|
41818 * | {@link ng.directive:ngRepeat#animations ngRepeat}                                                        | enter, leave and move                                                    |
41819 * | {@link ngRoute.directive:ngView#animations ngView}                                                       | enter and leave                                                          |
41820 * | {@link ng.directive:ngInclude#animations ngInclude}                                                      | enter and leave                                                          |
41821 * | {@link ng.directive:ngSwitch#animations ngSwitch}                                                        | enter and leave                                                          |
41822 * | {@link ng.directive:ngIf#animations ngIf}                                                                | enter and leave                                                          |
41823 * | {@link ng.directive:ngClass#animations ngClass}                                                          | add and remove (the CSS class(es) present)                               |
41824 * | {@link ng.directive:ngShow#animations ngShow} & {@link ng.directive:ngHide#animations ngHide}            | add and remove (the ng-hide class value)                                 |
41825 * | {@link ng.directive:form#animation-hooks form} & {@link ng.directive:ngModel#animation-hooks ngModel}
41825    | add and remove (dirty, pristine, valid, invalid & all other validations) |
41826 * | {@link module:ngMessages#animations ngMessages}                                                          | add and remove (ng-active & ng-inactive)                                 |
41827 * | {@link module:ngMessages#animations ngMessage}                                                           | enter and leave                                                          |
41828 *
41829 * (More information can be found by visiting each the documentation associated with each directive.)
41830 *
41831 * ## CSS-based Animations
41832 *
41833 * CSS-based animations with ngAnimate are unique since they require no JavaScript code at all. By using a CSS class that we reference between our HTML
41834 * and CSS code we can create an animation that will be picked up by Angular when an underlying directive performs an operation.
41835 *
41836 * The example below shows how an `enter` animation can be made possible on an element using `ng-if`:
41837 *
41838 * ```html
41839 * <div ng-if="bool" class="fade">
41840 *    Fade me in out
41841 * </div>
41842 * <button ng-click="bool=true">Fade In!</button>
41843 * <button ng-click="bool=false">Fade Out!</button>
41844 * ```
41845 *
41846 * Notice the CSS class **fade**? We can now create the CSS transition code that references this class:
41847 *
41848 * ```css
41849 * /&#42; The starting CSS styles for the enter animation &#42;/
41850 * .fade.ng-enter {
41851 *   transition:0.5s linear all;
41852 *   opacity:0;
41853 * }
41854 *
41855 * /&#42; The finishing CSS styles for the enter animation &#42;/
41856 * .fade.ng-enter.ng-enter-active {
41857 *   opacity:1;
41858 * }
41859 * ```
41860 *
41861 * The key thing to remember here is that, depending on the animation event (which each of the directives above trigger depending on what's going on) two
41862 * generated CSS classes will be applied to the element; in the example above we have `.ng-enter` and `.ng-enter-active`. For CSS transitions, the transition
41863 * code **must** be defined within the starting CSS class (in this case `.ng-enter`). The destination class is what the transition will animate towards.
41864 *
41865 * If for example we wanted to create animations for `leave` and `move` (ngRepeat triggers move) then we can do so using the same CSS naming conventions:
41866 *
41867 * ```css
41868 * /&#42; now the element will fade out before it is removed from the DOM &#42;/
41869 * .fade.ng-leave {
41870 *   transition:0.5s linear all;
41871 *   opacity:1;
41872 * }
41873 * .fade.ng-leave.ng-leave-active {
41874 *   opacity:0;
41875 * }
41876 * ```
41877 *
41878 * We can also make use of **CSS Keyframes** by referencing the keyframe animation within the starting CSS class:
41879 *
41880 * ```css
41881 * /&#42; there is no need to define anything inside of the destination
41882 * CSS class since the keyframe will take charge of the animation &#42;/
41883 * .fade.ng-leave {
41884 *   animation: my_fade_animation 0.5s linear;
41885 *   -webkit-animation: my_fade_animation 0.5s linear;
41886 * }
41887 *
41888 * @keyframes my_fade_animation {
41889 *   from { opacity:1; }
41890 *   to { opacity:0; }
41891 * }
41892 *
41893 * @-webkit-keyframes my_fade_animation {
41894 *   from { opacity:1; }
41895 *   to { opacity:0; }
41896 * }
41897 * ```
41898 *
41899 * Feel free also mix transitions and keyframes together as well as any other CSS classes on the same element.
41900 *
41901 * ### CSS Class-based Animations
41902 *
41903 * Class-based animations (animations that are triggered via `ngClass`, `ngShow`, `ngHide` and some other directives) have a slightly different
41904 * naming convention. Class-based animations are basic enough that a standard transition or keyframe can be referenced on the class being added
41905 * and removed.
41906 *
41907 * For example if we wanted to do a CSS animation for `ngHide` then we place an animation on the `.ng-hide` CSS class:
41908 *
41909 * ```html
41910 * <div ng-show="bool" class="fade">
41911 *   Show and hide me
41912 * </div>
41913 * <button ng-click="bool=!bool">Toggle</button>
41914 *
41915 * <style>
41916 * .fade.ng-hide {
41917 *   transition:0.5s linear all;
41918 *   opacity:0;
41919 * }
41920 * </style>
41921 * ```
41922 *
41923 * All that is going on here with ngShow/ngHide behind the scenes is the `.ng-hide` class is added/removed (when the hidden state is valid). Since
41924 * ngShow and ngHide are animation aware then we can match up a transition and ngAnimate handles the rest.
41925 *
41926 * In addition the addition and removal of the CSS class, ngAnimate also provides two helper methods that we can use to further decorate the animation
41927 * with CSS styles.
41928 *
41929 * ```html
41930 * <div ng-class="{on:onOff}" class="highlight">
41931 *   Highlight this box
41932 * </div>
41933 * <button ng-click="onOff=!onOff">Toggle</button>
41934 *
41935 * <style>
41936 * .highlight {
41937 *   transition:0.5s linear all;
41938 * }
41939 * .highlight.on-add {
41940 *   background:white;
41941 * }
41942 * .highlight.on {
41943 *   background:yellow;
41944 * }
41945 * .highlight.on-remove {
41946 *   background:black;
41947 * }
41948 * </style>
41949 * ```
41950 *
41951 * We can also make use of CSS keyframes by placing them within the CSS classes.
41952 *
41953 *
41954 * ### CSS Staggering Animations
41955 * A Staggering animation is a collection of animations that are issued with a slight delay in between each successive operation resulting in a
41956 * curtain-like effect. The ngAnimate module (versions >
41956=1.2) supports staggering animations and the stagger effect can be
41957 * performed by creating a **ng-EVENT-stagger** CSS class and attaching that class to the base CSS class used for
41958 * the animation. The style property expected within the stagger class can either be a **transition-delay** or an
41959 * **animation-delay** property (or both if your animation contains both transitions and keyframe animations).
41960 *
41961 * ```css
41962 * .my-animation.ng-enter {
41963 *   /&#42; standard transition code &#42;/
41964 *   transition: 1s linear all;
41965 *   opacity:0;
41966 * }
41967 * .my-animation.ng-enter-stagger {
41968 *   /&#42; this will have a 100ms delay between each successive leave animation &#42;/
41969 *   transition-delay: 0.1s;
41970 *
41971 *   /&#42; As of 1.4.4, this must always be set: it signals ngAnimate
41972 *     to not accidentally inherit a delay property from another CSS class &#42;/
41973 *   transition-duration: 0s;
41974 * }
41975 * .my-animation.ng-enter.ng-enter-active {
41976 *   /&#42; standard transition styles &#42;/
41977 *   opacity:1;
41978 * }
41979 * ```
41980 *
41981 * Staggering animations work by default in ngRepeat (so long as the CSS class is defined). Outside of ngRepeat, to use staggering animations
41982 * on your own, they can be triggered by firing multiple calls to the same event on $animate. However, the restrictions surrounding this
41983 * are that each of the elements must have the same CSS className value as well as the same parent element. A stagger operation
41984 * will also be reset if one or more animation frames have passed since the multiple calls to `$animate` were fired.
41985 *
41986 * The following code will issue the **ng-leave-stagger** event on the element provided:
41987 *
41988 * ```js
41989 * var kids = parent.children();
41990 *
41991 * $animate.leave(kids[0]); //stagger index=0
41992 * $animate.leave(kids[1]); //stagger index=1
41993 * $animate.leave(kids[2]); //stagger index=2
41994 * $animate.leave(kids[3]); //stagger index=3
41995 * $animate.leave(kids[4]); //stagger index=4
41996 *
41997 * window.requestAnimationFrame(function() {
41998 *   //stagger has reset itself
41999 *   $animate.leave(kids[5]); //stagger index=0
42000 *   $animate.leave(kids[6]); //stagger index=1
42001 *
42002 *   $scope.$digest();
42003 * });
42004 * ```
42005 *
42006 * Stagger animations are currently only supported within CSS-defined animations.
42007 *
42008 * ### The `ng-animate` CSS class
42009 *
42010 * When ngAnimate is animating an element it will apply the `ng-animate` CSS class to the element for the duration of the animation.
42011 * This is a temporary CSS class and it will be removed once the animation is over (for both JavaScript and CSS-based animations).
42012 *
42013 * Therefore, animations can be applied to an element using this temporary class directly via CSS.
42014 *
42015 * ```css
42016 * .zipper.ng-animate {
42017 *   transition:0.5s linear all;
42018 * }
42019 * .zipper.ng-enter {
42020 *   opacity:0;
42021 * }
42022 * .zipper.ng-enter.ng-enter-active {
42023 *   opacity:1;
42024 * }
42025 * .zipper.ng-leave {
42026 *   opacity:1;
42027 * }
42028 * .zipper.ng-leave.ng-leave-active {
42029 *   opacity:0;
42030 * }
42031 * ```
42032 *
42033 * (Note that the `ng-animate` CSS class is reserved and it cannot be applied on an element directly since ngAnimate will always remove
42034 * the CSS class once an animation has completed.)
42035 *
42036 *
42037 * ### The `ng-[event]-prepare` class
42038 *
42039 * This is a special class that can be used to prevent unwanted flickering / flash of content before
42040 * the actual animation starts. The class is added as soon as an animation is initialized, but removed
42041 * before the actual animation starts (after waiting for a $digest).
42042 * It is also only added for *structural* animations (`enter`, `move`, and `leave`).
42043 *
42044 * In practice, flickering can appear when nesting elements with structural animations such as `ngIf`
42045 * into elements that have class-based animations such as `ngClass`.
42046 *
42047 * ```html
42048 * <div ng-class="{red: myProp}">
42049 *   <div ng-class="{blue: myProp}">
42050 *     <div class="message" ng-if="myProp"></div>
42051 *   </div>
42052 * </div>
42053 * ```
42054 *
42055 * It is possible that during the `enter` animation, the `.message` div will be briefly visible before it starts animating.
42056 * In that case, you can add styles to the CSS that make sure the element stays hidden before the animation starts:
42057 *
42058 * ```css
42059 * .message.ng-enter-prepare {
42060 *   opacity: 0;
42061 * }
42062 *
42063 * ```
42064 *
42065 * ## JavaScript-based Animations
42066 *
42067 * ngAnimate also allows for animations to be consumed by JavaScript code. The approach is similar to CSS-based animations (where there is a shared
42068 * CSS class that is referenced in our HTML code) but in addition we need to register the JavaScript animation on the module. By making use of the
42069 * `module.animation()` module function we can register the animation.
42070 *
42071 * Let's see an example of a enter/leave animation using `ngRepeat`:
42072 *
42073 * ```html
42074 * <div ng-repeat="item in items" class="slide">
42075 *   {{ item }}
42076 * </div>
42077 * ```
42078 *
42079 * See the **slide** CSS class? Let's use that class to define an animation that we'll structure in our module code by using `module.animation`:
42080 *
42081 * ```js
42082 * myModule.animation('.slide', [function() {
42083 *   return {
42084 *     // make note that other events (like addClass/removeClass)
42085 *     // have different function input parameters
42086 *     enter: function(element, doneFn) {
42087 *       jQuery(element).fadeIn(1000, doneFn);
42088 *
42089 *       // remember to call doneFn so that angular
42090 *       // knows that the animation has concluded
42091 *     },
42092 *
42093 *     move: function(element, doneFn) {
42094 *       jQuery(element).fadeIn(1000, doneFn);
42095 *     },
42096 *
42097 *     leave: function(element, doneFn) {
42098 *       jQuery(element).fadeOut(1000, doneFn);
42099 *     }
42100 *   }
42101 * }]);
42102 * ```
42103 *
42104 * The nice thing about JS-based animations is that we can inject other services and make use of advanced animation libraries such as
42105 * greensock.js and velocity.js.
42106 *
42107 * If our animation code class-based (meaning that something like `ngClass`, `ngHide` and `ngShow` triggers it) then we can still define
42108 * our animations inside of the same registered animation, however, the function input arguments are a bit different:
42109 *
42110 * ```html
42111 * <div ng-class="color" class="colorful">
42112 *   this box is moody
42113 * </div>
42114 * <button ng-click="color='red'">Change to red</button>
42115 * <button ng-click="color='blue'">Change to blue</button>
42116 * <button ng-click="color='green'">Change to green</button>
42117 * ```
42118 *
42119 * ```js
42120 * myModule.animation('.colorful', [function() {
42121 *   return {
42122 *     addClass: function(element, className, doneFn) {
42123 *       // do some cool animation and call the doneFn
42124 *     },
42125 *     removeClass: function(element, className, doneFn) {
42126 *       // do some cool animation and call the doneFn
42127 *     },
42128 *     setClass: function(element, addedClass, removedClass, doneFn) {
42129 *       // do some cool animation and call the doneFn
42130 *     }
42131 *   }
42132 * }]);
42133 * ```
42134 *
42135 * ## CSS + JS Animations Together
42136 *
42137 * AngularJS 1.4 and higher has taken steps to make the amalgamation of CSS and JS animations more flexible. However, unlike earlier versions of Angular,
42138 * defining CSS and JS animations to work off of the same CSS class will not work anymore. Therefore the example below will only result in **JS animations taking
42139 * charge of the animation**:
42140 *
42141 * ```html
42142 * <div ng-if="bool" class="slide">
42143 *   Slide in and out
42144 * </div>
42145 * ```
42146 *
42147 * ```js
42148 * myModule.animation('.slide', [function() {
42149 *   return {
42150 *     enter: function(element, doneFn) {
42151 *       jQuery(element).slideIn(1000, doneFn);
42152 *     }
42153 *   }
42154 * }]);
42155 * ```
42156 *
42157 * ```css
42158 * .slide.ng-enter {
42159 *   transition:0.5s linear all;
42160 *   transform:translateY(-100px);
42161 * }
42162 * .slide.ng-enter.ng-enter-active {
42163 *   transform:translateY(0);
42164 * }
42165 * ```
42166 *
42167 * Does this mean that CSS and JS animations cannot be used together? Do JS-based animations always have higher priority? We can make up for the
42168 * lack of CSS animations by using the `$animateCss` service to trigger our own tweaked-out, CSS-based animations directly from
42169 * our own JS-based animation code:
42170 *
42171 * ```js
42172 * myModule.animation('.slide', ['$animateCss', function($animateCss) {
42173 *   return {
42174 *     enter: function(element) {
42175*        // this will trigger `.slide.ng-enter` and `.slide.ng-enter-active`.
42176 *       return $animateCss(element, {
42177 *         event: 'enter',
42178 *         structural: true
42179 *       });
42180 *     }
42181 *   }
42182 * }]);
42183 * ```
42184 *
42185 * The nice thing here is that we can save bandwidth by sticking to our CSS-based animation code and we don't need to rely on a 3rd-party animation framework.
42186 *
42187 * The `$animateCss` service is very powerful since we can feed in all kinds of extra properties that will be evaluated and fed into a CSS transition or
42188 * keyframe animation. For example if we wanted to animate the height of an element while adding and removing classes then we can do so by providing that
42189 * data into `$animateCss` directly:
42190 *
42191 * ```js
42192 * myModule.animation('.slide', ['$animateCss', function($animateCss) {
42193 *   return {
42194 *     enter: function(element) {
42195 *       return $animateCss(element, {
42196 *         event: 'enter',
42197 *         structural: true,
42198 *         addClass: 'maroon-setting',
42199 *         from: { height:0 },
42200 *         to: { height: 200 }
42201 *       });
42202 *     }
42203 *   }
42204 * }]);
42205 * ```
42206 *
42207 * Now we can fill in the rest via our transition CSS code:
42208 *
42209 * ```css
42210 * /&#42; the transition tells ngAnimate to make the animation happen &#42;/
42211 * .slide.ng-enter { transition:0.5s linear all; }
42212 *
42213 * /&#42; this extra CSS class will be absorbed into the transition
42214 * since the $animateCss code is adding the class &#42;/
42215 * .maroon-setting { background:red; }
42216 * ```
42217 *
42218 * And `$animateCss` will figure out the rest. Just make sure to have the `done()` callback fire the `doneFn` function to signal when the animation is over.
42219 *
42220 * To learn more about what's possible be sure to visit the {@link ngAnimate.$animateCss $animateCss service}.
42221 *
42222 * ## Animation Anchoring (via `ng-animate-ref`)
42223 *
42224 * ngAnimate in AngularJS 1.4 comes packed with the ability to cross-animate elements between
42225 * structural areas of an application (like views) by pairing up elements using an attribute
42226 * called `ng-animate-ref`.
42227 *
42228 * Let's say for example we have two views that are managed by `ng-view` and we want to show
42229 * that there is a relationship between two components situated in within these views. By using the
42230 * `ng-animate-ref` attribute we can identify that the two components are paired together and we
42231 * can then attach an animation, which is triggered when the view changes.
42232 *
42233 * Say for example we have the following template code:
42234 *
42235 * ```html
42236 * <!-- index.html -->
42237 * <div ng-view class="view-animation">
42238 * </div>
42239 *
42240 * <!-- home.html -->
42241 * <a href="#/banner-page">
42242 *   <img src="./banner.jpg" class="banner" ng-animate-ref="banner">
42243 * </a>
42244 *
42245 * <!-- banner-page.html -->
42246 * <img src="./banner.jpg" class="banner" ng-animate-ref="banner">
42247 * ```
42248 *
42249 * Now, when the view changes (once the link is clicked), ngAnimate will examine the
42250 * HTML contents to see if there is a match reference between any components in the view
42251 * that is leaving and the view that is entering. It will scan both the view which is being
42252 * removed (leave) and inserted (enter) to see if there are any paired DOM elements that
42253 * contain a matching ref value.
42254 *
42255 * The two images match since they share the same ref value. ngAnimate will now create a
42256 * transport element (which is a clone of the first image element) and it will then attempt
42257 * to animate to the position of the second image element in the next view. For the animation to
42258 * work a special CSS class called `ng-anchor` will be added to the transported element.
42259 *
42260 * We can now attach a transition onto the `.banner.ng-anchor` CSS class and then
42261 * ngAnimate will handle the entire transition for us as well as the addition and removal of
42262 * any changes of CSS classes between the elements:
42263 *
42264 * ```css
42265 * .banner.ng-anchor {
42266 *   /&#42; this animation will last for 1 second since there are
42267 *          two phases to the animation (an `in` and an `out` phase) &#42;/
42268 *   transition:0.5s linear all;
42269 * }
42270 * ```
42271 *
42272 * We also **must** include animations for the views that are being entered and removed
42273 * (otherwise anchoring wouldn't be possible since the new view would be inserted right away).
42274 *
42275 * ```css
42276 * .view-animation.ng-enter, .view-animation.ng-leave {
42277 *   transition:0.5s linear all;
42278 *   position:fixed;
42279 *   left:0;
42280 *   top:0;
42281 *   width:100%;
42282 * }
42283 * .view-animation.ng-enter {
42284 *   transform:translateX(100%);
42285 * }
42286 * .view-animation.ng-leave,
42287 * .view-animation.ng-enter.ng-enter-active {
42288 *   transform:translateX(0%);
42289 * }
42290 * .view-animation.ng-leave.ng-leave-active {
42291 *   transform:translateX(-100%);
42292 * }
42293 * ```
42294 *
42295 * Now we can jump back to the anchor animation. When the animation happens, there are two stages that occur:
42296 * an `out` and an `in` stage. The `out` stage happens first and that is when the element is animated away
42297 * from its origin. Once that animation is over then the `in` stage occurs which animates the
42298 * element to its destination. The reason why there are two animations is to give enough time
42299 * for the enter animation on the new element to be ready.
42300 *
42301 * The example above sets up a transition for both the in and out phases, but we can also target the out or
42302 * in phases directly via `ng-anchor-out` and `ng-anchor-in`.
42303 *
42304 * ```css
42305 * .banner.ng-anchor-out {
42306 *   transition: 0.5s linear all;
42307 *
42308 *   /&#42; the scale will be applied during the out animation,
42309 *          but will be animated away when the in animation runs &#42;/
42310 *   transform: scale(1.2);
42311 * }
42312 *
42313 * .banner.ng-anchor-in {
42314 *   transition: 1s linear all;
42315 * }
42316 * ```
42317 *
42318 *
42319 *
42320 *
42321 * ### Anchoring Demo
42322 *
42323  <example module="anchoringExample"
42324           name="anchoringExample"
42325           id="anchoringExample"
42326           deps="angular-animate.js;angular-route.js"
42327           animations="true">
42328    <file name="index.html">
42329      <a href="#/">Home</a>
42330      <hr />
42331      <div class="view-container">
42332        <div ng-view class="view"></div>
42333      </div>
42334    </file>
42335    <file name="script.js">
42336      angular.module('anchoringExample', ['ngAnimate', 'ngRoute'])
42337        .config(['$routeProvider', function($routeProvider) {
42338          $routeProvider.when('/', {
42339            templateUrl: 'home.html',
42340            controller: 'HomeController as home'
42341          });
42342          $routeProvider.when('/profile/:id', {
42343            templateUrl: 'profile.html',
42344            controller: 'ProfileController as profile'
42345          });
42346        }])
42347        .run(['$rootScope', function($rootScope) {
42348          $rootScope.records = [
42349            { id: 1, title: 'Miss Beulah Roob' },
42350            { id: 2, title: 'Trent Morissette' },
42351            { id: 3, title: 'Miss Ava Pouros' },
42352            { id: 4, title: 'Rod Pouros' },
42353            { id: 5, title: 'Abdul Rice' },
42354            { id: 6, title: 'Laurie Rutherford Sr.' },
42355            { id: 7, title: 'Nakia McLaughlin' },
42356            { id: 8, title: 'Jordon Blanda DVM' },
42357            { id: 9, title: 'Rhoda Hand' },
42358            { id: 10, title: 'Alexandrea Sauer' }
42359          ];
42360        }])
42361        .controller('HomeController', [function() {
42362          //empty
42363        }])
42364        .controller('ProfileController', ['$rootScope', '$routeParam
42364s',
42365            function ProfileController($rootScope, $routeParams) {
42366          var index = parseInt($routeParams.id, 10);
42367          var record = $rootScope.records[index - 1];
42368
42369          this.title = record.title;
42370          this.id = record.id;
42371        }]);
42372    </file>
42373    <file name="home.html">
42374      <h2>Welcome to the home page</h1>
42375      <p>Please click on an element</p>
42376      <a class="record"
42377         ng-href="#/profile/{{ record.id }}"
42378         ng-animate-ref="{{ record.id }}"
42379         ng-repeat="record in records">
42380        {{ record.title }}
42381      </a>
42382    </file>
42383    <file name="profile.html">
42384      <div class="profile record" ng-animate-ref="{{ profile.id }}">
42385        {{ profile.title }}
42386      </div>
42387    </file>
42388    <file name="animations.css">
42389      .record {
42390        display:block;
42391        font-size:20px;
42392      }
42393      .profile {
42394        background:black;
42395        color:white;
42396        font-size:100px;
42397      }
42398      .view-container {
42399        position:relative;
42400      }
42401      .view-container > .view.ng-animate {
42402        position:absolute;
42403        top:0;
42404        left:0;
42405        width:100%;
42406        min-height:500px;
42407      }
42408      .view.ng-enter, .view.ng-leave,
42409      .record.ng-anchor {
42410        transition:0.5s linear all;
42411      }
42412      .view.ng-enter {
42413        transform:translateX(100%);
42414      }
42415      .view.ng-enter.ng-enter-active, .view.ng-leave {
42416        transform:translateX(0%);
42417      }
42418      .view.ng-leave.ng-leave-active {
42419        transform:translateX(-100%);
42420      }
42421      .record.ng-anchor-out {
42422        background:red;
42423      }
42424    </file>
42425  </example>
42426 *
42427 * ### How is the element transported?
42428 *
42429 * When an anchor animation occurs, ngAnimate will clone the starting element and position it exactly where the starting
42430 * element is located on screen via absolute positioning. The cloned element will be placed inside of the root element
42431 * of the application (where ng-app was defined) and all of the CSS classes of the starting element will be applied. The
42432 * element will then animate into the `out` and `in` animations and will eventually reach the coordinates and match
42433 * the dimensions of the destination element. During the entire animation a CSS class of `.ng-animate-shim` will be applied
42434 * to both the starting and destination elements in order to hide them from being visible (the CSS styling for the class
42435 * is: `visibility:hidden`). Once the anchor reaches its destination then it will be removed and the destination element
42436 * will become visible since the shim class will be removed.
42437 *
42438 * ### How is the morphing handled?
42439 *
42440 * CSS Anchoring relies on transitions and keyframes and the internal code is intelligent enough to figure out
42441 * what CSS classes differ between the starting element and the destination element. These different CSS classes
42442 * will be added/removed on the anchor element and a transition will be applied (the transition that is provided
42443 * in the anchor class). Long story short, ngAnimate will figure out what classes to add and remove which will
42444 * make the transition of the element as smooth and automatic as possible. Be sure to use simple CSS classes that
42445 * do not rely on DOM nesting structure so that the anchor element appears the same as the starting element (since
42446 * the cloned element is placed inside of root element which is likely close to the body element).
42447 *
42448 * Note that if the root element is on the `<html>` element then the cloned node will be placed inside of body.
42449 *
42450 *
42451 * ## Using $animate in your directive code
42452 *
42453 * So far we've explored how to feed in animations into an Angular application, but how do we trigger animations within our own directives in our application?
42454 * By injecting the `$animate` service into our directive code, we can trigger structural and class-based hooks which can then be c
42454onsumed by animations. Let's
42455 * imagine we have a greeting box that shows and hides itself when the data changes
42456 *
42457 * ```html
42458 * <greeting-box active="onOrOff">Hi there</greeting-box>
42459 * ```
42460 *
42461 * ```js
42462 * ngModule.directive('greetingBox', ['$animate', function($animate) {
42463 *   return function(scope, element, attrs) {
42464 *     attrs.$observe('active', function(value) {
42465 *       value ? $animate.addClass(element, 'on') : $animate.removeClass(element, 'on');
42466 *     });
42467 *   });
42468 * }]);
42469 * ```
42470 *
42471 * Now the `on` CSS class is added and removed on the greeting box component. Now if we add a CSS class on top of the greeting box element
42472 * in our HTML code then we can trigger a CSS or JS animation to happen.
42473 *
42474 * ```css
42475 * /&#42; normally we would create a CSS class to reference on the element &#42;/
42476 * greeting-box.on { transition:0.5s linear all; background:green; color:white; }
42477 * ```
42478 *
42479 * The `$animate` service contains a variety of other methods like `enter`, `leave`, `animate` and `setClass`. To learn more about what's
42480 * possible be sure to visit the {@link ng.$animate $animate service API page}.
42481 *
42482 *
42483 * ## Callbacks and Promises
42484 *
42485 * When `$animate` is called it returns a promise that can be used to capture when the animation has ended. Therefore if we were to trigger
42486 * an animation (within our directive code) then we can continue performing directive and scope related activities after the animation has
42487 * ended by chaining onto the returned promise that animation method returns.
42488 *
42489 * ```js
42490 * // somewhere within the depths of the directive
42491 * $animate.enter(element, parent).then(function() {
42492 *   //the animation has completed
42493 * });
42494 * ```
42495 *
42496 * (Note that earlier versions of Angular prior to v1.4 required the promise code to be wrapped using `$scope.$apply(...)`. This is not the case
42497 * anymore.)
42498 *
42499 * In addition to the animation promise, we can also make use of animation-related callbacks within our directives and controller code by registering
42500 * an event listener using the `$animate` service. Let's say for example that an animation was triggered on our view
42501 * routing controller to hook into that:
42502 *
42503 * ```js
42504 * ngModule.controller('HomePageController', ['$animate', function($animate) {
42505 *   $animate.on('enter', ngViewElement, function(element) {
42506 *     // the animation for this route has completed
42507 *   }]);
42508 * }])
42509 * ```
42510 *
42511 * (Note that you will need to trigger a digest within the callback to get angular to notice any scope-related changes.)
42512 */
42513
42514var copy;
42515var extend;
42516var forEach;
42517var isArray;
42518var isDefined;
42519var isElement;
42520var isFunction;
42521var isObject;
42522var isString;
42523var isUndefined;
42524var jqLite;
42525var noop;
42526
42527/**
42528 * @ngdoc service
42529 * @name $animate
42530 * @kind object
42531 *
42532 * @description
42533 * The ngAnimate `$animate` service documentation is the same for the core `$animate` service.
42534 *
42535 * Click here {@link ng.$animate to learn more about animations with `$animate`}.
42536 */
42537angular.module('ngAnimate', [], function initAngularHelpers() {
42538  // Access helpers from angular core.
42539  // Do it inside a `config` block to ensure `window.angular` is available.
42540  noop        = angular.noop;
42541  copy        = angular.copy;
42542  extend      = angular.extend;
42543  jqLite      = angular.element;
42544  forEach     = angular.forEach;
42545  isArray     = angular.isArray;
42546  isString    = angular.isString;
42547  isObject    = angular.isObject;
42548  isUndefined = angular.isUndefined;
42549  isDefined   = angular.isDefined;
42550  isFunction  = angular.isFunction;
42551  isElement   = angular.isElement;
42552})
42553  .directive('ngAnimateSwap', ngAnimateSwapDirective)
42554
42555  .directive('ngAnimateChildren', $$AnimateChildrenDirective)
42556  .factory('$$rAFScheduler', $$rAFSchedulerFactory)
42557
42558  .provider('$$animateQueue', $$AnimateQueueProvider)
42559  .provider('$$animation', $$AnimationProvider)
42560
42561  .provider('$animateCss', $AnimateCssProvider)
42562  .provider('$$animateCssDriver', $$AnimateCssDriverProvider)
42563
42564  .provider('$$animateJs', $$AnimateJsProvider)
42565  .provider('$$animateJsDriver', $$AnimateJsDriverProvider);
42566
42567
42568})(window, window.angular);
42569
42570/**
42571 * @license AngularJS v1.5.11
42572 * (c) 2010-2017 Google, Inc. http://angularjs.org
42573 * License: MIT
42574 */
42575(function(window, angular) {'use strict';
42576
42577/**
42578 * @ngdoc module
42579 * @name ngCookies
42580 * @description
42581 *
42582 * # ngCookies
42583 *
42584 * The `ngCookies` module provides a convenient wrapper for reading and writing browser cookies.
42585 *
42586 *
42587 * <div doc-module-components="ngCookies"></div>
42588 *
42589 * See {@link ngCookies.$cookies `$cookies`} for usage.
42590 */
42591
42592
42593angular.module('ngCookies', ['ng']).
42594  /**
42595   * @ngdoc provider
42596   * @name $cookiesProvider
42597   * @description
42598   * Use `$cookiesProvider` to change the default behavior of the {@link ngCookies.$cookies $cookies} service.
42599   * */
42600   provider('$cookies', [/** @this */function $CookiesProvider() {
42601    /**
42602     * @ngdoc property
42603     * @name $cookiesProvider#defaults
42604     * @description
42605     *
42606     * Object containing default options to pass when setting cookies.
42607     *
42608     * The object may have following properties:
42609     *
42610     * - **path** - `{string}` - The cookie will be available only for this path and its
42611     *   sub-paths. By default, this is the URL that appears in your `<base>` tag.
42612     * - **domain** - `{string}` - The cookie will be available only for this domain and
42613     *   its sub-domains. For security reasons the user agent will not accept the cookie
42614     *   if the current domain is not a sub-domain of this domain or equal to it.
42615     * - **expires** - `{string|Date}` - String of the form "Wdy, DD Mon YYYY HH:MM:SS GMT"
42616     *   or a Date object indicating the exact date/time this cookie will expire.
42617     * - **secure** - `{boolean}` - If `true`, then the cookie will only be available through a
42618     *   secured connection.
42619     *
42620     * Note: By default, the address that appears in your `<base>` tag will be used as the path.
42621     * This is important so that cookies will be visible for all routes when html5mode is enabled.
42622     *
42623     * @example
42624     *
42625     * ```js
42626     * angular.module('cookiesProviderExample', ['ngCookies'])
42627     *   .config(['$cookiesProvider', function($cookiesProvider) {
42628     *     // Setting default options
42629     *     $cookiesProvider.defaults.domain = 'foo.com';
42630     *     $cookiesProvider.defaults.secure = true;
42631     *   }]);
42632     * ```
42633     **/
42634    var defaults = this.defaults = {};
42635
42636    function calcOptions(options) {
42637      return options ? angular.extend({}, defaults, options) : defaults;
42638    }
42639
42640    /**
42641     * @ngdoc service
42642     * @name $cookies
42643     *
42644     * @description
42645     * Provides read/write access to browser's cookies.
42646     *
42647     * <div class="alert alert-info">
42648     * Up until Angular 1.3, `$cookies` exposed properties that represented the
42649     * current browser cookie values. In version 1.4, this behavior has changed, and
42650     * `$cookies` now provides a standard api of getters, setters etc.
42651     * </div>
42652     *
42653     * Requires the {@link ngCookies `ngCookies`} module to be installed.
42654     *
42655     * @example
42656     *
42657     * ```js
42658     * angular.module('cookiesExample', ['ngCookies'])
42659     *   .controller('ExampleController', ['$cookies', function($cookies) {
42660     *     // Retrieving a cookie
42661     *     var favoriteCookie = $cookies.get('myFavorite');
42662     *     // Setting a cookie
42663     *     $cookies.put('myFavorite', 'oatmeal');
42664     *   }]);
42665     * ```
42666     */
42667    this.$get = ['$$cookieReader', '$$cookieWriter', function($$cookieReader, $$cookieWriter) {
42668      return {
42669        /**
42670         * @ngdoc method
42671         * @name $cookies#get
42672         *
42673         * @description
42674         * Returns the value of given cookie key
42675         *
42676         * @param {string} key Id to use for lookup.
42677         * @returns {string} Raw cookie value.
42678         */
42679        get: function(key) {
42680          return $$cookieReader()[key];
42681        },
42682
42683        /**
42684         * @ngdoc method
42685         * @name $cookies#getObject
42686         *
42687         * @description
42688         * Returns the deserialized value of given cookie key
42689         *
42690         * @param {string} key Id to use for lookup.
42691         * @returns {Object} Deserialized cookie value.
42692         */
42693        getObject: function(key) {
42694          var value = this.get(key);
42695          return value ? angular.fromJson(value) : value;
42696        },
42697
42698        /**
42699         * @ngdoc method
42700         * @name $cookies#getAll
42701         *
42702         * @description
42703         * Returns a key value object with all the cookies
42704         *
42705         * @returns {Object} All cookies
42706         */
42707        getAll: function() {
42708          return $$cookieReader();
42709        },
42710
42711        /**
42712         * @ngdoc method
42713         * @name $cookies#put
42714         *
42715         * @description
42716         * Sets a value for given cookie key
42717         *
42718         * @param {string} key Id for the `value`.
42719         * @param {string} value Raw value to be stored.
42720         * @param {Object=} options Options object.
42721         *    See {@link ngCookies.$cookiesProvider#defaults $cookiesProvider.defaults}
42722         */
42723        put: function(key, value, options) {
42724          $$cookieWriter(key, value, calcOptions(options));
42725        },
42726
42727        /**
42728         * @ngdoc method
42729         * @name $cookies#putObject
42730         *
42731         * @description
42732         * Serializes and sets a value for given cookie key
42733         *
42734         * @param {string} key Id for the `value`.
42735         * @param {Object} value Value to be stored.
42736         * @param {Object=} options Options object.
42737         *    See {@link ngCookies.$cookiesProvider#defaults $cookiesProvider.defaults}
42738         */
42739        putObject: function(key, value, options) {
42740          this.put(key, angular.toJson(value), options);
42741        },
42742
42743        /**
42744         * @ngdoc method
42745         * @name $cookies#remove
42746         *
42747         * @description
42748         * Remove given cookie
42749         *
42750         * @param {string} key Id of the key-value pair to delete.
42751         * @param {Object=} options Options object.
42752         *    See {@link ngCookies.$cookiesProvider#defaults $cookiesProvider.defaults}
42753         */
42754        remove: function(key, options) {
42755          $$cookieWriter(key, undefined, calcOptions(options));
42756        }
42757      };
42758    }];
42759  }]);
42760
42761angular.module('ngCookies').
42762/**
42763 * @ngdoc service
42764 * @name $cookieStore
42765 * @deprecated
42766 * sinceVersion="v1.4.0"
42767 * Please use the {@link ngCookies.$cookies `$cookies`} service instead.
42768 *
42769 * @requires $cookies
42770 *
42771 * @description
42772 * Provides a key-value (string-object) storage, that is backed by session cookies.
42773 * Objects put or retrieved from this storage are automatically serialized or
42774 * deserialized by angular's toJson/fromJson.
42775 *
42776 * Requires the {@link ngCookies `ngCookies`} module to be installed.
42777 *
42778 * @example
42779 *
42780 * ```js
42781 * angular.module('cookieStoreExample', ['ngCookies'])
42782 *   .controller('ExampleController', ['$cookieStore', function($cookieStore) {
42783 *     // Put cookie
42784 *     $cookieStore.put('myFavorite','oatmeal');
42785 *     // Get cookie
42786 *     var favoriteCookie = $cookieStore.get('myFavorite');
42787 *     // Removing a cookie
42788 *     $cookieStore.remove('myFavorite');
42789 *   }]);
42790 * ```
42791 */
42792 factory('$cookieStore', ['$cookies', function($cookies) {
42793
42794    return {
42795      /**
42796       * @ngdoc method
42797       * @name $cookieStore#get
42798       *
42799       * @description
42800       * Returns the value of given cookie key
42801       *
42802       * @param {string} key Id to use for lookup.
42803       * @returns {Object} Deserialized cookie value, undefined if the cookie does not exist.
42804       */
42805      get: function(key) {
42806        return $cookies.getObject(key);
42807      },
42808
42809      /**
42810       * @ngdoc method
42811       * @name $cookieStore#put
42812       *
42813       * @description
42814       * Sets a value for given cookie key
42815       *
42816       * @param {string} key Id for the `value`.
42817       * @param {Object} value Value to be stored.
42818       */
42819      put: function(key, value) {
42820        $cookies.putObject(key, value);
42821      },
42822
42823      /**
42824       * @ngdoc method
42825       * @name $cookieStore#remove
42826       *
42827       * @description
42828       * Remove given cookie
42829       *
42830       * @param {string} key Id of the key-value pair to delete.
42831       */
42832      remove: function(key) {
42833        $cookies.remove(key);
42834      }
42835    };
42836
42837  }]);
42838
42839/**
42840 * @name $$cookieWriter
42841 * @requires $document
42842 *
42843 * @description
42844 * This is a private service for writing cookies
42845 *
42846 * @param {string} name Cookie name
42847 * @param {string=} value Cookie value (if undefined, cookie will be deleted)
42848 * @param {Object=} options Object with options that need to be stored for the cookie.
42849 */
42850function $$CookieWriter($document, $log, $browser) {
42851  var cookiePath = $browser.baseHref();
42852  var rawDocument = $document[0];
42853
42854  function buildCookieString(name, value, options) {
42855    var path, expires;
42856    options = options || {};
42857    expires = options.expires;
42858    path = angular.isDefined(options.path) ? options.path : cookiePath;
42859    if (angular.isUndefined(value)) {
42860      expires = 'Thu, 01 Jan 1970 00:00:00 GMT';
42861      value = '';
42862    }
42863    if (angular.isString(expires)) {
42864      expires = new Date(expires);
42865    }
42866
42867    var str = encodeURIComponent(name) + '=' + encodeURIComponent(value);
42868    str += path ? ';path=' + path : '';
42869    str += options.domain ? ';domain=' + options.domain : '';
42870    str += expires ? ';expires=' + expires.toUTCString() : '';
42871    str += options.secure ? ';secure' : '';
42872
42873    // per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum:
42874    // - 300 cookies
42875    // - 20 cookies per unique domain
42876    // - 4096 bytes per cookie
42877    var cookieLength = str.length + 1;
42878    if (cookieLength > 4096) {
42879      $log.warn('Cookie \'' + name +
42880        '\' possibly not set or overflowed because it was too large (' +
42881        cookieLength + ' > 4096 bytes)!');
42882    }
42883
42884    return str;
42885  }
42886
42887  return function(name, value, options) {
42888    rawDocument.cookie = buildCookieString(name, value, options);
42889  };
42890}
42891
42892$$CookieWriter.$inject = ['$document', '$log', '$browser'];
42893
42894angular.module('ngCookies').provider('$$cookieWriter', /** @this */ function $$CookieWriterProvider() {
42895  this.$get = $$CookieWriter;
42896});
42897
42898
42899})(window, window.angular);
42900
42901/**
42902 * @license AngularJS v1.5.11
42903 * (c) 2010-2017 Google, Inc. http://angularjs.org
42904 * License: MIT
42905 */
42906(function(window, angular) {'use strict';
42907
42908/* global ngTouchClickDirectiveFactory: false */
42909
42910/**
42911 * @ngdoc module
42912 * @name ngTouch
42913 * @description
42914 *
42915 * # ngTouch
42916 *
42917 * The `ngTouch` module provides touch events and other helpers for touch-enabled devices.
42918 * The implementation is based on jQuery Mobile touch event handling
42919 * ([jquerymobile.com](http://jquerymobile.com/)).
42920 *
42921 *
42922 * See {@link ngTouch.$swipe `$swipe`} for usage.
42923 *
42924 * <div doc-module-components="ngTouch"></div>
42925 *
42926 */
42927
42928// define ngTouch module
42929/* global -ngTouch */
42930var ngTouch = angular.module('ngTouch', []);
42931
42932ngTouch.provider('$touch', $TouchProvider);
42933
42934function nodeName_(element) {
42935  return angular.lowercase(element.nodeName || (element[0] && element[0].nodeName));
42936}
42937
42938/**
42939 * @ngdoc provider
42940 * @name $touchProvider
42941 *
42942 * @description
42943 * The `$touchProvider` allows enabling / disabling {@link ngTouch.ngClick ngTouch's ngClick directive}.
42944 */
42945$TouchProvider.$inject = ['$provide', '$compileProvider'];
42946function $TouchProvider($provide, $compileProvider) {
42947
42948  /**
42949   * @ngdoc method
42950   * @name  $touchProvider#ngClickOverrideEnabled
42951   *
42952   * @param {boolean=} enabled update the ngClickOverrideEnabled state if provided, otherwise just return the
42953   * current ngClickOverrideEnabled state
42954   * @returns {*} current value if used as getter or itself (chaining) if used as setter
42955   *
42956   * @kind function
42957   *
42958   * @description
42959   * Call this method to enable/disable {@link ngTouch.ngClick ngTouch's ngClick directive}. If enabled,
42960   * the default ngClick directive will be replaced by a version that eliminates the 300ms delay for
42961   * click events on browser for touch-devices.
42962   *
42963   * The default is `false`.
42964   *
42965   */
42966  var ngClickOverrideEnabled = false;
42967  var ngClickDirectiveAdded = false;
42968  // eslint-disable-next-line no-invalid-this
42969  this.ngClickOverrideEnabled = function(enabled) {
42970    if (angular.isDefined(enabled)) {
42971
42972      if (enabled && !ngClickDirectiveAdded) {
42973        ngClickDirectiveAdded = true;
42974
42975        // Use this to identify the correct directive in the delegate
42976        ngTouchClickDirectiveFactory.$$moduleName = 'ngTouch';
42977        $compileProvider.directive('ngClick', ngTouchClickDirectiveFactory);
42978
42979        $provide.decorator('ngClickDirective', ['$delegate', function($delegate) {
42980          if (ngClickOverrideEnabled) {
42981            // drop the default ngClick directive
42982            $delegate.shift();
42983          } else {
42984            // drop the ngTouch ngClick directive if the override has been re-disabled (because
42985            // we cannot de-register added directives)
42986            var i = $delegate.length - 1;
42987            while (i >= 0) {
42988              if ($delegate[i].$$moduleName === 'ngTouch') {
42989                $delegate.splice(i, 1);
42990                break;
42991              }
42992              i--;
42993            }
42994          }
42995
42996          return $delegate;
42997        }]);
42998      }
42999
43000      ngClickOverrideEnabled = enabled;
43001      return this;
43002    }
43003
43004    return ngClickOverrideEnabled;
43005  };
43006
43007  /**
43008  * @ngdoc service
43009  * @name $touch
43010  * @kind object
43011  *
43012  * @description
43013  * Provides the {@link ngTouch.$touch#ngClickOverrideEnabled `ngClickOverrideEnabled`} method.
43014  *
43015  */
43016  // eslint-disable-next-line no-invalid-this
43017  this.$get = function() {
43018    return {
43019      /**
43020       * @ngdoc method
43021       * @name  $touch#ngClickOverrideEnabled
43022       *
43023       * @returns {*} current value of `ngClickOverrideEnabled` set in the {@link ngTouch.$touchProvider $touchProvi
43023der},
43024       * i.e. if {@link ngTouch.ngClick ngTouch's ngClick} directive is enabled.
43025       *
43026       * @kind function
43027       */
43028      ngClickOverrideEnabled: function() {
43029        return ngClickOverrideEnabled;
43030      }
43031    };
43032  };
43033
43034}
43035
43036/* global ngTouch: false */
43037
43038    /**
43039     * @ngdoc service
43040     * @name $swipe
43041     *
43042     * @description
43043     * The `$swipe` service is a service that abstracts the messier details of hold-and-drag swipe
43044     * behavior, to make implementing swipe-related directives more convenient.
43045     *
43046     * Requires the {@link ngTouch `ngTouch`} module to be installed.
43047     *
43048     * `$swipe` is used by the `ngSwipeLeft` and `ngSwipeRight` directives in `ngTouch`.
43049     *
43050     * # Usage
43051     * The `$swipe` service is an object with a single method: `bind`. `bind` takes an element
43052     * which is to be watched for swipes, and an object with four handler functions. See the
43053     * documentation for `bind` below.
43054     */
43055
43056ngTouch.factory('$swipe', [function() {
43057  // The total distance in any direction before we make the call on swipe vs. scroll.
43058  var MOVE_BUFFER_RADIUS = 10;
43059
43060  var POINTER_EVENTS = {
43061    'mouse': {
43062      start: 'mousedown',
43063      move: 'mousemove',
43064      end: 'mouseup'
43065    },
43066    'touch': {
43067      start: 'touchstart',
43068      move: 'touchmove',
43069      end: 'touchend',
43070      cancel: 'touchcancel'
43071    },
43072    'pointer': {
43073      start: 'pointerdown',
43074      move: 'pointermove',
43075      end: 'pointerup',
43076      cancel: 'pointercancel'
43077    }
43078  };
43079
43080  function getCoordinates(event) {
43081    var originalEvent = event.originalEvent || event;
43082    var touches = originalEvent.touches && originalEvent.touches.length ? originalEvent.touches : [originalEvent];
43083    var e = (originalEvent.changedTouches && originalEvent.changedTouches[0]) || touches[0];
43084
43085    return {
43086      x: e.clientX,
43087      y: e.clientY
43088    };
43089  }
43090
43091  function getEvents(pointerTypes, eventType) {
43092    var res = [];
43093    angular.forEach(pointerTypes, function(pointerType) {
43094      var eventName = POINTER_EVENTS[pointerType][eventType];
43095      if (eventName) {
43096        res.push(eventName);
43097      }
43098    });
43099    return res.join(' ');
43100  }
43101
43102  return {
43103    /**
43104     * @ngdoc method
43105     * @name $swipe#bind
43106     *
43107     * @description
43108     * The main method of `$swipe`. It takes an element to be watched for swipe motions, and an
43109     * object containing event handlers.
43110     * The pointer types that should be used can be specified via the optional
43111     * third argument, which is an array of strings `'mouse'`, `'touch'` and `'pointer'`. By default,
43112     * `$swipe` will listen for `mouse`, `touch` and `pointer` events.
43113     *
43114     * The four events are `start`, `move`, `end`, and `cancel`. `start`, `move`, and `end`
43115     * receive as a parameter a coordinates object of the form `{ x: 150, y: 310 }` and the raw
43116     * `event`. `cancel` receives the raw `event` as its single parameter.
43117     *
43118     * `start` is called on either `mousedown`, `touchstart` or `pointerdown`. After this event, `$swipe` is
43119     * watching for `touchmove`, `mousemove` or `pointermove` events. These events are ignored until the total
43120     * distance moved in either dimension exceeds a small threshold.
43121     *
43122     * Once this threshold is exceeded, either the horizontal or vertical delta is greater.
43123     * - If the horizontal distance is greater, this is a swipe and `move` and `end` events follow.
43124     * - If the vertical distance is greater, this is a scroll, and we let the browser take over.
43125     *   A `cancel` event is sent.
43126     *
43127     * `move` is called on `mousemove`, `touchmove` and `pointermove` after the above logic has determined that
43128     * a swipe is in progress.
43129     *
43130     * `end` is called when a swipe is successfully completed with a `touchend`, `mouseup` or `pointerup`.
43131     *
43132     * `cancel` is called either on a `touchcancel` or `pointercancel`  from the browser, or when we begin scrolling
43133     * as described above.
43134     *
43135     */
43136    bind: function(element, eventHandlers, pointerTypes) {
43137      // Absolute total movement, used to control swipe vs. scroll.
43138      var totalX, totalY;
43139      // Coordinates of the start position.
43140      var startCoords;
43141      // Last event's position.
43142      var lastPos;
43143      // Whether a swipe is active.
43144      var active = false;
vendor: 4,584 bytes, lines 43145-43269
43145
43146      pointerTypes = pointerTypes || ['mouse', 'touch', 'pointer'];
43147      element.on(getEvents(pointerTypes, 'start'), function(event) {
43148        startCoords = getCoordinates(event);
43149        active = true;
43150        totalX = 0;
43151        totalY = 0;
43152        lastPos = startCoords;
43153        if (eventHandlers['start']) {
43154          eventHandlers['start'](startCoords, event);
43155        }
43156      });
43157      var events = getEvents(pointerTypes, 'cancel');
43158      if (events) {
43159        element.on(events, function(event) {
43160          active = false;
43161          if (eventHandlers['cancel']) {
43162            eventHandlers['cancel'](event);
43163          }
43164        });
43165      }
43166
43167      element.on(getEvents(pointerTypes, 'move'), function(event) {
43168        if (!active) return;
43169
43170        // Android will send a touchcancel if it thinks we're starting to scroll.
43171        // So when the total distance (+ or - or both) exceeds 10px in either direction,
43172        // we either:
43173        // - On totalX > totalY, we send preventDefault() and treat this as a swipe.
43174        // - On totalY > totalX, we let the browser handle it as a scroll.
43175
43176        if (!startCoords) return;
43177        var coords = getCoordinates(event);
43178
43179        totalX += Math.abs(coords.x - lastPos.x);
43180        totalY += Math.abs(coords.y - lastPos.y);
43181
43182        lastPos = coords;
43183
43184        if (totalX < MOVE_BUFFER_RADIUS && totalY < MOVE_BUFFER_RADIUS) {
43185          return;
43186        }
43187
43188        // One of totalX or totalY has exceeded the buffer, so decide on swipe vs. scroll.
43189        if (totalY > totalX) {
43190          // Allow native scrolling to take over.
43191          active = false;
43192          if (eventHandlers['cancel']) {
43193            eventHandlers['cancel'](event);
43194          }
43195          return;
43196        } else {
43197          // Prevent the browser from scrolling.
43198          event.preventDefault();
43199          if (eventHandlers['move']) {
43200            eventHandlers['move'](coords, event);
43201          }
43202        }
43203      });
43204
43205      element.on(getEvents(pointerTypes, 'end'), function(event) {
43206        if (!active) return;
43207        active = false;
43208        if (eventHandlers['end']) {
43209          eventHandlers['end'](getCoordinates(event), event);
43210        }
43211      });
43212    }
43213  };
43214}]);
43215
43216/* global ngTouch: false,
43217  nodeName_: false
43218*/
43219
43220/**
43221 * @ngdoc directive
43222 * @name ngClick
43223 * @deprecated
43224 * sinceVersion="v1.5.0"
43225 * This directive is deprecated and **disabled** by default.
43226 * The directive will receive no further support and might be removed from future releases.
43227 * If you need the directive, you can enable it with the {@link ngTouch.$touchProvider $touchProvider#ngClickOverrideEnabled}
43228 * function. We also recommend that you migrate to [FastClick](https://github.com/ftlabs/fastclick).
43229 * To learn more about the 300ms delay, this [Telerik article](http://developer.telerik.com/featured/300-ms-click-delay-ios-8/)
43230 * gives a good overview.
43231 *
43232 * @description
43233 * A more powerful replacement for the default ngClick designed to be used on touchscreen
43234 * devices. Most mobile browsers wait about 300ms after a tap-and-release before sending
43235 * the click event. This version handles them immediately, and then prevents the
43236 * following click event from propagating.
43237 *
43238 * Requires the {@link ngTouch `ngTouch`} module to be installed.
43239 *
43240 * This directive can fall back to using an ordinary click event, and so works on desktop
43241 * browsers as well as mobile.
43242 *
43243 * This directive also sets the CSS class `ng-click-active` while the element is being held
43244 * down (by a mouse click or touch) so you can restyle the depressed element if you wish.
43245 *
43246 * @element ANY
43247 * @param {expression} ngClick {@link guide/expression Expression} to evaluate
43248 * upon tap. (Event object is available as `$event`)
43249 *
43250 * @example
43251    <example module="ngClickExample" deps="angular-touch.js" name="ng-touch-ng-click">
43252      <file name="index.html">
43253        <button ng-click="count = count + 1" ng-init="count=0">
43254          Increment
43255        </button>
43256        count: {{ count }}
43257      </file>
43258      <file name="script.js">
43259        angular.module('ngClickExample', ['ngTouch']);
43260      </file>
43261    </example>
43262 */
43263
43264var ngTouchClickDirectiveFactory = ['$parse', '$timeout', '$rootElement',
43265    function($parse, $timeout, $rootElement) {
43266  var TAP_DURATION = 750; // Shorter than 750ms is a tap, longer is a taphold or drag.
43267  var MOVE_TOLERANCE = 12; // 12px seems to work in most mobile browsers.
43268  var PREVENT_DURATION = 2500; // 2.5 seconds maximum from preventGhostClick call to click
43269  var CLICKBUSTER_THRESHOLD = 25; // 25 pixels in any dimension is the limit for busting clicks.
43270
43271  var ACTIVE_CLASS_NAME = 'ng-click-active';
43272  var lastPreventedTime;
43273  var touchCoordinates;
43274  var lastLabelClickCoordinates;
43275
43276
43277  // TAP EVENTS AND GHOST CLICKS
43278  //
43279  // Why tap events?
43280  // Mobile browsers detect a tap, then wait a moment (usually ~300ms) to see if you're
43281  // double-tapping, and then fire a click event.
43282  //
43283  // This delay sucks and makes mobile apps feel unresponsive.
43284  // So we detect touchstart, touchcancel and touchend ourselves and determine when
43285  // the user has tapped on something.
43286  //
43287  // What happens when the browser then generates a click event?
43288  // The browser, of course, also detects the tap and fires a click after a delay. This results in
43289  // tapping/clicking twice. We do "clickbusting" to prevent it.
43290  //
43291  // How does it work?
43292  // We attach global touchstart and click handlers, that run during the capture (early) phase.
43293  // So the sequence for a tap is:
43294  // - global touchstart: Sets an "allowable region" at the point touched.
43295  // - element's touchstart: Starts a touch
43296  // (- touchcancel ends the touch, no click follows)
43297  // - element's touchend: Determines if the tap is valid (didn't move too far away, didn't hold
43298  //   too long) and fires the user's tap handler. The touchend also calls preventGhostClick().
43299  // - preventGhostClick() removes the allowable region the global touchstart created.
43300  // - The browser generates a click event.
43301  // - The global click handler catches the click, and checks whether it was in an allowable region.
43302  //     - If preventGhostClick was called, the region will have been removed, the click is busted.
43303  //     - If the region is still there, the click proceeds normally. Therefore clicks on links and
43304  //       other elements without ngTap on them work normally.
43305  //
43306  // This is an ugly, terrible hack!
43307  // Yeah, tell me about it. The alternatives are using the slow click events, or making our users
43308  // deal with the ghost clicks, so I consider this the least of evils. Fortunately Angular
43309  // encapsulates this ugly logic away from the user.
43310  //
43311  // Why not just put click handlers on the element?
43312  // We do that too, just to be sure. If the tap event caused the DOM to change,
43313  // it is possible another element is now in that position. To take account for these possibly
43314  // distinct elements, the handlers are global and care only about coordinates.
43315
43316  // Checks if the coordinates are close enough to be within the region.
43317  function hit(x1, y1, x2, y2) {
43318    return Math.abs(x1 - x2) < CLICKBUSTER_THRESHOLD && Math.abs(y1 - y2) < CLICKBUSTER_THRESHOLD;
43319  }
43320
43321  // Checks a list of allowable regions against a click location.
43322  // Returns true if the click should be allowed.
43323  // Splices out the allowable region from the list after it has been used.
43324  function checkAllowableRegions(touchCoordinates, x, y) {
43325    for (var i = 0; i < touchCoordinates.length; i += 2) {
43326      if (hit(touchCoordinates[i], touchCoordinates[i + 1], x, y)) {
43327        touchCoordinates.splice(i, i + 2);
43328        return true; // allowable region
43329      }
43330    }
43331    return false; // No allowable region; bust it.
43332  }
43333
43334  // Global click handler that prevents the click if it's in a bustable zone and preventGhostClick
43335  // was called recently.
43336  function onClick(event) {
43337    if (Date.now() - lastPreventedTime > PREVENT_DURATION) {
43338      return; // Too old.
43339    }
43340
43341    var touches = event.touches && event.touches.length ? event.touches : [event];
43342    var x = touches[0].clientX;
43343    var y = touches[0].clientY;
43344    // Work around desktop Webkit quirk where clicking a label will fire two clicks (on the label
43345    // and on the input element). Depending on the exact browser, this second click we don't want
43346    // to bust has either (0,0), negative coordinates, or coordinates equal to triggering label
43347    // click event
43348    if (x < 1 && y < 1) {
43349      return; // offscreen
43350    }
43351    if (lastLabelClickCoordinates &&
43352        lastLabelClickCoordinates[0] === x && lastLabelClickCoordinates[1] === y) {
43353      return; // input click triggered by label click
43354    }
43355    // reset label click coordinates on first subsequent click
43356    if (lastLabelClickCoordinates) {
43357      lastLabelClickCoordinates = null;
43358    }
43359    // remember label click coordinates to prevent click busting of trigger click event on input
43360    if (nodeName_(event.target) === 'label') {
43361      lastLabelClickCoordinates = [x, y];
43362    }
43363
43364    // Look for an allowable region containing this click.
43365    // If we find one, that means it was created by touchstart and n
43365ot removed by
43366    // preventGhostClick, so we don't bust it.
43367    if (checkAllowableRegions(touchCoordinates, x, y)) {
43368      return;
43369    }
43370
43371    // If we didn't find an allowable region, bust the click.
43372    event.stopPropagation();
43373    event.preventDefault();
43374
43375    // Blur focused form elements
43376    if (event.target && event.target.blur) {
43377      event.target.blur();
43378    }
43379  }
43380
43381
43382  // Global touchstart handler that creates an allowable region for a click event.
43383  // This allowable region can be removed by preventGhostClick if we want to bust it.
43384  function onTouchStart(event) {
43385    var touches = event.touches && event.touches.length ? event.touches : [event];
43386    var x = touches[0].clientX;
43387    var y = touches[0].clientY;
43388    touchCoordinates.push(x, y);
43389
43390    $timeout(function() {
43391      // Remove the allowable region.
43392      for (var i = 0; i < touchCoordinates.length; i += 2) {
43393        if (touchCoordinates[i] === x && touchCoordinates[i + 1] === y) {
43394          touchCoordinates.splice(i, i + 2);
43395          return;
43396        }
43397      }
43398    }, PREVENT_DURATION, false);
43399  }
43400
43401  // On the first call, attaches some event handlers. Then whenever it gets called, it creates a
43402  // zone around the touchstart where clicks will get busted.
43403  function preventGhostClick(x, y) {
43404    if (!touchCoordinates) {
43405      $rootElement[0].addEventListener('click', onClick, true);
43406      $rootElement[0].addEventListener('touchstart', onTouchStart, true);
43407      touchCoordinates = [];
43408    }
43409
43410    lastPreventedTime = Date.now();
43411
43412    checkAllowableRegions(touchCoordinates, x, y);
43413  }
43414
43415  // Actual linking function.
43416  return function(scope, element, attr) {
43417    var clickHandler = $parse(attr.ngClick),
43418        tapping = false,
43419        tapElement,  // Used to blur the element after a tap.
43420        startTime,   // Used to check if the tap was held too long.
43421        touchStartX,
43422        touchStartY;
43423
43424    function resetState() {
43425      tapping = false;
43426      element.removeClass(ACTIVE_CLASS_NAME);
43427    }
43428
43429    element.on('touchstart', function(event) {
43430      tapping = true;
43431      tapElement = event.target ? event.target : event.srcElement; // IE uses srcElement.
43432      // Hack for Safari, which can target text nodes instead of containers.
43433      if (tapElement.nodeType === 3) {
43434        tapElement = tapElement.parentNode;
43435      }
43436
43437      element.addClass(ACTIVE_CLASS_NAME);
43438
43439      startTime = Date.now();
43440
43441      // Use jQuery originalEvent
43442      var originalEvent = event.originalEvent || event;
43443      var touches = originalEvent.touches && originalEvent.touches.length ? originalEvent.touches : [originalEvent];
43444      var e = touches[0];
43445      touchStartX = e.clientX;
43446      touchStartY = e.clientY;
43447    });
43448
43449    element.on('touchcancel', function(event) {
43450      resetState();
43451    });
43452
43453    element.on('touchend', function(event) {
43454      var diff = Date.now() - startTime;
43455
43456      // Use jQuery originalEvent
43457      var originalEvent = event.originalEvent || event;
43458      var touches = (originalEvent.changedTouches && originalEvent.changedTouches.length) ?
43459          originalEvent.changedTouches :
43460          ((originalEvent.touches && originalEvent.touches.length) ? originalEvent.touches : [originalEvent]);
43461      var e = touches[0];
43462      var x = e.clientX;
43463      var y = e.clientY;
43464      var dist = Math.sqrt(Math.pow(x - touchStartX, 2) + Math.pow(y - touchStartY, 2));
43465
43466      if (tapping && diff < TAP_DURATION && dist < MOVE_TOLERANCE) {
43467        // Call preventGhostClick so the clickbuster will catch the corresponding click.
43468        preventGhostClick(x, y);
43469
43470        // Blur the focused element (the button, probably) before firing the callback.
43471        // This doesn't work perfectly on Android Chrome, but seems to work elsewhere.
43472        // I couldn't get anything to work reliably on Android Chrome.
43473        if (tapElement) {
43474          tapElement.blur();
43475        }
43476
43477        if (!angular.isDefined(attr.disabled) || attr.disabled === false) {
43478          element.triggerHandler('click', [event]);
43479        }
43480      }
43481
43482      resetState();
43483    });
43484
43485    // Hack for iOS Safari's benefit. It goes searching for onclick handlers and is liable to click
43486    // something else nearby.
43487    element.onclick = function(event) { };
43488
43489    // Actual click handler.
43490    // There are three different kinds of clicks, only two of which reach this point.
43491    // - On desktop browsers without touch events, their clicks will always come here.
43492    // - On mobile browsers, the simulated "fast" click will call this.
43493    // - But the browser's follow-up slow click will be "busted" before it reaches this handler.
43494    // Therefore it's safe to use this directive on both mobile and desktop.
43495    element.on('click', function(event, touchend) {
43496      scope.$apply(function() {
43497        clickHandler(scope, {$event: (touchend || event)});
43498      });
43499    });
43500
43501    element.on('mousedown', function(event) {
43502      element.addClass(ACTIVE_CLASS_NAME);
43503    });
43504
43505    element.on('mousemove mouseup', function(event) {
43506      element.removeClass(ACTIVE_CLASS_NAME);
43507    });
43508
43509  };
43510}];
43511
43512/* global ngTouch: false */
43513
43514/**
43515 * @ngdoc directive
43516 * @name ngSwipeLeft
43517 *
43518 * @description
43519 * Specify custom behavior when an element is swiped to the left on a touchscreen device.
43520 * A leftward swipe is a quick, right-to-left slide of the finger.
43521 * Though ngSwipeLeft is designed for touch-based devices, it will work with a mouse click and drag
43522 * too.
43523 *
43524 * To disable the mouse click and drag functionality, add `ng-swipe-disable-mouse` to
43525 * the `ng-swipe-left` or `ng-swipe-right` DOM Element.
43526 *
43527 * Requires the {@link ngTouch `ngTouch`} module to be installed.
43528 *
43529 * @element ANY
43530 * @param {expression} ngSwipeLeft {@link guide/expression Expression} to evaluate
43531 * upon left swipe. (Event object is available as `$event`)
43532 *
43533 * @example
43534    <example module="ngSwipeLeftExample" deps="angular-touch.js" name="ng-swipe-left">
43535      <file name="index.html">
43536        <div ng-show="!showActions" ng-swipe-left="showActions = true">
43537          Some list content, like an email in the inbox
43538        </div>
43539        <div ng-show="showActions" ng-swipe-right="showActions = false">
43540          <button ng-click="reply()">Reply</button>
43541          <button ng-click="delete()">Delete</button>
43542        </div>
43543      </file>
43544      <file name="script.js">
43545        angular.module('ngSwipeLeftExample', ['ngTouch']);
43546      </file>
43547    </example>
43548 */
43549
43550/**
43551 * @ngdoc directive
43552 * @name ngSwipeRight
43553 *
43554 * @description
43555 * Specify custom behavior when an element is swiped to the right on a touchscreen device.
43556 * A rightward swipe is a quick, left-to-right slide of the finger.
43557 * Though ngSwipeRight is designed for touch-based devices, it will work with a mouse click and drag
43558 * too.
43559 *
43560 * Requires the {@link ngTouch `ngTouch`} module to be installed.
43561 *
43562 * @element ANY
43563 * @param {expression} ngSwipeRight {@link guide/expression Expression} to evaluate
43564 * upon right swipe. (Event object is available as `$event`)
43565 *
43566 * @example
43567    <example module="ngSwipeRightExample" deps="angular-touch.js" name="ng-swipe-right">
43568      <file name="index.html">
43569        <div ng-show="!showActions" ng-swipe-left="showActions = true">
43570          Some list content, like an email in the inbox
43571        </div>
43572        <div ng-show="showActions" ng-swipe-right="showActions = false">
43573          <button ng-click="reply()">Reply</button>
43574          <button ng-click="delete()">Delete</button>
43575        </div>
43576      </file>
43577      <file name="script.js">
43578        angular.module('ngSwipeRightExample', ['ngTouch']);
43579      </file>
43580    </example>
43581 */
43582
43583function makeSwipeDirective(directiveName, direction, eventName) {
43584  ngTouch.directive(directiveName, ['$parse', '$swipe', function($parse, $swipe) {
43585    // The maximum vertical delta for a swipe should be less than 75px.
43586    var MAX_VERTICAL_DISTANCE = 75;
43587    // Vertical distance should not be more than a fraction of the horizontal distance.
43588    var MAX_VERTICAL_RATIO = 0.3;
43589    // At least a 30px lateral motion is necessary for a swipe.
43590    var MIN_HORIZONTAL_DISTANCE = 30;
43591
43592    return function(scope, element, attr) {
43593      var swipeHandler = $parse(attr[directiveName]);
43594
43595      var startCoords, valid;
43596
43597      function validSwipe(coords) {
43598        // Check that it's within the coordinates.
43599        // Absolute vertical distance must be within tolerances.
43600        // Horizontal distance, we take the current X - the starting X.
43601        // This is negative for leftward swipes and positive for rightward swipes.
43602        // After multiplying by the direction (-1 for left, +1 for right), legal swipes
43603        // (ie. same direction as the directive wants) will have a positive delta and
43604        // illegal ones a negative delta.
43605        // Therefore this delta must be positive, and larger than the minimum.
43606        if (!startCoords) return false;
vendor: 4,010 bytes, lines 43607-43727
43607        var deltaY = Math.abs(coords.y - startCoords.y);
43608        var deltaX = (coords.x - startCoords.x) * direction;
43609        return valid && // Short circuit for already-invalidated swipes.
43610            deltaY < MAX_VERTICAL_DISTANCE &&
43611            deltaX > 0 &&
43612            deltaX > MIN_HORIZONTAL_DISTANCE &&
43613            deltaY / deltaX < MAX_VERTICAL_RATIO;
43614      }
43615
43616      var pointerTypes = ['touch'];
43617      if (!angular.isDefined(attr['ngSwipeDisableMouse'])) {
43618        pointerTypes.push('mouse');
43619      }
43620      $swipe.bind(element, {
43621        'start': function(coords, event) {
43622          startCoords = coords;
43623          valid = true;
43624        },
43625        'cancel': function(event) {
43626          valid = false;
43627        },
43628        'end': function(coords, event) {
43629          if (validSwipe(coords)) {
43630            scope.$apply(function() {
43631              element.triggerHandler(eventName);
43632              swipeHandler(scope, {$event: event});
43633            });
43634          }
43635        }
43636      }, pointerTypes);
43637    };
43638  }]);
43639}
43640
43641// Left is negative X-coordinate, right is positive.
43642makeSwipeDirective('ngSwipeLeft', -1, 'swipeleft');
43643makeSwipeDirective('ngSwipeRight', 1, 'swiperight');
43644
43645
43646
43647})(window, window.angular);
43648
43649/**
43650 * @license AngularJS v1.5.11
43651 * (c) 2010-2017 Google, Inc. http://angularjs.org
43652 * License: MIT
43653 */
43654(function(window, angular) {'use strict';
43655
43656/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
43657 *     Any commits to this file should be reviewed with security in mind.  *
43658 *   Changes to this file can potentially create security vulnerabilities. *
43659 *          An approval from 2 Core members with history of modifying      *
43660 *                         this file is required.                          *
43661 *                                                                         *
43662 *  Does the change somehow allow for arbitrary javascript to be executed? *
43663 *    Or allows for someone to change the prototype of built-in objects?   *
43664 *     Or gives undesired access to variables likes document or window?    *
43665 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
43666
43667var $sanitizeMinErr = angular.$$minErr('$sanitize');
43668var bind;
43669var extend;
43670var forEach;
43671var isDefined;
43672var lowercase;
43673var noop;
43674var htmlParser;
43675var htmlSanitizeWriter;
43676
43677/**
43678 * @ngdoc module
43679 * @name ngSanitize
43680 * @description
43681 *
43682 * # ngSanitize
43683 *
43684 * The `ngSanitize` module provides functionality to sanitize HTML.
43685 *
43686 *
43687 * <div doc-module-components="ngSanitize"></div>
43688 *
43689 * See {@link ngSanitize.$sanitize `$sanitize`} for usage.
43690 */
43691
43692/**
43693 * @ngdoc service
43694 * @name $sanitize
43695 * @kind function
43696 *
43697 * @description
43698 *   Sanitizes an html string by stripping all potentially dangerous tokens.
43699 *
43700 *   The input is sanitized by parsing the HTML into tokens. All safe tokens (from a whitelist) are
43701 *   then serialized back to properly escaped html string. This means that no unsafe input can make
43702 *   it into the returned string.
43703 *
43704 *   The whitelist for URL sanitization of attribute values is configured using the functions
43705 *   `aHrefSanitizationWhitelist` and `imgSrcSanitizationWhitelist` of {@link ng.$compileProvider
43706 *   `$compileProvider`}.
43707 *
43708 *   The input may also contain SVG markup if this is enabled via {@link $sanitizeProvider}.
43709 *
43710 * @param {string} html HTML input.
43711 * @returns {string} Sanitized HTML.
43712 *
43713 * @example
43714   <example module="sanitizeExample" deps="angular-sanitize.js" name="sanitize-service">
43715   <file name="index.html">
43716     <script>
43717         angular.module('sanitizeExample', ['ngSanitize'])
43718           .controller('ExampleController', ['$scope', '$sce', function($scope, $sce) {
43719             $scope.snippet =
43720               '<p style="color:blue">an html\n' +
43721               '<em onmouseover="this.textContent=\'PWN3D!\'">click here</em>\n' +
43722               'snippet</p>';
43723             $scope.deliberatelyTrustDangerousSnippet = function() {
43724               return $sce.trustAsHtml($scope.snippet);
43725             };
43726           }]);
43727     </script>
43728     <div ng-controller="ExampleController">
43729        Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
43730       <table>
43731         <tr>
43732           <td>Directive</td>
43733           <td>How</td>
43734           <td>Source</td>
43735           <td>Rendered</td>
43736         </tr>
43737         <tr id="bind-html-with-sanitize">
43738           <td>ng-bind-html</td>
43739           <td>Automatically uses $sanitize</td>
43740           <td><pre>&lt;div ng-bind-html="snippet"&gt;<br/>&lt;/div&gt;</pre></td>
43741           <td><div ng-bind-html="snippet"></div></td>
43742         </tr>
43743         <tr id="bind-html-with-trust">
43744           <td>ng-bind-html</td>
43745           <td>Bypass $sanitize by explicitly trusting the dangerous value</td>
43746           <td>
43747           <pre>&lt;div ng-bind-html="deliberatelyTrustDangerousSnippet()"&gt;
43748&lt;/div&gt;</pre>
43749           </td>
43750           <td><div ng-bind-html="deliberatelyTrustDangerousSnippet()"></div></td>
43751         </tr>
43752         <tr id="bind-default">
43753           <td>ng-bind</td>
43754           <td>Automatically escapes</td>
43755           <td><pre>&lt;div ng-bind="snippet"&gt;<br/>&lt;/div&gt;</pre></td>
43756           <td><div ng-bind="snippet"></div></td>
43757         </tr>
43758       </table>
43759       </div>
43760   </file>
43761   <file name="protractor.js" type="protractor">
43762     it('should sanitize the html snippet by default', function() {
43763       expect(element(by.css('#bind-html-with-sanitize div')).getAttribute('innerHTML')).
43764         toBe('<p>an html\n<em>click here</em>\nsnippet</p>');
43765     });
43766
43767     it('should inline raw snippet if bound to a trusted value', function() {
43768       expect(element(by.css('#bind-html-with-trust div')).getAttribute('innerHTML')).
43769         toBe("<p style=\"color:blue\">an html\n" +
43770              "<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
43771              "snippet</p>");
43772     });
43773
43774     it('should escape snippet without any filter', function() {
43775       expect(element(by.css('#bind-default div')).getAttribute('innerHTML')).
43776         toBe("&lt;p style=\"color:blue\"&gt;an html\n" +
43777              "&lt;em onmouseover=\"this.textContent='PWN3D!'\"&gt;click here&lt;/em&gt;\n" +
43778              "snippet&lt;/p&gt;");
43779     });
43780
43781     it('should update', function() {
43782       element(by.model('snippet')).clear();
43783       element(by.model('snippet')).sendKeys('new <b onclick="alert(1)">text</b>');
43784       expect(element(by.css('#bind-html-with-sanitize div')).getAttribute('innerHTML')).
43785         toBe('new <b>text</b>');
43786       expect(element(by.css('#bind-html-with-trust div')).getAttribute('innerHTML')).toBe(
43787         'new <b onclick="alert(1)">text</b>');
43788       expect(element(by.css('#bind-default div')).getAttribute('innerHTML')).toBe(
43789         "new &lt;b onclick=\"alert(1)\"&gt;text&lt;/b&gt;");
43790     });
43791   </file>
43792   </example>
43793 */
43794
43795
43796/**
43797 * @ngdoc provider
43798 * @name $sanitizeProvider
43799 * @this
43800 *
43801 * @description
43802 * Creates and configures {@link $sanitize} instance.
43803 */
43804function $SanitizeProvider() {
43805  var svgEnabled = false;
43806
43807  this.$get = ['$$sanitizeUri', function($$sanitizeUri) {
43808    if (svgEnabled) {
43809      extend(validElements, svgElements);
43810    }
43811    return function(html) {
43812      var buf = [];
43813      htmlParser(html, htmlSanitizeWriter(buf, function(uri, isImage) {
43814        return !/^unsafe:/.test($$sanitizeUri(uri, isImage));
43815      }));
43816      return buf.join('');
43817    };
43818  }];
43819
43820
43821  /**
43822   * @ngdoc method
43823   * @name $sanitizeProvider#enableSvg
43824   * @kind function
43825   *
43826   * @description
43827   * Enables a subset of svg to be supported by the sanitizer.
43828   *
43829   * <div class="alert alert-warning">
43830   *   <p>By enabling this setting without taking other precautions, you might expose your
43831   *   application to click-hijacking attacks. In these attacks, sanitized svg elements could be positioned
43832   *   outside of the containing element and be rendered over other elements on the page (e.g. a login
43833   *   link). Such behavior can then result in phishing incidents.</p>
43834   *
43835   *   <p>To protect against these, explicitly setup `overflow: hidden` css rule for all potential svg
43836   *   tags within the sanitized content:</p>
43837   *
43838   *   <br>
43839   *
43840   *   <pre><code>
43841   *   .rootOfTheIncludedContent svg {
43842   *     overflow: hidden !important;
43843   *   }
43844   *   </code></pre>
43845   * </div>
43846   *
43847   * @param {boolean=} flag Enable or disable SVG support in the sanitizer.
43848   * @returns {boolean|ng.$sanitizeProvider} Returns the currently configured value if called
43849   *    without an argument or self for chaining otherwise.
43850   */
43851  this.enableSvg = function(enableSvg) {
43852    if (isDefined(enableSvg)) {
43853      svgEnabled = enableSvg;
43854      return this;
43855    } else {
43856      return svgEnabled;
43857    }
43858  };
43859
43860  //////////////////////////////////////////////////////////////////////////////////////////////////
43861  // Private stuff
43862  //////////////////////////////////////////////////////////////////////////////////////////////////
43863
43864  bind = angular.bind;
43865  extend = angular.extend;
43866  forEach = angular.forEach;
43867  isDefined = angular.isDefined;
43868  lowercase = angular.lowercase;
43869  noop = angular.noop;
43870
43871  htmlParser = htmlParserImpl;
43872  htmlSanitizeWriter = htmlSanitizeWriterImpl;
43873
43874  // Regular Expressions for parsing tags and attributes
43875  var SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
43876    // Match everything outside of normal chars and " (quote character)
43877    NON_ALPHANUMERIC_REGEXP = /([^#-~ |!])/g;
43878
43879
43880  // Good source of info about elements and attributes
43881  // http://dev.w3.org/html5/spec/Overview.html#semantics
43882  // http://simon.html5.org/html-elements
43883
43884  // Safe Void Elements - HTML5
43885  // http://dev.w3.org/html5/spec/Overview.html#void-elements
43886  var voidElements = toMap('area,br,col,hr,img,wbr');
43887
43888  // Elements that you can, intentionally, leave open (and which close themselves)
43889  // http://dev.w3.org/html5/spec/Overview.html#optional-tags
43890  var optionalEndTagBlockElements = toMap('colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr'),
43891      optionalEndTagInlineElements = toMap('rp,rt'),
43892      optionalEndTagElements = extend({},
43893                                              optionalEndTagInlineElements,
43894                                              optionalEndTagBlockElements);
43895
43896  // Safe Block Elements - HTML5
43897  var blockElements = extend({}, optionalEndTagBlockElements, toMap('address,article,' +
43898          'aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,' +
43899          'h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,section,table,ul'));
43900
43901  // Inline Elements - HTML5
43902  var inlineElements = extend({}, optionalEndTagInlineElements, toMap('a,abbr,acronym,b,' +
43903          'bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,' +
43904          'samp,small,span,strike,strong,sub,sup,time,tt,u,var'));
43905
43906  // SVG Elements
43907  // https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Elements
43908  // Note: the elements animate,animateColor,animateMotion,animateTransform,set are intentionally omitted.
43909  // They can potentially allow for arbitrary javascript to be executed. See #11290
43910  var svgElements = toMap('circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph,' +
43911          'hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline,' +
43912          'radialGradient,rect,stop,svg,switch,text,title,tspan');
43913
43914  // Blocked Elements (will be stripped)
43915  var blockedElements = toMap('script,style');
43916
43917  var validElements = extend({},
43918                                     voidElements,
43919                                     blockElements,
43920                                     inlineElements,
43921                                     optionalEndTagElements);
43922
43923  //Attributes that have href and hence need to be sanitized
43924  var uriAttrs = toMap('background,cite,href,longdesc,src,xlink:href');
43925
43926  var htmlAttrs = toMap('abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,' +
43927      'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,' +
43928      'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,' +
43929      'scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,' +
43930      'valign,value,vspace,width');
43931
43932  // SVG attributes (without "id" and "name" attributes)
43933  // https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Attributes
43934  var svgAttrs = toMap('accent-height,accumulate,additive,alphabetic,arabic-form,ascent,' +
43935      'baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,' +
43936      'cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,' +
43937      'font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,' +
43938      'height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,' +
43939      'marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,' +
43940      'max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,' +
43941      'path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,' +
43942      'requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,' +
43943      'stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,' +
43944      'stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,' +
43945      'stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,' +
43946      'underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,' +
43947      'width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,' +
43948      'xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan', true);
43949
43950  var validAttrs = extend({},
43951                                  uriAttrs,
43952                                  svgAttrs,
43953                                  htmlAttrs);
43954
43955  function toMap(str, lowercaseKeys) {
43956    var obj = {}, items = str.split(','), i;
43957    for (i = 0; i < items.length; i++) {
43958      obj[lowercaseKeys ? lowercase(items[i]) : items[i]] = true;
43959    }
43960    return obj;
43961  }
43962
43963  var inertBodyElement;
43964  (function(window) {
43965    var doc;
43966    if (window.document && window.document.implementation) {
43967      doc = window.document.implementation.createHTMLDocument('inert');
43968    } else {
43969      throw $sanitizeMinErr('noinert', 'Can\'t create an inert html document');
43970    }
43971    var docElement = doc.documentElement || doc.getDocumentElement();
43972    var bodyElements = docElement.getElementsByTagName('body');
43973
43974    // usually there should be only one body element in the document, but IE doesn't have any, so we need to create one
43975    if (bodyElements.length === 1) {
43976      inertBodyElement = bodyElements[0];
43977    } else {
43978      var html = doc.createElement('html');
43979      inertBodyElement = doc.createElement('body');
43980      html.appendChild(inertBodyElement);
43981      doc.appendChild(html);
43982    }
43983  })(window);
43984
43985  /**
43986   * @example
43987   * htmlParser(htmlString, {
43988   *     start: function(tag, attrs) {},
43989   *     end: function(tag) {},
43990   *     chars: function(text) {},
43991   *     comment: function(text) {}
43992   * });
43993   *
43994   * @param {string} html string
43995   * @param {object} handler
43996   */
43997  function htmlParserImpl(html, handler) {
43998    if (html === null || html === undefined) {
43999      html = '';
44000    } else if (typeof html !== 'string') {
44001      html = '' + html;
44002    }
44003    inertBodyElement.innerHTML = html;
44004
44005    //mXSS protection
44006    var mXSSAttempts = 5;
44007    do {
44008      if (mXSSAttempts === 0) {
44009        throw $sanitizeMinErr('uinput', 'Failed to sanitize html because the input is unstable');
44010      }
44011      mXSSAttempts--;
44012
44013      // strip custom-namespaced attributes on IE<=11
44014      if (window.document.documentMode) {
44015        stripCustomNsAttrs(inertBodyElement);
44016      }
44017      html = inertBodyElement.innerHTML; //trigger mXSS
44018      inertBodyElement.innerHTML = html;
44019    } while (html !== inertBodyElement.innerHTML);
44020
44021    var node = inertBodyElement.firstChild;
44022    while (node) {
44023      switch (node.nodeType) {
44024        case 1: // ELEMENT_NODE
44025          handler.start(node.nodeName.toLowerCase(), attrToMap(node.attributes));
44026          break;
44027        case 3: // TEXT NODE
44028          handler.chars(node.textContent);
44029          break;
44030      }
44031
44032      var nextNode;
44033      if (!(nextNode = node.firstChild)) {
44034      if (node.nodeType === 1) {
44035          handler.end(node.nodeName.toLowerCase());
44036        }
44037        nextNode = node.nextSibling;
44038        if (!nextNode) {
44039          while (nextNode == null) {
44040            node = node.parentNode;
44041            if (node === inertBodyElement) break;
44042            nextNode = node.nextSibling;
44043          if (node.nodeType === 1) {
44044              handler.end(node.nodeName.toLowerCase());
44045            }
44046          }
44047        }
44048      }
44049      node = nextNode;
44050    }
44051
44052    while ((node = inertBodyElement.firstChild)) {
44053      inertBodyElement.removeChild(node);
44054    }
44055  }
44056
44057  function attrToMap(attrs) {
44058    var map = {};
44059    for (var i = 0, ii = attrs.length; i < ii; i++) {
44060      var attr = attrs[i];
44061      map[attr.name] = attr.value;
44062    }
44063    return map;
44064  }
44065
44066
44067  /**
44068   * Escapes all potentially dangerous characters, so that the
44069   * resulting string can be safely inserted into attribute or
44070   * element text.
44071   * @param value
44072   * @returns {string} escaped text
44073   */
44074  function encodeEntities(value) {
44075    return value.
44076      replace(/&/g, '&amp;').
44077      replace(SURROGATE_PAIR_REGEXP, function(value) {
44078        var hi = value.charCodeAt(0);
44079        var low = value.charCodeAt(1);
44080        return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';
44081      }).
44082      replace(NON_ALPHANUMERIC_REGEXP, function(value) {
44083        return '&#' + value.charCodeAt(0) + ';';
44084      }).
44085      replace(/</g, '&lt;').
44086      replace(/>/g, '&gt;');
44087  }
44088
44089  /**
44090   * create an HTML/XML writer which writes to buffer
44091   * @param {Array} buf use buf.join('') to get out sanitized html string
44092   * @returns {object} in the form of {
44093   *     start: function(tag, attrs) {},
44094   *     end: function(tag) {},
44095   *     chars: function(text) {},
44096   *     comment: function(text) {}
44097   * }
44098   */
44099  function htmlSanitizeWriterImpl(buf, uriValidator) {
44100    var ignoreCurrentElement = false;
vendor: 3,676 bytes, lines 44101-44221
44101    var out = bind(buf, buf.push);
44102    return {
44103      start: function(tag, attrs) {
44104        tag = lowercase(tag);
44105        if (!ignoreCurrentElement && blockedElements[tag]) {
44106          ignoreCurrentElement = tag;
44107        }
44108        if (!ignoreCurrentElement && validElements[tag] === true) {
44109          out('<');
44110          out(tag);
44111          forEach(attrs, function(value, key) {
44112            var lkey = lowercase(key);
44113            var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background');
44114            if (validAttrs[lkey] === true &&
44115              (uriAttrs[lkey] !== true || uriValidator(value, isImage))) {
44116              out(' ');
44117              out(key);
44118              out('="');
44119              out(encodeEntities(value));
44120              out('"');
44121            }
44122          });
44123          out('>');
44124        }
44125      },
44126      end: function(tag) {
44127        tag = lowercase(tag);
44128        if (!ignoreCurrentElement && validElements[tag] === true && voidElements[tag] !== true) {
44129          out('</');
44130          out(tag);
44131          out('>');
44132        }
44133        // eslint-disable-next-line eqeqeq
44134        if (tag == ignoreCurrentElement) {
44135          ignoreCurrentElement = false;
44136        }
44137      },
44138      chars: function(chars) {
44139        if (!ignoreCurrentElement) {
44140          out(encodeEntities(chars));
44141        }
44142      }
44143    };
44144  }
44145
44146
44147  /**
44148   * When IE9-11 comes across an unknown namespaced attribute e.g. 'xlink:foo' it adds 'xmlns:ns1' attribute to declare
44149   * ns1 namespace and prefixes the attribute with 'ns1' (e.g. 'ns1:xlink:foo'). This is undesirable since we don't want
44150   * to allow any of these custom attributes. This method strips them all.
44151   *
44152   * @param node Root element to process
44153   */
44154  function stripCustomNsAttrs(node) {
44155    while (node) {
44156      if (node.nodeType === window.Node.ELEMENT_NODE) {
44157        var attrs = node.attributes;
44158        for (var i = 0, l = attrs.length; i < l; i++) {
44159          var attrNode = attrs[i];
44160          var attrName = attrNode.name.toLowerCase();
44161          if (attrName === 'xmlns:ns1' || attrName.lastIndexOf('ns1:', 0) === 0) {
44162            node.removeAttributeNode(attrNode);
44163            i--;
44164            l--;
44165          }
44166        }
44167      }
44168
44169      var nextNode = node.firstChild;
44170      if (nextNode) {
44171        stripCustomNsAttrs(nextNode);
44172      }
44173
44174      node = node.nextSibling;
44175    }
44176  }
44177}
44178
44179function sanitizeText(chars) {
44180  var buf = [];
44181  var writer = htmlSanitizeWriter(buf, noop);
44182  writer.chars(chars);
44183  return buf.join('');
44184}
44185
44186
44187// define ngSanitize module and register $sanitize service
44188angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider);
44189
44190/**
44191 * @ngdoc filter
44192 * @name linky
44193 * @kind function
44194 *
44195 * @description
44196 * Finds links in text input and turns them into html links. Supports `http/https/ftp/mailto` and
44197 * plain email address links.
44198 *
44199 * Requires the {@link ngSanitize `ngSanitize`} module to be installed.
44200 *
44201 * @param {string} text Input text.
44202 * @param {string} target Window (`_blank|_self|_parent|_top`) or named frame to open links in.
44203 * @param {object|function(url)} [attributes] Add custom attributes to the link element.
44204 *
44205 *    Can be one of:
44206 *
44207 *    - `object`: A map of attributes
44208 *    - `function`: Takes the url as a parameter and returns a map of attributes
44209 *
44210 *    If the map of attributes contains a value for `target`, it overrides the value of
44211 *    the target parameter.
44212 *
44213 *
44214 * @returns {string} Html-linkified and {@link $sanitize sanitized} text.
44215 *
44216 * @usage
44217   <span ng-bind-html="linky_expression | linky"></span>
44218 *
44219 * @example
44220   <example module="linkyExample" deps="angular-sanitize.js" name="linky-filter">
44221     <file name="index.html">
44222       <div ng-controller="ExampleController">
44223       Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
44224       <table>
44225         <tr>
44226           <th>Filter</th>
44227           <th>Source</th>
44228           <th>Rendered</th>
44229         </tr>
44230         <tr id="linky-filter">
44231           <td>linky filter</td>
44232           <td>
44233             <pre>&lt;div ng-bind-html="snippet | linky"&gt;<br>&lt;/div&gt;</pre>
44234           </td>
44235           <td>
44236             <div ng-bind-html="snippet | linky"></div>
44237           </td>
44238         </tr>
44239         <tr id="linky-target">
44240          <td>linky target</td>
44241          <td>
44242            <pre>&lt;div ng-bind-html="snippetWithSingleURL | linky:'_blank'"&gt;<br>&lt;/div&gt;</pre>
44243          </td>
44244          <td>
44245            <div ng-bind-html="snippetWithSingleURL | linky:'_blank'"></div>
44246          </td>
44247         </tr>
44248         <tr id="linky-custom-attributes">
44249          <td>linky custom attributes</td>
44250          <td>
44251            <pre>&lt;div ng-bind-html="snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}"&gt;<br>&lt;/div&gt;</pre>
44252          </td>
44253          <td>
44254            <div ng-bind-html="snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}"></div>
44255          </td>
44256         </tr>
44257         <tr id="escaped-html">
44258           <td>no filter</td>
44259           <td><pre>&lt;div ng-bind="snippet"&gt;<br>&lt;/div&gt;</pre></td>
44260           <td><div ng-bind="snippet"></div></td>
44261         </tr>
44262       </table>
44263     </file>
44264     <file name="script.js">
44265       angular.module('linkyExample', ['ngSanitize'])
44266         .controller('ExampleController', ['$scope', function($scope) {
44267           $scope.snippet =
44268             'Pretty text with some links:\n' +
44269             'http://angularjs.org/,\n' +
44270             'mailto:[email protected],\n' +
44271             '[email protected],\n' +
44272             'and one more: ftp://127.0.0.1/.';
44273           $scope.snippetWithSingleURL = 'http://angularjs.org/';
44274         }]);
44275     </file>
44276     <file name="protractor.js" type="protractor">
44277       it('should linkify the snippet with urls', function() {
44278         expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
44279             toBe('Pretty text with some links: http://angularjs.org/, [email protected], ' +
44280                  '[email protected], and one more: ftp://127.0.0.1/.');
44281         expect(element.all(by.css('#linky-filter a')).count()).toEqual(4);
44282       });
44283
44284       it('should not linkify snippet without the linky filter', function() {
44285         expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()).
44286             toBe('Pretty text with some links: http://angularjs.org/, mailto:[email protected], ' +
44287                  '[email protected], and one more: ftp://127.0.0.1/.');
44288         expect(element.all(by.css('#escaped-html a')).count()).toEqual(0);
44289       });
44290
44291       it('should update', function() {
44292         element(by.model('snippet')).clear();
44293         element(by.model('snippet')).sendKeys('new http://link.');
44294         expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
44295             toBe('new http://link.');
44296         expect(element.all(by.css('#linky-filter a')).count()).toEqual(1);
44297         expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText())
44298             .toBe('new http://link.');
44299       });
44300
44301       it('should work with the target property', function() {
44302        expect(element(by.id('linky-target')).
44303            element(by.binding("snippetWithSingleURL | linky:'_blank'")).getText()).
44304            toBe('http://angularjs.org/');
44305        expect(element(by.css('#linky-target a')).getAttribute('target')).toEqual('_blank');
44306       });
44307
44308       it('should optionally add custom attributes', function() {
44309        expect(element(by.id('linky-custom-attributes')).
44310            element(by.binding("snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}")).getText()).
44311            toBe('http://angularjs.org/');
44312        expect(element(by.css('#linky-custom-attributes a')).getAttribute('rel')).toEqual('nofollow');
44313       });
44314     </file>
44315   </example>
44316 */
44317angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
44318  var LINKY_URL_REGEXP =
44319        /((ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"\u201d\u2019]/i,
44320      MAILTO_REGEXP = /^mailto:/i;
44321
44322  var linkyMinErr = angular.$$minErr('linky');
44323  var isDefined = angular.isDefined;
44324  var isFunction = angular.isFunction;
44325  var isObject = angular.isObject;
44326  var isString = angular.isString;
44327
44328  return function(text, target, attributes) {
44329    if (text == null || text === '') return text;
44330    if (!isString(text)) throw linkyMinErr('notstring', 'Expected string but received: {0}', text);
44331
44332    var attributesFn =
44333      isFunction(attributes) ? attributes :
44334      isObject(attributes) ? function getAttributesObject() {return attributes;} :
44335      function getEmptyAttributesObject() {return {};};
44336
44337    var match;
44338    var raw = text;
44339    var html = [];
44340    var url;
44341    var i;
44342    while ((match = raw.match(LINKY_URL_REGEXP))) {
44343      // We can not end in these as they are sometimes found at the end of the sentence
44344      url = match[0];
44345      // if we did not match ftp/http/www/mailto then assume mailto
44346      if (!match[2] && !match[4]) {
44347        url = (match[3] ? 'http://' : 'mailto:') + url;
44348      }
44349      i = match.index;
vendor: 4,083 bytes, lines 44350-44449
44350      addText(raw.substr(0, i));
44351      addLink(url, match[0].replace(MAILTO_REGEXP, ''));
44352      raw = raw.substring(i + match[0].length);
44353    }
44354    addText(raw);
44355    return $sanitize(html.join(''));
44356
44357    function addText(text) {
44358      if (!text) {
44359        return;
44360      }
44361      html.push(sanitizeText(text));
44362    }
44363
44364    function addLink(url, text) {
44365      var key, linkAttributes = attributesFn(url);
44366      html.push('<a ');
44367
44368      for (key in linkAttributes) {
44369        html.push(key + '="' + linkAttributes[key] + '" ');
44370      }
44371
44372      if (isDefined(target) && !('target' in linkAttributes)) {
44373        html.push('target="',
44374                  target,
44375                  '" ');
44376      }
44377      html.push('href="',
44378                url.replace(/"/g, '&quot;'),
44379                '">');
44380      addText(text);
44381      html.push('</a>');
44382    }
44383  };
44384}]);
44385
44386
44387})(window, window.angular);
44388
44389/**
44390 * @license AngularJS v1.5.11
44391 * (c) 2010-2017 Google, Inc. http://angularjs.org
44392 * License: MIT
44393 */
44394(function(window, angular) {'use strict';
44395
44396/**
44397 * @ngdoc module
44398 * @name ngAria
44399 * @description
44400 *
44401 * The `ngAria` module provides support for common
44402 * [<abbr title="Accessible Rich Internet Applications">ARIA</abbr>](http://www.w3.org/TR/wai-aria/)
44403 * attributes that convey state or semantic information about the application for users
44404 * of assistive technologies, such as screen readers.
44405 *
44406 * <div doc-module-components="ngAria"></div>
44407 *
44408 * ## Usage
44409 *
44410 * For ngAria to do its magic, simply include the module `ngAria` as a dependency. The following
44411 * directives are supported:
44412 * `ngModel`, `ngChecked`, `ngReadonly`, `ngRequired`, `ngValue`, `ngDisabled`, `ngShow`, `ngHide`, `ngClick`,
44413 * `ngDblClick`, and `ngMessages`.
44414 *
44415 * Below is a more detailed breakdown of the attributes handled by ngAria:
44416 *
44417 * | Directive                                   | Supported Attributes                                                                   |
44418 * |---------------------------------------------|----------------------------------------------------------------------------------------|
44419 * | {@link ng.directive:ngModel ngModel}        | aria-checked, aria-valuemin, aria-valuemax, aria-valuenow, aria-invalid, aria-required, input roles |
44420 * | {@link ng.directive:ngDisabled ngDisabled}  | aria-disabled                                                                          |
44421 * | {@link ng.directive:ngRequired ngRequired}  | aria-required
44422 * | {@link ng.directive:ngChecked ngChecked}    | aria-checked
44423 * | {@link ng.directive:ngReadonly ngReadonly}  | aria-readonly                                                                          |
44424 * | {@link ng.directive:ngValue ngValue}        | aria-checked                                                                           |
44425 * | {@link ng.directive:ngShow ngShow}          | aria-hidden                                                                            |
44426 * | {@link ng.directive:ngHide ngHide}          | aria-hidden                                                                            |
44427 * | {@link ng.directive:ngDblclick ngDblclick}  | tabindex                                                                               |
44428 * | {@link module:ngMessages ngMessages}        | aria-live                                                                              |
44429 * | {@link ng.directive:ngClick ngClick}        | tabindex, keypress event, button role                                                  |
44430 *
44431 * Find out more information about each directive by reading the
44432 * {@link guide/accessibility ngAria Developer Guide}.
44433 *
44434 * ## Example
44435 * Using ngDisabled with ngAria:
44436 * ```html
44437 * <md-checkbox ng-disabled="disabled">
44438 * ```
44439 * Becomes:
44440 * ```html
44441 * <md-checkbox ng-disabled="disabled" aria-disabled="true">
44442 * ```
44443 *
44444 * ## Disabling Attributes
44445 * It's possible to disable individual attributes added by ngAria with the
44446 * {@link ngAria.$ariaProvider#config config} method. For more details, see the
44447 * {@link guide/accessibility Developer Guide}.
44448 */
44449var ngAriaModule = angular.module('ngAria', ['ng']).
vendor: 4,119 bytes, lines 44450-44563
44450                        provider('$aria', $AriaProvider);
44451
44452/**
44453* Internal Utilities
44454*/
44455var nodeBlackList = ['BUTTON', 'A', 'INPUT', 'TEXTAREA', 'SELECT', 'DETAILS', 'SUMMARY'];
44456
44457var isNodeOneOf = function(elem, nodeTypeArray) {
44458  if (nodeTypeArray.indexOf(elem[0].nodeName) !== -1) {
44459    return true;
44460  }
44461};
44462/**
44463 * @ngdoc provider
44464 * @name $ariaProvider
44465 * @this
44466 *
44467 * @description
44468 *
44469 * Used for configuring the ARIA attributes injected and managed by ngAria.
44470 *
44471 * ```js
44472 * angular.module('myApp', ['ngAria'], function config($ariaProvider) {
44473 *   $ariaProvider.config({
44474 *     ariaValue: true,
44475 *     tabindex: false
44476 *   });
44477 * });
44478 *```
44479 *
44480 * ## Dependencies
44481 * Requires the {@link ngAria} module to be installed.
44482 *
44483 */
44484function $AriaProvider() {
44485  var config = {
44486    ariaHidden: true,
44487    ariaChecked: true,
44488    ariaReadonly: true,
44489    ariaDisabled: true,
44490    ariaRequired: true,
44491    ariaInvalid: true,
44492    ariaValue: true,
44493    tabindex: true,
44494    bindKeypress: true,
44495    bindRoleForClick: true
44496  };
44497
44498  /**
44499   * @ngdoc method
44500   * @name $ariaProvider#config
44501   *
44502   * @param {object} config object to enable/disable specific ARIA attributes
44503   *
44504   *  - **ariaHidden** – `{boolean}` – Enables/disables aria-hidden tags
44505   *  - **ariaChecked** – `{boolean}` – Enables/disables aria-checked tags
44506   *  - **ariaReadonly** – `{boolean}` – Enables/disables aria-readonly tags
44507   *  - **ariaDisabled** – `{boolean}` – Enables/disables aria-disabled tags
44508   *  - **ariaRequired** – `{boolean}` – Enables/disables aria-required tags
44509   *  - **ariaInvalid** – `{boolean}` – Enables/disables aria-invalid tags
44510   *  - **ariaValue** – `{boolean}` – Enables/disables aria-valuemin, aria-valuemax and aria-valuenow tags
44511   *  - **tabindex** – `{boolean}` – Enables/disables tabindex tags
44512   *  - **bindKeypress** – `{boolean}` – Enables/disables keypress event binding on `div` and
44513   *    `li` elements with ng-click
44514   *  - **bindRoleForClick** – `{boolean}` – Adds role=button to non-interactive elements like `div`
44515   *    using ng-click, making them more accessible to users of assistive technologies
44516   *
44517   * @description
44518   * Enables/disables various ARIA attributes
44519   */
44520  this.config = function(newConfig) {
44521    config = angular.extend(config, newConfig);
44522  };
44523
44524  function watchExpr(attrName, ariaAttr, nodeBlackList, negate) {
44525    return function(scope, elem, attr) {
44526      var ariaCamelName = attr.$normalize(ariaAttr);
44527      if (config[ariaCamelName] && !isNodeOneOf(elem, nodeBlackList) && !attr[ariaCamelName]) {
44528        scope.$watch(attr[attrName], function(boolVal) {
44529          // ensure boolean value
44530          boolVal = negate ? !boolVal : !!boolVal;
44531          elem.attr(ariaAttr, boolVal);
44532        });
44533      }
44534    };
44535  }
44536  /**
44537   * @ngdoc service
44538   * @name $aria
44539   *
44540   * @description
44541   * @priority 200
44542   *
44543   * The $aria service contains helper methods for applying common
44544   * [ARIA](http://www.w3.org/TR/wai-aria/) attributes to HTML directives.
44545   *
44546   * ngAria injects common accessibility attributes that tell assistive technologies when HTML
44547   * elements are enabled, selected, hidden, and more. To see how this is performed with ngAria,
44548   * let's review a code snippet from ngAria itself:
44549   *
44550   *```js
44551   * ngAriaModule.directive('ngDisabled', ['$aria', function($aria) {
44552   *   return $aria.$$watchExpr('ngDisabled', 'aria-disabled', nodeBlackList, false);
44553   * }])
44554   *```
44555   * Shown above, the ngAria module creates a directive with the same signature as the
44556   * traditional `ng-disabled` directive. But this ngAria version is dedicated to
44557   * solely managing accessibility attributes on custom elements. The internal `$aria` service is
44558   * used to watch the boolean attribute `ngDisabled`. If it has not been explicitly set by the
44559   * developer, `aria-disabled` is injected as an attribute with its value synchronized to the
44560   * value in `ngDisabled`.
44561   *
44562   * Because ngAria hooks into the `ng-disabled` directive, developers do not have to do
44563   * anything to enable this feature. The `aria-disabled` attribute is automatically managed
44564   * simply as a silent side-effect of using `ng-disabled` with the ngAria module.
44565   *
44566   * The full list of directives that interface with ngAria:
44567   * * **ngModel**
44568   * * **ngChecked**
44569   * * **ngReadonly**
44570   * * **ngRequired**
44571   * * **ngDisabled**
44572   * * **ngValue**
44573   * * **ngShow**
44574   * * **ngHide**
44575   * * **ngClick**
44576   * * **ngDblclick**
44577   * * **ngMessages**
44578   *
44579   * Read the {@link guide/accessibility ngAria Developer Guide} for a thorough explanation of each
44580   * directive.
44581   *
44582   *
44583   * ## Dependencies
44584   * Requires the {@link ngAria} module to be installed.
44585   */
44586  this.$get = function() {
44587    return {
44588      config: function(key) {
44589        return config[key];
44590      },
44591      $$watchExpr: watchExpr
44592    };
44593  };
44594}
44595
44596
44597ngAriaModule.directive('ngShow', ['$aria', function($aria) {
44598  return $aria.$$watchExpr('ngShow', 'aria-hidden', [], true);
44599}])
44600.directive('ngHide', ['$aria', function($aria) {
44601  return $aria.$$watchExpr('ngHide', 'aria-hidden', [], false);
44602}])
44603.directive('ngValue', ['$aria', function($aria) {
44604  return $aria.$$watchExpr('ngValue', 'aria-checked', nodeBlackList, false);
44605}])
44606.directive('ngChecked', ['$aria', function($aria) {
44607  return $aria.$$watchExpr('ngChecked', 'aria-checked', nodeBlackList, false);
44608}])
44609.directive('ngReadonly', ['$aria', function($aria) {
44610  return $aria.$$watchExpr('ngReadonly', 'aria-readonly', nodeBlackList, false);
44611}])
44612.directive('ngRequired', ['$aria', function($aria) {
44613  return $aria.$$watchExpr('ngRequired', 'aria-required', nodeBlackList, false);
44614}])
44615.directive('ngModel', ['$aria', function($aria) {
44616
44617  function shouldAttachAttr(attr, normalizedAttr, elem, allowBlacklistEls) {
44618    return $aria.config(normalizedAttr) && !elem.attr(attr) && (allowBlacklistEls || !isNodeOneOf(elem, nodeBlackList));
44619  }
44620
44621  function shouldAttachRole(role, elem) {
44622    // if element does not have role attribute
44623    // AND element type is equal to role (if custom element has a type equaling shape) <-- remove?
44624    // AND element is not INPUT
44625    return !elem.attr('role') && (elem.attr('type') === role) && (elem[0].nodeName !== 'INPUT');
44626  }
44627
44628  function getShape(attr, elem) {
44629    var type = attr.type,
44630        role = attr.role;
44631
44632    return ((type || role) === 'checkbox' || role === 'menuitemcheckbox') ? 'checkbox' :
44633           ((type || role) === 'radio'    || role === 'menuitemradio') ? 'radio' :
44634           (type === 'range'              || role === 'progressbar' || role === 'slider') ? 'range' : '';
44635  }
44636
44637  return {
44638    restrict: 'A',
44639    require: 'ngModel',
44640    priority: 200, //Make sure watches are fired after any other directives that affect the ngModel value
44641    compile: function(elem, attr) {
44642      var shape = getShape(attr, elem);
44643
44644      return {
44645        pre: function(scope, elem, attr, ngModel) {
44646          if (shape === 'checkbox') {
44647            //Use the input[checkbox] $isEmpty implementation for elements with checkbox roles
44648            ngModel.$isEmpty = function(value) {
44649              return value === false;
44650            };
44651          }
44652        },
44653        post: function(scope, elem, attr, ngModel) {
44654          var needsTabIndex = shouldAttachAttr('tabindex', 'tabindex', elem, false);
44655
44656          function ngAriaWatchModelValue() {
44657            return ngModel.$modelValue;
44658          }
44659
44660          function getRadioReaction(newVal) {
44661            // Strict comparison would cause a BC
44662            // eslint-disable-next-line eqeqeq
44663            var boolVal = (attr.value == ngModel.$viewValue);
44664            elem.attr('aria-checked', boolVal);
44665          }
44666
44667          function getCheckboxReaction() {
44668            elem.attr('aria-checked', !ngModel.$isEmpty(ngModel.$viewValue));
44669          }
44670
44671          switch (shape) {
44672            case 'radio':
44673            case 'checkbox':
44674              if (shouldAttachRole(shape, elem)) {
44675                elem.attr('role', shape);
44676              }
44677              if (shouldAttachAttr('aria-checked', 'ariaChecked', elem, false)) {
44678                scope.$watch(ngAriaWatchModelValue, shape === 'radio' ?
44679                    getRadioReaction : getCheckboxReaction);
44680              }
44681              if (needsTabIndex) {
44682                elem.attr('tabindex', 0);
44683              }
44684              break;
44685            case 'range':
44686              if (shouldAttachRole(shape, elem)) {
44687                elem.attr('role', 'slider');
44688              }
44689              if ($aria.config('ariaValue')) {
44690                var needsAriaValuemin = !elem.attr('aria-valuemin') &&
44691                    (attr.hasOwnProperty('min') || attr.hasOwnProperty('ngMin'));
44692                var needsAriaValuemax = !elem.attr('aria-valuemax') &&
44693                    (attr.hasOwnProperty('max') || attr.hasOwnProperty('ngMax'));
44694                var needsAriaValuenow = !elem.attr('aria-valuenow');
44695
44696                if (needsAriaValuemin) {
44697                  attr.$observe('min', function ngAriaValueMinReaction(newVal) {
44698                    elem.attr('aria-valuemin', newVal);
44699                  });
44700                }
44701                if (needsAriaValuemax) {
44702                  attr.$observe('max', function ngAriaValueMinReaction(newVal) {
44703                    elem.attr('aria-valuemax', newVal);
44704                  });
44705                }
44706                if (needsAriaValuenow) {
44707                  scope.$watch(ngAriaWatchModelValue, function ngAriaValueNowReaction(newVal) {
44708                    elem.attr('aria-valuenow', newVal);
44709                  });
44710                }
44711              }
44712              if (needsTabIndex) {
44713                elem.attr('tabindex', 0);
44714              }
44715              break;
44716          }
44717
44718          if (!attr.hasOwnProperty('ngRequired') && ngModel.$validators.required
44719            && shouldAttachAttr('aria-required', 'ariaRequired', elem, false)) {
44720            // ngModel.$error.required is undefined on custom controls
44721            attr.$observe('required', function() {
44722              elem.attr('aria-required', !!attr['required']);
44723            });
44724          }
44725
44726          if (shouldAttachAttr('aria-invalid', 'ariaInvalid', elem, true)) {
44727            scope.$watch(function ngAriaInvalidWatch() {
44728              return ngModel.$invalid;
44729            }, function ngAriaInvalidReaction(newVal) {
44730              elem.attr('aria-invalid', !!newVal);
44731            });
44732          }
44733        }
44734      };
44735    }
44736  };
44737}])
44738.directive('ngDisabled', ['$aria', function($aria) {
44739  return $aria.$$watchExpr('ngDisabled', 'aria-disabled', nodeBlackList, false);
44740}])
44741.directive('ngMessages', function() {
44742  return {
44743    restrict: 'A',
44744    require: '?ngMessages',
44745    link: function(scope, elem, attr, ngMessages) {
44746      if (!elem.attr('aria-live')) {
44747        elem.attr('aria-live', 'assertive');
44748      }
44749    }
44750  };
44751})
44752.directive('ngClick',['$aria', '$parse', function($aria, $parse) {
44753  return {
44754    restrict: 'A',
44755    compile: function(elem, attr) {
44756      var fn = $parse(attr.ngClick, /* interceptorFn */ null, /* expensiveChecks */ true);
44757      return function(scope, elem, attr) {
44758
44759        if (!isNodeOneOf(elem, nodeBlackList)) {
44760
44761          if ($aria.config('bindRoleForClick') && !elem.attr('role')) {
44762            elem.attr('role', 'button');
44763          }
44764
44765          if ($aria.config('tabindex') && !elem.attr('tabindex')) {
44766            elem.attr('tabindex', 0);
44767          }
44768
44769          if ($aria.config('bindKeypress') && !attr.ngKeypress) {
44770            elem.on('keypress', function(event) {
44771              var keyCode = event.which || event.keyCode;
44772              if (keyCode === 32 || keyCode === 13) {
44773                scope.$apply(callback);
44774              }
44775
44776              function callback() {
44777                fn(scope, { $event: event });
44778              }
44779            });
44780          }
44781        }
44782      };
44783    }
44784  };
44785}])
44786.directive('ngDblclick', ['$aria', function($aria) {
44787  return function(scope, elem, attr) {
44788    if ($aria.config('tabindex') && !elem.attr('tabindex') && !isNodeOneOf(elem, nodeBlackList)) {
44789      elem.attr('tabindex', 0);
44790    }
44791  };
44792}]);
44793
44794
44795})(window, window.angular);
44796
44797/**
44798 * @license AngularJS v1.5.11
44799 * (c) 2010-2017 Google, Inc. http://angularjs.org
44800 * License: MIT
44801 */
44802(function(window, angular) {'use strict';
44803
44804var $resourceMinErr = angular.$$minErr('$resource');
44805
44806// Helper functions and regex to lookup a dotted path on an object
44807// stopping at undefined/null.  The path must be composed of ASCII
44808// identifiers (just like $parse)
44809var MEMBER_NAME_REGEX = /^(\.[a-zA-Z_$@][0-9a-zA-Z_$@]*)+$/;
44810
44811function isValidDottedPath(path) {
44812  return (path != null && path !== '' && path !== 'hasOwnProperty' &&
44813      MEMBER_NAME_REGEX.test('.' + path));
44814}
44815
44816function lookupDottedPath(obj, path) {
44817  if (!isValidDottedPath(path)) {
44818    throw $resourceMinErr('badmember', 'Dotted member path "@{0}" is invalid.', path);
44819  }
44820  var keys = path.split('.');
44821  for (var i = 0, ii = keys.length; i < ii && angular.isDefined(obj); i++) {
44822    var key = keys[i];
44823    obj = (obj !== null) ? obj[key] : undefined;
44824  }
44825  return obj;
44826}
44827
44828/**
44829 * Create a shallow copy of an object and clear other fields from the destination
44830 */
44831function shallowClearAndCopy(src, dst) {
44832  dst = dst || {};
44833
44834  angular.forEach(dst, function(value, key) {
44835    delete dst[key];
44836  });
44837
44838  for (var key in src) {
44839    if (src.hasOwnProperty(key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) {
44840      dst[key] = src[key];
44841    }
44842  }
44843
44844  return dst;
44845}
44846
44847/**
44848 * @ngdoc module
44849 * @name ngResource
44850 * @description
44851 *
44852 * # ngResource
44853 *
44854 * The `ngResource` module provides interaction support with RESTful services
44855 * via the $resource service.
44856 *
44857 *
44858 * <div doc-module-components="ngResource"></div>
44859 *
44860 * See {@link ngResource.$resourceProvider} and {@link ngResource.$resource} for usage.
44861 */
44862
44863/**
44864 * @ngdoc provider
44865 * @name $resourceProvider
44866 *
44867 * @description
44868 *
44869 * Use `$resourceProvider` to change the default behavior of the {@link ngResource.$resource}
44870 * service.
44871 *
44872 * ## Dependencies
44873 * Requires the {@link ngResource } module to be installed.
44874 *
44875 */
44876
44877/**
44878 * @ngdoc service
44879 * @name $resource
44880 * @requires $http
44881 * @requires ng.$log
44882 * @requires $q
44883 * @requires ng.$timeout
44884 *
44885 * @description
44886 * A factory which creates a resource object that lets you interact with
44887 * [RESTful](http://en.wikipedia.org/wiki/Representational_State_Transfer) server-side data sources.
44888 *
44889 * The returned resource object has action methods which provide high-level behaviors without
44890 * the need to interact with the low level {@link ng.$http $http} service.
44891 *
44892 * Requires the {@link ngResource `ngResource`} module to be installed.
44893 *
44894 * By default, trailing slashes will be stripped from the calculated URLs,
44895 * which can pose problems with server backends that do not expect that
44896 * behavior.  This can be disabled by configuring the `$resourceProvider` like
44897 * this:
44898 *
44899 * ```js
44900     app.config(['$resourceProvider', function($resourceProvider) {
44901       // Don't strip trailing slashes from calculated URLs
44902       $resourceProvider.defaults.stripTrailingSlashes = false;
44903     }]);
44904 * ```
44905 *
44906 * @param {string} url A parameterized URL template with parameters prefixed by `:` as in
44907 *   `/user/:username`. If you are using a URL with a port number (e.g.
44908 *   `http://example.com:8080/api`), it will be respected.
44909 *
44910 *   If you are using a url with a suffix, just add the suffix, like this:
44911 *   `$resource('http://example.com/resource.json')` or `$resource('http://example.com/:id.json')`
44912 *   or even `$resource('http://example.com/resource/:resource_id.:format')`
44913 *   If the parameter before the suffix is empty, :resource_id in this case, then the `/.` will be
44914 *   collapsed down to a single `.`.  If you need this sequence to appear and not collapse then you
44915 *   can escape it with `/\.`.
44916 *
44917 * @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in
44918 *   `actions` methods. If a parameter value is a function, it will be called every time
44919 *   a param value needs to be obtained for a request (unless the param was overridden). The function
44920 *   will be passed the current data value as an argument.
44921 *
44922 *   Each key value in the parameter object is first bound to url template if present and then any
44923 *   excess keys are appended to the url search query after the `?`.
44924 *
44925 *   Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in
44926 *   URL `/path/greet?salutation=Hello`.
44927 *
44928 *   If the parameter value is prefixed with `@`, then the value for that parameter will be
44929 *   extracted from the corresponding property on the `data` object (provided when calling a
44930 *   "non-GET" action method).
44931 *   For example, if the `defaultParam` object is `{someParam: '@someProp'}` then the value of
44932 *   `someParam` will be `data.someProp`.
44933 *   Note that the parameter will be ignored, when calling a "GET" action method (i.e. an action
44934 *   method that does not accept a request body)
44935 *
44936 * @param {Object.<Object>=} actions Hash with declaration of custom actions that should extend
44937 *   the default set of resource actions. The declaration should be created in the format of {@link
44938 *   ng.$http#usage $http.config}:
44939 *
44940 *       {action1: {method:?, params:?, isArray:?, headers:?, ...},
44941 *        action2: {method:?, params:?, isArray:?, headers:?, ...},
44942 *        ...}
44943 *
44944 *   Where:
44945 *
44946 *   - **`action`** – {string} – The name of action. This name becomes the name of the method on
44947 *     your resource object.
44948 *   - **`method`** – {string} – Case insensitive HTTP method (e.g. `GET`, `POST`, `PUT`,
44949 *     `DELETE`, `JSONP`, etc).
44950 *   - **`params`** – {Object=} – Optional set of pre-bound parameters for this action. If any of
44951 *     the parameter value is a function, it will be called every time when a param value needs to
44952 *     be obtained for a request (unless the param was overridden). The function will be passed the
44953 *     current data value as an argument.
44954 *   - **`url`** – {string} – action specific `url` override. The url templating is supported just
44955 *     like for the resource-level urls.
44956 *   - **`isArray`** – {boolean=} – If true then the returned object for this action is an array,
44957 *     see `returns` section.
44958 *   - **`transformRequest`** –
44959 *     `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
44960 *     transform function or an array of such functions. The transform function takes the http
44961 *     request body and headers and returns its transformed (typically serialized) version.
44962 *     By default, transformRequest will contain one function that checks if the request data is
44963 *     an object and serializes it using `angular.toJson`. To prevent this behavior, set
44964 *     `transformRequest` to an empty array: `transformRequest: []`
44965 *   - **`transformResponse`** –
44966 *     `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` –
44967 *     transform function or an array of such functions. The transform function takes the http
44968 *     response body, headers and status and returns its transformed (typically deserialized)
44969 *     version.
44970 *     By default, transformResponse will contain one function that checks if the response looks
44971 *     like a JSON string and deserializes it using `angular.fromJson`. To prevent this behavior,
44972 *     set `transformResponse` to an empty array: `transformResponse: []`
44973 *   - **`cache`** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
44974 *     GET request, otherwise if a cache instance built with
44975 *     {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
44976 *     caching.
44977 *   - **`timeout`** – `{number}` – timeout in milliseconds.<br />
44978 *     **Note:** In contrast to {@link ng.$http#usage $http.config}, {@link ng.$q promises} are
44979 *     **not** supported in $resource, because the same value would be used for multiple requests.
44980 *     If you are looking for a way to cancel requests, you should use the `cancellable` option.
44981 *   - **`cancellable`** – `{boolean}` – if set to true, the request made by a "non-instance" call
44982 *     will be cancelled (if not already completed) by calling `$cancelRequest()` on the call's
44983 *     return value. Calling `$cancelRequest()` for a non-cancellable or an already
44984 *     completed/cancelled request will have no effect.<br />
44985 *   - **`withCredentials`** - `{boolean}` - whether to set the `withCredentials` flag on the
44986 *     XHR object. See
44987 *     [requests with credentials](https://developer.mozilla.org/en/http_access_control#section_5)
44988 *     for more information.
44989 *   - **`responseType`** - `{string}` - see
44990 *     [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType).
44991 *   - **`interceptor`** - `{Object=}` - The interceptor object has two optional methods -
44992 *     `response` and `responseError`. Both `response` and `responseError` interceptors get called
44993 *     with `http response` object. See {@link ng.$http $http interceptors}.
44994 *
44995 * @param {Object} options Hash with custom settings that should extend the
44996 *   default `$resourceProvider` behavior.  The supported options are:
44997 *
44998 *   - **`stripTrailingSlashes`** – {boolean} – If true then the trailing
44999 *   slashes from any calculated URL will be stripped. (Defaults to true.)
45000 *   - **`cancellable`** – {boolean} – If true, the request made by a "non-instance" call will be
45001 *   cancelled (if not already completed) by calling `$cancelRequest()` on the call's return value.
45002 *   This can be overwritten per action. (Defaults to false.)
45003 *
45004 * @returns {Object} A resource "class" object with methods for the default set of resource actions
45005 *   optionally extended with custom `actions`. The default set contains these actions:
45006 *   ```js
45007 *   { 'get':    {method:'GET'},
45008 *     'save':   {method:'POST'},
45009 *     'query':  {method:'GET', isArray:true},
45010 *     'remove': {method:'DELETE'},
45011 *     'delete': {method:'DELETE'} };
45012 *   ```
45013 *
45014 *   Calling these methods invoke an {@link ng.$http} with the specified http method,
45015 *   destination and parameters. When the data is returned from the server then the object is an
45016 *   instance of the resource class. The actions `save`, `remove` and `delete` are available on it
45017 *   as  methods with the `$` prefix. This allows you to easily perform CRUD operations (create,
45018 *   read, update, delete) on server-side data like this:
45019 *   ```js
45020 *   var User = $resource('/user/:userId', {userId:'@id'});
45021 *   var user = User.get({userId:123}, function() {
45022 *     user.abc = true;
45023 *     user.$save();
45024 *   });
45025 *   ```
45026 *
45027 *   It is important to realize that invoking a $resource object method immediately returns an
45028 *   empty reference (object or array depending on `isArray`). Once the data is returned from the
45029 *   server the existing reference is populated with the actual data. This is a useful trick since
45030 *   usually the resource is assigned to a model which is then rendered by the view. Having an empty
45031 *   object results in no rendering, once the data arrives from the server then the object is
45032 *   populated with the data and the view automatically re-renders itself showing the new data. This
45033 *   means that in most cases one never has to write a callback function for the action methods.
45034 *
45035 *   The action methods on the class object or instance object can be invoked with the following
45036 *   parameters:
45037 *
45038 *   - HTTP GET "class" actions: `Resource.action([parameters], [success], [error])`
45039 *   - non-GET "class" actions: `Resource.action([parameters], postData, [success], [error])`
45040 *   - non-GET instance actions:  `instance.$action([parameters], [success], [error])`
45041 *
45042 *
45043 *   Success callback is called with (value (Object|Array), responseHeaders (Function),
45044 *   status (number), statusText (string)) arguments, where the value is the populated resource
45045 *   instance or collection object. The error callback is called with (httpResponse) argument.
45046 *
45047 *   Class actions return empty instance (with additional properties below).
45048 *   Instance actions return promise of the action.
45049 *
45050 *   The Resource instances and collections have these additional properties:
45051 *
45052 *   - `$promise`: the {@link ng.$q promise} of the original server interaction that created this
45053 *     instance or collection.
45054 *
45055 *     On success, the promise is resolved with the same resource instance or collection object,
45056 *     updated with data from server. This makes it easy to use in
45057 *     {@link ngRoute.$routeProvider resolve section of $routeProvider.when()} to defer view
45058 *     rendering until the resource(s) are loaded.
45059 *
45060 *     On failure, the promise is rejected with the {@link ng.$http http response} object, without
45061 *     the `resource` property.
45062 *
45063 *     If an interceptor object was provided, the promise will instead be resolved with the value
45064 *     returned by the interceptor.
45065 *
45066 *   - `$resolved`: `true` after first server interaction is completed (either with success or
45067 *      rejection), `false` before that. Knowing if the Resource has been resolved 
45067is useful in
45068 *      data-binding.
45069 *
45070 *   The Resource instances and collections have these additional methods:
45071 *
45072 *   - `$cancelRequest`: If there is a cancellable, pending request related to the instance or
45073 *      collection, calling this method will abort the request.
45074 *
45075 *   The Resource instances have these additional methods:
45076 *
45077 *   - `toJSON`: It returns a simple object without any of the extra properties added as part of
45078 *     the Resource API. This object can be serialized through {@link angular.toJson} safely
45079 *     without attaching Angular-specific fields. Notice that `JSON.stringify` (and
45080 *     `angular.toJson`) automatically use this method when serializing a Resource instance
45081 *     (see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#toJSON()_behavior)).
45082 *
45083 * @example
45084 *
45085 * # Credit card resource
45086 *
45087 * ```js
45088     // Define CreditCard class
45089     var CreditCard = $resource('/user/:userId/card/:cardId',
45090      {userId:123, cardId:'@id'}, {
45091       charge: {method:'POST', params:{charge:true}}
45092      });
45093
45094     // We can retrieve a collection from the server
45095     var cards = CreditCard.query(function() {
45096       // GET: /user/123/card
45097       // server returns: [ {id:456, number:'1234', name:'Smith'} ];
45098
45099       var card = cards[0];
45100       // each item is an instance of CreditCard
45101       expect(card instanceof CreditCard).toEqual(true);
45102       card.name = "J. Smith";
45103       // non GET methods are mapped onto the instances
45104       card.$save();
45105       // POST: /user/123/card/456 {id:456, number:'1234', name:'J. Smith'}
45106       // server returns: {id:456, number:'1234', name: 'J. Smith'};
45107
45108       // our custom method is mapped as well.
45109       card.$charge({amount:9.99});
45110       // POST: /user/123/card/456?amount=9.99&charge=true {id:456, number:'1234', name:'J. Smith'}
45111     });
45112
45113     // we can create an instance as well
45114     var newCard = new CreditCard({number:'0123'});
45115     newCard.name = "Mike Smith";
45116     newCard.$save();
45117     // POST: /user/123/card {number:'0123', name:'Mike Smith'}
45118     // server returns: {id:789, number:'0123', name: 'Mike Smith'};
45119     expect(newCard.id).toEqual(789);
45120 * ```
45121 *
45122 * The object returned from this function execution is a resource "class" which has "static" method
45123 * for each action in the definition.
45124 *
45125 * Calling these methods invoke `$http` on the `url` template with the given `method`, `params` and
45126 * `headers`.
45127 *
45128 * @example
45129 *
45130 * # User resource
45131 *
45132 * When the data is returned from the server then the object is an instance of the resource type and
45133 * all of the non-GET methods are available with `$` prefix. This allows you to easily support CRUD
45134 * operations (create, read, update, delete) on server-side data.
45135
45136   ```js
45137     var User = $resource('/user/:userId', {userId:'@id'});
45138     User.get({userId:123}, function(user) {
45139       user.abc = true;
45140       user.$save();
45141     });
45142   ```
45143 *
45144 * It's worth noting that the success callback for `get`, `query` and other methods gets passed
45145 * in the response that came from the server as well as $http header getter function, so one
45146 * could rewrite the above example and get access to http headers as:
45147 *
45148   ```js
45149     var User = $resource('/user/:userId', {userId:'@id'});
45150     User.get({userId:123}, function(user, getResponseHeaders){
45151       user.abc = true;
45152       user.$save(function(user, putResponseHeaders) {
45153         //user => saved user object
45154         //putResponseHeaders => $http header getter
45155       });
45156     });
45157   ```
45158 *
45159 * You can also access the raw `$http` promise via the `$promise` property on the object returned
45160 *
45161   ```
45162     var User = $resource('/user/:userId', {userId:'@id'});
45163     User.get({userId:123})
45164         .$promise.then(function(user) {
45165           $scope.user = user;
45166         });
45167   ```
45168 *
45169 * @example
45170 *
45171 * # Creating a custom 'PUT' request
45172 *
45173 * In this example we create a custom method on our resource to make a PUT request
45174 * ```js
45175 *    var app = angular.module('app', ['ngResource', 'ngRoute']);
45176 *
45177 *    // Some APIs expect a PUT request in the format URL/object/ID
45178 *    // Here we are creating an 'update' method
45179 *    app.factory('Notes', ['$resource', function($resource) {
45180 *    return $resource('/notes/:id', null,
45181 *        {
45182 *            'update': { method:'PUT' }
45183 *        });
45184 *    }]);
45185 *
45186 *    // In our controller we get the ID from the URL using ngRoute and $routeParam
45186s
45187 *    // We pass in $routeParams and our Notes factory along with $scope
45188 *    app.controller('NotesCtrl', ['$scope', '$routeParams', 'Notes',
45189                                      function($scope, $routeParams, Notes) {
45190 *    // First get a note object from the factory
45191 *    var note = Notes.get({ id:$routeParams.id });
45192 *    $id = note.id;
45193 *
45194 *    // Now call update passing in the ID first then the object you are updating
45195 *    Notes.update({ id:$id }, note);
45196 *
45197 *    // This will PUT /notes/ID with the note object in the request payload
45198 *    }]);
45199 * ```
45200 *
45201 * @example
45202 *
45203 * # Cancelling requests
45204 *
45205 * If an action's configuration specifies that it is cancellable, you can cancel the request related
45206 * to an instance or collection (as long as it is a result of a "non-instance" call):
45207 *
45208   ```js
45209     // ...defining the `Hotel` resource...
45210     var Hotel = $resource('/api/hotel/:id', {id: '@id'}, {
45211       // Let's make the `query()` method cancellable
45212       query: {method: 'get', isArray: true, cancellable: true}
45213     });
45214
45215     // ...somewhere in the PlanVacationController...
45216     ...
45217     this.onDestinationChanged = function onDestinationChanged(destination) {
45218       // We don't care about any pending request for hotels
45219       // in a different destination any more
45220       this.availableHotels.$cancelRequest();
45221
45222       // Let's query for hotels in '<destination>'
45223       // (calls: /api/hotel?location=<destination>)
45224       this.availableHotels = Hotel.query({location: destination});
45225     };
45226   ```
45227 *
45228 */
45229angular.module('ngResource', ['ng']).
45230  provider('$resource', function ResourceProvider() {
45231    var PROTOCOL_AND_DOMAIN_REGEX = /^https?:\/\/[^/]*/;
45232
45233    var provider = this;
45234
45235    /**
45236     * @ngdoc property
45237     * @name $resourceProvider#defaults
45238     * @description
45239     * Object containing default options used when creating `$resource` instances.
45240     *
45241     * The default values satisfy a wide range of usecases, but you may choose to overwrite any of
45242     * them to further customize your instances. The available properties are:
45243     *
45244     * - **stripTrailingSlashes** – `{boolean}` – If true, then the trailing slashes from any
45245     *   calculated URL will be stripped.<br />
45246     *   (Defaults to true.)
45247     * - **cancellable** – `{boolean}` – If true, the request made by a "non-instance" call will be
45248     *   cancelled (if not already completed) by calling `$cancelRequest()` on the call's return
45249     *   value. For more details, see {@link ngResource.$resource}. This can be overwritten per
45250     *   resource class or action.<br />
45251     *   (Defaults to false.)
45252     * - **actions** - `{Object.<Object>}` - A hash with default actions declarations. Actions are
45253     *   high-level methods corresponding to RESTful actions/methods on resources. An action may
45254     *   specify what HTTP method to use, what URL to hit, if the return value will be a single
45255     *   object or a collection (array) of objects etc. For more details, see
45256     *   {@link ngResource.$resource}. The actions can also be enhanced or overwritten per resource
45257     *   class.<br />
45258     *   The default actions are:
45259     *   ```js
45260     *   {
45261     *     get: {method: 'GET'},
45262     *     save: {method: 'POST'},
45263     *     query: {method: 'GET', isArray: true},
45264     *     remove: {method: 'DELETE'},
45265     *     delete: {method: 'DELETE'}
45266     *   }
45267     *   ```
45268     *
45269     * #### Example
45270     *
45271     * For example, you can specify a new `update` action that uses the `PUT` HTTP verb:
45272     *
45273     * ```js
45274     *   angular.
45275     *     module('myApp').
45276     *     config(['$resourceProvider', function ($resourceProvider) {
45277     *       $resourceProvider.defaults.actions.update = {
45278     *         method: 'PUT'
45279     *       };
45280     *     });
45281     * ```
45282     *
45283     * Or you can even overwrite the whole `actions` list and specify your own:
45284     *
45285     * ```js
45286     *   angular.
45287     *     module('myApp').
45288     *     config(['$resourceProvider', function ($resourceProvider) {
45289     *       $resourceProvider.defaults.actions = {
45290     *         create: {method: 'POST'},
45291     *         get:    {method: 'GET'},
45292     *         getAll: {method: 'GET', isArray:true},
45293     *         update: {method: 'PUT'},
45294     *         delete: {method: 'DELETE'}
45295     *       };
45296     *     });
45297     * ```
45298     *
45299     */
45300    this.defaults = {
45301      // Strip slashes by default
45302      stripTrailingSlashes: true,
45303
45304      // Make non-instance requests cancellable (via `$cancelRequest()`)
45305      cancellable: false,
45306
45307      // Default actions configuration
45308      actions: {
45309        'get': {method: 'GET'},
45310        'save': {method: 'POST'},
45311        'query': {method: 'GET', isArray: true},
45312        'remove': {method: 'DELETE'},
45313        'delete': {method: 'DELETE'}
45314      }
45315    };
45316
45317    this.$get = ['$http', '$log', '$q', '$timeout', function($http, $log, $q, $timeout) {
45318
45319      var noop = angular.noop,
45320        forEach = angular.forEach,
45321        extend = angular.extend,
45322        copy = angular.copy,
45323        isArray = angular.isArray,
45324        isDefined = angular.isDefined,
45325        isFunction = angular.isFunction,
45326        isNumber = angular.isNumber;
45327
45328      /**
45329       * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
45330       * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set
45331       * (pchar) allowed in path segments:
45332       *    segment       = *pchar
45333       *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
45334       *    pct-encoded   = "%" HEXDIG HEXDIG
45335       *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
45336       *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
45337       *                     / "*" / "+" / "," / ";" / "="
45338       */
45339      function encodeUriSegment(val) {
45340        return encodeUriQuery(val, true).
45341          replace(/%26/gi, '&').
45342          replace(/%3D/gi, '=').
45343          replace(/%2B/gi, '+');
45344      }
45345
45346
45347      /**
45348       * This method is intended for encoding *key* or *value* parts of query component. We need a
45349       * custom method because encodeURIComponent is too aggressive and encodes stuff that doesn't
45350       * have to be encoded per http://tools.ietf.org/html/rfc3986:
45351       *    query       = *( pchar / "/" / "?" )
45352       *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
45353       *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
45354       *    pct-encoded   = "%" HEXDIG HEXDIG
45355       *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
45356       *                     / "*" / "+" / "," / ";" / "="
45357       */
45358      function encodeUriQuery(val, pctEncodeSpaces) {
45359        return encodeURIComponent(val).
45360          replace(/%40/gi, '@').
45361          replace(/%3A/gi, ':').
45362          replace(/%24/g, '$').
45363          replace(/%2C/gi, ',').
45364          replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
45365      }
45366
45367      function Route(template, defaults) {
45368        this.template = template;
45369        this.defaults = extend({}, provider.defaults, defaults);
45370        this.urlParams = {};
45371      }
45372
45373      Route.prototype = {
45374        setUrlParams: function(config, params, actionUrl) {
45375          var self = this,
45376            url = actionUrl || self.template,
45377            val,
45378            encodedVal,
45379            protocolAndDomain = '';
45380
45381          var urlParams = self.urlParams = {};
45382          forEach(url.split(/\W/), function(param) {
45383            if (param === 'hasOwnProperty') {
45384              throw $resourceMinErr('badname', 'hasOwnProperty is not a valid parameter name.');
45385            }
45386            if (!(new RegExp('^\\d+$').test(param)) && param &&
45387              (new RegExp('(^|[^\\\\]):' + param + '(\\W|$)').test(url))) {
45388              urlParams[param] = {
45389                isQueryParamValue: (new RegExp('\\?.*=:' + param + '(?:\\W|$)')).test(url)
45390              };
45391            }
45392          });
45393          url = url.replace(/\\:/g, ':');
45394          url = url.replace(PROTOCOL_AND_DOMAIN_REGEX, function(match) {
45395            protocolAndDomain = match;
45396            return '';
45397          });
45398
45399          params = params || {};
45400          forEach(self.urlParams, function(paramInfo, urlParam) {
45401            val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam];
45402            if (isDefined(val) && val !== null) {
45403              if (paramInfo.isQueryParamValue) {
45404                encodedVal = encodeUriQuery(val, true);
45405              } else {
45406                encodedVal = encodeUriSegment(val);
45407              }
45408              url = url.replace(new RegExp(':' + urlParam + '(\\W|$)', 'g'), function(match, p1) {
45409                return encodedVal + p1;
45410              });
45411            } else {
45412              url = url.replace(new RegExp('(/?):' + urlParam + '(\\W|$)', 'g'), function(match,
45413                  leadingSlashes, tail) {
45414                if (tail.charAt(0) === '/') {
45415                  return tail;
45416                } else {
45417                  return leadingSlashes + tail;
45418                }
45419              });
45420            }
45421          });
45422
45423          // strip trailing slashes and set the url (unless this behavior is specifically disabled)
45424          if (self.defaults.stripTrailingSlashes) {
45425            url = url.replace(/\/+$/, '') || '/';
45426          }
45427
45428          // then replace collapse `/.` if found in the last URL path segment before the query
45429          // E.g. `http://url.com/id./format?q=x` becomes `http://url.com/id.format?q=x`
45430          url = url.replace(/\/\.(?=\w+($|\?))/, '.');
45431          // replace escaped `/\.` with `/.`
45432          config.url = protocolAndDomain + url.replace(/\/\\\./, '/.');
45433
45434
45435          // set params - delegate param encoding to $http
45436          forEach(params, function(value, key) {
45437            if (!self.urlParams[key]) {
45438              config.params = config.params || {};
45439              config.params[key] = value;
45440            }
45441          });
45442        }
45443      };
45444
45445
45446      function resourceFactory(url, paramDefaults, actions, options) {
45447        var route = new Route(url, options);
45448
45449        actions = extend({}, provider.defaults.actions, actions);
45450
45451        function extractParams(data, actionParams) {
45452          var ids = {};
45453          actionParams = extend({}, paramDefaults, actionParams);
45454          forEach(actionParams, function(value, key) {
45455            if (isFunction(value)) { value = value(data); }
45456            ids[key] = value && value.charAt && value.charAt(0) === '@' ?
45457              lookupDottedPath(data, value.substr(1)) : value;
45458          });
45459          return ids;
45460        }
45461
45462        function defaultResponseInterceptor(response) {
45463          return response.resource;
45464        }
45465
45466        function Resource(value) {
45467          shallowClearAndCopy(value || {}, this);
45468        }
45469
45470        Resource.prototype.toJSON = function() {
45471          var data = extend({}, this);
45472          delete data.$promise;
45473          delete data.$resolved;
45474          delete data.$cancelRequest;
45475          return data;
45476        };
45477
45478        forEach(actions, function(action, name) {
45479          var hasBody = /^(POST|PUT|PATCH)$/i.test(action.method);
45480          var numericTimeout = action.timeout;
45481          var cancellable = isDefined(action.cancellable) ?
45482              action.cancellable : route.defaults.cancellable;
45483
45484          if (numericTimeout && !isNumber(numericTimeout)) {
45485            $log.debug('ngResource:\n' +
45486                       '  Only numeric values are allowed as `timeout`.\n' +
45487                       '  Promises are not supported in $resource, because the same value would ' +
45488                       'be used for multiple requests. If you are looking for a way to cancel ' +
45489                       'requests, you should use the `cancellable` option.');
45490            delete action.timeout;
45491            numericTimeout = null;
45492          }
45493
45494          Resource[name] = function(a1, a2, a3, a4) {
45495            var params = {}, data, success, error;
45496
45497            switch (arguments.length) {
45498              case 4:
45499                error = a4;
45500                success = a3;
45501                // falls through
45502              case 3:
45503              case 2:
45504                if (isFunction(a2)) {
45505                  if (isFunction(a1)) {
45506                    success = a1;
45507                    error = a2;
45508                    break;
45509                  }
45510
45511                  success = a2;
45512                  error = a3;
45513                  // falls through
45514                } else {
45515                  params = a1;
45516                  data = a2;
45517                  success = a3;
45518                  break;
45519                }
45520                // falls through
45521              case 1:
45522                if (isFunction(a1)) success = a1;
45523                else if (hasBody) data = a1;
45524                else params = a1;
45525                break;
45526              case 0: break;
45527              default:
45528                throw $resourceMinErr('badargs',
45529                  'Expected up to 4 arguments [params, data, success, error], got {0} arguments',
45530                  arguments.length);
45531            }
45532
45533            var isInstanceCall = this instanceof Resource;
45534            var value = isInstanceCall ? data : (action.isArray ? [] : new Resource(data));
45535            var httpConfig = {};
45536            var responseInterceptor = action.interceptor && action.interceptor.response ||
45537              defaultResponseInterceptor;
45538            var responseErrorInterceptor = action.interceptor && action.interceptor.responseError ||
45539              undefined;
45540            var timeoutDeferred;
45541            var numericTimeoutPromise;
45542
45543            forEach(action, function(value, key) {
45544              switch (key) {
45545                default:
45546                  httpConfig[key] = copy(value);
45547                  break;
45548                case 'params':
45549                case 'isArray':
45550                case 'interceptor':
45551                case 'cancellable':
45552                  break;
45553              }
45554            });
45555
45556            if (!isInstanceCall && cancellable) {
45557              timeoutDeferred = $q.defer();
45558              httpConfig.timeout = timeoutDeferred.promise;
45559
45560              if (numericTimeout) {
45561                numericTimeoutPromise = $timeout(timeoutDeferred.resolve, numericTimeout);
45562              }
45563            }
45564
45565            if (hasBody) httpConfig.data = data;
45566            route.setUrlParams(httpConfig,
45567              extend({}, extractParams(data, action.params || {}), params),
45568              action.url);
45569
45570            var promise = $http(httpConfig).then(function(response) {
45571              var data = response.data;
45572
45573              if (data) {
45574                // Need to convert action.isArray to boolean in case it is undefined
45575                if (isArray(data) !== (!!action.isArray)) {
45576                  throw $resourceMinErr('badcfg',
45577                      'Error in resource configuration for action `{0}`. Expected response to ' +
45578                      'contain an {1} but got an {2} (Request: {3} {4})', name, action.isArray ? 'array' : 'object',
45579                    isArray(data) ? 'array' : 'object', httpConfig.method, httpConfig.url);
45580                }
45581                if (action.isArray) {
45582                  value.length = 0;
45583                  forEach(data, function(item) {
45584                    if (typeof item === 'object') {
45585                      value.push(new Resource(item));
45586                    } else {
45587                      // Valid JSON values may be string literals, and these should not be converted
45588                      // into objects. These items will not have access to the Resource prototype
45589                      // methods, but unfortunately there
45590                      value.push(item);
45591                    }
45592                  });
45593                } else {
45594                  var promise = value.$promise;     // Save the promise
45595                  shallowClearAndCopy(data, value);
45596                  value.$promise = promise;         // Restore the promise
45597                }
45598              }
45599              response.resource = value;
45600
45601              return response;
45602            }, function(response) {
45603              (error || noop)(response);
45604              return $q.reject(response);
45605            });
45606
45607            promise['finally'](function() {
45608              value.$resolved = true;
45609              if (!isInstanceCall && cancellable) {
45610                value.$cancelRequest = noop;
45611                $timeout.cancel(numericTimeoutPromise);
45612                timeoutDeferred = numericTimeoutPromise = httpConfig.timeout = null;
45613              }
45614            });
45615
45616            promise = promise.then(
45617              function(response) {
45618                var value = responseInterceptor(response);
45619                (success || noop)(value, response.headers, response.status, response.statusText);
45620                return value;
45621              },
45622              responseErrorInterceptor);
45623
45624            if (!isInstanceCall) {
45625              // we are creating instance / collection
45626              // - set the initial promise
45627              // - return the instance / collection
45628              value.$promise = promise;
45629              value.$resolved = false;
45630              if (cancellable) value.$cancelRequest = timeoutDeferred.resolve;
45631
45632              return value;
45633            }
45634
45635            // instance call
45636            return promise;
45637          };
45638
45639
45640          Resource.prototype['$' + name] = function(params, success, error) {
45641            if (isFunction(params)) {
45642              error = success; success = params; params = {};
45643            }
45644            var result = Resource[name].call(this, params, this, success, error);
45645            return result.$promise || result;
45646          };
45647        });
45648
45649        Resource.bind = function(additionalParamDefaults) {
45650          var extendedParamDefaults = extend({}, paramDefaults, additionalParamDefaults);
45651          return resourceFactory(url, extendedParamDefaults, actions, options);
45652        };
45653
45654        return Resource;
45655      }
45656
45657      return resourceFactory;
45658    }];
45659  });
45660
45661
45662})(window, window.angular);
45663
45664/**
45665 * @license AngularJS v1.5.11
45666 * (c) 2010-2017 Google, Inc. http://angularjs.org
45667 * License: MIT
45668 */
45669(function(window, angular) {'use strict';
45670
45671var forEach;
45672var isArray;
45673var isString;
45674var jqLite;
45675
45676/**
45677 * @ngdoc module
45678 * @name ngMessages
45679 * @description
45680 *
45681 * The `ngMessages` module provides enhanced support for displaying messages within templates
45682 * (typically within forms or when rendering message objects that return key/value data).
45683 * Instead of relying on JavaScript code and/or complex ng-if statements within your form template to
45684 * show and hide error messages specific to the state of an input field, the `ngMessages` and
45685 * `ngMessage` directives are designed to handle the complexity, inheritance and priority
45686 * sequencing based on the order of how the messages are defined in the template.
45687 *
45688 * Currently, the ngMessages module only contains the code for the `ngMessages`, `ngMessagesInclude`
45689 * `ngMessage` and `ngMessageExp` directives.
45690 *
45691 * # Usage
45692 * The `ngMessages` directive allows keys in a key/value collection to be associated with a child element
45693 * (or 'message') that will show or hide based on the truthiness of that key's value in the collection. A common use
45694 * case for `ngMessages` is to display error messages for inputs using the `$error` object exposed by the
45695 * {@link ngModel ngModel} directive.
45696 *
45697 * The child elements of the `ngMessages` directive are matched to the collection keys by a `ngMessage` or
45698 * `ngMessageExp` directive. The value of these attributes must match a key in the collection that is provided by
45699 * the `ngMessages` directive.
45700 *
45701 * Consider the following example, which illustrates a typical use case of `ngMessages`. Within the form `myForm` we
45702 * have a text input named `myField` which is bound to the scope variable `field` using the {@link ngModel ngModel}
45703 * directive.
45704 *
45705 * The `myField` field is a required input of type `email` with a maximum length of 15 characters.
45706 *
45707 * ```html
45708 * <form name="myForm">
45709 *   <label>
45710 *     Enter text:
45711 *     <input type="email" ng-model="field" name="myField" required maxlength="15" />
45712 *   </label>
45713 *   <div ng-messages="myForm.myField.$error" role="alert">
45714 *     <div ng-message="required">Please enter a value for this field.</div>
45715 *     <div ng-message="email">This field must be a valid email address.</div>
45716 *     <div ng-message="maxlength">This field can be at most 15 characters long.</div>
45717 *   </div>
45718 * </form>
45719 * ```
45720 *
45721 * In order to show error messages corresponding to `myField` we first create an element with an `ngMessages` attribute
45722 * set to the `$error` object owned by the `myField` input in our `myForm` form.
45723 *
45724 * Within this element we then create separate elements for each of the possible errors that `myField` could have.
45725 * The `ngMessage` attribute is used to declare which element(s) will appear for which error - for example,
45726 * setting `ng-message="required"` specifies that this particular element should be displayed when there
45727 * is no value present for the required field `myField` (because the key `required` will be `true` in the object
45728 * `myForm.myField.$error`).
45729 *
45730 * ### Message order
45731 *
45732 * By default, `ngMessages` will only display one message for a particular key/value collection at any time. If more
45733 * than one message (or error) key is currently true, then which message is shown is determined by the order of messages
45734 * in the HTML template code (messages declared first are prioritised). This mechanism means the developer does not have
45735 * to prioritize messages using custom JavaScript code.
45736 *
45737 * Given the following error object for our example (which informs us that the field `myField` currently has both the
45738 * `required` and `email` errors):
45739 *
45740 * ```javascript
45741 * <!-- keep in mind that ngModel automatically sets these error flags -->
45742 * myField.$error = { required : true, email: true, maxlength: false };
45743 * ```
45744 * The `required` message will be displayed to the user since it appears before the `email` message in the DOM.
45745 * Once the user types a single character, the `required` message will disappear (since the field now has a value)
45746 * but the `email` message will be visible because it is still applicable.
45747 *
45748 * ### Displaying multiple messages at the same time
45749 *
45750 * While `ngMessages` will by default only display one error element at a time, the `ng-messages-multiple` attribute can
45751 * be applied to the `ngMessages` container element to cause it to display all applicable error messages at once:
45752 *
45753 * ```html
45754 * <!-- attribute-style usage -->
45755 * <div ng-messages="myForm.myField.$error" ng-messages-multiple>...</div>
45756 *
45757 * <!-- element-style usage -->
45758 * <ng-messages for="myForm.myField.$error" multiple>...</ng-messages>
45759 * ```
45760 *
45761 * ## Reusing and Overriding Messages
45762 * In addition to prioritization, ngMessages also allows for including messages from a remote or an inline
45763 * template. This allows for generic collection of messages to be reused across multiple parts of an
45764 * application.
45765 *
45766 * ```html
45767 * <script type="text/ng-template" id="error-messages">
45768 *   <div ng-message="required">This field is required</div>
45769 *   <div ng-message="minlength">This field is too short</div>
45770 * </script>
45771 *
45772 * <div ng-messages="myForm.myField.$error" role="alert">
45773 *   <div ng-messages-include="error-messages"></div>
45774 * </div>
45775 * ```
45776 *
45777 * However, including generic messages may not be useful enough to match all input fields, therefore,
45778 * `ngMessages` provides the ability to override messages defined in the remote template by redefining
45779 * them within the directive container.
45780 *
45781 * ```html
45782 * <!-- a generic template of error messages known as "my-custom-messages" -->
45783 * <script type="text/ng-template" id="my-custom-messages">
45784 *   <div ng-message="required">This field is required</div>
45785 *   <div ng-message="minlength">This field is too short</div>
45786 * </script>
45787 *
45788 * <form name="myForm">
45789 *   <label>
45790 *     Email address
45791 *     <input type="email"
45792 *            id="email"
45793 *            name="myEmail"
45794 *            ng-model="email"
45795 *            minlength="5"
45796 *            required />
45797 *   </label>
45798 *   <!-- any ng-message elements that appear BEFORE the ng-messages-include will
45799 *        override the messages present in the ng-messages-include template -->
45800 *   <div ng-messages="myForm.myEmail.$error" role="alert">
45801 *     <!-- this required message has overridden the template message -->
45802 *     <div ng-message="required">You did not enter your email address</div>
45803 *
45804 *     <!-- this is a brand new message and will appear last in the prioritization -->
45805 *     <div ng-message="email">
45805Your email address is invalid</div>
45806 *
45807 *     <!-- and here are the generic error messages -->
45808 *     <div ng-messages-include="my-custom-messages"></div>
45809 *   </div>
45810 * </form>
45811 * ```
45812 *
45813 * In the example HTML code above the message that is set on required will override the corresponding
45814 * required message defined within the remote template. Therefore, with particular input fields (such
45815 * email addresses, date fields, autocomplete inputs, etc...), specialized error messages can be applied
45816 * while more generic messages can be used to handle other, more general input errors.
45817 *
45818 * ## Dynamic Messaging
45819 * ngMessages also supports using expressions to dynamically change key values. Using arrays and
45820 * repeaters to list messages is also supported. This means that the code below will be able to
45821 * fully adapt itself and display the appropriate message when any of the expression data changes:
45822 *
45823 * ```html
45824 * <form name="myForm">
45825 *   <label>
45826 *     Email address
45827 *     <input type="email"
45828 *            name="myEmail"
45829 *            ng-model="email"
45830 *            minlength="5"
45831 *            required />
45832 *   </label>
45833 *   <div ng-messages="myForm.myEmail.$error" role="alert">
45834 *     <div ng-message="required">You did not enter your email address</div>
45835 *     <div ng-repeat="errorMessage in errorMessages">
45836 *       <!-- use ng-message-exp for a message whose key is given by an expression -->
45837 *       <div ng-message-exp="errorMessage.type">{{ errorMessage.text }}</div>
45838 *     </div>
45839 *   </div>
45840 * </form>
45841 * ```
45842 *
45843 * The `errorMessage.type` expression can be a string value or it can be an array so
45844 * that multiple errors can be associated with a single error message:
45845 *
45846 * ```html
45847 *   <label>
45848 *     Email address
45849 *     <input type="email"
45850 *            ng-model="data.email"
45851 *            name="myEmail"
45852 *            ng-minlength="5"
45853 *            ng-maxlength="100"
45854 *            required />
45855 *   </label>
45856 *   <div ng-messages="myForm.myEmail.$error" role="alert">
45857 *     <div ng-message-exp="'required'">You did not enter your email address</div>
45858 *     <div ng-message-exp="['minlength', 'maxlength']">
45859 *       Your email must be between 5 and 100 characters long
45860 *     </div>
45861 *   </div>
45862 * ```
45863 *
45864 * Feel free to use other structural directives such as ng-if and ng-switch to further control
45865 * what messages are active and when. Be careful, if you place ng-message on the same element
45866 * as these structural directives, Angular may not be able to determine if a message is active
45867 * or not. Therefore it is best to place the ng-message on a child element of the structural
45868 * directive.
45869 *
45870 * ```html
45871 * <div ng-messages="myForm.myEmail.$error" role="alert">
45872 *   <div ng-if="showRequiredError">
45873 *     <div ng-message="required">Please enter something</div>
45874 *   </div>
45875 * </div>
45876 * ```
45877 *
45878 * ## Animations
45879 * If the `ngAnimate` module is active within the application then the `ngMessages`, `ngMessage` and
45880 * `ngMessageExp` directives will trigger animations whenever any messages are added and removed from
45881 * the DOM by the `ngMessages` directive.
45882 *
45883 * Whenever the `ngMessages` directive contains one or more visible messages then the `.ng-active` CSS
45884 * class will be added to the element. The `.ng-inactive` CSS class will be applied when there are no
45885 * messages present. Therefore, CSS transitions and keyframes as well as JavaScript animations can
45886 * hook into the animations whenever these classes are added/removed.
45887 *
45888 * Let's say that our HTML code for our messages container looks like so:
45889 *
45890 * ```html
45891 * <div ng-messages="myMessages" class="my-messages" role="alert">
45892 *   <div ng-message="alert" class="some-message">...</div>
45893 *   <div ng-message="fail" class="some-message">...</div>
45894 * </div>
45895 * ```
45896 *
45897 * Then the CSS animation code for the message container looks like so:
45898 *
45899 * ```css
45900 * .my-messages {
45901 *   transition:1s linear all;
45902 * }
45903 * .my-messages.ng-active {
45904 *   // messages are visible
45905 * }
45906 * .my-messages.ng-inactive {
45907 *   // messages are hidden
45908 * }
45909 * ```
45910 *
45911 * Whenever an inner message is attached (becomes visible) or removed (becomes hidden) then the enter
45912 * and leave animation is triggered for each particular element bound to the `ngMessage` directive.
45913 *
45914 * Therefore, the CSS code for the inner messages looks like so:
45915 *
45916 * ```css
45917 * .some-message {
45918 *   transition:1s linear all;
45919 * }
45920 *
45921 * .some-message.ng-enter {}
45922 * .some-message.ng-enter.ng-enter-active {}
45923 *
45924 * .some-message.ng-leave {}
45925 * .some-message.ng-leave.ng-leave-active {}
45926 * ```
45927 *
45928 * {@link ngAnimate Click here} to learn how to use JavaScript animations or to learn more about ngAnimate.
45929 */
45930angular.module('ngMessages', [], function initAngularHelpers() {
45931  // Access helpers from angular core.
45932  // Do it inside a `config` block to ensure `window.angular` is available.
45933  forEach = angular.forEach;
45934  isArray = angular.isArray;
45935  isString = angular.isString;
45936  jqLite = angular.element;
45937})
45938
45939  /**
45940   * @ngdoc directive
45941   * @module ngMessages
45942   * @name ngMessages
45943   * @restrict AE
45944   *
45945   * @description
45946   * `ngMessages` is a directive that is designed to show and hide messages based on the state
45947   * of a key/value object that it listens on. The directive itself complements error message
45948   * reporting with the `ngModel` $error object (which stores a key/value state of validation errors).
45949   *
45950   * `ngMessages` manages the state of internal messages within its container element. The internal
45951   * messages use the `ngMessage` directive and will be inserted/removed from the page depending
45952   * on if they're present within the key/value object. By default, only one message will be displayed
45953   * at a time and this depends on the prioritization of the messages within the template. (This can
45954   * be changed by using the `ng-messages-multiple` or `multiple` attribute on the directive container.)
45955   *
45956   * A remote template can also be used to promote message reusability and messages can also be
45957   * overridden.
45958   *
45959   * {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`.
45960   *
45961   * @usage
45962   * ```html
45963   * <!-- using attribute directives -->
45964   * <ANY ng-messages="expression" role="alert">
45965   *   <ANY ng-message="stringValue">...</ANY>
45966   *   <ANY ng-message="stringValue1, stringValue2, ...">...</ANY>
45967   *   <ANY ng-message-exp="expressionValue">...</ANY>
45968   * </ANY>
45969   *
45970   * <!-- or by using element directives -->
45971   * <ng-messages for="expression" role="alert">
45972   *   <ng-message when="stringValue">...</ng-message>
45973   *   <ng-message when="stringValue1, stringValue2, ...">...</ng-message>
45974   *   <ng-message when-exp="expressionValue">...</ng-message>
45975   * </ng-messages>
45976   * ```
45977   *
45978   * @param {string} ngMessages an angular expression evaluating to a key/value object
45979   *                 (this is typically the $error object on an ngModel instance).
45980   * @param {string=} ngMessagesMultiple|multiple when set, all messages will be displayed with true
45981   *
45982   * @example
45983   * <example name="ngMessages-directive" module="ngMessagesExample"
45984   *          deps="angular-messages.js"
45985   *          animations="true" fixBase="true">
45986   *   <file name="index.html">
45987   *     <form name="myForm">
45988   *       <label>
45989   *         Enter your name:
45990   *         <input type="text"
45991   *                name="myName"
45992   *                ng-model="name"
45993   *                ng-minlength="5"
45994   *                ng-maxlength="20"
45995   *                required />
45996   *       </label>
45997   *       <pre>myForm.myName.$error = {{ myForm.myName.$error | json }}</pre>
45998   *
45999   *       <div ng-messages="myForm.myName.$error" style="color:maroon" role="alert">
46000   *         <div ng-message="required">You did not enter a field</div>
46001   *         <div ng-message="minlength">Your field is too short</div>
46002   *         <div ng-message="maxlength">Your field is too long</div>
46003   *       </div>
46004   *     </form>
46005   *   </file>
46006   *   <file name="script.js">
46007   *     angular.module('ngMessagesExample', ['ngMessages']);
46008   *   </file>
46009   * </example>
46010   */
46011  .directive('ngMessages', ['$animate', function($animate) {
46012    var ACTIVE_CLASS = 'ng-active';
46013    var INACTIVE_CLASS = 'ng-inactive';
46014
46015    return {
46016      require: 'ngMessages',
46017      restrict: 'AE',
46018      controller: ['$element', '$scope', '$attrs', function NgMessagesCtrl($element, $scope, $attrs) {
46019        var ctrl = this;
46020        var latestKey = 0;
46021        var nextAttachId = 0;
46022
46023        this.getAttachId = function getAttachId() { return nextAttachId++; };
46024
46025        var messages = this.messages = {};
46026        var renderLater, cachedCollection;
46027
46028        this.render = function(collection) {
46029          collection = collection || {};
46030
46031          renderLater = false;
46032          cachedCollection = collection;
46033
46034          // this is true if the attribute is empty or if the attribute value is truthy
46035          var multiple = isAttrTruthy($scope, $attrs.ngMessagesMultiple) ||
46036                         isAttrTruthy($scope, $attrs.multiple);
46037
46038          var unmatchedMessages = [];
46039          var matchedKeys = {};
46040          var messageItem = ctrl.head;
46041          var messageFound = false;
46042          var totalMessages = 0;
46043
46044          // we use != instead of !== to allow for both undefined and null values
46045          while (messageItem != null) {
46046            totalMessages++;
46047            var messageCtrl = messageItem.message;
46048
46049            var messageUsed = false;
46050            if (!messageFound) {
46051              forEach(collection, function(value, key) {
46052                if (!messageUsed && truthy(value) && messageCtrl.test(key)) {
46053                  // this is to prevent the same error name from showing up twice
46054                  if (matchedKeys[key]) return;
46055                  matchedKeys[key] = true;
46056
46057                  messageUsed = true;
46058                  messageCtrl.attach();
46059                }
46060              });
46061            }
46062
46063            if (messageUsed) {
46064              // unless we want to display multiple messages then we should
46065              // set a flag here to avoid displaying the next message in the list
46066              messageFound = !multiple;
46067            } else {
46068              unmatchedMessages.push(messageCtrl);
46069            }
46070
46071            messageItem = messageItem.next;
46072          }
46073
46074          forEach(unmatchedMessages, function(messageCtrl) {
46075            messageCtrl.detach();
46076          });
46077
46078          if (unmatchedMessages.length !== totalMessages) {
46079            $animate.setClass($element, ACTIVE_CLASS, INACTIVE_CLASS);
46080          } else {
46081            $animate.setClass($element, INACTIVE_CLASS, ACTIVE_CLASS);
46082          }
46083        };
46084
46085        $scope.$watchCollection($attrs.ngMessages || $attrs['for'], ctrl.render);
46086
46087        // If the element is destroyed, proactively destroy all the currently visible messages
46088        $element.on('$destroy', function() {
46089          forEach(messages, function(item) {
46090            item.message.detach();
46091          });
46092        });
46093
46094        this.reRender = function() {
46095          if (!renderLater) {
46096            renderLater = true;
46097            $scope.$evalAsync(function() {
46098              if (renderLater && cachedCollection) {
46099                ctrl.render(cachedCollection);
46100              }
46101            });
46102          }
46103        };
46104
46105        this.register = function(comment, messageCtrl) {
46106          var nextKey = latestKey.toString();
46107          messages[nextKey] = {
46108            message: messageCtrl
46109          };
46110          insertMessageNode($element[0], comment, nextKey);
46111          comment.$$ngMessageNode = nextKey;
46112          latestKey++;
46113
46114          ctrl.reRender();
46115        };
46116
46117        this.deregister = function(comment) {
46118          var key = comment.$$ngMessageNode;
46119          delete comment.$$ngMessageNode;
46120          removeMessageNode($element[0], comment, key);
46121          delete messages[key];
46122          ctrl.reRender();
46123        };
46124
46125        function findPreviousMessage(parent, comment) {
46126          var prevNode = comment;
46127          var parentLookup = [];
46128
46129          while (prevNode && prevNode !== parent) {
46130            var prevKey = prevNode.$$ngMessageNode;
46131            if (prevKey && prevKey.length) {
46132              return messages[prevKey];
46133            }
46134
46135            // dive deeper into the DOM and examine its children for any ngMessage
46136            // comments that may be in an element that appears deeper in the list
46137            if (prevNode.childNodes.length && parentLookup.indexOf(prevNode) === -1) {
46138              parentLookup.push(prevNode);
46139              prevNode = prevNode.childNodes[prevNode.childNodes.length - 1];
46140            } else if (prevNode.previousSibling) {
46141              prevNode = prevNode.previousSibling;
46142            } else {
46143              prevNode = prevNode.parentNode;
46144              parentLookup.push(prevNode);
46145            }
46146          }
46147        }
46148
46149        function insertMessageNode(parent, comment, key) {
46150          var messageNode = messages[key];
46151          if (!ctrl.head) {
46152            ctrl.head = messageNode;
46153          } else {
46154            var match = findPreviousMessage(parent, comment);
46155            if (match) {
46156              messageNode.next = match.next;
46157              match.next = messageNode;
46158            } else {
46159              messageNode.next = ctrl.head;
46160              ctrl.head = messageNode;
46161            }
46162          }
46163        }
46164
46165        function removeMessageNode(parent, comment, key) {
46166          var messageNode = messages[key];
46167
46168          var match = findPreviousMessage(parent, comment);
46169          if (match) {
46170            match.next = messageNode.next;
46171          } else {
46172            ctrl.head = messageNode.next;
46173          }
46174        }
46175      }]
46176    };
46177
46178    function isAttrTruthy(scope, attr) {
46179     return (isString(attr) && attr.length === 0) || //empty attribute
46180            truthy(scope.$eval(attr));
46181    }
46182
46183    function truthy(val) {
46184      return isString(val) ? val.length : !!val;
46185    }
46186  }])
46187
46188  /**
46189   * @ngdoc directive
46190   * @name ngMessagesInclude
46191   * @restrict AE
46192   * @scope
46193   *
46194   * @description
46195   * `ngMessagesInclude` is a directive with the purpose to import existing ngMessage template
46196   * code from a remote template and place the downloaded template code into the exact spot
46197   * that the ngMessagesInclude directive is placed within the ngMessages container. This allows
46198   * for a series of pre-defined messages to be reused and also allows for the developer to
46199   * determine what messages are overridden due to the placement of the ngMessagesInclude directive.
46200   *
46201   * @usage
46202   * ```html
46203   * <!-- using attribute directives -->
46204   * <ANY ng-messages="expression" role="alert">
46205   *   <ANY ng-messages-include="remoteTplString">...</ANY>
46206   * </ANY>
46207   *
46208   * <!-- or by using element directives -->
46209   * <ng-messages for="expression" role="alert">
46210   *   <ng-messages-include src="expressionValue1">...</ng-messages-include>
46211   * </ng-messages>
46212   * ```
46213   *
46214   * {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`.
46215   *
46216   * @param {string} ngMessagesInclude|src a string value corresponding to the remote template.
46217   */
46218  .directive('ngMessagesInclude',
46219    ['$templateRequest', '$document', '$compile', function($templateRequest, $document, $compile) {
46220
46221    return {
46222      restrict: 'AE',
46223      require: '^^ngMessages', // we only require this for validation sake
46224      link: function($scope, element, attrs) {
46225        var src = attrs.ngMessagesInclude || attrs.src;
46226        $templateRequest(src).then(function(html) {
46227          if ($scope.$$destroyed) return;
46228
46229          if (isString(html) && !html.trim()) {
46230            // Empty template - nothing to compile
46231            replaceElementWithMarker(element, src);
46232          } else {
46233            // Non-empty template - compile and link
46234            $compile(html)($scope, function(contents) {
46235              element.after(contents);
46236              replaceElementWithMarker(element, src);
46237            });
46238          }
46239        });
46240      }
46241    };
46242
46243    // Helpers
46244    function replaceElementWithMarker(element, src) {
46245      // A comment marker is placed for debugging purposes
46246      var comment = $compile.$$createComment ?
46247          $compile.$$createComment('ngMessagesInclude', src) :
46248          $document[0].createComment(' ngMessagesInclude: ' + src + ' ');
46249      var marker = jqLite(comment);
46250      element.after(marker);
46251
46252      // Don't pollute the DOM anymore by keeping an empty directive element
46253      element.remove();
46254    }
46255  }])
46256
46257  /**
46258   * @ngdoc directive
46259   * @name ngMessage
46260   * @restrict AE
46261   * @scope
46262   *
46263   * @description
46264   * `ngMessage` is a directive with the purpose to show and hide a particular message.
46265   * For `ngMessage` to operate, a parent `ngMessages` directive on a parent DOM element
46266   * must be situated since it determines which messages are visible based on the state
46267   * of the provided key/value map that `ngMessages` listens on.
46268   *
46269   * More information about using `ngMessage` can be found in the
46270   * {@link module:ngMessages `ngMessages` module documentation}.
46271   *
46272   * @usage
46273   * ```html
46274   * <!-- using attribute directives -->
46275   * <ANY ng-messages="expression" role="alert">
46276   *   <ANY ng-message="stringValue">...</ANY>
46277   *   <ANY ng-message="stringValue1, stringValue2, ...">...</ANY>
46278   * </ANY>
46279   *
46280   * <!-- or by using element directives -->
46281   * <ng-messages for="expression" role="alert">
46282   *   <ng-message when="stringValue">...</ng-message>
46283   *   <ng-message when="stringValue1, stringValue2, ...">...</ng-message>
46284   * </ng-messages>
46285   * ```
46286   *
46287   * @param {expression}
46287 ngMessage|when a string value corresponding to the message key.
46288   */
46289  .directive('ngMessage', ngMessageDirectiveFactory())
46290
46291
46292  /**
46293   * @ngdoc directive
46294   * @name ngMessageExp
46295   * @restrict AE
46296   * @priority 1
46297   * @scope
46298   *
46299   * @description
46300   * `ngMessageExp` is the same as {@link directive:ngMessage `ngMessage`}, but instead of a static
46301   * value, it accepts an expression to be evaluated for the message key.
46302   *
46303   * @usage
46304   * ```html
46305   * <!-- using attribute directives -->
46306   * <ANY ng-messages="expression">
46307   *   <ANY ng-message-exp="expressionValue">...</ANY>
46308   * </ANY>
46309   *
46310   * <!-- or by using element directives -->
46311   * <ng-messages for="expression">
46312   *   <ng-message when-exp="expressionValue">...</ng-message>
46313   * </ng-messages>
46314   * ```
46315   *
46316   * {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`.
46317   *
46318   * @param {expression} ngMessageExp|whenExp an expression value corresponding to the message key.
46319   */
46320  .directive('ngMessageExp', ngMessageDirectiveFactory());
46321
46322function ngMessageDirectiveFactory() {
46323  return ['$animate', function($animate) {
46324    return {
46325      restrict: 'AE',
46326      transclude: 'element',
46327      priority: 1, // must run before ngBind, otherwise the text is set on the comment
46328      terminal: true,
46329      require: '^^ngMessages',
46330      link: function(scope, element, attrs, ngMessagesCtrl, $transclude) {
46331        var commentNode = element[0];
46332
46333        var records;
46334        var staticExp = attrs.ngMessage || attrs.when;
46335        var dynamicExp = attrs.ngMessageExp || attrs.whenExp;
46336        var assignRecords = function(items) {
46337          records = items
46338              ? (isArray(items)
46339                  ? items
46340                  : items.split(/[\s,]+/))
46341              : null;
46342          ngMessagesCtrl.reRender();
46343        };
46344
46345        if (dynamicExp) {
46346          assignRecords(scope.$eval(dynamicExp));
46347          scope.$watchCollection(dynamicExp, assignRecords);
46348        } else {
46349          assignRecords(staticExp);
46350        }
46351
46352        var currentElement, messageCtrl;
46353        ngMessagesCtrl.register(commentNode, messageCtrl = {
46354          test: function(name) {
46355            return contains(records, name);
46356          },
46357          attach: function() {
46358            if (!currentElement) {
46359              $transclude(function(elm, newScope) {
46360                $animate.enter(elm, null, element);
46361                currentElement = elm;
46362
46363                // Each time we attach this node to a message we get a new id that we can match
46364                // when we are destroying the node later.
46365                var $$attachId = currentElement.$$attachId = ngMessagesCtrl.getAttachId();
46366
46367                // in the event that the element or a parent element is destroyed
46368                // by another structural directive then it's time
46369                // to deregister the message from the controller
46370                currentElement.on('$destroy', function() {
46371                  if (currentElement && currentElement.$$attachId === $$attachId) {
46372                    ngMessagesCtrl.deregister(commentNode);
46373                    messageCtrl.detach();
46374                  }
46375                  newScope.$destroy();
46376                });
46377              });
46378            }
46379          },
46380          detach: function() {
46381            if (currentElement) {
46382              var elm = currentElement;
46383              currentElement = null;
46384              $animate.leave(elm);
46385            }
46386          }
46387        });
46388      }
46389    };
46390  }];
46391
46392  function contains(collection, key) {
46393    if (collection) {
46394      return isArray(collection)
46395          ? collection.indexOf(key) >= 0
46396          : collection.hasOwnProperty(key);
46397    }
46398  }
46399}
46400
46401
46402})(window, window.angular);
46403
46404/*!
46405 * Select2 4.0.13
vendor: 17,402 bytes, lines 46405-46904
46405
46406 * https://select2.github.io
46407 *
46408 * Released under the MIT license
46409 * https://github.com/select2/select2/blob/master/LICENSE.md
46410 */
46411;(function (factory) {
46412  if (typeof define === 'function' && define.amd) {
46413    // AMD. Register as an anonymous module.
46414    define(['jquery'], factory);
46415  } else if (typeof module === 'object' && module.exports) {
46416    // Node/CommonJS
46417    module.exports = function (root, jQuery) {
46418      if (jQuery === undefined) {
46419        // require('jQuery') returns a factory that requires window to
46420        // build a jQuery instance, we normalize how we use modules
46421        // that require this pattern but the window provided is a noop
46422        // if it's defined (how jquery works)
46423        if (typeof window !== 'undefined') {
46424          jQuery = require('jquery');
46425        }
46426        else {
46427          jQuery = require('jquery')(root);
46428        }
46429      }
46430      factory(jQuery);
46431      return jQuery;
46432    };
46433  } else {
46434    // Browser globals
46435    factory(jQuery);
46436  }
46437} (function (jQuery) {
46438  // This is needed so we can catch the AMD loader configuration and use it
46439  // The inner file should be wrapped (by `banner.start.js`) in a function that
46440  // returns the AMD loader references.
46441  var S2 =(function () {
46442  // Restore the Select2 AMD loader so it can be used
46443  // Needed mostly in the language files, where the loader is not inserted
46444  if (jQuery && jQuery.fn && jQuery.fn.select2 && jQuery.fn.select2.amd) {
46445    var S2 = jQuery.fn.select2.amd;
46446  }
46447var S2;(function () { if (!S2 || !S2.requirejs) {
46448if (!S2) { S2 = {}; } else { require = S2; }
46449/**
46450 * @license almond 0.3.3 Copyright jQuery Foundation and other contributors.
46451 * Released under MIT license, http://github.com/requirejs/almond/LICENSE
46452 */
46453//Going sloppy to avoid 'use strict' string cost, but strict practices should
46454//be followed.
46455/*global setTimeout: false */
46456
46457var requirejs, require, define;
46458(function (undef) {
46459    var main, req, makeMap, handlers,
46460        defined = {},
46461        waiting = {},
46462        config = {},
46463        defining = {},
46464        hasOwn = Object.prototype.hasOwnProperty,
46465        aps = [].slice,
46466        jsSuffixRegExp = /\.js$/;
46467
46468    function hasProp(obj, prop) {
46469        return hasOwn.call(obj, prop);
46470    }
46471
46472    /**
46473     * Given a relative module name, like ./something, normalize it to
46474     * a real name that can be mapped to a path.
46475     * @param {String} name the relative name
46476     * @param {String} baseName a real name that the name arg is relative
46477     * to.
46478     * @returns {String} normalized name
46479     */
46480    function normalize(name, baseName) {
46481        var nameParts, nameSegment, mapValue, foundMap, lastIndex,
46482            foundI, foundStarMap, starI, i, j, part, normalizedBaseParts,
46483            baseParts = baseName && baseName.split("/"),
46484            map = config.map,
46485            starMap = (map && map['*']) || {};
46486
46487        //Adjust any relative paths.
46488        if (name) {
46489            name = name.split('/');
46490            lastIndex = name.length - 1;
46491
46492            // If wanting node ID compatibility, strip .js from end
46493            // of IDs. Have to do this here, and not in nameToUrl
46494            // because node allows either .js or non .js to map
46495            // to same file.
46496            if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {
46497                name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');
46498            }
46499
46500            // Starts with a '.' so need the baseName
46501            if (name[0].charAt(0) === '.' && baseParts) {
46502                //Convert baseName to array, and lop off the last part,
46503                //so that . matches that 'directory' and not name of the baseName's
46504                //module. For instance, baseName of 'one/two/three', maps to
46505                //'one/two/three.js', but we want the directory, 'one/two' for
46506                //this normalization.
46507                normalizedBaseParts = baseParts.slice(0, baseParts.length - 1);
46508                name = normalizedBaseParts.concat(name);
46509            }
46510
46511            //start trimDots
46512            for (i = 0; i < name.length; i++) {
46513                part = name[i];
46514                if (part === '.') {
46515                    name.splice(i, 1);
46516                    i -= 1;
46517                } else if (part === '..') {
46518                    // If at the start, or previous value is still ..,
46519                    // keep them so that when converted to a path it may
46520                    // still work when converted to a path, even though
46521                    // as an ID it is less than ideal. In larger point
46522                    // releases, may be better to just kick out an error.
46523                    if (i === 0 || (i === 1 && name[2] === '..') || name[i - 1] === '..') {
46524                        continue;
46525                    } else if (i > 0) {
46526                        name.splice(i - 1, 2);
46527                        i -= 2;
46528                    }
46529                }
46530            }
46531            //end trimDots
46532
46533            name = name.join('/');
46534        }
46535
46536        //Apply map config if available.
46537        if ((baseParts || starMap) && map) {
46538            nameParts = name.split('/');
46539
46540            for (i = nameParts.length; i > 0; i -= 1) {
46541                nameSegment = nameParts.slice(0, i).join("/");
46542
46543                if (baseParts) {
46544                    //Find the longest baseName segment match in the config.
46545                    //So, do joins on the biggest to smallest lengths of baseParts.
46546                    for (j = baseParts.length; j > 0; j -= 1) {
46547                        mapValue = map[baseParts.slice(0, j).join('/')];
46548
46549                        //baseName segment has  config, find if it has one for
46550                        //this name.
46551                        if (mapValue) {
46552                            mapValue = mapValue[nameSegment];
46553                            if (mapValue) {
46554                                //Match, update name to the new value.
46555                                foundMap = mapValue;
46556                                foundI = i;
46557                                break;
46558                            }
46559                        }
46560                    }
46561                }
46562
46563                if (foundMap) {
46564                    break;
46565                }
46566
46567                //Check for a star map match, but just hold on to it,
46568                //if there is a shorter segment match later in a matching
46569                //config, then favor over this star map.
46570                if (!foundStarMap && starMap && starMap[nameSegment]) {
46571                    foundStarMap = starMap[nameSegment];
46572                    starI = i;
46573                }
46574            }
46575
46576            if (!foundMap && foundStarMap) {
46577                foundMap = foundStarMap;
46578                foundI = starI;
46579            }
46580
46581            if (foundMap) {
46582                nameParts.splice(0, foundI, foundMap);
46583                name = nameParts.join('/');
46584            }
46585        }
46586
46587        return name;
46588    }
46589
46590    function makeRequire(relName, forceSync) {
46591        return function () {
46592            //A version of a require function that passes a moduleName
46593            //value for items that may need to
46594            //look up paths relative to the moduleName
46595            var args = aps.call(arguments, 0);
46596
46597            //If first arg is not require('string'), and there is only
46598            //one arg, it is the array form without a callback. Insert
46599            //a null so that the following concat is correct.
46600            if (typeof args[0] !== 'string' && args.length === 1) {
46601                args.push(null);
46602            }
46603            return req.apply(undef, args.concat([relName, forceSync]));
46604        };
46605    }
46606
46607    function makeNormalize(relName) {
46608        return function (name) {
46609            return normalize(name, relName);
46610        };
46611    }
46612
46613    function makeLoad(depName) {
46614        return function (value) {
46615            defined[depName] = value;
46616        };
46617    }
46618
46619    function callDep(name) {
46620        if (hasProp(waiting, name)) {
46621            var args = waiting[name];
46622            delete waiting[name];
46623            defining[name] = true;
46624            main.apply(undef, args);
46625        }
46626
46627        if (!hasProp(defined, name) && !hasProp(defining, name)) {
46628            throw new Error('No ' + name);
46629        }
46630        return defined[name];
46631    }
46632
46633    //Turns a plugin!resource to [plugin, resource]
46634    //with the plugin being undefined if the name
46635    //did not have a plugin prefix.
46636    function splitPrefix(name) {
46637        var prefix,
46638            index = name ? name.indexOf('!') : -1;
46639        if (index > -1) {
46640            prefix = name.substring(0, index);
46641            name = name.substring(index + 1, name.length);
46642        }
46643        return [prefix, name];
46644    }
46645
46646    //Creates a parts array for a relName where first part is plugin ID,
46647    //second part is resource ID. Assumes relName has already been normalized.
46648    function makeRelParts(relName) {
46649        return relName ? splitPrefix(relName) : [];
46650    }
46651
46652    /**
46653     * Makes a name map, normalizing the name, and using a plugin
46654     * for normalization if necessary. Grabs a ref to plugin
46655     * too, as an optimization.
46656     */
46657    makeMap = function (name, relParts) {
46658        var plugin,
46659            parts = splitPrefix(name),
46660            prefix = parts[0],
46661            relResourceName = relParts[1];
46662
46663        name = parts[1];
46664
46665        if (prefix) {
46666            prefix = normalize(prefix, relResourceName);
46667            plugin = callDep(prefix);
46668        }
46669
46670        //Normalize according
46671        if (prefix) {
46672            if (plugin && plugin.normalize) {
46673                name = plugin.normalize(name, makeNormalize(relResourceName));
46674            } else {
46675                name = normalize(name, relResourceName);
46676            }
46677        } else {
46678            name = normalize(name, relResourceName);
46679            parts = splitPrefix(name);
46680            prefix = parts[0];
46681            name = parts[1];
46682            if (prefix) {
46683                plugin = callDep(prefix);
46684            }
46685        }
46686
46687        //Using ridiculous property names for space reasons
46688        return {
46689            f: prefix ? prefix + '!' + name : name, //fullName
46690            n: name,
46691            pr: prefix,
46692            p: plugin
46693        };
46694    };
46695
46696    function makeConfig(name) {
46697        return function () {
46698            return (config && config.config && config.config[name]) || {};
46699        };
46700    }
46701
46702    handlers = {
46703        require: function (name) {
46704            return makeRequire(name);
46705        },
46706        exports: function (name) {
46707            var e = defined[name];
46708            if (typeof e !== 'undefined') {
46709                return e;
46710            } else {
46711                return (defined[name] = {});
46712            }
46713        },
46714        module: function (name) {
46715            return {
46716                id: name,
46717                uri: '',
46718                exports: defined[name],
46719                config: makeConfig(name)
46720            };
46721        }
46722    };
46723
46724    main = function (name, deps, callback, relName) {
46725        var cjsModule, depName, ret, map, i, relParts,
46726            args = [],
46727            callbackType = typeof callback,
46728            usingExports;
46729
46730        //Use name if no relName
46731        relName = relName || name;
46732        relParts = makeRelParts(relName);
46733
46734        //Call the callback to define the module, if necessary.
46735        if (callbackType === 'undefined' || callbackType === 'function') {
46736            //Pull out the defined dependencies and pass the ordered
46737            //values to the callback.
46738            //Default to [require, exports, module] if no deps
46739            deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps;
46740            for (i = 0; i < deps.length; i += 1) {
46741                map = makeMap(deps[i], relParts);
46742                depName = map.f;
46743
46744                //Fast path CommonJS standard dependencies.
46745                if (depName === "require") {
46746                    args[i] = handlers.require(name);
46747                } else if (depName === "exports") {
46748                    //CommonJS module spec 1.1
46749                    args[i] = handlers.exports(name);
46750                    usingExports = true;
46751                } else if (depName === "module") {
46752                    //CommonJS module spec 1.1
46753                    cjsModule = args[i] = handlers.module(name);
46754                } else if (hasProp(defined, depName) ||
46755                           hasProp(waiting, depName) ||
46756                           hasProp(defining, depName)) {
46757                    args[i] = callDep(depName);
46758                } else if (map.p) {
46759                    map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {});
46760                    args[i] = defined[depName];
46761                } else {
46762                    throw new Error(name + ' missing ' + depName);
46763                }
46764            }
46765
46766            ret = callback ? callback.apply(defined[name], args) : undefined;
46767
46768            if (name) {
46769                //If setting exports via "module" is in play,
46770                //favor that over return value and exports. After that,
46771                //favor a non-undefined return value over exports use.
46772                if (cjsModule && cjsModule.exports !== undef &&
46773                        cjsModule.exports !== defined[name]) {
46774                    defined[name] = cjsModule.exports;
46775                } else if (ret !== undef || !usingExports) {
46776                    //Use the return value from the function.
46777                    defined[name] = ret;
46778                }
46779            }
46780        } else if (name) {
46781            //May just be an object definition for the module. Only
46782            //worry about defining if have a module name.
46783            defined[name] = callback;
46784        }
46785    };
46786
46787    requirejs = require = req = function (deps, callback, relName, forceSync, alt) {
46788        if (typeof deps === "string") {
46789            if (handlers[deps]) {
46790                //callback in this case is really relName
46791                return handlers[deps](callback);
46792            }
46793            //Just return the module wanted. In this scenario, the
46794            //deps arg is the module name, and second arg (if passed)
46795            //is just the relName.
46796            //Normalize module name, if it contains . or ..
46797            return callDep(makeMap(deps, makeRelParts(callback)).f);
46798        } else if (!deps.splice) {
46799            //deps is a config object, not an array.
46800            config = deps;
46801            if (config.deps) {
46802                req(config.deps, config.callback);
46803            }
46804            if (!callback) {
46805                return;
46806            }
46807
46808            if (callback.splice) {
46809                //callback is an array, which means it is a dependency list.
46810                //Adjust args if there are dependencies
46811                deps = callback;
46812                callback = relName;
46813                relName = null;
46814            } else {
46815                deps = undef;
46816            }
46817        }
46818
46819        //Support require(['a'])
46820        callback = callback || function () {};
46821
46822        //If relName is a function, it is an errback handler,
46823        //so remove it.
46824        if (typeof relName === 'function') {
46825            relName = forceSync;
46826            forceSync = alt;
46827        }
46828
46829        //Simulate async callback;
46830        if (forceSync) {
46831            main(undef, deps, callback, relName);
46832        } else {
46833            //Using a non-zero value because of concern for what old browsers
46834            //do, and latest browsers "upgrade" to 4 if lower value is used:
46835            //http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#dom-windowtimers-settimeout:
46836            //If want a value immediately, use require('id') instead -- something
46837            //that works in almond on the global level, but not guaranteed and
46838            //unlikely to work in other AMD implementations.
46839            setTimeout(function () {
46840                main(undef, deps, callback, relName);
46841            }, 4);
46842        }
46843
46844        return req;
46845    };
46846
46847    /**
46848     * Just drops the config on the floor, but returns req in case
46849     * the config return value is used.
46850     */
46851    req.config = function (cfg) {
46852        return req(cfg);
46853    };
46854
46855    /**
46856     * Expose module registry for debugging and tooling
46857     */
46858    requirejs._defined = defined;
46859
46860    define = function (name, deps, callback) {
46861        if (typeof name !== 'string') {
46862            throw new Error('See almond README: incorrect module build, no module name');
46863        }
46864
46865        //This module may not have dependencies
46866        if (!deps.splice) {
46867            //deps is not an array, so probably means
46868            //an object literal or factory function for
46869            //the value. Adjust args.
46870            callback = deps;
46871            deps = [];
46872        }
46873
46874        if (!hasProp(defined, name) && !hasProp(waiting, name)) {
46875            waiting[name] = [name, deps, callback];
46876        }
46877    };
46878
46879    define.amd = {
46880        jQuery: true
46881    };
46882}());
46883
46884S2.requirejs = requirejs;S2.require = require;S2.define = define;
46885}
46886}());
46887S2.define("almond", function(){});
46888
46889/* global jQuery:false, $:false */
46890S2.define('jquery',[],function () {
46891  var _$ = jQuery || $;
46892
46893  if (_$ == null && console && console.error) {
46894    console.error(
46895      'Select2: An instance of jQuery or a jQuery-compatible library was not ' +
46896      'found. Make sure that you are including jQuery before Select2 on your ' +
46897      'web page.'
46898    );
46899  }
46900
46901  return _$;
46902});
46903
46904S2.define('select2/utils',[
vendor: 5,112 bytes, lines 46905-47117
46905  'jquery'
46906], function ($) {
46907  var Utils = {};
46908
46909  Utils.Extend = function (ChildClass, SuperClass) {
46910    var __hasProp = {}.hasOwnProperty;
46911
46912    function BaseConstructor () {
46913      this.constructor = ChildClass;
46914    }
46915
46916    for (var key in SuperClass) {
46917      if (__hasProp.call(SuperClass, key)) {
46918        ChildClass[key] = SuperClass[key];
46919      }
46920    }
46921
46922    BaseConstructor.prototype = SuperClass.prototype;
46923    ChildClass.prototype = new BaseConstructor();
46924    ChildClass.__super__ = SuperClass.prototype;
46925
46926    return ChildClass;
46927  };
46928
46929  function getMethods (theClass) {
46930    var proto = theClass.prototype;
46931
46932    var methods = [];
46933
46934    for (var methodName in proto) {
46935      var m = proto[methodName];
46936
46937      if (typeof m !== 'function') {
46938        continue;
46939      }
46940
46941      if (methodName === 'constructor') {
46942        continue;
46943      }
46944
46945      methods.push(methodName);
46946    }
46947
46948    return methods;
46949  }
46950
46951  Utils.Decorate = function (SuperClass, DecoratorClass) {
46952    var decoratedMethods = getMethods(DecoratorClass);
46953    var superMethods = getMethods(SuperClass);
46954
46955    function DecoratedClass () {
46956      var unshift = Array.prototype.unshift;
46957
46958      var argCount = DecoratorClass.prototype.constructor.length;
46959
46960      var calledConstructor = SuperClass.prototype.constructor;
46961
46962      if (argCount > 0) {
46963        unshift.call(arguments, SuperClass.prototype.constructor);
46964
46965        calledConstructor = DecoratorClass.prototype.constructor;
46966      }
46967
46968      calledConstructor.apply(this, arguments);
46969    }
46970
46971    DecoratorClass.displayName = SuperClass.displayName;
46972
46973    function ctr () {
46974      this.constructor = DecoratedClass;
46975    }
46976
46977    DecoratedClass.prototype = new ctr();
46978
46979    for (var m = 0; m < superMethods.length; m++) {
46980      var superMethod = superMethods[m];
46981
46982      DecoratedClass.prototype[superMethod] =
46983        SuperClass.prototype[superMethod];
46984    }
46985
46986    var calledMethod = function (methodName) {
46987      // Stub out the original method if it's not decorating an actual method
46988      var originalMethod = function () {};
46989
46990      if (methodName in DecoratedClass.prototype) {
46991        originalMethod = DecoratedClass.prototype[methodName];
46992      }
46993
46994      var decoratedMethod = DecoratorClass.prototype[methodName];
46995
46996      return function () {
46997        var unshift = Array.prototype.unshift;
46998
46999        unshift.call(arguments, originalMethod);
47000
47001        return decoratedMethod.apply(this, arguments);
47002      };
47003    };
47004
47005    for (var d = 0; d < decoratedMethods.length; d++) {
47006      var decoratedMethod = decoratedMethods[d];
47007
47008      DecoratedClass.prototype[decoratedMethod] = calledMethod(decoratedMethod);
47009    }
47010
47011    return DecoratedClass;
47012  };
47013
47014  var Observable = function () {
47015    this.listeners = {};
47016  };
47017
47018  Observable.prototype.on = function (event, callback) {
47019    this.listeners = this.listeners || {};
47020
47021    if (event in this.listeners) {
47022      this.listeners[event].push(callback);
47023    } else {
47024      this.listeners[event] = [callback];
47025    }
47026  };
47027
47028  Observable.prototype.trigger = function (event) {
47029    var slice = Array.prototype.slice;
47030    var params = slice.call(arguments, 1);
47031
47032    this.listeners = this.listeners || {};
47033
47034    // Params should always come in as an array
47035    if (params == null) {
47036      params = [];
47037    }
47038
47039    // If there are no arguments to the event, use a temporary object
47040    if (params.length === 0) {
47041      params.push({});
47042    }
47043
47044    // Set the `_type` of the first object to the event
47045    params[0]._type = event;
47046
47047    if (event in this.listeners) {
47048      this.invoke(this.listeners[event], slice.call(arguments, 1));
47049    }
47050
47051    if ('*' in this.listeners) {
47052      this.invoke(this.listeners['*'], arguments);
47053    }
47054  };
47055
47056  Observable.prototype.invoke = function (listeners, params) {
47057    for (var i = 0, len = listeners.length; i < len; i++) {
47058      listeners[i].apply(this, params);
47059    }
47060  };
47061
47062  Utils.Observable = Observable;
47063
47064  Utils.generateChars = function (length) {
47065    var chars = '';
47066
47067    for (var i = 0; i < length; i++) {
47068      var randomChar = Math.floor(Math.random() * 36);
47069      chars += randomChar.toString(36);
47070    }
47071
47072    return chars;
47073  };
47074
47075  Utils.bind = function (func, context) {
47076    return function () {
47077      func.apply(context, arguments);
47078    };
47079  };
47080
47081  Utils._convertData = function (data) {
47082    for (var originalKey in data) {
47083      var keys = originalKey.split('-');
47084
47085      var dataLevel = data;
47086
47087      if (keys.length === 1) {
47088        continue;
47089      }
47090
47091      for (var k = 0; k < keys.length; k++) {
47092        var key = keys[k];
47093
47094        // Lowercase the first letter
47095        // By default, dash-separated becomes camelCase
47096        key = key.substring(0, 1).toLowerCase() + key.substring(1);
47097
47098        if (!(key in dataLevel)) {
47099          dataLevel[key] = {};
47100        }
47101
47102        if (k == keys.length - 1) {
47103          dataLevel[key] = data[originalKey];
47104        }
47105
47106        dataLevel = dataLevel[key];
47107      }
47108
47109      delete data[originalKey];
47110    }
47111
47112    return data;
47113  };
47114
47115  Utils.hasScroll = function (index, el) {
47116    // Adapted from the function created by @ShadowScripter
47117    // and adapted by @BillBarry on the Stack Exchange Code Review website.
47118    // The original code can be found at
47119    // http://codereview.stackexchange.com/q/13338
47120    // and was designed to be used with the Sizzle selector engine.
47121
47122    var $el = $(el);
47123    var overflowX = el.style.overflowX;
47124    var overflowY = el.style.overflowY;
47125
47126    //Check both x and y declarations
47127    if (overflowX === overflowY &&
47128        (overflowY === 'hidden' || overflowY === 'visible')) {
47129      return false;
47130    }
47131
47132    if (overflowX === 'scroll' || overflowY === 'scroll') {
47133      return true;
47134    }
47135
47136    return ($el.innerHeight() < el.scrollHeight ||
47137      $el.innerWidth() < el.scrollWidth);
47138  };
47139
47140  Utils.escapeMarkup = function (markup) {
47141    var replaceMap = {
47142      '\\': '&#92;',
47143      '&': '&amp;',
47144      '<': '&lt;',
47145      '>': '&gt;',
47146      '"': '&quot;',
47147      '\'': '&#39;',
47148      '/': '&#47;'
47149    };
47150
47151    // Do not try to escape the markup if it's not a string
47152    if (typeof markup !== 'string') {
47153      return markup;
47154    }
47155
47156    return String(markup).replace(/[&<>"'\/\\]/g, function (match) {
47157      return replaceMap[match];
47158    });
47159  };
47160
47161  // Append an array of jQuery nodes to a given element.
47162  Utils.appendMany = function ($element, $nodes) {
47163    // jQuery 1.7.x does not support $.fn.append() with an array
47164    // Fall back to a jQuery object collection using $.fn.add()
47165    if ($.fn.jquery.substr(0, 3) === '1.7') {
47166      var $jqNodes = $();
47167
47168      $.map($nodes, function (node) {
47169        $jqNodes = $jqNodes.add(node);
47170      });
47171
47172      $nodes = $jqNodes;
47173    }
47174
47175    $element.append($nodes);
47176  };
47177
47178  // Cache objects in Utils.__cache instead of $.data (see #4346)
47179  Utils.__cache = {};
47180
47181  var id = 0;
47182  Utils.GetUniqueElementId = function (element) {
47183    // Get a unique element Id. If element has no id,
47184    // creates a new unique number, stores it in the id
47185    // attribute and returns the new id.
47186    // If an id already exists, it simply returns it.
47187
47188    var select2Id = element.getAttribute('data-select2-id');
47189    if (select2Id == null) {
47190      // If element has id, use it.
47191      if (element.id) {
47192        select2Id = element.id;
47193        element.setAttribute('data-select2-id', select2Id);
47194      } else {
47195        element.setAttribute('data-select2-id', ++id);
47196        select2Id = id.toString();
47197      }
47198    }
47199    return select2Id;
47200  };
47201
47202  Utils.StoreData = function (element, name, value) {
47203    // Stores an item in the cache for a specified element.
47204    // name is the cache key.
47205    var id = Utils.GetUniqueElementId(element);
47206    if (!Utils.__cache[id]) {
47207      Utils.__cache[id] = {};
47208    }
47209
47210    Utils.__cache[id][name] = value;
47211  };
47212
47213  Utils.GetData = function (element, name) {
47214    // Retrieves a value from the cache by its key (name)
47215    // name is optional. If no name specified, return
47216    // all cache items for the specified element.
47217    // and for a specified element.
47218    var id = Utils.GetUniqueElementId(element);
47219    if (name) {
47220      if (Utils.__cache[id]) {
47221        if (Utils.__cache[id][name] != null) {
47222          return Utils.__cache[id][name];
47223        }
47224        return $(element).data(name); // Fallback to HTML5 data attribs.
47225      }
47226      return $(element).data(name); // Fallback to HTML5 data attribs.
47227    } else {
47228      return Utils.__cache[id];
47229    }
47230  };
47231
47232  Utils.RemoveData = function (element) {
47233    // Removes all cached items for a specified element.
47234    var id = Utils.GetUniqueElementId(element);
47235    if (Utils.__cache[id] != null) {
47236      delete Utils.__cache[id];
47237    }
47238
47239    element.removeAttribute('data-select2-id');
47240  };
47241
47242  return Utils;
47243});
47244
47245S2.define('select2/results',[
47246  'jquery',
47247  './utils'
47248], function ($, Utils) {
47249  function Results ($element, options, dataAdapter) {
47250    this.$element = $element;
47251    this.data = dataAdapter;
47252    this.options = options;
47253
47254    Results.__super__.constructor.call(this);
47255  }
47256
47257  Utils.Extend(Results, Utils.Observable);
47258
47259  Results.prototype.render = function () {
47260    var $results = $(
47261      '<ul class="select2-results__options" role="listbox"></ul>'
47262    );
47263
47264    if (this.options.get('multiple')) {
47265      $results.attr('aria-multiselectable', 'true');
47266    }
47267
47268    this.$results = $results;
47269
47270    return $results;
47271  };
47272
47273  Results.prototype.clear = function () {
47274    this.$results.empty();
47275  };
47276
47277  Results.prototype.displayMessage = function (params) {
47278    var escapeMarkup = this.options.get('escapeMarkup');
47279
47280    this.clear();
47281    this.hideLoading();
47282
47283    var $message = $(
47284      '<li role="alert" aria-live="assertive"' +
47285      ' class="select2-results__option"></li>'
47286    );
47287
47288    var message = this.options.get('translations').get(params.message);
47289
47290    $message.append(
47291      escapeMarkup(
47292        message(params.args)
47293      )
47294    );
47295
47296    $message[0].className += ' select2-results__message';
vendor: 47,567 bytes, lines 47297-49314
47297
47298    this.$results.append($message);
47299  };
47300
47301  Results.prototype.hideMessages = function () {
47302    this.$results.find('.select2-results__message').remove();
47303  };
47304
47305  Results.prototype.append = function (data) {
47306    this.hideLoading();
47307
47308    var $options = [];
47309
47310    if (data.results == null || data.results.length === 0) {
47311      if (this.$results.children().length === 0) {
47312        this.trigger('results:message', {
47313          message: 'noResults'
47314        });
47315      }
47316
47317      return;
47318    }
47319
47320    data.results = this.sort(data.results);
47321
47322    for (var d = 0; d < data.results.length; d++) {
47323      var item = data.results[d];
47324
47325      var $option = this.option(item);
47326
47327      $options.push($option);
47328    }
47329
47330    this.$results.append($options);
47331  };
47332
47333  Results.prototype.position = function ($results, $dropdown) {
47334    var $resultsContainer = $dropdown.find('.select2-results');
47335    $resultsContainer.append($results);
47336  };
47337
47338  Results.prototype.sort = function (data) {
47339    var sorter = this.options.get('sorter');
47340
47341    return sorter(data);
47342  };
47343
47344  Results.prototype.highlightFirstItem = function () {
47345    var $options = this.$results
47346      .find('.select2-results__option[aria-selected]');
47347
47348    var $selected = $options.filter('[aria-selected=true]');
47349
47350    // Check if there are any selected options
47351    if ($selected.length > 0) {
47352      // If there are selected options, highlight the first
47353      $selected.first().trigger('mouseenter');
47354    } else {
47355      // If there are no selected options, highlight the first option
47356      // in the dropdown
47357      $options.first().trigger('mouseenter');
47358    }
47359
47360    this.ensureHighlightVisible();
47361  };
47362
47363  Results.prototype.setClasses = function () {
47364    var self = this;
47365
47366    this.data.current(function (selected) {
47367      var selectedIds = $.map(selected, function (s) {
47368        return s.id.toString();
47369      });
47370
47371      var $options = self.$results
47372        .find('.select2-results__option[aria-selected]');
47373
47374      $options.each(function () {
47375        var $option = $(this);
47376
47377        var item = Utils.GetData(this, 'data');
47378
47379        // id needs to be converted to a string when comparing
47380        var id = '' + item.id;
47381
47382        if ((item.element != null && item.element.selected) ||
47383            (item.element == null && $.inArray(id, selectedIds) > -1)) {
47384          $option.attr('aria-selected', 'true');
47385        } else {
47386          $option.attr('aria-selected', 'false');
47387        }
47388      });
47389
47390    });
47391  };
47392
47393  Results.prototype.showLoading = function (params) {
47394    this.hideLoading();
47395
47396    var loadingMore = this.options.get('translations').get('searching');
47397
47398    var loading = {
47399      disabled: true,
47400      loading: true,
47401      text: loadingMore(params)
47402    };
47403    var $loading = this.option(loading);
47404    $loading.className += ' loading-results';
47405
47406    this.$results.prepend($loading);
47407  };
47408
47409  Results.prototype.hideLoading = function () {
47410    this.$results.find('.loading-results').remove();
47411  };
47412
47413  Results.prototype.option = function (data) {
47414    var option = document.createElement('li');
47415    option.className = 'select2-results__option';
47416
47417    var attrs = {
47418      'role': 'option',
47419      'aria-selected': 'false'
47420    };
47421
47422    var matches = window.Element.prototype.matches ||
47423      window.Element.prototype.msMatchesSelector ||
47424      window.Element.prototype.webkitMatchesSelector;
47425
47426    if ((data.element != null && matches.call(data.element, ':disabled')) ||
47427        (data.element == null && data.disabled)) {
47428      delete attrs['aria-selected'];
47429      attrs['aria-disabled'] = 'true';
47430    }
47431
47432    if (data.id == null) {
47433      delete attrs['aria-selected'];
47434    }
47435
47436    if (data._resultId != null) {
47437      option.id = data._resultId;
47438    }
47439
47440    if (data.title) {
47441      option.title = data.title;
47442    }
47443
47444    if (data.children) {
47445      attrs.role = 'group';
47446      attrs['aria-label'] = data.text;
47447      delete attrs['aria-selected'];
47448    }
47449
47450    for (var attr in attrs) {
47451      var val = attrs[attr];
47452
47453      option.setAttribute(attr, val);
47454    }
47455
47456    if (data.children) {
47457      var $option = $(option);
47458
47459      var label = document.createElement('strong');
47460      label.className = 'select2-results__group';
47461
47462      var $label = $(label);
47463      this.template(data, label);
47464
47465      var $children = [];
47466
47467      for (var c = 0; c < data.children.length; c++) {
47468        var child = data.children[c];
47469
47470        var $child = this.option(child);
47471
47472        $children.push($child);
47473      }
47474
47475      var $childrenContainer = $('<ul></ul>', {
47476        'class': 'select2-results__options select2-results__options--nested'
47477      });
47478
47479      $childrenContainer.append($children);
47480
47481      $option.append(label);
47482      $option.append($childrenContainer);
47483    } else {
47484      this.template(data, option);
47485    }
47486
47487    Utils.StoreData(option, 'data', data);
47488
47489    return option;
47490  };
47491
47492  Results.prototype.bind = function (container, $container) {
47493    var self = this;
47494
47495    var id = container.id + '-results';
47496
47497    this.$results.attr('id', id);
47498
47499    container.on('results:all', function (params) {
47500      self.clear();
47501      self.append(params.data);
47502
47503      if (container.isOpen()) {
47504        self.setClasses();
47505        self.highlightFirstItem();
47506      }
47507    });
47508
47509    container.on('results:append', function (params) {
47510      self.append(params.data);
47511
47512      if (container.isOpen()) {
47513        self.setClasses();
47514      }
47515    });
47516
47517    container.on('query', function (params) {
47518      self.hideMessages();
47519      self.showLoading(params);
47520    });
47521
47522    container.on('select', function () {
47523      if (!container.isOpen()) {
47524        return;
47525      }
47526
47527      self.setClasses();
47528
47529      if (self.options.get('scrollAfterSelect')) {
47530        self.highlightFirstItem();
47531      }
47532    });
47533
47534    container.on('unselect', function () {
47535      if (!container.isOpen()) {
47536        return;
47537      }
47538
47539      self.setClasses();
47540
47541      if (self.options.get('scrollAfterSelect')) {
47542        self.highlightFirstItem();
47543      }
47544    });
47545
47546    container.on('open', function () {
47547      // When the dropdown is open, aria-expended="true"
47548      self.$results.attr('aria-expanded', 'true');
47549      self.$results.attr('aria-hidden', 'false');
47550
47551      self.setClasses();
47552      self.ensureHighlightVisible();
47553    });
47554
47555    container.on('close', function () {
47556      // When the dropdown is closed, aria-expended="false"
47557      self.$results.attr('aria-expanded', 'false');
47558      self.$results.attr('aria-hidden', 'true');
47559      self.$results.removeAttr('aria-activedescendant');
47560    });
47561
47562    container.on('results:toggle', function () {
47563      var $highlighted = self.getHighlightedResults();
47564
47565      if ($highlighted.length === 0) {
47566        return;
47567      }
47568
47569      $highlighted.trigger('mouseup');
47570    });
47571
47572    container.on('results:select', function () {
47573      var $highlighted = self.getHighlightedResults();
47574
47575      if ($highlighted.length === 0) {
47576        return;
47577      }
47578
47579      var data = Utils.GetData($highlighted[0], 'data');
47580
47581      if ($highlighted.attr('aria-selected') == 'true') {
47582        self.trigger('close', {});
47583      } else {
47584        self.trigger('select', {
47585          data: data
47586        });
47587      }
47588    });
47589
47590    container.on('results:previous', function () {
47591      var $highlighted = self.getHighlightedResults();
47592
47593      var $options = self.$results.find('[aria-selected]');
47594
47595      var currentIndex = $options.index($highlighted);
47596
47597      // If we are already at the top, don't move further
47598      // If no options, currentIndex will be -1
47599      if (currentIndex <= 0) {
47600        return;
47601      }
47602
47603      var nextIndex = currentIndex - 1;
47604
47605      // If none are highlighted, highlight the first
47606      if ($highlighted.length === 0) {
47607        nextIndex = 0;
47608      }
47609
47610      var $next = $options.eq(nextIndex);
47611
47612      $next.trigger('mouseenter');
47613
47614      var currentOffset = self.$results.offset().top;
47615      var nextTop = $next.offset().top;
47616      var nextOffset = self.$results.scrollTop() + (nextTop - currentOffset);
47617
47618      if (nextIndex === 0) {
47619        self.$results.scrollTop(0);
47620      } else if (nextTop - currentOffset < 0) {
47621        self.$results.scrollTop(nextOffset);
47622      }
47623    });
47624
47625    container.on('results:next', function () {
47626      var $highlighted = self.getHighlightedResults();
47627
47628      var $options = self.$results.find('[aria-selected]');
47629
47630      var currentIndex = $options.index($highlighted);
47631
47632      var nextIndex = currentIndex + 1;
47633
47634      // If we are at the last option, stay there
47635      if (nextIndex >= $options.length) {
47636        return;
47637      }
47638
47639      var $next = $options.eq(nextIndex);
47640
47641      $next.trigger('mouseenter');
47642
47643      var currentOffset = self.$results.offset().top +
47644        self.$results.outerHeight(false);
47645      var nextBottom = $next.offset().top + $next.outerHeight(false);
47646      var nextOffset = self.$results.scrollTop() + nextBottom - currentOffset;
47647
47648      if (nextIndex === 0) {
47649        self.$results.scrollTop(0);
47650      } else if (nextBottom > currentOffset) {
47651        self.$results.scrollTop(nextOffset);
47652      }
47653    });
47654
47655    container.on('results:focus', function (params) {
47656      params.element.addClass('select2-results__option--highlighted');
47657    });
47658
47659    container.on('results:message', function (params) {
47660      self.displayMessage(params);
47661    });
47662
47663    if ($.fn.mousewheel) {
47664      this.$results.on('mousewheel', function (e) {
47665        var top = self.$results.scrollTop();
47666
47667        var bottom = self.$results.get(0).scrollHeight - top + e.deltaY;
47668
47669        var isAtTop = e.deltaY > 0 && top - e.deltaY <= 0;
47670        var isAtBottom = e.deltaY < 0 && bottom <= self.$results.height();
47671
47672        if (isAtTop) {
47673          self.$results.scrollTop(0);
47674
47675          e.preventDefault();
47676          e.stopPropagation();
47677        } else if (isAtBottom) {
47678          self.$results.scrollTop(
47679            self.$results.get(0).scrollHeight - self.$results.height()
47680          );
47681
47682          e.preventDefault();
47683          e.stopPropagation();
47684        }
47685      });
47686    }
47687
47688    this.$results.on('mouseup', '.select2-results__option[aria-selected]',
47689      function (evt) {
47690      var $this = $(this);
47691
47692      var data = Utils.GetData(this, 'data');
47693
47694      if ($this.attr('aria-selected') === 'true') {
47695        if (self.options.get('multiple')) {
47696          self.trigger('unselect', {
47697            originalEvent: evt,
47698            data: data
47699          });
47700        } else {
47701          self.trigger('close', {});
47702        }
47703
47704        return;
47705      }
47706
47707      self.trigger('select', {
47708        originalEvent: evt,
47709        data: data
47710      });
47711    });
47712
47713    this.$results.on('mouseenter', '.select2-results__option[aria-selected]',
47714      function (evt) {
47715      var data = Utils.GetData(this, 'data');
47716
47717      self.getHighlightedResults()
47718          .removeClass('select2-results__option--highlighted');
47719
47720      self.trigger('results:focus', {
47721        data: data,
47722        element: $(this)
47723      });
47724    });
47725  };
47726
47727  Results.prototype.getHighlightedResults = function () {
47728    var $highlighted = this.$results
47729    .find('.select2-results__option--highlighted');
47730
47731    return $highlighted;
47732  };
47733
47734  Results.prototype.destroy = function () {
47735    this.$results.remove();
47736  };
47737
47738  Results.prototype.ensureHighlightVisible = function () {
47739    var $highlighted = this.getHighlightedResults();
47740
47741    if ($highlighted.length === 0) {
47742      return;
47743    }
47744
47745    var $options = this.$results.find('[aria-selected]');
47746
47747    var currentIndex = $options.index($highlighted);
47748
47749    var currentOffset = this.$results.offset().top;
47750    var nextTop = $highlighted.offset().top;
47751    var nextOffset = this.$results.scrollTop() + (nextTop - currentOffset);
47752
47753    var offsetDelta = nextTop - currentOffset;
47754    nextOffset -= $highlighted.outerHeight(false) * 2;
47755
47756    if (currentIndex <= 2) {
47757      this.$results.scrollTop(0);
47758    } else if (offsetDelta > this.$results.outerHeight() || offsetDelta < 0) {
47759      this.$results.scrollTop(nextOffset);
47760    }
47761  };
47762
47763  Results.prototype.template = function (result, container) {
47764    var template = this.options.get('templateResult');
47765    var escapeMarkup = this.options.get('escapeMarkup');
47766
47767    var content = template(result, container);
47768
47769    if (content == null) {
47770      container.style.display = 'none';
47771    } else if (typeof content === 'string') {
47772      container.innerHTML = escapeMarkup(content);
47773    } else {
47774      $(container).append(content);
47775    }
47776  };
47777
47778  return Results;
47779});
47780
47781S2.define('select2/keys',[
47782
47783], function () {
47784  var KEYS = {
47785    BACKSPACE: 8,
47786    TAB: 9,
47787    ENTER: 13,
47788    SHIFT: 16,
47789    CTRL: 17,
47790    ALT: 18,
47791    ESC: 27,
47792    SPACE: 32,
47793    PAGE_UP: 33,
47794    PAGE_DOWN: 34,
47795    END: 35,
47796    HOME: 36,
47797    LEFT: 37,
47798    UP: 38,
47799    RIGHT: 39,
47800    DOWN: 40,
47801    DELETE: 46
47802  };
47803
47804  return KEYS;
47805});
47806
47807S2.define('select2/selection/base',[
47808  'jquery',
47809  '../utils',
47810  '../keys'
47811], function ($, Utils, KEYS) {
47812  function BaseSelection ($element, options) {
47813    this.$element = $element;
47814    this.options = options;
47815
47816    BaseSelection.__super__.constructor.call(this);
47817  }
47818
47819  Utils.Extend(BaseSelection, Utils.Observable);
47820
47821  BaseSelection.prototype.render = function () {
47822    var $selection = $(
47823      '<span class="select2-selection" role="combobox" ' +
47824      ' aria-haspopup="true" aria-expanded="false">' +
47825      '</span>'
47826    );
47827
47828    this._tabindex = 0;
47829
47830    if (Utils.GetData(this.$element[0], 'old-tabindex') != null) {
47831      this._tabindex = Utils.GetData(this.$element[0], 'old-tabindex');
47832    } else if (this.$element.attr('tabindex') != null) {
47833      this._tabindex = this.$element.attr('tabindex');
47834    }
47835
47836    $selection.attr('title', this.$element.attr('title'));
47837    $selection.attr('tabindex', this._tabindex);
47838    $selection.attr('aria-disabled', 'false');
47839
47840    this.$selection = $selection;
47841
47842    return $selection;
47843  };
47844
47845  BaseSelection.prototype.bind = function (container, $container) {
47846    var self = this;
47847
47848    var resultsId = container.id + '-results';
47849
47850    this.container = container;
47851
47852    this.$selection.on('focus', function (evt) {
47853      self.trigger('focus', evt);
47854    });
47855
47856    this.$selection.on('blur', function (evt) {
47857      self._handleBlur(evt);
47858    });
47859
47860    this.$selection.on('keydown', function (evt) {
47861      self.trigger('keypress', evt);
47862
47863      if (evt.which === KEYS.SPACE) {
47864        evt.preventDefault();
47865      }
47866    });
47867
47868    container.on('results:focus', function (params) {
47869      self.$selection.attr('aria-activedescendant', params.data._resultId);
47870    });
47871
47872    container.on('selection:update', function (params) {
47873      self.update(params.data);
47874    });
47875
47876    container.on('open', function () {
47877      // When the dropdown is open, aria-expanded="true"
47878      self.$selection.attr('aria-expanded', 'true');
47879      self.$selection.attr('aria-owns', resultsId);
47880
47881      self._attachCloseHandler(container);
47882    });
47883
47884    container.on('close', function () {
47885      // When the dropdown is closed, aria-expanded="false"
47886      self.$selection.attr('aria-expanded', 'false');
47887      self.$selection.removeAttr('aria-activedescendant');
47888      self.$selection.removeAttr('aria-owns');
47889
47890      self.$selection.trigger('focus');
47891
47892      self._detachCloseHandler(container);
47893    });
47894
47895    container.on('enable', function () {
47896      self.$selection.attr('tabindex', self._tabindex);
47897      self.$selection.attr('aria-disabled', 'false');
47898    });
47899
47900    container.on('disable', function () {
47901      self.$selection.attr('tabindex', '-1');
47902      self.$selection.attr('aria-disabled', 'true');
47903    });
47904  };
47905
47906  BaseSelection.prototype._handleBlur = function (evt) {
47907    var self = this;
47908
47909    // This needs to be delayed as the active element is the body when the tab
47910    // key is pressed, possibly along with others.
47911    window.setTimeout(function () {
47912      // Don't trigger `blur` if the focus is still in the selection
47913      if (
47914        (document.activeElement == self.$selection[0]) ||
47915        ($.contains(self.$selection[0], document.activeElement))
47916      ) {
47917        return;
47918      }
47919
47920      self.trigger('blur', evt);
47921    }, 1);
47922  };
47923
47924  BaseSelection.prototype._attachCloseHandler = function (container) {
47925
47926    $(document.body).on('mousedown.select2.' + container.id, function (e) {
47927      var $target = $(e.target);
47928
47929      var $select = $target.closest('.select2');
47930
47931      var $all = $('.select2.select2-container--open');
47932
47933      $all.each(function () {
47934        if (this == $select[0]) {
47935          return;
47936        }
47937
47938        var $element = Utils.GetData(this, 'element');
47939
47940        $element.select2('close');
47941      });
47942    });
47943  };
47944
47945  BaseSelection.prototype._detachCloseHandler = function (container) {
47946    $(document.body).off('mousedown.select2.' + container.id);
47947  };
47948
47949  BaseSelection.prototype.position = function ($selection, $container) {
47950    var $selectionContainer = $container.find('.selection');
47951    $selectionContainer.append($selection);
47952  };
47953
47954  BaseSelection.prototype.destroy = function () {
47955    this._detachCloseHandler(this.container);
47956  };
47957
47958  BaseSelection.prototype.update = function (data) {
47959    throw new Error('The `update` method must be defined in child classes.');
47960  };
47961
47962  /**
47963   * Helper method to abstract the "enabled" (not "disabled") state of this
47964   * object.
47965   *
47966   * @return {true} if the instance is not disabled.
47967   * @return {false} if the instance is disabled.
47968   */
47969  BaseSelection.prototype.isEnabled = function () {
47970    return !this.isDisabled();
47971  };
47972
47973  /**
47974   * Helper method to abstract the "disabled" state of this object.
47975   *
47976   * @return {true} if the disabled option is true.
47977   * @return {false} if the disabled option is false.
47978   */
47979  BaseSelection.prototype.isDisabled = function () {
47980    return this.options.get('disabled');
47981  };
47982
47983  return BaseSelection;
47984});
47985
47986S2.define('select2/selection/single',[
47987  'jquery',
47988  './base',
47989  '../utils',
47990  '../keys'
47991], function ($, BaseSelection, Utils, KEYS) {
47992  function SingleSelection () {
47993    SingleSelection.__super__.constructor.apply(this, arguments);
47994  }
47995
47996  Utils.Extend(SingleSelection, BaseSelection);
47997
47998  SingleSelection.prototype.render = function () {
47999    var $selection = SingleSelection.__super__.render.call(this);
48000
48001    $selection.addClass('select2-selection--single');
48002
48003    $selection.html(
48004      '<span class="select2-selection__rendered"></span>' +
48005      '<span class="select2-selection__arrow" role="presentation">' +
48006        '<b role="presentation"></b>' +
48007      '</span>'
48008    );
48009
48010    return $selection;
48011  };
48012
48013  SingleSelection.prototype.bind = function (container, $container) {
48014    var self = this;
48015
48016    SingleSelection.__super__.bind.apply(this, arguments);
48017
48018    var id = container.id + '-container';
48019
48020    this.$selection.find('.select2-selection__rendered')
48021      .attr('id', id)
48022      .attr('role', 'textbox')
48023      .attr('aria-readonly', 'true');
48024    this.$selection.attr('aria-labelledby', id);
48025
48026    this.$selection.on('mousedown', function (evt) {
48027      // Only respond to left clicks
48028      if (evt.which !== 1) {
48029        return;
48030      }
48031
48032      self.trigger('toggle', {
48033        originalEvent: evt
48034      });
48035    });
48036
48037    this.$selection.on('focus', function (evt) {
48038      // User focuses on the container
48039    });
48040
48041    this.$selection.on('blur', function (evt) {
48042      // User exits the container
48043    });
48044
48045    container.on('focus', function (evt) {
48046      if (!container.isOpen()) {
48047        self.$selection.trigger('focus');
48048      }
48049    });
48050  };
48051
48052  SingleSelection.prototype.clear = function () {
48053    var $rendered = this.$selection.find('.select2-selection__rendered');
48054    $rendered.empty();
48055    $rendered.removeAttr('title'); // clear tooltip on empty
48056  };
48057
48058  SingleSelection.prototype.display = function (data, container) {
48059    var template = this.options.get('templateSelection');
48060    var escapeMarkup = this.options.get('escapeMarkup');
48061
48062    return escapeMarkup(template(data, container));
48063  };
48064
48065  SingleSelection.prototype.selectionContainer = function () {
48066    return $('<span></span>');
48067  };
48068
48069  SingleSelection.prototype.update = function (data) {
48070    if (data.length === 0) {
48071      this.clear();
48072      return;
48073    }
48074
48075    var selection = data[0];
48076
48077    var $rendered = this.$selection.find('.select2-selection__rendered');
48078    var formatted = this.display(selection, $rendered);
48079
48080    $rendered.empty().append(formatted);
48081
48082    var title = selection.title || selection.text;
48083
48084    if (title) {
48085      $rendered.attr('title', title);
48086    } else {
48087      $rendered.removeAttr('title');
48088    }
48089  };
48090
48091  return SingleSelection;
48092});
48093
48094S2.define('select2/selection/multiple',[
48095  'jquery',
48096  './base',
48097  '../utils'
48098], function ($, BaseSelection, Utils) {
48099  function MultipleSelection ($element, options) {
48100    MultipleSelection.__super__.constructor.apply(this, arguments);
48101  }
48102
48103  Utils.Extend(MultipleSelection, BaseSelection);
48104
48105  MultipleSelection.prototype.render = function () {
48106    var $selection = MultipleSelection.__super__.render.call(this);
48107
48108    $selection.addClass('select2-selection--multiple');
48109
48110    $selection.html(
48111      '<ul class="select2-selection__rendered"></ul>'
48112    );
48113
48114    return $selection;
48115  };
48116
48117  MultipleSelection.prototype.bind = function (container, $container) {
48118    var self = this;
48119
48120    MultipleSelection.__super__.bind.apply(this, arguments);
48121
48122    this.$selection.on('click', function (evt) {
48123      self.trigger('toggle', {
48124        originalEvent: evt
48125      });
48126    });
48127
48128    this.$selection.on(
48129      'click',
48130      '.select2-selection__choice__remove',
48131      function (evt) {
48132        // Ignore the event if it is disabled
48133        if (self.isDisabled()) {
48134          return;
48135        }
48136
48137        var $remove = $(this);
48138        var $selection = $remove.parent();
48139
48140        var data = Utils.GetData($selection[0], 'data');
48141
48142        self.trigger('unselect', {
48143          originalEvent: evt,
48144          data: data
48145        });
48146      }
48147    );
48148  };
48149
48150  MultipleSelection.prototype.clear = function () {
48151    var $rendered = this.$selection.find('.select2-selection__rendered');
48152    $rendered.empty();
48153    $rendered.removeAttr('title');
48154  };
48155
48156  MultipleSelection.prototype.display = function (data, container) {
48157    var template = this.options.get('templateSelection');
48158    var escapeMarkup = this.options.get('escapeMarkup');
48159
48160    return escapeMarkup(template(data, container));
48161  };
48162
48163  MultipleSelection.prototype.selectionContainer = function () {
48164    var $container = $(
48165      '<li class="select2-selection__choice">' +
48166        '<span class="select2-selection__choice__remove" role="presentation">' +
48167          '&times;' +
48168        '</span>' +
48169      '</li>'
48170    );
48171
48172    return $container;
48173  };
48174
48175  MultipleSelection.prototype.update = function (data) {
48176    this.clear();
48177
48178    if (data.length === 0) {
48179      return;
48180    }
48181
48182    var $selections = [];
48183
48184    for (var d = 0; d < data.length; d++) {
48185      var selection = data[d];
48186
48187      var $selection = this.selectionContainer();
48188      var formatted = this.display(selection, $selection);
48189
48190      $selection.append(formatted);
48191
48192      var title = selection.title || selection.text;
48193
48194      if (title) {
48195        $selection.attr('title', title);
48196      }
48197
48198      Utils.StoreData($selection[0], 'data', selection);
48199
48200      $selections.push($selection);
48201    }
48202
48203    var $rendered = this.$selection.find('.select2-selection__rendered');
48204
48205    Utils.appendMany($rendered, $selections);
48206  };
48207
48208  return MultipleSelection;
48209});
48210
48211S2.define('select2/selection/placeholder',[
48212  '../utils'
48213], function (Utils) {
48214  function Placeholder (decorated, $element, options) {
48215    this.placeholder = this.normalizePlaceholder(options.get('placeholder'));
48216
48217    decorated.call(this, $element, options);
48218  }
48219
48220  Placeholder.prototype.normalizePlaceholder = function (_, placeholder) {
48221    if (typeof placeholder === 'string') {
48222      placeholder = {
48223        id: '',
48224        text: placeholder
48225      };
48226    }
48227
48228    return placeholder;
48229  };
48230
48231  Placeholder.prototype.createPlaceholder = function (decorated, placeholder) {
48232    var $placeholder = this.selectionContainer();
48233
48234    $placeholder.html(this.display(placeholder));
48235    $placeholder.addClass('select2-selection__placeholder')
48236                .removeClass('select2-selection__choice');
48237
48238    return $placeholder;
48239  };
48240
48241  Placeholder.prototype.update = function (decorated, data) {
48242    var singlePlaceholder = (
48243      data.length == 1 && data[0].id != this.placeholder.id
48244    );
48245    var multipleSelections = data.length > 1;
48246
48247    if (multipleSelections || singlePlaceholder) {
48248      return decorated.call(this, data);
48249    }
48250
48251    this.clear();
48252
48253    var $placeholder = this.createPlaceholder(this.placeholder);
48254
48255    this.$selection.find('.select2-selection__rendered').append($placeholder);
48256  };
48257
48258  return Placeholder;
48259});
48260
48261S2.define('select2/selection/allowClear',[
48262  'jquery',
48263  '../keys',
48264  '../utils'
48265], function ($, KEYS, Utils) {
48266  function AllowClear () { }
48267
48268  AllowClear.prototype.bind = function (decorated, container, $container) {
48269    var self = this;
48270
48271    decorated.call(this, container, $container);
48272
48273    if (this.placeholder == null) {
48274      if (this.options.get('debug') && window.console && console.error) {
48275        console.error(
48276          'Select2: The `allowClear` option should be used in combination ' +
48277          'with the `placeholder` option.'
48278        );
48279      }
48280    }
48281
48282    this.$selection.on('mousedown', '.select2-selection__clear',
48283      function (evt) {
48284        self._handleClear(evt);
48285    });
48286
48287    container.on('keypress', function (evt) {
48288      self._handleKeyboardClear(evt, container);
48289    });
48290  };
48291
48292  AllowClear.prototype._handleClear = function (_, evt) {
48293    // Ignore the event if it is disabled
48294    if (this.isDisabled()) {
48295      return;
48296    }
48297
48298    var $clear = this.$selection.find('.select2-selection__clear');
48299
48300    // Ignore the event if nothing has been selected
48301    if ($clear.length === 0) {
48302      return;
48303    }
48304
48305    evt.stopPropagation();
48306
48307    var data = Utils.GetData($clear[0], 'data');
48308
48309    var previousVal = this.$element.val();
48310    this.$element.val(this.placeholder.id);
48311
48312    var unselectData = {
48313      data: data
48314    };
48315    this.trigger('clear', unselectData);
48316    if (unselectData.prevented) {
48317      this.$element.val(previousVal);
48318      return;
48319    }
48320
48321    for (var d = 0; d < data.length; d++) {
48322      unselectData = {
48323        data: data[d]
48324      };
48325
48326      // Trigger the `unselect` event, so people can prevent it from being
48327      // cleared.
48328      this.trigger('unselect', unselectData);
48329
48330      // If the event was prevented, don't clear it out.
48331      if (unselectData.prevented) {
48332        this.$element.val(previousVal);
48333        return;
48334      }
48335    }
48336
48337    this.$element.trigger('input').trigger('change');
48338
48339    this.trigger('toggle', {});
48340  };
48341
48342  AllowClear.prototype._handleKeyboardClear = function (_, evt, container) {
48343    if (container.isOpen()) {
48344      return;
48345    }
48346
48347    if (evt.which == KEYS.DELETE || evt.which == KEYS.BACKSPACE) {
48348      this._handleClear(evt);
48349    }
48350  };
48351
48352  AllowClear.prototype.update = function (decorated, data) {
48353    decorated.call(this, data);
48354
48355    if (this.$selection.find('.select2-selection__placeholder').length > 0 ||
48356        data.length === 0) {
48357      return;
48358    }
48359
48360    var removeAll = this.options.get('translations').get('removeAllItems');
48361
48362    var $remove = $(
48363      '<span class="select2-selection__clear" title="' + removeAll() +'">' +
48364        '&times;' +
48365      '</span>'
48366    );
48367    Utils.StoreData($remove[0], 'data', data);
48368
48369    this.$selection.find('.select2-selection__rendered').prepend($remove);
48370  };
48371
48372  return AllowClear;
48373});
48374
48375S2.define('select2/selection/search',[
48376  'jquery',
48377  '../utils',
48378  '../keys'
48379], function ($, Utils, KEYS) {
48380  function Search (decorated, $element, options) {
48381    decorated.call(this, $element, options);
48382  }
48383
48384  Search.prototype.render = function (decorated) {
48385    var $search = $(
48386      '<li class="select2-search select2-search--inline">' +
48387        '<input class="select2-search__field" type="search" tabindex="-1"' +
48388        ' autocomplete="off" autocorrect="off" autocapitalize="none"' +
48389        ' spellcheck="false" role="searchbox" aria-autocomplete="list" />' +
48390      '</li>'
48391    );
48392
48393    this.$searchContainer = $search;
48394    this.$search = $search.find('input');
48395
48396    var $rendered = decorated.call(this);
48397
48398    this._transferTabIndex();
48399
48400    return $rendered;
48401  };
48402
48403  Search.prototype.bind = function (decorated, container, $container) {
48404    var self = this;
48405
48406    var resultsId = container.id + '-results';
48407
48408    decorated.call(this, container, $container);
48409
48410    container.on('open', function () {
48411      self.$search.attr('aria-controls', resultsId);
48412      self.$search.trigger('focus');
48413    });
48414
48415    container.on('close', function () {
48416      self.$search.val('');
48417      self.$search.removeAttr('aria-controls');
48418      self.$search.removeAttr('aria-activedescendant');
48419      self.$search.trigger('focus');
48420    });
48421
48422    container.on('enable', function () {
48423      self.$search.prop('disabled', false);
48424
48425      self._transferTabIndex();
48426    });
48427
48428    container.on('disable', function () {
48429      self.$search.prop('disabled', true);
48430    });
48431
48432    container.on('focus', function (evt) {
48433      self.$search.trigger('focus');
48434    });
48435
48436    container.on('results:focus', function (params) {
48437      if (params.data._resultId) {
48438        self.$search.attr('aria-activedescendant', params.data._resultId);
48439      } else {
48440        self.$search.removeAttr('aria-activedescendant');
48441      }
48442    });
48443
48444    this.$selection.on('focusin', '.select2-search--inline', function (evt) {
48445      self.trigger('focus', evt);
48446    });
48447
48448    this.$selection.on('focusout', '.select2-search--inline', function (evt) {
48449      self._handleBlur(evt);
48450    });
48451
48452    this.$selection.on('keydown', '.select2-search--inline', function (evt) {
48453      evt.stopPropagation();
48454
48455      self.trigger('keypress', evt);
48456
48457      self._keyUpPrevented = evt.isDefaultPrevented();
48458
48459      var key = evt.which;
48460
48461      if (key === KEYS.BACKSPACE && self.$search.val() === '') {
48462        var $previousChoice = self.$searchContainer
48463          .prev('.select2-selection__choice');
48464
48465        if ($previousChoice.length > 0) {
48466          var item = Utils.GetData($previousChoice[0], 'data');
48467
48468          self.searchRemoveChoice(item);
48469
48470          evt.preventDefault();
48471        }
48472      }
48473    });
48474
48475    this.$selection.on('click', '.select2-search--inline', function (evt) {
48476      if (self.$search.val()) {
48477        evt.stopPropagation();
48478      }
48479    });
48480
48481    // Try to detect the IE version should the `documentMode` property that
48482    // is stored on the document. This is only implemented in IE and is
48483    // slightly cleaner than doing a user agent check.
48484    // This property is not available in Edge, but Edge also doesn't have
48485    // this bug.
48486    var msie = document.documentMode;
48487    var disableInputEvents = msie && msie <= 11;
48488
48489    // Workaround for browsers which do not support the `input` event
48490    // This will prevent double-triggering of events for browsers which support
48491    // both the `keyup` and `input` events.
48492    this.$selection.on(
48493      'input.searchcheck',
48494      '.select2-search--inline',
48495      function (evt) {
48496        // IE will trigger the `input` event when a placeholder is used on a
48497        // search box. To get around this issue, we are forced to ignore all
48498        // `input` events in IE and keep using `keyup`.
48499        if (disableInputEvents) {
48500          self.$selection.off('input.search input.searchcheck');
48501          return;
48502        }
48503
48504        // Unbind the duplicated `keyup` event
48505        self.$selection.off('keyup.search');
48506      }
48507    );
48508
48509    this.$selection.on(
48510      'keyup.search input.search',
48511      '.select2-search--inline',
48512      function (evt) {
48513        // IE will trigger the `input` event when a placeholder is used on a
48514        // search box. To get around this issue, we are forced to ignore all
48515        // `input` events in IE and keep using `keyup`.
48516        if (disableInputEvents && evt.type === 'input') {
48517          self.$selection.off('input.search input.searchcheck');
48518          return;
48519        }
48520
48521        var key = evt.which;
48522
48523        // We can freely ignore events from modifier keys
48524        if (key == KEYS.SHIFT || key == KEYS.CTRL || key == KEYS.ALT) {
48525          return;
48526        }
48527
48528        // Tabbing will be handled during the `keydown` phase
48529        if (key == KEYS.TAB) {
48530          return;
48531        }
48532
48533        self.handleSearch(evt);
48534      }
48535    );
48536  };
48537
48538  /**
48539   * This method will transfer the tabindex attribute from the rendered
48540   * selection to the search box. This allows for the search box to be used as
48541   * the primary focus instead of the selection container.
48542   *
48543   * @private
48544   */
48545  Search.prototype._transferTabIndex = function (decorated) {
48546    this.$search.attr('tabindex', this.$selection.attr('tabindex'));
48547    this.$selection.attr('tabindex', '-1');
48548  };
48549
48550  Search.prototype.createPlaceholder = function (decorated, placeholder) {
48551    this.$search.attr('placeholder', placeholder.text);
48552  };
48553
48554  Search.prototype.update = function (decorated, data) {
48555    var searchHadFocus = this.$search[0] == document.activeElement;
48556
48557    this.$search.attr('placeholder', '');
48558
48559    decorated.call(this, data);
48560
48561    this.$selection.find('.select2-selection__rendered')
48562                   .append(this.$searchContainer);
48563
48564    this.resizeSearch();
48565    if (searchHadFocus) {
48566      this.$search.trigger('focus');
48567    }
48568  };
48569
48570  Search.prototype.handleSearch = function () {
48571    this.resizeSearch();
48572
48573    if (!this._keyUpPrevented) {
48574      var input = this.$search.val();
48575
48576      this.trigger('query', {
48577        term: input
48578      });
48579    }
48580
48581    this._keyUpPrevented = false;
48582  };
48583
48584  Search.prototype.searchRemoveChoice = function (decorated, item) {
48585    this.trigger('unselect', {
48586      data: item
48587    });
48588
48589    this.$search.val(item.text);
48590    this.handleSearch();
48591  };
48592
48593  Search.prototype.resizeSearch = function () {
48594    this.$search.css('width', '25px');
48595
48596    var width = '';
48597
48598    if (this.$search.attr('placeholder') !== '') {
48599      width = this.$selection.find('.select2-selection__rendered').width();
48600    } else {
48601      var minimumWidth = this.$search.val().length + 1;
48602
48603      width = (minimumWidth * 0.75) + 'em';
48604    }
48605
48606    this.$search.css('width', width);
48607  };
48608
48609  return Search;
48610});
48611
48612S2.define('select2/selection/eventRelay',[
48613  'jquery'
48614], function ($) {
48615  function EventRelay () { }
48616
48617  EventRelay.prototype.bind = function (decorated, container, $container) {
48618    var self = this;
48619    var relayEvents = [
48620      'open', 'opening',
48621      'close', 'closing',
48622      'select', 'selecting',
48623      'unselect', 'unselecting',
48624      'clear', 'clearing'
48625    ];
48626
48627    var preventableEvents = [
48628      'opening', 'closing', 'selecting', 'unselecting', 'clearing'
48629    ];
48630
48631    decorated.call(this, container, $container);
48632
48633    container.on('*', function (name, params) {
48634      // Ignore events that should not be relayed
48635      if ($.inArray(name, relayEvents) === -1) {
48636        return;
48637      }
48638
48639      // The parameters should always be an object
48640      params = params || {};
48641
48642      // Generate the jQuery event for the Select2 event
48643      var evt = $.Event('select2:' + name, {
48644        params: params
48645      });
48646
48647      self.$element.trigger(evt);
48648
48649      // Only handle preventable events if it was one
48650      if ($.inArray(name, preventableEvents) === -1) {
48651        return;
48652      }
48653
48654      params.prevented = evt.isDefaultPrevented();
48655    });
48656  };
48657
48658  return EventRelay;
48659});
48660
48661S2.define('select2/translation',[
48662  'jquery',
48663  'require'
48664], function ($, require) {
48665  function Translation (dict) {
48666    this.dict = dict || {};
48667  }
48668
48669  Translation.prototype.all = function () {
48670    return this.dict;
48671  };
48672
48673  Translation.prototype.get = function (key) {
48674    return this.dict[key];
48675  };
48676
48677  Translation.prototype.extend = function (translation) {
48678    this.dict = $.extend({}, translation.all(), this.dict);
48679  };
48680
48681  // Static functions
48682
48683  Translation._cache = {};
48684
48685  Translation.loadPath = function (path) {
48686    if (!(path in Translation._cache)) {
48687      var translations = require(path);
48688
48689      Translation._cache[path] = translations;
48690    }
48691
48692    return new Translation(Translation._cache[path]);
48693  };
48694
48695  return Translation;
48696});
48697
48698S2.define('select2/diacritics',[
48699
48700], function () {
48701  var diacritics = {
48702    '\u24B6': 'A',
48703    '\uFF21': 'A',
48704    '\u00C0': 'A',
48705    '\u00C1': 'A',
48706    '\u00C2': 'A',
48707    '\u1EA6': 'A',
48708    '\u1EA4': 'A',
48709    '\u1EAA': 'A',
48710    '\u1EA8': 'A',
48711    '\u00C3': 'A',
48712    '\u0100': 'A',
48713    '\u0102': 'A',
48714    '\u1EB0': 'A',
48715    '\u1EAE': 'A',
48716    '\u1EB4': 'A',
48717    '\u1EB2': 'A',
48718    '\u0226': 'A',
48719    '\u01E0': 'A',
48720    '\u00C4': 'A',
48721    '\u01DE': 'A',
48722    '\u1EA2': 'A',
48723    '\u00C5': 'A',
48724    '\u01FA': 'A',
48725    '\u01CD': 'A',
48726    '\u0200': 'A',
48727    '\u0202': 'A',
48728    '\u1EA0': 'A',
48729    '\u1EAC': 'A',
48730    '\u1EB6': 'A',
48731    '\u1E00': 'A',
48732    '\u0104': 'A',
48733    '\u023A': 'A',
48734    '\u2C6F': 'A',
48735    '\uA732': 'AA',
48736    '\u00C6': 'AE',
48737    '\u01FC': 'AE',
48738    '\u01E2': 'AE',
48739    '\uA734': 'AO',
48740    '\uA736': 'AU',
48741    '\uA738': 'AV',
48742    '\uA73A': 'AV',
48743    '\uA73C': 'AY',
48744    '\u24B7': 'B',
48745    '\uFF22': 'B',
48746    '\u1E02': 'B',
48747    '\u1E04': 'B',
48748    '\u1E06': 'B',
48749    '\u0243': 'B',
48750    '\u0182': 'B',
48751    '\u0181': 'B',
48752    '\u24B8': 'C',
48753    '\uFF23': 'C',
48754    '\u0106': 'C',
48755    '\u0108': 'C',
48756    '\u010A': 'C',
48757    '\u010C': 'C',
48758    '\u00C7': 'C',
48759    '\u1E08': 'C',
48760    '\u0187': 'C',
48761    '\u023B': 'C',
48762    '\uA73E': 'C',
48763    '\u24B9': 'D',
48764    '\uFF24': 'D',
48765    '\u1E0A': 'D',
48766    '\u010E': 'D',
48767    '\u1E0C': 'D',
48768    '\u1E10': 'D',
48769    '\u1E12': 'D',
48770    '\u1E0E': 'D',
48771    '\u0110': 'D',
48772    '\u018B': 'D',
48773    '\u018A': 'D',
48774    '\u0189': 'D',
48775    '\uA779': 'D',
48776    '\u01F1': 'DZ',
48777    '\u01C4': 'DZ',
48778    '\u01F2': 'Dz',
48779    '\u01C5': 'Dz',
48780    '\u24BA': 'E',
48781    '\uFF25': 'E',
48782    '\u00C8': 'E',
48783    '\u00C9': 'E',
48784    '\u00CA': 'E',
48785    '\u1EC0': 'E',
48786    '\u1EBE': 'E',
48787    '\u1EC4': 'E',
48788    '\u1EC2': 'E',
48789    '\u1EBC': 'E',
48790    '\u0112': 'E',
48791    '\u1E14': 'E',
48792    '\u1E16': 'E',
48793    '\u0114': 'E',
48794    '\u0116': 'E',
48795    '\u00CB': 'E',
48796    '\u1EBA': 'E',
48797    '\u011A': 'E',
48798    '\u0204': 'E',
48799    '\u0206': 'E',
48800    '\u1EB8': 'E',
48801    '\u1EC6': 'E',
48802    '\u0228': 'E',
48803    '\u1E1C': 'E',
48804    '\u0118': 'E',
48805    '\u1E18': 'E',
48806    '\u1E1A': 'E',
48807    '\u0190': 'E',
48808    '\u018E': 'E',
48809    '\u24BB': 'F',
48810    '\uFF26': 'F',
48811    '\u1E1E': 'F',
48812    '\u0191': 'F',
48813    '\uA77B': 'F',
48814    '\u24BC': 'G',
48815    '\uFF27': 'G',
48816    '\u01F4': 'G',
48817    '\u011C': 'G',
48818    '\u1E20': 'G',
48819    '\u011E': 'G',
48820    '\u0120': 'G',
48821    '\u01E6': 'G',
48822    '\u0122': 'G',
48823    '\u01E4': 'G',
48824    '\u0193': 'G',
48825    '\uA7A0': 'G',
48826    '\uA77D': 'G',
48827    '\uA77E': 'G',
48828    '\u24BD': 'H',
48829    '\uFF28': 'H',
48830    '\u0124': 'H',
48831    '\u1E22': 'H',
48832    '\u1E26': 'H',
48833    '\u021E': 'H',
48834    '\u1E24': 'H',
48835    '\u1E28': 'H',
48836    '\u1E2A': 'H',
48837    '\u0126': 'H',
48838    '\u2C67': 'H',
48839    '\u2C75': 'H',
48840    '\uA78D': 'H',
48841    '\u24BE': 'I',
48842    '\uFF29': 'I',
48843    '\u00CC': 'I',
48844    '\u00CD': 'I',
48845    '\u00CE': 'I',
48846    '\u0128': 'I',
48847    '\u012A': 'I',
48848    '\u012C': 'I',
48849    '\u0130': 'I',
48850    '\u00CF': 'I',
48851    '\u1E2E': 'I',
48852    '\u1EC8': 'I',
48853    '\u01CF': 'I',
48854    '\u0208': 'I',
48855    '\u020A': 'I',
48856    '\u1ECA': 'I',
48857    '\u012E': 'I',
48858    '\u1E2C': 'I',
48859    '\u0197': 'I',
48860    '\u24BF': 'J',
48861    '\uFF2A': 'J',
48862    '\u0134': 'J',
48863    '\u0248': 'J',
48864    '\u24C0': 'K',
48865    '\uFF2B': 'K',
48866    '\u1E30': 'K',
48867    '\u01E8': 'K',
48868    '\u1E32': 'K',
48869    '\u0136': 'K',
48870    '\u1E34': 'K',
48871    '\u0198': 'K',
48872    '\u2C69': 'K',
48873    '\uA740': 'K',
48874    '\uA742': 'K',
48875    '\uA744': 'K',
48876    '\uA7A2': 'K',
48877    '\u24C1': 'L',
48878    '\uFF2C': 'L',
48879    '\u013F': 'L',
48880    '\u0139': 'L',
48881    '\u013D': 'L',
48882    '\u1E36': 'L',
48883    '\u1E38': 'L',
48884    '\u013B': 'L',
48885    '\u1E3C': 'L',
48886    '\u1E3A': 'L',
48887    '\u0141': 'L',
48888    '\u023D': 'L',
48889    '\u2C62': 'L',
48890    '\u2C60': 'L',
48891    '\uA748': 'L',
48892    '\uA746': 'L',
48893    '\uA780': 'L',
48894    '\u01C7': 'LJ',
48895    '\u01C8': 'Lj',
48896    '\u24C2': 'M',
48897    '\uFF2D': 'M',
48898    '\u1E3E': 'M',
48899    '\u1E40': 'M',
48900    '\u1E42': 'M',
48901    '\u2C6E': 'M',
48902    '\u019C': 'M',
48903    '\u24C3': 'N',
48904    '\uFF2E': 'N',
48905    '\u01F8': 'N',
48906    '\u0143': 'N',
48907    '\u00D1': 'N',
48908    '\u1E44': 'N',
48909    '\u0147': 'N',
48910    '\u1E46': 'N',
48911    '\u0145': 'N',
48912    '\u1E4A': 'N',
48913    '\u1E48': 'N',
48914    '\u0220': 'N',
48915    '\u019D': 'N',
48916    '\uA790': 'N',
48917    '\uA7A4': 'N',
48918    '\u01CA': 'NJ',
48919    '\u01CB': 'Nj',
48920    '\u24C4': 'O',
48921    '\uFF2F': 'O',
48922    '\u00D2': 'O',
48923    '\u00D3': 'O',
48924    '\u00D4': 'O',
48925    '\u1ED2': 'O',
48926    '\u1ED0': 'O',
48927    '\u1ED6': 'O',
48928    '\u1ED4': 'O',
48929    '\u00D5': 'O',
48930    '\u1E4C': 'O',
48931    '\u022C': 'O',
48932    '\u1E4E': 'O',
48933    '\u014C': 'O',
48934    '\u1E50': 'O',
48935    '\u1E52': 'O',
48936    '\u014E': 'O',
48937    '\u022E': 'O',
48938    '\u0230': 'O',
48939    '\u00D6': 'O',
48940    '\u022A': 'O',
48941    '\u1ECE': 'O',
48942    '\u0150': 'O',
48943    '\u01D1': 'O',
48944    '\u020C': 'O',
48945    '\u020E': 'O',
48946    '\u01A0': 'O',
48947    '\u1EDC': 'O',
48948    '\u1EDA': 'O',
48949    '\u1EE0': 'O',
48950    '\u1EDE': 'O',
48951    '\u1EE2': 'O',
48952    '\u1ECC': 'O',
48953    '\u1ED8': 'O',
48954    '\u01EA': 'O',
48955    '\u01EC': 'O',
48956    '\u00D8': 'O',
48957    '\u01FE': 'O',
48958    '\u0186': 'O',
48959    '\u019F': 'O',
48960    '\uA74A': 'O',
48961    '\uA74C': 'O',
48962    '\u0152': 'OE',
48963    '\u01A2': 'OI',
48964    '\uA74E': 'OO',
48965    '\u0222': 'OU',
48966    '\u24C5': 'P',
48967    '\uFF30': 'P',
48968    '\u1E54': 'P',
48969    '\u1E56': 'P',
48970    '\u01A4': 'P',
48971    '\u2C63': 'P',
48972    '\uA750': 'P',
48973    '\uA752': 'P',
48974    '\uA754': 'P',
48975    '\u24C6': 'Q',
48976    '\uFF31': 'Q',
48977    '\uA756': 'Q',
48978    '\uA758': 'Q',
48979    '\u024A': 'Q',
48980    '\u24C7': 'R',
48981    '\uFF32': 'R',
48982    '\u0154': 'R',
48983    '\u1E58': 'R',
48984    '\u0158': 'R',
48985    '\u0210': 'R',
48986    '\u0212': 'R',
48987    '\u1E5A': 'R',
48988    '\u1E5C': 'R',
48989    '\u0156': 'R',
48990    '\u1E5E': 'R',
48991    '\u024C': 'R',
48992    '\u2C64': 'R',
48993    '\uA75A': 'R',
48994    '\uA7A6': 'R',
48995    '\uA782': 'R',
48996    '\u24C8': 'S',
48997    '\uFF33': 'S',
48998    '\u1E9E': 'S',
48999    '\u015A': 'S',
49000    '\u1E64': 'S',
49001    '\u015C': 'S',
49002    '\u1E60': 'S',
49003    '\u0160': 'S',
49004    '\u1E66': 'S',
49005    '\u1E62': 'S',
49006    '\u1E68': 'S',
49007    '\u0218': 'S',
49008    '\u015E': 'S',
49009    '\u2C7E': 'S',
49010    '\uA7A8': 'S',
49011    '\uA784': 'S',
49012    '\u24C9': 'T',
49013    '\uFF34': 'T',
49014    '\u1E6A': 'T',
49015    '\u0164': 'T',
49016    '\u1E6C': 'T',
49017    '\u021A': 'T',
49018    '\u0162': 'T',
49019    '\u1E70': 'T',
49020    '\u1E6E': 'T',
49021    '\u0166': 'T',
49022    '\u01AC': 'T',
49023    '\u01AE': 'T',
49024    '\u023E': 'T',
49025    '\uA786': 'T',
49026    '\uA728': 'TZ',
49027    '\u24CA': 'U',
49028    '\uFF35': 'U',
49029    '\u00D9': 'U',
49030    '\u00DA': 'U',
49031    '\u00DB': 'U',
49032    '\u0168': 'U',
49033    '\u1E78': 'U',
49034    '\u016A': 'U',
49035    '\u1E7A': 'U',
49036    '\u016C': 'U',
49037    '\u00DC': 'U',
49038    '\u01DB': 'U',
49039    '\u01D7': 'U',
49040    '\u01D5': 'U',
49041    '\u01D9': 'U',
49042    '\u1EE6': 'U',
49043    '\u016E': 'U',
49044    '\u0170': 'U',
49045    '\u01D3': 'U',
49046    '\u0214': 'U',
49047    '\u0216': 'U',
49048    '\u01AF': 'U',
49049    '\u1EEA': 'U',
49050    '\u1EE8': 'U',
49051    '\u1EEE': 'U',
49052    '\u1EEC': 'U',
49053    '\u1EF0': 'U',
49054    '\u1EE4': 'U',
49055    '\u1E72': 'U',
49056    '\u0172': 'U',
49057    '\u1E76': 'U',
49058    '\u1E74': 'U',
49059    '\u0244': 'U',
49060    '\u24CB': 'V',
49061    '\uFF36': 'V',
49062    '\u1E7C': 'V',
49063    '\u1E7E': 'V',
49064    '\u01B2': 'V',
49065    '\uA75E': 'V',
49066    '\u0245': 'V',
49067    '\uA760': 'VY',
49068    '\u24CC': 'W',
49069    '\uFF37': 'W',
49070    '\u1E80': 'W',
49071    '\u1E82': 'W',
49072    '\u0174': 'W',
49073    '\u1E86': 'W',
49074    '\u1E84': 'W',
49075    '\u1E88': 'W',
49076    '\u2C72': 'W',
49077    '\u24CD': 'X',
49078    '\uFF38': 'X',
49079    '\u1E8A': 'X',
49080    '\u1E8C': 'X',
49081    '\u24CE': 'Y',
49082    '\uFF39': 'Y',
49083    '\u1EF2': 'Y',
49084    '\u00DD': 'Y',
49085    '\u0176': 'Y',
49086    '\u1EF8': 'Y',
49087    '\u0232': 'Y',
49088    '\u1E8E': 'Y',
49089    '\u0178': 'Y',
49090    '\u1EF6': 'Y',
49091    '\u1EF4': 'Y',
49092    '\u01B3': 'Y',
49093    '\u024E': 'Y',
49094    '\u1EFE': 'Y',
49095    '\u24CF': 'Z',
49096    '\uFF3A': 'Z',
49097    '\u0179': 'Z',
49098    '\u1E90': 'Z',
49099    '\u017B': 'Z',
49100    '\u017D': 'Z',
49101    '\u1E92': 'Z',
49102    '\u1E94': 'Z',
49103    '\u01B5': 'Z',
49104    '\u0224': 'Z',
49105    '\u2C7F': 'Z',
49106    '\u2C6B': 'Z',
49107    '\uA762': 'Z',
49108    '\u24D0': 'a',
49109    '\uFF41': 'a',
49110    '\u1E9A': 'a',
49111    '\u00E0': 'a',
49112    '\u00E1': 'a',
49113    '\u00E2': 'a',
49114    '\u1EA7': 'a',
49115    '\u1EA5': 'a',
49116    '\u1EAB': 'a',
49117    '\u1EA9': 'a',
49118    '\u00E3': 'a',
49119    '\u0101': 'a',
49120    '\u0103': 'a',
49121    '\u1EB1': 'a',
49122    '\u1EAF': 'a',
49123    '\u1EB5': 'a',
49124    '\u1EB3': 'a',
49125    '\u0227': 'a',
49126    '\u01E1': 'a',
49127    '\u00E4': 'a',
49128    '\u01DF': 'a',
49129    '\u1EA3': 'a',
49130    '\u00E5': 'a',
49131    '\u01FB': 'a',
49132    '\u01CE': 'a',
49133    '\u0201': 'a',
49134    '\u0203': 'a',
49135    '\u1EA1': 'a',
49136    '\u1EAD': 'a',
49137    '\u1EB7': 'a',
49138    '\u1E01': 'a',
49139    '\u0105': 'a',
49140    '\u2C65': 'a',
49141    '\u0250': 'a',
49142    '\uA733': 'aa',
49143    '\u00E6': 'ae',
49144    '\u01FD': 'ae',
49145    '\u01E3': 'ae',
49146    '\uA735': 'ao',
49147    '\uA737': 'au',
49148    '\uA739': 'av',
49149    '\uA73B': 'av',
49150    '\uA73D': 'ay',
49151    '\u24D1': 'b',
49152    '\uFF42': 'b',
49153    '\u1E03': 'b',
49154    '\u1E05': 'b',
49155    '\u1E07': 'b',
49156    '\u0180': 'b',
49157    '\u0183': 'b',
49158    '\u0253': 'b',
49159    '\u24D2': 'c',
49160    '\uFF43': 'c',
49161    '\u0107': 'c',
49162    '\u0109': 'c',
49163    '\u010B': 'c',
49164    '\u010D': 'c',
49165    '\u00E7': 'c',
49166    '\u1E09': 'c',
49167    '\u0188': 'c',
49168    '\u023C': 'c',
49169    '\uA73F': 'c',
49170    '\u2184': 'c',
49171    '\u24D3': 'd',
49172    '\uFF44': 'd',
49173    '\u1E0B': 'd',
49174    '\u010F': 'd',
49175    '\u1E0D': 'd',
49176    '\u1E11': 'd',
49177    '\u1E13': 'd',
49178    '\u1E0F': 'd',
49179    '\u0111': 'd',
49180    '\u018C': 'd',
49181    '\u0256': 'd',
49182    '\u0257': 'd',
49183    '\uA77A': 'd',
49184    '\u01F3': 'dz',
49185    '\u01C6': 'dz',
49186    '\u24D4': 'e',
49187    '\uFF45': 'e',
49188    '\u00E8': 'e',
49189    '\u00E9': 'e',
49190    '\u00EA': 'e',
49191    '\u1EC1': 'e',
49192    '\u1EBF': 'e',
49193    '\u1EC5': 'e',
49194    '\u1EC3': 'e',
49195    '\u1EBD': 'e',
49196    '\u0113': 'e',
49197    '\u1E15': 'e',
49198    '\u1E17': 'e',
49199    '\u0115': 'e',
49200    '\u0117': 'e',
49201    '\u00EB': 'e',
49202    '\u1EBB': 'e',
49203    '\u011B': 'e',
49204    '\u0205': 'e',
49205    '\u0207': 'e',
49206    '\u1EB9': 'e',
49207    '\u1EC7': 'e',
49208    '\u0229': 'e',
49209    '\u1E1D': 'e',
49210    '\u0119': 'e',
49211    '\u1E19': 'e',
49212    '\u1E1B': 'e',
49213    '\u0247': 'e',
49214    '\u025B': 'e',
49215    '\u01DD': 'e',
49216    '\u24D5': 'f',
49217    '\uFF46': 'f',
49218    '\u1E1F': 'f',
49219    '\u0192': 'f',
49220    '\uA77C': 'f',
49221    '\u24D6': 'g',
49222    '\uFF47': 'g',
49223    '\u01F5': 'g',
49224    '\u011D': 'g',
49225    '\u1E21': 'g',
49226    '\u011F': 'g',
49227    '\u0121': 'g',
49228    '\u01E7': 'g',
49229    '\u0123': 'g',
49230    '\u01E5': 'g',
49231    '\u0260': 'g',
49232    '\uA7A1': 'g',
49233    '\u1D79': 'g',
49234    '\uA77F': 'g',
49235    '\u24D7': 'h',
49236    '\uFF48': 'h',
49237    '\u0125': 'h',
49238    '\u1E23': 'h',
49239    '\u1E27': 'h',
49240    '\u021F': 'h',
49241    '\u1E25': 'h',
49242    '\u1E29': 'h',
49243    '\u1E2B': 'h',
49244    '\u1E96': 'h',
49245    '\u0127': 'h',
49246    '\u2C68': 'h',
49247    '\u2C76': 'h',
49248    '\u0265': 'h',
49249    '\u0195': 'hv',
49250    '\u24D8': 'i',
49251    '\uFF49': 'i',
49252    '\u00EC': 'i',
49253    '\u00ED': 'i',
49254    '\u00EE': 'i',
49255    '\u0129': 'i',
49256    '\u012B': 'i',
49257    '\u012D': 'i',
49258    '\u00EF': 'i',
49259    '\u1E2F': 'i',
49260    '\u1EC9': 'i',
49261    '\u01D0': 'i',
49262    '\u0209': 'i',
49263    '\u020B': 'i',
49264    '\u1ECB': 'i',
49265    '\u012F': 'i',
49266    '\u1E2D': 'i',
49267    '\u0268': 'i',
49268    '\u0131': 'i',
49269    '\u24D9': 'j',
49270    '\uFF4A': 'j',
49271    '\u0135': 'j',
49272    '\u01F0': 'j',
49273    '\u0249': 'j',
49274    '\u24DA': 'k',
49275    '\uFF4B': 'k',
49276    '\u1E31': 'k',
49277    '\u01E9': 'k',
49278    '\u1E33': 'k',
49279    '\u0137': 'k',
49280    '\u1E35': 'k',
49281    '\u0199': 'k',
49282    '\u2C6A': 'k',
49283    '\uA741': 'k',
49284    '\uA743': 'k',
49285    '\uA745': 'k',
49286    '\uA7A3': 'k',
49287    '\u24DB': 'l',
49288    '\uFF4C': 'l',
49289    '\u0140': 'l',
49290    '\u013A': 'l',
49291    '\u013E': 'l',
49292    '\u1E37': 'l',
49293    '\u1E39': 'l',
49294    '\u013C': 'l',
49295    '\u1E3D': 'l',
49296    '\u1E3B': 'l',
49297    '\u017F': 'l',
49298    '\u0142': 'l',
49299    '\u019A': 'l',
49300    '\u026B': 'l',
49301    '\u2C61': 'l',
49302    '\uA749': 'l',
49303    '\uA781': 'l',
49304    '\uA747': 'l',
49305    '\u01C9': 'lj',
49306    '\u24DC': 'm',
49307    '\uFF4D': 'm',
49308    '\u1E3F': 'm',
49309    '\u1E41': 'm',
49310    '\u1E43': 'm',
49311    '\u0271': 'm',
49312    '\u026F': 'm',
49313    '\u24DD': 'n',
49314    '\uFF4E': 'n',
49315    '\u01F9': 'n',
49316    '\u0144': 'n',
49317    '\u00F1': 'n',
49318    '\u1E45': 'n',
49319    '\u0148': 'n',
49320    '\u1E47': 'n',
49321    '\u0146': 'n',
49322    '\u1E4B': 'n',
49323    '\u1E49': 'n',
49324    '\u019E': 'n',
49325    '\u0272': 'n',
49326    '\u0149': 'n',
49327    '\uA791': 'n',
49328    '\uA7A5': 'n',
49329    '\u01CC': 'nj',
49330    '\u24DE': 'o',
49331    '\uFF4F': 'o',
49332    '\u00F2': 'o',
49333    '\u00F3': 'o',
49334    '\u00F4': 'o',
49335    '\u1ED3': 'o',
49336    '\u1ED1': 'o',
49337    '\u1ED7': 'o',
49338    '\u1ED5': 'o',
49339    '\u00F5': 'o',
49340    '\u1E4D': 'o',
49341    '\u022D': 'o',
49342    '\u1E4F': 'o',
49343    '\u014D': 'o',
49344    '\u1E51': 'o',
49345    '\u1E53': 'o',
49346    '\u014F': 'o',
49347    '\u022F': 'o',
49348    '\u0231': 'o',
49349    '\u00F6': 'o',
49350    '\u022B': 'o',
49351    '\u1ECF': 'o',
49352    '\u0151': 'o',
49353    '\u01D2': 'o',
49354    '\u020D': 'o',
49355    '\u020F': 'o',
49356    '\u01A1': 'o',
49357    '\u1EDD': 'o',
49358    '\u1EDB': 'o',
49359    '\u1EE1': 'o',
49360    '\u1EDF': 'o',
49361    '\u1EE3': 'o',
49362    '\u1ECD': 'o',
49363    '\u1ED9': 'o',
49364    '\u01EB': 'o',
49365    '\u01ED': 'o',
49366    '\u00F8': 'o',
49367    '\u01FF': 'o',
49368    '\u0254': 'o',
49369    '\uA74B': 'o',
49370    '\uA74D': 'o',
49371    '\u0275': 'o',
49372    '\u0153': 'oe',
49373    '\u01A3': 'oi',
49374    '\u0223': 'ou',
49375    '\uA74F': 'oo',
49376    '\u24DF': 'p',
49377    '\uFF50': 'p',
49378    '\u1E55': 'p',
49379    '\u1E57': 'p',
49380    '\u01A5': 'p',
49381    '\u1D7D': 'p',
49382    '\uA751': 'p',
49383    '\uA753': 'p',
49384    '\uA755': 'p',
49385    '\u24E0': 'q',
49386    '\uFF51': 'q',
49387    '\u024B': 'q',
49388    '\uA757': 'q',
49389    '\uA759': 'q',
49390    '\u24E1': 'r',
49391    '\uFF52': 'r',
49392    '\u0155': 'r',
49393    '\u1E59': 'r',
49394    '\u0159': 'r',
49395    '\u0211': 'r',
49396    '\u0213': 'r',
49397    '\u1E5B': 'r',
49398    '\u1E5D': 'r',
49399    '\u0157': 'r',
49400    '\u1E5F': 'r',
49401    '\u024D': 'r',
49402    '\u027D': 'r',
49403    '\uA75B': 'r',
49404    '\uA7A7': 'r',
49405    '\uA783': 'r',
49406    '\u24E2': 's',
49407    '\uFF53': 's',
49408    '\u00DF': 's',
49409    '\u015B': 's',
49410    '\u1E65': 's',
49411    '\u015D': 's',
49412    '\u1E61': 's',
49413    '\u0161': 's',
49414    '\u1E67': 's',
49415    '\u1E63': 's',
49416    '\u1E69': 's',
49417    '\u0219': 's',
49418    '\u015F': 's',
49419    '\u023F': 's',
49420    '\uA7A9': 's',
49421    '\uA785': 's',
49422    '\u1E9B': 's',
49423    '\u24E3': 't',
49424    '\uFF54': 't',
49425    '\u1E6B': 't',
49426    '\u1E97': 't',
49427    '\u0165': 't',
49428    '\u1E6D': 't',
49429    '\u021B': 't',
49430    '\u0163': 't',
49431    '\u1E71': 't',
49432    '\u1E6F': 't',
49433    '\u0167': 't',
49434    '\u01AD': 't',
49435    '\u0288': 't',
49436    '\u2C66': 't',
49437    '\uA787': 't',
49438    '\uA729': 'tz',
49439    '\u24E4': 'u',
49440    '\uFF55': 'u',
49441    '\u00F9': 'u',
49442    '\u00FA': 'u',
49443    '\u00FB': 'u',
49444    '\u0169': 'u',
49445    '\u1E79': 'u',
49446    '\u016B': 'u',
49447    '\u1E7B': 'u',
49448    '\u016D': 'u',
49449    '\u00FC': 'u',
49450    '\u01DC': 'u',
49451    '\u01D8': 'u',
49452    '\u01D6': 'u',
49453    '\u01DA': 'u',
49454    '\u1EE7': 'u',
49455    '\u016F': 'u',
49456    '\u0171': 'u',
49457    '\u01D4': 'u',
49458    '\u0215': 'u',
49459    '\u0217': 'u',
49460    '\u01B0': 'u',
49461    '\u1EEB': 'u',
49462    '\u1EE9': 'u',
49463    '\u1EEF': 'u',
49464    '\u1EED': 'u',
49465    '\u1EF1': 'u',
49466    '\u1EE5': 'u',
49467    '\u1E73': 'u',
49468    '\u0173': 'u',
49469    '\u1E77': 'u',
49470    '\u1E75': 'u',
49471    '\u0289': 'u',
49472    '\u24E5': 'v',
49473    '\uFF56': 'v',
49474    '\u1E7D': 'v',
49475    '\u1E7F': 'v',
49476    '\u028B': 'v',
49477    '\uA75F': 'v',
49478    '\u028C': 'v',
49479    '\uA761': 'vy',
49480    '\u24E6': 'w',
49481    '\uFF57': 'w',
49482    '\u1E81': 'w',
49483    '\u1E83': 'w',
49484    '\u0175': 'w',
49485    '\u1E87': 'w',
49486    '\u1E85': 'w',
49487    '\u1E98': 'w',
49488    '\u1E89': 'w',
49489    '\u2C73': 'w',
49490    '\u24E7': 'x',
49491    '\uFF58': 'x',
49492    '\u1E8B': 'x',
49493    '\u1E8D': 'x',
49494    '\u24E8': 'y',
49495    '\uFF59': 'y',
49496    '\u1EF3': 'y',
49497    '\u00FD': 'y',
49498    '\u0177': 'y',
49499    '\u1EF9': 'y',
49500    '\u0233': 'y',
49501    '\u1E8F': 'y',
49502    '\u00FF': 'y',
49503    '\u1EF7': 'y',
49504    '\u1E99': 'y',
49505    '\u1EF5': 'y',
49506    '\u01B4': 'y',
49507    '\u024F': 'y',
49508    '\u1EFF': 'y',
49509    '\u24E9': 'z',
49510    '\uFF5A': 'z',
49511    '\u017A': 'z',
49512    '\u1E91': 'z',
49513    '\u017C': 'z',
49514    '\u017E': 'z',
49515    '\u1E93': 'z',
49516    '\u1E95': 'z',
49517    '\u01B6': 'z',
49518    '\u0225': 'z',
49519    '\u0240': 'z',
49520    '\u2C6C': 'z',
49521    '\uA763': 'z',
49522    '\u0386': '\u0391',
49523    '\u0388': '\u0395',
49524    '\u0389': '\u0397',
49525    '\u038A': '\u0399',
49526    '\u03AA': '\u0399',
49527    '\u038C': '\u039F',
49528    '\u038E': '\u03A5',
49529    '\u03AB': '\u03A5',
49530    '\u038F': '\u03A9',
49531    '\u03AC': '\u03B1',
49532    '\u03AD': '\u03B5',
49533    '\u03AE': '\u03B7',
49534    '\u03AF': '\u03B9',
49535    '\u03CA': '\u03B9',
49536    '\u0390': '\u03B9',
49537    '\u03CC': '\u03BF',
49538    '\u03CD': '\u03C5',
49539    '\u03CB': '\u03C5',
49540    '\u03B0': '\u03C5',
49541    '\u03CE': '\u03C9',
49542    '\u03C2': '\u03C3',
49543    '\u2019': '\''
49544  };
49545
49546  return diacritics;
49547});
49548
49549S2.define('select2/data/base',[
49550  '../utils'
49551], function (Utils) {
49552  function BaseAdapter ($element, options) {
49553    BaseAdapter.__super__.constructor.call(this);
49554  }
49555
49556  Utils.Extend(BaseAdapter, Utils.Observable);
49557
49558  BaseAdapter.prototype.current = function (callback) {
49559    throw new Error('The `current` method must be defined in child classes.');
49560  };
49561
49562  BaseAdapter.prototype.query = function (params, callback) {
49563    throw new Error('The `query` method must be defined in child classes.');
49564  };
49565
49566  BaseAdapter.prototype.bind = function (container, $container) {
49567    // Can be implemented in subclasses
49568  };
49569
49570  BaseAdapter.prototype.destroy = function () {
49571    // Can be implemented in subclasses
49572  };
49573
49574  BaseAdapter.prototype.generateResultId = function (container, data) {
49575    var id = container.id + '-result-';
49576
49577    id += Utils.generateChars(4);
49578
49579    if (data.id != null) {
49580      id += '-' + data.id.toString();
49581    } else {
49582      id += '-' + Utils.generateChars(4);
49583    }
49584    return id;
49585  };
49586
49587  return BaseAdapter;
49588});
49589
49590S2.define('select2/data/select',[
49591  './base',
49592  '../utils',
49593  'jquery'
49594], function (BaseAdapter, Utils, $) {
49595  function SelectAdapter ($element, options) {
49596    this.$element = $element;
49597    this.options = options;
49598
49599    SelectAdapter.__super__.constructor.call(this);
49600  }
49601
49602  Utils.Extend(SelectAdapter, BaseAdapter);
49603
49604  SelectAdapter.prototype.current = function (callback) {
49605    var data = [];
49606    var self = this;
49607
49608    this.$element.find(':selected').each(function () {
49609      var $option = $(this);
49610
49611      var option = self.item($option);
49612
49613      data.push(option);
49614    });
49615
49616    callback(data);
49617  };
49618
49619  SelectAdapter.prototype.select = function (data) {
49620    var self = this;
49621
49622    data.selected = true;
49623
49624    // If data.element is a DOM node, use it instead
49625    if ($(data.element).is('option')) {
49626      data.element.selected = true;
49627
49628      this.$element.trigger('input').trigger('change');
49629
49630      return;
49631    }
49632
49633    if (this.$element.prop('multiple')) {
49634      this.current(function (currentData) {
49635        var val = [];
49636
49637        data = [data];
49638        data.push.apply(data, currentData);
49639
49640        for (var d = 0; d < data.length; d++) {
49641          var id = data[d].id;
49642
49643          if ($.inArray(id, val) === -1) {
49644            val.push(id);
49645          }
49646        }
49647
49648        self.$element.val(val);
49649        self.$element.trigger('input').trigger('change');
49650      });
49651    } else {
49652      var val = data.id;
49653
49654      this.$element.val(val);
49655      this.$element.trigger('input').trigger('change');
49656    }
49657  };
49658
49659  SelectAdapter.prototype.unselect = function (data) {
49660    var self = this;
49661
49662    if (!this.$element.prop('multiple')) {
49663      return;
49664    }
49665
49666    data.selected = false;
vendor: 59,256 bytes, lines 49667-52031
49667
49668    if ($(data.element).is('option')) {
49669      data.element.selected = false;
49670
49671      this.$element.trigger('input').trigger('change');
49672
49673      return;
49674    }
49675
49676    this.current(function (currentData) {
49677      var val = [];
49678
49679      for (var d = 0; d < currentData.length; d++) {
49680        var id = currentData[d].id;
49681
49682        if (id !== data.id && $.inArray(id, val) === -1) {
49683          val.push(id);
49684        }
49685      }
49686
49687      self.$element.val(val);
49688
49689      self.$element.trigger('input').trigger('change');
49690    });
49691  };
49692
49693  SelectAdapter.prototype.bind = function (container, $container) {
49694    var self = this;
49695
49696    this.container = container;
49697
49698    container.on('select', function (params) {
49699      self.select(params.data);
49700    });
49701
49702    container.on('unselect', function (params) {
49703      self.unselect(params.data);
49704    });
49705  };
49706
49707  SelectAdapter.prototype.destroy = function () {
49708    // Remove anything added to child elements
49709    this.$element.find('*').each(function () {
49710      // Remove any custom data set by Select2
49711      Utils.RemoveData(this);
49712    });
49713  };
49714
49715  SelectAdapter.prototype.query = function (params, callback) {
49716    var data = [];
49717    var self = this;
49718
49719    var $options = this.$element.children();
49720
49721    $options.each(function () {
49722      var $option = $(this);
49723
49724      if (!$option.is('option') && !$option.is('optgroup')) {
49725        return;
49726      }
49727
49728      var option = self.item($option);
49729
49730      var matches = self.matches(params, option);
49731
49732      if (matches !== null) {
49733        data.push(matches);
49734      }
49735    });
49736
49737    callback({
49738      results: data
49739    });
49740  };
49741
49742  SelectAdapter.prototype.addOptions = function ($options) {
49743    Utils.appendMany(this.$element, $options);
49744  };
49745
49746  SelectAdapter.prototype.option = function (data) {
49747    var option;
49748
49749    if (data.children) {
49750      option = document.createElement('optgroup');
49751      option.label = data.text;
49752    } else {
49753      option = document.createElement('option');
49754
49755      if (option.textContent !== undefined) {
49756        option.textContent = data.text;
49757      } else {
49758        option.innerText = data.text;
49759      }
49760    }
49761
49762    if (data.id !== undefined) {
49763      option.value = data.id;
49764    }
49765
49766    if (data.disabled) {
49767      option.disabled = true;
49768    }
49769
49770    if (data.selected) {
49771      option.selected = true;
49772    }
49773
49774    if (data.title) {
49775      option.title = data.title;
49776    }
49777
49778    var $option = $(option);
49779
49780    var normalizedData = this._normalizeItem(data);
49781    normalizedData.element = option;
49782
49783    // Override the option's data with the combined data
49784    Utils.StoreData(option, 'data', normalizedData);
49785
49786    return $option;
49787  };
49788
49789  SelectAdapter.prototype.item = function ($option) {
49790    var data = {};
49791
49792    data = Utils.GetData($option[0], 'data');
49793
49794    if (data != null) {
49795      return data;
49796    }
49797
49798    if ($option.is('option')) {
49799      data = {
49800        id: $option.val(),
49801        text: $option.text(),
49802        disabled: $option.prop('disabled'),
49803        selected: $option.prop('selected'),
49804        title: $option.prop('title')
49805      };
49806    } else if ($option.is('optgroup')) {
49807      data = {
49808        text: $option.prop('label'),
49809        children: [],
49810        title: $option.prop('title')
49811      };
49812
49813      var $children = $option.children('option');
49814      var children = [];
49815
49816      for (var c = 0; c < $children.length; c++) {
49817        var $child = $($children[c]);
49818
49819        var child = this.item($child);
49820
49821        children.push(child);
49822      }
49823
49824      data.children = children;
49825    }
49826
49827    data = this._normalizeItem(data);
49828    data.element = $option[0];
49829
49830    Utils.StoreData($option[0], 'data', data);
49831
49832    return data;
49833  };
49834
49835  SelectAdapter.prototype._normalizeItem = function (item) {
49836    if (item !== Object(item)) {
49837      item = {
49838        id: item,
49839        text: item
49840      };
49841    }
49842
49843    item = $.extend({}, {
49844      text: ''
49845    }, item);
49846
49847    var defaults = {
49848      selected: false,
49849      disabled: false
49850    };
49851
49852    if (item.id != null) {
49853      item.id = item.id.toString();
49854    }
49855
49856    if (item.text != null) {
49857      item.text = item.text.toString();
49858    }
49859
49860    if (item._resultId == null && item.id && this.container != null) {
49861      item._resultId = this.generateResultId(this.container, item);
49862    }
49863
49864    return $.extend({}, defaults, item);
49865  };
49866
49867  SelectAdapter.prototype.matches = function (params, data) {
49868    var matcher = this.options.get('matcher');
49869
49870    return matcher(params, data);
49871  };
49872
49873  return SelectAdapter;
49874});
49875
49876S2.define('select2/data/array',[
49877  './select',
49878  '../utils',
49879  'jquery'
49880], function (SelectAdapter, Utils, $) {
49881  function ArrayAdapter ($element, options) {
49882    this._dataToConvert = options.get('data') || [];
49883
49884    ArrayAdapter.__super__.constructor.call(this, $element, options);
49885  }
49886
49887  Utils.Extend(ArrayAdapter, SelectAdapter);
49888
49889  ArrayAdapter.prototype.bind = function (container, $container) {
49890    ArrayAdapter.__super__.bind.call(this, container, $container);
49891
49892    this.addOptions(this.convertToOptions(this._dataToConvert));
49893  };
49894
49895  ArrayAdapter.prototype.select = function (data) {
49896    var $option = this.$element.find('option').filter(function (i, elm) {
49897      return elm.value == data.id.toString();
49898    });
49899
49900    if ($option.length === 0) {
49901      $option = this.option(data);
49902
49903      this.addOptions($option);
49904    }
49905
49906    ArrayAdapter.__super__.select.call(this, data);
49907  };
49908
49909  ArrayAdapter.prototype.convertToOptions = function (data) {
49910    var self = this;
49911
49912    var $existing = this.$element.find('option');
49913    var existingIds = $existing.map(function () {
49914      return self.item($(this)).id;
49915    }).get();
49916
49917    var $options = [];
49918
49919    // Filter out all items except for the one passed in the argument
49920    function onlyItem (item) {
49921      return function () {
49922        return $(this).val() == item.id;
49923      };
49924    }
49925
49926    for (var d = 0; d < data.length; d++) {
49927      var item = this._normalizeItem(data[d]);
49928
49929      // Skip items which were pre-loaded, only merge the data
49930      if ($.inArray(item.id, existingIds) >= 0) {
49931        var $existingOption = $existing.filter(onlyItem(item));
49932
49933        var existingData = this.item($existingOption);
49934        var newData = $.extend(true, {}, item, existingData);
49935
49936        var $newOption = this.option(newData);
49937
49938        $existingOption.replaceWith($newOption);
49939
49940        continue;
49941      }
49942
49943      var $option = this.option(item);
49944
49945      if (item.children) {
49946        var $children = this.convertToOptions(item.children);
49947
49948        Utils.appendMany($option, $children);
49949      }
49950
49951      $options.push($option);
49952    }
49953
49954    return $options;
49955  };
49956
49957  return ArrayAdapter;
49958});
49959
49960S2.define('select2/data/ajax',[
49961  './array',
49962  '../utils',
49963  'jquery'
49964], function (ArrayAdapter, Utils, $) {
49965  function AjaxAdapter ($element, options) {
49966    this.ajaxOptions = this._applyDefaults(options.get('ajax'));
49967
49968    if (this.ajaxOptions.processResults != null) {
49969      this.processResults = this.ajaxOptions.processResults;
49970    }
49971
49972    AjaxAdapter.__super__.constructor.call(this, $element, options);
49973  }
49974
49975  Utils.Extend(AjaxAdapter, ArrayAdapter);
49976
49977  AjaxAdapter.prototype._applyDefaults = function (options) {
49978    var defaults = {
49979      data: function (params) {
49980        return $.extend({}, params, {
49981          q: params.term
49982        });
49983      },
49984      transport: function (params, success, failure) {
49985        var $request = $.ajax(params);
49986
49987        $request.then(success);
49988        $request.fail(failure);
49989
49990        return $request;
49991      }
49992    };
49993
49994    return $.extend({}, defaults, options, true);
49995  };
49996
49997  AjaxAdapter.prototype.processResults = function (results) {
49998    return results;
49999  };
50000
50001  AjaxAdapter.prototype.query = function (params, callback) {
50002    var matches = [];
50003    var self = this;
50004
50005    if (this._request != null) {
50006      // JSONP requests cannot always be aborted
50007      if ($.isFunction(this._request.abort)) {
50008        this._request.abort();
50009      }
50010
50011      this._request = null;
50012    }
50013
50014    var options = $.extend({
50015      type: 'GET'
50016    }, this.ajaxOptions);
50017
50018    if (typeof options.url === 'function') {
50019      options.url = options.url.call(this.$element, params);
50020    }
50021
50022    if (typeof options.data === 'function') {
50023      options.data = options.data.call(this.$element, params);
50024    }
50025
50026    function request () {
50027      var $request = options.transport(options, function (data) {
50028        var results = self.processResults(data, params);
50029
50030        if (self.options.get('debug') && window.console && console.error) {
50031          // Check to make sure that the response included a `results` key.
50032          if (!results || !results.results || !$.isArray(results.results)) {
50033            console.error(
50034              'Select2: The AJAX results did not return an array in the ' +
50035              '`results` key of the response.'
50036            );
50037          }
50038        }
50039
50040        callback(results);
50041      }, function () {
50042        // Attempt to detect if a request was aborted
50043        // Only works if the transport exposes a status property
50044        if ('status' in $request &&
50045            ($request.status === 0 || $request.status === '0')) {
50046          return;
50047        }
50048
50049        self.trigger('results:message', {
50050          message: 'errorLoading'
50051        });
50052      });
50053
50054      self._request = $request;
50055    }
50056
50057    if (this.ajaxOptions.delay && params.term != null) {
50058      if (this._queryTimeout) {
50059        window.clearTimeout(this._queryTimeout);
50060      }
50061
50062      this._queryTimeout = window.setTimeout(request, this.ajaxOptions.delay);
50063    } else {
50064      request();
50065    }
50066  };
50067
50068  return AjaxAdapter;
50069});
50070
50071S2.define('select2/data/tags',[
50072  'jquery'
50073], function ($) {
50074  function Tags (decorated, $element, options) {
50075    var tags = options.get('tags');
50076
50077    var createTag = options.get('createTag');
50078
50079    if (createTag !== undefined) {
50080      this.createTag = createTag;
50081    }
50082
50083    var insertTag = options.get('insertTag');
50084
50085    if (insertTag !== undefined) {
50086        this.insertTag = insertTag;
50087    }
50088
50089    decorated.call(this, $element, options);
50090
50091    if ($.isArray(tags)) {
50092      for (var t = 0; t < tags.length; t++) {
50093        var tag = tags[t];
50094        var item = this._normalizeItem(tag);
50095
50096        var $option = this.option(item);
50097
50098        this.$element.append($option);
50099      }
50100    }
50101  }
50102
50103  Tags.prototype.query = function (decorated, params, callback) {
50104    var self = this;
50105
50106    this._removeOldTags();
50107
50108    if (params.term == null || params.page != null) {
50109      decorated.call(this, params, callback);
50110      return;
50111    }
50112
50113    function wrapper (obj, child) {
50114      var data = obj.results;
50115
50116      for (var i = 0; i < data.length; i++) {
50117        var option = data[i];
50118
50119        var checkChildren = (
50120          option.children != null &&
50121          !wrapper({
50122            results: option.children
50123          }, true)
50124        );
50125
50126        var optionText = (option.text || '').toUpperCase();
50127        var paramsTerm = (params.term || '').toUpperCase();
50128
50129        var checkText = optionText === paramsTerm;
50130
50131        if (checkText || checkChildren) {
50132          if (child) {
50133            return false;
50134          }
50135
50136          obj.data = data;
50137          callback(obj);
50138
50139          return;
50140        }
50141      }
50142
50143      if (child) {
50144        return true;
50145      }
50146
50147      var tag = self.createTag(params);
50148
50149      if (tag != null) {
50150        var $option = self.option(tag);
50151        $option.attr('data-select2-tag', true);
50152
50153        self.addOptions([$option]);
50154
50155        self.insertTag(data, tag);
50156      }
50157
50158      obj.results = data;
50159
50160      callback(obj);
50161    }
50162
50163    decorated.call(this, params, wrapper);
50164  };
50165
50166  Tags.prototype.createTag = function (decorated, params) {
50167    var term = $.trim(params.term);
50168
50169    if (term === '') {
50170      return null;
50171    }
50172
50173    return {
50174      id: term,
50175      text: term
50176    };
50177  };
50178
50179  Tags.prototype.insertTag = function (_, data, tag) {
50180    data.unshift(tag);
50181  };
50182
50183  Tags.prototype._removeOldTags = function (_) {
50184    var $options = this.$element.find('option[data-select2-tag]');
50185
50186    $options.each(function () {
50187      if (this.selected) {
50188        return;
50189      }
50190
50191      $(this).remove();
50192    });
50193  };
50194
50195  return Tags;
50196});
50197
50198S2.define('select2/data/tokenizer',[
50199  'jquery'
50200], function ($) {
50201  function Tokenizer (decorated, $element, options) {
50202    var tokenizer = options.get('tokenizer');
50203
50204    if (tokenizer !== undefined) {
50205      this.tokenizer = tokenizer;
50206    }
50207
50208    decorated.call(this, $element, options);
50209  }
50210
50211  Tokenizer.prototype.bind = function (decorated, container, $container) {
50212    decorated.call(this, container, $container);
50213
50214    this.$search =  container.dropdown.$search || container.selection.$search ||
50215      $container.find('.select2-search__field');
50216  };
50217
50218  Tokenizer.prototype.query = function (decorated, params, callback) {
50219    var self = this;
50220
50221    function createAndSelect (data) {
50222      // Normalize the data object so we can use it for checks
50223      var item = self._normalizeItem(data);
50224
50225      // Check if the data object already exists as a tag
50226      // Select it if it doesn't
50227      var $existingOptions = self.$element.find('option').filter(function () {
50228        return $(this).val() === item.id;
50229      });
50230
50231      // If an existing option wasn't found for it, create the option
50232      if (!$existingOptions.length) {
50233        var $option = self.option(item);
50234        $option.attr('data-select2-tag', true);
50235
50236        self._removeOldTags();
50237        self.addOptions([$option]);
50238      }
50239
50240      // Select the item, now that we know there is an option for it
50241      select(item);
50242    }
50243
50244    function select (data) {
50245      self.trigger('select', {
50246        data: data
50247      });
50248    }
50249
50250    params.term = params.term || '';
50251
50252    var tokenData = this.tokenizer(params, this.options, createAndSelect);
50253
50254    if (tokenData.term !== params.term) {
50255      // Replace the search term if we have the search box
50256      if (this.$search.length) {
50257        this.$search.val(tokenData.term);
50258        this.$search.trigger('focus');
50259      }
50260
50261      params.term = tokenData.term;
50262    }
50263
50264    decorated.call(this, params, callback);
50265  };
50266
50267  Tokenizer.prototype.tokenizer = function (_, params, options, callback) {
50268    var separators = options.get('tokenSeparators') || [];
50269    var term = params.term;
50270    var i = 0;
50271
50272    var createTag = this.createTag || function (params) {
50273      return {
50274        id: params.term,
50275        text: params.term
50276      };
50277    };
50278
50279    while (i < term.length) {
50280      var termChar = term[i];
50281
50282      if ($.inArray(termChar, separators) === -1) {
50283        i++;
50284
50285        continue;
50286      }
50287
50288      var part = term.substr(0, i);
50289      var partParams = $.extend({}, params, {
50290        term: part
50291      });
50292
50293      var data = createTag(partParams);
50294
50295      if (data == null) {
50296        i++;
50297        continue;
50298      }
50299
50300      callback(data);
50301
50302      // Reset the term to not include the tokenized portion
50303      term = term.substr(i + 1) || '';
50304      i = 0;
50305    }
50306
50307    return {
50308      term: term
50309    };
50310  };
50311
50312  return Tokenizer;
50313});
50314
50315S2.define('select2/data/minimumInputLength',[
50316
50317], function () {
50318  function MinimumInputLength (decorated, $e, options) {
50319    this.minimumInputLength = options.get('minimumInputLength');
50320
50321    decorated.call(this, $e, options);
50322  }
50323
50324  MinimumInputLength.prototype.query = function (decorated, params, callback) {
50325    params.term = params.term || '';
50326
50327    if (params.term.length < this.minimumInputLength) {
50328      this.trigger('results:message', {
50329        message: 'inputTooShort',
50330        args: {
50331          minimum: this.minimumInputLength,
50332          input: params.term,
50333          params: params
50334        }
50335      });
50336
50337      return;
50338    }
50339
50340    decorated.call(this, params, callback);
50341  };
50342
50343  return MinimumInputLength;
50344});
50345
50346S2.define('select2/data/maximumInputLength',[
50347
50348], function () {
50349  function MaximumInputLength (decorated, $e, options) {
50350    this.maximumInputLength = options.get('maximumInputLength');
50351
50352    decorated.call(this, $e, options);
50353  }
50354
50355  MaximumInputLength.prototype.query = function (decorated, params, callback) {
50356    params.term = params.term || '';
50357
50358    if (this.maximumInputLength > 0 &&
50359        params.term.length > this.maximumInputLength) {
50360      this.trigger('results:message', {
50361        message: 'inputTooLong',
50362        args: {
50363          maximum: this.maximumInputLength,
50364          input: params.term,
50365          params: params
50366        }
50367      });
50368
50369      return;
50370    }
50371
50372    decorated.call(this, params, callback);
50373  };
50374
50375  return MaximumInputLength;
50376});
50377
50378S2.define('select2/data/maximumSelectionLength',[
50379
50380], function (){
50381  function MaximumSelectionLength (decorated, $e, options) {
50382    this.maximumSelectionLength = options.get('maximumSelectionLength');
50383
50384    decorated.call(this, $e, options);
50385  }
50386
50387  MaximumSelectionLength.prototype.bind =
50388    function (decorated, container, $container) {
50389      var self = this;
50390
50391      decorated.call(this, container, $container);
50392
50393      container.on('select', function () {
50394        self._checkIfMaximumSelected();
50395      });
50396  };
50397
50398  MaximumSelectionLength.prototype.query =
50399    function (decorated, params, callback) {
50400      var self = this;
50401
50402      this._checkIfMaximumSelected(function () {
50403        decorated.call(self, params, callback);
50404      });
50405  };
50406
50407  MaximumSelectionLength.prototype._checkIfMaximumSelected =
50408    function (_, successCallback) {
50409      var self = this;
50410
50411      this.current(function (currentData) {
50412        var count = currentData != null ? currentData.length : 0;
50413        if (self.maximumSelectionLength > 0 &&
50414          count >= self.maximumSelectionLength) {
50415          self.trigger('results:message', {
50416            message: 'maximumSelected',
50417            args: {
50418              maximum: self.maximumSelectionLength
50419            }
50420          });
50421          return;
50422        }
50423
50424        if (successCallback) {
50425          successCallback();
50426        }
50427      });
50428  };
50429
50430  return MaximumSelectionLength;
50431});
50432
50433S2.define('select2/dropdown',[
50434  'jquery',
50435  './utils'
50436], function ($, Utils) {
50437  function Dropdown ($element, options) {
50438    this.$element = $element;
50439    this.options = options;
50440
50441    Dropdown.__super__.constructor.call(this);
50442  }
50443
50444  Utils.Extend(Dropdown, Utils.Observable);
50445
50446  Dropdown.prototype.render = function () {
50447    var $dropdown = $(
50448      '<span class="select2-dropdown">' +
50449        '<span class="select2-results"></span>' +
50450      '</span>'
50451    );
50452
50453    $dropdown.attr('dir', this.options.get('dir'));
50454
50455    this.$dropdown = $dropdown;
50456
50457    return $dropdown;
50458  };
50459
50460  Dropdown.prototype.bind = function () {
50461    // Should be implemented in subclasses
50462  };
50463
50464  Dropdown.prototype.position = function ($dropdown, $container) {
50465    // Should be implemented in subclasses
50466  };
50467
50468  Dropdown.prototype.destroy = function () {
50469    // Remove the dropdown from the DOM
50470    this.$dropdown.remove();
50471  };
50472
50473  return Dropdown;
50474});
50475
50476S2.define('select2/dropdown/search',[
50477  'jquery',
50478  '../utils'
50479], function ($, Utils) {
50480  function Search () { }
50481
50482  Search.prototype.render = function (decorated) {
50483    var $rendered = decorated.call(this);
50484
50485    var $search = $(
50486      '<span class="select2-search select2-search--dropdown">' +
50487        '<input class="select2-search__field" type="search" tabindex="-1"' +
50488        ' autocomplete="off" autocorrect="off" autocapitalize="none"' +
50489        ' spellcheck="false" role="searchbox" aria-autocomplete="list" />' +
50490      '</span>'
50491    );
50492
50493    this.$searchContainer = $search;
50494    this.$search = $search.find('input');
50495
50496    $rendered.prepend($search);
50497
50498    return $rendered;
50499  };
50500
50501  Search.prototype.bind = function (decorated, container, $container) {
50502    var self = this;
50503
50504    var resultsId = container.id + '-results';
50505
50506    decorated.call(this, container, $container);
50507
50508    this.$search.on('keydown', function (evt) {
50509      self.trigger('keypress', evt);
50510
50511      self._keyUpPrevented = evt.isDefaultPrevented();
50512    });
50513
50514    // Workaround for browsers which do not support the `input` event
50515    // This will prevent double-triggering of events for browsers which support
50516    // both the `keyup` and `input` events.
50517    this.$search.on('input', function (evt) {
50518      // Unbind the duplicated `keyup` event
50519      $(this).off('keyup');
50520    });
50521
50522    this.$search.on('keyup input', function (evt) {
50523      self.handleSearch(evt);
50524    });
50525
50526    container.on('open', function () {
50527      self.$search.attr('tabindex', 0);
50528      self.$search.attr('aria-controls', resultsId);
50529
50530      self.$search.trigger('focus');
50531
50532      window.setTimeout(function () {
50533        self.$search.trigger('focus');
50534      }, 0);
50535    });
50536
50537    container.on('close', function () {
50538      self.$search.attr('tabindex', -1);
50539      self.$search.removeAttr('aria-controls');
50540      self.$search.removeAttr('aria-activedescendant');
50541
50542      self.$search.val('');
50543      self.$search.trigger('blur');
50544    });
50545
50546    container.on('focus', function () {
50547      if (!container.isOpen()) {
50548        self.$search.trigger('focus');
50549      }
50550    });
50551
50552    container.on('results:all', function (params) {
50553      if (params.query.term == null || params.query.term === '') {
50554        var showSearch = self.showSearch(params);
50555
50556        if (showSearch) {
50557          self.$searchContainer.removeClass('select2-search--hide');
50558        } else {
50559          self.$searchContainer.addClass('select2-search--hide');
50560        }
50561      }
50562    });
50563
50564    container.on('results:focus', function (params) {
50565      if (params.data._resultId) {
50566        self.$search.attr('aria-activedescendant', params.data._resultId);
50567      } else {
50568        self.$search.removeAttr('aria-activedescendant');
50569      }
50570    });
50571  };
50572
50573  Search.prototype.handleSearch = function (evt) {
50574    if (!this._keyUpPrevented) {
50575      var input = this.$search.val();
50576
50577      this.trigger('query', {
50578        term: input
50579      });
50580    }
50581
50582    this._keyUpPrevented = false;
50583  };
50584
50585  Search.prototype.showSearch = function (_, params) {
50586    return true;
50587  };
50588
50589  return Search;
50590});
50591
50592S2.define('select2/dropdown/hidePlaceholder',[
50593
50594], function () {
50595  function HidePlaceholder (decorated, $element, options, dataAdapter) {
50596    this.placeholder = this.normalizePlaceholder(options.get('placeholder'));
50597
50598    decorated.call(this, $element, options, dataAdapter);
50599  }
50600
50601  HidePlaceholder.prototype.append = function (decorated, data) {
50602    data.results = this.removePlaceholder(data.results);
50603
50604    decorated.call(this, data);
50605  };
50606
50607  HidePlaceholder.prototype.normalizePlaceholder = function (_, placeholder) {
50608    if (typeof placeholder === 'string') {
50609      placeholder = {
50610        id: '',
50611        text: placeholder
50612      };
50613    }
50614
50615    return placeholder;
50616  };
50617
50618  HidePlaceholder.prototype.removePlaceholder = function (_, data) {
50619    var modifiedData = data.slice(0);
50620
50621    for (var d = data.length - 1; d >= 0; d--) {
50622      var item = data[d];
50623
50624      if (this.placeholder.id === item.id) {
50625        modifiedData.splice(d, 1);
50626      }
50627    }
50628
50629    return modifiedData;
50630  };
50631
50632  return HidePlaceholder;
50633});
50634
50635S2.define('select2/dropdown/infiniteScroll',[
50636  'jquery'
50637], function ($) {
50638  function InfiniteScroll (decorated, $element, options, dataAdapter) {
50639    this.lastParams = {};
50640
50641    decorated.call(this, $element, options, dataAdapter);
50642
50643    this.$loadingMore = this.createLoadingMore();
50644    this.loading = false;
50645  }
50646
50647  InfiniteScroll.prototype.append = function (decorated, data) {
50648    this.$loadingMore.remove();
50649    this.loading = false;
50650
50651    decorated.call(this, data);
50652
50653    if (this.showLoadingMore(data)) {
50654      this.$results.append(this.$loadingMore);
50655      this.loadMoreIfNeeded();
50656    }
50657  };
50658
50659  InfiniteScroll.prototype.bind = function (decorated, container, $container) {
50660    var self = this;
50661
50662    decorated.call(this, container, $container);
50663
50664    container.on('query', function (params) {
50665      self.lastParams = params;
50666      self.loading = true;
50667    });
50668
50669    container.on('query:append', function (params) {
50670      self.lastParams = params;
50671      self.loading = true;
50672    });
50673
50674    this.$results.on('scroll', this.loadMoreIfNeeded.bind(this));
50675  };
50676
50677  InfiniteScroll.prototype.loadMoreIfNeeded = function () {
50678    var isLoadMoreVisible = $.contains(
50679      document.documentElement,
50680      this.$loadingMore[0]
50681    );
50682
50683    if (this.loading || !isLoadMoreVisible) {
50684      return;
50685    }
50686
50687    var currentOffset = this.$results.offset().top +
50688      this.$results.outerHeight(false);
50689    var loadingMoreOffset = this.$loadingMore.offset().top +
50690      this.$loadingMore.outerHeight(false);
50691
50692    if (currentOffset + 50 >= loadingMoreOffset) {
50693      this.loadMore();
50694    }
50695  };
50696
50697  InfiniteScroll.prototype.loadMore = function () {
50698    this.loading = true;
50699
50700    var params = $.extend({}, {page: 1}, this.lastParams);
50701
50702    params.page++;
50703
50704    this.trigger('query:append', params);
50705  };
50706
50707  InfiniteScroll.prototype.showLoadingMore = function (_, data) {
50708    return data.pagination && data.pagination.more;
50709  };
50710
50711  InfiniteScroll.prototype.createLoadingMore = function () {
50712    var $option = $(
50713      '<li ' +
50714      'class="select2-results__option select2-results__option--load-more"' +
50715      'role="option" aria-disabled="true"></li>'
50716    );
50717
50718    var message = this.options.get('translations').get('loadingMore');
50719
50720    $option.html(message(this.lastParams));
50721
50722    return $option;
50723  };
50724
50725  return InfiniteScroll;
50726});
50727
50728S2.define('select2/dropdown/attachBody',[
50729  'jquery',
50730  '../utils'
50731], function ($, Utils) {
50732  function AttachBody (decorated, $element, options) {
50733    this.$dropdownParent = $(options.get('dropdownParent') || document.body);
50734
50735    decorated.call(this, $element, options);
50736  }
50737
50738  AttachBody.prototype.bind = function (decorated, container, $container) {
50739    var self = this;
50740
50741    decorated.call(this, container, $container);
50742
50743    container.on('open', function () {
50744      self._showDropdown();
50745      self._attachPositioningHandler(container);
50746
50747      // Must bind after the results handlers to ensure correct sizing
50748      self._bindContainerResultHandlers(container);
50749    });
50750
50751    container.on('close', function () {
50752      self._hideDropdown();
50753      self._detachPositioningHandler(container);
50754    });
50755
50756    this.$dropdownContainer.on('mousedown', function (evt) {
50757      evt.stopPropagation();
50758    });
50759  };
50760
50761  AttachBody.prototype.destroy = function (decorated) {
50762    decorated.call(this);
50763
50764    this.$dropdownContainer.remove();
50765  };
50766
50767  AttachBody.prototype.position = function (decorated, $dropdown, $container) {
50768    // Clone all of the container classes
50769    $dropdown.attr('class', $container.attr('class'));
50770
50771    $dropdown.removeClass('select2');
50772    $dropdown.addClass('select2-container--open');
50773
50774    $dropdown.css({
50775      position: 'absolute',
50776      top: -999999
50777    });
50778
50779    this.$container = $container;
50780  };
50781
50782  AttachBody.prototype.render = function (decorated) {
50783    var $container = $('<span></span>');
50784
50785    var $dropdown = decorated.call(this);
50786    $container.append($dropdown);
50787
50788    this.$dropdownContainer = $container;
50789
50790    return $container;
50791  };
50792
50793  AttachBody.prototype._hideDropdown = function (decorated) {
50794    this.$dropdownContainer.detach();
50795  };
50796
50797  AttachBody.prototype._bindContainerResultHandlers =
50798      function (decorated, container) {
50799
50800    // These should only be bound once
50801    if (this._containerResultsHandlersBound) {
50802      return;
50803    }
50804
50805    var self = this;
50806
50807    container.on('results:all', function () {
50808      self._positionDropdown();
50809      self._resizeDropdown();
50810    });
50811
50812    container.on('results:append', function () {
50813      self._positionDropdown();
50814      self._resizeDropdown();
50815    });
50816
50817    container.on('results:message', function () {
50818      self._positionDropdown();
50819      self._resizeDropdown();
50820    });
50821
50822    container.on('select', function () {
50823      self._positionDropdown();
50824      self._resizeDropdown();
50825    });
50826
50827    container.on('unselect', function () {
50828      self._positionDropdown();
50829      self._resizeDropdown();
50830    });
50831
50832    this._containerResultsHandlersBound = true;
50833  };
50834
50835  AttachBody.prototype._attachPositioningHandler =
50836      function (decorated, container) {
50837    var self = this;
50838
50839    var scrollEvent = 'scroll.select2.' + container.id;
50840    var resizeEvent = 'resize.select2.' + container.id;
50841    var orientationEvent = 'orientationchange.select2.' + container.id;
50842
50843    var $watchers = this.$container.parents().filter(Utils.hasScroll);
50844    $watchers.each(function () {
50845      Utils.StoreData(this, 'select2-scroll-position', {
50846        x: $(this).scrollLeft(),
50847        y: $(this).scrollTop()
50848      });
50849    });
50850
50851    $watchers.on(scrollEvent, function (ev) {
50852      var position = Utils.GetData(this, 'select2-scroll-position');
50853      $(this).scrollTop(position.y);
50854    });
50855
50856    $(window).on(scrollEvent + ' ' + resizeEvent + ' ' + orientationEvent,
50857      function (e) {
50858      self._positionDropdown();
50859      self._resizeDropdown();
50860    });
50861  };
50862
50863  AttachBody.prototype._detachPositioningHandler =
50864      function (decorated, container) {
50865    var scrollEvent = 'scroll.select2.' + container.id;
50866    var resizeEvent = 'resize.select2.' + container.id;
50867    var orientationEvent = 'orientationchange.select2.' + container.id;
50868
50869    var $watchers = this.$container.parents().filter(Utils.hasScroll);
50870    $watchers.off(scrollEvent);
50871
50872    $(window).off(scrollEvent + ' ' + resizeEvent + ' ' + orientationEvent);
50873  };
50874
50875  AttachBody.prototype._positionDropdown = function () {
50876    var $window = $(window);
50877
50878    var isCurrentlyAbove = this.$dropdown.hasClass('select2-dropdown--above');
50879    var isCurrentlyBelow = this.$dropdown.hasClass('select2-dropdown--below');
50880
50881    var newDirection = null;
50882
50883    var offset = this.$container.offset();
50884
50885    offset.bottom = offset.top + this.$container.outerHeight(false);
50886
50887    var container = {
50888      height: this.$container.outerHeight(false)
50889    };
50890
50891    container.top = offset.top;
50892    container.bottom = offset.top + container.height;
50893
50894    var dropdown = {
50895      height: this.$dropdown.outerHeight(false)
50896    };
50897
50898    var viewport = {
50899      top: $window.scrollTop(),
50900      bottom: $window.scrollTop() + $window.height()
50901    };
50902
50903    var enoughRoomAbove = viewport.top < (offset.top - dropdown.height);
50904    var enoughRoomBelow = viewport.bottom > (offset.bottom + dropdown.height);
50905
50906    var css = {
50907      left: offset.left,
50908      top: container.bottom
50909    };
50910
50911    // Determine what the parent element is to use for calculating the offset
50912    var $offsetParent = this.$dropdownParent;
50913
50914    // For statically positioned elements, we need to get the element
50915    // that is determining the offset
50916    if ($offsetParent.css('position') === 'static') {
50917      $offsetParent = $offsetParent.offsetParent();
50918    }
50919
50920    var parentOffset = {
50921      top: 0,
50922      left: 0
50923    };
50924
50925    if (
50926      $.contains(document.body, $offsetParent[0]) ||
50927      $offsetParent[0].isConnected
50928      ) {
50929      parentOffset = $offsetParent.offset();
50930    }
50931
50932    css.top -= parentOffset.top;
50933    css.left -= parentOffset.left;
50934
50935    if (!isCurrentlyAbove && !isCurrentlyBelow) {
50936      newDirection = 'below';
50937    }
50938
50939    if (!enoughRoomBelow && enoughRoomAbove && !isCurrentlyAbove) {
50940      newDirection = 'above';
50941    } else if (!enoughRoomAbove && enoughRoomBelow && isCurrentlyAbove) {
50942      newDirection = 'below';
50943    }
50944
50945    if (newDirection == 'above' ||
50946      (isCurrentlyAbove && newDirection !== 'below')) {
50947      css.top = container.top - parentOffset.top - dropdown.height;
50948    }
50949
50950    if (newDirection != null) {
50951      this.$dropdown
50952        .removeClass('select2-dropdown--below select2-dropdown--above')
50953        .addClass('select2-dropdown--' + newDirection);
50954      this.$container
50955        .removeClass('select2-container--below select2-container--above')
50956        .addClass('select2-container--' + newDirection);
50957    }
50958
50959    this.$dropdownContainer.css(css);
50960  };
50961
50962  AttachBody.prototype._resizeDropdown = function () {
50963    var css = {
50964      width: this.$container.outerWidth(false) + 'px'
50965    };
50966
50967    if (this.options.get('dropdownAutoWidth')) {
50968      css.minWidth = css.width;
50969      css.position = 'relative';
50970      css.width = 'auto';
50971    }
50972
50973    this.$dropdown.css(css);
50974  };
50975
50976  AttachBody.prototype._showDropdown = function (decorated) {
50977    this.$dropdownContainer.appendTo(this.$dropdownParent);
50978
50979    this._positionDropdown();
50980    this._resizeDropdown();
50981  };
50982
50983  return AttachBody;
50984});
50985
50986S2.define('select2/dropdown/minimumResultsForSearch',[
50987
50988], function () {
50989  function countResults (data) {
50990    var count = 0;
50991
50992    for (var d = 0; d < data.length; d++) {
50993      var item = data[d];
50994
50995      if (item.children) {
50996        count += countResults(item.children);
50997      } else {
50998        count++;
50999      }
51000    }
51001
51002    return count;
51003  }
51004
51005  function MinimumResultsForSearch (decorated, $element, options, dataAdapter) {
51006    this.minimumResultsForSearch = options.get('minimumResultsForSearch');
51007
51008    if (this.minimumResultsForSearch < 0) {
51009      this.minimumResultsForSearch = Infinity;
51010    }
51011
51012    decorated.call(this, $element, options, dataAdapter);
51013  }
51014
51015  MinimumResultsForSearch.prototype.showSearch = function (decorated, params) {
51016    if (countResults(params.data.results) < this.minimumResultsForSearch) {
51017      return false;
51018    }
51019
51020    return decorated.call(this, params);
51021  };
51022
51023  return MinimumResultsForSearch;
51024});
51025
51026S2.define('select2/dropdown/selectOnClose',[
51027  '../utils'
51028], function (Utils) {
51029  function SelectOnClose () { }
51030
51031  SelectOnClose.prototype.bind = function (decorated, container, $container) {
51032    var self = this;
51033
51034    decorated.call(this, container, $container);
51035
51036    container.on('close', function (params) {
51037      self._handleSelectOnClose(params);
51038    });
51039  };
51040
51041  SelectOnClose.prototype._handleSelectOnClose = function (_, params) {
51042    if (params && params.originalSelect2Event != null) {
51043      var event = params.originalSelect2Event;
51044
51045      // Don't select an item if the close event was triggered from a select or
51046      // unselect event
51047      if (event._type === 'select' || event._type === 'unselect') {
51048        return;
51049      }
51050    }
51051
51052    var $highlightedResults = this.getHighlightedResults();
51053
51054    // Only select highlighted results
51055    if ($highlightedResults.length < 1) {
51056      return;
51057    }
51058
51059    var data = Utils.GetData($highlightedResults[0], 'data');
51060
51061    // Don't re-select already selected resulte
51062    if (
51063      (data.element != null && data.element.selected) ||
51064      (data.element == null && data.selected)
51065    ) {
51066      return;
51067    }
51068
51069    this.trigger('select', {
51070        data: data
51071    });
51072  };
51073
51074  return SelectOnClose;
51075});
51076
51077S2.define('select2/dropdown/closeOnSelect',[
51078
51079], function () {
51080  function CloseOnSelect () { }
51081
51082  CloseOnSelect.prototype.bind = function (decorated, container, $container) {
51083    var self = this;
51084
51085    decorated.call(this, container, $container);
51086
51087    container.on('select', function (evt) {
51088      self._selectTriggered(evt);
51089    });
51090
51091    container.on('unselect', function (evt) {
51092      self._selectTriggered(evt);
51093    });
51094  };
51095
51096  CloseOnSelect.prototype._selectTriggered = function (_, evt) {
51097    var originalEvent = evt.originalEvent;
51098
51099    // Don't close if the control key is being held
51100    if (originalEvent && (originalEvent.ctrlKey || originalEvent.metaKey)) {
51101      return;
51102    }
51103
51104    this.trigger('close', {
51105      originalEvent: originalEvent,
51106      originalSelect2Event: evt
51107    });
51108  };
51109
51110  return CloseOnSelect;
51111});
51112
51113S2.define('select2/i18n/en',[],function () {
51114  // English
51115  return {
51116    errorLoading: function () {
51117      return 'The results could not be loaded.';
51118    },
51119    inputTooLong: function (args) {
51120      var overChars = args.input.length - args.maximum;
51121
51122      var message = 'Please delete ' + overChars + ' character';
51123
51124      if (overChars != 1) {
51125        message += 's';
51126      }
51127
51128      return message;
51129    },
51130    inputTooShort: function (args) {
51131      var remainingChars = args.minimum - args.input.length;
51132
51133      var message = 'Please enter ' + remainingChars + ' or more characters';
51134
51135      return message;
51136    },
51137    loadingMore: function () {
51138      return 'Loading more results…';
51139    },
51140    maximumSelected: function (args) {
51141      var message = 'You can only select ' + args.maximum + ' item';
51142
51143      if (args.maximum != 1) {
51144        message += 's';
51145      }
51146
51147      return message;
51148    },
51149    noResults: function () {
51150      return 'No results found';
51151    },
51152    searching: function () {
51153      return 'Searching…';
51154    },
51155    removeAllItems: function () {
51156      return 'Remove all items';
51157    }
51158  };
51159});
51160
51161S2.define('select2/defaults',[
51162  'jquery',
51163  'require',
51164
51165  './results',
51166
51167  './selection/single',
51168  './selection/multiple',
51169  './selection/placeholder',
51170  './selection/allowClear',
51171  './selection/search',
51172  './selection/eventRelay',
51173
51174  './utils',
51175  './translation',
51176  './diacritics',
51177
51178  './data/select',
51179  './data/array',
51180  './data/ajax',
51181  './data/tags',
51182  './data/tokenizer',
51183  './data/minimumInputLength',
51184  './data/maximumInputLength',
51185  './data/maximumSelectionLength',
51186
51187  './dropdown',
51188  './dropdown/search',
51189  './dropdown/hidePlaceholder',
51190  './dropdown/infiniteScroll',
51191  './dropdown/attachBody',
51192  './dropdown/minimumResultsForSearch',
51193  './dropdown/selectOnClose',
51194  './dropdown/closeOnSelect',
51195
51196  './i18n/en'
51197], function ($, require,
51198
51199             ResultsList,
51200
51201             SingleSelection, MultipleSelection, Placeholder, AllowClear,
51202             SelectionSearch, EventRelay,
51203
51204             Utils, Translation, DIACRITICS,
51205
51206             SelectData, ArrayData, AjaxData, Tags, Tokenizer,
51207             MinimumInputLength, MaximumInputLength, MaximumSelectionLength,
51208
51209             Dropdown, DropdownSearch, HidePlaceholder, InfiniteScroll,
51210             AttachBody, MinimumResultsForSearch, SelectOnClose, CloseOnSelect,
51211
51212             EnglishTranslation) {
51213  function Defaults () {
51214    this.reset();
51215  }
51216
51217  Defaults.prototype.apply = function (options) {
51218    options = $.extend(true, {}, this.defaults, options);
51219
51220    if (options.dataAdapter == null) {
51221      if (options.ajax != null) {
51222        options.dataAdapter = AjaxData;
51223      } else if (options.data != null) {
51224        options.dataAdapter = ArrayData;
51225      } else {
51226        options.dataAdapter = SelectData;
51227      }
51228
51229      if (options.minimumInputLength > 0) {
51230        options.dataAdapter = Utils.Decorate(
51231          options.dataAdapter,
51232          MinimumInputLength
51233        );
51234      }
51235
51236      if (options.maximumInputLength > 0) {
51237        options.dataAdapter = Utils.Decorate(
51238          options.dataAdapter,
51239          MaximumInputLength
51240        );
51241      }
51242
51243      if (options.maximumSelectionLength > 0) {
51244        options.dataAdapter = Utils.Decorate(
51245          options.dataAdapter,
51246          MaximumSelectionLength
51247        );
51248      }
51249
51250      if (options.tags) {
51251        options.dataAdapter = Utils.Decorate(options.dataAdapter, Tags);
51252      }
51253
51254      if (options.tokenSeparators != null || options.tokenizer != null) {
51255        options.dataAdapter = Utils.Decorate(
51256          options.dataAdapter,
51257          Tokenizer
51258        );
51259      }
51260
51261      if (options.query != null) {
51262        var Query = require(options.amdBase + 'compat/query');
51263
51264        options.dataAdapter = Utils.Decorate(
51265          options.dataAdapter,
51266          Query
51267        );
51268      }
51269
51270      if (options.initSelection != null) {
51271        var InitSelection = require(options.amdBase + 'compat/initSelection');
51272
51273        options.dataAdapter = Utils.Decorate(
51274          options.dataAdapter,
51275          InitSelection
51276        );
51277      }
51278    }
51279
51280    if (options.resultsAdapter == null) {
51281      options.resultsAdapter = ResultsList;
51282
51283      if (options.ajax != null) {
51284        options.resultsAdapter = Utils.Decorate(
51285          options.resultsAdapter,
51286          InfiniteScroll
51287        );
51288      }
51289
51290      if (options.placeholder != null) {
51291        options.resultsAdapter = Utils.Decorate(
51292          options.resultsAdapter,
51293          HidePlaceholder
51294        );
51295      }
51296
51297      if (options.selectOnClose) {
51298        options.resultsAdapter = Utils.Decorate(
51299          options.resultsAdapter,
51300          SelectOnClose
51301        );
51302      }
51303    }
51304
51305    if (options.dropdownAdapter == null) {
51306      if (options.multiple) {
51307        options.dropdownAdapter = Dropdown;
51308      } else {
51309        var SearchableDropdown = Utils.Decorate(Dropdown, DropdownSearch);
51310
51311        options.dropdownAdapter = SearchableDropdown;
51312      }
51313
51314      if (options.minimumResultsForSearch !== 0) {
51315        options.dropdownAdapter = Utils.Decorate(
51316          options.dropdownAdapter,
51317          MinimumResultsForSearch
51318        );
51319      }
51320
51321      if (options.closeOnSelect) {
51322        options.dropdownAdapter = Utils.Decorate(
51323          options.dropdownAdapter,
51324          CloseOnSelect
51325        );
51326      }
51327
51328      if (
51329        options.dropdownCssClass != null ||
51330        options.dropdownCss != null ||
51331        options.adaptDropdownCssClass != null
51332      ) {
51333        var DropdownCSS = require(options.amdBase + 'compat/dropdownCss');
51334
51335        options.dropdownAdapter = Utils.Decorate(
51336          options.dropdownAdapter,
51337          DropdownCSS
51338        );
51339      }
51340
51341      options.dropdownAdapter = Utils.Decorate(
51342        options.dropdownAdapter,
51343        AttachBody
51344      );
51345    }
51346
51347    if (options.selectionAdapter == null) {
51348      if (options.multiple) {
51349        options.selectionAdapter = MultipleSelection;
51350      } else {
51351        options.selectionAdapter = SingleSelection;
51352      }
51353
51354      // Add the placeholder mixin if a placeholder was specified
51355      if (options.placeholder != null) {
51356        options.selectionAdapter = Utils.Decorate(
51357          options.selectionAdapter,
51358          Placeholder
51359        );
51360      }
51361
51362      if (options.allowClear) {
51363        options.selectionAdapter = Utils.Decorate(
51364          options.selectionAdapter,
51365          AllowClear
51366        );
51367      }
51368
51369      if (options.multiple) {
51370        options.selectionAdapter = Utils.Decorate(
51371          options.selectionAdapter,
51372          SelectionSearch
51373        );
51374      }
51375
51376      if (
51377        options.containerCssClass != null ||
51378        options.containerCss != null ||
51379        options.adaptContainerCssClass != null
51380      ) {
51381        var ContainerCSS = require(options.amdBase + 'compat/containerCss');
51382
51383        options.selectionAdapter = Utils.Decorate(
51384          options.selectionAdapter,
51385          ContainerCSS
51386        );
51387      }
51388
51389      options.selectionAdapter = Utils.Decorate(
51390        options.selectionAdapter,
51391        EventRelay
51392      );
51393    }
51394
51395    // If the defaults were not previously applied from an element, it is
51396    // possible for the language option to have not been resolved
51397    options.language = this._resolveLanguage(options.language);
51398
51399    // Always fall back to English since it will always be complete
51400    options.language.push('en');
51401
51402    var uniqueLanguages = [];
51403
51404    for (var l = 0; l < options.language.length; l++) {
51405      var language = options.language[l];
51406
51407      if (uniqueLanguages.indexOf(language) === -1) {
51408        uniqueLanguages.push(language);
51409      }
51410    }
51411
51412    options.language = uniqueLanguages;
51413
51414    options.translations = this._processTranslations(
51415      options.language,
51416      options.debug
51417    );
51418
51419    return options;
51420  };
51421
51422  Defaults.prototype.reset = function () {
51423    function stripDiacritics (text) {
51424      // Used 'uni range + named function' from http://jsperf.com/diacritics/18
51425      function match(a) {
51426        return DIACRITICS[a] || a;
51427      }
51428
51429      return text.replace(/[^\u0000-\u007E]/g, match);
51430    }
51431
51432    function matcher (params, data) {
51433      // Always return the object if there is nothing to compare
51434      if ($.trim(params.term) === '') {
51435        return data;
51436      }
51437
51438      // Do a recursive check for options with children
51439      if (data.children && data.children.length > 0) {
51440        // Clone the data object if there are children
51441        // This is required as we modify the object to remove any non-matches
51442        var match = $.extend(true, {}, data);
51443
51444        // Check each child of the option
51445        for (var c = data.children.length - 1; c >= 0; c--) {
51446          var child = data.children[c];
51447
51448          var matches = matcher(params, child);
51449
51450          // If there wasn't a match, remove the object in the array
51451          if (matches == null) {
51452            match.children.splice(c, 1);
51453          }
51454        }
51455
51456        // If any children matched, return the new object
51457        if (match.children.length > 0) {
51458          return match;
51459        }
51460
51461        // If there were no matching children, check just the plain object
51462        return matcher(params, match);
51463      }
51464
51465      var original = stripDiacritics(data.text).toUpperCase();
51466      var term = stripDiacritics(params.term).toUpperCase();
51467
51468      // Check if the text contains the term
51469      if (original.indexOf(term) > -1) {
51470        return data;
51471      }
51472
51473      // If it doesn't contain the term, don't return anything
51474      return null;
51475    }
51476
51477    this.defaults = {
51478      amdBase: './',
51479      amdLanguageBase: './i18n/',
51480      closeOnSelect: true,
51481      debug: false,
51482      dropdownAutoWidth: false,
51483      escapeMarkup: Utils.escapeMarkup,
51484      language: {},
51485      matcher: matcher,
51486      minimumInputLength: 0,
51487      maximumInputLength: 0,
51488      maximumSelectionLength: 0,
51489      minimumResultsForSearch: 0,
51490      selectOnClose: false,
51491      scrollAfterSelect: false,
51492      sorter: function (data) {
51493        return data;
51494      },
51495      templateResult: function (result) {
51496        return result.text;
51497      },
51498      templateSelection: function (selection) {
51499        return selection.text;
51500      },
51501      theme: 'default',
51502      width: 'resolve'
51503    };
51504  };
51505
51506  Defaults.prototype.applyFromElement = function (options, $element) {
51507    var optionLanguage = options.language;
51508    var defaultLanguage = this.defaults.language;
51509    var elementLanguage = $element.prop('lang');
51510    var parentLanguage = $element.closest('[lang]').prop('lang');
51511
51512    var languages = Array.prototype.concat.call(
51513      this._resolveLanguage(elementLanguage),
51514      this._resolveLanguage(optionLanguage),
51515      this._resolveLanguage(defaultLanguage),
51516      this._resolveLanguage(parentLanguage)
51517    );
51518
51519    options.language = languages;
51520
51521    return options;
51522  };
51523
51524  Defaults.prototype._resolveLanguage = function (language) {
51525    if (!language) {
51526      return [];
51527    }
51528
51529    if ($.isEmptyObject(language)) {
51530      return [];
51531    }
51532
51533    if ($.isPlainObject(language)) {
51534      return [language];
51535    }
51536
51537    var languages;
51538
51539    if (!$.isArray(language)) {
51540      languages = [language];
51541    } else {
51542      languages = language;
51543    }
51544
51545    var resolvedLanguages = [];
51546
51547    for (var l = 0; l < languages.length; l++) {
51548      resolvedLanguages.push(languages[l]);
51549
51550      if (typeof languages[l] === 'string' && languages[l].indexOf('-') > 0) {
51551        // Extract the region information if it is included
51552        var languageParts = languages[l].split('-');
51553        var baseLanguage = languageParts[0];
51554
51555        resolvedLanguages.push(baseLanguage);
51556      }
51557    }
51558
51559    return resolvedLanguages;
51560  };
51561
51562  Defaults.prototype._processTranslations = function (languages, debug) {
51563    var translations = new Translation();
51564
51565    for (var l = 0; l < languages.length; l++) {
51566      var languageData = new Translation();
51567
51568      var language = languages[l];
51569
51570      if (typeof language === 'string') {
51571        try {
51572          // Try to load it with the original name
51573          languageData = Translation.loadPath(language);
51574        } catch (e) {
51575          try {
51576            // If we couldn't load it, check if it wasn't the full path
51577            language = this.defaults.amdLanguageBase + language;
51578            languageData = Translation.loadPath(language);
51579          } catch (ex) {
51580            // The translation could not be loaded at all. Sometimes this is
51581            // because of a configuration problem, other times this can be
51582            // because of how Select2 helps load all possible translation files
51583            if (debug && window.console && console.warn) {
51584              console.warn(
51585                'Select2: The language file for "' + language + '" could ' +
51586                'not be automatically loaded. A fallback will be used instead.'
51587              );
51588            }
51589          }
51590        }
51591      } else if ($.isPlainObject(language)) {
51592        languageData = new Translation(language);
51593      } else {
51594        languageData = language;
51595      }
51596
51597      translations.extend(languageData);
51598    }
51599
51600    return translations;
51601  };
51602
51603  Defaults.prototype.set = function (key, value) {
51604    var camelKey = $.camelCase(key);
51605
51606    var data = {};
51607    data[camelKey] = value;
51608
51609    var convertedData = Utils._convertData(data);
51610
51611    $.extend(true, this.defaults, convertedData);
51612  };
51613
51614  var defaults = new Defaults();
51615
51616  return defaults;
51617});
51618
51619S2.define('select2/options',[
51620  'require',
51621  'jquery',
51622  './defaults',
51623  './utils'
51624], function (require, $, Defaults, Utils) {
51625  function Options (options, $element) {
51626    this.options = options;
51627
51628    if ($element != null) {
51629      this.fromElement($element);
51630    }
51631
51632    if ($element != null) {
51633      this.options = Defaults.applyFromElement(this.options, $element);
51634    }
51635
51636    this.options = Defaults.apply(this.options);
51637
51638    if ($element && $element.is('input')) {
51639      var InputCompat = require(this.get('amdBase') + 'compat/inputData');
51640
51641      this.options.dataAdapter = Utils.Decorate(
51642        this.options.dataAdapter,
51643        InputCompat
51644      );
51645    }
51646  }
51647
51648  Options.prototype.fromElement = function ($e) {
51649    var excludedData = ['select2'];
51650
51651    if (this.options.multiple == null) {
51652      this.options.multiple = $e.prop('multiple');
51653    }
51654
51655    if (this.options.disabled == null) {
51656      this.options.disabled = $e.prop('disabled');
51657    }
51658
51659    if (this.options.dir == null) {
51660      if ($e.prop('dir')) {
51661        this.options.dir = $e.prop('dir');
51662      } else if ($e.closest('[dir]').prop('dir')) {
51663        this.options.dir = $e.closest('[dir]').prop('dir');
51664      } else {
51665        this.options.dir = 'ltr';
51666      }
51667    }
51668
51669    $e.prop('disabled', this.options.disabled);
51670    $e.prop('multiple', this.options.multiple);
51671
51672    if (Utils.GetData($e[0], 'select2Tags')) {
51673      if (this.options.debug && window.console && console.warn) {
51674        console.warn(
51675          'Select2: The `data-select2-tags` attribute has been changed to ' +
51676          'use the `data-data` and `data-tags="true"` attributes and will be ' +
51677          'removed in future versions of Select2.'
51678        );
51679      }
51680
51681      Utils.StoreData($e[0], 'data', Utils.GetData($e[0], 'select2Tags'));
51682      Utils.StoreData($e[0], 'tags', true);
51683    }
51684
51685    if (Utils.GetData($e[0], 'ajaxUrl')) {
51686      if (this.options.debug && window.console && console.warn) {
51687        console.warn(
51688          'Select2: The `data-ajax-url` attribute has been changed to ' +
51689          '`data-ajax--url` and support for the old attribute will be removed' +
51690          ' in future versions of Select2.'
51691        );
51692      }
51693
51694      $e.attr('ajax--url', Utils.GetData($e[0], 'ajaxUrl'));
51695      Utils.StoreData($e[0], 'ajax-Url', Utils.GetData($e[0], 'ajaxUrl'));
51696    }
51697
51698    var dataset = {};
51699
51700    function upperCaseLetter(_, letter) {
51701      return letter.toUpperCase();
51702    }
51703
51704    // Pre-load all of the attributes which are prefixed with `data-`
51705    for (var attr = 0; attr < $e[0].attributes.length; attr++) {
51706      var attributeName = $e[0].attributes[attr].name;
51707      var prefix = 'data-';
51708
51709      if (attributeName.substr(0, prefix.length) == prefix) {
51710        // Get the contents of the attribute after `data-`
51711        var dataName = attributeName.substring(prefix.length);
51712
51713        // Get the data contents from the consistent source
51714        // This is more than likely the jQuery data helper
51715        var dataValue = Utils.GetData($e[0], dataName);
51716
51717        // camelCase the attribute name to match the spec
51718        var camelDataName = dataName.replace(/-([a-z])/g, upperCaseLetter);
51719
51720        // Store the data attribute contents into the dataset since
51721        dataset[camelDataName] = dataValue;
51722      }
51723    }
51724
51725    // Prefer the element's `dataset` attribute if it exists
51726    // jQuery 1.x does not correctly handle data attributes with multiple dashes
51727    if ($.fn.jquery && $.fn.jquery.substr(0, 2) == '1.' && $e[0].dataset) {
51728      dataset = $.extend(true, {}, $e[0].dataset, dataset);
51729    }
51730
51731    // Prefer our internal data cache if it exists
51732    var data = $.extend(true, {}, Utils.GetData($e[0]), dataset);
51733
51734    data = Utils._convertData(data);
51735
51736    for (var key in data) {
51737      if ($.inArray(key, excludedData) > -1) {
51738        continue;
51739      }
51740
51741      if ($.isPlainObject(this.options[key])) {
51742        $.extend(this.options[key], data[key]);
51743      } else {
51744        this.options[key] = data[key];
51745      }
51746    }
51747
51748    return this;
51749  };
51750
51751  Options.prototype.get = function (key) {
51752    return this.options[key];
51753  };
51754
51755  Options.prototype.set = function (key, val) {
51756    this.options[key] = val;
51757  };
51758
51759  return Options;
51760});
51761
51762S2.define('select2/core',[
51763  'jquery',
51764  './options',
51765  './utils',
51766  './keys'
51767], function ($, Options, Utils, KEYS) {
51768  var Select2 = function ($element, options) {
51769    if (Utils.GetData($element[0], 'select2') != null) {
51770      Utils.GetData($element[0], 'select2').destroy();
51771    }
51772
51773    this.$element = $element;
51774
51775    this.id = this._generateId($element);
51776
51777    options = options || {};
51778
51779    this.options = new Options(options, $element);
51780
51781    Select2.__super__.constructor.call(this);
51782
51783    // Set up the tabindex
51784
51785    var tabindex = $element.attr('tabindex') || 0;
51786    Utils.StoreData($element[0], 'old-tabindex', tabindex);
51787    $element.attr('tabindex', '-1');
51788
51789    // Set up containers and adapters
51790
51791    var DataAdapter = this.options.get('dataAdapter');
51792    this.dataAdapter = new DataAdapter($element, this.options);
51793
51794    var $container = this.render();
51795
51796    this._placeContainer($container);
51797
51798    var SelectionAdapter = this.options.get('selectionAdapter');
51799    this.selection = new SelectionAdapter($element, this.options);
51800    this.$selection = this.selection.render();
51801
51802    this.selection.position(this.$selection, $container);
51803
51804    var DropdownAdapter = this.options.get('dropdownAdapter');
51805    this.dropdown = new DropdownAdapter($element, this.options);
51806    this.$dropdown = this.dropdown.render();
51807
51808    this.dropdown.position(this.$dropdown, $container);
51809
51810    var ResultsAdapter = this.options.get('resultsAdapter');
51811    this.results = new ResultsAdapter($element, this.options, this.dataAdapter);
51812    this.$results = this.results.render();
51813
51814    this.results.position(this.$results, this.$dropdown);
51815
51816    // Bind events
51817
51818    var self = this;
51819
51820    // Bind the container to all of the adapters
51821    this._bindAdapters();
51822
51823    // Register any DOM event handlers
51824    this._registerDomEvents();
51825
51826    // Register any internal event handlers
51827    this._registerDataEvents();
51828    this._registerSelectionEvents();
51829    this._registerDropdownEvents();
51830    this._registerResultsEvents();
51831    this._registerEvents();
51832
51833    // Set the initial state
51834    this.dataAdapter.current(function (initialData) {
51835      self.trigger('selection:update', {
51836        data: initialData
51837      });
51838    });
51839
51840    // Hide the original select
51841    $element.addClass('select2-hidden-accessible');
51842    $element.attr('aria-hidden', 'true');
51843
51844    // Synchronize any monitored attributes
51845    this._syncAttributes();
51846
51847    Utils.StoreData($element[0], 'select2', this);
51848
51849    // Ensure backwards compatibility with $element.data('select2').
51850    $element.data('select2', this);
51851  };
51852
51853  Utils.Extend(Select2, Utils.Observable);
51854
51855  Select2.prototype._generateId = function ($element) {
51856    var id = '';
51857
51858    if ($element.attr('id') != null) {
51859      id = $element.attr('id');
51860    } else if ($element.attr('name') != null) {
51861      id = $element.attr('name') + '-' + Utils.generateChars(2);
51862    } else {
51863      id = Utils.generateChars(4);
51864    }
51865
51866    id = id.replace(/(:|\.|\[|\]|,)/g, '');
51867    id = 'select2-' + id;
51868
51869    return id;
51870  };
51871
51872  Select2.prototype._placeContainer = function ($container) {
51873    $container.insertAfter(this.$element);
51874
51875    var width = this._resolveWidth(this.$element, this.options.get('width'));
51876
51877    if (width != null) {
51878      $container.css('width', width);
51879    }
51880  };
51881
51882  Select2.prototype._resolveWidth = function ($element, method) {
51883    var WIDTH = /^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i;
51884
51885    if (method == 'resolve') {
51886      var styleWidth = this._resolveWidth($element, 'style');
51887
51888      if (styleWidth != null) {
51889        return styleWidth;
51890      }
51891
51892      return this._resolveWidth($element, 'element');
51893    }
51894
51895    if (method == 'element') {
51896      var elementWidth = $element.outerWidth(false);
51897
51898      if (elementWidth <= 0) {
51899        return 'auto';
51900      }
51901
51902      return elementWidth + 'px';
51903    }
51904
51905    if (method == 'style') {
51906      var style = $element.attr('style');
51907
51908      if (typeof(style) !== 'string') {
51909        return null;
51910      }
51911
51912      var attrs = style.split(';');
51913
51914      for (var i = 0, l = attrs.length; i < l; i = i + 1) {
51915        var attr = attrs[i].replace(/\s/g, '');
51916        var matches = attr.match(WIDTH);
51917
51918        if (matches !== null && matches.length >= 1) {
51919          return matches[1];
51920        }
51921      }
51922
51923      return null;
51924    }
51925
51926    if (method == 'computedstyle') {
51927      var computedStyle = window.getComputedStyle($element[0]);
51928
51929      return computedStyle.width;
51930    }
51931
51932    return method;
51933  };
51934
51935  Select2.prototype._bindAdapters = function () {
51936    this.dataAdapter.bind(this, this.$container);
51937    this.selection.bind(this, this.$container);
51938
51939    this.dropdown.bind(this, this.$container);
51940    this.results.bind(this, this.$container);
51941  };
51942
51943  Select2.prototype._registerDomEvents = function () {
51944    var self = this;
51945
51946    this.$element.on('change.select2', function () {
51947      self.dataAdapter.current(function (data) {
51948        self.trigger('selection:update', {
51949          data: data
51950        });
51951      });
51952    });
51953
51954    this.$element.on('focus.select2', function (evt) {
51955      self.trigger('focus', evt);
51956    });
51957
51958    this._syncA = Utils.bind(this._syncAttributes, this);
51959    this._syncS = Utils.bind(this._syncSubtree, this);
51960
51961    if (this.$element[0].attachEvent) {
51962      this.$element[0].attachEvent('onpropertychange', this._syncA);
51963    }
51964
51965    var observer = window.MutationObserver ||
51966      window.WebKitMutationObserver ||
51967      window.MozMutationObserver
51968    ;
51969
51970    if (observer != null) {
51971      this._observer = new observer(function (mutations) {
51972        self._syncA();
51973        self._syncS(null, mutations);
51974      });
51975      this._observer.observe(this.$element[0], {
51976        attributes: true,
51977        childList: true,
51978        subtree: false
51979      });
51980    } else if (this.$element[0].addEventListener) {
51981      this.$element[0].addEventListener(
51982        'DOMAttrModified',
51983        self._syncA,
51984        false
51985      );
51986      this.$element[0].addEventListener(
51987        'DOMNodeInserted',
51988        self._syncS,
51989        false
51990      );
51991      this.$element[0].addEventListener(
51992        'DOMNodeRemoved',
51993        self._syncS,
51994        false
51995      );
51996    }
51997  };
51998
51999  Select2.prototype._registerDataEvents = function () {
52000    var self = this;
52001
52002    this.dataAdapter.on('*', function (name, params) {
52003      self.trigger(name, params);
52004    });
52005  };
52006
52007  Select2.prototype._registerSelectionEvents = function () {
52008    var self = this;
52009    var nonRelayEvents = ['toggle', 'focus'];
52010
52011    this.selection.on('toggle', function () {
52012      self.toggleDropdown();
52013    });
52014
52015    this.selection.on('focus', function (params) {
52016      self.focus(params);
52017    });
52018
52019    this.selection.on('*', function (name, params) {
52020      if ($.inArray(name, nonRelayEvents) !== -1) {
52021        return;
52022      }
52023
52024      self.trigger(name, params);
52025    });
52026  };
52027
52028  Select2.prototype._registerDropdownEvents = function () {
52029    var self = this;
52030
52031    this.dropdown.on('*', fu
52031nction (name, params) {
52032      self.trigger(name, params);
52033    });
52034  };
52035
52036  Select2.prototype._registerResultsEvents = function () {
52037    var self = this;
52038
52039    this.results.on('*', function (name, params) {
52040      self.trigger(name, params);
52041    });
52042  };
52043
52044  Select2.prototype._registerEvents = function () {
52045    var self = this;
52046
52047    this.on('open', function () {
52048      self.$container.addClass('select2-container--open');
52049    });
52050
52051    this.on('close', function () {
52052      self.$container.removeClass('select2-container--open');
52053    });
52054
52055    this.on('enable', function () {
52056      self.$container.removeClass('select2-container--disabled');
52057    });
52058
52059    this.on('disable', function () {
52060      self.$container.addClass('select2-container--disabled');
52061    });
52062
52063    this.on('blur', function () {
52064      self.$container.removeClass('select2-container--focus');
52065    });
52066
52067    this.on('query', function (params) {
52068      if (!self.isOpen()) {
52069        self.trigger('open', {});
52070      }
52071
52072      this.dataAdapter.query(params, function (data) {
52073        self.trigger('results:all', {
52074          data: data,
52075          query: params
52076        });
52077      });
52078    });
52079
52080    this.on('query:append', function (params) {
52081      this.dataAdapter.query(params, function (data) {
52082        self.trigger('results:append', {
52083          data: data,
52084          query: params
52085        });
52086      });
52087    });
52088
52089    this.on('keypress', function (evt) {
52090      var key = evt.which;
52091
52092      if (self.isOpen()) {
52093        if (key === KEYS.ESC || key === KEYS.TAB ||
52094            (key === KEYS.UP && evt.altKey)) {
52095          self.close(evt);
52096
52097          evt.preventDefault();
52098        } else if (key === KEYS.ENTER) {
52099          self.trigger('results:select', {});
52100
52101          evt.preventDefault();
52102        } else if ((key === KEYS.SPACE && evt.ctrlKey)) {
52103          self.trigger('results:toggle', {});
52104
52105          evt.preventDefault();
52106        } else if (key === KEYS.UP) {
52107          self.trigger('results:previous', {});
52108
52109          evt.preventDefault();
52110        } else if (key === KEYS.DOWN) {
52111          self.trigger('results:next', {});
52112
52113          evt.preventDefault();
52114        }
52115      } else {
52116        if (key === KEYS.ENTER || key === KEYS.SPACE ||
52117            (key === KEYS.DOWN && evt.altKey)) {
52118          self.open();
52119
52120          evt.preventDefault();
52121        }
52122      }
52123    });
52124  };
52125
52126  Select2.prototype._syncAttributes = function () {
52127    this.options.set('disabled', this.$element.prop('disabled'));
52128
52129    if (this.isDisabled()) {
52130      if (this.isOpen()) {
52131        this.close();
52132      }
52133
52134      this.trigger('disable', {});
52135    } else {
52136      this.trigger('enable', {});
52137    }
52138  };
52139
52140  Select2.prototype._isChangeMutation = function (evt, mutations) {
52141    var changed = false;
52142    var self = this;
52143
52144    // Ignore any mutation events raised for elements that aren't options or
52145    // optgroups. This handles the case when the select element is destroyed
52146    if (
52147      evt && evt.target && (
52148        evt.target.nodeName !== 'OPTION' && evt.target.nodeName !== 'OPTGROUP'
52149      )
52150    ) {
52151      return;
52152    }
52153
52154    if (!mutations) {
52155      // If mutation events aren't supported, then we can only assume that the
52156      // change affected the selections
52157      changed = true;
52158    } else if (mutations.addedNodes && mutations.addedNodes.length > 0) {
52159      for (var n = 0; n < mutations.addedNodes.length; n++) {
52160        var node = mutations.addedNodes[n];
52161
52162        if (node.selected) {
52163          changed = true;
52164        }
52165      }
52166    } else if (mutations.removedNodes && mutations.removedNodes.length > 0) {
52167      changed = true;
52168    } else if ($.isArray(mutations)) {
52169      $.each(mutations, function(evt, mutation) {
52170        if (self._isChangeMutation(evt, mutation)) {
52171          // We've found a change mutation.
52172          // Let's escape from the loop and continue
52173          changed = true;
52174          return false;
52175        }
52176      });
52177    }
52178    return changed;
52179  };
52180
52181  Select2.prototype._syncSubtree = function (evt, mutations) {
52182    var changed = this._isChangeMutation(evt, mutations);
52183    var self = this;
52184
52185    // Only re-pull the data if we think there is a change
52186    if (changed) {
52187      this.dataAdapter.current(function (currentData) {
52188        self.trigger('selection:update', {
52189          data: currentData
52190        });
52191      });
52192    }
52193  };
52194
52195  /**
52196   * Override the trigger method to automatically trigger pre-events when
52197   * there are events that can be prevented.
52198   */
52199  Select2.prototype.trigger = function (name, args) {
52200    var actualTrigger = Select2.__super__.trigger;
52201    var preTriggerMap = {
52202      'open': 'opening',
52203      'close': 'closing',
52204      'select': 'selecting',
52205      'unselect': 'unselecting',
52206      'clear': 'clearing'
52207    };
52208
52209    if (args === undefined) {
52210      args = {};
52211    }
52212
52213    if (name in preTriggerMap) {
52214      var preTriggerName = preTriggerMap[name];
52215      var preTriggerArgs = {
52216        prevented: false,
52217        name: name,
52218        args: args
52219      };
52220
52221      actualTrigger.call(this, preTriggerName, preTriggerArgs);
52222
52223      if (preTriggerArgs.prevented) {
52224        args.prevented = true;
52225
52226        return;
52227      }
52228    }
52229
52230    actualTrigger.call(this, name, args);
52231  };
52232
52233  Select2.prototype.toggleDropdown = function () {
52234    if (this.isDisabled()) {
52235      return;
52236    }
52237
52238    if (this.isOpen()) {
52239      this.close();
52240    } else {
52241      this.open();
52242    }
52243  };
52244
52245  Select2.prototype.open = function () {
52246    if (this.isOpen()) {
52247      return;
52248    }
52249
52250    if (this.isDisabled()) {
52251      return;
52252    }
52253
52254    this.trigger('query', {});
52255  };
52256
52257  Select2.prototype.close = function (evt) {
52258    if (!this.isOpen()) {
52259      return;
52260    }
52261
52262    this.trigger('close', { originalEvent : evt });
52263  };
52264
52265  /**
52266   * Helper method to abstract the "enabled" (not "disabled") state of this
52267   * object.
52268   *
52269   * @return {true} if the instance is not disabled.
52270   * @return {false} if the instance is disabled.
52271   */
52272  Select2.prototype.isEnabled = function () {
52273    return !this.isDisabled();
52274  };
52275
52276  /**
52277   * Helper method to abstract the "disabled" state of this object.
52278   *
52279   * @return {true} if the disabled option is true.
52280   * @return {false} if the disabled option is false.
52281   */
52282  Select2.prototype.isDisabled = function () {
52283    return this.options.get('disabled');
52284  };
52285
52286  Select2.prototype.isOpen = function () {
52287    return this.$container.hasClass('select2-container--open');
52288  };
52289
52290  Select2.prototype.hasFocus = function () {
52291    return this.$container.hasClass('select2-container--focus');
52292  };
52293
52294  Select2.prototype.focus = function (data) {
52295    // No need to re-trigger focus events if we are already focused
52296    if (this.hasFocus()) {
52297      return;
52298    }
52299
52300    this.$container.addClass('select2-container--focus');
52301    this.trigger('focus', {});
52302  };
52303
52304  Select2.prototype.enable = function (args) {
52305    if (this.options.get('debug') && window.console && console.warn) {
52306      console.warn(
52307        'Select2: The `select2("enable")` method has been deprecated and will' +
52308        ' be removed in later Select2 versions. Use $element.prop("disabled")' +
52309        ' instead.'
52310      );
52311    }
52312
52313    if (args == null || args.length === 0) {
52314      args = [true];
52315    }
52316
52317    var disabled = !args[0];
52318
52319    this.$element.prop('disabled', disabled);
52320  };
52321
52322  Select2.prototype.data = function () {
52323    if (this.options.get('debug') &&
52324        arguments.length > 0 && window.console && console.warn) {
52325      console.warn(
52326        'Select2: Data can no longer be set using `select2("data")`. You ' +
52327        'should consider setting the value instead using `$element.val()`.'
52328      );
52329    }
52330
52331    var data = [];
52332
52333    this.dataAdapter.current(function (currentData) {
52334      data = currentData;
52335    });
52336
52337    return data;
52338  };
52339
52340  Select2.prototype.val = function (args) {
52341    if (this.options.get('debug') && window.console && console.warn) {
52342      console.warn(
52343        'Select2: The `select2("val")` method has been deprecated and will be' +
52344        ' removed in later Select2 versions. Use $element.val() instead.'
52345      );
52346    }
52347
52348    if (args == null || args.length === 0) {
52349      return this.$element.val();
52350    }
52351
52352    var newVal = args[0];
52353
52354    if ($.isArray(newVal)) {
52355      newVal = $.map(newVal, function (obj) {
52356        return obj.toString();
52357      });
52358    }
52359
52360    this.$element.val(newVal).trigger('input').trigger('change');
52361  };
52362
52363  Select2.prototype.destroy = function () {
52364    this.$container.remove();
52365
52366    if (this.$element[0].detachEvent) {
52367      this.$element[0].detachEvent('onpropertychange', this._syncA);
52368    }
52369
52370    if (this._observer != null) {
52371      this._observer.disconnect();
52372      this._observer = null;
52373    } else if (this.$element[0].removeEventListener) {
52374      this.$element[0]
52375        .removeEventListener('DOMAttrModified', this._syncA, false);
52376      this.$element[0]
52377        .removeEventListener('DOMNodeInserted', this._syncS, false);
52378      this.$element[0]
52379        .removeEventListener('DOMNodeRemoved', this._syncS, false);
52380    }
52381
52382    this._syncA = null;
52383    this._syncS = null;
52384
52385    this.$element.off('.select2');
52386    this.$element.attr('tabindex',
52387    Utils.GetData(this.$element[0], 'old-tabindex'));
52388
52389    this.$element.removeClass('select2-hidden-accessible');
52390    this.$element.attr('aria-hidden', 'false');
52391    Utils.RemoveData(this.$element[0]);
52392    this.$element.removeData('select2');
52393
52394    this.dataAdapter.destroy();
52395    this.selection.destroy();
52396    this.dropdown.destroy();
52397    this.results.destroy();
52398
52399    this.dataAdapter = null;
52400    this.selection = null;
52401    this.dropdown = null;
52402    this.results = null;
52403  };
52404
52405  Select2.prototype.render = function () {
52406    var $container = $(
52407      '<span class="select2 select2-container">' +
52408        '<span class="selection"></span>' +
52409        '<span class="dropdown-wrapper" aria-hidden="true"></span>' +
52410      '</span>'
52411    );
52412
52413    $container.attr('dir', this.options.get('dir'));
52414
52415    this.$container = $container;
52416
52417    this.$container.addClass('select2-container--' + this.options.get('theme'));
52418
52419    Utils.StoreData($container[0], 'element', this.$element);
52420
52421    return $container;
52422  };
52423
52424  return Select2;
52425});
52426
52427S2.define('jquery-mousewheel',[
52428  'jquery'
52429], function ($) {
52430  // Used to shim jQuery.mousewheel for non-full builds.
52431  return $;
52432});
52433
52434S2.define('jquery.select2',[
52435  'jquery',
52436  'jquery-mousewheel',
52437
52438  './select2/core',
52439  './select2/defaults',
52440  './select2/utils'
52441], function ($, _, Select2, Defaults, Utils) {
52442  if ($.fn.select2 == null) {
52443    // All methods that should return the element
52444    var thisMethods = ['open', 'close', 'destroy'];
52445
52446    $.fn.select2 = function (options) {
52447      options = options || {};
52448
52449      if (typeof options === 'object') {
52450        this.each(function () {
52451          var instanceOptions = $.extend(true, {}, options);
52452
52453          var instance = new Select2($(this), instanceOptions);
52454        });
52455
52456        return this;
52457      } else if (typeof options === 'string') {
52458        var ret;
52459        var args = Array.prototype.slice.call(arguments, 1);
52460
52461        this.each(function () {
52462          var instance = Utils.GetData(this, 'select2');
52463
52464          if (instance == null && window.console && console.error) {
52465            console.error(
52466              'The select2(\'' + options + '\') method was called on an ' +
52467              'element that is not using Select2.'
52468            );
52469          }
52470
52471          ret = instance[options].apply(instance, args);
52472        });
52473
52474        // Check if we should be returning `this`
52475        if ($.inArray(options, thisMethods) > -1) {
52476          return this;
52477        }
52478
52479        return ret;
52480      } else {
52481        throw new Error('Invalid arguments for Select2: ' + options);
52482      }
52483    };
52484  }
52485
52486  if ($.fn.select2.defaults == null) {
52487    $.fn.select2.defaults = Defaults;
52488  }
52489
52490  return Select2;
52491});
52492
52493  // Return the AMD loader configuration so it can be used outside of this file
52494  return {
52495    define: S2.define,
52496    require: S2.require
52497  };
52498}());
52499
52500  // Autoload the jQuery bindings
52501  // We know that all of the modules exist above this, so we're safe
52502  var select2 = S2.require('jquery.select2');
52503
52504  // Hold the AMD module references on the jQuery function that was just loaded
52505  // This allows Select2 to use the internal loader outside of this file, such
52506  // as in the language files.
52507  jQuery.fn.select2.amd = S2;
52508
52509  // Return the Select2 instance for anyone who is importing it.
52510  return select2;
52511}));
52512
52513/*! Select2 4.0.13 | https://github.com/select2/select2/blob/master/LICENSE.md */
52514
52515!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/sv",[],function(){return{errorLoading:function(){return"Resultat kunde inte laddas."},inputTooLong:function(n){return"Vänligen sudda ut "+(n.input.length-n.maximum)+" tecken"},inputTooShort:function(n){return"Vänligen skriv in "+(n.minimum-n.input.length)+" eller fler tecken"}
vendor: 10,360 bytes, lines 52515-52878
52515,loadingMore:function(){return"Laddar fler resultat…"},maximumSelected:function(n){return"Du kan max välja "+n.maximum+" element"},noResults:function(){return"Inga träffar"},searching:function(){return"Söker…"},removeAllItems:function(){return"Ta bort alla objekt"}}}),n.define,n.require}();
52516(function (factory) {
52517  if (typeof define === "function" && define.amd) {
52518    // AMD. Register as an anonymous module.
52519    define([], factory);
52520  } else if (typeof exports === "object") {
52521    // Node/CommonJS
52522    module.exports = factory();
52523  } else {
52524    // Browser globals
52525    window.wNumb = factory();
52526  }
52527})(function () {
52528  "use strict";
52529
52530  var FormatOptions = ["decimals", "thousand", "mark", "prefix", "suffix", "encoder", "decoder", "negativeBefore", "negative", "edit", "undo"];
52531
52532  // General
52533
52534  // Reverse a string
52535  function strReverse(a) {
52536    return a.split("").reverse().join("");
52537  }
52538
52539  // Check if a string starts with a specified prefix.
52540  function strStartsWith(input, match) {
52541    return input.substring(0, match.length) === match;
52542  }
52543
52544  // Check is a string ends in a specified suffix.
52545  function strEndsWith(input, match) {
52546    return input.slice(-1 * match.length) === match;
52547  }
52548
52549  // Throw an error if formatting options are incompatible.
52550  function throwEqualError(F, a, b) {
52551    if ((F[a] || F[b]) && F[a] === F[b]) {
52552      throw new Error(a);
52553    }
52554  }
52555
52556  // Check if a number is finite and not NaN
52557  function isValidNumber(input) {
52558    return typeof input === "number" && isFinite(input);
52559  }
52560
52561  // Provide rounding-accurate toFixed method.
52562  // Borrowed: http://stackoverflow.com/a/21323330/775265
52563  function toFixed(value, exp) {
52564    value = value.toString().split("e");
52565    value = Math.round(+(value[0] + "e" + (value[1] ? +value[1] + exp : exp)));
52566    value = value.toString().split("e");
52567    return (+(value[0] + "e" + (value[1] ? +value[1] - exp : -exp))).toFixed(exp);
52568  }
52569
52570  // Formatting
52571
52572  // Accept a number as input, output formatted string.
52573  function formatTo(decimals, thousand, mark, prefix, suffix, encoder, decoder, negativeBefore, negative, edit, undo, input) {
52574    var originalInput = input,
52575      inputIsNegative,
52576      inputPieces,
52577      inputBase,
52578      inputDecimals = "",
52579      output = "";
52580
52581    // Apply user encoder to the input.
52582    // Expected outcome: number.
52583    if (encoder) {
52584      input = encoder(input);
52585    }
52586
52587    // Stop if no valid number was provided, the number is infinite or NaN.
52588    if (!isValidNumber(input)) {
52589      return false;
52590    }
52591
52592    // Rounding away decimals might cause a value of -0
52593    // when using very small ranges. Remove those cases.
52594    if (decimals !== false && parseFloat(input.toFixed(decimals)) === 0) {
52595      input = 0;
52596    }
52597
52598    // Formatting is done on absolute numbers,
52599    // decorated by an optional negative symbol.
52600    if (input < 0) {
52601      inputIsNegative = true;
52602      input = Math.abs(input);
52603    }
52604
52605    // Reduce the number of decimals to the specified option.
52606    if (decimals !== false) {
52607      input = toFixed(input, decimals);
52608    }
52609
52610    // Transform the number into a string, so it can be split.
52611    input = input.toString();
52612
52613    // Break the number on the decimal separator.
52614    if (input.indexOf(".") !== -1) {
52615      inputPieces = input.split(".");
52616
52617      inputBase = inputPieces[0];
52618
52619      if (mark) {
52620        inputDecimals = mark + inputPieces[1];
52621      }
52622    } else {
52623      // If it isn't split, the entire number will do.
52624      inputBase = input;
52625    }
52626
52627    // Group numbers in sets of three.
52628    if (thousand) {
52629      inputBase = strReverse(inputBase).match(/.{1,3}/g);
52630      inputBase = strReverse(inputBase.join(strReverse(thousand)));
52631    }
52632
52633    // If the number is negative, prefix with negation symbol.
52634    if (inputIsNegative && negativeBefore) {
52635      output += negativeBefore;
52636    }
52637
52638    // Prefix the number
52639    if (prefix) {
52640      output += prefix;
52641    }
52642
52643    // Normal negative option comes after the prefix. Defaults to '-'.
52644    if (inputIsNegative && negative) {
52645      output += negative;
52646    }
52647
52648    // Append the actual number.
52649    output += inputBase;
52650    output += inputDecimals;
52651
52652    // Apply the suffix.
52653    if (suffix) {
52654      output += suffix;
52655    }
52656
52657    // Run the output through a user-specified post-formatter.
52658    if (edit) {
52659      output = edit(output, originalInput);
52660    }
52661
52662    // All done.
52663    return output;
52664  }
52665
52666  // Accept a sting as input, output decoded number.
52667  function formatFrom(decimals, thousand, mark, prefix, suffix, encoder, decoder, negativeBefore, negative, edit, undo, input) {
52668    var originalInput = input,
52669      inputIsNegative,
52670      output = "";
52671
52672    // User defined pre-decoder. Result must be a non empty string.
52673    if (undo) {
52674      input = undo(input);
52675    }
52676
52677    // Test the input. Can't be empty.
52678    if (!input || typeof input !== "string") {
52679      return false;
52680    }
52681
52682    // If the string starts with the negativeBefore value: remove it.
52683    // Remember is was there, the number is negative.
52684    if (negativeBefore && strStartsWith(input, negativeBefore)) {
52685      input = input.replace(negativeBefore, "");
52686      inputIsNegative = true;
52687    }
52688
52689    // Repeat the same procedure for the prefix.
52690    if (prefix && strStartsWith(input, prefix)) {
52691      input = input.replace(prefix, "");
52692    }
52693
52694    // And again for negative.
52695    if (negative && strStartsWith(input, negative)) {
52696      input = input.replace(negative, "");
52697      inputIsNegative = true;
52698    }
52699
52700    // Remove the suffix.
52701    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/slice
52702    if (suffix && strEndsWith(input, suffix)) {
52703      input = input.slice(0, -1 * suffix.length);
52704    }
52705
52706    // Remove the thousand grouping.
52707    if (thousand) {
52708      input = input.split(thousand).join("");
52709    }
52710
52711    // Set the decimal separator back to period.
52712    if (mark) {
52713      input = input.replace(mark, ".");
52714    }
52715
52716    // Prepend the negative symbol.
52717    if (inputIsNegative) {
52718      output += "-";
52719    }
52720
52721    // Add the number
52722    output += input;
52723
52724    // Trim all non-numeric characters (allow '.' and '-');
52725    output = output.replace(/[^0-9\.\-.]/g, "");
52726
52727    // The value contains no parse-able number.
52728    if (output === "") {
52729      return false;
52730    }
52731
52732    // Covert to number.
52733    output = Number(output);
52734
52735    // Run the user-specified post-decoder.
52736    if (decoder) {
52737      output = decoder(output);
52738    }
52739
52740    // Check is the output is valid, otherwise: return false.
52741    if (!isValidNumber(output)) {
52742      return false;
52743    }
52744
52745    return output;
52746  }
52747
52748  // Framework
52749
52750  // Validate formatting options
52751  function validate(inputOptions) {
52752    var i,
52753      optionName,
52754      optionValue,
52755      filteredOptions = {};
52756
52757    if (inputOptions["suffix"] === undefined) {
52758      inputOptions["suffix"] = inputOptions["postfix"];
52759    }
52760
52761    for (i = 0; i < FormatOptions.length; i += 1) {
52762      optionName = FormatOptions[i];
52763      optionValue = inputOptions[optionName];
52764
52765      if (optionValue === undefined) {
52766        // Only default if negativeBefore isn't set.
52767        if (optionName === "negative" && !filteredOptions.negativeBefore) {
52768          filteredOptions[optionName] = "-";
52769          // Don't set a default for mark when 'thousand' is set.
52770        } else if (optionName === "mark" && filteredOptions.thousand !== ".") {
52771          filteredOptions[optionName] = ".";
52772        } else {
52773          filteredOptions[optionName] = false;
52774        }
52775
52776        // Floating points in JS are stable up to 7 decimals.
52777      } else if (optionName === "decimals") {
52778        if (optionValue >= 0 && optionValue < 8) {
52779          filteredOptions[optionName] = optionValue;
52780        } else {
52781          throw new Error(optionName);
52782        }
52783
52784        // These options, when provided, must be functions.
52785      } else if (optionName === "encoder" || optionName === "decoder" || optionName === "edit" || optionName === "undo") {
52786        if (typeof optionValue === "function") {
52787          filteredOptions[optionName] = optionValue;
52788        } else {
52789          throw new Error(optionName);
52790        }
52791
52792        // Other options are strings.
52793      } else {
52794        if (typeof optionValue === "string") {
52795          filteredOptions[optionName] = optionValue;
52796        } else {
52797          throw new Error(optionName);
52798        }
52799      }
52800    }
52801
52802    // Some values can't be extracted from a
52803    // string if certain combinations are present.
52804    throwEqualError(filteredOptions, "mark", "thousand");
52805    throwEqualError(filteredOptions, "prefix", "negative");
52806    throwEqualError(filteredOptions, "prefix", "negativeBefore");
52807
52808    return filteredOptions;
52809  }
52810
52811  // Pass all options as function arguments
52812  function passAll(options, method, input) {
52813    var i,
52814      args = [];
52815
52816    // Add all options in order of FormatOptions
52817    for (i = 0; i < FormatOptions.length; i += 1) {
52818      args.push(options[FormatOptions[i]]);
52819    }
52820
52821    // Append the input, then call the method, presenting all
52822    // options as arguments.
52823    args.push(input);
52824    return method.apply("", args);
52825  }
52826
52827  function wNumb(options) {
52828    if (!(this instanceof wNumb)) {
52829      return new wNumb(options);
52830    }
52831
52832    if (typeof options !== "object") {
52833      return;
52834    }
52835
52836    options = validate(options);
52837
52838    // Call 'formatTo' with proper arguments.
52839    this.to = function (input) {
52840      return passAll(options, formatTo, input);
52841    };
52842
52843    // Call 'formatFrom' with proper arguments.
52844    this.from = function (input) {
52845      return passAll(options, formatFrom, input);
52846    };
52847  }
52848
52849  return wNumb;
52850});
52851
52852/*! nouislider - 14.7.0 - 4/6/2021 */
52853(function(factory) {
52854    if (typeof define === "function" && define.amd) {
52855        define([], factory);
52856    } else if (typeof exports === "object") {
52857        module.exports = factory();
52858    } else {
52859        window.noUiSlider = factory();
52860    }
52861})(function() {
52862    "use strict";
52863    var VERSION = "14.7.0";
52864    //region Helper Methods
52865    function isValidFormatter(entry) {
52866        return typeof entry === "object" && typeof entry.to === "function" && typeof entry.from === "function";
52867    }
52868    function removeElement(el) {
52869        el.parentElement.removeChild(el);
52870    }
52871    function isSet(value) {
52872        return value !== null && value !== undefined;
52873    }
52874    // Bindable version
52875    function preventDefault(e) {
52876        e.preventDefault();
52877    }
52878    // Removes duplicates from an array.
vendor: 8,282 bytes, lines 52879-53101
52879    function unique(array) {
52880        return array.filter(function (a) {
52881            return !this[a] ? (this[a] = true) : false;
52882        }, {});
52883    }
52884    // Round a value to the closest 'to'.
52885    function closest(value, to) {
52886        return Math.round(value / to) * to;
52887    }
52888    // Current position of an element relative to the document.
52889    function offset(elem, orientation) {
52890        var rect = elem.getBoundingClientRect();
52891        var doc = elem.ownerDocument;
52892        var docElem = doc.documentElement;
52893        var pageOffset = getPageOffset(doc);
52894        // getBoundingClientRect contains left scroll in Chrome on Android.
52895        // I haven't found a feature detection that proves this. Worst case
52896        // scenario on mis-match: the 'tap' feature on horizontal sliders breaks.
52897        if (/webkit.*Chrome.*Mobile/i.test(navigator.userAgent)) {
52898            pageOffset.x = 0;
52899        }
52900        return orientation ? rect.top + pageOffset.y - docElem.clientTop : rect.left + pageOffset.x - docElem.clientLeft;
52901    }
52902    // Checks whether a value is numerical.
52903    function isNumeric(a) {
52904        return typeof a === "number" && !isNaN(a) && isFinite(a);
52905    }
52906    // Sets a class and removes it after [duration] ms.
52907    function addClassFor(element, className, duration) {
52908        if (duration > 0) {
52909            addClass(element, className);
52910            setTimeout(function () {
52911                removeClass(element, className);
52912            }, duration);
52913        }
52914    }
52915    // Limits a value to 0 - 100
52916    function limit(a) {
52917        return Math.max(Math.min(a, 100), 0);
52918    }
52919    // Wraps a variable as an array, if it isn't one yet.
52920    // Note that an input array is returned by reference!
52921    function asArray(a) {
52922        return Array.isArray(a) ? a : [a];
52923    }
52924    // Counts decimals
52925    function countDecimals(numStr) {
52926        numStr = String(numStr);
52927        var pieces = numStr.split(".");
52928        return pieces.length > 1 ? pieces[1].length : 0;
52929    }
52930    // http://youmightnotneedjquery.com/#add_class
52931    function addClass(el, className) {
52932        if (el.classList && !/\s/.test(className)) {
52933            el.classList.add(className);
52934        }
52935        else {
52936            el.className += " " + className;
52937        }
52938    }
52939    // http://youmightnotneedjquery.com/#remove_class
52940    function removeClass(el, className) {
52941        if (el.classList && !/\s/.test(className)) {
52942            el.classList.remove(className);
52943        }
52944        else {
52945            el.className = el.className.replace(new RegExp("(^|\\b)" + className.split(" ").join("|") + "(\\b|$)", "gi"), " ");
52946        }
52947    }
52948    // https://plainjs.com/javascript/attributes/adding-removing-and-testing-for-classes-9/
52949    function hasClass(el, className) {
52950        return el.classList ? el.classList.contains(className) : new RegExp("\\b" + className + "\\b").test(el.className);
52951    }
52952    // https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollY#Notes
52953    function getPageOffset(doc) {
52954        var supportPageOffset = window.pageXOffset !== undefined;
52955        var isCSS1Compat = (doc.compatMode || "") === "CSS1Compat";
52956        var x = supportPageOffset
52957            ? window.pageXOffset
52958            : isCSS1Compat
52959                ? doc.documentElement.scrollLeft
52960                : doc.body.scrollLeft;
52961        var y = supportPageOffset ? window.pageYOffset : isCSS1Compat ? doc.documentElement.scrollTop : doc.body.scrollTop;
52962        return {
52963            x: x,
52964            y: y
52965        };
52966    }
52967    // we provide a function to compute constants instead
52968    // of accessing window.* as soon as the module needs it
52969    // so that we do not compute anything if not needed
52970    function getActions() {
52971        // Determine the events to bind. IE11 implements pointerEvents without
52972        // a prefix, which breaks compatibility with the IE10 implementation.
52973        return window.navigator.pointerEnabled
52974            ? {
52975                start: "pointerdown",
52976                move: "pointermove",
52977                end: "pointerup"
52978            }
52979            : window.navigator.msPointerEnabled
52980                ? {
52981                    start: "MSPointerDown",
52982                    move: "MSPointerMove",
52983                    end: "MSPointerUp"
52984                }
52985                : {
52986                    start: "mousedown touchstart",
52987                    move: "mousemove touchmove",
52988                    end: "mouseup touchend"
52989                };
52990    }
52991    // https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md
52992    // Issue #785
52993    function getSupportsPassive() {
52994        var supportsPassive = false;
52995        /* eslint-disable */
52996        try {
52997            var opts = Object.defineProperty({}, "passive", {
52998                get: function () {
52999                    supportsPassive = true;
53000                }
53001            });
53002            window.addEventListener("test", null, opts);
53003        }
53004        catch (e) { }
53005        /* eslint-enable */
53006        return supportsPassive;
53007    }
53008    function getSupportsTouchActionNone() {
53009        return window.CSS && CSS.supports && CSS.supports("touch-action", "none");
53010    }
53011    //endregion
53012    //region Range Calculation
53013    // Determine the size of a sub-range in relation to a full range.
53014    function subRangeRatio(pa, pb) {
53015        return 100 / (pb - pa);
53016    }
53017    // (percentage) How many percent is this value of this range?
53018    function fromPercentage(range, value, startRange) {
53019        return (value * 100) / (range[startRange + 1] - range[startRange]);
53020    }
53021    // (percentage) Where is this value on this range?
53022    function toPercentage(range, value) {
53023        return fromPercentage(range, range[0] < 0 ? value + Math.abs(range[0]) : value - range[0], 0);
53024    }
53025    // (value) How much is this percentage on this range?
53026    function isPercentage(range, value) {
53027        return (value * (range[1] - range[0])) / 100 + range[0];
53028    }
53029    function getJ(value, arr) {
53030        var j = 1;
53031        while (value >= arr[j]) {
53032            j += 1;
53033        }
53034        return j;
53035    }
53036    // (percentage) Input a value, find where, on a scale of 0-100, it applies.
53037    function toStepping(xVal, xPct, value) {
53038        if (value >= xVal.slice(-1)[0]) {
53039            return 100;
53040        }
53041        var j = getJ(value, xVal);
53042        var va = xVal[j - 1];
53043        var vb = xVal[j];
53044        var pa = xPct[j - 1];
53045        var pb = xPct[j];
53046        return pa + toPercentage([va, vb], value) / subRangeRatio(pa, pb);
53047    }
53048    // (value) Input a percentage, find where it is on the specified range.
53049    function fromStepping(xVal, xPct, value) {
53050        // There is no range group that fits 100
53051        if (value >= 100) {
53052            return xVal.slice(-1)[0];
53053        }
53054        var j = getJ(value, xPct);
53055        var va = xVal[j - 1];
53056        var vb = xVal[j];
53057        var pa = xPct[j - 1];
53058        var pb = xPct[j];
53059        return isPercentage([va, vb], (value - pa) * subRangeRatio(pa, pb));
53060    }
53061    // (percentage) Get the step that applies at a certain value.
53062    function getStep(xPct, xSteps, snap, value) {
53063        if (value === 100) {
53064            return value;
53065        }
53066        var j = getJ(value, xPct);
53067        var a = xPct[j - 1];
53068        var b = xPct[j];
53069        // If 'snap' is set, steps are used as fixed points on the slider.
53070        if (snap) {
53071            // Find the closest position, a or b.
53072            if (value - a > (b - a) / 2) {
53073                return b;
53074            }
53075            return a;
53076        }
53077        if (!xSteps[j - 1]) {
53078            return value;
53079        }
53080        return xPct[j - 1] + closest(value - xPct[j - 1], xSteps[j - 1]);
53081    }
53082    function handleEntryPoint(index, value, that) {
53083        var percentage;
53084        // Wrap numerical input in an array.
53085        if (typeof value === "number") {
53086            value = [value];
53087        }
53088        // Reject any invalid input, by testing whether value is an array.
53089        if (!Array.isArray(value)) {
53090            throw new Error("noUiSlider (" + VERSION + "): 'range' contains invalid value.");
53091        }
53092        // Covert min/max syntax to 0 and 100.
53093        if (index === "min") {
53094            percentage = 0;
53095        }
53096        else if (index === "max") {
53097            percentage = 100;
53098        }
53099        else {
53100            percentage = parseFloat(index);
53101        }
53102        // Check for correct input.
53103        if (!isNumeric(percentage) || !isNumeric(value[0])) {
53104            throw new Error("noUiSlider (" + VERSION + "): 'range' value isn't numeric.");
53105        }
53106        // Store values.
53107        that.xPct.push(percentage);
53108        that.xVal.push(value[0]);
53109        // NaN will evaluate to false too, but to keep
53110        // logging clear, set step explicitly. Make sure
53111        // not to override the 'step' setting with false.
53112        if (!percentage) {
53113            if (!isNaN(value[1])) {
53114                that.xSteps[0] = value[1];
53115            }
53116        }
53117        else {
53118            that.xSteps.push(isNaN(value[1]) ? false : value[1]);
53119        }
53120        that.xHighestCompleteStep.push(0);
53121    }
53122    function handleStepPoint(i, n, that) {
53123        // Ignore 'false' stepping.
53124        if (!n) {
53125            return;
53126        }
53127        // Step over zero-length ranges (#948);
53128        if (that.xVal[i] === that.xVal[i + 1]) {
53129            that.xSteps[i] = that.xHighestCompleteStep[i] = that.xVal[i];
53130            return;
53131        }
53132        // Factor to range ratio
53133        that.xSteps[i] =
53134            fromPercentage([that.xVal[i], that.xVal[i + 1]], n, 0) / subRangeRatio(that.xPct[i], that.xPct[i + 1]);
53135        var totalSteps = (that.xVal[i + 1] - that.xVal[i]) / that.xNumSteps[i];
53136        var highestStep = Math.ceil(Number(totalSteps.toFixed(3)) - 1);
53137        var step = that.xVal[i] + that.xNumSteps[i] * highestStep;
53138        that.xHighestCompleteStep[i] = step;
53139    }
53140    //endregion
53141    //region Spectrum
53142    function Spectrum(entry, snap, singleStep) {
53143        this.xPct = [];
53144        this.xVal = [];
53145        this.xSteps = [singleStep || false];
53146        this.xNumSteps = [false];
53147        this.xHighestCompleteStep = [];
53148        this.snap = snap;
53149        var index;
53150        var ordered = []; // [0, 'min'], [1, '50%'], [2, 'max']
53151        // Map the object keys to an array.
53152        for (index in entry) {
53153            if (entry.hasOwnProperty(index)) {
53154                ordered.push([entry[index], index]);
53155            }
53156        }
53157        // Sort all entries by value (numeric sort).
53158        if (ordered.length && typeof ordered[0][0] === "object") {
53159            ordered.sort(function (a, b) {
53160                return a[0][0] - b[0][0];
53161            });
53162        }
53163        else {
53164            ordered.sort(function (a, b) {
53165                return a[0] - b[0];
53166            });
53167        }
53168        // Convert all entries to subranges.
53169        for (index = 0; index < ordered.length; index++) {
53170            handleEntryPoint(ordered[index][1], ordered[index][0], this);
53171        }
53172        // Store the actual step values.
53173        // xSteps is sorted in the same order as xPct and xVal.
53174        this.xNumSteps = this.xSteps.slice(0);
53175        // Convert all numeric steps to the percentage of the subrange they represent.
53176        for (index = 0; index < this.xNumSteps.length; index++) {
53177            handleStepPoint(index, this.xNumSteps[index], this);
53178        }
53179    }
53180    Spectrum.prototype.getDistance = function (value) {
53181        var index;
53182        var distances = [];
53183        for (index = 0; index < this.xNumSteps.length - 1; index++) {
vendor: 5,217 bytes, lines 53184-53291
53184            // last "range" can't contain step size as it is purely an endpoint.
53185            var step = this.xNumSteps[index];
53186            if (step && (value / step) % 1 !== 0) {
53187                throw new Error("noUiSlider (" +
53188                    VERSION +
53189                    "): 'limit', 'margin' and 'padding' of " +
53190                    this.xPct[index] +
53191                    "% range must be divisible by step.");
53192            }
53193            // Calculate percentual distance in current range of limit, margin or padding
53194            distances[index] = fromPercentage(this.xVal, value, index);
53195        }
53196        return distances;
53197    };
53198    // Calculate the percentual distance over the whole scale of ranges.
53199    // direction: 0 = backwards / 1 = forwards
53200    Spectrum.prototype.getAbsoluteDistance = function (value, distances, direction) {
53201        var xPct_index = 0;
53202        // Calculate range where to start calculation
53203        if (value < this.xPct[this.xPct.length - 1]) {
53204            while (value > this.xPct[xPct_index + 1]) {
53205                xPct_index++;
53206            }
53207        }
53208        else if (value === this.xPct[this.xPct.length - 1]) {
53209            xPct_index = this.xPct.length - 2;
53210        }
53211        // If looking backwards and the value is exactly at a range separator then look one range further
53212        if (!direction && value === this.xPct[xPct_index + 1]) {
53213            xPct_index++;
53214        }
53215        var start_factor;
53216        var rest_factor = 1;
53217        var rest_rel_distance = distances[xPct_index];
53218        var range_pct = 0;
53219        var rel_range_distance = 0;
53220        var abs_distance_counter = 0;
53221        var range_counter = 0;
53222        // Calculate what part of the start range the value is
53223        if (direction) {
53224            start_factor = (value - this.xPct[xPct_index]) / (this.xPct[xPct_index + 1] - this.xPct[xPct_index]);
53225        }
53226        else {
53227            start_factor = (this.xPct[xPct_index + 1] - value) / (this.xPct[xPct_index + 1] - this.xPct[xPct_index]);
53228        }
53229        // Do until the complete distance across ranges is calculated
53230        while (rest_rel_distance > 0) {
53231            // Calculate the percentage of total range
53232            range_pct = this.xPct[xPct_index + 1 + range_counter] - this.xPct[xPct_index + range_counter];
53233            // Detect if the margin, padding or limit is larger then the current range and calculate
53234            if (distances[xPct_index + range_counter] * rest_factor + 100 - start_factor * 100 > 100) {
53235                // If larger then take the percentual distance of the whole range
53236                rel_range_distance = range_pct * start_factor;
53237                // Rest factor of relative percentual distance still to be calculated
53238                rest_factor = (rest_rel_distance - 100 * start_factor) / distances[xPct_index + range_counter];
53239                // Set start factor to 1 as for next range it does not apply.
53240                start_factor = 1;
53241            }
53242            else {
53243                // If smaller or equal then take the percentual distance of the calculate percentual part of that range
53244                rel_range_distance = ((distances[xPct_index + range_counter] * range_pct) / 100) * rest_factor;
53245                // No rest left as the rest fits in current range
53246                rest_factor = 0;
53247            }
53248            if (direction) {
53249                abs_distance_counter = abs_distance_counter - rel_range_distance;
53250                // Limit range to first range when distance becomes outside of minimum range
53251                if (this.xPct.length + range_counter >= 1) {
53252                    range_counter--;
53253                }
53254            }
53255            else {
53256                abs_distance_counter = abs_distance_counter + rel_range_distance;
53257                // Limit range to last range when distance becomes outside of maximum range
53258                if (this.xPct.length - range_counter >= 1) {
53259                    range_counter++;
53260                }
53261            }
53262            // Rest of relative percentual distance still to be calculated
53263            rest_rel_distance = distances[xPct_index + range_counter] * rest_factor;
53264        }
53265        return value + abs_distance_counter;
53266    };
53267    Spectrum.prototype.toStepping = function (value) {
53268        value = toStepping(this.xVal, this.xPct, value);
53269        return value;
53270    };
53271    Spectrum.prototype.fromStepping = function (value) {
53272        return fromStepping(this.xVal, this.xPct, value);
53273    };
53274    Spectrum.prototype.getStep = function (value) {
53275        value = getStep(this.xPct, this.xSteps, this.snap, value);
53276        return value;
53277    };
53278    Spectrum.prototype.getDefaultStep = function (value, isDown, size) {
53279        var j = getJ(value, this.xPct);
53280        // When at the top or stepping down, look at the previous sub-range
53281        if (value === 100 || (isDown && value === this.xPct[j - 1])) {
53282            j = Math.max(j - 1, 1);
53283        }
53284        return (this.xVal[j] - this.xVal[j - 1]) / size;
53285    };
53286    Spectrum.prototype.getNearbySteps = function (value) {
53287        var j = getJ(value, this.xPct);
53288        return {
53289            stepBefore: {
53290                startValue: this.xVal[j - 2],
53291                step: this.xNumSteps[j - 2],
53292                highestStep: this.xHighestCompleteStep[j - 2]
53293            },
53294            thisStep: {
53295                startValue: this.xVal[j - 1],
53296                step: this.xNumSteps[j - 1],
53297                highestStep: this.xHighestCompleteStep[j - 1]
53298            },
53299            stepAfter: {
53300                startValue: this.xVal[j],
53301                step: this.xNumSteps[j],
53302                highestStep: this.xHighestCompleteStep[j]
53303            }
53304        };
53305    };
53306    Spectrum.prototype.countStepDecimals = function () {
53307        var stepDecimals = this.xNumSteps.map(countDecimals);
53308        return Math.max.apply(null, stepDecimals);
53309    };
53310    // Outside testing
53311    Spectrum.prototype.convert = function (value) {
53312        return this.getStep(this.toStepping(value));
53313    };
53314    //endregion
53315    //region Options
53316    /*	Every input option is tested and parsed. This'll prevent
53317        endless validation in internal methods. These tests are
53318        structured with an item for every option available. An
53319        option can be marked as required by setting the 'r' flag.
53320        The testing function is provided with three arguments:
53321            - The provided value for the option;
53322            - A reference to the options object;
53323            - The name for the option;
53324    
53325        The testing function returns false when an error is detected,
53326        or true when everything is OK. It can also modify the option
53327        object, to make sure all values can be correctly looped elsewhere. */
53328    //region Defaults
53329    var defaultFormatter = {
53330        to: function (value) {
53331            return value !== undefined && value.toFixed(2);
53332        },
53333        from: Number
53334    };
53335    var cssClasses = {
53336        target: "target",
53337        base: "base",
53338        origin: "origin",
53339        handle: "handle",
53340        handleLower: "handle-lower",
53341        handleUpper: "handle-upper",
53342        touchArea: "touch-area",
53343        horizontal: "horizontal",
53344        vertical: "vertical",
53345        background: "background",
53346        connect: "connect",
53347        connects: "connects",
53348        ltr: "ltr",
53349        rtl: "rtl",
53350        textDirectionLtr: "txt-dir-ltr",
53351        textDirectionRtl: "txt-dir-rtl",
53352        draggable: "draggable",
53353        drag: "state-drag",
53354        tap: "state-tap",
53355        active: "active",
53356        tooltip: "tooltip",
53357        pips: "pips",
53358        pipsHorizontal: "pips-horizontal",
53359        pipsVertical: "pips-vertical",
53360        marker: "marker",
53361        markerHorizontal: "marker-horizontal",
53362        markerVertical: "marker-vertical",
53363        markerNormal: "marker-normal",
53364        markerLarge: "marker-large",
53365        markerSub: "marker-sub",
53366        value: "value",
53367        valueHorizontal: "value-horizontal",
53368        valueVertical: "value-vertical",
53369        valueNormal: "value-normal",
53370        valueLarge: "value-large",
53371        valueSub: "value-sub"
53372    };
53373    // Namespaces of internal event listeners
53374    var INTERNAL_EVENT_NS = {
53375        tooltips: ".__tooltips",
53376        aria: ".__aria"
53377    };
53378    //endregion
53379    function validateFormat(entry) {
53380        // Any object with a to and from method is supported.
53381        if (isValidFormatter(entry)) {
53382            return true;
53383        }
53384        throw new Error("noUiSlider (" + VERSION + "): 'format' requires 'to' and 'from' methods.");
53385    }
53386    function testStep(parsed, entry) {
53387        if (!isNumeric(entry)) {
53388            throw new Error("noUiSlider (" + VERSION + "): 'step' is not numeric.");
53389        }
53390        // The step option can still be used to set stepping
53391        // for linear sliders. Overwritten if set in 'range'.
53392        parsed.singleStep = entry;
53393    }
53394    function testKeyboardPageMultiplier(parsed, entry) {
53395        if (!isNumeric(entry)) {
53396            throw new Error("noUiSlider (" + VERSION + "): 'keyboardPageMultiplier' is not numeric.");
53397        }
53398        parsed.keyboardPageMultiplier = entry;
53399    }
53400    function testKeyboardDefaultStep(parsed, entry) {
53401        if (!isNumeric(entry)) {
53402            throw new Error("noUiSlider (" + VERSION + "): 'keyboardDefaultStep' is not numeric.");
53403        }
53404        parsed.keyboardDefaultStep = entry;
53405    }
53406    function testRange(parsed, entry) {
53407        // Filter incorrect input.
53408        if (typeof entry !== "object" || Array.isArray(entry)) {
53409            throw new Error("noUiSlider (" + VERSION + "): 'range' is not an object.");
53410        }
53411        // Catch missing start or end.
53412        if (entry.min === undefined || entry.max === undefined) {
53413            throw new Error("noUiSlider (" + VERSION + "): Missing 'min' or 'max' in 'range'.");
53414        }
53415        // Catch equal start or end.
53416        if (entry.min === entry.max) {
53417            throw new Error("noUiSlider (" + VERSION + "): 'range' 'min' and 'max' cannot be equal.");
53418        }
53419        parsed.spectrum = new Spectrum(entry, parsed.snap, parsed.singleStep);
53420    }
53421    function testStart(parsed, entry) {
53422        entry = asArray(entry);
53423        // Validate input. Values aren't tested, as the public .val method
53424        // will always provide a valid location.
53425        if (!Array.isArray(entry) || !entry.length) {
53426            throw new Error("noUiSlider (" + VERSION + "): 'start' option is incorrect.");
53427        }
53428        // Store the number of handles.
53429        parsed.handles = entry.length;
53430        // When the slider is initialized, the .val method will
53431        // be called with the start options.
53432        parsed.start = entry;
53433    }
53434    function testSnap(parsed, entry) {
53435        // Enforce 100% stepping within subranges.
53436        parsed.snap = entry;
53437        if (typeof entry !== "boolean") {
53438            throw new Error("noUiSlider (" + VERSION + "): 'snap' option must be a boolean.");
53439        }
53440    }
53441    function testAnimate(parsed, entry) {
53442        // Enforce 100% stepping within subranges.
53443        parsed.animate = entry;
53444        if (typeof entry !== "boolean") {
53445            throw new Error("noUiSlider (" + VERSION + "): 'animate' option must be a boolean.");
53446        }
53447    }
53448    function testAnimationDuration(parsed, entry) {
53449        parsed.animationDuration = entry;
53450        if (typeof entry !== "number") {
53451            throw new Error("noUiSlider (" + VERSION + "): 'animationDuration' option must be a number.");
53452        }
53453    }
53454    function testConnect(parsed, entry) {
53455        var connect = [false];
53456        var i;
53457        // Map legacy options
53458        if (entry === "lower") {
53459            entry = [true, false];
53460        }
53461        else if (entry === "upper") {
53462            entry = [false, true];
53463        }
53464        // Handle boolean options
53465        if (entry === true || entry === false) {
53466            for (i = 1; i < parsed.handles; i++) {
53467                connect.push(entry);
53468            }
53469            connect.push(false);
53470        }
53471        // Reject invalid input
53472        else if (!Array.isArray(entry) || !entry.length || entry.length !== parsed.handles + 1) {
53473            throw new Error("noUiSlider (" + VERSION + "): 'connect' option doesn't match handle count.");
53474        }
53475        else {
53476            connect = entry;
53477        }
53478        parsed.connect = connect;
53479    }
53480    function testOrientation(parsed, entry) {
53481        // Set orientation to an a numerical value for easy
53482        // array selection.
53483        switch (entry) {
53484            case "horizontal":
53485                parsed.ort = 0;
53486                break;
53487            case "vertical":
53488                parsed.ort = 1;
53489                break;
53490            default:
53491                throw new Error("noUiSlider (" + VERSION + "): 'orientation' option is invalid.");
53492        }
53493    }
53494    function testMargin(parsed, entry) {
53495        if (!isNumeric(entry)) {
53496            throw new Error("noUiSlider (" + VERSION + "): 'margin' option must be numeric.");
53497        }
53498        // Issue #582
53499        if (entry === 0) {
53500            return;
53501        }
53502        parsed.margin = parsed.spectrum.getDistance(entry);
53503    }
53504    function testLimit(parsed, entry) {
53505        if (!isNumeric(entry)) {
53506            throw new Error("noUiSlider (" + VERSION + "): 'limit' option must be numeric.");
53507        }
53508        parsed.limit = parsed.spectrum.getDistance(entry);
53509        if (!parsed.limit || parsed.handles < 2) {
53510            throw new Error("noUiSlider (" + VERSION + "): 'limit' option is only supported on linear sliders with 2 or more handles.");
53511        }
53512    }
53513    function testPadding(parsed, entry) {
53514        var index;
53515        if (!isNumeric(entry) && !Array.isArray(entry)) {
53516            throw new Error("noUiSlider (" + VERSION + "): 'padding' option must be numeric or array of exactly 2 numbers.");
53517        }
53518        if (Array.isArray(entry) && !(entry.length === 2 || isNumeric(entry[0]) || isNumeric(entry[1]))) {
53519            throw new Error("noUiSlider (" + VERSION + "): 'padding' option must be numeric or array of exactly 2 numbers.");
53520        }
53521        if (entry === 0) {
53522            return;
53523        }
53524        if (!Array.isArray(entry)) {
53525            entry = [entry, entry];
53526        }
53527        // 'getDistance' returns false for invalid values.
53528        parsed.padding = [parsed.spectrum.getDistance(entry[0]), parsed.spectrum.getDistance(entry[1])];
53529        for (index = 0; index < parsed.spectrum.xNumSteps.length - 1; index++) {
53530            // last "range" can't contain step size as it is purely an endpoint.
53531            if (parsed.padding[0][index] < 0 || parsed.padding[1][index] < 0) {
53532                throw new Error("noUiSlider (" + VERSION + "): 'padding' option must be a positive number(s).");
53533            }
53534        }
53535        var totalPadding = entry[0] + entry[1];
53536        var firstValue = parsed.spectrum.xVal[0];
53537        var lastValue = parsed.spectrum.xVal[parsed.spectrum.xVal.length - 1];
53538        if (totalPadding / (lastValue - firstValue) > 1) {
53539            throw new Error("noUiSlider (" + VERSION + "): 'padding' option must not exceed 100% of the range.");
53540        }
53541    }
53542    function testDirection(parsed, entry) {
53543        // Set direction as a numerical value for easy parsing.
53544        // Invert connection for RTL sliders, so that the proper
53545        // handles get the connect/background classes.
53546        switch (entry) {
53547            case "ltr":
53548                parsed.dir = 0;
53549                break;
53550            case "rtl":
53551                parsed.dir = 1;
53552                break;
53553            default:
53554                throw new Error("noUiSlider (" + VERSION + "): 'direction' option was not recognized.");
53555        }
53556    }
53557    function testBehaviour(parsed, entry) {
53558        // Make sure the input is a string.
53559        if (typeof entry !== "string") {
53560            throw new Error("noUiSlider (" + VERSION + "): 'behaviour' must be a string containing options.");
53561        }
53562        // Check if the string contains any keywords.
53563        // None are required.
53564        var tap = entry.indexOf("tap") >= 0;
53565        var drag = entry.indexOf("drag") >= 0;
53566        var fixed = entry.indexOf("fixed") >= 0;
53567        var snap = entry.indexOf("snap") >= 0;
53568        var hover = entry.indexOf("hover") >= 0;
53569        var unconstrained = entry.indexOf("unconstrained") >= 0;
53570        if (fixed) {
53571            if (parsed.handles !== 2) {
53572                throw new Error("noUiSlider (" + VERSION + "): 'fixed' behaviour must be used with 2 handles");
53573            }
53574            // Use margin to enforce fixed state
53575            testMargin(parsed, parsed.start[1] - parsed.start[0]);
53576        }
53577        if (unconstrained && (parsed.margin || parsed.limit)) {
53578            throw new Error("noUiSlider (" + VERSION + "): 'unconstrained' behaviour cannot be used with margin or limit");
53579        }
53580        parsed.events = {
53581            tap: tap || snap,
53582            drag: drag,
53583            fixed: fixed,
53584            snap: snap,
53585            hover: hover,
53586            unconstrained: unconstrained
53587        };
53588    }
53589    function testTooltips(parsed, entry) {
53590        if (entry === false) {
53591            return;
53592        }
53593        if (entry === true) {
53594            parsed.tooltips = [];
53595            for (var i = 0; i < parsed.handles; i++) {
53596                parsed.tooltips.push(true);
53597            }
53598        }
53599        else {
53600            parsed.tooltips = asArray(entry);
53601            if (parsed.tooltips.length !== parsed.handles) {
53602                throw new Error("noUiSlider (" + VERSION + "): must pass a formatter for all handles.");
53603            }
53604            parsed.tooltips.forEach(function (formatter) {
53605                if (typeof formatter !== "boolean" &&
53606                    (typeof formatter !== "object" || typeof formatter.to !== "function")) {
53607                    throw new Error("noUiSlider (" + VERSION + "): 'tooltips' must be passed a formatter or 'false'.");
53608                }
53609            });
53610        }
53611    }
53612    function testAriaFormat(parsed, entry) {
53613        parsed.ariaFormat = entry;
53614        validateFormat(entry);
53615    }
53616    function testFormat(parsed, entry) {
53617        parsed.format = entry;
53618        validateFormat(entry);
53619    }
53620    function testKeyboardSupport(parsed, entry) {
53621        parsed.keyboardSupport = entry;
53622        if (typeof entry !== "boolean") {
53623            throw new Error("noUiSlider (" + VERSION + "): 'keyboardSupport' option must be a boolean.");
53624        }
53625    }
53626    function testDocumentElement(parsed, entry) {
53627        // This is an advanced option. Passed values are used without validation.
53628        parsed.documentElement = entry;
53629    }
53630    function testCssPrefix(parsed, entry) {
53631        if (typeof entry !== "string" && entry !== false) {
53632            throw new Error("noUiSlider (" + VERSION + "): 'cssPrefix' must be a string or `false`.");
53633        }
53634        parsed.cssPrefix = entry;
53635    }
53636    function testCssClasses(parsed, entry) {
53637        if (typeof entry !== "object") {
53638            throw new Error("noUiSlider (" + VERSION + "): 'cssClasses' must be an object.");
53639        }
53640        if (typeof parsed.cssPrefix === "string") {
53641            parsed.cssClasses = {};
53642            for (var key in entry) {
53643                if (!entry.hasOwnProperty(key)) {
53644                    continue;
53645                }
53646                parsed.cssClasses[key] = parsed.cssPrefix + entry[key];
53647            }
53648        }
53649        else {
53650            parsed.cssClasses = entry;
53651        }
53652    }
53653    // Test all developer settings and parse to assumption-safe values.
53654    function testOptions(options) {
53655        // To prove a fix for #537, freeze options here.
53656        // If the object is modified, an error will be thrown.
53657        // Object.freeze(options);
53658        var parsed = {
53659            margin: 0,
53660            limit: 0,
53661            padding: 0,
53662            animate: true,
53663            animationDuration: 300,
53664            ariaFormat: defaultFormatter,
53665            format: defaultFormatter
53666        };
53667        // Tests are executed in the order they are presented here.
53668        var tests = {
53669            step: { r: false, t: testStep },
53670            keyboardPageMultiplier: { r: false, t: testKeyboardPageMultiplier },
53671            keyboardDefaultStep: { r: false, t: testKeyboardDefaultStep },
53672            start: { r: true, t: testStart },
53673            connect: { r: true, t: testConnect },
53674            direction: { r: true, t: testDirection },
53675            snap: { r: false, t: testSnap },
53676            animate: { r: false, t: testAnimate },
53677            animationDuration: { r: false, t: testAnimationDuration },
53678            range: { r: true, t: testRange },
53679            orientation: { r: false, t: testOrientation },
53680            margin: { r: false, t: testMargin },
53681            limit: { r: false, t: testLimit },
53682            padding: { r: false, t: testPadding },
53683            behaviour: { r: true, t: testBehaviour },
53684            ariaFormat: { r: false, t: testAriaFormat },
53685            format: { r: false, t: testFormat },
53686            tooltips: { r: false, t: testTooltips },
53687            keyboardSupport: { r: true, t: testKeyboardSupport },
53688            documentElement: { r: false, t: testDocumentElement },
53689            cssPrefix: { r: true, t: testCssPrefix },
53690            cssClasses: { r: true, t: testCssClasses }
53691        };
53692        var defaults = {
53693            connect: false,
53694            direction: "ltr",
53695            behaviour: "tap",
53696            orientation: "horizontal",
53697            keyboardSupport: true,
53698            cssPrefix: "noUi-",
53699            cssClasses: cssClasses,
53700            keyboardPageMultiplier: 5,
53701            keyboardDefaultStep: 10
53702        };
53703        // AriaFormat defaults to regular format, if any.
53704        if (options.format && !options.ariaFormat) {
53705            options.ariaFormat = options.format;
53706        }
53707        // Run all options through a testing mechanism to ensure correct
53708        // input. It should be noted that options might get modified to
53709        // be handled properly. E.g. wrapping integers in arrays.
53710        Object.keys(tests).forEach(function (name) {
53711            // If the option isn't set, but it is required, throw an error.
53712            if (!isSet(options[name]) && defaults[name] === undefined) {
53713                if (tests[name].r) {
53714                    throw new Error("noUiSlider (" + VERSION + "): '" + name + "' is required.");
53715                }
53716                return true;
53717            }
53718            tests[name].t(parsed, !isSet(options[name]) ? defaults[name] : options[name]);
53719        });
53720        // Forward pips options
53721        parsed.pips = options.pips;
53722        // All recent browsers accept unprefixed transform.
53723        // We need -ms- for IE9 and -webkit- for older Android;
53724        // Assume use of -webkit- if unprefixed and -ms- are not supported.
53725        // https://caniuse.com/#feat=transforms2d
53726        var d = document.createElement("div");
53727        var msPrefix = d.style.msTransform !== undefined;
53728        var noPrefix = d.style.transform !== undefined;
53729        parsed.transformRule = noPrefix ? "transform" : msPrefix ? "msTransform" : "webkitTransform";
53730        // Pips don't move, so we can place them using left/top.
53731        var styles = [["left", "top"], ["right", "bottom"]];
53732        parsed.style = styles[parsed.dir][parsed.ort];
53733        return parsed;
53734    }
53735    //endregion
53736    function scope(target, options, originalOptions) {
53737        var actions = getActions();
53738        var supportsTouchActionNone = getSupportsTouchActionNone();
53739        var supportsPassive = supportsTouchActionNone && getSupportsPassive();
53740        // All variables local to 'scope' are prefixed with 'scope_'
53741        // Slider DOM Nodes
53742        var scope_Target = target;
53743        var scope_Base;
53744        var scope_Handles;
53745        var scope_Connects;
53746        var scope_Pips;
53747        var scope_Tooltips;
53748        // Slider state values
53749        var scope_Spectrum = options.spectrum;
53750        var scope_Values = [];
53751        var scope_Locations = [];
53752        var scope_HandleNumbers = [];
53753        var scope_ActiveHandlesCount = 0;
53754        var scope_Events = {};
53755        // Exposed API
53756        var scope_Self;
53757        // Document Nodes
53758        var scope_Document = target.ownerDocument;
53759        var scope_DocumentElement = options.documentElement || scope_Document.documentElement;
53760        var scope_Body = scope_Document.body;
53761        // Pips constants
53762        var PIPS_NONE = -1;
53763        var PIPS_NO_VALUE = 0;
53764        var PIPS_LARGE_VALUE = 1;
53765        var PIPS_SMALL_VALUE = 2;
53766        // For horizontal sliders in standard ltr documents,
53767        // make .noUi-origin overflow to the left so the document doesn't scroll.
53768        var scope_DirOffset = scope_Document.dir === "rtl" || options.ort === 1 ? 0 : 100;
53769        // Creates a node, adds it to target, returns the new node.
53770        function addNodeTo(addTarget, className) {
53771            var div = scope_Document.createElement("div");
53772            if (className) {
53773                addClass(div, className);
53774            }
53775            addTarget.appendChild(div);
53776            return div;
53777        }
53778        // Append a origin to the base
53779        function addOrigin(base, handleNumber) {
53780            var origin = addNodeTo(base, options.cssClasses.origin);
53781            var handle = addNodeTo(origin, options.cssClasses.handle);
vendor: 5,013 bytes, lines 53782-53893
53782            addNodeTo(handle, options.cssClasses.touchArea);
53783            handle.setAttribute("data-handle", handleNumber);
53784            if (options.keyboardSupport) {
53785                // https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex
53786                // 0 = focusable and reachable
53787                handle.setAttribute("tabindex", "0");
53788                handle.addEventListener("keydown", function (event) {
53789                    return eventKeydown(event, handleNumber);
53790                });
53791            }
53792            handle.setAttribute("role", "slider");
53793            handle.setAttribute("aria-orientation", options.ort ? "vertical" : "horizontal");
53794            if (handleNumber === 0) {
53795                addClass(handle, options.cssClasses.handleLower);
53796            }
53797            else if (handleNumber === options.handles - 1) {
53798                addClass(handle, options.cssClasses.handleUpper);
53799            }
53800            return origin;
53801        }
53802        // Insert nodes for connect elements
53803        function addConnect(base, add) {
53804            if (!add) {
53805                return false;
53806            }
53807            return addNodeTo(base, options.cssClasses.connect);
53808        }
53809        // Add handles to the slider base.
53810        function addElements(connectOptions, base) {
53811            var connectBase = addNodeTo(base, options.cssClasses.connects);
53812            scope_Handles = [];
53813            scope_Connects = [];
53814            scope_Connects.push(addConnect(connectBase, connectOptions[0]));
53815            // [::::O====O====O====]
53816            // connectOptions = [0, 1, 1, 1]
53817            for (var i = 0; i < options.handles; i++) {
53818                // Keep a list of all added handles.
53819                scope_Handles.push(addOrigin(base, i));
53820                scope_HandleNumbers[i] = i;
53821                scope_Connects.push(addConnect(connectBase, connectOptions[i + 1]));
53822            }
53823        }
53824        // Initialize a single slider.
53825        function addSlider(addTarget) {
53826            // Apply classes and data to the target.
53827            addClass(addTarget, options.cssClasses.target);
53828            if (options.dir === 0) {
53829                addClass(addTarget, options.cssClasses.ltr);
53830            }
53831            else {
53832                addClass(addTarget, options.cssClasses.rtl);
53833            }
53834            if (options.ort === 0) {
53835                addClass(addTarget, options.cssClasses.horizontal);
53836            }
53837            else {
53838                addClass(addTarget, options.cssClasses.vertical);
53839            }
53840            var textDirection = getComputedStyle(addTarget).direction;
53841            if (textDirection === "rtl") {
53842                addClass(addTarget, options.cssClasses.textDirectionRtl);
53843            }
53844            else {
53845                addClass(addTarget, options.cssClasses.textDirectionLtr);
53846            }
53847            return addNodeTo(addTarget, options.cssClasses.base);
53848        }
53849        function addTooltip(handle, handleNumber) {
53850            if (!options.tooltips[handleNumber]) {
53851                return false;
53852            }
53853            return addNodeTo(handle.firstChild, options.cssClasses.tooltip);
53854        }
53855        function isSliderDisabled() {
53856            return scope_Target.hasAttribute("disabled");
53857        }
53858        // Disable the slider dragging if any handle is disabled
53859        function isHandleDisabled(handleNumber) {
53860            var handleOrigin = scope_Handles[handleNumber];
53861            return handleOrigin.hasAttribute("disabled");
53862        }
53863        function removeTooltips() {
53864            if (scope_Tooltips) {
53865                removeEvent("update" + INTERNAL_EVENT_NS.tooltips);
53866                scope_Tooltips.forEach(function (tooltip) {
53867                    if (tooltip) {
53868                        removeElement(tooltip);
53869                    }
53870                });
53871                scope_Tooltips = null;
53872            }
53873        }
53874        // The tooltips option is a shorthand for using the 'update' event.
53875        function tooltips() {
53876            removeTooltips();
53877            // Tooltips are added with options.tooltips in original order.
53878            scope_Tooltips = scope_Handles.map(addTooltip);
53879            bindEvent("update" + INTERNAL_EVENT_NS.tooltips, function (values, handleNumber, unencoded) {
53880                if (!scope_Tooltips[handleNumber]) {
53881                    return;
53882                }
53883                var formattedValue = values[handleNumber];
53884                if (options.tooltips[handleNumber] !== true) {
53885                    formattedValue = options.tooltips[handleNumber].to(unencoded[handleNumber]);
53886                }
53887                scope_Tooltips[handleNumber].innerHTML = formattedValue;
53888            });
53889        }
53890        function aria() {
53891            removeEvent("update" + INTERNAL_EVENT_NS.aria);
53892            bindEvent("update" + INTERNAL_EVENT_NS.aria, function (values, handleNumber, unencoded, tap, positions) {
53893                // Update Aria Values for all handles, as a change in one changes min and max values for the next.
53894                scope_HandleNumbers.forEach(function (index) {
53895                    var handle = scope_Handles[index];
53896                    var min = checkHandlePosition(scope_Locations, index, 0, true, true, true);
53897                    var max = checkHandlePosition(scope_Locations, index, 100, true, true, true);
53898                    var now = positions[index];
53899                    // Formatted value for display
53900                    var text = options.ariaFormat.to(unencoded[index]);
53901                    // Map to slider range values
53902                    min = scope_Spectrum.fromStepping(min).toFixed(1);
53903                    max = scope_Spectrum.fromStepping(max).toFixed(1);
53904                    now = scope_Spectrum.fromStepping(now).toFixed(1);
53905                    handle.children[0].setAttribute("aria-valuemin", min);
53906                    handle.children[0].setAttribute("aria-valuemax", max);
53907                    handle.children[0].setAttribute("aria-valuenow", now);
53908                    handle.children[0].setAttribute("aria-valuetext", text);
53909                });
53910            });
53911        }
53912        function getGroup(mode, values, stepped) {
53913            // Use the range.
53914            if (mode === "range" || mode === "steps") {
53915                return scope_Spectrum.xVal;
53916            }
53917            if (mode === "count") {
53918                if (values < 2) {
53919                    throw new Error("noUiSlider (" + VERSION + "): 'values' (>= 2) required for mode 'count'.");
53920                }
53921                // Divide 0 - 100 in 'count' parts.
53922                var interval = values - 1;
53923                var spread = 100 / interval;
53924                values = [];
53925                // List these parts and have them handled as 'positions'.
53926                while (interval--) {
53927                    values[interval] = interval * spread;
53928                }
53929                values.push(100);
53930                mode = "positions";
53931            }
53932            if (mode === "positions") {
53933                // Map all percentages to on-range values.
53934                return values.map(function (value) {
53935                    return scope_Spectrum.fromStepping(stepped ? scope_Spectrum.getStep(value) : value);
53936                });
53937            }
53938            if (mode === "values") {
53939                // If the value must be stepped, it needs to be converted to a percentage first.
53940                if (stepped) {
53941                    return values.map(function (value) {
53942                        // Convert to percentage, apply step, return to value.
53943                        return scope_Spectrum.fromStepping(scope_Spectrum.getStep(scope_Spectrum.toStepping(value)));
53944                    });
53945                }
53946                // Otherwise, we can simply use the values.
53947                return values;
53948            }
53949        }
53950        function generateSpread(density, mode, group) {
53951            function safeIncrement(value, increment) {
53952                // Avoid floating point variance by dropping the smallest decimal places.
53953                return (value + increment).toFixed(7) / 1;
53954            }
53955            var indexes = {};
53956            var firstInRange = scope_Spectrum.xVal[0];
53957            var lastInRange = scope_Spectrum.xVal[scope_Spectrum.xVal.length - 1];
53958            var ignoreFirst = false;
53959            var ignoreLast = false;
53960            var prevPct = 0;
53961            // Create a copy of the group, sort it and filter away all duplicates.
53962            group = unique(group.slice().sort(function (a, b) {
53963                return a - b;
53964            }));
53965            // Make sure the range starts with the first element.
53966            if (group[0] !== firstInRange) {
53967                group.unshift(firstInRange);
53968                ignoreFirst = true;
53969            }
53970            // Likewise for the last one.
53971            if (group[group.length - 1] !== lastInRange) {
53972                group.push(lastInRange);
53973                ignoreLast = true;
53974            }
53975            group.forEach(function (current, index) {
53976                // Get the current step and the lower + upper positions.
53977                var step;
53978                var i;
53979                var q;
53980                var low = current;
53981                var high = group[index + 1];
53982                var newPct;
53983                var pctDifference;
53984                var pctPos;
53985                var type;
53986                var steps;
53987                var realSteps;
53988                var stepSize;
53989                var isSteps = mode === "steps";
53990                // When using 'steps' mode, use the provided steps.
53991                // Otherwise, we'll step on to the next subrange.
53992                if (isSteps) {
53993                    step = scope_Spectrum.xNumSteps[index];
53994                }
53995                // Default to a 'full' step.
53996                if (!step) {
53997                    step = high - low;
53998                }
53999                // Low can be 0, so test for false. Index 0 is already handled.
54000                if (low === false) {
54001                    return;
54002                }
54003                // If high is undefined we are at the last subrange. Make sure it iterates once (#1088)
54004                if (high === undefined) {
54005                    high = low;
54006                }
54007                // Make sure step isn't 0, which would cause an infinite loop (#654)
54008                step = Math.max(step, 0.0000001);
54009                // Find all steps in the subrange.
54010                for (i = low; i <= high; i = safeIncrement(i, step)) {
54011                    // Get the percentage value for the current step,
54012                    // calculate the size for the subrange.
54013                    newPct = scope_Spectrum.toStepping(i);
54014                    pctDifference = newPct - prevPct;
54015                    steps = pctDifference / density;
54016                    realSteps = Math.round(steps);
54017                    // This ratio represents the amount of percentage-space a point indicates.
54018                    // For a density 1 the points/percentage = 1. For density 2, that percentage needs to be re-divided.
54019                    // Round the percentage offset to an even number, then divide by two
54020                    // to spread the offset on both sides of the range.
54021                    stepSize = pctDifference / realSteps;
54022                    // Divide all points evenly, adding the correct number to this subrange.
54023                    // Run up to <= so that 100% gets a point, event if ignoreLast is set.
54024                    for (q = 1; q <= realSteps; q += 1) {
54025                        // The ratio between the rounded value and the actual size might be ~1% off.
54026                        // Correct the percentage offset by the number of points
54027                        // per subrange. density = 1 will result in 100 points on the
54028                        // full range, 2 for 50, 4 for 25, etc.
54029                        pctPos = prevPct + q * stepSize;
54030                        indexes[pctPos.toFixed(5)] = [scope_Spectrum.fromStepping(pctPos), 0];
54031                    }
54032                    // Determine the point type.
54033                    type = group.indexOf(i) > -1 ? PIPS_LARGE_VALUE : isSteps ? PIPS_SMALL_VALUE : PIPS_NO_VALUE;
54034                    // Enforce the 'ignoreFirst' option by overwriting the type for 0.
54035                    if (!index && ignoreFirst && i !== high) {
54036                        type = 0;
54037                    }
54038                    if (!(i === high && ignoreLast)) {
54039                        // Mark the 'type' of this point. 0 = plain, 1 = real value, 2 = step value.
54040                        indexes[newPct.toFixed(5)] = [i, type];
54041                    }
54042                    // Update the percentage count.
54043                    prevPct = newPct;
54044                }
54045            });
54046            return indexes;
54047        }
54048        function addMarking(spread, filterFunc, formatter) {
54049            var element = scope_Document.createElement("div");
54050            var valueSizeClasses = [];
54051            valueSizeClasses[PIPS_NO_VALUE] = options.cssClasses.valueNormal;
54052            valueSizeClasses[PIPS_LARGE_VALUE] = options.cssClasses.valueLarge;
54053            valueSizeClasses[PIPS_SMALL_VALUE] = options.cssClasses.valueSub;
54054            var markerSizeClasses = [];
54055            markerSizeClasses[PIPS_NO_VALUE] = options.cssClasses.markerNormal;
54056            markerSizeClasses[PIPS_LARGE_VALUE] = options.cssClasses.markerLarge;
54057            markerSizeClasses[PIPS_SMALL_VALUE] = options.cssClasses.markerSub;
54058            var valueOrientationClasses = [options.cssClasses.valueHorizontal, options.cssClasses.valueVertical];
54059            var markerOrientationClasses = [options.cssClasses.markerHorizontal, options.cssClasses.markerVertical];
54060            addClass(element, options.cssClasses.pips);
54061            addClass(element, options.ort === 0 ? options.cssClasses.pipsHorizontal : options.cssClasses.pipsVertical);
54062            function getClasses(type, source) {
54063                var a = source === options.cssClasses.value;
54064                var orientationClasses = a ? valueOrientationClasses : markerOrientationClasses;
54065                var sizeClasses = a ? valueSizeClasses : markerSizeClasses;
54066                return source + " " + orientationClasses[options.ort] + " " + sizeClasses[type];
54067            }
54068            function addSpread(offset, value, type) {
54069                // Apply the filter function, if it is set.
54070                type = filterFunc ? filterFunc(value, type) : type;
54071                if (type === PIPS_NONE) {
54072                    return;
54073                }
54074                // Add a marker for every point
54075                var node = addNodeTo(element, false);
54076                node.className = getClasses(type, options.cssClasses.marker);
54077                node.style[options.style] = offset + "%";
54078                // Values are only appended for points marked '1' or '2'.
54079                if (type > PIPS_NO_VALUE) {
54080                    node = addNodeTo(element, false);
54081                    node.className = getClasses(type, options.cssClasses.value);
54082                    node.setAttribute("data-value", value);
54083                    node.style[options.style] = offset + "%";
54084                    node.innerHTML = formatter.to(value);
54085                }
54086            }
54087            // Append all points.
54088            Object.keys(spread).forEach(function (offset) {
54089                addSpread(offset, spread[offset][0], spread[offset][1]);
54090            });
54091            return element;
54092        }
54093        function removePips() {
54094            if (scope_Pips) {
54095                removeElement(scope_Pips);
54096                scope_Pips = null;
54097            }
54098        }
54099        function pips(grid) {
54100            // Fix #669
54101            removePips();
54102            var mode = grid.mode;
54103            var density = grid.density || 1;
54104            var filter = grid.filter || false;
54105            var values = grid.values || false;
54106            var stepped = grid.stepped || false;
54107            var group = getGroup(mode, values, stepped);
54108            var spread = generateSpread(density, mode, group);
54109            var format = grid.format || {
54110                to: Math.round
54111            };
54112            scope_Pips = scope_Target.appendChild(addMarking(spread, filter, format));
54113            return scope_Pips;
54114        }
54115        // Shorthand for base dimensions.
54116        function baseSize() {
54117            var rect = scope_Base.getBoundingClientRect();
54118            var alt = "offset" + ["Width", "Height"][options.ort];
54119            return options.ort === 0 ? rect.width || scope_Base[alt] : rect.height || scope_Base[alt];
54120        }
54121        // Handler for attaching events trough a proxy.
54122        function attachEvent(events, element, callback, data) {
54123            // This function can be used to 'filter' events to the slider.
54124            // element is a node, not a nodeList
54125            var method = function (e) {
54126                e = fixEvent(e, data.pageOffset, data.target || element);
54127                // fixEvent returns false if this event has a different target
54128                // when handling (multi-) touch events;
54129                if (!e) {
54130                    return false;
54131                }
54132                // doNotReject is passed by all end events to make sure released touches
54133                // are not rejected, leaving the slider "stuck" to the cursor;
54134                if (isSliderDisabled() && !data.doNotReject) {
54135                    return false;
54136                }
54137                // Stop if an active 'tap' transition is taking place.
54138                if (hasClass(scope_Target, options.cssClasses.tap) && !data.doNotReject) {
54139                    return false;
54140                }
54141                // Ignore right or middle clicks on start #454
54142                if (events === actions.start && e.buttons !== undefined && e.buttons > 1) {
54143                    return false;
54144                }
54145                // Ignore right or middle clicks on start #454
54146                if (data.hover && e.buttons) {
54147                    return false;
54148                }
54149                // 'supportsPassive' is only true if a browser also supports touch-action: none in CSS.
54150                // iOS safari does not, so it doesn't get to benefit from passive scrolling. iOS does support
54151                // touch-action: manipulation, but that allows panning, which breaks
54152                // sliders after zooming/on non-responsive pages.
54153                // See: https://bugs.webkit.org/show_bug.cgi?id=133112
54154                if (!supportsPassive) {
54155                    e.preventDefault();
54156                }
54157                e.calcPoint = e.points[options.ort];
54158                // Call the event handler with the event [ and additional data ].
54159                callback(e, data);
54160            };
54161            var methods = [];
54162            // Bind a closure on the target for every event type.
54163            events.split(" ").forEach(function (eventName) {
54164                element.addEventListener(eventName, method, supportsPassive ? { passive: true } : false);
54165                methods.push([eventName, method]);
54166            });
54167            return methods;
54168        }
54169        // Provide a clean event with standardized offset values.
54170        function fixEvent(e, pageOffset, eventTarget) {
54171            // Filter the event to register the type, which can be
54172            // touch, mouse or pointer. Offset changes need to be
54173            // made on an event specific basis.
54174            var touch = e.type.indexOf("touch") === 0;
54175            var mouse = e.type.indexOf("mouse") === 0;
54176            var pointer = e.type.indexOf("pointer") === 0;
54177            var x;
54178            var y;
54179            // IE10 implemented pointer events with a prefix;
54180            if (e.type.indexOf("MSPointer") === 0) {
54181                pointer = true;
54182            }
54183            // Erroneous events seem to be passed in occasionally on iOS/iPadOS after user finishes interacting with
54184            // the slider. They appear to be of type MouseEvent, yet they don't have usual properties set. Ignore
54185            // events that have no touches or buttons associated with them. (#1057, #1079, #1095)
54186            if (e.type === "mousedown" && !e.buttons && !e.touches) {
54187                return false;
54188            }
54189            // The only thing one handle should be concerned about is the touches that originated on top of it.
54190            if (touch) {
54191                // Returns true if a touch originated on the target.
54192                var isTouchOnTarget = function (checkTouch) {
54193                    return (checkTouch.target === eventTarget ||
54194                        eventTarget.contains(checkTouch.target) ||
54195                        (checkTouch.target.shadowRoot && checkTouch.target.shadowRoot.contains(eventTarget)));
54196                };
54197                // In the case of touchstart events, we need to make sure there is still no more than one
54198                // touch on the target so we look amongst all touches.
54199                if (e.type === "touchstart") {
54200                    var targetTouches = Array.prototype.filter.call(e.touches, isTouchOnTarget);
54201                    // Do not support more than one touch per handle.
54202                    if (targetTouches.length > 1) {
54203                        return false;
54204                    }
54205                    x = targetTouches[0].pageX;
54206                    y = targetTouches[0].pageY;
54207                }
54208                else {
54209                    // In the other cases, find on changedTouches is enough.
54210                    var targetTouch = Array.prototype.find.call(e.changedTouches, isTouchOnTarget);
54211                    // Cancel if the target touch has not moved.
54212                    if (!targetTouch) {
54213                        return false;
vendor: 20,990 bytes, lines 54214-54627
54214                    }
54215                    x = targetTouch.pageX;
54216                    y = targetTouch.pageY;
54217                }
54218            }
54219            pageOffset = pageOffset || getPageOffset(scope_Document);
54220            if (mouse || pointer) {
54221                x = e.clientX + pageOffset.x;
54222                y = e.clientY + pageOffset.y;
54223            }
54224            e.pageOffset = pageOffset;
54225            e.points = [x, y];
54226            e.cursor = mouse || pointer; // Fix #435
54227            return e;
54228        }
54229        // Translate a coordinate in the document to a percentage on the slider
54230        function calcPointToPercentage(calcPoint) {
54231            var location = calcPoint - offset(scope_Base, options.ort);
54232            var proposal = (location * 100) / baseSize();
54233            // Clamp proposal between 0% and 100%
54234            // Out-of-bound coordinates may occur when .noUi-base pseudo-elements
54235            // are used (e.g. contained handles feature)
54236            proposal = limit(proposal);
54237            return options.dir ? 100 - proposal : proposal;
54238        }
54239        // Find handle closest to a certain percentage on the slider
54240        function getClosestHandle(clickedPosition) {
54241            var smallestDifference = 100;
54242            var handleNumber = false;
54243            scope_Handles.forEach(function (handle, index) {
54244                // Disabled handles are ignored
54245                if (isHandleDisabled(index)) {
54246                    return;
54247                }
54248                var handlePosition = scope_Locations[index];
54249                var differenceWithThisHandle = Math.abs(handlePosition - clickedPosition);
54250                // Initial state
54251                var clickAtEdge = differenceWithThisHandle === 100 && smallestDifference === 100;
54252                // Difference with this handle is smaller than the previously checked handle
54253                var isCloser = differenceWithThisHandle < smallestDifference;
54254                var isCloserAfter = differenceWithThisHandle <= smallestDifference && clickedPosition > handlePosition;
54255                if (isCloser || isCloserAfter || clickAtEdge) {
54256                    handleNumber = index;
54257                    smallestDifference = differenceWithThisHandle;
54258                }
54259            });
54260            return handleNumber;
54261        }
54262        // Fire 'end' when a mouse or pen leaves the document.
54263        function documentLeave(event, data) {
54264            if (event.type === "mouseout" && event.target.nodeName === "HTML" && event.relatedTarget === null) {
54265                eventEnd(event, data);
54266            }
54267        }
54268        // Handle movement on document for handle and range drag.
54269        function eventMove(event, data) {
54270            // Fix #498
54271            // Check value of .buttons in 'start' to work around a bug in IE10 mobile (data.buttonsProperty).
54272            // https://connect.microsoft.com/IE/feedback/details/927005/mobile-ie10-windows-phone-buttons-property-of-pointermove-event-always-zero
54273            // IE9 has .buttons and .which zero on mousemove.
54274            // Firefox breaks the spec MDN defines.
54275            if (navigator.appVersion.indexOf("MSIE 9") === -1 && event.buttons === 0 && data.buttonsProperty !== 0) {
54276                return eventEnd(event, data);
54277            }
54278            // Check if we are moving up or down
54279            var movement = (options.dir ? -1 : 1) * (event.calcPoint - data.startCalcPoint);
54280            // Convert the movement into a percentage of the slider width/height
54281            var proposal = (movement * 100) / data.baseSize;
54282            moveHandles(movement > 0, proposal, data.locations, data.handleNumbers);
54283        }
54284        // Unbind move events on document, call callbacks.
54285        function eventEnd(event, data) {
54286            // The handle is no longer active, so remove the class.
54287            if (data.handle) {
54288                removeClass(data.handle, options.cssClasses.active);
54289                scope_ActiveHandlesCount -= 1;
54290            }
54291            // Unbind the move and end events, which are added on 'start'.
54292            data.listeners.forEach(function (c) {
54293                scope_DocumentElement.removeEventListener(c[0], c[1]);
54294            });
54295            if (scope_ActiveHandlesCount === 0) {
54296                // Remove dragging class.
54297                removeClass(scope_Target, options.cssClasses.drag);
54298                setZindex();
54299                // Remove cursor styles and text-selection events bound to the body.
54300                if (event.cursor) {
54301                    scope_Body.style.cursor = "";
54302                    scope_Body.removeEventListener("selectstart", preventDefault);
54303                }
54304            }
54305            data.handleNumbers.forEach(function (handleNumber) {
54306                fireEvent("change", handleNumber);
54307                fireEvent("set", handleNumber);
54308                fireEvent("end", handleNumber);
54309            });
54310        }
54311        // Bind move events on document.
54312        function eventStart(event, data) {
54313            // Ignore event if any handle is disabled
54314            if (data.handleNumbers.some(isHandleDisabled)) {
54315                return false;
54316            }
54317            var handle;
54318            if (data.handleNumbers.length === 1) {
54319                var handleOrigin = scope_Handles[data.handleNumbers[0]];
54320                handle = handleOrigin.children[0];
54321                scope_ActiveHandlesCount += 1;
54322                // Mark the handle as 'active' so it can be styled.
54323                addClass(handle, options.cssClasses.active);
54324            }
54325            // A drag should never propagate up to the 'tap' event.
54326            event.stopPropagation();
54327            // Record the event listeners.
54328            var listeners = [];
54329            // Attach the move and end events.
54330            var moveEvent = attachEvent(actions.move, scope_DocumentElement, eventMove, {
54331                // The event target has changed so we need to propagate the original one so that we keep
54332                // relying on it to extract target touches.
54333                target: event.target,
54334                handle: handle,
54335                listeners: listeners,
54336                startCalcPoint: event.calcPoint,
54337                baseSize: baseSize(),
54338                pageOffset: event.pageOffset,
54339                handleNumbers: data.handleNumbers,
54340                buttonsProperty: event.buttons,
54341                locations: scope_Locations.slice()
54342            });
54343            var endEvent = attachEvent(actions.end, scope_DocumentElement, eventEnd, {
54344                target: event.target,
54345                handle: handle,
54346                listeners: listeners,
54347                doNotReject: true,
54348                handleNumbers: data.handleNumbers
54349            });
54350            var outEvent = attachEvent("mouseout", scope_DocumentElement, documentLeave, {
54351                target: event.target,
54352                handle: handle,
54353                listeners: listeners,
54354                doNotReject: true,
54355                handleNumbers: data.handleNumbers
54356            });
54357            // We want to make sure we pushed the listeners in the listener list rather than creating
54358            // a new one as it has already been passed to the event handlers.
54359            listeners.push.apply(listeners, moveEvent.concat(endEvent, outEvent));
54360            // Text selection isn't an issue on touch devices,
54361            // so adding cursor styles can be skipped.
54362            if (event.cursor) {
54363                // Prevent the 'I' cursor and extend the range-drag cursor.
54364                scope_Body.style.cursor = getComputedStyle(event.target).cursor;
54365                // Mark the target with a dragging state.
54366                if (scope_Handles.length > 1) {
54367                    addClass(scope_Target, options.cssClasses.drag);
54368                }
54369                // Prevent text selection when dragging the handles.
54370                // In noUiSlider <= 9.2.0, this was handled by calling preventDefault on mouse/touch start/move,
54371                // which is scroll blocking. The selectstart event is supported by FireFox starting from version 52,
54372                // meaning the only holdout is iOS Safari. This doesn't matter: text selection isn't triggered there.
54373                // The 'cursor' flag is false.
54374                // See: http://caniuse.com/#search=selectstart
54375                scope_Body.addEventListener("selectstart", preventDefault, false);
54376            }
54377            data.handleNumbers.forEach(function (handleNumber) {
54378                fireEvent("start", handleNumber);
54379            });
54380        }
54381        // Move closest handle to tapped location.
54382        function eventTap(event) {
54383            // The tap event shouldn't propagate up
54384            event.stopPropagation();
54385            var proposal = calcPointToPercentage(event.calcPoint);
54386            var handleNumber = getClosestHandle(proposal);
54387            // Tackle the case that all handles are 'disabled'.
54388            if (handleNumber === false) {
54389                return false;
54390            }
54391            // Flag the slider as it is now in a transitional state.
54392            // Transition takes a configurable amount of ms (default 300). Re-enable the slider after that.
54393            if (!options.events.snap) {
54394                addClassFor(scope_Target, options.cssClasses.tap, options.animationDuration);
54395            }
54396            setHandle(handleNumber, proposal, true, true);
54397            setZindex();
54398            fireEvent("slide", handleNumber, true);
54399            fireEvent("update", handleNumber, true);
54400            fireEvent("change", handleNumber, true);
54401            fireEvent("set", handleNumber, true);
54402            if (options.events.snap) {
54403                eventStart(event, { handleNumbers: [handleNumber] });
54404            }
54405        }
54406        // Fires a 'hover' event for a hovered mouse/pen position.
54407        function eventHover(event) {
54408            var proposal = calcPointToPercentage(event.calcPoint);
54409            var to = scope_Spectrum.getStep(proposal);
54410            var value = scope_Spectrum.fromStepping(to);
54411            Object.keys(scope_Events).forEach(function (targetEvent) {
54412                if ("hover" === targetEvent.split(".")[0]) {
54413                    scope_Events[targetEvent].forEach(function (callback) {
54414                        callback.call(scope_Self, value);
54415                    });
54416                }
54417            });
54418        }
54419        // Handles keydown on focused handles
54420        // Don't move the document when pressing arrow keys on focused handles
54421        function eventKeydown(event, handleNumber) {
54422            if (isSliderDisabled() || isHandleDisabled(handleNumber)) {
54423                return false;
54424            }
54425            var horizontalKeys = ["Left", "Right"];
54426            var verticalKeys = ["Down", "Up"];
54427            var largeStepKeys = ["PageDown", "PageUp"];
54428            var edgeKeys = ["Home", "End"];
54429            if (options.dir && !options.ort) {
54430                // On an right-to-left slider, the left and right keys act inverted
54431                horizontalKeys.reverse();
54432            }
54433            else if (options.ort && !options.dir) {
54434                // On a top-to-bottom slider, the up and down keys act inverted
54435                verticalKeys.reverse();
54436                largeStepKeys.reverse();
54437            }
54438            // Strip "Arrow" for IE compatibility. https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key
54439            var key = event.key.replace("Arrow", "");
54440            var isLargeDown = key === largeStepKeys[0];
54441            var isLargeUp = key === largeStepKeys[1];
54442            var isDown = key === verticalKeys[0] || key === horizontalKeys[0] || isLargeDown;
54443            var isUp = key === verticalKeys[1] || key === horizontalKeys[1] || isLargeUp;
54444            var isMin = key === edgeKeys[0];
54445            var isMax = key === edgeKeys[1];
54446            if (!isDown && !isUp && !isMin && !isMax) {
54447                return true;
54448            }
54449            event.preventDefault();
54450            var to;
54451            if (isUp || isDown) {
54452                var multiplier = options.keyboardPageMultiplier;
54453                var direction = isDown ? 0 : 1;
54454                var steps = getNextStepsForHandle(handleNumber);
54455                var step = steps[direction];
54456                // At the edge of a slider, do nothing
54457                if (step === null) {
54458                    return false;
54459                }
54460                // No step set, use the default of 10% of the sub-range
54461                if (step === false) {
54462                    step = scope_Spectrum.getDefaultStep(scope_Locations[handleNumber], isDown, options.keyboardDefaultStep);
54463                }
54464                if (isLargeUp || isLargeDown) {
54465                    step *= multiplier;
54466                }
54467                // Step over zero-length ranges (#948);
54468                step = Math.max(step, 0.0000001);
54469                // Decrement for down steps
54470                step = (isDown ? -1 : 1) * step;
54471                to = scope_Values[handleNumber] + step;
54472            }
54473            else if (isMax) {
54474                // End key
54475                to = options.spectrum.xVal[options.spectrum.xVal.length - 1];
54476            }
54477            else {
54478                // Home key
54479                to = options.spectrum.xVal[0];
54480            }
54481            setHandle(handleNumber, scope_Spectrum.toStepping(to), true, true);
54482            fireEvent("slide", handleNumber);
54483            fireEvent("update", handleNumber);
54484            fireEvent("change", handleNumber);
54485            fireEvent("set", handleNumber);
54486            return false;
54487        }
54488        // Attach events to several slider parts.
54489        function bindSliderEvents(behaviour) {
54490            // Attach the standard drag event to the handles.
54491            if (!behaviour.fixed) {
54492                scope_Handles.forEach(function (handle, index) {
54493                    // These events are only bound to the visual handle
54494                    // element, not the 'real' origin element.
54495                    attachEvent(actions.start, handle.children[0], eventStart, {
54496                        handleNumbers: [index]
54497                    });
54498                });
54499            }
54500            // Attach the tap event to the slider base.
54501            if (behaviour.tap) {
54502                attachEvent(actions.start, scope_Base, eventTap, {});
54503            }
54504            // Fire hover events
54505            if (behaviour.hover) {
54506                attachEvent(actions.move, scope_Base, eventHover, {
54507                    hover: true
54508                });
54509            }
54510            // Make the range draggable.
54511            if (behaviour.drag) {
54512                scope_Connects.forEach(function (connect, index) {
54513                    if (connect === false || index === 0 || index === scope_Connects.length - 1) {
54514                        return;
54515                    }
54516                    var handleBefore = scope_Handles[index - 1];
54517                    var handleAfter = scope_Handles[index];
54518                    var eventHolders = [connect];
54519                    addClass(connect, options.cssClasses.draggable);
54520                    // When the range is fixed, the entire range can
54521                    // be dragged by the handles. The handle in the first
54522                    // origin will propagate the start event upward,
54523                    // but it needs to be bound manually on the other.
54524                    if (behaviour.fixed) {
54525                        eventHolders.push(handleBefore.children[0]);
54526                        eventHolders.push(handleAfter.children[0]);
54527                    }
54528                    eventHolders.forEach(function (eventHolder) {
54529                        attachEvent(actions.start, eventHolder, eventStart, {
54530                            handles: [handleBefore, handleAfter],
54531                            handleNumbers: [index - 1, index]
54532                        });
54533                    });
54534                });
54535            }
54536        }
54537        // Attach an event to this slider, possibly including a namespace
54538        function bindEvent(namespacedEvent, callback) {
54539            scope_Events[namespacedEvent] = scope_Events[namespacedEvent] || [];
54540            scope_Events[namespacedEvent].push(callback);
54541            // If the event bound is 'update,' fire it immediately for all handles.
54542            if (namespacedEvent.split(".")[0] === "update") {
54543                scope_Handles.forEach(function (a, index) {
54544                    fireEvent("update", index);
54545                });
54546            }
54547        }
54548        function isInternalNamespace(namespace) {
54549            return namespace === INTERNAL_EVENT_NS.aria || namespace === INTERNAL_EVENT_NS.tooltips;
54550        }
54551        // Undo attachment of event
54552        function removeEvent(namespacedEvent) {
54553            var event = namespacedEvent && namespacedEvent.split(".")[0];
54554            var namespace = event ? namespacedEvent.substring(event.length) : namespacedEvent;
54555            Object.keys(scope_Events).forEach(function (bind) {
54556                var tEvent = bind.split(".")[0];
54557                var tNamespace = bind.substring(tEvent.length);
54558                if ((!event || event === tEvent) && (!namespace || namespace === tNamespace)) {
54559                    // only delete protected internal event if intentional
54560                    if (!isInternalNamespace(tNamespace) || namespace === tNamespace) {
54561                        delete scope_Events[bind];
54562                    }
54563                }
54564            });
54565        }
54566        // External event handling
54567        function fireEvent(eventName, handleNumber, tap) {
54568            Object.keys(scope_Events).forEach(function (targetEvent) {
54569                var eventType = targetEvent.split(".")[0];
54570                if (eventName === eventType) {
54571                    scope_Events[targetEvent].forEach(function (callback) {
54572                        callback.call(
54573                        // Use the slider public API as the scope ('this')
54574                        scope_Self, 
54575                        // Return values as array, so arg_1[arg_2] is always valid.
54576                        scope_Values.map(options.format.to), 
54577                        // Handle index, 0 or 1
54578                        handleNumber, 
54579                        // Un-formatted slider values
54580                        scope_Values.slice(), 
54581                        // Event is fired by tap, true or false
54582                        tap || false, 
54583                        // Left offset of the handle, in relation to the slider
54584                        scope_Locations.slice(), 
54585                        // add the slider public API to an accessible parameter when this is unavailable
54586                        scope_Self);
54587                    });
54588                }
54589            });
54590        }
54591        // Split out the handle positioning logic so the Move event can use it, too
54592        function checkHandlePosition(reference, handleNumber, to, lookBackward, lookForward, getValue) {
54593            var distance;
54594            // For sliders with multiple handles, limit movement to the other handle.
54595            // Apply the margin option by adding it to the handle positions.
54596            if (scope_Handles.length > 1 && !options.events.unconstrained) {
54597                if (lookBackward && handleNumber > 0) {
54598                    distance = scope_Spectrum.getAbsoluteDistance(reference[handleNumber - 1], options.margin, 0);
54599                    to = Math.max(to, distance);
54600                }
54601                if (lookForward && handleNumber < scope_Handles.length - 1) {
54602                    distance = scope_Spectrum.getAbsoluteDistance(reference[handleNumber + 1], options.margin, 1);
54603                    to = Math.min(to, distance);
54604                }
54605            }
54606            // The limit option has the opposite effect, limiting handles to a
54607            // maximum distance from another. Limit must be > 0, as otherwise
54608            // handles would be unmovable.
54609            if (scope_Handles.length > 1 && options.limit) {
54610                if (lookBackward && handleNumber > 0) {
54611                    distance = scope_Spectrum.getAbsoluteDistance(reference[handleNumber - 1], options.limit, 0);
54612                    to = Math.min(to, distance);
54613                }
54614                if (lookForward && handleNumber < scope_Handles.length - 1) {
54615                    distance = scope_Spectrum.getAbsoluteDistance(reference[handleNumber + 1], options.limit, 1);
54616                    to = Math.max(to, distance);
54617                }
54618            }
54619            // The padding option keeps the handles a certain distance from the
54620            // edges of the slider. Padding must be > 0.
54621            if (options.padding) {
54622                if (handleNumber === 0) {
54623                    distance = scope_Spectrum.getAbsoluteDistance(0, options.padding[0], 0);
54624                    to = Math.max(to, distance);
54625                }
54626                if (handleNumber === scope_Handles.length - 1) {
54627                    distance = scope_Spectrum.getAbsoluteDistance(100, options.padding[1], 1
54627);
54628                    to = Math.min(to, distance);
54629                }
54630            }
54631            to = scope_Spectrum.getStep(to);
54632            // Limit percentage to the 0 - 100 range
54633            to = limit(to);
54634            // Return false if handle can't move
54635            if (to === reference[handleNumber] && !getValue) {
54636                return false;
54637            }
54638            return to;
54639        }
54640        // Uses slider orientation to create CSS rules. a = base value;
54641        function inRuleOrder(v, a) {
54642            var o = options.ort;
54643            return (o ? a : v) + ", " + (o ? v : a);
54644        }
54645        // Moves handle(s) by a percentage
54646        // (bool, % to move, [% where handle started, ...], [index in scope_Handles, ...])
54647        function moveHandles(upward, proposal, locations, handleNumbers) {
54648            var proposals = locations.slice();
54649            var b = [!upward, upward];
54650            var f = [upward, !upward];
54651            // Copy handleNumbers so we don't change the dataset
54652            handleNumbers = handleNumbers.slice();
54653            // Check to see which handle is 'leading'.
54654            // If that one can't move the second can't either.
54655            if (upward) {
54656                handleNumbers.reverse();
54657            }
54658            // Step 1: get the maximum percentage that any of the handles can move
54659            if (handleNumbers.length > 1) {
54660                handleNumbers.forEach(function (handleNumber, o) {
54661                    var to = checkHandlePosition(proposals, handleNumber, proposals[handleNumber] + proposal, b[o], f[o], false);
54662                    // Stop if one of the handles can't move.
54663                    if (to === false) {
54664                        proposal = 0;
54665                    }
54666                    else {
54667                        proposal = to - proposals[handleNumber];
54668                        proposals[handleNumber] = to;
54669                    }
54670                });
54671            }
54672            // If using one handle, check backward AND forward
54673            else {
54674                b = f = [true];
54675            }
54676            var state = false;
54677            // Step 2: Try to set the handles with the found percentage
54678            handleNumbers.forEach(function (handleNumber, o) {
54679                state = setHandle(handleNumber, locations[handleNumber] + proposal, b[o], f[o]) || state;
54680            });
54681            // Step 3: If a handle moved, fire events
54682            if (state) {
54683                handleNumbers.forEach(function (handleNumber) {
54684                    fireEvent("update", handleNumber);
54685                    fireEvent("slide", handleNumber);
54686                });
54687            }
54688        }
54689        // Takes a base value and an offset. This offset is used for the connect bar size.
54690        // In the initial design for this feature, the origin element was 1% wide.
54691        // Unfortunately, a rounding bug in Chrome makes it impossible to implement this feature
54692        // in this manner: https://bugs.chromium.org/p/chromium/issues/detail?id=798223
54693        function transformDirection(a, b) {
54694            return options.dir ? 100 - a - b : a;
54695        }
54696        // Updates scope_Locations and scope_Values, updates visual state
54697        function updateHandlePosition(handleNumber, to) {
54698            // Update locations.
54699            scope_Locations[handleNumber] = to;
54700            // Convert the value to the slider stepping/range.
54701            scope_Values[handleNumber] = scope_Spectrum.fromStepping(to);
54702            var translation = 10 * (transformDirection(to, 0) - scope_DirOffset);
54703            var translateRule = "translate(" + inRuleOrder(translation + "%", "0") + ")";
54704            scope_Handles[handleNumber].style[options.transformRule] = translateRule;
54705            updateConnect(handleNumber);
54706            updateConnect(handleNumber + 1);
54707        }
54708        // Handles before the slider middle are stacked later = higher,
54709        // Handles after the middle later is lower
54710        // [[7] [8] .......... | .......... [5] [4]
54711        function setZindex() {
54712            scope_HandleNumbers.forEach(function (handleNumber) {
54713                var dir = scope_Locations[handleNumber] > 50 ? -1 : 1;
54714                var zIndex = 3 + (scope_Handles.length + dir * handleNumber);
54715                scope_Handles[handleNumber].style.zIndex = zIndex;
54716            });
54717        }
54718        // Test suggested values and apply margin, step.
54719        // if exactInput is true, don't run checkHandlePosition, then the handle can be placed in between steps (#436)
54720        function setHandle(handleNumber, to, lookBackward, lookForward, exactInput) {
54721            if (!exactInput) {
54722                to = checkHandlePosition(scope_Locations, handleNumber, to, lookBackward, lookForward, false);
54723            }
54724            if (to === false) {
54725                return false;
vendor: 12,979 bytes, lines 54726-55021
54726            }
54727            updateHandlePosition(handleNumber, to);
54728            return true;
54729        }
54730        // Updates style attribute for connect nodes
54731        function updateConnect(index) {
54732            // Skip connects set to false
54733            if (!scope_Connects[index]) {
54734                return;
54735            }
54736            var l = 0;
54737            var h = 100;
54738            if (index !== 0) {
54739                l = scope_Locations[index - 1];
54740            }
54741            if (index !== scope_Connects.length - 1) {
54742                h = scope_Locations[index];
54743            }
54744            // We use two rules:
54745            // 'translate' to change the left/top offset;
54746            // 'scale' to change the width of the element;
54747            // As the element has a width of 100%, a translation of 100% is equal to 100% of the parent (.noUi-base)
54748            var connectWidth = h - l;
54749            var translateRule = "translate(" + inRuleOrder(transformDirection(l, connectWidth) + "%", "0") + ")";
54750            var scaleRule = "scale(" + inRuleOrder(connectWidth / 100, "1") + ")";
54751            scope_Connects[index].style[options.transformRule] = translateRule + " " + scaleRule;
54752        }
54753        // Parses value passed to .set method. Returns current value if not parse-able.
54754        function resolveToValue(to, handleNumber) {
54755            // Setting with null indicates an 'ignore'.
54756            // Inputting 'false' is invalid.
54757            if (to === null || to === false || to === undefined) {
54758                return scope_Locations[handleNumber];
54759            }
54760            // If a formatted number was passed, attempt to decode it.
54761            if (typeof to === "number") {
54762                to = String(to);
54763            }
54764            to = options.format.from(to);
54765            to = scope_Spectrum.toStepping(to);
54766            // If parsing the number failed, use the current value.
54767            if (to === false || isNaN(to)) {
54768                return scope_Locations[handleNumber];
54769            }
54770            return to;
54771        }
54772        // Set the slider value.
54773        function valueSet(input, fireSetEvent, exactInput) {
54774            var values = asArray(input);
54775            var isInit = scope_Locations[0] === undefined;
54776            // Event fires by default
54777            fireSetEvent = fireSetEvent === undefined ? true : !!fireSetEvent;
54778            // Animation is optional.
54779            // Make sure the initial values were set before using animated placement.
54780            if (options.animate && !isInit) {
54781                addClassFor(scope_Target, options.cssClasses.tap, options.animationDuration);
54782            }
54783            // First pass, without lookAhead but with lookBackward. Values are set from left to right.
54784            scope_HandleNumbers.forEach(function (handleNumber) {
54785                setHandle(handleNumber, resolveToValue(values[handleNumber], handleNumber), true, false, exactInput);
54786            });
54787            var i = scope_HandleNumbers.length === 1 ? 0 : 1;
54788            // Secondary passes. Now that all base values are set, apply constraints.
54789            // Iterate all handles to ensure constraints are applied for the entire slider (Issue #1009)
54790            for (; i < scope_HandleNumbers.length; ++i) {
54791                scope_HandleNumbers.forEach(function (handleNumber) {
54792                    setHandle(handleNumber, scope_Locations[handleNumber], true, true, exactInput);
54793                });
54794            }
54795            setZindex();
54796            scope_HandleNumbers.forEach(function (handleNumber) {
54797                fireEvent("update", handleNumber);
54798                // Fire the event only for handles that received a new value, as per #579
54799                if (values[handleNumber] !== null && fireSetEvent) {
54800                    fireEvent("set", handleNumber);
54801                }
54802            });
54803        }
54804        // Reset slider to initial values
54805        function valueReset(fireSetEvent) {
54806            valueSet(options.start, fireSetEvent);
54807        }
54808        // Set value for a single handle
54809        function valueSetHandle(handleNumber, value, fireSetEvent, exactInput) {
54810            // Ensure numeric input
54811            handleNumber = Number(handleNumber);
54812            if (!(handleNumber >= 0 && handleNumber < scope_HandleNumbers.length)) {
54813                throw new Error("noUiSlider (" + VERSION + "): invalid handle number, got: " + handleNumber);
54814            }
54815            // Look both backward and forward, since we don't want this handle to "push" other handles (#960);
54816            // The exactInput argument can be used to ignore slider stepping (#436)
54817            setHandle(handleNumber, resolveToValue(value, handleNumber), true, true, exactInput);
54818            fireEvent("update", handleNumber);
54819            if (fireSetEvent) {
54820                fireEvent("set", handleNumber);
54821            }
54822        }
54823        // Get the slider value.
54824        function valueGet() {
54825            var values = scope_Values.map(options.format.to);
54826            // If only one handle is used, return a single value.
54827            if (values.length === 1) {
54828                return values[0];
54829            }
54830            return values;
54831        }
54832        // Removes classes from the root and empties it.
54833        function destroy() {
54834            // remove protected internal listeners
54835            removeEvent(INTERNAL_EVENT_NS.aria);
54836            removeEvent(INTERNAL_EVENT_NS.tooltips);
54837            for (var key in options.cssClasses) {
54838                if (!options.cssClasses.hasOwnProperty(key)) {
54839                    continue;
54840                }
54841                removeClass(scope_Target, options.cssClasses[key]);
54842            }
54843            while (scope_Target.firstChild) {
54844                scope_Target.removeChild(scope_Target.firstChild);
54845            }
54846            delete scope_Target.noUiSlider;
54847        }
54848        function getNextStepsForHandle(handleNumber) {
54849            var location = scope_Locations[handleNumber];
54850            var nearbySteps = scope_Spectrum.getNearbySteps(location);
54851            var value = scope_Values[handleNumber];
54852            var increment = nearbySteps.thisStep.step;
54853            var decrement = null;
54854            // If snapped, directly use defined step value
54855            if (options.snap) {
54856                return [
54857                    value - nearbySteps.stepBefore.startValue || null,
54858                    nearbySteps.stepAfter.startValue - value || null
54859                ];
54860            }
54861            // If the next value in this step moves into the next step,
54862            // the increment is the start of the next step - the current value
54863            if (increment !== false) {
54864                if (value + increment > nearbySteps.stepAfter.startValue) {
54865                    increment = nearbySteps.stepAfter.startValue - value;
54866                }
54867            }
54868            // If the value is beyond the starting point
54869            if (value > nearbySteps.thisStep.startValue) {
54870                decrement = nearbySteps.thisStep.step;
54871            }
54872            else if (nearbySteps.stepBefore.step === false) {
54873                decrement = false;
54874            }
54875            // If a handle is at the start of a step, it always steps back into the previous step first
54876            else {
54877                decrement = value - nearbySteps.stepBefore.highestStep;
54878            }
54879            // Now, if at the slider edges, there is no in/decrement
54880            if (location === 100) {
54881                increment = null;
54882            }
54883            else if (location === 0) {
54884                decrement = null;
54885            }
54886            // As per #391, the comparison for the decrement step can have some rounding issues.
54887            var stepDecimals = scope_Spectrum.countStepDecimals();
54888            // Round per #391
54889            if (increment !== null && increment !== false) {
54890                increment = Number(increment.toFixed(stepDecimals));
54891            }
54892            if (decrement !== null && decrement !== false) {
54893                decrement = Number(decrement.toFixed(stepDecimals));
54894            }
54895            return [decrement, increment];
54896        }
54897        // Get the current step size for the slider.
54898        function getNextSteps() {
54899            return scope_HandleNumbers.map(getNextStepsForHandle);
54900        }
54901        // Updateable: margin, limit, padding, step, range, animate, snap
54902        function updateOptions(optionsToUpdate, fireSetEvent) {
54903            // Spectrum is created using the range, snap, direction and step options.
54904            // 'snap' and 'step' can be updated.
54905            // If 'snap' and 'step' are not passed, they should remain unchanged.
54906            var v = valueGet();
54907            var updateAble = [
54908                "margin",
54909                "limit",
54910                "padding",
54911                "range",
54912                "animate",
54913                "snap",
54914                "step",
54915                "format",
54916                "pips",
54917                "tooltips"
54918            ];
54919            // Only change options that we're actually passed to update.
54920            updateAble.forEach(function (name) {
54921                // Check for undefined. null removes the value.
54922                if (optionsToUpdate[name] !== undefined) {
54923                    originalOptions[name] = optionsToUpdate[name];
54924                }
54925            });
54926            var newOptions = testOptions(originalOptions);
54927            // Load new options into the slider state
54928            updateAble.forEach(function (name) {
54929                if (optionsToUpdate[name] !== undefined) {
54930                    options[name] = newOptions[name];
54931                }
54932            });
54933            scope_Spectrum = newOptions.spectrum;
54934            // Limit, margin and padding depend on the spectrum but are stored outside of it. (#677)
54935            options.margin = newOptions.margin;
54936            options.limit = newOptions.limit;
54937            options.padding = newOptions.padding;
54938            // Update pips, removes existing.
54939            if (options.pips) {
54940                pips(options.pips);
54941            }
54942            else {
54943                removePips();
54944            }
54945            // Update tooltips, removes existing.
54946            if (options.tooltips) {
54947                tooltips();
54948            }
54949            else {
54950                removeTooltips();
54951            }
54952            // Invalidate the current positioning so valueSet forces an update.
54953            scope_Locations = [];
54954            valueSet(isSet(optionsToUpdate.start) ? optionsToUpdate.start : v, fireSetEvent);
54955        }
54956        // Initialization steps
54957        function setupSlider() {
54958            // Create the base element, initialize HTML and set classes.
54959            // Add handles and connect elements.
54960            scope_Base = addSlider(scope_Target);
54961            addElements(options.connect, scope_Base);
54962            // Attach user events.
54963            bindSliderEvents(options.events);
54964            // Use the public value method to set the start values.
54965            valueSet(options.start);
54966            if (options.pips) {
54967                pips(options.pips);
54968            }
54969            if (options.tooltips) {
54970                tooltips();
54971            }
54972            aria();
54973        }
54974        setupSlider();
54975        // noinspection JSUnusedGlobalSymbols
54976        scope_Self = {
54977            destroy: destroy,
54978            steps: getNextSteps,
54979            on: bindEvent,
54980            off: removeEvent,
54981            get: valueGet,
54982            set: valueSet,
54983            setHandle: valueSetHandle,
54984            reset: valueReset,
54985            // Exposed for unit testing, don't use this in your application.
54986            __moveHandles: function (a, b, c) {
54987                moveHandles(a, b, scope_Locations, c);
54988            },
54989            options: originalOptions,
54990            updateOptions: updateOptions,
54991            target: scope_Target,
54992            removePips: removePips,
54993            removeTooltips: removeTooltips,
54994            getTooltips: function () {
54995                return scope_Tooltips;
54996            },
54997            getOrigins: function () {
54998                return scope_Handles;
54999            },
55000            pips: pips // Issue #594
55001        };
55002        return scope_Self;
55003    }
55004    // Run the standard initializer
55005    function initialize(target, originalOptions) {
55006        if (!target || !target.nodeName) {
55007            throw new Error("noUiSlider (" + VERSION + "): create requires a single element, got: " + target);
55008        }
55009        // Throw an error if the slider was already initialized.
55010        if (target.noUiSlider) {
55011            throw new Error("noUiSlider (" + VERSION + "): Slider was already initialized.");
55012        }
55013        // Test the options and create the slider environment;
55014        var options = testOptions(originalOptions);
55015        var api = scope(target, options, originalOptions);
55016        target.noUiSlider = api;
55017        return api;
55018    }
55019    // Use an object instead of a function for future expandability;
55020    return {
55021        // Exposed for unit testing, don't use this in your application.
55022        __spectrum: Spectrum,
55023        version: VERSION,
55024        // A reference to the default classes, allows global changes.
55025        // Use the cssClasses option for changes to one slider.
55026        cssClasses: cssClasses,
55027        create: initialize
55028    };
55029});
55030
55031/**
55032 * @namespace Global top namespace
55033 */
55034var revide = {};
55035
55036(function () {
55037  $(document).ready(function () {
55038    $("#forgot-password").click(function (e) {
55039      $("#login-box").hide();
55040      $("#reset-password-error").hide();
55041      $("#resend-password-box").show();
55042    });
55043
55044    $("#close-resend-password-box").click(function (e) {
55045      $("#login-box").show();
55046      $("#reset-password-error").hide();
55047      $("#resend-password-box").hide();
55048    });
55049
55050    $("#resend-password").click(function (e) {
55051      $("#reset-password-error").hide();
55052      $.ajax({
55053        type: "POST",
55054        traditional: true,
55055        url: "/umbraco/surface/member/resetpassword",
55056        data: {
55057          email: $("#resend-email-input").val().trim(),
55058        },
55059        dataType: "json",
55060        success: function (response) {
55061          $("#reset-password-error").html(response.messages);
55062          $("#reset-password-error").show();
55063        },
55064      });
55065    });
55066
55067    //Mobile
55068    $("#mob-forgot-password").click(function (e) {
55069      $("#mob-login-box").hide();
55070      $("#mob-reset-password-error").hide();
55071      $("#mob-resend-password-box").show();
55072    });
55073
55074    $("#mob-close-resend-password-box").click(function (e) {
55075      $("#mob-login-box").show();
55076      $("#mob-reset-password-error").hide();
55077      $("#mob-resend-password-box").hide();
55078    });
55079
55080    $("#mob-resend-password").click(function (e) {
55081      $("#mob-reset-password-error").hide();
55082      $.ajax({
55083        type: "POST",
55084        traditional: true,
55085        url: "/umbraco/surface/member/resetpassword",
55086        data: {
55087          email: $("#mob-resend-email-input").val(),
55088        },
55089        dataType: "json",
55090        success: function (response) {
55091          $("#mob-reset-password-error").html(response.messages);
55092          $("#mob-reset-password-error").show();
55093        },
55094      });
55095    });
55096  });
55097})();
55098
55099(function () {
55100  "use strict";
55101
55102  angular.module("webApp", ["ngAnimate", "ngCookies", "ngTouch", "ngSanitize", "ngMessages", "ngAria", "ngResource"]);
55103})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.