PageSourceSearch

https://schlemmer-marbach.de/js/big_js.js?n=9524

js schlemmer-marbach.de collected 2026-10-02 01:00:45 UTC 2,153,537 bytes, 55,564 lines download raw bytes

vendor: 4,536 bytes, lines 1-2
1/*! jQuery v1.11.2 | (c) 2005, 2014 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="1.11.2",m=function(a,b){return new m.fn.init(a,b)},n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,o=/^-ms-/,p=/-([\da-z])/gi,q=function(a,b){return b.toUpperCase()};m.fn=m.prototype={jquery:l,constructor:m,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=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.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},m.extend=m.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||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},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=r(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(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[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=r(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&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b=a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=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: 3,071 bytes, line 2
2w Date,v=a.document,w=0,x=0,y=hb(),z=hb(),A=hb(),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-]+))$/,ab=/[+~]/,bb=/'|\\/g,cb=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),db=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)},eb=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(fb){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 gb(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(bb,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+rb(o[l]);w=ab.test(a)&&pb(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 hb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ib(a){return a[u]=!0,a}
vendor: 9,785 bytes, line 2
2function jb(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function kb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function lb(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 mb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function nb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function ob(a){return ib(function(b){return b=+b,ib(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 pb(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=gb.support={},f=gb.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=gb.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",eb,!1):e.attachEvent&&e.attachEvent("onunload",eb)),p=!f(g),c.attributes=jb(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=jb(function(a){return a.appendChild(g.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(g.getElementsByClassName),c.getById=jb(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(cb,db);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(cb,db);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))&&(jb(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(".#.+[+~]")}),jb(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))&&jb(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 lb(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?lb(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},g):n},gb.matches=function(a,b){return gb(a,null,null,b)},gb.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 gb(b,n,null,[a]).length>0},gb.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},gb.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},gb.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},gb.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=gb.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=gb.selectors={cacheLength:50,createPseudo:ib,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(cb,db),a[3]=(a[3]||a[4]||a[5]||"").replace(cb,db),"~="===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]||gb.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]&&gb.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(cb,db).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=gb.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()]||gb.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ib(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:ib(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?ib(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:ib(function(a){return function(b){return gb(a,b).length>0}}),contains:ib(function(a){return a=a.replace(cb,db),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ib(function(a){return W.test(a||"")||gb.error("unsupported lang: "+a),a=a.replace(cb,db).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)}
vendor: 7,673 bytes, line 2
2,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:ob(function(){return[0]}),last:ob(function(a,b){return[b-1]}),eq:ob(function(a,b,c){return[0>c?c+b:c]}),even:ob(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:ob(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:ob(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:ob(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]=mb(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=nb(b);function qb(){}qb.prototype=d.filters=d.pseudos,d.setFilters=new qb,g=gb.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?gb.error(a):z(a,i).slice(0)};function rb(a){for(var b=0,c=a.length,d="";c>b;b++)d+=a[b].value;return d}function sb(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 tb(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 ub(a,b,c){for(var d=0,e=b.length;e>d;d++)gb(a,b[d],c);return c}function vb(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 wb(a,b,c,d,e,f){return d&&!d[u]&&(d=wb(d)),e&&!e[u]&&(e=wb(e,f)),ib(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ub(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:vb(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=vb(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=vb(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function xb(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=sb(function(a){return a===b},h,!0),l=sb(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=[sb(tb(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 wb(i>1&&tb(m),i>1&&rb(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&xb(a.slice(i,e)),f>e&&xb(a=a.slice(e)),f>e&&rb(a))}m.push(c)}return tb(m)}function yb(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=vb(s)}H.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&gb.uniqueSort(i)}return k&&(w=v,j=t),r};return c?ib(f):f}return h=gb.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=xb(b[c]),f[u]?d.push(f):e.push(f);f=A(a,yb(e,d)),f.selector=a}return f},i=gb.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(cb,db),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(cb,db),ab.test(j[0].type)&&pb(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&rb(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,ab.test(a)&&pb(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=jb(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),jb(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||kb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&jb(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||kb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),jb(function(a){return null==a.getAttribute("disabled")})||kb(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}),gb}(a);m.find=s,m.expr=s.selectors,m.expr[":"]=m.expr.pseudos,m.unique=s.uniqueSort,m.text=s.getText,m.isXMLDoc=s.isXML,m.contains=s.contains;var t=m.expr.match.needsContext,u=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,v=/^.[^:#\[\.,]*$/;function w(a,b,c){if(m.isFunction(b))return m.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return m.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(v.test(b))return m.filter(b,a,c);b=m.filter(b,a)}return m.grep(a,function(a){return m.inArray(a,b)>=0!==c})}m.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?m.find.matchesSelector(d,a)?[d]:[]:m.find.matches(a,m.grep(b,function(a){return 1===a.nodeType}))},m.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(m(a).filter(function(){for(b=0;e>b;b++)if(m.contains(d[b],this))return!0}));for(b=0;e>b;b++)m.find(a,d[b],c);return c=this.pushStack(e>1?m.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(w(this,a||[],!1))},not:function(a){return this.pushStack(w(this,a||[],!0))},is:function(a){return!!w(this,"string"==typeof a&&t.test(a)?m(a):a||[],!1).length}});var x,y=a.document,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=m.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||x).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof m?b[0]:b,m.merge(this,m.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:y,!0)),u.test(c[1])&&m.isPlainObject(b))for(c in b)m.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=y.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return x.find(a);this.length=1,this[0]=d}return this.context=y,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):m.isFunction(a)?"undefined"!=typeof x.ready?x.ready(a):a(m):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),m.makeArray(a,this))};A.prototype=m.fn,x=m(y);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};m.extend({dir:function(a,b,c){var d=[],e=a[b];
vendor: 4,825 bytes, line 2
2while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!m(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),m.fn.extend({has:function(a){var b,c=m(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(m.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=t.test(a)||"string"!=typeof a?m(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&&m.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?m.unique(f):f)},index:function(a){return a?"string"==typeof a?m.inArray(this[0],m(a)):m.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(m.unique(m.merge(this.get(),m(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}m.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return m.dir(a,"parentNode")},parentsUntil:function(a,b,c){return m.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return m.dir(a,"nextSibling")},prevAll:function(a){return m.dir(a,"previousSibling")},nextUntil:function(a,b,c){return m.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return m.dir(a,"previousSibling",c)},siblings:function(a){return m.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return m.sibling(a.firstChild)},contents:function(a){return m.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:m.merge([],a.childNodes)}},function(a,b){m.fn[a]=function(c,d){var e=m.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=m.filter(d,e)),this.length>1&&(C[a]||(e=m.unique(e)),B.test(a)&&(e=e.reverse())),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return m.each(a.match(E)||[],function(a,c){b[c]=!0}),b}m.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):m.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){m.each(b,function(b,c){var d=m.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&m.each(arguments,function(a,c){var d;while((d=m.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?m.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},m.extend({Deferred:function(a){var b=[["resolve","done",m.Callbacks("once memory"),"resolved"],["reject","fail",m.Callbacks("once memory"),"rejected"],["notify","progress",m.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return m.Deferred(function(c){m.each(b,function(b,f){var g=m.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&m.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?m.extend(a,d):d}},e={};return d.pipe=d.then,m.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&&m.isFunction(a.promise)?e:0,g=1===f?a:m.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]&&m.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;m.fn.ready=function(a){return m.ready.promise().done(a),this},m.extend({isReady:!1,readyWait:1,holdReady:function(a){a?m.readyWait++:m.ready(!0)}
vendor: 4,061 bytes, lines 2-3
2,ready:function(a){if(a===!0?!--m.readyWait:!m.isReady){if(!y.body)return setTimeout(m.ready);m.isReady=!0,a!==!0&&--m.readyWait>0||(H.resolveWith(y,[m]),m.fn.triggerHandler&&(m(y).triggerHandler("ready"),m(y).off("ready")))}}});function I(){y.addEventListener?(y.removeEventListener("DOMContentLoaded",J,!1),a.removeEventListener("load",J,!1)):(y.detachEvent("onreadystatechange",J),a.detachEvent("onload",J))}function J(){(y.addEventListener||"load"===event.type||"complete"===y.readyState)&&(I(),m.ready())}m.ready.promise=function(b){if(!H)if(H=m.Deferred(),"complete"===y.readyState)setTimeout(m.ready);else if(y.addEventListener)y.addEventListener("DOMContentLoaded",J,!1),a.addEventListener("load",J,!1);else{y.attachEvent("onreadystatechange",J),a.attachEvent("onload",J);var c=!1;try{c=null==a.frameElement&&y.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!m.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}I(),m.ready()}}()}return H.promise(b)};var K="undefined",L;for(L in m(k))break;k.ownLast="0"!==L,k.inlineBlockNeedsLayout=!1,m(function(){var a,b,c,d;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",k.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(d))}),function(){var a=y.createElement("div");if(null==k.deleteExpando){k.deleteExpando=!0;try{delete a.test}catch(b){k.deleteExpando=!1}}a=null}(),m.acceptData=function(a){var b=m.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var M=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,N=/([A-Z])/g;function O(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(N,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:M.test(c)?m.parseJSON(c):c}catch(e){}m.data(a,b,c)}else c=void 0}return c}function P(a){var b;for(b in a)if(("data"!==b||!m.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;
3return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.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.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;
vendor: 18,810 bytes, line 3
3return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.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 m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?m.queue(this[0],a):void 0===b?this:this.each(function(){var c=m.queue(this,a,b);m._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&m.dequeue(this,a)})},dequeue:function(a){return this.each(function(){m.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=m.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=m._data(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var S=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=["Top","Right","Bottom","Left"],U=function(a,b){return a=b||a,"none"===m.css(a,"display")||!m.contains(a.ownerDocument,a)},V=m.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===m.type(c)){e=!0;for(h in c)m.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,m.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(m(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},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML="  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav></:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="<textarea>x</textarea>",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="<input type='radio' checked='checked' name='t'/>",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function ab(){return!0}function bb(){return!1}function cb(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=m.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return typeof m===K||a&&m.event.triggered===a.type?void 0:m.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(E)||[""],h=b.length;while(h--)f=_.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=m.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=m.event.special[o]||{},l=m.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&m.expr.match.needsContext.test(e),namespace:p.join(".")},i),(n=g[o])||(n=g[o]=[],n.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?n.splice(n.delegateCount++,0,l):n.push(l),m.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m.hasData(a)&&m._data(a);if(r&&(k=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=_.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=m.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,n=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=n.length;while(f--)g=n[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(n.splice(f,1),g.selector&&n.delegateCount--,l.remove&&l.remove.call(a,g));i&&!n.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||m.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)m.event.remove(a,o+b[j],c,d,!0);m.isEmptyObject(k)&&(delete r.handle,m._removeData(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,n,o=[d||y],p=j.call(b,"type")?b.type:b,q=j.call(b,"namespace")?b.namespace.split("."):[];if(h=l=d=d||y,3!==d.nodeType&&8!==d.nodeType&&!$.test(p+m.event.triggered)&&(p.indexOf(".")>=0&&(q=p.split("."),p=q.shift(),q.sort()),g=p.indexOf(":")<0&&"on"+p,b=b[m.expando]?b:new m.Event(p,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=q.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:m.makeArray(c,[b]),k=m.event.special[p]||{},e||!k.trigger||k.trigger.apply(d,c)!==!1)){if(!e&&!k.noBubble&&!m.isWindow(d)){for(i=k.delegateType||p,$.test(i+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),l=h;l===(d.ownerDocument||y)&&o.push(l.defaultView||l.parentWindow||a)}n=0;while((h=o[n++])&&!b.isPropagationStopped())b.type=n>1?i:k.bindType||p,f=(m._data(h,"events")||{})[b.type]&&m._data(h,"handle"),f&&f.apply(h,c),f=g&&h[g],f&&f.apply&&m.acceptData(h)&&(b.result=f.apply(h,c),b.result===!1&&b.preventDefault());if(b.type=p,!e&&!b.isDefaultPrevented()&&(!k._default||k._default.apply(o.pop(),c)===!1)&&m.acceptData(d)&&g&&d[p]&&!m.isWindow(d)){l=d[g],l&&(d[g]=null),m.event.triggered=p;try{d[p]()}catch(r){}m.event.triggered=void 0,l&&(d[g]=l)}return b.result}},dispatch:function(a){a=m.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(m._data(this,"events")||{})[a.type]||[],k=m.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=m.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,g=0;while((e=f.handlers[g++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(e.namespace))&&(a.handleObj=e,a.data=e.data,c=((m.event.special[e.origType]||{}).handle||e.handler).apply(f.elem,i),void 0!==c&&(a.result=c)===!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(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(e=[],f=0;h>f;f++)d=b[f],c=d.selector+" ",void 0===e[c]&&(e[c]=d.needsContext?m(c,this).index(i)>=0:m.find(c,this,null,[i]).length),e[c]&&e.push(d);e.length&&g.push({elem:i,handlers:e})}return h<b.length&&g.push({elem:this,handlers:b.slice(h)}),g},fix:function(a){if(a[m.expando])return a;var b,c,d,e=a.type,f=a,g=this.fixHooks[e];g||(this.fixHooks[e]=g=Z.test(e)?this.mouseHooks:Y.test(e)?this.keyHooks:{}),d=g.props?this.props.concat(g.props):this.props,a=new m.Event(f),b=d.length;while(b--)c=d[b],a[c]=f[c];return a.target||(a.target=f.srcElement||y),3===a.target.nodeType&&(a.target=a.target.parentNode),a.metaKey=!!a.metaKey,g.filter?g.filter(a,f):a},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 fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,b){var c,d,e,f=b.button,g=b.fromElement;return null==a.pageX&&null!=b.clientX&&(d=a.target.ownerDocument||y,e=d.documentElement,c=d.body,a.pageX=b.clientX+(e&&e.scrollLeft||c&&c.scrollLeft||0)-(e&&e.clientLeft||c&&c.clientLeft||0),a.pageY=b.clientY+(e&&e.scrollTop||c&&c.scrollTop||0)-(e&&e.clientTop||c&&c.clientTop||0)),!a.relatedTarget&&g&&(a.relatedTarget=g===a.target?b.toElement:g),a.which||void 0===f||(a.which=1&f?1:2&f?3:4&f?2:0),a}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==cb()&&this.focus)try{return this.focus(),!1}catch(a){}},delegateType:"focusin"},blur:{trigger:function(){return this===cb()&&this.blur?(this.blur(),!1):void 0},delegateType:"focusout"},click:{trigger:function(){return m.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):void 0},_default:function(a){return m.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=m.extend(new m.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?m.event.trigger(e,null,b):m.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},m.removeEvent=y.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){var d="on"+b;a.detachEvent&&(typeof a[d]===K&&(a[d]=null),a.detachEvent(d,c))},m.Event=function(a,b){return this instanceof m.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&a.returnValue===!1?ab:bb):this.type=a,b&&m.extend(this,b),this.timeStamp=a&&a.timeStamp||m.now(),void(this[m.expando]=!0)):new m.Event(a,b)},m.Event.prototype={isDefaultPrevented:bb,isPropagationStopped:bb,isImmediatePropagationStopped:bb,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=ab,a&&(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=ab,a&&(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){var a=this.originalEvent;this.isImmediatePropagationStopped=ab,a&&a.stopImmediatePropagation&&a.stopImmediatePropagation(),this.stopPropagation()}},m.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(a,b){m.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return(!e||e!==d&&!m.contains(d,e))&&(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),k.submitBubbles||(m.event.special.submit={setup:function(){return m.nodeName(this,"form")?!1:void m.event.add(this,"click._submit keypress._submit",function(a){var b=a.target,c=m.nodeName(b,"input")||m.nodeName(b,"button")?b.form:void 0;c&&!m._data(c,"submitBubbles")&&(m.event.add(c,"submit._submit",function(a){a._submit_bubble=!0}),m._data(c,"submitBubbles",!0))})},postDispatch:function(a){a._submit_bubble&&(delete a._submit_bubble,this.parentNode&&!a.isTrigger&&m.event.simulate("submit",this.parentNode,a,!0))},teardown:function(){return m.nodeName(this,"form")?!1:void m.event.remove(this,"._submit")}}),k.changeBubbles||(m.event.special.change={setup:function(){return X.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(m.event.add(this,"propertychange._change",function(a){"checked"===a.originalEvent.propertyName&&(this._just_changed=!0)}),m.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1),m.event.simulate("change",this,a,!0)})),!1):void m.event.add(this,"beforeactivate._change",function(a){var b=a.target;X.test(b.nodeName)&&!m._data(b,"changeBubbles")&&(m.event.add(b,"change._change",function(a){!this.parentNode||a.isSimulated||a.isTrigger||m.event.simulate("change",this.parentNode,a,!0)}),m._data(b,"changeBubbles",!0))})},handle:function(a){var b=a.target;return this!==b||a.isSimulated||a.isTrigger||"radio"!==b.type&&"checkbox"!==b.type?a.handleObj.handler.apply(this,arguments):void 0},teardown:function(){return m.event.remove(this,"._change"),!X.test(this.nodeName)}}),k.focusinBubbles||m.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){m.event.simulate(b,a.target,m.event.fix(a),!0)};m.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=m._data(d,b);e||d.addEventListener(a,c,!0),m._data(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=m._data(d,b)-1;e?m._data(d,b,e):(d.removeEventListener(a,c,!0),m._removeData(d,b))}}}),m.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(f in a)this.on(f,b,c,a[f],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=bb;else if(!d)return this;return 1===e&&(g=d,d=function(a){return m().off(a),g.apply(this,arguments)},d.guid=g.guid||(g.guid=m.guid++)),this.each(function(){m.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,m(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=bb),this.each(function(){m.event.remove(this,a,c,b)})},trigger:function(a,b){return this.each(function(){m.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];return c?m.event.trigger(a,b,c,!0):void 0}});function db(a){var b=eb.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}var eb="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",fb=/ jQuery\d+="(?:null|\d+)"/g,gb=new RegExp("<(?:"+eb+")[\\s/>]","i"),hb=/^\s+/,ib=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,jb=/<([\w:]+)/,kb=/<tbody/i,lb=/<|&#?\w+;/,mb=/<(?:script|style|link)/i,nb=/checked\s*(?:[^=]|=\s*.checked.)/i,ob=/^$|\/(?:java|ecma)script/i,pb=/^true\/(.*)/,qb=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,rb={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],area:[1,"<map>","</map>"],param:[1,"<object>","</object>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:k.htmlSerialize?[0,"",""]:[1,"X<div>","</div>"]},sb=db(y),tb=sb.appendChild(y.createElement("div"));rb.optgroup=rb.option,rb.tbody=rb.tfoot=rb.colgroup=rb.caption=rb.thead,rb.th=rb.td;function ub(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==K?a.getElementsByTagName(b||"*"):typeof a.querySelectorAll!==K?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||m.nodeName(d,b)?f.push(d):m.merge(f,ub(d,b));return void 0===b||b&&m.nodeName(a,b)?m.merge([a],f):f}function vb(a){W.test(a.type)&&(a.defaultChecked=a.checked)}function wb(a,b){return m.nodeName(a,"table")&&m.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function xb(a){return a.type=(null!==m.find.attr(a,"type"))+"/"+a.type,a}function yb(a){var b=pb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function zb(a,b){for(var c,d=0;null!=(c=a[d]);d++)m._data(c,"globalEval",!b||m._data(b[d],"globalEval"))}function Ab(a,b){if(1===b.nodeType&&m.hasData(a)){var c,d,e,f=m._data(a),g=m._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)m.event.add(b,c,h[c][d])}g.data&&(g.data=m.extend({},g.data))}}function Bb(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!k.noCloneEvent&&b[m.expando]){e=m._data(b);for(d in e.events)m.removeEvent(b,d,e.handle);b.removeAttribute(m.expando)}"script"===c&&b.text!==a.text?(xb(b).text=a.text,yb(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),k.html5Clone&&a.innerHTML&&!m.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&W.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}m.extend({clone:function(a,b,c){var d,e,f,g,h,i=m.contains(a.ownerDocument,a);if(k.html5Clone||m.isXMLDoc(a)||!gb.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(tb.innerHTML=a.outerHTML,tb.removeChild(f=tb.firstChild)),!(k.noCloneEvent&&k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||m.isXMLDoc(a)))for(d=ub(f),h=ub(a),g=0;null!=(e=h[g]);++g)d[g]&&Bb(e,d[g]);if(b)if(c)for(h=h||ub(a),d=d||ub(f),g=0;null!=(e=h[g]);g++)Ab(e,d[g]);else Ab(a,f);return d=ub(f,"script"),d.length>0&&zb(d,!i&&ub(a,"script")),d=h=e=null,f},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,l,n=a.length,o=db(b),p=[],q=0;n>q;q++)if(f=a[q],f||0===f)if("object"===m.type(f))m.merge(p,f.nodeType?[f]:f);else if(lb.test(f)){h=h||o.appendChild(b.createElement("div")),i=(jb.exec(f)||["",""])[1].toLowerCase(),l=rb[i]||rb._default,h.innerHTML=l[1]+f.replace(ib,"<$1></$2>")+l[2],e=l[0];while(e--)h=h.lastChild;if(!k.leadingWhitespace&&hb.test(f)&&p.push(b.createTextNode(hb.exec(f)[0])),!k.tbody){f="table"!==i||kb.test(f)?"<table>"!==l[1]||kb.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)m.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}m.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),k.appendChecked||m.grep(ub(p,"input"),vb),q=0;while(f=p[q++])if((!d||-1===m.inArray(f,d))&&(g=m.contains(f.ownerDocument,f),h=ub(o.appendChild(f),"script"),g&&zb(h),c)){e=0;while(f=h[e++])ob.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=m.expando,j=m.cache,l=k.deleteExpando,n=m.event.special;null!=(d=a[h]);h++)if((b||m.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)n[e]?m.event.remove(d,e):m.removeEvent(d,e,g.handle);j[f]&&(delete j[f],l?delete d[i]:typeof d.removeAttribute!==K?d.removeAttribute(i):d[i]=null,c.push(f))}}}),m.fn.extend({text:function(a){return V(this,function(a){return void 0===a?m.text(this):this.empty().append((this[0]&&this[0].ownerDocument||y).createTextNode(a))},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if
vendor: 14,035 bytes, lines 3-4
3(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(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=wb(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?m.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||m.cleanData(ub(c)),c.parentNode&&(b&&m.contains(c.ownerDocument,c)&&zb(ub(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&m.cleanData(ub(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&m.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return m.clone(this,a,b)})},html:function(a){return V(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(fb,""):void 0;if(!("string"!=typeof a||mb.test(a)||!k.htmlSerialize&&gb.test(a)||!k.leadingWhitespace&&hb.test(a)||rb[(jb.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(ib,"<$1></$2>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(m.cleanData(ub(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,m.cleanData(ub(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,n=this,o=l-1,p=a[0],q=m.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&nb.test(p))return this.each(function(c){var d=n.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(i=m.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=m.map(ub(i,"script"),xb),f=g.length;l>j;j++)d=i,j!==o&&(d=m.clone(d,!0,!0),f&&m.merge(g,ub(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,m.map(g,yb),j=0;f>j;j++)d=g[j],ob.test(d.type||"")&&!m._data(d,"globalEval")&&m.contains(h,d)&&(d.src?m._evalUrl&&m._evalUrl(d.src):m.globalEval((d.text||d.textContent||d.innerHTML||"").replace(qb,"")));i=c=null}return this}}),m.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){m.fn[a]=function(a){for(var c,d=0,e=[],g=m(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),m(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Cb,Db={};function Eb(b,c){var d,e=m(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:m.css(e[0],"display");return e.detach(),f}function Fb(a){var b=y,c=Db[a];return c||(c=Eb(a,b),"none"!==c&&c||(Cb=(Cb||m("<iframe frameborder='0' width='0' height='0'/>")).appendTo(b.documentElement),b=(Cb[0].contentWindow||Cb[0].contentDocument).document,b.write(),b.close(),c=Eb(a,b),Cb.detach()),Db[a]=c),c}!function(){var a;k.shrinkWrapBlocks=function(){if(null!=a)return a;a=!1;var b,c,d;return c=y.getElementsByTagName("body")[0],c&&c.style?(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:1px;width:1px;zoom:1",b.appendChild(y.createElement("div")).style.width="5px",a=3!==b.offsetWidth),c.removeChild(d),a):void 0}}();var Gb=/^margin/,Hb=new RegExp("^("+S+")(?!px)[a-z%]+$","i"),Ib,Jb,Kb=/^(top|right|bottom|left)$/;a.getComputedStyle?(Ib=function(b){return b.ownerDocument.defaultView.opener?b.ownerDocument.defaultView.getComputedStyle(b,null):a.getComputedStyle(b,null)},Jb=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Ib(a),g=c?c.getPropertyValue(b)||c[b]:void 0,c&&(""!==g||m.contains(a.ownerDocument,a)||(g=m.style(a,b)),Hb.test(g)&&Gb.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+""}):y.documentElement.currentStyle&&(Ib=function(a){return a.currentStyle},Jb=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Ib(a),g=c?c[b]:void 0,null==g&&h&&h[b]&&(g=h[b]),Hb.test(g)&&!Kb.test(b)&&(d=h.left,e=a.runtimeStyle,f=e&&e.left,f&&(e.left=a.currentStyle.left),h.left="fontSize"===b?"1em":g,g=h.pixelLeft+"px",h.left=d,f&&(e.left=f)),void 0===g?g:g+""||"auto"});function Lb(a,b){return{get:function(){var c=a();if(null!=c)return c?void delete this.get:(this.get=b).apply(this,arguments)}}}!function(){var b,c,d,e,f,g,h;if(b=y.createElement("div"),b.innerHTML="  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",d=b.getElementsByTagName("a")[0],c=d&&d.style){c.cssText="float:left;opacity:.5",k.opacity="0.5"===c.opacity,k.cssFloat=!!c.cssFloat,b.style.backgroundClip="content-box",b.cloneNode(!0).style.backgroundClip="",k.clearCloneStyle="content-box"===b.style.backgroundClip,k.boxSizing=""===c.boxSizing||""===c.MozBoxSizing||""===c.WebkitBoxSizing,m.extend(k,{reliableHiddenOffsets:function(){return null==g&&i(),g},boxSizingReliable:function(){return null==f&&i(),f},pixelPosition:function(){return null==e&&i(),e},reliableMarginRight:function(){return null==h&&i(),h}});function i(){var b,c,d,i;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),b.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",e=f=!1,h=!0,a.getComputedStyle&&(e="1%"!==(a.getComputedStyle(b,null)||{}).top,f="4px"===(a.getComputedStyle(b,null)||{width:"4px"}).width,i=b.appendChild(y.createElement("div")),i.style.cssText=b.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0",i.style.marginRight=i.style.width="0",b.style.width="1px",h=!parseFloat((a.getComputedStyle(i,null)||{}).marginRight),b.removeChild(i)),b.innerHTML="<table><tr><td></td><td>t</td></tr></table>",i=b.getElementsByTagName("td"),i[0].style.cssText="margin:0;border:0;padding:0;display:none",g=0===i[0].offsetHeight,g&&(i[0].style.display="",i[1].style.display="none",g=0===i[0].offsetHeight),c.removeChild(d))}}}(),m.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 Mb=/alpha\([^)]*\)/i,Nb=/opacity\s*=\s*([^)]*)/,Ob=/^(none|table(?!-c[ea]).+)/,Pb=new RegExp("^("+S+")(.*)$","i"),Qb=new RegExp("^([+-])=("+S+")","i"),Rb={position:"absolute",visibility:"hidden",display:"block"},Sb={letterSpacing:"0",fontWeight:"400"},Tb=["Webkit","O","Moz","ms"];function Ub(a,b){if(b in a)return b;var c=b.charAt(0).toUpperCase()+b.slice(1),d=b,e=Tb.length;while(e--)if(b=Tb[e]+c,b in a)return b;return d}function Vb(a,b){for(var c,d,e,f=[],g=0,h=a.length;h>g;g++)d=a[g],d.style&&(f[g]=m._data(d,"olddisplay"),c=d.style.display,b?(f[g]||"none"!==c||(d.style.display=""),""===d.style.display&&U(d)&&(f[g]=m._data(d,"olddisplay",Fb(d.nodeName)))):(e=U(d),(c&&"none"!==c||!e)&&m._data(d,"olddisplay",e?c:m.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}function Wb(a,b,c){var d=Pb.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function Xb(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+=m.css(a,c+T[f],!0,e)),d?("content"===c&&(g-=m.css(a,"padding"+T[f],!0,e)),"margin"!==c&&(g-=m.css(a,"border"+T[f]+"Width",!0,e))):(g+=m.css(a,"padding"+T[f],!0,e),"padding"!==c&&(g+=m.css(a,"border"+T[f]+"Width",!0,e)));return g}function Yb(a,b,c){var d=!0,e="width"===b?a.offsetWidth:a.offsetHeight,f=Ib(a),g=k.boxSizing&&"border-box"===m.css(a,"boxSizing",!1,f);if(0>=e||null==e){if(e=Jb(a,b,f),(0>e||null==e)&&(e=a.style[b]),Hb.test(e))return e;d=g&&(k.boxSizingReliable()||e===a.style[b]),e=parseFloat(e)||0}return e+Xb(a,b,c||(g?"border":"content"),d,f)+"px"}m.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=Jb(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":k.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=m.camelCase(b),i=a.style;if(b=m.cssProps[h]||(m.cssProps[h]=Ub(i,h)),g=m.cssHooks[b]||m.cssHooks[h],void 0===c)return g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b];if(f=typeof c,"string"===f&&(e=Qb.exec(c))&&(c=(e[1]+1)*e[2]+parseFloat(m.css(a,b)),f="number"),null!=c&&c===c&&("number"!==f||m.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)))))try{i[b]=c}catch(j){}}},css:function(a,b,c,d){var e,f,g,h=m.camelCase(b);return b=m.cssProps[h]||(m.cssProps[h]=Ub(a.style,h)),g=m.cssHooks[b]||m.cssHooks[h],g&&"get"in g&&(f=g.get(a,!0,c)),void 0===f&&(f=Jb(a,b,d)),"normal"===f&&b in Sb&&(f=Sb[b]),""===c||c?(e=parseFloat(f),c===!0||m.isNumeric(e)?e||0:f):f}}),m.each(["height","width"],function(a,b){m.cssHooks[b]={get:function(a,c,d){return c?Ob.test(m.css(a,"display"))&&0===a.offsetWidth?m.swap(a,Rb,function(){return Yb(a,b,d)}):Yb(a,b,d):void 0},set:function(a,c,d){var e=d&&Ib(a);return Wb(a,c,d?Xb(a,b,d,k.boxSizing&&"border-box"===m.css(a,"boxSizing",!1,e),e):0)}}}),k.opacity||(m.cssHooks.opacity={get:function(a,b){return Nb.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=m.isNumeric(b)?"alpha(opacity="+100*b+")":"",f=d&&d.filter||c.filter||"";c.zoom=1,(b>=1||""===b)&&""===m.trim(f.replace(Mb,""))&&c.removeAttribute&&(c.removeAttribute("filter"),""===b||d&&!d.filter)||(c.filter=Mb.test(f)?f.replace(Mb,e):f+" "+e)}}),m.cssHooks.marginRight=Lb(k.reliableMarginRight,function(a,b){return b?m.swap(a,{display:"inline-block"},Jb,[a,"marginRight"]):void 0}),m.each({margin:"",padding:"",border:"Width"},function(a,b){m.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];4>d;d++)e[a+T[d]+b]=f[d]||f[d-2]||f[0];return e}},Gb.test(a)||(m.cssHooks[a+b].set=Wb)}),m.fn.extend({css:function(a,b){return V(this,function(a,b,c){var d,e,f={},g=0;if(m.isArray(b)){for(d=Ib(a),e=b.length;e>g;g++)f[b[g]]=m.css(a,b[g],!1,d);return f}return void 0!==c?m.style(a,b,c):m.css(a,b)},a,b,arguments.length>1)},show:function(){return Vb(this,!0)},hide:function(){return Vb(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){U(this)?m(this).show():m(this).hide()})}});function Zb(a,b,c,d,e){return new Zb.prototype.init(a,b,c,d,e)
4}m.Tween=Zb,Zb.prototype={constructor:Zb,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||(m.cssNumber[c]?"":"px")},cur:function(){var a=Zb.propHooks[this.prop];return a&&a.get?a.get(this):Zb.propHooks._default.get(this)},run:function(a){var b,c=Zb.propHooks[this.prop];return this.pos=b=this.options.duration?m.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):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):Zb.propHooks._default.set(this),this}},Zb.prototype.init.prototype=Zb.prototype,Zb.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=m.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){m.fx.step[a.prop]?m.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[m.cssProps[a.prop]]||m.cssHooks[a.prop])?m.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},Zb.propHooks.scrollTop=Zb.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},m.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},m.fx=Zb.prototype.init,m.fx.step={};var $b,_b,ac=/^(?:toggle|show|hide)$/,bc=new RegExp("^(?:([+-])=|)("+S+")([a-z%]*)$","i"),cc=/queueHooks$/,dc=[ic],ec={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=bc.exec(b),f=e&&e[3]||(m.cssNumber[a]?"":"px"),g=(m.cssNumber[a]||"px"!==f&&+d)&&bc.exec(m.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,m.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 fc(){return setTimeout(function(){$b=void 0}),$b=m.now()}function gc(a,b){var c,d={height:a},e=0;for(b=b?1:0;4>e;e+=2-b)c=T[e],d["margin"+c]=d["padding"+c]=a;return b&&(d.opacity=d.width=a),d}function hc(a,b,c){for(var d,e=(ec[b]||[]).concat(ec["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function ic(a,b,c){var d,e,f,g,h,i,j,l,n=this,o={},p=a.style,q=a.nodeType&&U(a),r=m._data(a,"fxshow");c.queue||(h=m._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,n.always(function(){n.always(function(){h.unqueued--,m.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[p.overflow,p.overflowX,p.overflowY],j=m.css(a,"display"),l="none"===j?m._data(a,"olddisplay")||Fb(a.nodeName):j,"inline"===l&&"n
vendor: 16,335 bytes, line 4
4one"===m.css(a,"float")&&(k.inlineBlockNeedsLayout&&"inline"!==Fb(a.nodeName)?p.zoom=1:p.display="inline-block")),c.overflow&&(p.overflow="hidden",k.shrinkWrapBlocks()||n.always(function(){p.overflow=c.overflow[0],p.overflowX=c.overflow[1],p.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],ac.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(q?"hide":"show")){if("show"!==e||!r||void 0===r[d])continue;q=!0}o[d]=r&&r[d]||m.style(a,d)}else j=void 0;if(m.isEmptyObject(o))"inline"===("none"===j?Fb(a.nodeName):j)&&(p.display=j);else{r?"hidden"in r&&(q=r.hidden):r=m._data(a,"fxshow",{}),f&&(r.hidden=!q),q?m(a).show():n.done(function(){m(a).hide()}),n.done(function(){var b;m._removeData(a,"fxshow");for(b in o)m.style(a,b,o[b])});for(d in o)g=hc(q?r[d]:0,d,n),d in r||(r[d]=g.start,q&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function jc(a,b){var c,d,e,f,g;for(c in a)if(d=m.camelCase(c),e=b[d],f=a[c],m.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=m.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 kc(a,b,c){var d,e,f=0,g=dc.length,h=m.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=$b||fc(),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:m.extend({},b),opts:m.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:$b||fc(),duration:c.duration,tweens:[],createTween:function(b,c){var d=m.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(jc(k,j.opts.specialEasing);g>f;f++)if(d=dc[f].call(j,a,k,j.opts))return d;return m.map(k,hc,j),m.isFunction(j.opts.start)&&j.opts.start.call(a,j),m.fx.timer(m.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)}m.Animation=m.extend(kc,{tweener:function(a,b){m.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],ec[c]=ec[c]||[],ec[c].unshift(b)},prefilter:function(a,b){b?dc.unshift(a):dc.push(a)}}),m.speed=function(a,b,c){var d=a&&"object"==typeof a?m.extend({},a):{complete:c||!c&&b||m.isFunction(a)&&a,duration:a,easing:c&&b||b&&!m.isFunction(b)&&b};return d.duration=m.fx.off?0:"number"==typeof d.duration?d.duration:d.duration in m.fx.speeds?m.fx.speeds[d.duration]:m.fx.speeds._default,(null==d.queue||d.queue===!0)&&(d.queue="fx"),d.old=d.complete,d.complete=function(){m.isFunction(d.old)&&d.old.call(this),d.queue&&m.dequeue(this,d.queue)},d},m.fn.extend({fadeTo:function(a,b,c,d){return this.filter(U).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=m.isEmptyObject(a),f=m.speed(b,c,d),g=function(){var b=kc(this,m.extend({},a),f);(e||m._data(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=m.timers,g=m._data(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&cc.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)&&m.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=m._data(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=m.timers,g=d?d.length:0;for(c.finish=!0,m.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})}}),m.each(["toggle","show","hide"],function(a,b){var c=m.fn[b];m.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(gc(b,!0),a,d,e)}}),m.each({slideDown:gc("show"),slideUp:gc("hide"),slideToggle:gc("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){m.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),m.timers=[],m.fx.tick=function(){var a,b=m.timers,c=0;for($b=m.now();c<b.length;c++)a=b[c],a()||b[c]!==a||b.splice(c--,1);b.length||m.fx.stop(),$b=void 0},m.fx.timer=function(a){m.timers.push(a),a()?m.fx.start():m.timers.pop()},m.fx.interval=13,m.fx.start=function(){_b||(_b=setInterval(m.fx.tick,m.fx.interval))},m.fx.stop=function(){clearInterval(_b),_b=null},m.fx.speeds={slow:600,fast:200,_default:400},m.fn.delay=function(a,b){return a=m.fx?m.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,b,c,d,e;b=y.createElement("div"),b.setAttribute("className","t"),b.innerHTML="  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",d=b.getElementsByTagName("a")[0],c=y.createElement("select"),e=c.appendChild(y.createElement("option")),a=b.getElementsByTagName("input")[0],d.style.cssText="top:1px",k.getSetAttribute="t"!==b.className,k.style=/top/.test(d.getAttribute("style")),k.hrefNormalized="/a"===d.getAttribute("href"),k.checkOn=!!a.value,k.optSelected=e.selected,k.enctype=!!y.createElement("form").enctype,c.disabled=!0,k.optDisabled=!e.disabled,a=y.createElement("input"),a.setAttribute("value",""),k.input=""===a.getAttribute("value"),a.value="t",a.setAttribute("type","radio"),k.radioValue="t"===a.value}();var lc=/\r/g;m.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=m.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,m(this).val()):a,null==e?e="":"number"==typeof e?e+="":m.isArray(e)&&(e=m.map(e,function(a){return null==a?"":a+""})),b=m.valHooks[this.type]||m.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=m.valHooks[e.type]||m.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(lc,""):null==c?"":c)}}}),m.extend({valHooks:{option:{get:function(a){var b=m.find.attr(a,"value");return null!=b?b:m.trim(m.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&&m.nodeName(c.parentNode,"optgroup"))){if(b=m(c).val(),f)return b;g.push(b)}return g},set:function(a,b){var c,d,e=a.options,f=m.makeArray(b),g=e.length;while(g--)if(d=e[g],m.inArray(m.valHooks.option.get(d),f)>=0)try{d.selected=c=!0}catch(h){d.scrollHeight}else d.selected=!1;return c||(a.selectedIndex=-1),e}}}}),m.each(["radio","checkbox"],function(){m.valHooks[this]={set:function(a,b){return m.isArray(b)?a.checked=m.inArray(m(a).val(),b)>=0:void 0}},k.checkOn||(m.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var mc,nc,oc=m.expr.attrHandle,pc=/^(?:checked|selected)$/i,qc=k.getSetAttribute,rc=k.input;m.fn.extend({attr:function(a,b){return V(this,m.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){m.removeAttr(this,a)})}}),m.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(a&&3!==f&&8!==f&&2!==f)return typeof a.getAttribute===K?m.prop(a,b,c):(1===f&&m.isXMLDoc(a)||(b=b.toLowerCase(),d=m.attrHooks[b]||(m.expr.match.bool.test(b)?nc:mc)),void 0===c?d&&"get"in d&&null!==(e=d.get(a,b))?e:(e=m.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 m.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=m.propFix[c]||c,m.expr.match.bool.test(c)?rc&&qc||!pc.test(c)?a[d]=!1:a[m.camelCase("default-"+c)]=a[d]=!1:m.attr(a,c,""),a.removeAttribute(qc?c:d)},attrHooks:{type:{set:function(a,b){if(!k.radioValue&&"radio"===b&&m.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}}}),nc={set:function(a,b,c){return b===!1?m.removeAttr(a,c):rc&&qc||!pc.test(c)?a.setAttribute(!qc&&m.propFix[c]||c,c):a[m.camelCase("default-"+c)]=a[c]=!0,c}},m.each(m.expr.match.bool.source.match(/\w+/g),function(a,b){var c=oc[b]||m.find.attr;oc[b]=rc&&qc||!pc.test(b)?function(a,b,d){var e,f;return d||(f=oc[b],oc[b]=e,e=null!=c(a,b,d)?b.toLowerCase():null,oc[b]=f),e}:function(a,b,c){return c?void 0:a[m.camelCase("default-"+b)]?b.toLowerCase():null}}),rc&&qc||(m.attrHooks.value={set:function(a,b,c){return m.nodeName(a,"input")?void(a.defaultValue=b):mc&&mc.set(a,b,c)}}),qc||(mc={set:function(a,b,c){var d=a.getAttributeNode(c);return d||a.setAttributeNode(d=a.ownerDocument.createAttribute(c)),d.value=b+="","value"===c||b===a.getAttribute(c)?b:void 0}},oc.id=oc.name=oc.coords=function(a,b,c){var d;return c?void 0:(d=a.getAttributeNode(b))&&""!==d.value?d.value:null},m.valHooks.button={get:function(a,b){var c=a.getAttributeNode(b);return c&&c.specified?c.value:void 0},set:mc.set},m.attrHooks.contenteditable={set:function(a,b,c){mc.set(a,""===b?!1:b,c)}},m.each(["width","height"],function(a,b){m.attrHooks[b]={set:function(a,c){return""===c?(a.setAttribute(b,"auto"),c):void 0}}})),k.style||(m.attrHooks.style={get:function(a){return a.style.cssText||void 0},set:function(a,b){return a.style.cssText=b+""}});var sc=/^(?:input|select|textarea|button|object)$/i,tc=/^(?:a|area)$/i;m.fn.extend({prop:function(a,b){return V(this,m.prop,a,b,arguments.length>1)},removeProp:function(a){return a=m.propFix[a]||a,this.each(function(){try{this[a]=void 0,delete this[a]}catch(b){}})}}),m.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||!m.isXMLDoc(a),f&&(b=m.propFix[b]||b,e=m.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){var b=m.find.attr(a,"tabindex");return b?parseInt(b,10):sc.test(a.nodeName)||tc.test(a.nodeName)&&a.href?0:-1}}}}),k.hrefNormalized||m.each(["href","src"],function(a,b){m.propHooks[b]={get:function(a){return a.getAttribute(b,4)}}}),k.optSelected||(m.propHooks.selected={get:function(a){var b=a.parentNode;return b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex),null}}),m.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){m.propFix[this.toLowerCase()]=this}),k.enctype||(m.propFix.enctype="encoding");var uc=/[\t\r\n\f]/g;m.fn.extend({addClass:function(a){var b,c,d,e,f,g,h=0,i=this.length,j="string"==typeof a&&a;if(m.isFunction(a))return this.each(function(b){m(this).addClass(a.call(this,b,this.className))});if(j)for(b=(a||"").match(E)||[];i>h;h++)if(c=this[h],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(uc," "):" ")){f=0;while(e=b[f++])d.indexOf(" "+e+" ")<0&&(d+=e+" ");g=m.trim(d),c.className!==g&&(c.className=g)}return this},removeClass:function(a){var b,c,d,e,f,g,h=0,i=this.length,j=0===arguments.length||"string"==typeof a&&a;if(m.isFunction(a))return this.each(function(b){m(this).removeClass(a.call(this,b,this.className))});if(j)for(b=(a||"").match(E)||[];i>h;h++)if(c=this[h],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(uc," "):"")){f=0;while(e=b[f++])while(d.indexOf(" "+e+" ")>=0)d=d.replace(" "+e+" "," ");g=a?m.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(m.isFunction(a)?function(c){m(this).toggleClass(a.call(this,c,this.className,b),b)}:function(){if("string"===c){var b,d=0,e=m(this),f=a.match(E)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else(c===K||"boolean"===c)&&(this.className&&m._data(this,"__className__",this.className),this.className=this.className||a===!1?"":m._data(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(uc," ").indexOf(b)>=0)return!0;return!1}}),m.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){m.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),m.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 vc=m.now(),wc=/\?/,xc=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;m.parseJSON=function(b){if(a.JSON&&a.JSON.parse)return a.JSON.parse(b+"");var c,d=null,e=m.trim(b+"");return e&&!m.trim(e.replace(xc,function(a,b,e,f){return c&&b&&(d=0),0===d?a:(c=e||b,d+=!f-!e,"")}))?Function("return "+e)():m.error("Invalid JSON: "+b)},m.parseXML=function(b){var c,d;if(!b||"string"!=typeof b)return null;try{a.DOMParser?(d=new DOMParser,c=d.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b))}catch(e){c=void 0}return c&&c.documentElement&&!c.getElementsByTagName("parsererror").length||m.error("Invalid XML: "+b),c};var yc,zc,Ac=/#.*$/,Bc=/([?&])_=[^&]*/,Cc=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Dc=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Ec=/^(?:GET|HEAD)$/,Fc=/^\/\//,Gc=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,Hc={},Ic={},Jc="*/".concat("*");try{zc=location.href}catch(Kc){zc=y.createElement("a"),zc.href="",zc=zc.href}yc=Gc.exec(zc.toLowerCase())||[];function Lc(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(E)||[];if(m.isFunction(c))while(d=f[e++])"+"===d.charAt(0)?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Mc(a,b,c,d){var e={},f=a===Ic;function g(h){var i;return e[h]=!0,m.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 Nc(a,b){var c,d,e=m.ajaxSettings.flatOptions||{};for(d in b)void 0!==b[d]&&((e[d]?a:c||(c={}))[d]=b[d]);return c&&m.extend(!0,a,c),a}function Oc(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===e&&(e=a.mimeType||b.getResponseHeader("Content-Type"));if(e)for(g in h)if(h[g]&&h[g].test(e)){i.unshift(g);break}if(i[0]in c)f=i[0];else{for(g in c){if(!i[0]||a.converters[g+" "+i[0]]){f=g;break}d||(d=g)}f=f||d}return f?(f!==i[0]&&i.unshift(f),c[f]):void 0}function Pc(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}}m.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:zc,type:"GET",isLocal:Dc.test(yc[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Jc,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":m.parseJSON,"text xml":m.parseXML}
vendor: 12,800 bytes, line 4
4,flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Nc(Nc(a,m.ajaxSettings),b):Nc(m.ajaxSettings,a)},ajaxPrefilter:Lc(Hc),ajaxTransport:Lc(Ic),ajax:function(a,b){"object"==typeof a&&(b=a,a=void 0),b=b||{};var c,d,e,f,g,h,i,j,k=m.ajaxSetup({},b),l=k.context||k,n=k.context&&(l.nodeType||l.jquery)?m(l):m.event,o=m.Deferred(),p=m.Callbacks("once memory"),q=k.statusCode||{},r={},s={},t=0,u="canceled",v={readyState:0,getResponseHeader:function(a){var b;if(2===t){if(!j){j={};while(b=Cc.exec(f))j[b[1].toLowerCase()]=b[2]}b=j[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return 2===t?f: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 i&&i.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||zc)+"").replace(Ac,"").replace(Fc,yc[1]+"//"),k.type=b.method||b.type||k.method||k.type,k.dataTypes=m.trim(k.dataType||"*").toLowerCase().match(E)||[""],null==k.crossDomain&&(c=Gc.exec(k.url.toLowerCase()),k.crossDomain=!(!c||c[1]===yc[1]&&c[2]===yc[2]&&(c[3]||("http:"===c[1]?"80":"443"))===(yc[3]||("http:"===yc[1]?"80":"443")))),k.data&&k.processData&&"string"!=typeof k.data&&(k.data=m.param(k.data,k.traditional)),Mc(Hc,k,b,v),2===t)return v;h=m.event&&k.global,h&&0===m.active++&&m.event.trigger("ajaxStart"),k.type=k.type.toUpperCase(),k.hasContent=!Ec.test(k.type),e=k.url,k.hasContent||(k.data&&(e=k.url+=(wc.test(e)?"&":"?")+k.data,delete k.data),k.cache===!1&&(k.url=Bc.test(e)?e.replace(Bc,"$1_="+vc++):e+(wc.test(e)?"&":"?")+"_="+vc++)),k.ifModified&&(m.lastModified[e]&&v.setRequestHeader("If-Modified-Since",m.lastModified[e]),m.etag[e]&&v.setRequestHeader("If-None-Match",m.etag[e])),(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]?", "+Jc+"; q=0.01":""):k.accepts["*"]);for(d in k.headers)v.setRequestHeader(d,k.headers[d]);if(k.beforeSend&&(k.beforeSend.call(l,v,k)===!1||2===t))return v.abort();u="abort";for(d in{success:1,error:1,complete:1})v[d](k[d]);if(i=Mc(Ic,k,b,v)){v.readyState=1,h&&n.trigger("ajaxSend",[v,k]),k.async&&k.timeout>0&&(g=setTimeout(function(){v.abort("timeout")},k.timeout));try{t=1,i.send(r,x)}catch(w){if(!(2>t))throw w;x(-1,w)}}else x(-1,"No Transport");function x(a,b,c,d){var j,r,s,u,w,x=b;2!==t&&(t=2,g&&clearTimeout(g),i=void 0,f=d||"",v.readyState=a>0?4:0,j=a>=200&&300>a||304===a,c&&(u=Oc(k,v,c)),u=Pc(k,u,v,j),j?(k.ifModified&&(w=v.getResponseHeader("Last-Modified"),w&&(m.lastModified[e]=w),w=v.getResponseHeader("etag"),w&&(m.etag[e]=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,h&&n.trigger(j?"ajaxSuccess":"ajaxError",[v,k,j?r:s]),p.fireWith(l,[v,x]),h&&(n.trigger("ajaxComplete",[v,k]),--m.active||m.event.trigger("ajaxStop")))}return v},getJSON:function(a,b,c){return m.get(a,b,c,"json")},getScript:function(a,b){return m.get(a,void 0,b,"script")}}),m.each(["get","post"],function(a,b){m[b]=function(a,c,d,e){return m.isFunction(c)&&(e=e||d,d=c,c=void 0),m.ajax({url:a,type:b,dataType:e,data:c,success:d})}}),m._evalUrl=function(a){return m.ajax({url:a,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})},m.fn.extend({wrapAll:function(a){if(m.isFunction(a))return this.each(function(b){m(this).wrapAll(a.call(this,b))});if(this[0]){var b=m(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&1===a.firstChild.nodeType)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return this.each(m.isFunction(a)?function(b){m(this).wrapInner(a.call(this,b))}:function(){var b=m(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=m.isFunction(a);return this.each(function(c){m(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){m.nodeName(this,"body")||m(this).replaceWith(this.childNodes)}).end()}}),m.expr.filters.hidden=function(a){return a.offsetWidth<=0&&a.offsetHeight<=0||!k.reliableHiddenOffsets()&&"none"===(a.style&&a.style.display||m.css(a,"display"))},m.expr.filters.visible=function(a){return!m.expr.filters.hidden(a)};var Qc=/%20/g,Rc=/\[\]$/,Sc=/\r?\n/g,Tc=/^(?:submit|button|image|reset|file)$/i,Uc=/^(?:input|select|textarea|keygen)/i;function Vc(a,b,c,d){var e;if(m.isArray(b))m.each(b,function(b,e){c||Rc.test(a)?d(a,e):Vc(a+"["+("object"==typeof e?b:"")+"]",e,c,d)});else if(c||"object"!==m.type(b))d(a,b);else for(e in b)Vc(a+"["+e+"]",b[e],c,d)}m.param=function(a,b){var c,d=[],e=function(a,b){b=m.isFunction(b)?b():null==b?"":b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};if(void 0===b&&(b=m.ajaxSettings&&m.ajaxSettings.traditional),m.isArray(a)||a.jquery&&!m.isPlainObject(a))m.each(a,function(){e(this.name,this.value)});else for(c in a)Vc(c,a[c],b,e);return d.join("&").replace(Qc,"+")},m.fn.extend({serialize:function(){return m.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=m.prop(this,"elements");return a?m.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!m(this).is(":disabled")&&Uc.test(this.nodeName)&&!Tc.test(a)&&(this.checked||!W.test(a))}).map(function(a,b){var c=m(this).val();return null==c?null:m.isArray(c)?m.map(c,function(a){return{name:b.name,value:a.replace(Sc,"\r\n")}}):{name:b.name,value:c.replace(Sc,"\r\n")}}).get()}}),m.ajaxSettings.xhr=void 0!==a.ActiveXObject?function(){return!this.isLocal&&/^(get|post|head|put|delete|options)$/i.test(this.type)&&Zc()||$c()}:Zc;var Wc=0,Xc={},Yc=m.ajaxSettings.xhr();a.attachEvent&&a.attachEvent("onunload",function(){for(var a in Xc)Xc[a](void 0,!0)}),k.cors=!!Yc&&"withCredentials"in Yc,Yc=k.ajax=!!Yc,Yc&&m.ajaxTransport(function(a){if(!a.crossDomain||k.cors){var b;return{send:function(c,d){var e,f=a.xhr(),g=++Wc;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)void 0!==c[e]&&f.setRequestHeader(e,c[e]+"");f.send(a.hasContent&&a.data||null),b=function(c,e){var h,i,j;if(b&&(e||4===f.readyState))if(delete Xc[g],b=void 0,f.onreadystatechange=m.noop,e)4!==f.readyState&&f.abort();else{j={},h=f.status,"string"==typeof f.responseText&&(j.text=f.responseText);try{i=f.statusText}catch(k){i=""}h||!a.isLocal||a.crossDomain?1223===h&&(h=204):h=j.text?200:404}j&&d(h,i,j,f.getAllResponseHeaders())},a.async?4===f.readyState?setTimeout(b):f.onreadystatechange=Xc[g]=b:b()},abort:function(){b&&b(void 0,!0)}}}});function Zc(){try{return new a.XMLHttpRequest}catch(b){}}function $c(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}m.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(a){return m.globalEval(a),a}}}),m.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),m.ajaxTransport("script",function(a){if(a.crossDomain){var b,c=y.head||m("head")[0]||y.documentElement;return{send:function(d,e){b=y.createElement("script"),b.async=!0,a.scriptCharset&&(b.charset=a.scriptCharset),b.src=a.url,b.onload=b.onreadystatechange=function(a,c){(c||!b.readyState||/loaded|complete/.test(b.readyState))&&(b.onload=b.onreadystatechange=null,b.parentNode&&b.parentNode.removeChild(b),b=null,c||e(200,"success"))},c.insertBefore(b,c.firstChild)},abort:function(){b&&b.onload(void 0,!0)}}}});var _c=[],ad=/(=)\?(?=&|$)|\?\?/;m.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=_c.pop()||m.expando+"_"+vc++;return this[a]=!0,a}}),m.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(ad.test(b.url)?"url":"string"==typeof b.data&&!(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&ad.test(b.data)&&"data");return h||"jsonp"===b.dataTypes[0]?(e=b.jsonpCallback=m.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(ad,"$1"+e):b.jsonp!==!1&&(b.url+=(wc.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||m.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,_c.push(e)),g&&m.isFunction(f)&&f(g[0]),g=f=void 0}),"script"):void 0}),m.parseHTML=function(a,b,c){if(!a||"string"!=typeof a)return null;"boolean"==typeof b&&(c=b,b=!1),b=b||y;var d=u.exec(a),e=!c&&[];return d?[b.createElement(d[1])]:(d=m.buildFragment([a],b,e),e&&e.length&&m(e).remove(),m.merge([],d.childNodes))};var bd=m.fn.load;m.fn.load=function(a,b,c){if("string"!=typeof a&&bd)return bd.apply(this,arguments);var d,e,f,g=this,h=a.indexOf(" ");return h>=0&&(d=m.trim(a.slice(h,a.length)),a=a.slice(0,h)),m.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(f="POST"),g.length>0&&m.ajax({url:a,type:f,dataType:"html",data:b}).done(function(a){e=arguments,g.html(d?m("<div>").append(m.parseHTML(a)).find(d):a)}).complete(c&&function(a,b){g.each(c,e||[a.responseText,b,a])}),this},m.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){m.fn[b]=function(a){return this.on(b,a)}}),m.expr.filters.animated=function(a){return m.grep(m.timers,function(b){return a===b.elem}).length};var cd=a.document.documentElement;function dd(a){return m.isWindow(a)?a:9===a.nodeType?a.defaultView||a.parentWindow:!1}m.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=m.css(a,"position"),l=m(a),n={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=m.css(a,"top"),i=m.css(a,"left"),j=("absolute"===k||"fixed"===k)&&m.inArray("auto",[f,i])>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),m.isFunction(b)&&(b=b.call(a,c,h)),null!=b.top&&(n.top=b.top-h.top+g),null!=b.left&&(n.left=b.left-h.left+e),"using"in b?b.using.call(a,n):l.css(n)}},m.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){m.offset.setOffset(this,a,b)});var b,c,d={top:0,left:0},e=this[0],f=e&&e.ownerDocument;if(f)return b=f.documentElement,m.contains(b,e)?(typeof e.getBoundingClientRect!==K&&(d=e.getBoundingClientRect()),c=dd(f),{top:d.top+(c.pageYOffset||b.scrollTop)-(b.clientTop||0),left:d.left+(c.pageXOffset||b.scrollLeft)-(b.clientLeft||0)}):d},position:function(){if(this[0]){var a,b,c={top:0,left:0},d=this[0];return"fixed"===m.css(d,"position")?b=d.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),m.nodeName(a[0],"html")||(c=a.offset()),c.top+=m.css(a[0],"borderTopWidth",!0),c.left+=m.css(a[0],"borderLeftWidth",!0)),{top:b.top-c.top-m.css(d,"marginTop",!0),left:b.left-c.left-m.css(d,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||cd;while(a&&!m.nodeName(a,"html")&&"static"===m.css(a,"position"))a=a.offsetParent;return a||cd})}}),m.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,b){var c=/Y/.test(b);m.fn[a]=function(d){return V(this,function(a,d,e){var f=dd(a);return void 0===e?f?b in f?f[b]:f.document.documentElement[d]:a[d]:void(f?f.scrollTo(c?m(f).scrollLeft():e,c?e:m(f).scrollTop()):a[d]=e)},a,d,arguments.length,null)}}),m.each(["top","left"],function(a,b){m.cssHooks[b]=Lb(k.pixelPosition,function(a,c){return c?(c=Jb(a,b),Hb.test(c)?m(a).position()[b]+"px":c):void 0})}),m.each({Height:"height",Width:"width"},function(a,b){m.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){m.fn[d]=function(d,e){var f=arguments.length&&(c||"boolean"!=typeof d),g=c||(d===!0||e===!0?"margin":"border");return V(this,function(b,c,d){var e;return m.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?m.css(b,c,g):m.style(b,c,d,g)},b,f?d:void 0,f,null)}})}),m.fn.size=function(){return this.length},m.fn.andSelf=m.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return m});var ed=a.jQuery,fd=a.$;return m.noConflict=function(b){return a.$===m&&(a.$=fd),b&&a.jQuery===m&&(a.jQuery=ed),m},typeof b===K&&(a.jQuery=a.$=m),m});
5
vendor: 10,979 bytes, lines 6-393
6/*! jQuery UI - v1.12.0-pre - 2015-03-17
7* http://jqueryui.com
8* Includes: core.js, widget.js, mouse.js, draggable.js, droppable.js, resizable.js, selectable.js, sortable.js, effect.js, accordion.js, autocomplete.js, button.js, datepicker.js, dialog.js, effect-blind.js, effect-bounce.js, effect-clip.js, effect-drop.js, effect-explode.js, effect-fade.js, effect-fold.js, effect-highlight.js, effect-puff.js, effect-pulsate.js, effect-scale.js, effect-shake.js, effect-size.js, effect-slide.js, effect-transfer.js, menu.js, position.js, progressbar.js, selectmenu.js, slider.js, spinner.js, tabs.js, tooltip.js
9* Copyright jQuery Foundation and other contributors; Licensed MIT */
10//>>label: Core
11//>>group: UI Core
12//>>description: The core of jQuery UI, required for all interactions and widgets.
13//>>docs: http://api.jqueryui.com/category/ui-core/
14//>>demos: http://jqueryui.com/
15
16(function( factory ) {
17	if ( typeof define === "function" && define.amd ) {
18
19		// AMD. Register as an anonymous module.
20		define( [ "jquery" ], factory );
21	} else {
22
23		// Browser globals
24		factory( jQuery );
25	}
26}(function( $ ) {
27
28// $.ui might exist from components with no dependencies, e.g., $.ui.position
29$.ui = $.ui || {};
30
31$.extend( $.ui, {
32	version: "@VERSION",
33
34	keyCode: {
35		BACKSPACE: 8,
36		COMMA: 188,
37		DELETE: 46,
38		DOWN: 40,
39		END: 35,
40		ENTER: 13,
41		ESCAPE: 27,
42		HOME: 36,
43		LEFT: 37,
44		PAGE_DOWN: 34,
45		PAGE_UP: 33,
46		PERIOD: 190,
47		RIGHT: 39,
48		SPACE: 32,
49		TAB: 9,
50		UP: 38
51	},
52
53	// Internal use only
54	safeActiveElement: function( document ) {
55		var activeElement;
56
57		// Support: IE 9 only
58		// IE9 throws an "Unspecified error" accessing document.activeElement from an <iframe>
59		try {
60			activeElement = document.activeElement;
61		} catch ( error ) {
62			activeElement = document.body;
63		}
64
65		return activeElement;
66	},
67
68	// Internal use only
69	safeBlur: function( element ) {
70
71		// Support: IE9 - 10 only
72		// If the <body> is blurred, IE will switch windows, see #9420
73		if ( element && element.nodeName.toLowerCase() !== "body" ) {
74			$( element ).blur();
75		}
76	}
77});
78
79// plugins
80$.fn.extend({
81	scrollParent: function( includeHidden ) {
82		var position = this.css( "position" ),
83			excludeStaticParent = position === "absolute",
84			overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/,
85			scrollParent = this.parents().filter( function() {
86				var parent = $( this );
87				if ( excludeStaticParent && parent.css( "position" ) === "static" ) {
88					return false;
89				}
90				return overflowRegex.test( parent.css( "overflow" ) + parent.css( "overflow-y" ) + parent.css( "overflow-x" ) );
91			}).eq( 0 );
92
93		return position === "fixed" || !scrollParent.length ? $( this[ 0 ].ownerDocument || document ) : scrollParent;
94	},
95
96	uniqueId: (function() {
97		var uuid = 0;
98
99		return function() {
100			return this.each(function() {
101				if ( !this.id ) {
102					this.id = "ui-id-" + ( ++uuid );
103				}
104			});
105		};
106	})(),
107
108	removeUniqueId: function() {
109		return this.each(function() {
110			if ( /^ui-id-\d+$/.test( this.id ) ) {
111				$( this ).removeAttr( "id" );
112			}
113		});
114	}
115});
116
117// selectors
118function focusable( element, isTabIndexNotNaN ) {
119	var map, mapName, img,
120		nodeName = element.nodeName.toLowerCase();
121	if ( "area" === nodeName ) {
122		map = element.parentNode;
123		mapName = map.name;
124		if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
125			return false;
126		}
127		img = $( "img[usemap='#" + mapName + "']" )[ 0 ];
128		return !!img && visible( img );
129	}
130	return ( /^(input|select|textarea|button|object)$/.test( nodeName ) ?
131		!element.disabled :
132		"a" === nodeName ?
133			element.href || isTabIndexNotNaN :
134			isTabIndexNotNaN) &&
135		// the element and all of its ancestors must be visible
136		visible( element );
137}
138
139function visible( element ) {
140	return $.expr.filters.visible( element ) &&
141		!$( element ).parents().addBack().filter(function() {
142			return $.css( this, "visibility" ) === "hidden";
143		}).length;
144}
145
146$.extend( $.expr[ ":" ], {
147	data: $.expr.createPseudo ?
148		$.expr.createPseudo(function( dataName ) {
149			return function( elem ) {
150				return !!$.data( elem, dataName );
151			};
152		}) :
153		// support: jQuery <1.8
154		function( elem, i, match ) {
155			return !!$.data( elem, match[ 3 ] );
156		},
157
158	focusable: function( element ) {
159		return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) );
160	},
161
162	tabbable: function( element ) {
163		var tabIndex = $.attr( element, "tabindex" ),
164			isTabIndexNaN = isNaN( tabIndex );
165		return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN );
166	}
167});
168
169// support: jQuery 1.7 only
170// Not a great way to check versions, but since we only support 1.7+ and only
171// need to detect <1.8, this is a simple check that should suffice. Checking
172// for "1.7." would be a bit safer, but the version string is 1.7, not 1.7.0
173// and we'll never reach 1.70.0 (if we do, we certainly won't be supporting
174// 1.7 anymore). See #11197 for why we're not using feature detection.
175if ( $.fn.jquery.substring( 0, 3 ) === "1.7" ) {
176
177	// Setters for .innerWidth(), .innerHeight(), .outerWidth(), .outerHeight()
178	// Unlike jQuery Core 1.8+, these only support numeric values to set the
179	// dimensions in pixels
180	$.each( [ "Width", "Height" ], function( i, name ) {
181		var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
182			type = name.toLowerCase(),
183			orig = {
184				innerWidth: $.fn.innerWidth,
185				innerHeight: $.fn.innerHeight,
186				outerWidth: $.fn.outerWidth,
187				outerHeight: $.fn.outerHeight
188			};
189
190		function reduce( elem, size, border, margin ) {
191			$.each( side, function() {
192				size -= parseFloat( $.css( elem, "padding" + this ) ) || 0;
193				if ( border ) {
194					size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0;
195				}
196				if ( margin ) {
197					size -= parseFloat( $.css( elem, "margin" + this ) ) || 0;
198				}
199			});
200			return size;
201		}
202
203		$.fn[ "inner" + name ] = function( size ) {
204			if ( size === undefined ) {
205				return orig[ "inner" + name ].call( this );
206			}
207
208			return this.each(function() {
209				$( this ).css( type, reduce( this, size ) + "px" );
210			});
211		};
212
213		$.fn[ "outer" + name] = function( size, margin ) {
214			if ( typeof size !== "number" ) {
215				return orig[ "outer" + name ].call( this, size );
216			}
217
218			return this.each(function() {
219				$( this).css( type, reduce( this, size, true, margin ) + "px" );
220			});
221		};
222	});
223
224	$.fn.addBack = function( selector ) {
225		return this.add( selector == null ?
226			this.prevObject : this.prevObject.filter( selector )
227		);
228	};
229}
230
231// deprecated
232$.ui.ie = !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() );
233
234$.fn.extend({
235	disableSelection: (function() {
236		var eventType = "onselectstart" in document.createElement( "div" ) ?
237			"selectstart" :
238			"mousedown";
239
240		return function() {
241			return this.bind( eventType + ".ui-disableSelection", function( event ) {
242				event.preventDefault();
243			});
244		};
245	})(),
246
247	enableSelection: function() {
248		return this.unbind( ".ui-disableSelection" );
249	}
250});
251
252// $.ui.plugin is deprecated. Use $.widget() extensions instead.
253$.ui.plugin = {
254	add: function( module, option, set ) {
255		var i,
256			proto = $.ui[ module ].prototype;
257		for ( i in set ) {
258			proto.plugins[ i ] = proto.plugins[ i ] || [];
259			proto.plugins[ i ].push( [ option, set[ i ] ] );
260		}
261	},
262	call: function( instance, name, args, allowDisconnected ) {
263		var i,
264			set = instance.plugins[ name ];
265
266		if ( !set ) {
267			return;
268		}
269
270		if ( !allowDisconnected && ( !instance.element[ 0 ].parentNode || instance.element[ 0 ].parentNode.nodeType === 11 ) ) {
271			return;
272		}
273
274		for ( i = 0; i < set.length; i++ ) {
275			if ( instance.options[ set[ i ][ 0 ] ] ) {
276				set[ i ][ 1 ].apply( instance.element, args );
277			}
278		}
279	}
280};
281
282}));
283
284//>>label: Widget
285//>>group: UI Core
286//>>description: Provides a factory for creating stateful widgets with a common API.
287//>>docs: http://api.jqueryui.com/jQuery.widget/
288//>>demos: http://jqueryui.com/widget/
289
290(function( factory ) {
291	if ( typeof define === "function" && define.amd ) {
292
293		// AMD. Register as an anonymous module.
294		define( [ "jquery" ], factory );
295	} else {
296
297		// Browser globals
298		factory( jQuery );
299	}
300}(function( $ ) {
301
302var widget_uuid = 0,
303	widget_slice = Array.prototype.slice;
304
305$.cleanData = (function( orig ) {
306	return function( elems ) {
307		var events, elem, i;
308		for ( i = 0; (elem = elems[i]) != null; i++ ) {
309			try {
310
311				// Only trigger remove when necessary to save time
312				events = $._data( elem, "events" );
313				if ( events && events.remove ) {
314					$( elem ).triggerHandler( "remove" );
315				}
316
317			// http://bugs.jquery.com/ticket/8235
318			} catch ( e ) {}
319		}
320		orig( elems );
321	};
322})( $.cleanData );
323
324$.widget = function( name, base, prototype ) {
325	var fullName, existingConstructor, constructor, basePrototype,
326		// proxiedPrototype allows the provided prototype to remain unmodified
327		// so that it can be used as a mixin for multiple widgets (#8876)
328		proxiedPrototype = {},
329		namespace = name.split( "." )[ 0 ];
330
331	name = name.split( "." )[ 1 ];
332	fullName = namespace + "-" + name;
333
334	if ( !prototype ) {
335		prototype = base;
336		base = $.Widget;
337	}
338
339	// create selector for plugin
340	$.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) {
341		return !!$.data( elem, fullName );
342	};
343
344	$[ namespace ] = $[ namespace ] || {};
345	existingConstructor = $[ namespace ][ name ];
346	constructor = $[ namespace ][ name ] = function( options, element ) {
347		// allow instantiation without "new" keyword
348		if ( !this._createWidget ) {
349			return new constructor( options, element );
350		}
351
352		// allow instantiation without initializing for simple inheritance
353		// must use "new" keyword (the code above always passes args)
354		if ( arguments.length ) {
355			this._createWidget( options, element );
356		}
357	};
358	// extend with the existing constructor to carry over any static properties
359	$.extend( constructor, existingConstructor, {
360		version: prototype.version,
361		// copy the object used to create the prototype in case we need to
362		// redefine the widget later
363		_proto: $.extend( {}, prototype ),
364		// track widgets that inherit from this widget in case this widget is
365		// redefined after a widget inherits from it
366		_childConstructors: []
367	});
368
369	basePrototype = new base();
370	// we need to make the options hash a property directly on the new instance
371	// otherwise we'll modify the options hash on the prototype that we're
372	// inheriting from
373	basePrototype.options = $.widget.extend( {}, basePrototype.options );
374	$.each( prototype, function( prop, value ) {
375		if ( !$.isFunction( value ) ) {
376			proxiedPrototype[ prop ] = value;
377			return;
378		}
379		proxiedPrototype[ prop ] = (function() {
380			var _super = function() {
381					return base.prototype[ prop ].apply( this, arguments );
382				},
383				_superApply = function( args ) {
384					return base.prototype[ prop ].apply( this, args );
385				};
386			return function() {
387				var __super = this._super,
388					__superApply = this._superApply,
389					returnValue;
390
391				this._super = _super;
392				this._superApply = _superApply;
393
vendor: 4,806 bytes, lines 394-555
394				returnValue = value.apply( this, arguments );
395
396				this._super = __super;
397				this._superApply = __superApply;
398
399				return returnValue;
400			};
401		})();
402	});
403	constructor.prototype = $.widget.extend( basePrototype, {
404		// TODO: remove support for widgetEventPrefix
405		// always use the name + a colon as the prefix, e.g., draggable:start
406		// don't prefix for widgets that aren't DOM-based
407		widgetEventPrefix: existingConstructor ? (basePrototype.widgetEventPrefix || name) : name
408	}, proxiedPrototype, {
409		constructor: constructor,
410		namespace: namespace,
411		widgetName: name,
412		widgetFullName: fullName
413	});
414
415	// If this widget is being redefined then we need to find all widgets that
416	// are inheriting from it and redefine all of them so that they inherit from
417	// the new version of this widget. We're essentially trying to replace one
418	// level in the prototype chain.
419	if ( existingConstructor ) {
420		$.each( existingConstructor._childConstructors, function( i, child ) {
421			var childPrototype = child.prototype;
422
423			// redefine the child widget using the same prototype that was
424			// originally used, but inherit from the new version of the base
425			$.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto );
426		});
427		// remove the list of existing child constructors from the old constructor
428		// so the old child constructors can be garbage collected
429		delete existingConstructor._childConstructors;
430	} else {
431		base._childConstructors.push( constructor );
432	}
433
434	$.widget.bridge( name, constructor );
435
436	return constructor;
437};
438
439$.widget.extend = function( target ) {
440	var input = widget_slice.call( arguments, 1 ),
441		inputIndex = 0,
442		inputLength = input.length,
443		key,
444		value;
445	for ( ; inputIndex < inputLength; inputIndex++ ) {
446		for ( key in input[ inputIndex ] ) {
447			value = input[ inputIndex ][ key ];
448			if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
449				// Clone objects
450				if ( $.isPlainObject( value ) ) {
451					target[ key ] = $.isPlainObject( target[ key ] ) ?
452						$.widget.extend( {}, target[ key ], value ) :
453						// Don't extend strings, arrays, etc. with objects
454						$.widget.extend( {}, value );
455				// Copy everything else by reference
456				} else {
457					target[ key ] = value;
458				}
459			}
460		}
461	}
462	return target;
463};
464
465$.widget.bridge = function( name, object ) {
466	var fullName = object.prototype.widgetFullName || name;
467	$.fn[ name ] = function( options ) {
468		var isMethodCall = typeof options === "string",
469			args = widget_slice.call( arguments, 1 ),
470			returnValue = this;
471
472		if ( isMethodCall ) {
473			this.each(function() {
474				var methodValue,
475					instance = $.data( this, fullName );
476				if ( options === "instance" ) {
477					returnValue = instance;
478					return false;
479				}
480				if ( !instance ) {
481					return $.error( "cannot call methods on " + name + " prior to initialization; " +
482						"attempted to call method '" + options + "'" );
483				}
484				if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) {
485					return $.error( "no such method '" + options + "' for " + name + " widget instance" );
486				}
487				methodValue = instance[ options ].apply( instance, args );
488				if ( methodValue !== instance && methodValue !== undefined ) {
489					returnValue = methodValue && methodValue.jquery ?
490						returnValue.pushStack( methodValue.get() ) :
491						methodValue;
492					return false;
493				}
494			});
495		} else {
496
497			// Allow multiple hashes to be passed on init
498			if ( args.length ) {
499				options = $.widget.extend.apply( null, [ options ].concat(args) );
500			}
501
502			this.each(function() {
503				var instance = $.data( this, fullName );
504				if ( instance ) {
505					instance.option( options || {} );
506					if ( instance._init ) {
507						instance._init();
508					}
509				} else {
510					$.data( this, fullName, new object( options, this ) );
511				}
512			});
513		}
514
515		return returnValue;
516	};
517};
518
519$.Widget = function( /* options, element */ ) {};
520$.Widget._childConstructors = [];
521
522$.Widget.prototype = {
523	widgetName: "widget",
524	widgetEventPrefix: "",
525	defaultElement: "<div>",
526	options: {
527		classes: {},
528		disabled: false,
529
530		// callbacks
531		create: null
532	},
533	_createWidget: function( options, element ) {
534		element = $( element || this.defaultElement || this )[ 0 ];
535		this.element = $( element );
536		this.uuid = widget_uuid++;
537		this.eventNamespace = "." + this.widgetName + this.uuid;
538
539		this.bindings = $();
540		this.hoverable = $();
541		this.focusable = $();
542		this.classesElementLookup = {};
543
544		if ( element !== this ) {
545			$.data( element, this.widgetFullName, this );
546			this._on( true, this.element, {
547				remove: function( event ) {
548					if ( event.target === element ) {
549						this.destroy();
550					}
551				}
552			});
553			this.document = $( element.style ?
554				// element within the document
555				element.ownerDocument :
vendor: 5,924 bytes, lines 556-771
556				// element is window or document
557				element.document || element );
558			this.window = $( this.document[0].defaultView || this.document[0].parentWindow );
559		}
560
561		this.options = $.widget.extend( {},
562			this.options,
563			this._getCreateOptions(),
564			options );
565
566		this._create();
567		this._trigger( "create", null, this._getCreateEventData() );
568		this._init();
569	},
570	_getCreateOptions: $.noop,
571	_getCreateEventData: $.noop,
572	_create: $.noop,
573	_init: $.noop,
574
575	destroy: function() {
576		var that = this;
577
578		this._destroy();
579		$.each( this.classesElementLookup, function( key, value ) {
580			that._removeClass( value, key );
581		});
582
583		// we can probably remove the unbind calls in 2.0
584		// all event bindings should go through this._on()
585		this.element
586			.unbind( this.eventNamespace )
587			.removeData( this.widgetFullName );
588		this.widget()
589			.unbind( this.eventNamespace )
590			.removeAttr( "aria-disabled" );
591
592		// clean up events and states
593		this.bindings.unbind( this.eventNamespace );
594	},
595	_destroy: $.noop,
596
597	widget: function() {
598		return this.element;
599	},
600
601	option: function( key, value ) {
602		var options = key,
603			parts,
604			curOption,
605			i;
606
607		if ( arguments.length === 0 ) {
608			// don't return a reference to the internal hash
609			return $.widget.extend( {}, this.options );
610		}
611
612		if ( typeof key === "string" ) {
613			// handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
614			options = {};
615			parts = key.split( "." );
616			key = parts.shift();
617			if ( parts.length ) {
618				curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
619				for ( i = 0; i < parts.length - 1; i++ ) {
620					curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
621					curOption = curOption[ parts[ i ] ];
622				}
623				key = parts.pop();
624				if ( arguments.length === 1 ) {
625					return curOption[ key ] === undefined ? null : curOption[ key ];
626				}
627				curOption[ key ] = value;
628			} else {
629				if ( arguments.length === 1 ) {
630					return this.options[ key ] === undefined ? null : this.options[ key ];
631				}
632				options[ key ] = value;
633			}
634		}
635
636		this._setOptions( options );
637
638		return this;
639	},
640	_setOptions: function( options ) {
641		var key;
642
643		for ( key in options ) {
644			this._setOption( key, options[ key ] );
645		}
646
647		return this;
648	},
649	_setOption: function( key, value ) {
650		if ( key === "classes" ) {
651			this._setOptionClasses( value );
652		}
653
654		this.options[ key ] = value;
655
656		if ( key === "disabled" ) {
657			this._toggleClass( this.widget(), this.widgetFullName + "-disabled", null, !!value );
658
659			// If the widget is becoming disabled, then nothing is interactive
660			if ( value ) {
661				this._removeClass( this.hoverable, null, "ui-state-hover" );
662				this._removeClass( this.focusable, null, "ui-state-focus" );
663			}
664		}
665
666		return this;
667	},
668	_setOptionClasses: function( value ) {
669		var classKey, elements, currentElements;
670
671		for ( classKey in value ) {
672			currentElements = this.classesElementLookup[ classKey ];
673			if ( value[ classKey ] === this.options.classes[ classKey ] ||
674					!currentElements ||
675					!currentElements.length ) {
676				continue;
677			}
678
679			// We are doing this to create a new jQuery object because the _removeClass() call
680			// on the next line is going to destroy the reference to the current elements being
681			// tracked. We need to save a copy of this collection so that we can add the new classes
682			// below.
683			elements = $( currentElements.get() );
684			this._removeClass( currentElements, classKey );
685
686			// We don't use _addClass() here, because that uses this.options.classes
687			// for generating the string of classes. We want to use the value passed in from
688			// _setOption(), this is the new value of the classes option which was passed to
689			// _setOption(). We pass this value directly to _classes().
690			elements.addClass( this._classes({
691				element: elements,
692				keys: classKey,
693				classes: value,
694				add: true
695			}));
696		}
697	},
698
699	enable: function() {
700		return this._setOptions({ disabled: false });
701	},
702	disable: function() {
703		return this._setOptions({ disabled: true });
704	},
705
706	_classes: function( options ) {
707		var full = [],
708			that = this;
709
710		options = $.extend({
711			element: this.element,
712			classes: this.options.classes || {}
713		}, options );
714
715		function processClassString( classes, checkOption ) {
716			var current, i;
717			for ( i = 0; i < classes.length; i++ ) {
718				current = that.classesElementLookup[ classes[ i ] ] || $();
719				if ( options.add ) {
720					current = $( $.unique( current.get().concat( options.element.get() ) ) );
721				} else {
722					current = $( current.not( options.element ).get() );
723				}
724				that.classesElementLookup[ classes[ i ] ] = current;
725				full.push( classes[ i ] );
726				if ( checkOption && options.classes[ classes[ i ] ] ) {
727					full.push( options.classes[ classes[ i ] ] );
728				}
729			}
730		}
731
732		if ( options.keys ) {
733			processClassString( options.keys.match( /\S+/g ) || [], true );
734		}
735		if ( options.extra ) {
736			processClassString( options.extra.match( /\S+/g ) || [] );
737		}
738
739		return full.join( " " );
740	},
741
742	_removeClass: function( element, keys, extra ) {
743		return this._toggleClass( element, keys, extra, false );
744	},
745
746	_addClass: function( element, keys, extra ) {
747		return this._toggleClass( element, keys, extra, true );
748	},
749
750	_toggleClass: function( element, keys, extra, add ) {
751		add = ( typeof add === "boolean" ) ? add : extra;
752		var shift = ( typeof element === "string" || element === null ),
753			options = {
754				extra: shift ? keys : extra,
755				keys: shift ? element : keys,
756				element: shift ? this.element : element,
757				add: add
758			};
759		options.element.toggleClass( this._classes( options ), add );
760		return this;
761	},
762
763	_on: function( suppressDisabledCheck, element, handlers ) {
764		var delegateElement,
765			instance = this;
766
767		// no suppressDisabledCheck flag, shuffle arguments
768		if ( typeof suppressDisabledCheck !== "boolean" ) {
769			handlers = element;
770			element = suppressDisabledCheck;
771			suppressDisabledCheck = false;
vendor: 18,511 bytes, lines 772-1461
772		}
773
774		// no element argument, shuffle and use this.element
775		if ( !handlers ) {
776			handlers = element;
777			element = this.element;
778			delegateElement = this.widget();
779		} else {
780			element = delegateElement = $( element );
781			this.bindings = this.bindings.add( element );
782		}
783
784		$.each( handlers, function( event, handler ) {
785			function handlerProxy() {
786				// allow widgets to customize the disabled handling
787				// - disabled as an array instead of boolean
788				// - disabled class as method for disabling individual parts
789				if ( !suppressDisabledCheck &&
790						( instance.options.disabled === true ||
791							$( this ).hasClass( "ui-state-disabled" ) ) ) {
792					return;
793				}
794				return ( typeof handler === "string" ? instance[ handler ] : handler )
795					.apply( instance, arguments );
796			}
797
798			// copy the guid so direct unbinding works
799			if ( typeof handler !== "string" ) {
800				handlerProxy.guid = handler.guid =
801					handler.guid || handlerProxy.guid || $.guid++;
802			}
803
804			var match = event.match( /^([\w:-]*)\s*(.*)$/ ),
805				eventName = match[1] + instance.eventNamespace,
806				selector = match[2];
807			if ( selector ) {
808				delegateElement.delegate( selector, eventName, handlerProxy );
809			} else {
810				element.bind( eventName, handlerProxy );
811			}
812		});
813	},
814
815	_off: function( element, eventName ) {
816		eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) +
817			this.eventNamespace;
818		element.unbind( eventName ).undelegate( eventName );
819
820		// Clear the stack to avoid memory leaks (#10056)
821		this.bindings = $( this.bindings.not( element ).get() );
822		this.focusable = $( this.focusable.not( element ).get() );
823		this.hoverable = $( this.hoverable.not( element ).get() );
824	},
825
826	_delay: function( handler, delay ) {
827		function handlerProxy() {
828			return ( typeof handler === "string" ? instance[ handler ] : handler )
829				.apply( instance, arguments );
830		}
831		var instance = this;
832		return setTimeout( handlerProxy, delay || 0 );
833	},
834
835	_hoverable: function( element ) {
836		this.hoverable = this.hoverable.add( element );
837		this._on( element, {
838			mouseenter: function( event ) {
839				this._addClass( $( event.currentTarget ), null, "ui-state-hover" );
840			},
841			mouseleave: function( event ) {
842				this._removeClass( $( event.currentTarget ), null, "ui-state-hover" );
843			}
844		});
845	},
846
847	_focusable: function( element ) {
848		this.focusable = this.focusable.add( element );
849		this._on( element, {
850			focusin: function( event ) {
851				this._addClass( $( event.currentTarget ), null, "ui-state-focus" );
852			},
853			focusout: function( event ) {
854				this._removeClass( $( event.currentTarget ), null, "ui-state-focus" );
855			}
856		});
857	},
858
859	_trigger: function( type, event, data ) {
860		var prop, orig,
861			callback = this.options[ type ];
862
863		data = data || {};
864		event = $.Event( event );
865		event.type = ( type === this.widgetEventPrefix ?
866			type :
867			this.widgetEventPrefix + type ).toLowerCase();
868		// the original event may come from any element
869		// so we need to reset the target on the new event
870		event.target = this.element[ 0 ];
871
872		// copy original event properties over to the new event
873		orig = event.originalEvent;
874		if ( orig ) {
875			for ( prop in orig ) {
876				if ( !( prop in event ) ) {
877					event[ prop ] = orig[ prop ];
878				}
879			}
880		}
881
882		this.element.trigger( event, data );
883		return !( $.isFunction( callback ) &&
884			callback.apply( this.element[0], [ event ].concat( data ) ) === false ||
885			event.isDefaultPrevented() );
886	}
887};
888
889$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
890	$.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
891		if ( typeof options === "string" ) {
892			options = { effect: options };
893		}
894		var hasOptions,
895			effectName = !options ?
896				method :
897				options === true || typeof options === "number" ?
898					defaultEffect :
899					options.effect || defaultEffect;
900		options = options || {};
901		if ( typeof options === "number" ) {
902			options = { duration: options };
903		}
904		hasOptions = !$.isEmptyObject( options );
905		options.complete = callback;
906		if ( options.delay ) {
907			element.delay( options.delay );
908		}
909		if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
910			element[ method ]( options );
911		} else if ( effectName !== method && element[ effectName ] ) {
912			element[ effectName ]( options.duration, options.easing, callback );
913		} else {
914			element.queue(function( next ) {
915				$( this )[ method ]();
916				if ( callback ) {
917					callback.call( element[ 0 ] );
918				}
919				next();
920			});
921		}
922	};
923});
924
925return $.widget;
926
927}));
928
929//>>label: Mouse
930//>>group: UI Core
931//>>description: Abstracts mouse-based interactions to assist in creating certain widgets.
932//>>docs: http://api.jqueryui.com/mouse/
933
934(function( factory ) {
935	if ( typeof define === "function" && define.amd ) {
936
937		// AMD. Register as an anonymous module.
938		define([
939			"jquery",
940			"./widget"
941		], factory );
942	} else {
943
944		// Browser globals
945		factory( jQuery );
946	}
947}(function( $ ) {
948
949var mouseHandled = false;
950$( document ).mouseup( function() {
951	mouseHandled = false;
952});
953
954return $.widget("ui.mouse", {
955	version: "@VERSION",
956	options: {
957		cancel: "input,textarea,button,select,option",
958		distance: 1,
959		delay: 0
960	},
961	_mouseInit: function() {
962		var that = this;
963
964		this.element
965			.bind("mousedown." + this.widgetName, function(event) {
966				return that._mouseDown(event);
967			})
968			.bind("click." + this.widgetName, function(event) {
969				if (true === $.data(event.target, that.widgetName + ".preventClickEvent")) {
970					$.removeData(event.target, that.widgetName + ".preventClickEvent");
971					event.stopImmediatePropagation();
972					return false;
973				}
974			});
975
976		this.started = false;
977	},
978
979	// TODO: make sure destroying one instance of mouse doesn't mess with
980	// other instances of mouse
981	_mouseDestroy: function() {
982		this.element.unbind("." + this.widgetName);
983		if ( this._mouseMoveDelegate ) {
984			this.document
985				.unbind("mousemove." + this.widgetName, this._mouseMoveDelegate)
986				.unbind("mouseup." + this.widgetName, this._mouseUpDelegate);
987		}
988	},
989
990	_mouseDown: function(event) {
991		// don't let more than one widget handle mouseStart
992		if ( mouseHandled ) {
993			return;
994		}
995
996		this._mouseMoved = false;
997
998		// we may have missed mouseup (out of window)
999		(this._mouseStarted && this._mouseUp(event));
1000
1001		this._mouseDownEvent = event;
1002
1003		var that = this,
1004			btnIsLeft = (event.which === 1),
1005			// event.target.nodeName works around a bug in IE 8 with
1006			// disabled inputs (#7620)
1007			elIsCancel = (typeof this.options.cancel === "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false);
1008		if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
1009			return true;
1010		}
1011
1012		this.mouseDelayMet = !this.options.delay;
1013		if (!this.mouseDelayMet) {
1014			this._mouseDelayTimer = setTimeout(function() {
1015				that.mouseDelayMet = true;
1016			}, this.options.delay);
1017		}
1018
1019		if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
1020			this._mouseStarted = (this._mouseStart(event) !== false);
1021			if (!this._mouseStarted) {
1022				event.preventDefault();
1023				return true;
1024			}
1025		}
1026
1027		// Click event may never have fired (Gecko & Opera)
1028		if (true === $.data(event.target, this.widgetName + ".preventClickEvent")) {
1029			$.removeData(event.target, this.widgetName + ".preventClickEvent");
1030		}
1031
1032		// these delegates are required to keep context
1033		this._mouseMoveDelegate = function(event) {
1034			return that._mouseMove(event);
1035		};
1036		this._mouseUpDelegate = function(event) {
1037			return that._mouseUp(event);
1038		};
1039
1040		this.document
1041			.bind( "mousemove." + this.widgetName, this._mouseMoveDelegate )
1042			.bind( "mouseup." + this.widgetName, this._mouseUpDelegate );
1043
1044		event.preventDefault();
1045
1046		mouseHandled = true;
1047		return true;
1048	},
1049
1050	_mouseMove: function(event) {
1051		// Only check for mouseups outside the document if you've moved inside the document
1052		// at least once. This prevents the firing of mouseup in the case of IE<9, which will
1053		// fire a mousemove event if content is placed under the cursor. See #7778
1054		// Support: IE <9
1055		if ( this._mouseMoved ) {
1056			// IE mouseup check - mouseup happened when mouse was out of window
1057			if ($.ui.ie && ( !document.documentMode || document.documentMode < 9 ) && !event.button) {
1058				return this._mouseUp(event);
1059
1060			// Iframe mouseup check - mouseup occurred in another document
1061			} else if ( !event.which ) {
1062				return this._mouseUp( event );
1063			}
1064		}
1065
1066		if ( event.which || event.button ) {
1067			this._mouseMoved = true;
1068		}
1069
1070		if (this._mouseStarted) {
1071			this._mouseDrag(event);
1072			return event.preventDefault();
1073		}
1074
1075		if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
1076			this._mouseStarted =
1077				(this._mouseStart(this._mouseDownEvent, event) !== false);
1078			(this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
1079		}
1080
1081		return !this._mouseStarted;
1082	},
1083
1084	_mouseUp: function(event) {
1085		this.document
1086			.unbind( "mousemove." + this.widgetName, this._mouseMoveDelegate )
1087			.unbind( "mouseup." + this.widgetName, this._mouseUpDelegate );
1088
1089		if (this._mouseStarted) {
1090			this._mouseStarted = false;
1091
1092			if (event.target === this._mouseDownEvent.target) {
1093				$.data(event.target, this.widgetName + ".preventClickEvent", true);
1094			}
1095
1096			this._mouseStop(event);
1097		}
1098
1099		mouseHandled = false;
1100		return false;
1101	},
1102
1103	_mouseDistanceMet: function(event) {
1104		return (Math.max(
1105				Math.abs(this._mouseDownEvent.pageX - event.pageX),
1106				Math.abs(this._mouseDownEvent.pageY - event.pageY)
1107			) >= this.options.distance
1108		);
1109	},
1110
1111	_mouseDelayMet: function(/* event */) {
1112		return this.mouseDelayMet;
1113	},
1114
1115	// These are placeholder methods, to be overriden by extending plugin
1116	_mouseStart: function(/* event */) {},
1117	_mouseDrag: function(/* event */) {},
1118	_mouseStop: function(/* event */) {},
1119	_mouseCapture: function(/* event */) { return true; }
1120});
1121
1122}));
1123
1124//>>label: Draggable
1125//>>group: Interactions
1126//>>description: Enables dragging functionality for any element.
1127//>>docs: http://api.jqueryui.com/draggable/
1128//>>demos: http://jqueryui.com/draggable/
1129//>>css.structure: ../themes/base/draggable.css
1130
1131(function( factory ) {
1132	if ( typeof define === "function" && define.amd ) {
1133
1134		// AMD. Register as an anonymous module.
1135		define([
1136			"jquery",
1137			"./core",
1138			"./mouse",
1139			"./widget"
1140		], factory );
1141	} else {
1142
1143		// Browser globals
1144		factory( jQuery );
1145	}
1146}(function( $ ) {
1147
1148$.widget("ui.draggable", $.ui.mouse, {
1149	version: "@VERSION",
1150	widgetEventPrefix: "drag",
1151	options: {
1152		addClasses: true,
1153		appendTo: "parent",
1154		axis: false,
1155		connectToSortable: false,
1156		containment: false,
1157		cursor: "auto",
1158		cursorAt: false,
1159		grid: false,
1160		handle: false,
1161		helper: "original",
1162		iframeFix: false,
1163		opacity: false,
1164		refreshPositions: false,
1165		revert: false,
1166		revertDuration: 500,
1167		scope: "default",
1168		scroll: true,
1169		scrollSensitivity: 20,
1170		scrollSpeed: 20,
1171		snap: false,
1172		snapMode: "both",
1173		snapTolerance: 20,
1174		stack: false,
1175		zIndex: false,
1176
1177		// callbacks
1178		drag: null,
1179		start: null,
1180		stop: null
1181	},
1182	_create: function() {
1183
1184		if ( this.options.helper === "original" ) {
1185			this._setPositionRelative();
1186		}
1187		if (this.options.addClasses){
1188			this._addClass( "ui-draggable" );
1189		}
1190		if (this.options.disabled){
1191			this._addClass( "ui-draggable-disabled" );
1192		}
1193		this._setHandleClassName();
1194
1195		this._mouseInit();
1196	},
1197
1198	_setOption: function( key, value ) {
1199		this._super( key, value );
1200		if ( key === "handle" ) {
1201			this._removeHandleClassName();
1202			this._setHandleClassName();
1203		}
1204	},
1205
1206	_destroy: function() {
1207		if ( ( this.helper || this.element ).is( ".ui-draggable-dragging" ) ) {
1208			this.destroyOnClear = true;
1209			return;
1210		}
1211		this._removeHandleClassName();
1212		this._mouseDestroy();
1213	},
1214
1215	_mouseCapture: function(event) {
1216		var o = this.options;
1217
1218		this._blurActiveElement( event );
1219
1220		// among others, prevent a drag on a resizable-handle
1221		if (this.helper || o.disabled || $(event.target).closest(".ui-resizable-handle").length > 0) {
1222			return false;
1223		}
1224
1225		//Quit if we're not on a valid handle
1226		this.handle = this._getHandle(event);
1227		if (!this.handle) {
1228			return false;
1229		}
1230
1231		this._blockFrames( o.iframeFix === true ? "iframe" : o.iframeFix );
1232
1233		return true;
1234
1235	},
1236
1237	_blockFrames: function( selector ) {
1238		this.iframeBlocks = this.document.find( selector ).map(function() {
1239			var iframe = $( this );
1240
1241			return $( "<div>" )
1242				.css( "position", "absolute" )
1243				.appendTo( iframe.parent() )
1244				.outerWidth( iframe.outerWidth() )
1245				.outerHeight( iframe.outerHeight() )
1246				.offset( iframe.offset() )[ 0 ];
1247		});
1248	},
1249
1250	_unblockFrames: function() {
1251		if ( this.iframeBlocks ) {
1252			this.iframeBlocks.remove();
1253			delete this.iframeBlocks;
1254		}
1255	},
1256
1257	_blurActiveElement: function( event ) {
1258
1259		// Only need to blur if the event occurred on the draggable itself, see #10527
1260		if ( !this.handleElement.is( event.target ) ) {
1261			return;
1262		}
1263
1264		// Blur any element that currently has focus, see #4261
1265		$.ui.safeBlur( $.ui.safeActiveElement( this.document[ 0 ] ) );
1266	},
1267
1268	_mouseStart: function(event) {
1269
1270		var o = this.options;
1271
1272		//Create and append the visible helper
1273		this.helper = this._createHelper(event);
1274
1275		this._addClass( this.helper, "ui-draggable-dragging" );
1276
1277		//Cache the helper size
1278		this._cacheHelperProportions();
1279
1280		//If ddmanager is used for droppables, set the global draggable
1281		if ($.ui.ddmanager) {
1282			$.ui.ddmanager.current = this;
1283		}
1284
1285		/*
1286		 * - Position generation -
1287		 * This block generates everything position related - it's the core of draggables.
1288		 */
1289
1290		//Cache the margins of the original element
1291		this._cacheMargins();
1292
1293		//Store the helper's css position
1294		this.cssPosition = this.helper.css( "position" );
1295		this.scrollParent = this.helper.scrollParent( true );
1296		this.offsetParent = this.helper.offsetParent();
1297		this.hasFixedAncestor = this.helper.parents().filter(function() {
1298				return $( this ).css( "position" ) === "fixed";
1299			}).length > 0;
1300
1301		//The element's absolute position on the page minus margins
1302		this.positionAbs = this.element.offset();
1303		this._refreshOffsets( event );
1304
1305		//Generate the original position
1306		this.originalPosition = this.position = this._generatePosition( event, false );
1307		this.originalPageX = event.pageX;
1308		this.originalPageY = event.pageY;
1309
1310		//Adjust the mouse offset relative to the helper if "cursorAt" is supplied
1311		(o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
1312
1313		//Set a containment if given in the options
1314		this._setContainment();
1315
1316		//Trigger event + callbacks
1317		if (this._trigger("start", event) === false) {
1318			this._clear();
1319			return false;
1320		}
1321
1322		//Recache the helper size
1323		this._cacheHelperProportions();
1324
1325		//Prepare the droppable offsets
1326		if ($.ui.ddmanager && !o.dropBehaviour) {
1327			$.ui.ddmanager.prepareOffsets(this, event);
1328		}
1329
1330		// Reset helper's right/bottom css if they're set and set explicit width/height instead
1331		// as this prevents resizing of elements with right/bottom set (see #7772)
1332		this._normalizeRightBottom();
1333
1334		this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
1335
1336		//If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)
1337		if ( $.ui.ddmanager ) {
1338			$.ui.ddmanager.dragStart(this, event);
1339		}
1340
1341		return true;
1342	},
1343
1344	_refreshOffsets: function( event ) {
1345		this.offset = {
1346			top: this.positionAbs.top - this.margins.top,
1347			left: this.positionAbs.left - this.margins.left,
1348			scroll: false,
1349			parent: this._getParentOffset(),
1350			relative: this._getRelativeOffset()
1351		};
1352
1353		this.offset.click = {
1354			left: event.pageX - this.offset.left,
1355			top: event.pageY - this.offset.top
1356		};
1357	},
1358
1359	_mouseDrag: function(event, noPropagation) {
1360		// reset any necessary cached properties (see #5009)
1361		if ( this.hasFixedAncestor ) {
1362			this.offset.parent = this._getParentOffset();
1363		}
1364
1365		//Compute the helpers position
1366		this.position = this._generatePosition( event, true );
1367		this.positionAbs = this._convertPositionTo("absolute");
1368
1369		//Call plugins and callbacks and use the resulting position if something is returned
1370		if (!noPropagation) {
1371			var ui = this._uiHash();
1372			if (this._trigger("drag", event, ui) === false) {
1373				this._mouseUp({});
1374				return false;
1375			}
1376			this.position = ui.position;
1377		}
1378
1379		this.helper[ 0 ].style.left = this.position.left + "px";
1380		this.helper[ 0 ].style.top = this.position.top + "px";
1381
1382		if ($.ui.ddmanager) {
1383			$.ui.ddmanager.drag(this, event);
1384		}
1385
1386		return false;
1387	},
1388
1389	_mouseStop: function(event) {
1390
1391		//If we are using droppables, inform the manager about the drop
1392		var that = this,
1393			dropped = false;
1394		if ($.ui.ddmanager && !this.options.dropBehaviour) {
1395			dropped = $.ui.ddmanager.drop(this, event);
1396		}
1397
1398		//if a drop comes from outside (a sortable)
1399		if (this.dropped) {
1400			dropped = this.dropped;
1401			this.dropped = false;
1402		}
1403
1404		if ((this.options.revert === "invalid" && !dropped) || (this.options.revert === "valid" && dropped) || this.options.revert === true || ($.isFunction(this.options.revert) && this.options.revert.call(this.element, dropped))) {
1405			$(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
1406				if (that._trigger("stop", event) !== false) {
1407					that._clear();
1408				}
1409			});
1410		} else {
1411			if (this._trigger("stop", event) !== false) {
1412				this._clear();
1413			}
1414		}
1415
1416		return false;
1417	},
1418
1419	_mouseUp: function( event ) {
1420		this._unblockFrames();
1421
1422		//If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003)
1423		if ( $.ui.ddmanager ) {
1424			$.ui.ddmanager.dragStop(this, event);
1425		}
1426
1427		// Only need to focus if the event occurred on the draggable itself, see #10527
1428		if ( this.handleElement.is( event.target ) ) {
1429			// The interaction is over; whether or not the click resulted in a drag, focus the element
1430			this.element.focus();
1431		}
1432
1433		return $.ui.mouse.prototype._mouseUp.call(this, event);
1434	},
1435
1436	cancel: function() {
1437
1438		if (this.helper.is(".ui-draggable-dragging")) {
1439			this._mouseUp({});
1440		} else {
1441			this._clear();
1442		}
1443
1444		return this;
1445
1446	},
1447
1448	_getHandle: function(event) {
1449		return this.options.handle ?
1450			!!$( event.target ).closest( this.element.find( this.options.handle ) ).length :
1451			true;
1452	},
1453
1454	_setHandleClassName: function() {
1455		this.handleElement = this.options.handle ?
1456			this.element.find( this.options.handle ) : this.element;
1457		this._addClass( this.handleElement, "ui-draggable-handle" );
1458	},
1459
1460	_removeHandleClassName: function() {
1461		this._removeClass( this.handleElement,
vendor: 10,648 bytes, lines 1461-1765
1461 "ui-draggable-handle" );
1462	},
1463
1464	_createHelper: function(event) {
1465
1466		var o = this.options,
1467			helperIsFunction = $.isFunction( o.helper ),
1468			helper = helperIsFunction ?
1469				$( o.helper.apply( this.element[ 0 ], [ event ] ) ) :
1470				( o.helper === "clone" ?
1471					this.element.clone().removeAttr( "id" ) :
1472					this.element );
1473
1474		if (!helper.parents("body").length) {
1475			helper.appendTo((o.appendTo === "parent" ? this.element[0].parentNode : o.appendTo));
1476		}
1477
1478		// http://bugs.jqueryui.com/ticket/9446
1479		// a helper function can return the original element
1480		// which wouldn't have been set to relative in _create
1481		if ( helperIsFunction && helper[ 0 ] === this.element[ 0 ] ) {
1482			this._setPositionRelative();
1483		}
1484
1485		if (helper[0] !== this.element[0] && !(/(fixed|absolute)/).test(helper.css("position"))) {
1486			helper.css("position", "absolute");
1487		}
1488
1489		return helper;
1490
1491	},
1492
1493	_setPositionRelative: function() {
1494		if ( !( /^(?:r|a|f)/ ).test( this.element.css( "position" ) ) ) {
1495			this.element[ 0 ].style.position = "relative";
1496		}
1497	},
1498
1499	_adjustOffsetFromHelper: function(obj) {
1500		if (typeof obj === "string") {
1501			obj = obj.split(" ");
1502		}
1503		if ($.isArray(obj)) {
1504			obj = { left: +obj[0], top: +obj[1] || 0 };
1505		}
1506		if ("left" in obj) {
1507			this.offset.click.left = obj.left + this.margins.left;
1508		}
1509		if ("right" in obj) {
1510			this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
1511		}
1512		if ("top" in obj) {
1513			this.offset.click.top = obj.top + this.margins.top;
1514		}
1515		if ("bottom" in obj) {
1516			this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
1517		}
1518	},
1519
1520	_isRootNode: function( element ) {
1521		return ( /(html|body)/i ).test( element.tagName ) || element === this.document[ 0 ];
1522	},
1523
1524	_getParentOffset: function() {
1525
1526		//Get the offsetParent and cache its position
1527		var po = this.offsetParent.offset(),
1528			document = this.document[ 0 ];
1529
1530		// This is a special case where we need to modify a offset calculated on start, since the following happened:
1531		// 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
1532		// 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
1533		//    the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
1534		if (this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) {
1535			po.left += this.scrollParent.scrollLeft();
1536			po.top += this.scrollParent.scrollTop();
1537		}
1538
1539		if ( this._isRootNode( this.offsetParent[ 0 ] ) ) {
1540			po = { top: 0, left: 0 };
1541		}
1542
1543		return {
1544			top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"), 10) || 0),
1545			left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"), 10) || 0)
1546		};
1547
1548	},
1549
1550	_getRelativeOffset: function() {
1551		if ( this.cssPosition !== "relative" ) {
1552			return { top: 0, left: 0 };
1553		}
1554
1555		var p = this.element.position(),
1556			scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] );
1557
1558		return {
1559			top: p.top - ( parseInt(this.helper.css( "top" ), 10) || 0 ) + ( !scrollIsRootNode ? this.scrollParent.scrollTop() : 0 ),
1560			left: p.left - ( parseInt(this.helper.css( "left" ), 10) || 0 ) + ( !scrollIsRootNode ? this.scrollParent.scrollLeft() : 0 )
1561		};
1562
1563	},
1564
1565	_cacheMargins: function() {
1566		this.margins = {
1567			left: (parseInt(this.element.css("marginLeft"), 10) || 0),
1568			top: (parseInt(this.element.css("marginTop"), 10) || 0),
1569			right: (parseInt(this.element.css("marginRight"), 10) || 0),
1570			bottom: (parseInt(this.element.css("marginBottom"), 10) || 0)
1571		};
1572	},
1573
1574	_cacheHelperProportions: function() {
1575		this.helperProportions = {
1576			width: this.helper.outerWidth(),
1577			height: this.helper.outerHeight()
1578		};
1579	},
1580
1581	_setContainment: function() {
1582
1583		var isUserScrollable, c, ce,
1584			o = this.options,
1585			document = this.document[ 0 ];
1586
1587		this.relativeContainer = null;
1588
1589		if ( !o.containment ) {
1590			this.containment = null;
1591			return;
1592		}
1593
1594		if ( o.containment === "window" ) {
1595			this.containment = [
1596				$( window ).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
1597				$( window ).scrollTop() - this.offset.relative.top - this.offset.parent.top,
1598				$( window ).scrollLeft() + $( window ).width() - this.helperProportions.width - this.margins.left,
1599				$( window ).scrollTop() + ( $( window ).height() || document.body.parentNode.scrollHeight ) - this.helperProportions.height - this.margins.top
1600			];
1601			return;
1602		}
1603
1604		if ( o.containment === "document") {
1605			this.containment = [
1606				0,
1607				0,
1608				$( document ).width() - this.helperProportions.width - this.margins.left,
1609				( $( document ).height() || document.body.parentNode.scrollHeight ) - this.helperProportions.height - this.margins.top
1610			];
1611			return;
1612		}
1613
1614		if ( o.containment.constructor === Array ) {
1615			this.containment = o.containment;
1616			return;
1617		}
1618
1619		if ( o.containment === "parent" ) {
1620			o.containment = this.helper[ 0 ].parentNode;
1621		}
1622
1623		c = $( o.containment );
1624		ce = c[ 0 ];
1625
1626		if ( !ce ) {
1627			return;
1628		}
1629
1630		isUserScrollable = /(scroll|auto)/.test( c.css( "overflow" ) );
1631
1632		this.containment = [
1633			( parseInt( c.css( "borderLeftWidth" ), 10 ) || 0 ) + ( parseInt( c.css( "paddingLeft" ), 10 ) || 0 ),
1634			( parseInt( c.css( "borderTopWidth" ), 10 ) || 0 ) + ( parseInt( c.css( "paddingTop" ), 10 ) || 0 ),
1635			( isUserScrollable ? Math.max( ce.scrollWidth, ce.offsetWidth ) : ce.offsetWidth ) -
1636				( parseInt( c.css( "borderRightWidth" ), 10 ) || 0 ) -
1637				( parseInt( c.css( "paddingRight" ), 10 ) || 0 ) -
1638				this.helperProportions.width -
1639				this.margins.left -
1640				this.margins.right,
1641			( isUserScrollable ? Math.max( ce.scrollHeight, ce.offsetHeight ) : ce.offsetHeight ) -
1642				( parseInt( c.css( "borderBottomWidth" ), 10 ) || 0 ) -
1643				( parseInt( c.css( "paddingBottom" ), 10 ) || 0 ) -
1644				this.helperProportions.height -
1645				this.margins.top -
1646				this.margins.bottom
1647		];
1648		this.relativeContainer = c;
1649	},
1650
1651	_convertPositionTo: function(d, pos) {
1652
1653		if (!pos) {
1654			pos = this.position;
1655		}
1656
1657		var mod = d === "absolute" ? 1 : -1,
1658			scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] );
1659
1660		return {
1661			top: (
1662				pos.top	+																// The absolute mouse position
1663				this.offset.relative.top * mod +										// Only for relative positioned nodes: Relative offset from element to offset parent
1664				this.offset.parent.top * mod -										// The offsetParent's offset without borders (offset + border)
1665				( ( this.cssPosition === "fixed" ? -this.offset.scroll.top : ( scrollIsRootNode ? 0 : this.offset.scroll.top ) ) * mod)
1666			),
1667			left: (
1668				pos.left +																// The absolute mouse position
1669				this.offset.relative.left * mod +										// Only for relative positioned nodes: Relative offset from element to offset parent
1670				this.offset.parent.left * mod	-										// The offsetParent's offset without borders (offset + border)
1671				( ( this.cssPosition === "fixed" ? -this.offset.scroll.left : ( scrollIsRootNode ? 0 : this.offset.scroll.left ) ) * mod)
1672			)
1673		};
1674
1675	},
1676
1677	_generatePosition: function( event, constrainPosition ) {
1678
1679		var containment, co, top, left,
1680			o = this.options,
1681			scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] ),
1682			pageX = event.pageX,
1683			pageY = event.pageY;
1684
1685		// Cache the scroll
1686		if ( !scrollIsRootNode || !this.offset.scroll ) {
1687			this.offset.scroll = {
1688				top: this.scrollParent.scrollTop(),
1689				left: this.scrollParent.scrollLeft()
1690			};
1691		}
1692
1693		/*
1694		 * - Position constraining -
1695		 * Constrain the position to a mix of grid, containment.
1696		 */
1697
1698		// If we are not dragging yet, we won't check for options
1699		if ( constrainPosition ) {
1700			if ( this.containment ) {
1701				if ( this.relativeContainer ){
1702					co = this.relativeContainer.offset();
1703					containment = [
1704						this.containment[ 0 ] + co.left,
1705						this.containment[ 1 ] + co.top,
1706						this.containment[ 2 ] + co.left,
1707						this.containment[ 3 ] + co.top
1708					];
1709				} else {
1710					containment = this.containment;
1711				}
1712
1713				if (event.pageX - this.offset.click.left < containment[0]) {
1714					pageX = containment[0] + this.offset.click.left;
1715				}
1716				if (event.pageY - this.offset.click.top < containment[1]) {
1717					pageY = containment[1] + this.offset.click.top;
1718				}
1719				if (event.pageX - this.offset.click.left > containment[2]) {
1720					pageX = containment[2] + this.offset.click.left;
1721				}
1722				if (event.pageY - this.offset.click.top > containment[3]) {
1723					pageY = containment[3] + this.offset.click.top;
1724				}
1725			}
1726
1727			if (o.grid) {
1728				//Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950)
1729				top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY;
1730				pageY = containment ? ((top - this.offset.click.top >= containment[1] || top - this.offset.click.top > containment[3]) ? top : ((top - this.offset.click.top >= containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
1731
1732				left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX;
1733				pageX = containment ? ((left - this.offset.click.left >= containment[0] || left - this.offset.click.left > containment[2]) ? left : ((left - this.offset.click.left >= containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
1734			}
1735
1736			if ( o.axis === "y" ) {
1737				pageX = this.originalPageX;
1738			}
1739
1740			if ( o.axis === "x" ) {
1741				pageY = this.originalPageY;
1742			}
1743		}
1744
1745		return {
1746			top: (
1747				pageY -																	// The absolute mouse position
1748				this.offset.click.top	-												// Click offset (relative to the element)
1749				this.offset.relative.top -												// Only for relative positioned nodes: Relative offset from element to offset parent
1750				this.offset.parent.top +												// The offsetParent's offset without borders (offset + border)
1751				( this.cssPosition === "fixed" ? -this.offset.scroll.top : ( scrollIsRootNode ? 0 : this.offset.scroll.top ) )
1752			),
1753			left: (
1754				pageX -																	// The absolute mouse position
1755				this.offset.click.left -												// Click offset (relative to the element)
1756				this.offset.relative.left -												// Only for relative positioned nodes: Relative offset from element to offset parent
1757				this.offset.parent.left +												// The offsetParent's offset without borders (offset + border)
1758				( this.cssPosition === "fixed" ? -this.offset.scroll.left : ( scrollIsRootNode ? 0 : this.offset.scroll.left ) )
1759			)
1760		};
1761
1762	},
1763
1764	_clear: function() {
1765		this.
1765_removeClass( this.helper, "ui-draggable-dragging" 
vendor: 15,213 bytes, lines 1765-2237
1765);
1766		if (this.helper[0] !== this.element[0] && !this.cancelHelperRemoval) {
1767			this.helper.remove();
1768		}
1769		this.helper = null;
1770		this.cancelHelperRemoval = false;
1771		if ( this.destroyOnClear ) {
1772			this.destroy();
1773		}
1774	},
1775
1776	_normalizeRightBottom: function() {
1777		if ( this.options.axis !== "y" && this.helper.css( "right" ) !== "auto" ) {
1778			this.helper.width( this.helper.width() );
1779			this.helper.css( "right", "auto" );
1780		}
1781		if ( this.options.axis !== "x" && this.helper.css( "bottom" ) !== "auto" ) {
1782			this.helper.height( this.helper.height() );
1783			this.helper.css( "bottom", "auto" );
1784		}
1785	},
1786
1787	// From now on bulk stuff - mainly helpers
1788
1789	_trigger: function( type, event, ui ) {
1790		ui = ui || this._uiHash();
1791		$.ui.plugin.call( this, type, [ event, ui, this ], true );
1792
1793		// Absolute position and offset (see #6884 ) have to be recalculated after plugins
1794		if ( /^(drag|start|stop)/.test( type ) ) {
1795			this.positionAbs = this._convertPositionTo( "absolute" );
1796			ui.offset = this.positionAbs;
1797		}
1798		return $.Widget.prototype._trigger.call( this, type, event, ui );
1799	},
1800
1801	plugins: {},
1802
1803	_uiHash: function() {
1804		return {
1805			helper: this.helper,
1806			position: this.position,
1807			originalPosition: this.originalPosition,
1808			offset: this.positionAbs
1809		};
1810	}
1811
1812});
1813
1814$.ui.plugin.add( "draggable", "connectToSortable", {
1815	start: function( event, ui, draggable ) {
1816		var uiSortable = $.extend( {}, ui, {
1817			item: draggable.element
1818		});
1819
1820		draggable.sortables = [];
1821		$( draggable.options.connectToSortable ).each(function() {
1822			var sortable = $( this ).sortable( "instance" );
1823
1824			if ( sortable && !sortable.options.disabled ) {
1825				draggable.sortables.push( sortable );
1826
1827				// refreshPositions is called at drag start to refresh the containerCache
1828				// which is used in drag. This ensures it's initialized and synchronized
1829				// with any changes that might have happened on the page since initialization.
1830				sortable.refreshPositions();
1831				sortable._trigger("activate", event, uiSortable);
1832			}
1833		});
1834	},
1835	stop: function( event, ui, draggable ) {
1836		var uiSortable = $.extend( {}, ui, {
1837			item: draggable.element
1838		});
1839
1840		draggable.cancelHelperRemoval = false;
1841
1842		$.each( draggable.sortables, function() {
1843			var sortable = this;
1844
1845			if ( sortable.isOver ) {
1846				sortable.isOver = 0;
1847
1848				// Allow this sortable to handle removing the helper
1849				draggable.cancelHelperRemoval = true;
1850				sortable.cancelHelperRemoval = false;
1851
1852				// Use _storedCSS To restore properties in the sortable,
1853				// as this also handles revert (#9675) since the draggable
1854				// may have modified them in unexpected ways (#8809)
1855				sortable._storedCSS = {
1856					position: sortable.placeholder.css( "position" ),
1857					top: sortable.placeholder.css( "top" ),
1858					left: sortable.placeholder.css( "left" )
1859				};
1860
1861				sortable._mouseStop(event);
1862
1863				// Once drag has ended, the sortable should return to using
1864				// its original helper, not the shared helper from draggable
1865				sortable.options.helper = sortable.options._helper;
1866			} else {
1867				// Prevent this Sortable from removing the helper.
1868				// However, don't set the draggable to remove the helper
1869				// either as another connected Sortable may yet handle the removal.
1870				sortable.cancelHelperRemoval = true;
1871
1872				sortable._trigger( "deactivate", event, uiSortable );
1873			}
1874		});
1875	},
1876	drag: function( event, ui, draggable ) {
1877		$.each( draggable.sortables, function() {
1878			var innermostIntersecting = false,
1879				sortable = this;
1880
1881			// Copy over variables that sortable's _intersectsWith uses
1882			sortable.positionAbs = draggable.positionAbs;
1883			sortable.helperProportions = draggable.helperProportions;
1884			sortable.offset.click = draggable.offset.click;
1885
1886			if ( sortable._intersectsWith( sortable.containerCache ) ) {
1887				innermostIntersecting = true;
1888
1889				$.each( draggable.sortables, function() {
1890					// Copy over variables that sortable's _intersectsWith uses
1891					this.positionAbs = draggable.positionAbs;
1892					this.helperProportions = draggable.helperProportions;
1893					this.offset.click = draggable.offset.click;
1894
1895					if ( this !== sortable &&
1896							this._intersectsWith( this.containerCache ) &&
1897							$.contains( sortable.element[ 0 ], this.element[ 0 ] ) ) {
1898						innermostIntersecting = false;
1899					}
1900
1901					return innermostIntersecting;
1902				});
1903			}
1904
1905			if ( innermostIntersecting ) {
1906				// If it intersects, we use a little isOver variable and set it once,
1907				// so that the move-in stuff gets fired only once.
1908				if ( !sortable.isOver ) {
1909					sortable.isOver = 1;
1910
1911					// Store draggable's parent in case we need to reappend to it later.
1912					draggable._parent = ui.helper.parent();
1913
1914					sortable.currentItem = ui.helper
1915						.appendTo( sortable.element )
1916						.data( "ui-sortable-item", true );
1917
1918					// Store helper option to later restore it
1919					sortable.options._helper = sortable.options.helper;
1920
1921					sortable.options.helper = function() {
1922						return ui.helper[ 0 ];
1923					};
1924
1925					// Fire the start events of the sortable with our passed browser event,
1926					// and our own helper (so it doesn't create a new one)
1927					event.target = sortable.currentItem[ 0 ];
1928					sortable._mouseCapture( event, true );
1929					sortable._mouseStart( event, true, true );
1930
1931					// Because the browser event is way off the new appended portlet,
1932					// modify necessary variables to reflect the changes
1933					sortable.offset.click.top = draggable.offset.click.top;
1934					sortable.offset.click.left = draggable.offset.click.left;
1935					sortable.offset.parent.left -= draggable.offset.parent.left -
1936						sortable.offset.parent.left;
1937					sortable.offset.parent.top -= draggable.offset.parent.top -
1938						sortable.offset.parent.top;
1939
1940					draggable._trigger( "toSortable", event );
1941
1942					// Inform draggable that the helper is in a valid drop zone,
1943					// used solely in the revert option to handle "valid/invalid".
1944					draggable.dropped = sortable.element;
1945
1946					// Need to refreshPositions of all sortables in the case that
1947					// adding to one sortable changes the location of the other sortables (#9675)
1948					$.each( draggable.sortables, function() {
1949						this.refreshPositions();
1950					});
1951
1952					// hack so receive/update callbacks work (mostly)
1953					draggable.currentItem = draggable.element;
1954					sortable.fromOutside = draggable;
1955				}
1956
1957				if ( sortable.currentItem ) {
1958					sortable._mouseDrag( event );
1959					// Copy the sortable's position because the draggable's can potentially reflect
1960					// a relative position, while sortable is always absolute, which the dragged
1961					// element has now become. (#8809)
1962					ui.position = sortable.position;
1963				}
1964			} else {
1965				// If it doesn't intersect with the sortable, and it intersected before,
1966				// we fake the drag stop of the sortable, but make sure it doesn't remove
1967				// the helper by using cancelHelperRemoval.
1968				if ( sortable.isOver ) {
1969
1970					sortable.isOver = 0;
1971					sortable.cancelHelperRemoval = true;
1972
1973					// Calling sortable's mouseStop would trigger a revert,
1974					// so revert must be temporarily false until after mouseStop is called.
1975					sortable.options._revert = sortable.options.revert;
1976					sortable.options.revert = false;
1977
1978					sortable._trigger( "out", event, sortable._uiHash( sortable ) );
1979					sortable._mouseStop( event, true );
1980
1981					// restore sortable behaviors that were modfied
1982					// when the draggable entered the sortable area (#9481)
1983					sortable.options.revert = sortable.options._revert;
1984					sortable.options.helper = sortable.options._helper;
1985
1986					if ( sortable.placeholder ) {
1987						sortable.placeholder.remove();
1988					}
1989
1990					// Restore and recalculate the draggable's offset considering the sortable
1991					// may have modified them in unexpected ways. (#8809, #10669)
1992					ui.helper.appendTo( draggable._parent );
1993					draggable._refreshOffsets( event );
1994					ui.position = draggable._generatePosition( event, true );
1995
1996					draggable._trigger( "fromSortable", event );
1997
1998					// Inform draggable that the helper is no longer in a valid drop zone
1999					draggable.dropped = false;
2000
2001					// Need to refreshPositions of all sortables just in case removing
2002					// from one sortable changes the location of other sortables (#9675)
2003					$.each( draggable.sortables, function() {
2004						this.refreshPositions();
2005					});
2006				}
2007			}
2008		});
2009	}
2010});
2011
2012$.ui.plugin.add("draggable", "cursor", {
2013	start: function( event, ui, instance ) {
2014		var t = $( "body" ),
2015			o = instance.options;
2016
2017		if (t.css("cursor")) {
2018			o._cursor = t.css("cursor");
2019		}
2020		t.css("cursor", o.cursor);
2021	},
2022	stop: function( event, ui, instance ) {
2023		var o = instance.options;
2024		if (o._cursor) {
2025			$("body").css("cursor", o._cursor);
2026		}
2027	}
2028});
2029
2030$.ui.plugin.add("draggable", "opacity", {
2031	start: function( event, ui, instance ) {
2032		var t = $( ui.helper ),
2033			o = instance.options;
2034		if (t.css("opacity")) {
2035			o._opacity = t.css("opacity");
2036		}
2037		t.css("opacity", o.opacity);
2038	},
2039	stop: function( event, ui, instance ) {
2040		var o = instance.options;
2041		if (o._opacity) {
2042			$(ui.helper).css("opacity", o._opacity);
2043		}
2044	}
2045});
2046
2047$.ui.plugin.add("draggable", "scroll", {
2048	start: function( event, ui, i ) {
2049		if ( !i.scrollParentNotHidden ) {
2050			i.scrollParentNotHidden = i.helper.scrollParent( false );
2051		}
2052
2053		if ( i.scrollParentNotHidden[ 0 ] !== i.document[ 0 ] && i.scrollParentNotHidden[ 0 ].tagName !== "HTML" ) {
2054			i.overflowOffset = i.scrollParentNotHidden.offset();
2055		}
2056	},
2057	drag: function( event, ui, i  ) {
2058
2059		var o = i.options,
2060			scrolled = false,
2061			scrollParent = i.scrollParentNotHidden[ 0 ],
2062			document = i.document[ 0 ];
2063
2064		if ( scrollParent !== document && scrollParent.tagName !== "HTML" ) {
2065			if ( !o.axis || o.axis !== "x" ) {
2066				if ( ( i.overflowOffset.top + scrollParent.offsetHeight ) - event.pageY < o.scrollSensitivity ) {
2067					scrollParent.scrollTop = scrolled = scrollParent.scrollTop + o.scrollSpeed;
2068				} else if ( event.pageY - i.overflowOffset.top < o.scrollSensitivity ) {
2069					scrollParent.scrollTop = scrolled = scrollParent.scrollTop - o.scrollSpeed;
2070				}
2071			}
2072
2073			if ( !o.axis || o.axis !== "y" ) {
2074				if ( ( i.overflowOffset.left + scrollParent.offsetWidth ) - event.pageX < o.scrollSensitivity ) {
2075					scrollParent.scrollLeft = scrolled = scrollParent.scrollLeft + o.scrollSpeed;
2076				} else if ( event.pageX - i.overflowOffset.left < o.scrollSensitivity ) {
2077					scrollParent.scrollLeft = scrolled = scrollParent.scrollLeft - o.scrollSpeed;
2078				}
2079			}
2080
2081		} else {
2082
2083			if (!o.axis || o.axis !== "x") {
2084				if (event.pageY - $(document).scrollTop() < o.scrollSensitivity) {
2085					scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
2086				} else if ($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) {
2087					scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
2088				}
2089			}
2090
2091			if (!o.axis || o.axis !== "y") {
2092				if (event.pageX - $(document).scrollLeft() < o.scrollSensitivity) {
2093					scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
2094				} else if ($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) {
2095					scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
2096				}
2097			}
2098
2099		}
2100
2101		if (scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) {
2102			$.ui.ddmanager.prepareOffsets(i, event);
2103		}
2104
2105	}
2106});
2107
2108$.ui.plugin.add("draggable", "snap", {
2109	start: function( event, ui, i ) {
2110
2111		var o = i.options;
2112
2113		i.snapElements = [];
2114
2115		$(o.snap.constructor !== String ? ( o.snap.items || ":data(ui-draggable)" ) : o.snap).each(function() {
2116			var $t = $(this),
2117				$o = $t.offset();
2118			if (this !== i.element[0]) {
2119				i.snapElements.push({
2120					item: this,
2121					width: $t.outerWidth(), height: $t.outerHeight(),
2122					top: $o.top, left: $o.left
2123				});
2124			}
2125		});
2126
2127	},
2128	drag: function( event, ui, inst ) {
2129
2130		var ts, bs, ls, rs, l, r, t, b, i, first,
2131			o = inst.options,
2132			d = o.snapTolerance,
2133			x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
2134			y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
2135
2136		for (i = inst.snapElements.length - 1; i >= 0; i--){
2137
2138			l = inst.snapElements[i].left - inst.margins.left;
2139			r = l + inst.snapElements[i].width;
2140			t = inst.snapElements[i].top - inst.margins.top;
2141			b = t + inst.snapElements[i].height;
2142
2143			if ( x2 < l - d || x1 > r + d || y2 < t - d || y1 > b + d || !$.contains( inst.snapElements[ i ].item.ownerDocument, inst.snapElements[ i ].item ) ) {
2144				if (inst.snapElements[i].snapping) {
2145					(inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
2146				}
2147				inst.snapElements[i].snapping = false;
2148				continue;
2149			}
2150
2151			if (o.snapMode !== "inner") {
2152				ts = Math.abs(t - y2) <= d;
2153				bs = Math.abs(b - y1) <= d;
2154				ls = Math.abs(l - x2) <= d;
2155				rs = Math.abs(r - x1) <= d;
2156				if (ts) {
2157					ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top;
2158				}
2159				if (bs) {
2160					ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top;
2161				}
2162				if (ls) {
2163					ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left;
2164				}
2165				if (rs) {
2166					ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left;
2167				}
2168			}
2169
2170			first = (ts || bs || ls || rs);
2171
2172			if (o.snapMode !== "outer") {
2173				ts = Math.abs(t - y1) <= d;
2174				bs = Math.abs(b - y2) <= d;
2175				ls = Math.abs(l - x1) <= d;
2176				rs = Math.abs(r - x2) <= d;
2177				if (ts) {
2178					ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top;
2179				}
2180				if (bs) {
2181					ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top;
2182				}
2183				if (ls) {
2184					ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left;
2185				}
2186				if (rs) {
2187					ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left;
2188				}
2189			}
2190
2191			if (!inst.snapElements[i].snapping && (ts || bs || ls || rs || first)) {
2192				(inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
2193			}
2194			inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
2195
2196		}
2197
2198	}
2199});
2200
2201$.ui.plugin.add("draggable", "stack", {
2202	start: function( event, ui, instance ) {
2203		var min,
2204			o = instance.options,
2205			group = $.makeArray($(o.stack)).sort(function(a, b) {
2206				return (parseInt($(a).css("zIndex"), 10) || 0) - (parseInt($(b).css("zIndex"), 10) || 0);
2207			});
2208
2209		if (!group.length) { return; }
2210
2211		min = parseInt($(group[0]).css("zIndex"), 10) || 0;
2212		$(group).each(function(i) {
2213			$(this).css("zIndex", min + i);
2214		});
2215		this.css("zIndex", (min + group.length));
2216	}
2217});
2218
2219$.ui.plugin.add("draggable", "zIndex", {
2220	start: function( event, ui, instance ) {
2221		var t = $( ui.helper ),
2222			o = instance.options;
2223
2224		if (t.css("zIndex")) {
2225			o._zIndex = t.css("zIndex");
2226		}
2227		t.css("zIndex", o.zIndex);
2228	},
2229	stop: function( event, ui, instance ) {
2230		var o = instance.options;
2231
2232		if (o._zIndex) {
2233			$(ui.helper).css("zIndex", o._zIndex);
2234		}
2235	}
2236});
2237
vendor: 13,909 bytes, lines 2238-2761
2238return $.ui.draggable;
2239
2240}));
2241
2242//>>label: Droppable
2243//>>group: Interactions
2244//>>description: Enables drop targets for draggable elements.
2245//>>docs: http://api.jqueryui.com/droppable/
2246//>>demos: http://jqueryui.com/droppable/
2247
2248(function( factory ) {
2249	if ( typeof define === "function" && define.amd ) {
2250
2251		// AMD. Register as an anonymous module.
2252		define([
2253			"jquery",
2254			"./core",
2255			"./widget",
2256			"./mouse",
2257			"./draggable"
2258		], factory );
2259	} else {
2260
2261		// Browser globals
2262		factory( jQuery );
2263	}
2264}(function( $ ) {
2265
2266$.widget( "ui.droppable", {
2267	version: "@VERSION",
2268	widgetEventPrefix: "drop",
2269	options: {
2270		accept: "*",
2271		addClasses: true,
2272		greedy: false,
2273		scope: "default",
2274		tolerance: "intersect",
2275
2276		// callbacks
2277		activate: null,
2278		deactivate: null,
2279		drop: null,
2280		out: null,
2281		over: null
2282	},
2283	_create: function() {
2284
2285		var proportions,
2286			o = this.options,
2287			accept = o.accept;
2288
2289		this.isover = false;
2290		this.isout = true;
2291
2292		this.accept = $.isFunction( accept ) ? accept : function( d ) {
2293			return d.is( accept );
2294		};
2295
2296		this.proportions = function( /* valueToWrite */ ) {
2297			if ( arguments.length ) {
2298				// Store the droppable's proportions
2299				proportions = arguments[ 0 ];
2300			} else {
2301				// Retrieve or derive the droppable's proportions
2302				return proportions ?
2303					proportions :
2304					proportions = {
2305						width: this.element[ 0 ].offsetWidth,
2306						height: this.element[ 0 ].offsetHeight
2307					};
2308			}
2309		};
2310
2311		this._addToManager( o.scope );
2312
2313		o.addClasses && this._addClass( "ui-droppable" );
2314
2315	},
2316
2317	_addToManager: function( scope ) {
2318		// Add the reference and positions to the manager
2319		$.ui.ddmanager.droppables[ scope ] = $.ui.ddmanager.droppables[ scope ] || [];
2320		$.ui.ddmanager.droppables[ scope ].push( this );
2321	},
2322
2323	_splice: function( drop ) {
2324		var i = 0;
2325		for ( ; i < drop.length; i++ ) {
2326			if ( drop[ i ] === this ) {
2327				drop.splice( i, 1 );
2328			}
2329		}
2330	},
2331
2332	_destroy: function() {
2333		var drop = $.ui.ddmanager.droppables[ this.options.scope ];
2334
2335		this._splice( drop );
2336	},
2337
2338	_setOption: function( key, value ) {
2339
2340		if ( key === "accept" ) {
2341			this.accept = $.isFunction( value ) ? value : function( d ) {
2342				return d.is( value );
2343			};
2344		} else if ( key === "scope" ) {
2345			var drop = $.ui.ddmanager.droppables[ this.options.scope ];
2346
2347			this._splice( drop );
2348			this._addToManager( value );
2349		}
2350
2351		this._super( key, value );
2352	},
2353
2354	_activate: function( event ) {
2355		var draggable = $.ui.ddmanager.current;
2356
2357		this._addActiveClass();
2358		if ( draggable ){
2359			this._trigger( "activate", event, this.ui( draggable ) );
2360		}
2361	},
2362
2363	_deactivate: function( event ) {
2364		var draggable = $.ui.ddmanager.current;
2365
2366		this._removeActiveClass();
2367		if ( draggable ){
2368			this._trigger( "deactivate", event, this.ui( draggable ) );
2369		}
2370	},
2371
2372	_over: function( event ) {
2373
2374		var draggable = $.ui.ddmanager.current;
2375
2376		// Bail if draggable and droppable are same element
2377		if ( !draggable || ( draggable.currentItem || draggable.element )[ 0 ] === this.element[ 0 ] ) {
2378			return;
2379		}
2380
2381		if ( this.accept.call( this.element[ 0 ], ( draggable.currentItem || draggable.element ) ) ) {
2382			this._addHoverClass();
2383			this._trigger( "over", event, this.ui( draggable ) );
2384		}
2385
2386	},
2387
2388	_out: function( event ) {
2389
2390		var draggable = $.ui.ddmanager.current;
2391
2392		// Bail if draggable and droppable are same element
2393		if ( !draggable || ( draggable.currentItem || draggable.element )[ 0 ] === this.element[ 0 ] ) {
2394			return;
2395		}
2396
2397		if ( this.accept.call( this.element[ 0 ], ( draggable.currentItem || draggable.element ) ) ) {
2398			this._removeHoverClass();
2399			this._trigger( "out", event, this.ui( draggable ) );
2400		}
2401
2402	},
2403
2404	_drop: function( event, custom ) {
2405
2406		var draggable = custom || $.ui.ddmanager.current,
2407			childrenIntersection = false;
2408
2409		// Bail if draggable and droppable are same element
2410		if ( !draggable || ( draggable.currentItem || draggable.element )[ 0 ] === this.element[ 0 ] ) {
2411			return false;
2412		}
2413
2414		this.element.find( ":data(ui-droppable)" ).not( ".ui-draggable-dragging" ).each(function() {
2415			var inst = $( this ).droppable( "instance" );
2416			if (
2417				inst.options.greedy &&
2418				!inst.options.disabled &&
2419				inst.options.scope === draggable.options.scope &&
2420				inst.accept.call( inst.element[ 0 ], ( draggable.currentItem || draggable.element ) ) &&
2421				intersect( draggable, $.extend( inst, { offset: inst.element.offset() } ), inst.options.tolerance, event )
2422			) { childrenIntersection = true; return false; }
2423		});
2424		if ( childrenIntersection ) {
2425			return false;
2426		}
2427
2428		if ( this.accept.call( this.element[ 0 ], ( draggable.currentItem || draggable.element ) ) ) {
2429			this._removeActiveClass();
2430			this._removeHoverClass();
2431
2432			this._trigger( "drop", event, this.ui( draggable ) );
2433			return this.element;
2434		}
2435
2436		return false;
2437
2438	},
2439
2440	ui: function( c ) {
2441		return {
2442			draggable: ( c.currentItem || c.element ),
2443			helper: c.helper,
2444			position: c.position,
2445			offset: c.positionAbs
2446		};
2447	},
2448
2449	// Extension points just to make backcompat sane and avoid duplicating logic
2450	// TODO: Remove in 1.13 along with call to it below
2451	_addHoverClass: function() {
2452		this._addClass( "ui-droppable-hover" );
2453	},
2454
2455	_removeHoverClass: function() {
2456		this._removeClass( "ui-droppable-hover" );
2457	},
2458
2459	_addActiveClass: function() {
2460		this._addClass( "ui-droppable-active" );
2461	},
2462
2463	_removeActiveClass: function() {
2464		this._removeClass( "ui-droppable-active" );
2465	}
2466});
2467
2468var intersect = (function() {
2469	function isOverAxis( x, reference, size ) {
2470		return ( x >= reference ) && ( x < ( reference + size ) );
2471	}
2472
2473	return function( draggable, droppable, toleranceMode, event ) {
2474
2475		if ( !droppable.offset ) {
2476			return false;
2477		}
2478
2479		var x1 = ( draggable.positionAbs || draggable.position.absolute ).left + draggable.margins.left,
2480			y1 = ( draggable.positionAbs || draggable.position.absolute ).top + draggable.margins.top,
2481			x2 = x1 + draggable.helperProportions.width,
2482			y2 = y1 + draggable.helperProportions.height,
2483			l = droppable.offset.left,
2484			t = droppable.offset.top,
2485			r = l + droppable.proportions().width,
2486			b = t + droppable.proportions().height;
2487
2488		switch ( toleranceMode ) {
2489		case "fit":
2490			return ( l <= x1 && x2 <= r && t <= y1 && y2 <= b );
2491		case "intersect":
2492			return ( l < x1 + ( draggable.helperProportions.width / 2 ) && // Right Half
2493				x2 - ( draggable.helperProportions.width / 2 ) < r && // Left Half
2494				t < y1 + ( draggable.helperProportions.height / 2 ) && // Bottom Half
2495				y2 - ( draggable.helperProportions.height / 2 ) < b ); // Top Half
2496		case "pointer":
2497			return isOverAxis( event.pageY, t, droppable.proportions().height ) && isOverAxis( event.pageX, l, droppable.proportions().width );
2498		case "touch":
2499			return (
2500				( y1 >= t && y1 <= b ) || // Top edge touching
2501				( y2 >= t && y2 <= b ) || // Bottom edge touching
2502				( y1 < t && y2 > b ) // Surrounded vertically
2503			) && (
2504				( x1 >= l && x1 <= r ) || // Left edge touching
2505				( x2 >= l && x2 <= r ) || // Right edge touching
2506				( x1 < l && x2 > r ) // Surrounded horizontally
2507			);
2508		default:
2509			return false;
2510		}
2511	};
2512})();
2513
2514/*
2515	This manager tracks offsets of draggables and droppables
2516*/
2517$.ui.ddmanager = {
2518	current: null,
2519	droppables: { "default": [] },
2520	prepareOffsets: function( t, event ) {
2521
2522		var i, j,
2523			m = $.ui.ddmanager.droppables[ t.options.scope ] || [],
2524			type = event ? event.type : null, // workaround for #2317
2525			list = ( t.currentItem || t.element ).find( ":data(ui-droppable)" ).addBack();
2526
2527		droppablesLoop: for ( i = 0; i < m.length; i++ ) {
2528
2529			// No disabled and non-accepted
2530			if ( m[ i ].options.disabled || ( t && !m[ i ].accept.call( m[ i ].element[ 0 ], ( t.currentItem || t.element ) ) ) ) {
2531				continue;
2532			}
2533
2534			// Filter out elements in the current dragged item
2535			for ( j = 0; j < list.length; j++ ) {
2536				if ( list[ j ] === m[ i ].element[ 0 ] ) {
2537					m[ i ].proportions().height = 0;
2538					continue droppablesLoop;
2539				}
2540			}
2541
2542			m[ i ].visible = m[ i ].element.css( "display" ) !== "none";
2543			if ( !m[ i ].visible ) {
2544				continue;
2545			}
2546
2547			// Activate the droppable if used directly from draggables
2548			if ( type === "mousedown" ) {
2549				m[ i ]._activate.call( m[ i ], event );
2550			}
2551
2552			m[ i ].offset = m[ i ].element.offset();
2553			m[ i ].proportions({ width: m[ i ].element[ 0 ].offsetWidth, height: m[ i ].element[ 0 ].offsetHeight });
2554
2555		}
2556
2557	},
2558	drop: function( draggable, event ) {
2559
2560		var dropped = false;
2561		// Create a copy of the droppables in case the list changes during the drop (#9116)
2562		$.each( ( $.ui.ddmanager.droppables[ draggable.options.scope ] || [] ).slice(), function() {
2563
2564			if ( !this.options ) {
2565				return;
2566			}
2567			if ( !this.options.disabled && this.visible && intersect( draggable, this, this.options.tolerance, event ) ) {
2568				dropped = this._drop.call( this, event ) || dropped;
2569			}
2570
2571			if ( !this.options.disabled && this.visible && this.accept.call( this.element[ 0 ], ( draggable.currentItem || draggable.element ) ) ) {
2572				this.isout = true;
2573				this.isover = false;
2574				this._deactivate.call( this, event );
2575			}
2576
2577		});
2578		return dropped;
2579
2580	},
2581	dragStart: function( draggable, event ) {
2582		// Listen for scrolling so that if the dragging causes scrolling the position of the droppables can be recalculated (see #5003)
2583		draggable.element.parentsUntil( "body" ).bind( "scroll.droppable", function() {
2584			if ( !draggable.options.refreshPositions ) {
2585				$.ui.ddmanager.prepareOffsets( draggable, event );
2586			}
2587		});
2588	},
2589	drag: function( draggable, event ) {
2590
2591		// If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
2592		if ( draggable.options.refreshPositions ) {
2593			$.ui.ddmanager.prepareOffsets( draggable, event );
2594		}
2595
2596		// Run through all droppables and check their positions based on specific tolerance options
2597		$.each( $.ui.ddmanager.droppables[ draggable.options.scope ] || [], function() {
2598
2599			if ( this.options.disabled || this.greedyChild || !this.visible ) {
2600				return;
2601			}
2602
2603			var parentInstance, scope, parent,
2604				intersects = intersect( draggable, this, this.options.tolerance, event ),
2605				c = !intersects && this.isover ? "isout" : ( intersects && !this.isover ? "isover" : null );
2606			if ( !c ) {
2607				return;
2608			}
2609
2610			if ( this.options.greedy ) {
2611				// find droppable parents with same scope
2612				scope = this.options.scope;
2613				parent = this.element.parents( ":data(ui-droppable)" ).filter(function() {
2614					return $( this ).droppable( "instance" ).options.scope === scope;
2615				});
2616
2617				if ( parent.length ) {
2618					parentInstance = $( parent[ 0 ] ).droppable( "instance" );
2619					parentInstance.greedyChild = ( c === "isover" );
2620				}
2621			}
2622
2623			// we just moved into a greedy child
2624			if ( parentInstance && c === "isover" ) {
2625				parentInstance.isover = false;
2626				parentInstance.isout = true;
2627				parentInstance._out.call( parentInstance, event );
2628			}
2629
2630			this[ c ] = true;
2631			this[c === "isout" ? "isover" : "isout"] = false;
2632			this[c === "isover" ? "_over" : "_out"].call( this, event );
2633
2634			// we just moved out of a greedy child
2635			if ( parentInstance && c === "isout" ) {
2636				parentInstance.isout = false;
2637				parentInstance.isover = true;
2638				parentInstance._over.call( parentInstance, event );
2639			}
2640		});
2641
2642	},
2643	dragStop: function( draggable, event ) {
2644		draggable.element.parentsUntil( "body" ).unbind( "scroll.droppable" );
2645		// Call prepareOffsets one final time since IE does not fire return scroll events when overflow was caused by drag (see #5003)
2646		if ( !draggable.options.refreshPositions ) {
2647			$.ui.ddmanager.prepareOffsets( draggable, event );
2648		}
2649	}
2650};
2651
2652// DEPRECATED
2653// TODO: switch return back to widget declaration at top of file when this is removed
2654if ( $.uiBackCompat !== false ) {
2655
2656	// Backcompat for activeClass and hoverClass options
2657	$.widget( "ui.droppable", $.ui.droppable, {
2658		options: {
2659			hoverClass: false,
2660			activeClass: false
2661		},
2662		_addActiveClass: function() {
2663			this._super();
2664			if ( this.options.activeClass ) {
2665				this.element.addClass( this.options.activeClass );
2666			}
2667		},
2668		_removeActiveClass: function() {
2669			this._super();
2670			if ( this.options.activeClass ) {
2671				this.element.removeClass( this.options.activeClass );
2672			}
2673		},
2674		_addHoverClass: function() {
2675			this._super();
2676			if ( this.options.hoverClass ) {
2677				this.element.addClass( this.options.hoverClass );
2678			}
2679		},
2680		_removeHoverClass: function() {
2681			this._super();
2682			if ( this.options.hoverClass ) {
2683				this.element.removeClass( this.options.hoverClass );
2684			}
2685		}
2686	});
2687}
2688
2689return $.ui.droppable;
2690
2691}));
2692
2693//>>label: Resizable
2694//>>group: Interactions
2695//>>description: Enables resize functionality for any element.
2696//>>docs: http://api.jqueryui.com/resizable/
2697//>>demos: http://jqueryui.com/resizable/
2698//>>css.structure: ../themes/base/core.css
2699//>>css.structure: ../themes/base/resizable.css
2700//>>css.theme: ../themes/base/theme.css
2701
2702(function( factory ) {
2703	if ( typeof define === "function" && define.amd ) {
2704
2705		// AMD. Register as an anonymous module.
2706		define([
2707			"jquery",
2708			"./core",
2709			"./mouse",
2710			"./widget"
2711		], factory );
2712	} else {
2713
2714		// Browser globals
2715		factory( jQuery );
2716	}
2717}(function( $ ) {
2718
2719$.widget("ui.resizable", $.ui.mouse, {
2720	version: "@VERSION",
2721	widgetEventPrefix: "resize",
2722	options: {
2723		alsoResize: false,
2724		animate: false,
2725		animateDuration: "slow",
2726		animateEasing: "swing",
2727		aspectRatio: false,
2728		autoHide: false,
2729		classes: {
2730			"ui-resizable-se": "ui-icon ui-icon-gripsmall-diagonal-se"
2731		},
2732		containment: false,
2733		ghost: false,
2734		grid: false,
2735		handles: "e,s,se",
2736		helper: false,
2737		maxHeight: null,
2738		maxWidth: null,
2739		minHeight: 10,
2740		minWidth: 10,
2741		// See #7960
2742		zIndex: 90,
2743
2744		// callbacks
2745		resize: null,
2746		start: null,
2747		stop: null
2748	},
2749
2750	_num: function( value ) {
2751		return parseInt( value, 10 ) || 0;
2752	},
2753
2754	_isNumber: function( value ) {
2755		return !isNaN( parseInt( value, 10 ) );
2756	},
2757
2758	_hasScroll: function( el, a ) {
2759
2760		if ( $( el ).css( "overflow" ) === "hidden") {
2761			return false;
vendor: 5,849 bytes, lines 2762-2993
2762		}
2763
2764		var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop",
2765			has = false;
2766
2767		if ( el[ scroll ] > 0 ) {
2768			return true;
2769		}
2770
2771		// TODO: determine which cases actually cause this to happen
2772		// if the element doesn't have the scroll set, see if it's possible to
2773		// set the scroll
2774		el[ scroll ] = 1;
2775		has = ( el[ scroll ] > 0 );
2776		el[ scroll ] = 0;
2777		return has;
2778	},
2779
2780	_create: function() {
2781
2782		var n, i, handle, axis, hname,
2783			that = this,
2784			o = this.options;
2785		this._addClass( "ui-resizable" );
2786
2787		$.extend(this, {
2788			_aspectRatio: !!(o.aspectRatio),
2789			aspectRatio: o.aspectRatio,
2790			originalElement: this.element,
2791			_proportionallyResizeElements: [],
2792			_helper: o.helper || o.ghost || o.animate ? o.helper || "ui-resizable-helper" : null
2793		});
2794
2795		// Wrap the element if it cannot hold child nodes
2796		if (this.element[0].nodeName.match(/^(canvas|textarea|input|select|button|img)$/i)) {
2797
2798			this.element.wrap(
2799				$("<div class='ui-wrapper' style='overflow: hidden;'></div>").css({
2800					position: this.element.css("position"),
2801					width: this.element.outerWidth(),
2802					height: this.element.outerHeight(),
2803					top: this.element.css("top"),
2804					left: this.element.css("left")
2805				})
2806			);
2807
2808			this.element = this.element.parent().data(
2809				"ui-resizable", this.element.resizable( "instance" )
2810			);
2811
2812			this.elementIsWrapper = true;
2813
2814			this.element.css({
2815				marginLeft: this.originalElement.css("marginLeft"),
2816				marginTop: this.originalElement.css("marginTop"),
2817				marginRight: this.originalElement.css("marginRight"),
2818				marginBottom: this.originalElement.css("marginBottom")
2819			});
2820			this.originalElement.css({
2821				marginLeft: 0,
2822				marginTop: 0,
2823				marginRight: 0,
2824				marginBottom: 0
2825			});
2826			// support: Safari
2827			// Prevent Safari textarea resize
2828			this.originalResizeStyle = this.originalElement.css("resize");
2829			this.originalElement.css("resize", "none");
2830
2831			this._proportionallyResizeElements.push( this.originalElement.css({
2832				position: "static",
2833				zoom: 1,
2834				display: "block"
2835			}) );
2836
2837			// support: IE9
2838			// avoid IE jump (hard set the margin)
2839			this.originalElement.css({ margin: this.originalElement.css("margin") });
2840
2841			this._proportionallyResize();
2842		}
2843
2844		this.handles = o.handles ||
2845			( !$(".ui-resizable-handle", this.element).length ?
2846				"e,s,se" : {
2847					n: ".ui-resizable-n",
2848					e: ".ui-resizable-e",
2849					s: ".ui-resizable-s",
2850					w: ".ui-resizable-w",
2851					se: ".ui-resizable-se",
2852					sw: ".ui-resizable-sw",
2853					ne: ".ui-resizable-ne",
2854					nw: ".ui-resizable-nw"
2855				} );
2856
2857		this._handles = $();
2858		if ( this.handles.constructor === String ) {
2859
2860			if ( this.handles === "all") {
2861				this.handles = "n,e,s,w,se,sw,ne,nw";
2862			}
2863
2864			n = this.handles.split(",");
2865			this.handles = {};
2866
2867			for (i = 0; i < n.length; i++) {
2868
2869				handle = $.trim(n[i]);
2870				hname = "ui-resizable-" + handle;
2871				axis = $("<div>");
2872				this._addClass( axis, "ui-resizable-handle " + hname );
2873
2874				axis.css({ zIndex: o.zIndex });
2875
2876				this.handles[handle] = ".ui-resizable-" + handle;
2877				this.element.append(axis);
2878			}
2879
2880		}
2881
2882		this._renderAxis = function(target) {
2883
2884			var i, axis, padPos, padWrapper;
2885
2886			target = target || this.element;
2887
2888			for (i in this.handles) {
2889
2890				if (this.handles[i].constructor === String) {
2891					this.handles[i] = this.element.children( this.handles[ i ] ).first().show();
2892				} else if ( this.handles[ i ].jquery || this.handles[ i ].nodeType ) {
2893					this.handles[ i ] = $( this.handles[ i ] );
2894					this._on( this.handles[ i ], { "mousedown": that._mouseDown });
2895				}
2896
2897				if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/^(textarea|input|select|button)$/i)) {
2898
2899					axis = $(this.handles[i], this.element);
2900
2901					padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth();
2902
2903					padPos = [ "padding",
2904						/ne|nw|n/.test(i) ? "Top" :
2905						/se|sw|s/.test(i) ? "Bottom" :
2906						/^e$/.test(i) ? "Right" : "Left" ].join("");
2907
2908					target.css(padPos, padWrapper);
2909
2910					this._proportionallyResize();
2911				}
2912
2913				this._handles = this._handles.add( this.handles[ i ] );
2914			}
2915		};
2916
2917		// TODO: make renderAxis a prototype function
2918		this._renderAxis(this.element);
2919
2920		this._handles = this._handles.add( this.element.find( ".ui-resizable-handle" ) );
2921		this._handles.disableSelection();
2922
2923		this._handles.mouseover(function() {
2924			if (!that.resizing) {
2925				if (this.className) {
2926					axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);
2927				}
2928				that.axis = axis && axis[1] ? axis[1] : "se";
2929			}
2930		});
2931
2932		if (o.autoHide) {
2933			this._handles.hide();
2934			this._addClass( "ui-resizable-autohide" );
2935			$(this.element)
2936				.mouseenter(function() {
2937					if (o.disabled) {
2938						return;
2939					}
2940					that._removeClass( "ui-resizable-autohide" );
2941					that._handles.show();
2942				})
2943				.mouseleave(function() {
2944					if (o.disabled) {
2945						return;
2946					}
2947					if (!that.resizing) {
2948						that._addClass( "ui-resizable-autohide" );
2949						that._handles.hide();
2950					}
2951				});
2952		}
2953
2954		this._mouseInit();
2955	},
2956
2957	_destroy: function() {
2958
2959		this._mouseDestroy();
2960
2961		var wrapper,
2962			_destroy = function(exp) {
2963				$(exp)
2964					.removeData("resizable")
2965					.removeData("ui-resizable")
2966					.unbind(".resizable")
2967					.find(".ui-resizable-handle")
2968						.remove();
2969			};
2970
2971		// TODO: Unwrap at same DOM position
2972		if (this.elementIsWrapper) {
2973			_destroy(this.element);
2974			wrapper = this.element;
2975			this.originalElement.css({
2976				position: wrapper.css("position"),
2977				width: wrapper.outerWidth(),
2978				height: wrapper.outerHeight(),
2979				top: wrapper.css("top"),
2980				left: wrapper.css("left")
2981			}).insertAfter( wrapper );
2982			wrapper.remove();
2983		}
2984
2985		this.originalElement.css("resize", this.originalResizeStyle);
2986		_destroy(this.originalElement);
2987
2988		return this;
2989	},
2990
2991	_mouseCapture: function(event) {
2992		var i, handle,
2993			capture = false;
vendor: 13,417 bytes, lines 2994-3533
2994
2995		for (i in this.handles) {
2996			handle = $(this.handles[i])[0];
2997			if (handle === event.target || $.contains(handle, event.target)) {
2998				capture = true;
2999			}
3000		}
3001
3002		return !this.options.disabled && capture;
3003	},
3004
3005	_mouseStart: function(event) {
3006
3007		var curleft, curtop, cursor,
3008			o = this.options,
3009			el = this.element;
3010
3011		this.resizing = true;
3012
3013		this._renderProxy();
3014
3015		curleft = this._num(this.helper.css("left"));
3016		curtop = this._num(this.helper.css("top"));
3017
3018		if (o.containment) {
3019			curleft += $(o.containment).scrollLeft() || 0;
3020			curtop += $(o.containment).scrollTop() || 0;
3021		}
3022
3023		this.offset = this.helper.offset();
3024		this.position = { left: curleft, top: curtop };
3025
3026		this.size = this._helper ? {
3027				width: this.helper.width(),
3028				height: this.helper.height()
3029			} : {
3030				width: el.width(),
3031				height: el.height()
3032			};
3033
3034		this.originalSize = this._helper ? {
3035				width: el.outerWidth(),
3036				height: el.outerHeight()
3037			} : {
3038				width: el.width(),
3039				height: el.height()
3040			};
3041
3042		this.sizeDiff = {
3043			width: el.outerWidth() - el.width(),
3044			height: el.outerHeight() - el.height()
3045		};
3046
3047		this.originalPosition = { left: curleft, top: curtop };
3048		this.originalMousePosition = { left: event.pageX, top: event.pageY };
3049
3050		this.aspectRatio = (typeof o.aspectRatio === "number") ?
3051			o.aspectRatio :
3052			((this.originalSize.width / this.originalSize.height) || 1);
3053
3054		cursor = $(".ui-resizable-" + this.axis).css("cursor");
3055		$("body").css("cursor", cursor === "auto" ? this.axis + "-resize" : cursor);
3056
3057		this._addClass( "ui-resizable-resizing" );
3058		this._propagate("start", event);
3059		return true;
3060	},
3061
3062	_mouseDrag: function(event) {
3063
3064		var data, props,
3065			smp = this.originalMousePosition,
3066			a = this.axis,
3067			dx = (event.pageX - smp.left) || 0,
3068			dy = (event.pageY - smp.top) || 0,
3069			trigger = this._change[a];
3070
3071		this._updatePrevProperties();
3072
3073		if (!trigger) {
3074			return false;
3075		}
3076
3077		data = trigger.apply(this, [ event, dx, dy ]);
3078
3079		this._updateVirtualBoundaries(event.shiftKey);
3080		if (this._aspectRatio || event.shiftKey) {
3081			data = this._updateRatio(data, event);
3082		}
3083
3084		data = this._respectSize(data, event);
3085
3086		this._updateCache(data);
3087
3088		this._propagate("resize", event);
3089
3090		props = this._applyChanges();
3091
3092		if ( !this._helper && this._proportionallyResizeElements.length ) {
3093			this._proportionallyResize();
3094		}
3095
3096		if ( !$.isEmptyObject( props ) ) {
3097			this._updatePrevProperties();
3098			this._trigger( "resize", event, this.ui() );
3099			this._applyChanges();
3100		}
3101
3102		return false;
3103	},
3104
3105	_mouseStop: function(event) {
3106
3107		this.resizing = false;
3108		var pr, ista, soffseth, soffsetw, s, left, top,
3109			o = this.options, that = this;
3110
3111		if (this._helper) {
3112
3113			pr = this._proportionallyResizeElements;
3114			ista = pr.length && (/textarea/i).test(pr[0].nodeName);
3115			soffseth = ista && this._hasScroll(pr[0], "left") ? 0 : that.sizeDiff.height;
3116			soffsetw = ista ? 0 : that.sizeDiff.width;
3117
3118			s = {
3119				width: (that.helper.width()  - soffsetw),
3120				height: (that.helper.height() - soffseth)
3121			};
3122			left = (parseInt(that.element.css("left"), 10) +
3123				(that.position.left - that.originalPosition.left)) || null;
3124			top = (parseInt(that.element.css("top"), 10) +
3125				(that.position.top - that.originalPosition.top)) || null;
3126
3127			if (!o.animate) {
3128				this.element.css($.extend(s, { top: top, left: left }));
3129			}
3130
3131			that.helper.height(that.size.height);
3132			that.helper.width(that.size.width);
3133
3134			if (this._helper && !o.animate) {
3135				this._proportionallyResize();
3136			}
3137		}
3138
3139		$("body").css("cursor", "auto");
3140
3141		this._removeClass( "ui-resizable-resizing" );
3142
3143		this._propagate("stop", event);
3144
3145		if (this._helper) {
3146			this.helper.remove();
3147		}
3148
3149		return false;
3150
3151	},
3152
3153	_updatePrevProperties: function() {
3154		this.prevPosition = {
3155			top: this.position.top,
3156			left: this.position.left
3157		};
3158		this.prevSize = {
3159			width: this.size.width,
3160			height: this.size.height
3161		};
3162	},
3163
3164	_applyChanges: function() {
3165		var props = {};
3166
3167		if ( this.position.top !== this.prevPosition.top ) {
3168			props.top = this.position.top + "px";
3169		}
3170		if ( this.position.left !== this.prevPosition.left ) {
3171			props.left = this.position.left + "px";
3172		}
3173		if ( this.size.width !== this.prevSize.width ) {
3174			props.width = this.size.width + "px";
3175		}
3176		if ( this.size.height !== this.prevSize.height ) {
3177			props.height = this.size.height + "px";
3178		}
3179
3180		this.helper.css( props );
3181
3182		return props;
3183	},
3184
3185	_updateVirtualBoundaries: function(forceAspectRatio) {
3186		var pMinWidth, pMaxWidth, pMinHeight, pMaxHeight, b,
3187			o = this.options;
3188
3189		b = {
3190			minWidth: this._isNumber(o.minWidth) ? o.minWidth : 0,
3191			maxWidth: this._isNumber(o.maxWidth) ? o.maxWidth : Infinity,
3192			minHeight: this._isNumber(o.minHeight) ? o.minHeight : 0,
3193			maxHeight: this._isNumber(o.maxHeight) ? o.maxHeight : Infinity
3194		};
3195
3196		if (this._aspectRatio || forceAspectRatio) {
3197			pMinWidth = b.minHeight * this.aspectRatio;
3198			pMinHeight = b.minWidth / this.aspectRatio;
3199			pMaxWidth = b.maxHeight * this.aspectRatio;
3200			pMaxHeight = b.maxWidth / this.aspectRatio;
3201
3202			if (pMinWidth > b.minWidth) {
3203				b.minWidth = pMinWidth;
3204			}
3205			if (pMinHeight > b.minHeight) {
3206				b.minHeight = pMinHeight;
3207			}
3208			if (pMaxWidth < b.maxWidth) {
3209				b.maxWidth = pMaxWidth;
3210			}
3211			if (pMaxHeight < b.maxHeight) {
3212				b.maxHeight = pMaxHeight;
3213			}
3214		}
3215		this._vBoundaries = b;
3216	},
3217
3218	_updateCache: function(data) {
3219		this.offset = this.helper.offset();
3220		if (this._isNumber(data.left)) {
3221			this.position.left = data.left;
3222		}
3223		if (this._isNumber(data.top)) {
3224			this.position.top = data.top;
3225		}
3226		if (this._isNumber(data.height)) {
3227			this.size.height = data.height;
3228		}
3229		if (this._isNumber(data.width)) {
3230			this.size.width = data.width;
3231		}
3232	},
3233
3234	_updateRatio: function( data ) {
3235
3236		var cpos = this.position,
3237			csize = this.size,
3238			a = this.axis;
3239
3240		if (this._isNumber(data.height)) {
3241			data.width = (data.height * this.aspectRatio);
3242		} else if (this._isNumber(data.width)) {
3243			data.height = (data.width / this.aspectRatio);
3244		}
3245
3246		if (a === "sw") {
3247			data.left = cpos.left + (csize.width - data.width);
3248			data.top = null;
3249		}
3250		if (a === "nw") {
3251			data.top = cpos.top + (csize.height - data.height);
3252			data.left = cpos.left + (csize.width - data.width);
3253		}
3254
3255		return data;
3256	},
3257
3258	_respectSize: function( data ) {
3259
3260		var o = this._vBoundaries,
3261			a = this.axis,
3262			ismaxw = this._isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width),
3263			ismaxh = this._isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height),
3264			isminw = this._isNumber(data.width) && o.minWidth && (o.minWidth > data.width),
3265			isminh = this._isNumber(data.height) && o.minHeight && (o.minHeight > data.height),
3266			dw = this.originalPosition.left + this.originalSize.width,
3267			dh = this.position.top + this.size.height,
3268			cw = /sw|nw|w/.test(a), ch = /nw|ne|n/.test(a);
3269		if (isminw) {
3270			data.width = o.minWidth;
3271		}
3272		if (isminh) {
3273			data.height = o.minHeight;
3274		}
3275		if (ismaxw) {
3276			data.width = o.maxWidth;
3277		}
3278		if (ismaxh) {
3279			data.height = o.maxHeight;
3280		}
3281
3282		if (isminw && cw) {
3283			data.left = dw - o.minWidth;
3284		}
3285		if (ismaxw && cw) {
3286			data.left = dw - o.maxWidth;
3287		}
3288		if (isminh && ch) {
3289			data.top = dh - o.minHeight;
3290		}
3291		if (ismaxh && ch) {
3292			data.top = dh - o.maxHeight;
3293		}
3294
3295		// Fixing jump error on top/left - bug #2330
3296		if (!data.width && !data.height && !data.left && data.top) {
3297			data.top = null;
3298		} else if (!data.width && !data.height && !data.top && data.left) {
3299			data.left = null;
3300		}
3301
3302		return data;
3303	},
3304
3305	_getPaddingPlusBorderDimensions: function( element ) {
3306		var i = 0,
3307			widths = [],
3308			borders = [
3309				element.css( "borderTopWidth" ),
3310				element.css( "borderRightWidth" ),
3311				element.css( "borderBottomWidth" ),
3312				element.css( "borderLeftWidth" )
3313			],
3314			paddings = [
3315				element.css( "paddingTop" ),
3316				element.css( "paddingRight" ),
3317				element.css( "paddingBottom" ),
3318				element.css( "paddingLeft" )
3319			];
3320
3321		for ( ; i < 4; i++ ) {
3322			widths[ i ] = ( parseInt( borders[ i ], 10 ) || 0 );
3323			widths[ i ] += ( parseInt( paddings[ i ], 10 ) || 0 );
3324		}
3325
3326		return {
3327			height: widths[ 0 ] + widths[ 2 ],
3328			width: widths[ 1 ] + widths[ 3 ]
3329		};
3330	},
3331
3332	_proportionallyResize: function() {
3333
3334		if (!this._proportionallyResizeElements.length) {
3335			return;
3336		}
3337
3338		var prel,
3339			i = 0,
3340			element = this.helper || this.element;
3341
3342		for ( ; i < this._proportionallyResizeElements.length; i++) {
3343
3344			prel = this._proportionallyResizeElements[i];
3345
3346			// TODO: Seems like a bug to cache this.outerDimensions
3347			// considering that we are in a loop.
3348			if (!this.outerDimensions) {
3349				this.outerDimensions = this._getPaddingPlusBorderDimensions( prel );
3350			}
3351
3352			prel.css({
3353				height: (element.height() - this.outerDimensions.height) || 0,
3354				width: (element.width() - this.outerDimensions.width) || 0
3355			});
3356
3357		}
3358
3359	},
3360
3361	_renderProxy: function() {
3362
3363		var el = this.element, o = this.options;
3364		this.elementOffset = el.offset();
3365
3366		if (this._helper) {
3367
3368			this.helper = this.helper || $("<div style='overflow:hidden;'></div>");
3369
3370			this._addClass( this.helper, this._helper );
3371			this.helper.css({
3372				width: this.element.outerWidth() - 1,
3373				height: this.element.outerHeight() - 1,
3374				position: "absolute",
3375				left: this.elementOffset.left + "px",
3376				top: this.elementOffset.top + "px",
3377				zIndex: ++o.zIndex //TODO: Don't modify option
3378			});
3379
3380			this.helper
3381				.appendTo("body")
3382				.disableSelection();
3383
3384		} else {
3385			this.helper = this.element;
3386		}
3387
3388	},
3389
3390	_change: {
3391		e: function(event, dx) {
3392			return { width: this.originalSize.width + dx };
3393		},
3394		w: function(event, dx) {
3395			var cs = this.originalSize, sp = this.originalPosition;
3396			return { left: sp.left + dx, width: cs.width - dx };
3397		},
3398		n: function(event, dx, dy) {
3399			var cs = this.originalSize, sp = this.originalPosition;
3400			return { top: sp.top + dy, height: cs.height - dy };
3401		},
3402		s: function(event, dx, dy) {
3403			return { height: this.originalSize.height + dy };
3404		},
3405		se: function(event, dx, dy) {
3406			return $.extend(this._change.s.apply(this, arguments),
3407				this._change.e.apply(this, [ event, dx, dy ]));
3408		},
3409		sw: function(event, dx, dy) {
3410			return $.extend(this._change.s.apply(this, arguments),
3411				this._change.w.apply(this, [ event, dx, dy ]));
3412		},
3413		ne: function(event, dx, dy) {
3414			return $.extend(this._change.n.apply(this, arguments),
3415				this._change.e.apply(this, [ event, dx, dy ]));
3416		},
3417		nw: function(event, dx, dy) {
3418			return $.extend(this._change.n.apply(this, arguments),
3419				this._change.w.apply(this, [ event, dx, dy ]));
3420		}
3421	},
3422
3423	_propagate: function(n, event) {
3424		$.ui.plugin.call(this, n, [ event, this.ui() ]);
3425		(n !== "resize" && this._trigger(n, event, this.ui()));
3426	},
3427
3428	plugins: {},
3429
3430	ui: function() {
3431		return {
3432			originalElement: this.originalElement,
3433			element: this.element,
3434			helper: this.helper,
3435			position: this.position,
3436			size: this.size,
3437			originalSize: this.originalSize,
3438			originalPosition: this.originalPosition
3439		};
3440	}
3441
3442});
3443
3444/*
3445 * Resizable Extensions
3446 */
3447
3448$.ui.plugin.add("resizable", "animate", {
3449
3450	stop: function( event ) {
3451		var that = $(this).resizable( "instance" ),
3452			o = that.options,
3453			pr = that._proportionallyResizeElements,
3454			ista = pr.length && (/textarea/i).test(pr[0].nodeName),
3455			soffseth = ista && that._hasScroll(pr[0], "left") ? 0 : that.sizeDiff.height,
3456			soffsetw = ista ? 0 : that.sizeDiff.width,
3457			style = { width: (that.size.width - soffsetw), height: (that.size.height - soffseth) },
3458			left = (parseInt(that.element.css("left"), 10) +
3459				(that.position.left - that.originalPosition.left)) || null,
3460			top = (parseInt(that.element.css("top"), 10) +
3461				(that.position.top - that.originalPosition.top)) || null;
3462
3463		that.element.animate(
3464			$.extend(style, top && left ? { top: top, left: left } : {}), {
3465				duration: o.animateDuration,
3466				easing: o.animateEasing,
3467				step: function() {
3468
3469					var data = {
3470						width: parseInt(that.element.css("width"), 10),
3471						height: parseInt(that.element.css("height"), 10),
3472						top: parseInt(that.element.css("top"), 10),
3473						left: parseInt(that.element.css("left"), 10)
3474					};
3475
3476					if (pr && pr.length) {
3477						$(pr[0]).css({ width: data.width, height: data.height });
3478					}
3479
3480					// propagating resize, and updating values for each animation step
3481					that._updateCache(data);
3482					that._propagate("resize", event);
3483
3484				}
3485			}
3486		);
3487	}
3488
3489});
3490
3491$.ui.plugin.add( "resizable", "containment", {
3492
3493	start: function() {
3494		var element, p, co, ch, cw, width, height,
3495			that = $( this ).resizable( "instance" ),
3496			o = that.options,
3497			el = that.element,
3498			oc = o.containment,
3499			ce = ( oc instanceof $ ) ? oc.get( 0 ) : ( /parent/.test( oc ) ) ? el.parent().get( 0 ) : oc;
3500
3501		if ( !ce ) {
3502			return;
3503		}
3504
3505		that.containerElement = $( ce );
3506
3507		if ( /document/.test( oc ) || oc === document ) {
3508			that.containerOffset = {
3509				left: 0,
3510				top: 0
3511			};
3512			that.containerPosition = {
3513				left: 0,
3514				top: 0
3515			};
3516
3517			that.parentData = {
3518				element: $( document ),
3519				left: 0,
3520				top: 0,
3521				width: $( document ).width(),
3522				height: $( document ).height() || document.body.parentNode.scrollHeight
3523			};
3524		} else {
3525			element = $( ce );
3526			p = [];
3527			$([ "Top", "Right", "Left", "Bottom" ]).each(function( i, name ) {
3528				p[ i ] = that._num( element.css( "padding" + name ) );
3529			});
3530
3531			that.containerOffset = element.offset();
3532			that.containerPosition = element.position();
3533			that.containerSize = {
vendor: 5,782 bytes, lines 3534-3758
3534				height: ( element.innerHeight() - p[ 3 ] ),
3535				width: ( element.innerWidth() - p[ 1 ] )
3536			};
3537
3538			co = that.containerOffset;
3539			ch = that.containerSize.height;
3540			cw = that.containerSize.width;
3541			width = ( that._hasScroll ( ce, "left" ) ? ce.scrollWidth : cw );
3542			height = ( that._hasScroll ( ce ) ? ce.scrollHeight : ch ) ;
3543
3544			that.parentData = {
3545				element: ce,
3546				left: co.left,
3547				top: co.top,
3548				width: width,
3549				height: height
3550			};
3551		}
3552	},
3553
3554	resize: function( event ) {
3555		var woset, hoset, isParent, isOffsetRelative,
3556			that = $( this ).resizable( "instance" ),
3557			o = that.options,
3558			co = that.containerOffset,
3559			cp = that.position,
3560			pRatio = that._aspectRatio || event.shiftKey,
3561			cop = {
3562				top: 0,
3563				left: 0
3564			},
3565			ce = that.containerElement,
3566			continueResize = true;
3567
3568		if ( ce[ 0 ] !== document && ( /static/ ).test( ce.css( "position" ) ) ) {
3569			cop = co;
3570		}
3571
3572		if ( cp.left < ( that._helper ? co.left : 0 ) ) {
3573			that.size.width = that.size.width +
3574				( that._helper ?
3575					( that.position.left - co.left ) :
3576					( that.position.left - cop.left ) );
3577
3578			if ( pRatio ) {
3579				that.size.height = that.size.width / that.aspectRatio;
3580				continueResize = false;
3581			}
3582			that.position.left = o.helper ? co.left : 0;
3583		}
3584
3585		if ( cp.top < ( that._helper ? co.top : 0 ) ) {
3586			that.size.height = that.size.height +
3587				( that._helper ?
3588					( that.position.top - co.top ) :
3589					that.position.top );
3590
3591			if ( pRatio ) {
3592				that.size.width = that.size.height * that.aspectRatio;
3593				continueResize = false;
3594			}
3595			that.position.top = that._helper ? co.top : 0;
3596		}
3597
3598		isParent = that.containerElement.get( 0 ) === that.element.parent().get( 0 );
3599		isOffsetRelative = /relative|absolute/.test( that.containerElement.css( "position" ) );
3600
3601		if ( isParent && isOffsetRelative ) {
3602			that.offset.left = that.parentData.left + that.position.left;
3603			that.offset.top = that.parentData.top + that.position.top;
3604		} else {
3605			that.offset.left = that.element.offset().left;
3606			that.offset.top = that.element.offset().top;
3607		}
3608
3609		woset = Math.abs( that.sizeDiff.width +
3610			(that._helper ?
3611				that.offset.left - cop.left :
3612				(that.offset.left - co.left)) );
3613
3614		hoset = Math.abs( that.sizeDiff.height +
3615			(that._helper ?
3616				that.offset.top - cop.top :
3617				(that.offset.top - co.top)) );
3618
3619		if ( woset + that.size.width >= that.parentData.width ) {
3620			that.size.width = that.parentData.width - woset;
3621			if ( pRatio ) {
3622				that.size.height = that.size.width / that.aspectRatio;
3623				continueResize = false;
3624			}
3625		}
3626
3627		if ( hoset + that.size.height >= that.parentData.height ) {
3628			that.size.height = that.parentData.height - hoset;
3629			if ( pRatio ) {
3630				that.size.width = that.size.height * that.aspectRatio;
3631				continueResize = false;
3632			}
3633		}
3634
3635		if ( !continueResize ) {
3636			that.position.left = that.prevPosition.left;
3637			that.position.top = that.prevPosition.top;
3638			that.size.width = that.prevSize.width;
3639			that.size.height = that.prevSize.height;
3640		}
3641	},
3642
3643	stop: function() {
3644		var that = $( this ).resizable( "instance" ),
3645			o = that.options,
3646			co = that.containerOffset,
3647			cop = that.containerPosition,
3648			ce = that.containerElement,
3649			helper = $( that.helper ),
3650			ho = helper.offset(),
3651			w = helper.outerWidth() - that.sizeDiff.width,
3652			h = helper.outerHeight() - that.sizeDiff.height;
3653
3654		if ( that._helper && !o.animate && ( /relative/ ).test( ce.css( "position" ) ) ) {
3655			$( this ).css({
3656				left: ho.left - cop.left - co.left,
3657				width: w,
3658				height: h
3659			});
3660		}
3661
3662		if ( that._helper && !o.animate && ( /static/ ).test( ce.css( "position" ) ) ) {
3663			$( this ).css({
3664				left: ho.left - cop.left - co.left,
3665				width: w,
3666				height: h
3667			});
3668		}
3669	}
3670});
3671
3672$.ui.plugin.add("resizable", "alsoResize", {
3673
3674	start: function() {
3675		var that = $(this).resizable( "instance" ),
3676			o = that.options;
3677
3678		$(o.alsoResize).each(function() {
3679			var el = $(this);
3680			el.data("ui-resizable-alsoresize", {
3681				width: parseInt(el.width(), 10), height: parseInt(el.height(), 10),
3682				left: parseInt(el.css("left"), 10), top: parseInt(el.css("top"), 10)
3683			});
3684		});
3685	},
3686
3687	resize: function(event, ui) {
3688		var that = $(this).resizable( "instance" ),
3689			o = that.options,
3690			os = that.originalSize,
3691			op = that.originalPosition,
3692			delta = {
3693				height: (that.size.height - os.height) || 0,
3694				width: (that.size.width - os.width) || 0,
3695				top: (that.position.top - op.top) || 0,
3696				left: (that.position.left - op.left) || 0
3697			};
3698
3699			$(o.alsoResize).each(function() {
3700				var el = $(this), start = $(this).data("ui-resizable-alsoresize"), style = {},
3701					css = el.parents(ui.originalElement[0]).length ?
3702							[ "width", "height" ] :
3703							[ "width", "height", "top", "left" ];
3704
3705				$.each(css, function(i, prop) {
3706					var sum = (start[prop] || 0) + (delta[prop] || 0);
3707					if (sum && sum >= 0) {
3708						style[prop] = sum || null;
3709					}
3710				});
3711
3712				el.css(style);
3713			});
3714	},
3715
3716	stop: function() {
3717		$(this).removeData("resizable-alsoresize");
3718	}
3719});
3720
3721$.ui.plugin.add("resizable", "ghost", {
3722
3723	start: function() {
3724
3725		var that = $(this).resizable( "instance" ), cs = that.size;
3726
3727		that.ghost = that.originalElement.clone();
3728		that.ghost.css({
3729			opacity: 0.25,
3730			display: "block",
3731			position: "relative",
3732			height: cs.height,
3733			width: cs.width,
3734			margin: 0,
3735			left: 0,
3736			top: 0
3737		});
3738
3739		that._addClass( that.ghost, "ui-resizable-ghost" );
3740
3741		// DEPRECATED
3742		// TODO: remove after 1.12
3743		if ( $.uiBackCompat !== false && typeof that.options.ghost === "string" ) {
3744
3745			// Ghost option
3746			that.ghost.addClass( this.options.ghost );
3747		}
3748
3749		that.ghost.appendTo(that.helper);
3750
3751	},
3752
3753	resize: function() {
3754		var that = $(this).resizable( "instance" );
3755		if (that.ghost) {
3756			that.ghost.css({
3757				position: "relative",
3758				height: that.size.height,
vendor: 4,871 bytes, lines 3759-3965
3759				width: that.size.width
3760			});
3761		}
3762	},
3763
3764	stop: function() {
3765		var that = $(this).resizable( "instance" );
3766		if (that.ghost && that.helper) {
3767			that.helper.get(0).removeChild(that.ghost.get(0));
3768		}
3769	}
3770
3771});
3772
3773$.ui.plugin.add("resizable", "grid", {
3774
3775	resize: function() {
3776		var outerDimensions,
3777			that = $(this).resizable( "instance" ),
3778			o = that.options,
3779			cs = that.size,
3780			os = that.originalSize,
3781			op = that.originalPosition,
3782			a = that.axis,
3783			grid = typeof o.grid === "number" ? [ o.grid, o.grid ] : o.grid,
3784			gridX = (grid[0] || 1),
3785			gridY = (grid[1] || 1),
3786			ox = Math.round((cs.width - os.width) / gridX) * gridX,
3787			oy = Math.round((cs.height - os.height) / gridY) * gridY,
3788			newWidth = os.width + ox,
3789			newHeight = os.height + oy,
3790			isMaxWidth = o.maxWidth && (o.maxWidth < newWidth),
3791			isMaxHeight = o.maxHeight && (o.maxHeight < newHeight),
3792			isMinWidth = o.minWidth && (o.minWidth > newWidth),
3793			isMinHeight = o.minHeight && (o.minHeight > newHeight);
3794
3795		o.grid = grid;
3796
3797		if (isMinWidth) {
3798			newWidth += gridX;
3799		}
3800		if (isMinHeight) {
3801			newHeight += gridY;
3802		}
3803		if (isMaxWidth) {
3804			newWidth -= gridX;
3805		}
3806		if (isMaxHeight) {
3807			newHeight -= gridY;
3808		}
3809
3810		if (/^(se|s|e)$/.test(a)) {
3811			that.size.width = newWidth;
3812			that.size.height = newHeight;
3813		} else if (/^(ne)$/.test(a)) {
3814			that.size.width = newWidth;
3815			that.size.height = newHeight;
3816			that.position.top = op.top - oy;
3817		} else if (/^(sw)$/.test(a)) {
3818			that.size.width = newWidth;
3819			that.size.height = newHeight;
3820			that.position.left = op.left - ox;
3821		} else {
3822			if ( newHeight - gridY <= 0 || newWidth - gridX <= 0) {
3823				outerDimensions = that._getPaddingPlusBorderDimensions( this );
3824			}
3825
3826			if ( newHeight - gridY > 0 ) {
3827				that.size.height = newHeight;
3828				that.position.top = op.top - oy;
3829			} else {
3830				newHeight = gridY - outerDimensions.height;
3831				that.size.height = newHeight;
3832				that.position.top = op.top + os.height - newHeight;
3833			}
3834			if ( newWidth - gridX > 0 ) {
3835				that.size.width = newWidth;
3836				that.position.left = op.left - ox;
3837			} else {
3838				newWidth = gridX - outerDimensions.width;
3839				that.size.width = newWidth;
3840				that.position.left = op.left + os.width - newWidth;
3841			}
3842		}
3843	}
3844
3845});
3846
3847return $.ui.resizable;
3848
3849}));
3850
3851//>>label: Selectable
3852//>>group: Interactions
3853//>>description: Allows groups of elements to be selected with the mouse.
3854//>>docs: http://api.jqueryui.com/selectable/
3855//>>demos: http://jqueryui.com/selectable/
3856//>>css.structure: ../themes/base/selectable.css
3857
3858(function( factory ) {
3859	if ( typeof define === "function" && define.amd ) {
3860
3861		// AMD. Register as an anonymous module.
3862		define([
3863			"jquery",
3864			"./core",
3865			"./mouse",
3866			"./widget"
3867		], factory );
3868	} else {
3869
3870		// Browser globals
3871		factory( jQuery );
3872	}
3873}(function( $ ) {
3874
3875return $.widget("ui.selectable", $.ui.mouse, {
3876	version: "@VERSION",
3877	options: {
3878		appendTo: "body",
3879		autoRefresh: true,
3880		distance: 0,
3881		filter: "*",
3882		tolerance: "touch",
3883
3884		// callbacks
3885		selected: null,
3886		selecting: null,
3887		start: null,
3888		stop: null,
3889		unselected: null,
3890		unselecting: null
3891	},
3892	_create: function() {
3893		var that = this;
3894
3895		this._addClass( "ui-selectable" );
3896
3897		this.dragged = false;
3898
3899		// cache selectee children based on filter
3900		this.refresh = function() {
3901			that.selectees = $(that.options.filter, that.element[0]);
3902			that._addClass( that.selectees, "ui-selectee" );
3903			that.selectees.each(function() {
3904				var $this = $(this),
3905					pos = $this.offset();
3906				$.data(this, "selectable-item", {
3907					element: this,
3908					$element: $this,
3909					left: pos.left,
3910					top: pos.top,
3911					right: pos.left + $this.outerWidth(),
3912					bottom: pos.top + $this.outerHeight(),
3913					startselected: false,
3914					selected: $this.hasClass("ui-selected"),
3915					selecting: $this.hasClass("ui-selecting"),
3916					unselecting: $this.hasClass("ui-unselecting")
3917				});
3918			});
3919		};
3920		this.refresh();
3921
3922		this._mouseInit();
3923
3924		this.helper = $("<div>");
3925		this._addClass( this.helper, "ui-selectable-helper" );
3926	},
3927
3928	_destroy: function() {
3929		this.selectees.removeData("selectable-item");
3930		this._mouseDestroy();
3931	},
3932
3933	_mouseStart: function(event) {
3934		var that = this,
3935			options = this.options;
3936
3937		this.opos = [ event.pageX, event.pageY ];
3938
3939		if (this.options.disabled) {
3940			return;
3941		}
3942
3943		this.selectees = $(options.filter, this.element[0]);
3944
3945		this._trigger("start", event);
3946
3947		$(options.appendTo).append(this.helper);
3948		// position helper (lasso)
3949		this.helper.css({
3950			"left": event.pageX,
3951			"top": event.pageY,
3952			"width": 0,
3953			"height": 0
3954		});
3955
3956		if (options.autoRefresh) {
3957			this.refresh();
3958		}
3959
3960		this.selectees.filter(".ui-selected").each(function() {
3961			var selectee = $.data(this, "selectable-item");
3962			selectee.startselected = true;
3963			if (!event.metaKey && !event.ctrlKey) {
3964				that._removeClass( selectee.$element, "ui-selected" );
3965				selectee.selected = false;
vendor: 4,359 bytes, lines 3966-4112
3966				that._addClass( selectee.$element, "ui-unselecting" );
3967				selectee.unselecting = true;
3968				// selectable UNSELECTING callback
3969				that._trigger("unselecting", event, {
3970					unselecting: selectee.element
3971				});
3972			}
3973		});
3974
3975		$(event.target).parents().addBack().each(function() {
3976			var doSelect,
3977				selectee = $.data(this, "selectable-item");
3978			if (selectee) {
3979				doSelect = (!event.metaKey && !event.ctrlKey) || !selectee.$element.hasClass("ui-selected");
3980				that._removeClass( selectee.$element, doSelect ? "ui-unselecting" : "ui-selected" )
3981					._addClass( selectee.$element, doSelect ? "ui-selecting" : "ui-unselecting" );
3982				selectee.unselecting = !doSelect;
3983				selectee.selecting = doSelect;
3984				selectee.selected = doSelect;
3985				// selectable (UN)SELECTING callback
3986				if (doSelect) {
3987					that._trigger("selecting", event, {
3988						selecting: selectee.element
3989					});
3990				} else {
3991					that._trigger("unselecting", event, {
3992						unselecting: selectee.element
3993					});
3994				}
3995				return false;
3996			}
3997		});
3998
3999	},
4000
4001	_mouseDrag: function(event) {
4002
4003		this.dragged = true;
4004
4005		if (this.options.disabled) {
4006			return;
4007		}
4008
4009		var tmp,
4010			that = this,
4011			options = this.options,
4012			x1 = this.opos[0],
4013			y1 = this.opos[1],
4014			x2 = event.pageX,
4015			y2 = event.pageY;
4016
4017		if (x1 > x2) { tmp = x2; x2 = x1; x1 = tmp; }
4018		if (y1 > y2) { tmp = y2; y2 = y1; y1 = tmp; }
4019		this.helper.css({ left: x1, top: y1, width: x2 - x1, height: y2 - y1 });
4020
4021		this.selectees.each(function() {
4022			var selectee = $.data(this, "selectable-item"),
4023				hit = false;
4024
4025			//prevent helper from being selected if appendTo: selectable
4026			if (!selectee || selectee.element === that.element[0]) {
4027				return;
4028			}
4029
4030			if (options.tolerance === "touch") {
4031				hit = ( !(selectee.left > x2 || selectee.right < x1 || selectee.top > y2 || selectee.bottom < y1) );
4032			} else if (options.tolerance === "fit") {
4033				hit = (selectee.left > x1 && selectee.right < x2 && selectee.top > y1 && selectee.bottom < y2);
4034			}
4035
4036			if (hit) {
4037				// SELECT
4038				if (selectee.selected) {
4039					that._removeClass( selectee.$element, "ui-selected" );
4040					selectee.selected = false;
4041				}
4042				if (selectee.unselecting) {
4043					that._removeClass( selectee.$element, "ui-unselecting" );
4044					selectee.unselecting = false;
4045				}
4046				if (!selectee.selecting) {
4047					that._addClass( selectee.$element, "ui-selecting" );
4048					selectee.selecting = true;
4049					// selectable SELECTING callback
4050					that._trigger("selecting", event, {
4051						selecting: selectee.element
4052					});
4053				}
4054			} else {
4055				// UNSELECT
4056				if (selectee.selecting) {
4057					if ((event.metaKey || event.ctrlKey) && selectee.startselected) {
4058						that._removeClass( selectee.$element, "ui-selecting" );
4059						selectee.selecting = false;
4060						that._addClass( selectee.$element, "ui-selected" );
4061						selectee.selected = true;
4062					} else {
4063						that._removeClass( selectee.$element, "ui-selecting" );
4064						selectee.selecting = false;
4065						if (selectee.startselected) {
4066							that._addClass( selectee.$element, "ui-unselecting" );
4067							selectee.unselecting = true;
4068						}
4069						// selectable UNSELECTING callback
4070						that._trigger("unselecting", event, {
4071							unselecting: selectee.element
4072						});
4073					}
4074				}
4075				if (selectee.selected) {
4076					if (!event.metaKey && !event.ctrlKey && !selectee.startselected) {
4077						that._removeClass( selectee.$element, "ui-selected" );
4078						selectee.selected = false;
4079
4080						that._addClass( selectee.$element, "ui-unselecting" );
4081						selectee.unselecting = true;
4082						// selectable UNSELECTING callback
4083						that._trigger("unselecting", event, {
4084							unselecting: selectee.element
4085						});
4086					}
4087				}
4088			}
4089		});
4090
4091		return false;
4092	},
4093
4094	_mouseStop: function(event) {
4095		var that = this;
4096
4097		this.dragged = false;
4098
4099		$(".ui-unselecting", this.element[0]).each(function() {
4100			var selectee = $.data(this, "selectable-item");
4101			that._removeClass( selectee.$element, "ui-unselecting" );
4102			selectee.unselecting = false;
4103			selectee.startselected = false;
4104			that._trigger("unselected", event, {
4105				unselected: selectee.element
4106			});
4107		});
4108		$(".ui-selecting", this.element[0]).each(function() {
4109			var selectee = $.data(this, "selectable-item");
4110			that._removeClass( selectee.$element, "ui-selecting" )
4111				._addClass( selectee.$element, "ui-selected" );
4112			selectee.selecting = false;
vendor: 7,719 bytes, lines 4113-4431
4113			selectee.selected = true;
4114			selectee.startselected = true;
4115			that._trigger("selected", event, {
4116				selected: selectee.element
4117			});
4118		});
4119		this._trigger("stop", event);
4120
4121		this.helper.remove();
4122
4123		return false;
4124	}
4125
4126});
4127
4128}));
4129
4130//>>label: Sortable
4131//>>group: Interactions
4132//>>description: Enables items in a list to be sorted using the mouse.
4133//>>docs: http://api.jqueryui.com/sortable/
4134//>>demos: http://jqueryui.com/sortable/
4135//>>css.structure: ../themes/base/sortable.css
4136
4137(function( factory ) {
4138	if ( typeof define === "function" && define.amd ) {
4139
4140		// AMD. Register as an anonymous module.
4141		define([
4142			"jquery",
4143			"./core",
4144			"./mouse",
4145			"./widget"
4146		], factory );
4147	} else {
4148
4149		// Browser globals
4150		factory( jQuery );
4151	}
4152}(function( $ ) {
4153
4154return $.widget("ui.sortable", $.ui.mouse, {
4155	version: "@VERSION",
4156	widgetEventPrefix: "sort",
4157	ready: false,
4158	options: {
4159		appendTo: "parent",
4160		axis: false,
4161		connectWith: false,
4162		containment: false,
4163		cursor: "auto",
4164		cursorAt: false,
4165		dropOnEmpty: true,
4166		forcePlaceholderSize: false,
4167		forceHelperSize: false,
4168		grid: false,
4169		handle: false,
4170		helper: "original",
4171		items: "> *",
4172		opacity: false,
4173		placeholder: false,
4174		revert: false,
4175		scroll: true,
4176		scrollSensitivity: 20,
4177		scrollSpeed: 20,
4178		scope: "default",
4179		tolerance: "intersect",
4180		zIndex: 1000,
4181
4182		// callbacks
4183		activate: null,
4184		beforeStop: null,
4185		change: null,
4186		deactivate: null,
4187		out: null,
4188		over: null,
4189		receive: null,
4190		remove: null,
4191		sort: null,
4192		start: null,
4193		stop: null,
4194		update: null
4195	},
4196
4197	_isOverAxis: function( x, reference, size ) {
4198		return ( x >= reference ) && ( x < ( reference + size ) );
4199	},
4200
4201	_isFloating: function( item ) {
4202		return (/left|right/).test(item.css("float")) || (/inline|table-cell/).test(item.css("display"));
4203	},
4204
4205	_create: function() {
4206		this.containerCache = {};
4207		this._addClass( "ui-sortable" );
4208
4209		//Get the items
4210		this.refresh();
4211
4212		//Let's determine the parent's offset
4213		this.offset = this.element.offset();
4214
4215		//Initialize mouse events for interaction
4216		this._mouseInit();
4217
4218		this._setHandleClassName();
4219
4220		//We're ready to go
4221		this.ready = true;
4222
4223	},
4224
4225	_setOption: function( key, value ) {
4226		this._super( key, value );
4227
4228		if ( key === "handle" ) {
4229			this._setHandleClassName();
4230		}
4231	},
4232
4233	_setHandleClassName: function() {
4234		var that = this;
4235		this._removeClass( this.element.find( ".ui-sortable-handle" ), "ui-sortable-handle" );
4236		$.each( this.items, function() {
4237			that._addClass(
4238				this.instance.options.handle ?
4239					this.item.find( this.instance.options.handle ) :
4240					this.item,
4241				"ui-sortable-handle"
4242			);
4243		});
4244	},
4245
4246	_destroy: function() {
4247		this._mouseDestroy();
4248
4249		for ( var i = this.items.length - 1; i >= 0; i-- ) {
4250			this.items[i].item.removeData(this.widgetName + "-item");
4251		}
4252
4253		return this;
4254	},
4255
4256	_mouseCapture: function(event, overrideHandle) {
4257		var currentItem = null,
4258			validHandle = false,
4259			that = this;
4260
4261		if (this.reverting) {
4262			return false;
4263		}
4264
4265		if(this.options.disabled || this.options.type === "static") {
4266			return false;
4267		}
4268
4269		//We have to refresh the items data once first
4270		this._refreshItems(event);
4271
4272		//Find out if the clicked node (or one of its parents) is a actual item in this.items
4273		$(event.target).parents().each(function() {
4274			if($.data(this, that.widgetName + "-item") === that) {
4275				currentItem = $(this);
4276				return false;
4277			}
4278		});
4279		if($.data(event.target, that.widgetName + "-item") === that) {
4280			currentItem = $(event.target);
4281		}
4282
4283		if(!currentItem) {
4284			return false;
4285		}
4286		if(this.options.handle && !overrideHandle) {
4287			$(this.options.handle, currentItem).find("*").addBack().each(function() {
4288				if(this === event.target) {
4289					validHandle = true;
4290				}
4291			});
4292			if(!validHandle) {
4293				return false;
4294			}
4295		}
4296
4297		this.currentItem = currentItem;
4298		this._removeCurrentsFromItems();
4299		return true;
4300
4301	},
4302
4303	_mouseStart: function(event, overrideHandle, noActivation) {
4304
4305		var i, body,
4306			o = this.options;
4307
4308		this.currentContainer = this;
4309
4310		//We only need to call refreshPositions, because the refreshItems call has been moved to mouseCapture
4311		this.refreshPositions();
4312
4313		//Create and append the visible helper
4314		this.helper = this._createHelper(event);
4315
4316		//Cache the helper size
4317		this._cacheHelperProportions();
4318
4319		/*
4320		 * - Position generation -
4321		 * This block generates everything position related - it's the core of draggables.
4322		 */
4323
4324		//Cache the margins of the original element
4325		this._cacheMargins();
4326
4327		//Get the next scrolling parent
4328		this.scrollParent = this.helper.scrollParent();
4329
4330		//The element's absolute position on the page minus margins
4331		this.offset = this.currentItem.offset();
4332		this.offset = {
4333			top: this.offset.top - this.margins.top,
4334			left: this.offset.left - this.margins.left
4335		};
4336
4337		$.extend(this.offset, {
4338			click: { //Where the click happened, relative to the element
4339				left: event.pageX - this.offset.left,
4340				top: event.pageY - this.offset.top
4341			},
4342			parent: this._getParentOffset(),
4343			relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
4344		});
4345
4346		// Only after we got the offset, we can change the helper's position to absolute
4347		// TODO: Still need to figure out a way to make relative sorting possible
4348		this.helper.css("position", "absolute");
4349		this.cssPosition = this.helper.css("position");
4350
4351		//Generate the original position
4352		this.originalPosition = this._generatePosition(event);
4353		this.originalPageX = event.pageX;
4354		this.originalPageY = event.pageY;
4355
4356		//Adjust the mouse offset relative to the helper if "cursorAt" is supplied
4357		(o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
4358
4359		//Cache the former DOM position
4360		this.domPosition = { prev: this.currentItem.prev()[0], parent: this.currentItem.parent()[0] };
4361
4362		//If the helper is not the original, hide the original so it's not playing any role during the drag, won't cause anything bad this way
4363		if(this.helper[0] !== this.currentItem[0]) {
4364			this.currentItem.hide();
4365		}
4366
4367		//Create the placeholder
4368		this._createPlaceholder();
4369
4370		//Set a containment if given in the options
4371		if(o.containment) {
4372			this._setContainment();
4373		}
4374
4375		if( o.cursor && o.cursor !== "auto" ) { // cursor option
4376			body = this.document.find( "body" );
4377
4378			// support: IE
4379			this.storedCursor = body.css( "cursor" );
4380			body.css( "cursor", o.cursor );
4381
4382			this.storedStylesheet = $( "<style>*{ cursor: "+o.cursor+" !important; }</style>" ).appendTo( body );
4383		}
4384
4385		if(o.opacity) { // opacity option
4386			if (this.helper.css("opacity")) {
4387				this._storedOpacity = this.helper.css("opacity");
4388			}
4389			this.helper.css("opacity", o.opacity);
4390		}
4391
4392		if(o.zIndex) { // zIndex option
4393			if (this.helper.css("zIndex")) {
4394				this._storedZIndex = this.helper.css("zIndex");
4395			}
4396			this.helper.css("zIndex", o.zIndex);
4397		}
4398
4399		//Prepare scrolling
4400		if(this.scrollParent[0] !== this.document[0] && this.scrollParent[0].tagName !== "HTML") {
4401			this.overflowOffset = this.scrollParent.offset();
4402		}
4403
4404		//Call callbacks
4405		this._trigger("start", event, this._uiHash());
4406
4407		//Recache the helper size
4408		if(!this._preserveHelperProportions) {
4409			this._cacheHelperProportions();
4410		}
4411
4412
4413		//Post "activate" events to possible containers
4414		if( !noActivation ) {
4415			for ( i = this.containers.length - 1; i >= 0; i-- ) {
4416				this.containers[ i ]._trigger( "activate", event, this._uiHash( this ) );
4417			}
4418		}
4419
4420		//Prepare possible droppables
4421		if($.ui.ddmanager) {
4422			$.ui.ddmanager.current = this;
4423		}
4424
4425		if ($.ui.ddmanager && !o.dropBehaviour) {
4426			$.ui.ddmanager.prepareOffsets(this, event);
4427		}
4428
4429		this.dragging = true;
4430
4431		this._addClass( this.helper, "ui-sortable-helper" 
vendor: 5,558 bytes, lines 4431-4600
4431);
4432		this._mouseDrag(event); //Execute the drag once - this causes the helper not to be visible before getting its correct position
4433		return true;
4434
4435	},
4436
4437	_mouseDrag: function(event) {
4438		var i, item, itemElement, intersection,
4439			o = this.options,
4440			scrolled = false;
4441
4442		//Compute the helpers position
4443		this.position = this._generatePosition(event);
4444		this.positionAbs = this._convertPositionTo("absolute");
4445
4446		if (!this.lastPositionAbs) {
4447			this.lastPositionAbs = this.positionAbs;
4448		}
4449
4450		//Do scrolling
4451		if(this.options.scroll) {
4452			if(this.scrollParent[0] !== this.document[0] && this.scrollParent[0].tagName !== "HTML") {
4453
4454				if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) {
4455					this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
4456				} else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity) {
4457					this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
4458				}
4459
4460				if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) {
4461					this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
4462				} else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity) {
4463					this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
4464				}
4465
4466			} else {
4467
4468				if(event.pageY - this.document.scrollTop() < o.scrollSensitivity) {
4469					scrolled = this.document.scrollTop(this.document.scrollTop() - o.scrollSpeed);
4470				} else if(this.window.height() - (event.pageY - this.document.scrollTop()) < o.scrollSensitivity) {
4471					scrolled = this.document.scrollTop(this.document.scrollTop() + o.scrollSpeed);
4472				}
4473
4474				if(event.pageX - this.document.scrollLeft() < o.scrollSensitivity) {
4475					scrolled = this.document.scrollLeft(this.document.scrollLeft() - o.scrollSpeed);
4476				} else if(this.window.width() - (event.pageX - this.document.scrollLeft()) < o.scrollSensitivity) {
4477					scrolled = this.document.scrollLeft(this.document.scrollLeft() + o.scrollSpeed);
4478				}
4479
4480			}
4481
4482			if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) {
4483				$.ui.ddmanager.prepareOffsets(this, event);
4484			}
4485		}
4486
4487		//Regenerate the absolute position used for position checks
4488		this.positionAbs = this._convertPositionTo("absolute");
4489
4490		//Set the helper position
4491		if(!this.options.axis || this.options.axis !== "y") {
4492			this.helper[0].style.left = this.position.left+"px";
4493		}
4494		if(!this.options.axis || this.options.axis !== "x") {
4495			this.helper[0].style.top = this.position.top+"px";
4496		}
4497
4498		//Rearrange
4499		for (i = this.items.length - 1; i >= 0; i--) {
4500
4501			//Cache variables and intersection, continue if no intersection
4502			item = this.items[i];
4503			itemElement = item.item[0];
4504			intersection = this._intersectsWithPointer(item);
4505			if (!intersection) {
4506				continue;
4507			}
4508
4509			// Only put the placeholder inside the current Container, skip all
4510			// items from other containers. This works because when moving
4511			// an item from one container to another the
4512			// currentContainer is switched before the placeholder is moved.
4513			//
4514			// Without this, moving items in "sub-sortables" can cause
4515			// the placeholder to jitter between the outer and inner container.
4516			if (item.instance !== this.currentContainer) {
4517				continue;
4518			}
4519
4520			// cannot intersect with itself
4521			// no useless actions that have been done before
4522			// no action if the item moved is the parent of the item checked
4523			if (itemElement !== this.currentItem[0] &&
4524				this.placeholder[intersection === 1 ? "next" : "prev"]()[0] !== itemElement &&
4525				!$.contains(this.placeholder[0], itemElement) &&
4526				(this.options.type === "semi-dynamic" ? !$.contains(this.element[0], itemElement) : true)
4527			) {
4528
4529				this.direction = intersection === 1 ? "down" : "up";
4530
4531				if (this.options.tolerance === "pointer" || this._intersectsWithSides(item)) {
4532					this._rearrange(event, item);
4533				} else {
4534					break;
4535				}
4536
4537				this._trigger("change", event, this._uiHash());
4538				break;
4539			}
4540		}
4541
4542		//Post events to containers
4543		this._contactContainers(event);
4544
4545		//Interconnect with droppables
4546		if($.ui.ddmanager) {
4547			$.ui.ddmanager.drag(this, event);
4548		}
4549
4550		//Call callbacks
4551		this._trigger("sort", event, this._uiHash());
4552
4553		this.lastPositionAbs = this.positionAbs;
4554		return false;
4555
4556	},
4557
4558	_mouseStop: function(event, noPropagation) {
4559
4560		if(!event) {
4561			return;
4562		}
4563
4564		//If we are using droppables, inform the manager about the drop
4565		if ($.ui.ddmanager && !this.options.dropBehaviour) {
4566			$.ui.ddmanager.drop(this, event);
4567		}
4568
4569		if(this.options.revert) {
4570			var that = this,
4571				cur = this.placeholder.offset(),
4572				axis = this.options.axis,
4573				animation = {};
4574
4575			if ( !axis || axis === "x" ) {
4576				animation.left = cur.left - this.offset.parent.left - this.margins.left + (this.offsetParent[0] === this.document[0].body ? 0 : this.offsetParent[0].scrollLeft);
4577			}
4578			if ( !axis || axis === "y" ) {
4579				animation.top = cur.top - this.offset.parent.top - this.margins.top + (this.offsetParent[0] === this.document[0].body ? 0 : this.offsetParent[0].scrollTop);
4580			}
4581			this.reverting = true;
4582			$(this.helper).animate( animation, parseInt(this.options.revert, 10) || 500, function() {
4583				that._clear(event);
4584			});
4585		} else {
4586			this._clear(event, noPropagation);
4587		}
4588
4589		return false;
4590
4591	},
4592
4593	cancel: function() {
4594
4595		if(this.dragging) {
4596
4597			this._mouseUp({ target: null });
4598
4599			if(this.options.helper === "original") {
4600				this.currentItem.css(
4600 this._storedCSS );
4601				this._removeClass( this.currentItem, "ui-sortable-helper" 
vendor: 9,828 bytes, lines 4601-4914
4601);
4602			} else {
4603				this.currentItem.show();
4604			}
4605
4606			//Post deactivating events to containers
4607			for (var i = this.containers.length - 1; i >= 0; i--){
4608				this.containers[i]._trigger("deactivate", null, this._uiHash(this));
4609				if(this.containers[i].containerCache.over) {
4610					this.containers[i]._trigger("out", null, this._uiHash(this));
4611					this.containers[i].containerCache.over = 0;
4612				}
4613			}
4614
4615		}
4616
4617		if (this.placeholder) {
4618			//$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
4619			if(this.placeholder[0].parentNode) {
4620				this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
4621			}
4622			if(this.options.helper !== "original" && this.helper && this.helper[0].parentNode) {
4623				this.helper.remove();
4624			}
4625
4626			$.extend(this, {
4627				helper: null,
4628				dragging: false,
4629				reverting: false,
4630				_noFinalSort: null
4631			});
4632
4633			if(this.domPosition.prev) {
4634				$(this.domPosition.prev).after(this.currentItem);
4635			} else {
4636				$(this.domPosition.parent).prepend(this.currentItem);
4637			}
4638		}
4639
4640		return this;
4641
4642	},
4643
4644	serialize: function(o) {
4645
4646		var items = this._getItemsAsjQuery(o && o.connected),
4647			str = [];
4648		o = o || {};
4649
4650		$(items).each(function() {
4651			var res = ($(o.item || this).attr(o.attribute || "id") || "").match(o.expression || (/(.+)[\-=_](.+)/));
4652			if (res) {
4653				str.push((o.key || res[1]+"[]")+"="+(o.key && o.expression ? res[1] : res[2]));
4654			}
4655		});
4656
4657		if(!str.length && o.key) {
4658			str.push(o.key + "=");
4659		}
4660
4661		return str.join("&");
4662
4663	},
4664
4665	toArray: function(o) {
4666
4667		var items = this._getItemsAsjQuery(o && o.connected),
4668			ret = [];
4669
4670		o = o || {};
4671
4672		items.each(function() { ret.push($(o.item || this).attr(o.attribute || "id") || ""); });
4673		return ret;
4674
4675	},
4676
4677	/* Be careful with the following core functions */
4678	_intersectsWith: function(item) {
4679
4680		var x1 = this.positionAbs.left,
4681			x2 = x1 + this.helperProportions.width,
4682			y1 = this.positionAbs.top,
4683			y2 = y1 + this.helperProportions.height,
4684			l = item.left,
4685			r = l + item.width,
4686			t = item.top,
4687			b = t + item.height,
4688			dyClick = this.offset.click.top,
4689			dxClick = this.offset.click.left,
4690			isOverElementHeight = ( this.options.axis === "x" ) || ( ( y1 + dyClick ) > t && ( y1 + dyClick ) < b ),
4691			isOverElementWidth = ( this.options.axis === "y" ) || ( ( x1 + dxClick ) > l && ( x1 + dxClick ) < r ),
4692			isOverElement = isOverElementHeight && isOverElementWidth;
4693
4694		if ( this.options.tolerance === "pointer" ||
4695			this.options.forcePointerForContainers ||
4696			(this.options.tolerance !== "pointer" && this.helperProportions[this.floating ? "width" : "height"] > item[this.floating ? "width" : "height"])
4697		) {
4698			return isOverElement;
4699		} else {
4700
4701			return (l < x1 + (this.helperProportions.width / 2) && // Right Half
4702				x2 - (this.helperProportions.width / 2) < r && // Left Half
4703				t < y1 + (this.helperProportions.height / 2) && // Bottom Half
4704				y2 - (this.helperProportions.height / 2) < b ); // Top Half
4705
4706		}
4707	},
4708
4709	_intersectsWithPointer: function(item) {
4710
4711		var isOverElementHeight = (this.options.axis === "x") || this._isOverAxis(this.positionAbs.top + this.offset.click.top, item.top, item.height),
4712			isOverElementWidth = (this.options.axis === "y") || this._isOverAxis(this.positionAbs.left + this.offset.click.left, item.left, item.width),
4713			isOverElement = isOverElementHeight && isOverElementWidth,
4714			verticalDirection = this._getDragVerticalDirection(),
4715			horizontalDirection = this._getDragHorizontalDirection();
4716
4717		if (!isOverElement) {
4718			return false;
4719		}
4720
4721		return this.floating ?
4722			( ((horizontalDirection && horizontalDirection === "right") || verticalDirection === "down") ? 2 : 1 )
4723			: ( verticalDirection && (verticalDirection === "down" ? 2 : 1) );
4724
4725	},
4726
4727	_intersectsWithSides: function(item) {
4728
4729		var isOverBottomHalf = this._isOverAxis(this.positionAbs.top + this.offset.click.top, item.top + (item.height/2), item.height),
4730			isOverRightHalf = this._isOverAxis(this.positionAbs.left + this.offset.click.left, item.left + (item.width/2), item.width),
4731			verticalDirection = this._getDragVerticalDirection(),
4732			horizontalDirection = this._getDragHorizontalDirection();
4733
4734		if (this.floating && horizontalDirection) {
4735			return ((horizontalDirection === "right" && isOverRightHalf) || (horizontalDirection === "left" && !isOverRightHalf));
4736		} else {
4737			return verticalDirection && ((verticalDirection === "down" && isOverBottomHalf) || (verticalDirection === "up" && !isOverBottomHalf));
4738		}
4739
4740	},
4741
4742	_getDragVerticalDirection: function() {
4743		var delta = this.positionAbs.top - this.lastPositionAbs.top;
4744		return delta !== 0 && (delta > 0 ? "down" : "up");
4745	},
4746
4747	_getDragHorizontalDirection: function() {
4748		var delta = this.positionAbs.left - this.lastPositionAbs.left;
4749		return delta !== 0 && (delta > 0 ? "right" : "left");
4750	},
4751
4752	refresh: function(event) {
4753		this._refreshItems(event);
4754		this._setHandleClassName();
4755		this.refreshPositions();
4756		return this;
4757	},
4758
4759	_connectWith: function() {
4760		var options = this.options;
4761		return options.connectWith.constructor === String ? [options.connectWith] : options.connectWith;
4762	},
4763
4764	_getItemsAsjQuery: function(connected) {
4765
4766		var i, j, cur, inst,
4767			items = [],
4768			queries = [],
4769			connectWith = this._connectWith();
4770
4771		if(connectWith && connected) {
4772			for (i = connectWith.length - 1; i >= 0; i--){
4773				cur = $(connectWith[i], this.document[0]);
4774				for ( j = cur.length - 1; j >= 0; j--){
4775					inst = $.data(cur[j], this.widgetFullName);
4776					if(inst && inst !== this && !inst.options.disabled) {
4777						queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element) : $(inst.options.items, inst.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"), inst]);
4778					}
4779				}
4780			}
4781		}
4782
4783		queries.push([$.isFunction(this.options.items) ? this.options.items.call(this.element, null, { options: this.options, item: this.currentItem }) : $(this.options.items, this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"), this]);
4784
4785		function addItems() {
4786			items.push( this );
4787		}
4788		for (i = queries.length - 1; i >= 0; i--){
4789			queries[i][0].each( addItems );
4790		}
4791
4792		return $(items);
4793
4794	},
4795
4796	_removeCurrentsFromItems: function() {
4797
4798		var list = this.currentItem.find(":data(" + this.widgetName + "-item)");
4799
4800		this.items = $.grep(this.items, function (item) {
4801			for (var j=0; j < list.length; j++) {
4802				if(list[j] === item.item[0]) {
4803					return false;
4804				}
4805			}
4806			return true;
4807		});
4808
4809	},
4810
4811	_refreshItems: function(event) {
4812
4813		this.items = [];
4814		this.containers = [this];
4815
4816		var i, j, cur, inst, targetData, _queries, item, queriesLength,
4817			items = this.items,
4818			queries = [[$.isFunction(this.options.items) ? this.options.items.call(this.element[0], event, { item: this.currentItem }) : $(this.options.items, this.element), this]],
4819			connectWith = this._connectWith();
4820
4821		if(connectWith && this.ready) { //Shouldn't be run the first time through due to massive slow-down
4822			for (i = connectWith.length - 1; i >= 0; i--){
4823				cur = $(connectWith[i], this.document[0]);
4824				for (j = cur.length - 1; j >= 0; j--){
4825					inst = $.data(cur[j], this.widgetFullName);
4826					if(inst && inst !== this && !inst.options.disabled) {
4827						queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element[0], event, { item: this.currentItem }) : $(inst.options.items, inst.element), inst]);
4828						this.containers.push(inst);
4829					}
4830				}
4831			}
4832		}
4833
4834		for (i = queries.length - 1; i >= 0; i--) {
4835			targetData = queries[i][1];
4836			_queries = queries[i][0];
4837
4838			for (j=0, queriesLength = _queries.length; j < queriesLength; j++) {
4839				item = $(_queries[j]);
4840
4841				item.data(this.widgetName + "-item", targetData); // Data for target checking (mouse manager)
4842
4843				items.push({
4844					item: item,
4845					instance: targetData,
4846					width: 0, height: 0,
4847					left: 0, top: 0
4848				});
4849			}
4850		}
4851
4852	},
4853
4854	refreshPositions: function(fast) {
4855
4856		// Determine whether items are being displayed horizontally
4857		this.floating = this.items.length ?
4858			this.options.axis === "x" || this._isFloating( this.items[ 0 ].item ) :
4859			false;
4860
4861		//This has to be redone because due to the item being moved out/into the offsetParent, the offsetParent's position will change
4862		if(this.offsetParent && this.helper) {
4863			this.offset.parent = this._getParentOffset();
4864		}
4865
4866		var i, item, t, p;
4867
4868		for (i = this.items.length - 1; i >= 0; i--){
4869			item = this.items[i];
4870
4871			//We ignore calculating positions of all connected containers when we're not over them
4872			if(item.instance !== this.currentContainer && this.currentContainer && item.item[0] !== this.currentItem[0]) {
4873				continue;
4874			}
4875
4876			t = this.options.toleranceElement ? $(this.options.toleranceElement, item.item) : item.item;
4877
4878			if (!fast) {
4879				item.width = t.outerWidth();
4880				item.height = t.outerHeight();
4881			}
4882
4883			p = t.offset();
4884			item.left = p.left;
4885			item.top = p.top;
4886		}
4887
4888		if(this.options.custom && this.options.custom.refreshContainers) {
4889			this.options.custom.refreshContainers.call(this);
4890		} else {
4891			for (i = this.containers.length - 1; i >= 0; i--){
4892				p = this.containers[i].element.offset();
4893				this.containers[i].containerCache.left = p.left;
4894				this.containers[i].containerCache.top = p.top;
4895				this.containers[i].containerCache.width = this.containers[i].element.outerWidth();
4896				this.containers[i].containerCache.height = this.containers[i].element.outerHeight();
4897			}
4898		}
4899
4900		return this;
4901	},
4902
4903	_createPlaceholder: function(that) {
4904		that = that || this;
4905		var className,
4906			o = that.options;
4907
4908		if(!o.placeholder || o.placeholder.constructor === String) {
4909			className = o.placeholder;
4910			o.placeholder = {
4911				element: function() {
4912
4913					var nodeName = that.currentItem[0].nodeName.toLowerCase(),
4914						element = $( "<" + nodeName + ">", that.document[0] )
4914;
4915
4916						that._addClass( element, "ui-sortable-placeholder",
4917								className || that.currentItem[ 0 ].className )
4918							._removeClass( element, "ui-sortable-helper" 
vendor: 17,002 bytes, lines 4918-5344
4918);
4919
4920					if ( nodeName === "tbody" ) {
4921						that._createTrPlaceholder(
4922							that.currentItem.find( "tr" ).eq( 0 ),
4923							$( "<tr>", that.document[ 0 ] ).appendTo( element )
4924						);
4925					} else if ( nodeName === "tr" ) {
4926						that._createTrPlaceholder( that.currentItem, element );
4927					} else if ( nodeName === "img" ) {
4928						element.attr( "src", that.currentItem.attr( "src" ) );
4929					}
4930
4931					if ( !className ) {
4932						element.css( "visibility", "hidden" );
4933					}
4934
4935					return element;
4936				},
4937				update: function(container, p) {
4938
4939					// 1. If a className is set as 'placeholder option, we don't force sizes - the class is responsible for that
4940					// 2. The option 'forcePlaceholderSize can be enabled to force it even if a class name is specified
4941					if(className && !o.forcePlaceholderSize) {
4942						return;
4943					}
4944
4945					//If the element doesn't have a actual height by itself (without styles coming from a stylesheet), it receives the inline height from the dragged item
4946					if(!p.height()) { p.height(that.currentItem.innerHeight() - parseInt(that.currentItem.css("paddingTop")||0, 10) - parseInt(that.currentItem.css("paddingBottom")||0, 10)); }
4947					if(!p.width()) { p.width(that.currentItem.innerWidth() - parseInt(that.currentItem.css("paddingLeft")||0, 10) - parseInt(that.currentItem.css("paddingRight")||0, 10)); }
4948				}
4949			};
4950		}
4951
4952		//Create the placeholder
4953		that.placeholder = $(o.placeholder.element.call(that.element, that.currentItem));
4954
4955		//Append it after the actual current item
4956		that.currentItem.after(that.placeholder);
4957
4958		//Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317)
4959		o.placeholder.update(that, that.placeholder);
4960
4961	},
4962
4963	_createTrPlaceholder: function( sourceTr, targetTr ) {
4964		var that = this;
4965
4966		sourceTr.children().each(function() {
4967			$( "<td>&#160;</td>", that.document[ 0 ] )
4968				.attr( "colspan", $( this ).attr( "colspan" ) || 1 )
4969				.appendTo( targetTr );
4970		});
4971	},
4972
4973	_contactContainers: function(event) {
4974		var i, j, dist, itemWithLeastDistance, posProperty, sizeProperty, cur, nearBottom, floating, axis,
4975			innermostContainer = null,
4976			innermostIndex = null;
4977
4978		// get innermost container that intersects with item
4979		for (i = this.containers.length - 1; i >= 0; i--) {
4980
4981			// never consider a container that's located within the item itself
4982			if($.contains(this.currentItem[0], this.containers[i].element[0])) {
4983				continue;
4984			}
4985
4986			if(this._intersectsWith(this.containers[i].containerCache)) {
4987
4988				// if we've already found a container and it's more "inner" than this, then continue
4989				if(innermostContainer && $.contains(this.containers[i].element[0], innermostContainer.element[0])) {
4990					continue;
4991				}
4992
4993				innermostContainer = this.containers[i];
4994				innermostIndex = i;
4995
4996			} else {
4997				// container doesn't intersect. trigger "out" event if necessary
4998				if(this.containers[i].containerCache.over) {
4999					this.containers[i]._trigger("out", event, this._uiHash(this));
5000					this.containers[i].containerCache.over = 0;
5001				}
5002			}
5003
5004		}
5005
5006		// if no intersecting containers found, return
5007		if(!innermostContainer) {
5008			return;
5009		}
5010
5011		// move the item into the container if it's not there already
5012		if(this.containers.length === 1) {
5013			if (!this.containers[innermostIndex].containerCache.over) {
5014				this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
5015				this.containers[innermostIndex].containerCache.over = 1;
5016			}
5017		} else {
5018
5019			//When entering a new container, we will find the item with the least distance and append our item near it
5020			dist = 10000;
5021			itemWithLeastDistance = null;
5022			floating = innermostContainer.floating || this._isFloating(this.currentItem);
5023			posProperty = floating ? "left" : "top";
5024			sizeProperty = floating ? "width" : "height";
5025			axis = floating ? "clientX" : "clientY";
5026
5027			for (j = this.items.length - 1; j >= 0; j--) {
5028				if(!$.contains(this.containers[innermostIndex].element[0], this.items[j].item[0])) {
5029					continue;
5030				}
5031				if(this.items[j].item[0] === this.currentItem[0]) {
5032					continue;
5033				}
5034
5035				cur = this.items[j].item.offset()[posProperty];
5036				nearBottom = false;
5037				if ( event[ axis ] - cur > this.items[ j ][ sizeProperty ] / 2 ) {
5038					nearBottom = true;
5039				}
5040
5041				if ( Math.abs( event[ axis ] - cur ) < dist ) {
5042					dist = Math.abs( event[ axis ] - cur );
5043					itemWithLeastDistance = this.items[ j ];
5044					this.direction = nearBottom ? "up": "down";
5045				}
5046			}
5047
5048			//Check if dropOnEmpty is enabled
5049			if(!itemWithLeastDistance && !this.options.dropOnEmpty) {
5050				return;
5051			}
5052
5053			if(this.currentContainer === this.containers[innermostIndex]) {
5054				if ( !this.currentContainer.containerCache.over ) {
5055					this.containers[ innermostIndex ]._trigger( "over", event, this._uiHash() );
5056					this.currentContainer.containerCache.over = 1;
5057				}
5058				return;
5059			}
5060
5061			itemWithLeastDistance ? this._rearrange(event, itemWithLeastDistance, null, true) : this._rearrange(event, null, this.containers[innermostIndex].element, true);
5062			this._trigger("change", event, this._uiHash());
5063			this.containers[innermostIndex]._trigger("change", event, this._uiHash(this));
5064			this.currentContainer = this.containers[innermostIndex];
5065
5066			//Update the placeholder
5067			this.options.placeholder.update(this.currentContainer, this.placeholder);
5068
5069			this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
5070			this.containers[innermostIndex].containerCache.over = 1;
5071		}
5072
5073
5074	},
5075
5076	_createHelper: function(event) {
5077
5078		var o = this.options,
5079			helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event, this.currentItem])) : (o.helper === "clone" ? this.currentItem.clone() : this.currentItem);
5080
5081		//Add the helper to the DOM if that didn't happen already
5082		if(!helper.parents("body").length) {
5083			$(o.appendTo !== "parent" ? o.appendTo : this.currentItem[0].parentNode)[0].appendChild(helper[0]);
5084		}
5085
5086		if(helper[0] === this.currentItem[0]) {
5087			this._storedCSS = { width: this.currentItem[0].style.width, height: this.currentItem[0].style.height, position: this.currentItem.css("position"), top: this.currentItem.css("top"), left: this.currentItem.css("left") };
5088		}
5089
5090		if(!helper[0].style.width || o.forceHelperSize) {
5091			helper.width(this.currentItem.width());
5092		}
5093		if(!helper[0].style.height || o.forceHelperSize) {
5094			helper.height(this.currentItem.height());
5095		}
5096
5097		return helper;
5098
5099	},
5100
5101	_adjustOffsetFromHelper: function(obj) {
5102		if (typeof obj === "string") {
5103			obj = obj.split(" ");
5104		}
5105		if ($.isArray(obj)) {
5106			obj = {left: +obj[0], top: +obj[1] || 0};
5107		}
5108		if ("left" in obj) {
5109			this.offset.click.left = obj.left + this.margins.left;
5110		}
5111		if ("right" in obj) {
5112			this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
5113		}
5114		if ("top" in obj) {
5115			this.offset.click.top = obj.top + this.margins.top;
5116		}
5117		if ("bottom" in obj) {
5118			this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
5119		}
5120	},
5121
5122	_getParentOffset: function() {
5123
5124
5125		//Get the offsetParent and cache its position
5126		this.offsetParent = this.helper.offsetParent();
5127		var po = this.offsetParent.offset();
5128
5129		// This is a special case where we need to modify a offset calculated on start, since the following happened:
5130		// 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
5131		// 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
5132		//    the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
5133		if(this.cssPosition === "absolute" && this.scrollParent[0] !== this.document[0] && $.contains(this.scrollParent[0], this.offsetParent[0])) {
5134			po.left += this.scrollParent.scrollLeft();
5135			po.top += this.scrollParent.scrollTop();
5136		}
5137
5138		// This needs to be actually done for all browsers, since pageX/pageY includes this information
5139		// with an ugly IE fix
5140		if( this.offsetParent[0] === this.document[0].body || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() === "html" && $.ui.ie)) {
5141			po = { top: 0, left: 0 };
5142		}
5143
5144		return {
5145			top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
5146			left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
5147		};
5148
5149	},
5150
5151	_getRelativeOffset: function() {
5152
5153		if(this.cssPosition === "relative") {
5154			var p = this.currentItem.position();
5155			return {
5156				top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
5157				left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
5158			};
5159		} else {
5160			return { top: 0, left: 0 };
5161		}
5162
5163	},
5164
5165	_cacheMargins: function() {
5166		this.margins = {
5167			left: (parseInt(this.currentItem.css("marginLeft"),10) || 0),
5168			top: (parseInt(this.currentItem.css("marginTop"),10) || 0)
5169		};
5170	},
5171
5172	_cacheHelperProportions: function() {
5173		this.helperProportions = {
5174			width: this.helper.outerWidth(),
5175			height: this.helper.outerHeight()
5176		};
5177	},
5178
5179	_setContainment: function() {
5180
5181		var ce, co, over,
5182			o = this.options;
5183		if(o.containment === "parent") {
5184			o.containment = this.helper[0].parentNode;
5185		}
5186		if(o.containment === "document" || o.containment === "window") {
5187			this.containment = [
5188				0 - this.offset.relative.left - this.offset.parent.left,
5189				0 - this.offset.relative.top - this.offset.parent.top,
5190				o.containment === "document" ? this.document.width() : this.window.width() - this.helperProportions.width - this.margins.left,
5191				(o.containment === "document" ? this.document.width() : this.window.height() || this.document[0].body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
5192			];
5193		}
5194
5195		if(!(/^(document|window|parent)$/).test(o.containment)) {
5196			ce = $(o.containment)[0];
5197			co = $(o.containment).offset();
5198			over = ($(ce).css("overflow") !== "hidden");
5199
5200			this.containment = [
5201				co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left,
5202				co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0) - this.margins.top,
5203				co.left+(over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left,
5204				co.top+(over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top
5205			];
5206		}
5207
5208	},
5209
5210	_convertPositionTo: function(d, pos) {
5211
5212		if(!pos) {
5213			pos = this.position;
5214		}
5215		var mod = d === "absolute" ? 1 : -1,
5216			scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== this.document[0] && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent,
5217			scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
5218
5219		return {
5220			top: (
5221				pos.top	+																// The absolute mouse position
5222				this.offset.relative.top * mod +										// Only for relative positioned nodes: Relative offset from element to offset parent
5223				this.offset.parent.top * mod -											// The offsetParent's offset without borders (offset + border)
5224				( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
5225			),
5226			left: (
5227				pos.left +																// The absolute mouse position
5228				this.offset.relative.left * mod +										// Only for relative positioned nodes: Relative offset from element to offset parent
5229				this.offset.parent.left * mod	-										// The offsetParent's offset without borders (offset + border)
5230				( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
5231			)
5232		};
5233
5234	},
5235
5236	_generatePosition: function(event) {
5237
5238		var top, left,
5239			o = this.options,
5240			pageX = event.pageX,
5241			pageY = event.pageY,
5242			scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== this.document[0] && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
5243
5244		// This is another very weird special case that only happens for relative elements:
5245		// 1. If the css position is relative
5246		// 2. and the scroll parent is the document or similar to the offset parent
5247		// we have to refresh the relative offset during the scroll so there are no jumps
5248		if(this.cssPosition === "relative" && !(this.scrollParent[0] !== this.document[0] && this.scrollParent[0] !== this.offsetParent[0])) {
5249			this.offset.relative = this._getRelativeOffset();
5250		}
5251
5252		/*
5253		 * - Position constraining -
5254		 * Constrain the position to a mix of grid, containment.
5255		 */
5256
5257		if(this.originalPosition) { //If we are not dragging yet, we won't check for options
5258
5259			if(this.containment) {
5260				if(event.pageX - this.offset.click.left < this.containment[0]) {
5261					pageX = this.containment[0] + this.offset.click.left;
5262				}
5263				if(event.pageY - this.offset.click.top < this.containment[1]) {
5264					pageY = this.containment[1] + this.offset.click.top;
5265				}
5266				if(event.pageX - this.offset.click.left > this.containment[2]) {
5267					pageX = this.containment[2] + this.offset.click.left;
5268				}
5269				if(event.pageY - this.offset.click.top > this.containment[3]) {
5270					pageY = this.containment[3] + this.offset.click.top;
5271				}
5272			}
5273
5274			if(o.grid) {
5275				top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1];
5276				pageY = this.containment ? ( (top - this.offset.click.top >= this.containment[1] && top - this.offset.click.top <= this.containment[3]) ? top : ((top - this.offset.click.top >= this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
5277
5278				left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0];
5279				pageX = this.containment ? ( (left - this.offset.click.left >= this.containment[0] && left - this.offset.click.left <= this.containment[2]) ? left : ((left - this.offset.click.left >= this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
5280			}
5281
5282		}
5283
5284		return {
5285			top: (
5286				pageY -																// The absolute mouse position
5287				this.offset.click.top -													// Click offset (relative to the element)
5288				this.offset.relative.top	-											// Only for relative positioned nodes: Relative offset from element to offset parent
5289				this.offset.parent.top +												// The offsetParent's offset without borders (offset + border)
5290				( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
5291			),
5292			left: (
5293				pageX -																// The absolute mouse position
5294				this.offset.click.left -												// Click offset (relative to the element)
5295				this.offset.relative.left	-											// Only for relative positioned nodes: Relative offset from element to offset parent
5296				this.offset.parent.left +												// The offsetParent's offset without borders (offset + border)
5297				( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
5298			)
5299		};
5300
5301	},
5302
5303	_rearrange: function(event, i, a, hardRefresh) {
5304
5305		a ? a[0].appendChild(this.placeholder[0]) : i.item[0].parentNode.insertBefore(this.placeholder[0], (this.direction === "down" ? i.item[0] : i.item[0].nextSibling));
5306
5307		//Various things done here to improve the performance:
5308		// 1. we create a setTimeout, that calls refreshPositions
5309		// 2. on the instance, we have a counter variable, that get's higher after every append
5310		// 3. on the local scope, we copy the counter variable, and check in the timeout, if it's still the same
5311		// 4. this lets only the last addition to the timeout stack through
5312		this.counter = this.counter ? ++this.counter : 1;
5313		var counter = this.counter;
5314
5315		this._delay(function() {
5316			if(counter === this.counter) {
5317				this.refreshPositions(!hardRefresh); //Precompute after each DOM insertion, NOT on mousemove
5318			}
5319		});
5320
5321	},
5322
5323	_clear: function(event, noPropagation) {
5324
5325		this.reverting = false;
5326		// We delay all events that have to be triggered to after the point where the placeholder has been removed and
5327		// everything else normalized again
5328		var i,
5329			delayedTriggers = [];
5330
5331		// We first have to update the dom position of the actual currentItem
5332		// Note: don't do it if the current item is already removed (by a user), or it gets reappended (see #4088)
5333		if(!this._noFinalSort && this.currentItem.parent().length) {
5334			this.placeholder.before(this.currentItem);
5335		}
5336		this._noFinalSort = null;
5337
5338		if(this.helper[0] === this.currentItem[0]) {
5339			for(i in this._storedCSS) {
5340				if(this._storedCSS[i] === "auto" || this._storedCSS[i] === "static") {
5341					this._storedCSS[i] = "";
5342				}
5343			}
5344			this.currentItem.css(
vendor: 2,806 bytes, lines 5344-5419
5344 this._storedCSS );
5345			this._removeClass( this.currentItem, "ui-sortable-helper" );
5346		} else {
5347			this.currentItem.show();
5348		}
5349
5350		if(this.fromOutside && !noPropagation) {
5351			delayedTriggers.push(function(event) { this._trigger("receive", event, this._uiHash(this.fromOutside)); });
5352		}
5353		if((this.fromOutside || this.domPosition.prev !== this.currentItem.prev().not(".ui-sortable-helper")[0] || this.domPosition.parent !== this.currentItem.parent()[0]) && !noPropagation) {
5354			delayedTriggers.push(function(event) { this._trigger("update", event, this._uiHash()); }); //Trigger update callback if the DOM position has changed
5355		}
5356
5357		// Check if the items Container has Changed and trigger appropriate
5358		// events.
5359		if (this !== this.currentContainer) {
5360			if(!noPropagation) {
5361				delayedTriggers.push(function(event) { this._trigger("remove", event, this._uiHash()); });
5362				delayedTriggers.push((function(c) { return function(event) { c._trigger("receive", event, this._uiHash(this)); };  }).call(this, this.currentContainer));
5363				delayedTriggers.push((function(c) { return function(event) { c._trigger("update", event, this._uiHash(this));  }; }).call(this, this.currentContainer));
5364			}
5365		}
5366
5367
5368		//Post events to containers
5369		function delayEvent( type, instance, container ) {
5370			return function( event ) {
5371				container._trigger( type, event, instance._uiHash( instance ) );
5372			};
5373		}
5374		for (i = this.containers.length - 1; i >= 0; i--){
5375			if (!noPropagation) {
5376				delayedTriggers.push( delayEvent( "deactivate", this, this.containers[ i ] ) );
5377			}
5378			if(this.containers[i].containerCache.over) {
5379				delayedTriggers.push( delayEvent( "out", this, this.containers[ i ] ) );
5380				this.containers[i].containerCache.over = 0;
5381			}
5382		}
5383
5384		//Do what was originally in plugins
5385		if ( this.storedCursor ) {
5386			this.document.find( "body" ).css( "cursor", this.storedCursor );
5387			this.storedStylesheet.remove();
5388		}
5389		if(this._storedOpacity) {
5390			this.helper.css("opacity", this._storedOpacity);
5391		}
5392		if(this._storedZIndex) {
5393			this.helper.css("zIndex", this._storedZIndex === "auto" ? "" : this._storedZIndex);
5394		}
5395
5396		this.dragging = false;
5397
5398		if(!noPropagation) {
5399			this._trigger("beforeStop", event, this._uiHash());
5400		}
5401
5402		//$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
5403		this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
5404
5405		if ( !this.cancelHelperRemoval ) {
5406			if ( this.helper[ 0 ] !== this.currentItem[ 0 ] ) {
5407				this.helper.remove();
5408			}
5409			this.helper = null;
5410		}
5411
5412		if(!noPropagation) {
5413			for (i=0; i < delayedTriggers.length; i++) {
5414				delayedTriggers[i].call(this, event);
5415			} //Trigger all delayed events
5416			this._trigger("stop", event, this._uiHash());
5417		}
5418
5419		this.fromOutside = false;
vendor: 13,963 bytes, lines 5420-5993
5420		return !this.cancelHelperRemoval;
5421
5422	},
5423
5424	_trigger: function() {
5425		if ($.Widget.prototype._trigger.apply(this, arguments) === false) {
5426			this.cancel();
5427		}
5428	},
5429
5430	_uiHash: function(_inst) {
5431		var inst = _inst || this;
5432		return {
5433			helper: inst.helper,
5434			placeholder: inst.placeholder || $([]),
5435			position: inst.position,
5436			originalPosition: inst.originalPosition,
5437			offset: inst.positionAbs,
5438			item: inst.currentItem,
5439			sender: _inst ? _inst.element : null
5440		};
5441	}
5442
5443});
5444
5445}));
5446
5447//>>label: Effects Core
5448//>>group: Effects
5449//>>description: Extends the internal jQuery effects. Includes morphing and easing. Required by all other effects.
5450//>>docs: http://api.jqueryui.com/category/effects-core/
5451//>>demos: http://jqueryui.com/effect/
5452
5453(function( factory ) {
5454	if ( typeof define === "function" && define.amd ) {
5455
5456		// AMD. Register as an anonymous module.
5457		define( [ "jquery" ], factory );
5458	} else {
5459
5460		// Browser globals
5461		factory( jQuery );
5462	}
5463}(function( $ ) {
5464
5465var dataSpace = "ui-effects-",
5466	dataSpaceStyle = "ui-effects-style",
5467	dataSpaceAnimated = "ui-effects-animated",
5468
5469	// Create a local jQuery because jQuery Color relies on it and the
5470	// global may not exist with AMD and a custom build (#10199)
5471	jQuery = $;
5472
5473$.effects = {
5474	effect: {}
5475};
5476
5477/*!
5478 * jQuery Color Animations v2.1.2
5479 * https://github.com/jquery/jquery-color
5480 *
5481 * Copyright 2014 jQuery Foundation and other contributors
5482 * Released under the MIT license.
5483 * http://jquery.org/license
5484 *
5485 * Date: Wed Jan 16 08:47:09 2013 -0600
5486 */
5487(function( jQuery, undefined ) {
5488
5489	var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",
5490
5491	// plusequals test for += 100 -= 100
5492	rplusequals = /^([\-+])=\s*(\d+\.?\d*)/,
5493	// a set of RE's that can match strings and generate color tuples.
5494	stringParsers = [ {
5495			re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
5496			parse: function( execResult ) {
5497				return [
5498					execResult[ 1 ],
5499					execResult[ 2 ],
5500					execResult[ 3 ],
5501					execResult[ 4 ]
5502				];
5503			}
5504		}, {
5505			re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
5506			parse: function( execResult ) {
5507				return [
5508					execResult[ 1 ] * 2.55,
5509					execResult[ 2 ] * 2.55,
5510					execResult[ 3 ] * 2.55,
5511					execResult[ 4 ]
5512				];
5513			}
5514		}, {
5515			// this regex ignores A-F because it's compared against an already lowercased string
5516			re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,
5517			parse: function( execResult ) {
5518				return [
5519					parseInt( execResult[ 1 ], 16 ),
5520					parseInt( execResult[ 2 ], 16 ),
5521					parseInt( execResult[ 3 ], 16 )
5522				];
5523			}
5524		}, {
5525			// this regex ignores A-F because it's compared against an already lowercased string
5526			re: /#([a-f0-9])([a-f0-9])([a-f0-9])/,
5527			parse: function( execResult ) {
5528				return [
5529					parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ),
5530					parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ),
5531					parseInt( execResult[ 3 ] + execResult[ 3 ], 16 )
5532				];
5533			}
5534		}, {
5535			re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
5536			space: "hsla",
5537			parse: function( execResult ) {
5538				return [
5539					execResult[ 1 ],
5540					execResult[ 2 ] / 100,
5541					execResult[ 3 ] / 100,
5542					execResult[ 4 ]
5543				];
5544			}
5545		} ],
5546
5547	// jQuery.Color( )
5548	color = jQuery.Color = function( color, green, blue, alpha ) {
5549		return new jQuery.Color.fn.parse( color, green, blue, alpha );
5550	},
5551	spaces = {
5552		rgba: {
5553			props: {
5554				red: {
5555					idx: 0,
5556					type: "byte"
5557				},
5558				green: {
5559					idx: 1,
5560					type: "byte"
5561				},
5562				blue: {
5563					idx: 2,
5564					type: "byte"
5565				}
5566			}
5567		},
5568
5569		hsla: {
5570			props: {
5571				hue: {
5572					idx: 0,
5573					type: "degrees"
5574				},
5575				saturation: {
5576					idx: 1,
5577					type: "percent"
5578				},
5579				lightness: {
5580					idx: 2,
5581					type: "percent"
5582				}
5583			}
5584		}
5585	},
5586	propTypes = {
5587		"byte": {
5588			floor: true,
5589			max: 255
5590		},
5591		"percent": {
5592			max: 1
5593		},
5594		"degrees": {
5595			mod: 360,
5596			floor: true
5597		}
5598	},
5599	support = color.support = {},
5600
5601	// element for support tests
5602	supportElem = jQuery( "<p>" )[ 0 ],
5603
5604	// colors = jQuery.Color.names
5605	colors,
5606
5607	// local aliases of functions called often
5608	each = jQuery.each;
5609
5610// determine rgba support immediately
5611supportElem.style.cssText = "background-color:rgba(1,1,1,.5)";
5612support.rgba = supportElem.style.backgroundColor.indexOf( "rgba" ) > -1;
5613
5614// define cache name and alpha properties
5615// for rgba and hsla spaces
5616each( spaces, function( spaceName, space ) {
5617	space.cache = "_" + spaceName;
5618	space.props.alpha = {
5619		idx: 3,
5620		type: "percent",
5621		def: 1
5622	};
5623});
5624
5625function clamp( value, prop, allowEmpty ) {
5626	var type = propTypes[ prop.type ] || {};
5627
5628	if ( value == null ) {
5629		return (allowEmpty || !prop.def) ? null : prop.def;
5630	}
5631
5632	// ~~ is an short way of doing floor for positive numbers
5633	value = type.floor ? ~~value : parseFloat( value );
5634
5635	// IE will pass in empty strings as value for alpha,
5636	// which will hit this case
5637	if ( isNaN( value ) ) {
5638		return prop.def;
5639	}
5640
5641	if ( type.mod ) {
5642		// we add mod before modding to make sure that negatives values
5643		// get converted properly: -10 -> 350
5644		return (value + type.mod) % type.mod;
5645	}
5646
5647	// for now all property types without mod have min and max
5648	return 0 > value ? 0 : type.max < value ? type.max : value;
5649}
5650
5651function stringParse( string ) {
5652	var inst = color(),
5653		rgba = inst._rgba = [];
5654
5655	string = string.toLowerCase();
5656
5657	each( stringParsers, function( i, parser ) {
5658		var parsed,
5659			match = parser.re.exec( string ),
5660			values = match && parser.parse( match ),
5661			spaceName = parser.space || "rgba";
5662
5663		if ( values ) {
5664			parsed = inst[ spaceName ]( values );
5665
5666			// if this was an rgba parse the assignment might happen twice
5667			// oh well....
5668			inst[ spaces[ spaceName ].cache ] = parsed[ spaces[ spaceName ].cache ];
5669			rgba = inst._rgba = parsed._rgba;
5670
5671			// exit each( stringParsers ) here because we matched
5672			return false;
5673		}
5674	});
5675
5676	// Found a stringParser that handled it
5677	if ( rgba.length ) {
5678
5679		// if this came from a parsed string, force "transparent" when alpha is 0
5680		// chrome, (and maybe others) return "transparent" as rgba(0,0,0,0)
5681		if ( rgba.join() === "0,0,0,0" ) {
5682			jQuery.extend( rgba, colors.transparent );
5683		}
5684		return inst;
5685	}
5686
5687	// named colors
5688	return colors[ string ];
5689}
5690
5691color.fn = jQuery.extend( color.prototype, {
5692	parse: function( red, green, blue, alpha ) {
5693		if ( red === undefined ) {
5694			this._rgba = [ null, null, null, null ];
5695			return this;
5696		}
5697		if ( red.jquery || red.nodeType ) {
5698			red = jQuery( red ).css( green );
5699			green = undefined;
5700		}
5701
5702		var inst = this,
5703			type = jQuery.type( red ),
5704			rgba = this._rgba = [];
5705
5706		// more than 1 argument specified - assume ( red, green, blue, alpha )
5707		if ( green !== undefined ) {
5708			red = [ red, green, blue, alpha ];
5709			type = "array";
5710		}
5711
5712		if ( type === "string" ) {
5713			return this.parse( stringParse( red ) || colors._default );
5714		}
5715
5716		if ( type === "array" ) {
5717			each( spaces.rgba.props, function( key, prop ) {
5718				rgba[ prop.idx ] = clamp( red[ prop.idx ], prop );
5719			});
5720			return this;
5721		}
5722
5723		if ( type === "object" ) {
5724			if ( red instanceof color ) {
5725				each( spaces, function( spaceName, space ) {
5726					if ( red[ space.cache ] ) {
5727						inst[ space.cache ] = red[ space.cache ].slice();
5728					}
5729				});
5730			} else {
5731				each( spaces, function( spaceName, space ) {
5732					var cache = space.cache;
5733					each( space.props, function( key, prop ) {
5734
5735						// if the cache doesn't exist, and we know how to convert
5736						if ( !inst[ cache ] && space.to ) {
5737
5738							// if the value was null, we don't need to copy it
5739							// if the key was alpha, we don't need to copy it either
5740							if ( key === "alpha" || red[ key ] == null ) {
5741								return;
5742							}
5743							inst[ cache ] = space.to( inst._rgba );
5744						}
5745
5746						// this is the only case where we allow nulls for ALL properties.
5747						// call clamp with alwaysAllowEmpty
5748						inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true );
5749					});
5750
5751					// everything defined but alpha?
5752					if ( inst[ cache ] && jQuery.inArray( null, inst[ cache ].slice( 0, 3 ) ) < 0 ) {
5753						// use the default of 1
5754						inst[ cache ][ 3 ] = 1;
5755						if ( space.from ) {
5756							inst._rgba = space.from( inst[ cache ] );
5757						}
5758					}
5759				});
5760			}
5761			return this;
5762		}
5763	},
5764	is: function( compare ) {
5765		var is = color( compare ),
5766			same = true,
5767			inst = this;
5768
5769		each( spaces, function( _, space ) {
5770			var localCache,
5771				isCache = is[ space.cache ];
5772			if (isCache) {
5773				localCache = inst[ space.cache ] || space.to && space.to( inst._rgba ) || [];
5774				each( space.props, function( _, prop ) {
5775					if ( isCache[ prop.idx ] != null ) {
5776						same = ( isCache[ prop.idx ] === localCache[ prop.idx ] );
5777						return same;
5778					}
5779				});
5780			}
5781			return same;
5782		});
5783		return same;
5784	},
5785	_space: function() {
5786		var used = [],
5787			inst = this;
5788		each( spaces, function( spaceName, space ) {
5789			if ( inst[ space.cache ] ) {
5790				used.push( spaceName );
5791			}
5792		});
5793		return used.pop();
5794	},
5795	transition: function( other, distance ) {
5796		var end = color( other ),
5797			spaceName = end._space(),
5798			space = spaces[ spaceName ],
5799			startColor = this.alpha() === 0 ? color( "transparent" ) : this,
5800			start = startColor[ space.cache ] || space.to( startColor._rgba ),
5801			result = start.slice();
5802
5803		end = end[ space.cache ];
5804		each( space.props, function( key, prop ) {
5805			var index = prop.idx,
5806				startValue = start[ index ],
5807				endValue = end[ index ],
5808				type = propTypes[ prop.type ] || {};
5809
5810			// if null, don't override start value
5811			if ( endValue === null ) {
5812				return;
5813			}
5814			// if null - use end
5815			if ( startValue === null ) {
5816				result[ index ] = endValue;
5817			} else {
5818				if ( type.mod ) {
5819					if ( endValue - startValue > type.mod / 2 ) {
5820						startValue += type.mod;
5821					} else if ( startValue - endValue > type.mod / 2 ) {
5822						startValue -= type.mod;
5823					}
5824				}
5825				result[ index ] = clamp( ( endValue - startValue ) * distance + startValue, prop );
5826			}
5827		});
5828		return this[ spaceName ]( result );
5829	},
5830	blend: function( opaque ) {
5831		// if we are already opaque - return ourself
5832		if ( this._rgba[ 3 ] === 1 ) {
5833			return this;
5834		}
5835
5836		var rgb = this._rgba.slice(),
5837			a = rgb.pop(),
5838			blend = color( opaque )._rgba;
5839
5840		return color( jQuery.map( rgb, function( v, i ) {
5841			return ( 1 - a ) * blend[ i ] + a * v;
5842		}));
5843	},
5844	toRgbaString: function() {
5845		var prefix = "rgba(",
5846			rgba = jQuery.map( this._rgba, function( v, i ) {
5847				return v == null ? ( i > 2 ? 1 : 0 ) : v;
5848			});
5849
5850		if ( rgba[ 3 ] === 1 ) {
5851			rgba.pop();
5852			prefix = "rgb(";
5853		}
5854
5855		return prefix + rgba.join() + ")";
5856	},
5857	toHslaString: function() {
5858		var prefix = "hsla(",
5859			hsla = jQuery.map( this.hsla(), function( v, i ) {
5860				if ( v == null ) {
5861					v = i > 2 ? 1 : 0;
5862				}
5863
5864				// catch 1 and 2
5865				if ( i && i < 3 ) {
5866					v = Math.round( v * 100 ) + "%";
5867				}
5868				return v;
5869			});
5870
5871		if ( hsla[ 3 ] === 1 ) {
5872			hsla.pop();
5873			prefix = "hsl(";
5874		}
5875		return prefix + hsla.join() + ")";
5876	},
5877	toHexString: function( includeAlpha ) {
5878		var rgba = this._rgba.slice(),
5879			alpha = rgba.pop();
5880
5881		if ( includeAlpha ) {
5882			rgba.push( ~~( alpha * 255 ) );
5883		}
5884
5885		return "#" + jQuery.map( rgba, function( v ) {
5886
5887			// default to 0 when nulls exist
5888			v = ( v || 0 ).toString( 16 );
5889			return v.length === 1 ? "0" + v : v;
5890		}).join("");
5891	},
5892	toString: function() {
5893		return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString();
5894	}
5895});
5896color.fn.parse.prototype = color.fn;
5897
5898// hsla conversions adapted from:
5899// https://code.google.com/p/maashaack/source/browse/packages/graphics/trunk/src/graphics/colors/HUE2RGB.as?r=5021
5900
5901function hue2rgb( p, q, h ) {
5902	h = ( h + 1 ) % 1;
5903	if ( h * 6 < 1 ) {
5904		return p + ( q - p ) * h * 6;
5905	}
5906	if ( h * 2 < 1) {
5907		return q;
5908	}
5909	if ( h * 3 < 2 ) {
5910		return p + ( q - p ) * ( ( 2 / 3 ) - h ) * 6;
5911	}
5912	return p;
5913}
5914
5915spaces.hsla.to = function( rgba ) {
5916	if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) {
5917		return [ null, null, null, rgba[ 3 ] ];
5918	}
5919	var r = rgba[ 0 ] / 255,
5920		g = rgba[ 1 ] / 255,
5921		b = rgba[ 2 ] / 255,
5922		a = rgba[ 3 ],
5923		max = Math.max( r, g, b ),
5924		min = Math.min( r, g, b ),
5925		diff = max - min,
5926		add = max + min,
5927		l = add * 0.5,
5928		h, s;
5929
5930	if ( min === max ) {
5931		h = 0;
5932	} else if ( r === max ) {
5933		h = ( 60 * ( g - b ) / diff ) + 360;
5934	} else if ( g === max ) {
5935		h = ( 60 * ( b - r ) / diff ) + 120;
5936	} else {
5937		h = ( 60 * ( r - g ) / diff ) + 240;
5938	}
5939
5940	// chroma (diff) == 0 means greyscale which, by definition, saturation = 0%
5941	// otherwise, saturation is based on the ratio of chroma (diff) to lightness (add)
5942	if ( diff === 0 ) {
5943		s = 0;
5944	} else if ( l <= 0.5 ) {
5945		s = diff / add;
5946	} else {
5947		s = diff / ( 2 - add );
5948	}
5949	return [ Math.round(h) % 360, s, l, a == null ? 1 : a ];
5950};
5951
5952spaces.hsla.from = function( hsla ) {
5953	if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) {
5954		return [ null, null, null, hsla[ 3 ] ];
5955	}
5956	var h = hsla[ 0 ] / 360,
5957		s = hsla[ 1 ],
5958		l = hsla[ 2 ],
5959		a = hsla[ 3 ],
5960		q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s,
5961		p = 2 * l - q;
5962
5963	return [
5964		Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ),
5965		Math.round( hue2rgb( p, q, h ) * 255 ),
5966		Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ),
5967		a
5968	];
5969};
5970
5971each( spaces, function( spaceName, space ) {
5972	var props = space.props,
5973		cache = space.cache,
5974		to = space.to,
5975		from = space.from;
5976
5977	// makes rgba() and hsla()
5978	color.fn[ spaceName ] = function( value ) {
5979
5980		// generate a cache for this space if it doesn't exist
5981		if ( to && !this[ cache ] ) {
5982			this[ cache ] = to( this._rgba );
5983		}
5984		if ( value === undefined ) {
5985			return this[ cache ].slice();
5986		}
5987
5988		var ret,
5989			type = jQuery.type( value ),
5990			arr = ( type === "array" || type === "object" ) ? value : arguments,
5991			local = this[ cache ].slice();
5992
5993		each( props, function( key, prop ) {
vendor: 10,120 bytes, lines 5994-6387
5994			var val = arr[ type === "object" ? key : prop.idx ];
5995			if ( val == null ) {
5996				val = local[ prop.idx ];
5997			}
5998			local[ prop.idx ] = clamp( val, prop );
5999		});
6000
6001		if ( from ) {
6002			ret = color( from( local ) );
6003			ret[ cache ] = local;
6004			return ret;
6005		} else {
6006			return color( local );
6007		}
6008	};
6009
6010	// makes red() green() blue() alpha() hue() saturation() lightness()
6011	each( props, function( key, prop ) {
6012		// alpha is included in more than one space
6013		if ( color.fn[ key ] ) {
6014			return;
6015		}
6016		color.fn[ key ] = function( value ) {
6017			var vtype = jQuery.type( value ),
6018				fn = ( key === "alpha" ? ( this._hsla ? "hsla" : "rgba" ) : spaceName ),
6019				local = this[ fn ](),
6020				cur = local[ prop.idx ],
6021				match;
6022
6023			if ( vtype === "undefined" ) {
6024				return cur;
6025			}
6026
6027			if ( vtype === "function" ) {
6028				value = value.call( this, cur );
6029				vtype = jQuery.type( value );
6030			}
6031			if ( value == null && prop.empty ) {
6032				return this;
6033			}
6034			if ( vtype === "string" ) {
6035				match = rplusequals.exec( value );
6036				if ( match ) {
6037					value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 );
6038				}
6039			}
6040			local[ prop.idx ] = value;
6041			return this[ fn ]( local );
6042		};
6043	});
6044});
6045
6046// add cssHook and .fx.step function for each named hook.
6047// accept a space separated string of properties
6048color.hook = function( hook ) {
6049	var hooks = hook.split( " " );
6050	each( hooks, function( i, hook ) {
6051		jQuery.cssHooks[ hook ] = {
6052			set: function( elem, value ) {
6053				var parsed, curElem,
6054					backgroundColor = "";
6055
6056				if ( value !== "transparent" && ( jQuery.type( value ) !== "string" || ( parsed = stringParse( value ) ) ) ) {
6057					value = color( parsed || value );
6058					if ( !support.rgba && value._rgba[ 3 ] !== 1 ) {
6059						curElem = hook === "backgroundColor" ? elem.parentNode : elem;
6060						while (
6061							(backgroundColor === "" || backgroundColor === "transparent") &&
6062							curElem && curElem.style
6063						) {
6064							try {
6065								backgroundColor = jQuery.css( curElem, "backgroundColor" );
6066								curElem = curElem.parentNode;
6067							} catch ( e ) {
6068							}
6069						}
6070
6071						value = value.blend( backgroundColor && backgroundColor !== "transparent" ?
6072							backgroundColor :
6073							"_default" );
6074					}
6075
6076					value = value.toRgbaString();
6077				}
6078				try {
6079					elem.style[ hook ] = value;
6080				} catch ( e ) {
6081					// wrapped to prevent IE from throwing errors on "invalid" values like 'auto' or 'inherit'
6082				}
6083			}
6084		};
6085		jQuery.fx.step[ hook ] = function( fx ) {
6086			if ( !fx.colorInit ) {
6087				fx.start = color( fx.elem, hook );
6088				fx.end = color( fx.end );
6089				fx.colorInit = true;
6090			}
6091			jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) );
6092		};
6093	});
6094
6095};
6096
6097color.hook( stepHooks );
6098
6099jQuery.cssHooks.borderColor = {
6100	expand: function( value ) {
6101		var expanded = {};
6102
6103		each( [ "Top", "Right", "Bottom", "Left" ], function( i, part ) {
6104			expanded[ "border" + part + "Color" ] = value;
6105		});
6106		return expanded;
6107	}
6108};
6109
6110// Basic color names only.
6111// Usage of any of the other color names requires adding yourself or including
6112// jquery.color.svg-names.js.
6113colors = jQuery.Color.names = {
6114	// 4.1. Basic color keywords
6115	aqua: "#00ffff",
6116	black: "#000000",
6117	blue: "#0000ff",
6118	fuchsia: "#ff00ff",
6119	gray: "#808080",
6120	green: "#008000",
6121	lime: "#00ff00",
6122	maroon: "#800000",
6123	navy: "#000080",
6124	olive: "#808000",
6125	purple: "#800080",
6126	red: "#ff0000",
6127	silver: "#c0c0c0",
6128	teal: "#008080",
6129	white: "#ffffff",
6130	yellow: "#ffff00",
6131
6132	// 4.2.3. "transparent" color keyword
6133	transparent: [ null, null, null, 0 ],
6134
6135	_default: "#ffffff"
6136};
6137
6138})( jQuery );
6139
6140/******************************************************************************/
6141/****************************** CLASS ANIMATIONS ******************************/
6142/******************************************************************************/
6143(function() {
6144
6145var classAnimationActions = [ "add", "remove", "toggle" ],
6146	shorthandStyles = {
6147		border: 1,
6148		borderBottom: 1,
6149		borderColor: 1,
6150		borderLeft: 1,
6151		borderRight: 1,
6152		borderTop: 1,
6153		borderWidth: 1,
6154		margin: 1,
6155		padding: 1
6156	};
6157
6158$.each([ "borderLeftStyle", "borderRightStyle", "borderBottomStyle", "borderTopStyle" ], function( _, prop ) {
6159	$.fx.step[ prop ] = function( fx ) {
6160		if ( fx.end !== "none" && !fx.setAttr || fx.pos === 1 && !fx.setAttr ) {
6161			jQuery.style( fx.elem, prop, fx.end );
6162			fx.setAttr = true;
6163		}
6164	};
6165});
6166
6167function getElementStyles( elem ) {
6168	var key, len,
6169		style = elem.ownerDocument.defaultView ?
6170			elem.ownerDocument.defaultView.getComputedStyle( elem, null ) :
6171			elem.currentStyle,
6172		styles = {};
6173
6174	if ( style && style.length && style[ 0 ] && style[ style[ 0 ] ] ) {
6175		len = style.length;
6176		while ( len-- ) {
6177			key = style[ len ];
6178			if ( typeof style[ key ] === "string" ) {
6179				styles[ $.camelCase( key ) ] = style[ key ];
6180			}
6181		}
6182	// support: Opera, IE <9
6183	} else {
6184		for ( key in style ) {
6185			if ( typeof style[ key ] === "string" ) {
6186				styles[ key ] = style[ key ];
6187			}
6188		}
6189	}
6190
6191	return styles;
6192}
6193
6194function styleDifference( oldStyle, newStyle ) {
6195	var diff = {},
6196		name, value;
6197
6198	for ( name in newStyle ) {
6199		value = newStyle[ name ];
6200		if ( oldStyle[ name ] !== value ) {
6201			if ( !shorthandStyles[ name ] ) {
6202				if ( $.fx.step[ name ] || !isNaN( parseFloat( value ) ) ) {
6203					diff[ name ] = value;
6204				}
6205			}
6206		}
6207	}
6208
6209	return diff;
6210}
6211
6212// support: jQuery <1.8
6213if ( !$.fn.addBack ) {
6214	$.fn.addBack = function( selector ) {
6215		return this.add( selector == null ?
6216			this.prevObject : this.prevObject.filter( selector )
6217		);
6218	};
6219}
6220
6221$.effects.animateClass = function( value, duration, easing, callback ) {
6222	var o = $.speed( duration, easing, callback );
6223
6224	return this.queue( function() {
6225		var animated = $( this ),
6226			baseClass = animated.attr( "class" ) || "",
6227			applyClassChange,
6228			allAnimations = o.children ? animated.find( "*" ).addBack() : animated;
6229
6230		// map the animated objects to store the original styles.
6231		allAnimations = allAnimations.map(function() {
6232			var el = $( this );
6233			return {
6234				el: el,
6235				start: getElementStyles( this )
6236			};
6237		});
6238
6239		// apply class change
6240		applyClassChange = function() {
6241			$.each( classAnimationActions, function(i, action) {
6242				if ( value[ action ] ) {
6243					animated[ action + "Class" ]( value[ action ] );
6244				}
6245			});
6246		};
6247		applyClassChange();
6248
6249		// map all animated objects again - calculate new styles and diff
6250		allAnimations = allAnimations.map(function() {
6251			this.end = getElementStyles( this.el[ 0 ] );
6252			this.diff = styleDifference( this.start, this.end );
6253			return this;
6254		});
6255
6256		// apply original class
6257		animated.attr( "class", baseClass );
6258
6259		// map all animated objects again - this time collecting a promise
6260		allAnimations = allAnimations.map(function() {
6261			var styleInfo = this,
6262				dfd = $.Deferred(),
6263				opts = $.extend({}, o, {
6264					queue: false,
6265					complete: function() {
6266						dfd.resolve( styleInfo );
6267					}
6268				});
6269
6270			this.el.animate( this.diff, opts );
6271			return dfd.promise();
6272		});
6273
6274		// once all animations have completed:
6275		$.when.apply( $, allAnimations.get() ).done(function() {
6276
6277			// set the final class
6278			applyClassChange();
6279
6280			// for each animated element,
6281			// clear all css properties that were animated
6282			$.each( arguments, function() {
6283				var el = this.el;
6284				$.each( this.diff, function(key) {
6285					el.css( key, "" );
6286				});
6287			});
6288
6289			// this is guarnteed to be there if you use jQuery.speed()
6290			// it also handles dequeuing the next anim...
6291			o.complete.call( animated[ 0 ] );
6292		});
6293	});
6294};
6295
6296$.fn.extend({
6297	addClass: (function( orig ) {
6298		return function( classNames, speed, easing, callback ) {
6299			return speed ?
6300				$.effects.animateClass.call( this,
6301					{ add: classNames }, speed, easing, callback ) :
6302				orig.apply( this, arguments );
6303		};
6304	})( $.fn.addClass ),
6305
6306	removeClass: (function( orig ) {
6307		return function( classNames, speed, easing, callback ) {
6308			return arguments.length > 1 ?
6309				$.effects.animateClass.call( this,
6310					{ remove: classNames }, speed, easing, callback ) :
6311				orig.apply( this, arguments );
6312		};
6313	})( $.fn.removeClass ),
6314
6315	toggleClass: (function( orig ) {
6316		return function( classNames, force, speed, easing, callback ) {
6317			if ( typeof force === "boolean" || force === undefined ) {
6318				if ( !speed ) {
6319					// without speed parameter
6320					return orig.apply( this, arguments );
6321				} else {
6322					return $.effects.animateClass.call( this,
6323						(force ? { add: classNames } : { remove: classNames }),
6324						speed, easing, callback );
6325				}
6326			} else {
6327				// without force parameter
6328				return $.effects.animateClass.call( this,
6329					{ toggle: classNames }, force, speed, easing );
6330			}
6331		};
6332	})( $.fn.toggleClass ),
6333
6334	switchClass: function( remove, add, speed, easing, callback) {
6335		return $.effects.animateClass.call( this, {
6336			add: add,
6337			remove: remove
6338		}, speed, easing, callback );
6339	}
6340});
6341
6342})();
6343
6344/******************************************************************************/
6345/*********************************** EFFECTS **********************************/
6346/******************************************************************************/
6347
6348(function() {
6349
6350if ( $.expr && $.expr.filters && $.expr.filters.animated ) {
6351	$.expr.filters.animated = (function( orig ) {
6352		return function( elem ) {
6353			return !!$( elem ).data( dataSpaceAnimated ) || orig( elem );
6354		};
6355	})( $.expr.filters.animated );
6356}
6357
6358if ( $.uiBackCompat !== false ) {
6359	$.extend( $.effects, {
6360		// Saves a set of properties in a data storage
6361		save: function( element, set ) {
6362			var i = 0, length = set.length;
6363			for ( ; i < length; i++ ) {
6364				if ( set[ i ] !== null ) {
6365					element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] );
6366				}
6367			}
6368		},
6369
6370		// Restores a set of previously saved properties from a data storage
6371		restore: function( element, set ) {
6372			var val, i = 0, length = set.length;
6373			for ( ; i < length; i++ ) {
6374				if ( set[ i ] !== null ) {
6375					val = element.data( dataSpace + set[ i ] );
6376					element.css( set[ i ], val );
6377				}
6378			}
6379		},
6380
6381		setMode: function( el, mode ) {
6382			if ( mode === "toggle" ) {
6383				mode = el.is( ":hidden" ) ? "show" : "hide";
6384			}
6385			return mode;
6386		},
6387
vendor: 4,548 bytes, lines 6388-6577
6388		// Wraps the element around a wrapper that copies position properties
6389		createWrapper: function( element ) {
6390
6391			// if the element is already wrapped, return it
6392			if ( element.parent().is( ".ui-effects-wrapper" ) ) {
6393				return element.parent();
6394			}
6395
6396			// wrap the element
6397			var props = {
6398					width: element.outerWidth( true ),
6399					height: element.outerHeight( true ),
6400					"float": element.css( "float" )
6401				},
6402				wrapper = $( "<div></div>" )
6403					.addClass( "ui-effects-wrapper" )
6404					.css({
6405						fontSize: "100%",
6406						background: "transparent",
6407						border: "none",
6408						margin: 0,
6409						padding: 0
6410					}),
6411				// Store the size in case width/height are defined in % - Fixes #5245
6412				size = {
6413					width: element.width(),
6414					height: element.height()
6415				},
6416				active = document.activeElement;
6417
6418			// support: Firefox
6419			// Firefox incorrectly exposes anonymous content
6420			// https://bugzilla.mozilla.org/show_bug.cgi?id=561664
6421			try {
6422				active.id;
6423			} catch ( e ) {
6424				active = document.body;
6425			}
6426
6427			element.wrap( wrapper );
6428
6429			// Fixes #7595 - Elements lose focus when wrapped.
6430			if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
6431				$( active ).focus();
6432			}
6433
6434			wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually lose the reference to the wrapped element
6435
6436			// transfer positioning properties to the wrapper
6437			if ( element.css( "position" ) === "static" ) {
6438				wrapper.css({ position: "relative" });
6439				element.css({ position: "relative" });
6440			} else {
6441				$.extend( props, {
6442					position: element.css( "position" ),
6443					zIndex: element.css( "z-index" )
6444				});
6445				$.each([ "top", "left", "bottom", "right" ], function(i, pos) {
6446					props[ pos ] = element.css( pos );
6447					if ( isNaN( parseInt( props[ pos ], 10 ) ) ) {
6448						props[ pos ] = "auto";
6449					}
6450				});
6451				element.css({
6452					position: "relative",
6453					top: 0,
6454					left: 0,
6455					right: "auto",
6456					bottom: "auto"
6457				});
6458			}
6459			element.css(size);
6460
6461			return wrapper.css( props ).show();
6462		},
6463
6464		removeWrapper: function( element ) {
6465			var active = document.activeElement;
6466
6467			if ( element.parent().is( ".ui-effects-wrapper" ) ) {
6468				element.parent().replaceWith( element );
6469
6470				// Fixes #7595 - Elements lose focus when wrapped.
6471				if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
6472					$( active ).focus();
6473				}
6474			}
6475
6476			return element;
6477		}
6478	});
6479}
6480
6481$.extend( $.effects, {
6482	version: "@VERSION",
6483
6484	define: function( name, mode, effect ) {
6485		if ( !effect ) {
6486			effect = mode;
6487			mode = "effect";
6488		}
6489
6490		$.effects.effect[ name ] = effect;
6491		$.effects.effect[ name ].mode = mode;
6492
6493		return effect;
6494	},
6495
6496	scaledDimensions: function( element, percent, direction ) {
6497		if ( percent === 0 ) {
6498			return {
6499				height: 0,
6500				width: 0,
6501				outerHeight: 0,
6502				outerWidth: 0
6503			};
6504		}
6505
6506		var x = direction !== "horizontal" ? ( ( percent || 100 ) / 100 ) : 1,
6507			y = direction !== "vertical" ? ( ( percent || 100 ) / 100 ) : 1;
6508
6509		return {
6510			height: element.height() * y,
6511			width: element.width() * x,
6512			outerHeight: element.outerHeight() * y,
6513			outerWidth: element.outerWidth() * x
6514		};
6515
6516	},
6517
6518	clipToBox: function( animation ) {
6519		return {
6520			width: animation.clip.right - animation.clip.left,
6521			height: animation.clip.bottom - animation.clip.top,
6522			left: animation.clip.left,
6523			top: animation.clip.top
6524		};
6525	},
6526
6527	// Injects recently queued functions to be first in line (after "inprogress")
6528	unshift: function( element, queueLength, count ) {
6529		var queue = element.queue();
6530
6531		if ( queueLength > 1 ) {
6532			queue.splice.apply( queue,
6533				[ 1, 0 ].concat( queue.splice( queueLength, count ) ) );
6534		}
6535		element.dequeue();
6536	},
6537
6538	saveStyle: function( element ) {
6539		element.data( dataSpaceStyle, element[ 0 ].style.cssText );
6540	},
6541
6542	restoreStyle: function( element ) {
6543		element[ 0 ].style.cssText = element.data( dataSpaceStyle ) || "";
6544		element.removeData( dataSpaceStyle );
6545	},
6546
6547	mode: function( element, mode ) {
6548		var hidden = element.is( ":hidden" );
6549
6550		if ( mode === "toggle" ) {
6551			mode = hidden ? "show" : "hide";
6552		}
6553		if ( hidden ? mode === "hide" : mode === "show" ) {
6554			mode = "none";
6555		}
6556		return mode;
6557	},
6558
6559	// Translates a [top,left] array into a baseline value
6560	getBaseline: function( origin, original ) {
6561		var y, x;
6562
6563		switch ( origin[ 0 ] ) {
6564		case "top":
6565			y = 0;
6566			break;
6567		case "middle":
6568			y = 0.5;
6569			break;
6570		case "bottom":
6571			y = 1;
6572			break;
6573		default:
6574			y = origin[ 0 ] / original.height;
6575		}
6576
6577		switch ( origin[ 1 ] ) {
vendor: 13,423 bytes, lines 6578-7098
6578		case "left":
6579			x = 0;
6580			break;
6581		case "center":
6582			x = 0.5;
6583			break;
6584		case "right":
6585			x = 1;
6586			break;
6587		default:
6588			x = origin[ 1 ] / original.width;
6589		}
6590
6591		return {
6592			x: x,
6593			y: y
6594		};
6595	},
6596
6597	// Creates a placeholder element so that the original element can be made absolute
6598	createPlaceholder: function( element ) {
6599		var placeholder,
6600			cssPosition = element.css( "position" ),
6601			position = element.position();
6602
6603		// Lock in margins first to account for form elements, which
6604		// will change margin if you explicitly set height
6605		// see: http://jsfiddle.net/JZSMt/3/ https://bugs.webkit.org/show_bug.cgi?id=107380
6606		// Support: Safari
6607		element.css({
6608			marginTop: element.css( "marginTop" ),
6609			marginBottom: element.css( "marginBottom" ),
6610			marginLeft: element.css( "marginLeft" ),
6611			marginRight: element.css( "marginRight" )
6612		})
6613		.outerWidth( element.outerWidth() )
6614		.outerHeight( element.outerHeight() );
6615
6616		if ( /^(static|relative)/.test( cssPosition ) ) {
6617			cssPosition = "absolute";
6618
6619			placeholder = $( "<" + element[ 0 ].nodeName + ">" ).insertAfter( element ).css({
6620
6621				// Convert inline to inline block to account for inline elements
6622				// that turn to inline block based on content (like img)
6623				display: /^(inline|ruby)/.test( element.css( "display" ) ) ? "inline-block" : "block",
6624				visibility: "hidden",
6625
6626				// Margins need to be set to account for margin collapse
6627				marginTop: element.css( "marginTop" ),
6628				marginBottom: element.css( "marginBottom" ),
6629				marginLeft: element.css( "marginLeft" ),
6630				marginRight: element.css( "marginRight" ),
6631				"float": element.css( "float" )
6632			})
6633			.outerWidth( element.outerWidth() )
6634			.outerHeight( element.outerHeight() )
6635			.addClass( "ui-effects-placeholder" );
6636
6637			element.data( dataSpace + "placeholder", placeholder );
6638		}
6639
6640		element.css({
6641			position: cssPosition,
6642			left: position.left,
6643			top: position.top
6644		});
6645
6646		return placeholder;
6647	},
6648
6649	removePlaceholder: function( element ) {
6650		var dataKey = dataSpace + "placeholder",
6651				placeholder = element.data( dataKey );
6652
6653		if ( placeholder ) {
6654			placeholder.remove();
6655			element.removeData( dataKey );
6656		}
6657	},
6658
6659	// Removes a placeholder if it exists and restores
6660	// properties that were modified during placeholder creation
6661	cleanUp: function( element ) {
6662		$.effects.restoreStyle( element );
6663		$.effects.removePlaceholder( element );
6664	},
6665
6666	setTransition: function( element, list, factor, value ) {
6667		value = value || {};
6668		$.each( list, function( i, x ) {
6669			var unit = element.cssUnit( x );
6670			if ( unit[ 0 ] > 0 ) {
6671				value[ x ] = unit[ 0 ] * factor + unit[ 1 ];
6672			}
6673		});
6674		return value;
6675	}
6676});
6677
6678// return an effect options object for the given parameters:
6679function _normalizeArguments( effect, options, speed, callback ) {
6680
6681	// allow passing all options as the first parameter
6682	if ( $.isPlainObject( effect ) ) {
6683		options = effect;
6684		effect = effect.effect;
6685	}
6686
6687	// convert to an object
6688	effect = { effect: effect };
6689
6690	// catch (effect, null, ...)
6691	if ( options == null ) {
6692		options = {};
6693	}
6694
6695	// catch (effect, callback)
6696	if ( $.isFunction( options ) ) {
6697		callback = options;
6698		speed = null;
6699		options = {};
6700	}
6701
6702	// catch (effect, speed, ?)
6703	if ( typeof options === "number" || $.fx.speeds[ options ] ) {
6704		callback = speed;
6705		speed = options;
6706		options = {};
6707	}
6708
6709	// catch (effect, options, callback)
6710	if ( $.isFunction( speed ) ) {
6711		callback = speed;
6712		speed = null;
6713	}
6714
6715	// add options to effect
6716	if ( options ) {
6717		$.extend( effect, options );
6718	}
6719
6720	speed = speed || options.duration;
6721	effect.duration = $.fx.off ? 0 :
6722		typeof speed === "number" ? speed :
6723		speed in $.fx.speeds ? $.fx.speeds[ speed ] :
6724		$.fx.speeds._default;
6725
6726	effect.complete = callback || options.complete;
6727
6728	return effect;
6729}
6730
6731function standardAnimationOption( option ) {
6732	// Valid standard speeds (nothing, number, named speed)
6733	if ( !option || typeof option === "number" || $.fx.speeds[ option ] ) {
6734		return true;
6735	}
6736
6737	// Invalid strings - treat as "normal" speed
6738	if ( typeof option === "string" && !$.effects.effect[ option ] ) {
6739		return true;
6740	}
6741
6742	// Complete callback
6743	if ( $.isFunction( option ) ) {
6744		return true;
6745	}
6746
6747	// Options hash (but not naming an effect)
6748	if ( typeof option === "object" && !option.effect ) {
6749		return true;
6750	}
6751
6752	// Didn't match any standard API
6753	return false;
6754}
6755
6756$.fn.extend({
6757	effect: function( /* effect, options, speed, callback */ ) {
6758		var args = _normalizeArguments.apply( this, arguments ),
6759			effectMethod = $.effects.effect[ args.effect ],
6760			defaultMode = effectMethod.mode,
6761			queue = args.queue,
6762			queueName = queue || "fx",
6763			complete = args.complete,
6764			mode = args.mode,
6765			modes = [],
6766			prefilter = function( next ) {
6767				var el = $( this ),
6768					normalizedMode = $.effects.mode( el, mode ) || defaultMode;
6769
6770				// Sentinel for duck-punching the :animated psuedo-selector
6771				el.data( dataSpaceAnimated, true );
6772
6773				// Save effect mode for later use,
6774				// we can't just call $.effects.mode again later,
6775				// as the .show() below destroys the initial state
6776				modes.push( normalizedMode );
6777
6778				// See $.uiBackCompat inside of run() for removal of defaultMode in 1.13
6779				if ( defaultMode && ( normalizedMode === "show" ||
6780						( normalizedMode === defaultMode && normalizedMode === "hide" ) ) ) {
6781					el.show();
6782				}
6783
6784				if ( !defaultMode || normalizedMode !== "none" ) {
6785					$.effects.saveStyle( el );
6786				}
6787
6788				if ( $.isFunction( next ) ) {
6789					next();
6790				}
6791			};
6792
6793		if ( $.fx.off || !effectMethod ) {
6794			// delegate to the original method (e.g., .show()) if possible
6795			if ( mode ) {
6796				return this[ mode ]( args.duration, complete );
6797			} else {
6798				return this.each( function() {
6799					if ( complete ) {
6800						complete.call( this );
6801					}
6802				});
6803			}
6804		}
6805
6806		function run( next ) {
6807			var elem = $( this );
6808
6809			function cleanup() {
6810				elem.removeData( dataSpaceAnimated );
6811
6812				$.effects.cleanUp( elem );
6813
6814				if ( args.mode === "hide" ) {
6815					elem.hide();
6816				}
6817
6818				done();
6819			}
6820
6821			function done() {
6822				if ( $.isFunction( complete ) ) {
6823					complete.call( elem[ 0 ] );
6824				}
6825
6826				if ( $.isFunction( next ) ) {
6827					next();
6828				}
6829			}
6830
6831			// Override mode option on a per element basis,
6832			// as toggle can be either show or hide depending on element state
6833			args.mode = modes.shift();
6834
6835			if ( $.uiBackCompat !== false && !defaultMode ) {
6836				if ( elem.is( ":hidden" ) ? mode === "hide" : mode === "show" ) {
6837
6838					// Call the core method to track "olddisplay" properly
6839					elem[ mode ]();
6840					done();
6841				} else {
6842					effectMethod.call( elem[ 0 ], args, done );
6843				}
6844			} else {
6845				if ( args.mode === "none" ) {
6846
6847					// Call the core method to track "olddisplay" properly
6848					elem[ mode ]();
6849					done();
6850				} else {
6851					effectMethod.call( elem[ 0 ], args, cleanup );
6852				}
6853			}
6854		}
6855
6856		// Run prefilter on all elements first to ensure that
6857		// any showing or hiding happens before placeholder creation,
6858		// which ensures that any layout changes are correctly captured.
6859		return queue === false ?
6860			this.each( prefilter ).each( run ) :
6861			this.queue( queueName, prefilter ).queue( queueName, run );
6862	},
6863
6864	show: (function( orig ) {
6865		return function( option ) {
6866			if ( standardAnimationOption( option ) ) {
6867				return orig.apply( this, arguments );
6868			} else {
6869				var args = _normalizeArguments.apply( this, arguments );
6870				args.mode = "show";
6871				return this.effect.call( this, args );
6872			}
6873		};
6874	})( $.fn.show ),
6875
6876	hide: (function( orig ) {
6877		return function( option ) {
6878			if ( standardAnimationOption( option ) ) {
6879				return orig.apply( this, arguments );
6880			} else {
6881				var args = _normalizeArguments.apply( this, arguments );
6882				args.mode = "hide";
6883				return this.effect.call( this, args );
6884			}
6885		};
6886	})( $.fn.hide ),
6887
6888	toggle: (function( orig ) {
6889		return function( option ) {
6890			if ( standardAnimationOption( option ) || typeof option === "boolean" ) {
6891				return orig.apply( this, arguments );
6892			} else {
6893				var args = _normalizeArguments.apply( this, arguments );
6894				args.mode = "toggle";
6895				return this.effect.call( this, args );
6896			}
6897		};
6898	})( $.fn.toggle ),
6899
6900	cssUnit: function(key) {
6901		var style = this.css( key ),
6902			val = [];
6903
6904		$.each( [ "em", "px", "%", "pt" ], function( i, unit ) {
6905			if ( style.indexOf( unit ) > 0 ) {
6906				val = [ parseFloat( style ), unit ];
6907			}
6908		});
6909		return val;
6910	},
6911
6912	cssClip: function( clipObj ) {
6913		return clipObj ?
6914			this.css( "clip", "rect(" + clipObj.top + "px " + clipObj.right + "px " + clipObj.bottom + "px " + clipObj.left + "px)" ) :
6915			parseClip( this.css("clip"), this );
6916	},
6917
6918	transfer: function( options, done ) {
6919		var element = $( this ),
6920			target = $( options.to ),
6921			targetFixed = target.css( "position" ) === "fixed",
6922			body = $( "body" ),
6923			fixTop = targetFixed ? body.scrollTop() : 0,
6924			fixLeft = targetFixed ? body.scrollLeft() : 0,
6925			endPosition = target.offset(),
6926			animation = {
6927				top: endPosition.top - fixTop,
6928				left: endPosition.left - fixLeft,
6929				height: target.innerHeight(),
6930				width: target.innerWidth()
6931			},
6932			startPosition = element.offset(),
6933			transfer = $( "<div class='ui-effects-transfer'></div>" )
6934				.appendTo( "body" )
6935				.addClass( options.className )
6936				.css({
6937					top: startPosition.top - fixTop,
6938					left: startPosition.left - fixLeft,
6939					height: element.innerHeight(),
6940					width: element.innerWidth(),
6941					position: targetFixed ? "fixed" : "absolute"
6942				})
6943				.animate( animation, options.duration, options.easing, function() {
6944					transfer.remove();
6945					done();
6946				});
6947	}
6948});
6949
6950function parseClip( str, element ) {
6951		var outerWidth = element.outerWidth(),
6952			outerHeight = element.outerHeight(),
6953			clipRegex = /^rect\((-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto)\)$/,
6954			values = clipRegex.exec( str ) || [ "", 0, outerWidth, outerHeight, 0 ];
6955
6956		return {
6957			top: parseFloat( values[ 1 ] ) || 0,
6958			right: values[ 2 ] === "auto" ? outerWidth : parseFloat( values[ 2 ] ),
6959			bottom: values[ 3 ] === "auto" ? outerHeight : parseFloat( values[ 3 ] ),
6960			left: parseFloat( values[ 4 ] ) || 0
6961		};
6962}
6963
6964$.fx.step.clip = function( fx ) {
6965	if ( !fx.clipInit ) {
6966		fx.start = $( fx.elem ).cssClip();
6967		if ( typeof fx.end === "string" ) {
6968			fx.end = parseClip( fx.end, fx.elem );
6969		}
6970		fx.clipInit = true;
6971	}
6972
6973	$( fx.elem ).cssClip({
6974		top: fx.pos * (fx.end.top - fx.start.top) + fx.start.top,
6975		right: fx.pos * (fx.end.right - fx.start.right) + fx.start.right,
6976		bottom: fx.pos * (fx.end.bottom - fx.start.bottom) + fx.start.bottom,
6977		left: fx.pos * (fx.end.left - fx.start.left) + fx.start.left
6978	});
6979};
6980
6981})();
6982
6983/******************************************************************************/
6984/*********************************** EASING ***********************************/
6985/******************************************************************************/
6986
6987(function() {
6988
6989// based on easing equations from Robert Penner (http://www.robertpenner.com/easing)
6990
6991var baseEasings = {};
6992
6993$.each( [ "Quad", "Cubic", "Quart", "Quint", "Expo" ], function( i, name ) {
6994	baseEasings[ name ] = function( p ) {
6995		return Math.pow( p, i + 2 );
6996	};
6997});
6998
6999$.extend( baseEasings, {
7000	Sine: function( p ) {
7001		return 1 - Math.cos( p * Math.PI / 2 );
7002	},
7003	Circ: function( p ) {
7004		return 1 - Math.sqrt( 1 - p * p );
7005	},
7006	Elastic: function( p ) {
7007		return p === 0 || p === 1 ? p :
7008			-Math.pow( 2, 8 * (p - 1) ) * Math.sin( ( (p - 1) * 80 - 7.5 ) * Math.PI / 15 );
7009	},
7010	Back: function( p ) {
7011		return p * p * ( 3 * p - 2 );
7012	},
7013	Bounce: function( p ) {
7014		var pow2,
7015			bounce = 4;
7016
7017		while ( p < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {}
7018		return 1 / Math.pow( 4, 3 - bounce ) - 7.5625 * Math.pow( ( pow2 * 3 - 2 ) / 22 - p, 2 );
7019	}
7020});
7021
7022$.each( baseEasings, function( name, easeIn ) {
7023	$.easing[ "easeIn" + name ] = easeIn;
7024	$.easing[ "easeOut" + name ] = function( p ) {
7025		return 1 - easeIn( 1 - p );
7026	};
7027	$.easing[ "easeInOut" + name ] = function( p ) {
7028		return p < 0.5 ?
7029			easeIn( p * 2 ) / 2 :
7030			1 - easeIn( p * -2 + 2 ) / 2;
7031	};
7032});
7033
7034})();
7035
7036return $.effects;
7037
7038}));
7039
7040//>>label: Accordion
7041//>>group: Widgets
7042//>>description: Displays collapsible content panels for presenting information in a limited amount of space.
7043//>>docs: http://api.jqueryui.com/accordion/
7044//>>demos: http://jqueryui.com/accordion/
7045//>>css.structure: ../themes/base/core.css
7046//>>css.structure: ../themes/base/accordion.css
7047//>>css.theme: ../themes/base/theme.css
7048
7049(function( factory ) {
7050	if ( typeof define === "function" && define.amd ) {
7051
7052		// AMD. Register as an anonymous module.
7053		define([
7054			"jquery",
7055			"./core",
7056			"./widget"
7057		], factory );
7058	} else {
7059
7060		// Browser globals
7061		factory( jQuery );
7062	}
7063}(function( $ ) {
7064
7065return $.widget( "ui.accordion", {
7066	version: "@VERSION",
7067	options: {
7068		active: 0,
7069		animate: {},
7070		classes: {
7071			"ui-accordion-header": "ui-corner-top",
7072			"ui-accordion-header-collapsed": "ui-corner-all",
7073			"ui-accordion-content": "ui-corner-bottom"
7074		},
7075		collapsible: false,
7076		event: "click",
7077		header: "> li > :first-child,> :not(li):even",
7078		heightStyle: "auto",
7079		icons: {
7080			activeHeader: "ui-icon-triangle-1-s",
7081			header: "ui-icon-triangle-1-e"
7082		},
7083
7084		// callbacks
7085		activate: null,
7086		beforeActivate: null
7087	},
7088
7089	hideProps: {
7090		borderTopWidth: "hide",
7091		borderBottomWidth: "hide",
7092		paddingTop: "hide",
7093		paddingBottom: "hide",
7094		height: "hide"
7095	},
7096
7097	showProps: {
7098		borderTopWidth: "show",
vendor: 4,209 bytes, lines 7099-7270
7099		borderBottomWidth: "show",
7100		paddingTop: "show",
7101		paddingBottom: "show",
7102		height: "show"
7103	},
7104
7105	_create: function() {
7106		var options = this.options;
7107
7108		this.prevShow = this.prevHide = $();
7109		this._addClass( "ui-accordion", "ui-widget ui-helper-reset" );
7110		this.element.attr( "role", "tablist" );
7111
7112		// don't allow collapsible: false and active: false / null
7113		if ( !options.collapsible && (options.active === false || options.active == null) ) {
7114			options.active = 0;
7115		}
7116
7117		this._processPanels();
7118		// handle negative values
7119		if ( options.active < 0 ) {
7120			options.active += this.headers.length;
7121		}
7122		this._refresh();
7123	},
7124
7125	_getCreateEventData: function() {
7126		return {
7127			header: this.active,
7128			panel: !this.active.length ? $() : this.active.next()
7129		};
7130	},
7131
7132	_createIcons: function() {
7133		var icon, children,
7134			icons = this.options.icons;
7135
7136		if ( icons ) {
7137			icon = $( "<span>" );
7138			this._addClass( icon, "ui-accordion-header-icon", "ui-icon " + icons.header );
7139			icon.prependTo( this.headers );
7140			children = this.active.children( ".ui-accordion-header-icon" );
7141			this._removeClass( children, icons.header )
7142				._addClass( children, null, icons.activeHeader )
7143				._addClass( this.headers, "ui-accordion-icons" );
7144		}
7145	},
7146
7147	_destroyIcons: function() {
7148		this._removeClass( this.headers, "ui-accordion-icons" );
7149		this.headers.children( ".ui-accordion-header-icon" ).remove();
7150	},
7151
7152	_destroy: function() {
7153		var contents;
7154
7155		// clean up main element
7156		this.element.removeAttr( "role" );
7157
7158		// clean up headers
7159		this.headers
7160			.removeAttr( "role" )
7161			.removeAttr( "aria-expanded" )
7162			.removeAttr( "aria-selected" )
7163			.removeAttr( "aria-controls" )
7164			.removeAttr( "tabIndex" )
7165			.removeUniqueId();
7166
7167		this._destroyIcons();
7168
7169		// clean up content panels
7170		contents = this.headers.next()
7171			.css( "display", "" )
7172			.removeAttr( "role" )
7173			.removeAttr( "aria-hidden" )
7174			.removeAttr( "aria-labelledby" )
7175			.removeUniqueId();
7176
7177		if ( this.options.heightStyle !== "content" ) {
7178			contents.css( "height", "" );
7179		}
7180	},
7181
7182	_setOption: function( key, value ) {
7183		if ( key === "active" ) {
7184			// _activate() will handle invalid values and update this.options
7185			this._activate( value );
7186			return;
7187		}
7188
7189		if ( key === "event" ) {
7190			if ( this.options.event ) {
7191				this._off( this.headers, this.options.event );
7192			}
7193			this._setupEvents( value );
7194		}
7195
7196		this._super( key, value );
7197
7198		// setting collapsible: false while collapsed; open first panel
7199		if ( key === "collapsible" && !value && this.options.active === false ) {
7200			this._activate( 0 );
7201		}
7202
7203		if ( key === "icons" ) {
7204			this._destroyIcons();
7205			if ( value ) {
7206				this._createIcons();
7207			}
7208		}
7209
7210		// Support: IE8 Only
7211		// #5332 / #6059 - opacity doesn't cascade to positioned elements in IE
7212		// so we need to add the disabled class to the headers and panels
7213		if ( key === "disabled" ) {
7214			this.element.attr( "aria-disabled", value );
7215
7216			this._toggleClass( null, "ui-state-disabled", !!value );
7217			this._toggleClass( this.headers.add( this.headers.next() ), null, "ui-state-disabled",
7218				!!value );
7219		}
7220	},
7221
7222	_keydown: function( event ) {
7223		if ( event.altKey || event.ctrlKey ) {
7224			return;
7225		}
7226
7227		var keyCode = $.ui.keyCode,
7228			length = this.headers.length,
7229			currentIndex = this.headers.index( event.target ),
7230			toFocus = false;
7231
7232		switch ( event.keyCode ) {
7233			case keyCode.RIGHT:
7234			case keyCode.DOWN:
7235				toFocus = this.headers[ ( currentIndex + 1 ) % length ];
7236				break;
7237			case keyCode.LEFT:
7238			case keyCode.UP:
7239				toFocus = this.headers[ ( currentIndex - 1 + length ) % length ];
7240				break;
7241			case keyCode.SPACE:
7242			case keyCode.ENTER:
7243				this._eventHandler( event );
7244				break;
7245			case keyCode.HOME:
7246				toFocus = this.headers[ 0 ];
7247				break;
7248			case keyCode.END:
7249				toFocus = this.headers[ length - 1 ];
7250				break;
7251		}
7252
7253		if ( toFocus ) {
7254			$( event.target ).attr( "tabIndex", -1 );
7255			$( toFocus ).attr( "tabIndex", 0 );
7256			toFocus.focus();
7257			event.preventDefault();
7258		}
7259	},
7260
7261	_panelKeyDown: function( event ) {
7262		if ( event.keyCode === $.ui.keyCode.UP && event.ctrlKey ) {
7263			$( event.currentTarget ).prev().focus();
7264		}
7265	},
7266
7267	refresh: function() {
7268		var options = this.options;
7269		this._processPanels();
7270
vendor: 12,399 bytes, lines 7271-7715
7271		// was collapsed or no panel
7272		if ( ( options.active === false && options.collapsible === true ) || !this.headers.length ) {
7273			options.active = false;
7274			this.active = $();
7275		// active false only when collapsible is true
7276		} else if ( options.active === false ) {
7277			this._activate( 0 );
7278		// was active, but active panel is gone
7279		} else if ( this.active.length && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
7280			// all remaining panel are disabled
7281			if ( this.headers.length === this.headers.find(".ui-state-disabled").length ) {
7282				options.active = false;
7283				this.active = $();
7284			// activate previous panel
7285			} else {
7286				this._activate( Math.max( 0, options.active - 1 ) );
7287			}
7288		// was active, active panel still exists
7289		} else {
7290			// make sure active index is correct
7291			options.active = this.headers.index( this.active );
7292		}
7293
7294		this._destroyIcons();
7295
7296		this._refresh();
7297	},
7298
7299	_processPanels: function() {
7300		var prevHeaders = this.headers,
7301			prevPanels = this.panels;
7302
7303		this.headers = this.element.find( this.options.header );
7304		this._addClass( this.headers, "ui-accordion-header ui-accordion-header-collapsed",
7305			"ui-state-default" );
7306
7307		this.panels = this.headers.next().filter( ":not(.ui-accordion-content-active)" ).hide();
7308		this._addClass( this.panels, "ui-accordion-content", "ui-helper-reset ui-widget-content" );
7309
7310		// Avoid memory leaks (#10056)
7311		if ( prevPanels ) {
7312			this._off( prevHeaders.not( this.headers ) );
7313			this._off( prevPanels.not( this.panels ) );
7314		}
7315	},
7316
7317	_refresh: function() {
7318		var maxHeight,
7319			options = this.options,
7320			heightStyle = options.heightStyle,
7321			parent = this.element.parent();
7322
7323		this.active = this._findActive( options.active );
7324		this._addClass( this.active, "ui-accordion-header-active", "ui-state-active" )
7325			._removeClass( this.active, "ui-accordion-header-collapsed" );
7326		this._addClass( this.active.next(), "ui-accordion-content-active" );
7327		this.active.next().show();
7328
7329		this.headers
7330			.attr( "role", "tab" )
7331			.each(function() {
7332				var header = $( this ),
7333					headerId = header.uniqueId().attr( "id" ),
7334					panel = header.next(),
7335					panelId = panel.uniqueId().attr( "id" );
7336				header.attr( "aria-controls", panelId );
7337				panel.attr( "aria-labelledby", headerId );
7338			})
7339			.next()
7340				.attr( "role", "tabpanel" );
7341
7342		this.headers
7343			.not( this.active )
7344			.attr({
7345				"aria-selected": "false",
7346				"aria-expanded": "false",
7347				tabIndex: -1
7348			})
7349			.next()
7350				.attr({
7351					"aria-hidden": "true"
7352				})
7353				.hide();
7354
7355		// make sure at least one header is in the tab order
7356		if ( !this.active.length ) {
7357			this.headers.eq( 0 ).attr( "tabIndex", 0 );
7358		} else {
7359			this.active.attr({
7360				"aria-selected": "true",
7361				"aria-expanded": "true",
7362				tabIndex: 0
7363			})
7364			.next()
7365				.attr({
7366					"aria-hidden": "false"
7367				});
7368		}
7369
7370		this._createIcons();
7371
7372		this._setupEvents( options.event );
7373
7374		if ( heightStyle === "fill" ) {
7375			maxHeight = parent.height();
7376			this.element.siblings( ":visible" ).each(function() {
7377				var elem = $( this ),
7378					position = elem.css( "position" );
7379
7380				if ( position === "absolute" || position === "fixed" ) {
7381					return;
7382				}
7383				maxHeight -= elem.outerHeight( true );
7384			});
7385
7386			this.headers.each(function() {
7387				maxHeight -= $( this ).outerHeight( true );
7388			});
7389
7390			this.headers.next()
7391				.each(function() {
7392					$( this ).height( Math.max( 0, maxHeight -
7393						$( this ).innerHeight() + $( this ).height() ) );
7394				})
7395				.css( "overflow", "auto" );
7396		} else if ( heightStyle === "auto" ) {
7397			maxHeight = 0;
7398			this.headers.next()
7399				.each(function() {
7400					maxHeight = Math.max( maxHeight, $( this ).css( "height", "" ).height() );
7401				})
7402				.height( maxHeight );
7403		}
7404	},
7405
7406	_activate: function( index ) {
7407		var active = this._findActive( index )[ 0 ];
7408
7409		// trying to activate the already active panel
7410		if ( active === this.active[ 0 ] ) {
7411			return;
7412		}
7413
7414		// trying to collapse, simulate a click on the currently active header
7415		active = active || this.active[ 0 ];
7416
7417		this._eventHandler({
7418			target: active,
7419			currentTarget: active,
7420			preventDefault: $.noop
7421		});
7422	},
7423
7424	_findActive: function( selector ) {
7425		return typeof selector === "number" ? this.headers.eq( selector ) : $();
7426	},
7427
7428	_setupEvents: function( event ) {
7429		var events = {
7430			keydown: "_keydown"
7431		};
7432		if ( event ) {
7433			$.each( event.split( " " ), function( index, eventName ) {
7434				events[ eventName ] = "_eventHandler";
7435			});
7436		}
7437
7438		this._off( this.headers.add( this.headers.next() ) );
7439		this._on( this.headers, events );
7440		this._on( this.headers.next(), { keydown: "_panelKeyDown" });
7441		this._hoverable( this.headers );
7442		this._focusable( this.headers );
7443	},
7444
7445	_eventHandler: function( event ) {
7446		var activeChildren, clickedChildren,
7447			options = this.options,
7448			active = this.active,
7449			clicked = $( event.currentTarget ),
7450			clickedIsActive = clicked[ 0 ] === active[ 0 ],
7451			collapsing = clickedIsActive && options.collapsible,
7452			toShow = collapsing ? $() : clicked.next(),
7453			toHide = active.next(),
7454			eventData = {
7455				oldHeader: active,
7456				oldPanel: toHide,
7457				newHeader: collapsing ? $() : clicked,
7458				newPanel: toShow
7459			};
7460
7461		event.preventDefault();
7462
7463		if (
7464				// click on active header, but not collapsible
7465				( clickedIsActive && !options.collapsible ) ||
7466				// allow canceling activation
7467				( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
7468			return;
7469		}
7470
7471		options.active = collapsing ? false : this.headers.index( clicked );
7472
7473		// when the call to ._toggle() comes after the class changes
7474		// it causes a very odd bug in IE 8 (see #6720)
7475		this.active = clickedIsActive ? $() : clicked;
7476		this._toggle( eventData );
7477
7478		// switch classes
7479		// corner classes on the previously active header stay after the animation
7480		this._removeClass( active, "ui-accordion-header-active", "ui-state-active" );
7481		if ( options.icons ) {
7482			activeChildren = active.children( ".ui-accordion-header-icon" );
7483			this._removeClass( activeChildren, null, options.icons.activeHeader )
7484				._addClass( activeChildren, null, options.icons.header );
7485		}
7486
7487		if ( !clickedIsActive ) {
7488			this._removeClass( clicked, "ui-accordion-header-collapsed" )
7489				._addClass( clicked, "ui-accordion-header-active", "ui-state-active" );
7490			if ( options.icons ) {
7491				clickedChildren = clicked.children( ".ui-accordion-header-icon" );
7492				this._removeClass( clickedChildren, null, options.icons.header )
7493					._addClass( clickedChildren, null, options.icons.activeHeader );
7494			}
7495
7496			this._addClass( clicked.next(), "ui-accordion-content-active" );
7497		}
7498	},
7499
7500	_toggle: function( data ) {
7501		var toShow = data.newPanel,
7502			toHide = this.prevShow.length ? this.prevShow : data.oldPanel;
7503
7504		// handle activating a panel during the animation for another activation
7505		this.prevShow.add( this.prevHide ).stop( true, true );
7506		this.prevShow = toShow;
7507		this.prevHide = toHide;
7508
7509		if ( this.options.animate ) {
7510			this._animate( toShow, toHide, data );
7511		} else {
7512			toHide.hide();
7513			toShow.show();
7514			this._toggleComplete( data );
7515		}
7516
7517		toHide.attr({
7518			"aria-hidden": "true"
7519		});
7520		toHide.prev().attr({
7521			"aria-selected": "false",
7522			"aria-expanded": "false"
7523		});
7524		// if we're switching panels, remove the old header from the tab order
7525		// if we're opening from collapsed state, remove the previous header from the tab order
7526		// if we're collapsing, then keep the collapsing header in the tab order
7527		if ( toShow.length && toHide.length ) {
7528			toHide.prev().attr({
7529				"tabIndex": -1,
7530				"aria-expanded": "false"
7531			});
7532		} else if ( toShow.length ) {
7533			this.headers.filter(function() {
7534				return parseInt( $( this ).attr( "tabIndex" ), 10 ) === 0;
7535			})
7536			.attr( "tabIndex", -1 );
7537		}
7538
7539		toShow
7540			.attr( "aria-hidden", "false" )
7541			.prev()
7542				.attr({
7543					"aria-selected": "true",
7544					"aria-expanded": "true",
7545					tabIndex: 0
7546				});
7547	},
7548
7549	_animate: function( toShow, toHide, data ) {
7550		var total, easing, duration,
7551			that = this,
7552			adjust = 0,
7553			boxSizing = toShow.css( "box-sizing" ),
7554			down = toShow.length &&
7555				( !toHide.length || ( toShow.index() < toHide.index() ) ),
7556			animate = this.options.animate || {},
7557			options = down && animate.down || animate,
7558			complete = function() {
7559				that._toggleComplete( data );
7560			};
7561
7562		if ( typeof options === "number" ) {
7563			duration = options;
7564		}
7565		if ( typeof options === "string" ) {
7566			easing = options;
7567		}
7568		// fall back from options to animation in case of partial down settings
7569		easing = easing || options.easing || animate.easing;
7570		duration = duration || options.duration || animate.duration;
7571
7572		if ( !toHide.length ) {
7573			return toShow.animate( this.showProps, duration, easing, complete );
7574		}
7575		if ( !toShow.length ) {
7576			return toHide.animate( this.hideProps, duration, easing, complete );
7577		}
7578
7579		total = toShow.show().outerHeight();
7580		toHide.animate( this.hideProps, {
7581			duration: duration,
7582			easing: easing,
7583			step: function( now, fx ) {
7584				fx.now = Math.round( now );
7585			}
7586		});
7587		toShow
7588			.hide()
7589			.animate( this.showProps, {
7590				duration: duration,
7591				easing: easing,
7592				complete: complete,
7593				step: function( now, fx ) {
7594					fx.now = Math.round( now );
7595					if ( fx.prop !== "height" ) {
7596						if ( boxSizing === "content-box" ) {
7597							adjust += fx.now;
7598						}
7599					} else if ( that.options.heightStyle !== "content" ) {
7600						fx.now = Math.round( total - toHide.outerHeight() - adjust );
7601						adjust = 0;
7602					}
7603				}
7604			});
7605	},
7606
7607	_toggleComplete: function( data ) {
7608		var toHide = data.oldPanel,
7609			prev = toHide.prev();
7610
7611		this._removeClass( toHide, "ui-accordion-content-active" );
7612		this._removeClass( prev, "ui-accordion-header-active" )
7613			._addClass( prev, "ui-accordion-header-collapsed" );
7614
7615		// Work around for rendering bug in IE (#5421)
7616		if ( toHide.length ) {
7617			toHide.parent()[ 0 ].className = toHide.parent()[ 0 ].className;
7618		}
7619		this._trigger( "activate", null, data );
7620	}
7621});
7622
7623}));
7624
7625//>>label: Autocomplete
7626//>>group: Widgets
7627//>>description: Lists suggested words as the user is typing.
7628//>>docs: http://api.jqueryui.com/autocomplete/
7629//>>demos: http://jqueryui.com/autocomplete/
7630//>>css.structure: ../themes/base/core.css
7631//>>css.structure: ../themes/base/autocomplete.css
7632//>>css.theme: ../themes/base/theme.css
7633
7634(function( factory ) {
7635	if ( typeof define === "function" && define.amd ) {
7636
7637		// AMD. Register as an anonymous module.
7638		define([
7639			"jquery",
7640			"./core",
7641			"./widget",
7642			"./position",
7643			"./menu"
7644		], factory );
7645	} else {
7646
7647		// Browser globals
7648		factory( jQuery );
7649	}
7650}(function( $ ) {
7651
7652$.widget( "ui.autocomplete", {
7653	version: "@VERSION",
7654	defaultElement: "<input>",
7655	options: {
7656		appendTo: null,
7657		autoFocus: false,
7658		classes: {},
7659		delay: 300,
7660		minLength: 1,
7661		position: {
7662			my: "left top",
7663			at: "left bottom",
7664			collision: "none"
7665		},
7666		source: null,
7667
7668		// callbacks
7669		change: null,
7670		close: null,
7671		focus: null,
7672		open: null,
7673		response: null,
7674		search: null,
7675		select: null
7676	},
7677
7678	requestIndex: 0,
7679	pending: 0,
7680
7681	_create: function() {
7682		// Some browsers only repeat keydown events, not keypress events,
7683		// so we use the suppressKeyPress flag to determine if we've already
7684		// handled the keydown event. #7269
7685		// Unfortunately the code for & in keypress is the same as the up arrow,
7686		// so we use the suppressKeyPressRepeat flag to avoid handling keypress
7687		// events when we know the keydown event was used to modify the
7688		// search term. #7799
7689		var suppressKeyPress, suppressKeyPressRepeat, suppressInput,
7690			nodeName = this.element[ 0 ].nodeName.toLowerCase(),
7691			isTextarea = nodeName === "textarea",
7692			isInput = nodeName === "input";
7693
7694		// Textareas are always multi-line
7695		// Inputs are always single-line, even if inside a contentEditable element
7696		// IE also treats inputs as contentEditable
7697		// All other element types are determined by whether or not they're contentEditable
7698		this.isMultiLine = isTextarea || !isInput && this.element.prop( "isContentEditable" );
7699
7700		this.valueMethod = this.element[ isTextarea || isInput ? "val" : "text" ];
7701		this.isNewMenu = true;
7702
7703		this._addClass( "ui-autocomplete-input" );
7704		this.element.attr( "autocomplete", "off" );
7705
7706		this._on( this.element, {
7707			keydown: function( event ) {
7708				if ( this.element.prop( "readOnly" ) ) {
7709					suppressKeyPress = true;
7710					suppressInput = true;
7711					suppressKeyPressRepeat = true;
7712					return;
7713				}
7714
7715				suppressKeyPress = false;
vendor: 4,819 bytes, lines 7716-7879
7716				suppressInput = false;
7717				suppressKeyPressRepeat = false;
7718				var keyCode = $.ui.keyCode;
7719				switch ( event.keyCode ) {
7720				case keyCode.PAGE_UP:
7721					suppressKeyPress = true;
7722					this._move( "previousPage", event );
7723					break;
7724				case keyCode.PAGE_DOWN:
7725					suppressKeyPress = true;
7726					this._move( "nextPage", event );
7727					break;
7728				case keyCode.UP:
7729					suppressKeyPress = true;
7730					this._keyEvent( "previous", event );
7731					break;
7732				case keyCode.DOWN:
7733					suppressKeyPress = true;
7734					this._keyEvent( "next", event );
7735					break;
7736				case keyCode.ENTER:
7737					// when menu is open and has focus
7738					if ( this.menu.active ) {
7739						// #6055 - Opera still allows the keypress to occur
7740						// which causes forms to submit
7741						suppressKeyPress = true;
7742						event.preventDefault();
7743						this.menu.select( event );
7744					}
7745					break;
7746				case keyCode.TAB:
7747					if ( this.menu.active ) {
7748						this.menu.select( event );
7749					}
7750					break;
7751				case keyCode.ESCAPE:
7752					if ( this.menu.element.is( ":visible" ) ) {
7753						if ( !this.isMultiLine ) {
7754							this._value( this.term );
7755						}
7756						this.close( event );
7757						// Different browsers have different default behavior for escape
7758						// Single press can mean undo or clear
7759						// Double press in IE means clear the whole form
7760						event.preventDefault();
7761					}
7762					break;
7763				default:
7764					suppressKeyPressRepeat = true;
7765					// search timeout should be triggered before the input value is changed
7766					this._searchTimeout( event );
7767					break;
7768				}
7769			},
7770			keypress: function( event ) {
7771				if ( suppressKeyPress ) {
7772					suppressKeyPress = false;
7773					if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
7774						event.preventDefault();
7775					}
7776					return;
7777				}
7778				if ( suppressKeyPressRepeat ) {
7779					return;
7780				}
7781
7782				// replicate some key handlers to allow them to repeat in Firefox and Opera
7783				var keyCode = $.ui.keyCode;
7784				switch ( event.keyCode ) {
7785				case keyCode.PAGE_UP:
7786					this._move( "previousPage", event );
7787					break;
7788				case keyCode.PAGE_DOWN:
7789					this._move( "nextPage", event );
7790					break;
7791				case keyCode.UP:
7792					this._keyEvent( "previous", event );
7793					break;
7794				case keyCode.DOWN:
7795					this._keyEvent( "next", event );
7796					break;
7797				}
7798			},
7799			input: function( event ) {
7800				if ( suppressInput ) {
7801					suppressInput = false;
7802					event.preventDefault();
7803					return;
7804				}
7805				this._searchTimeout( event );
7806			},
7807			focus: function() {
7808				this.selectedItem = null;
7809				this.previous = this._value();
7810			},
7811			blur: function( event ) {
7812				if ( this.cancelBlur ) {
7813					delete this.cancelBlur;
7814					return;
7815				}
7816
7817				clearTimeout( this.searching );
7818				this.close( event );
7819				this._change( event );
7820			}
7821		});
7822
7823		this._initSource();
7824		this.menu = $( "<ul>" )
7825			.appendTo( this._appendTo() )
7826			.menu({
7827				// disable ARIA support, the live region takes care of that
7828				role: null
7829			})
7830			.hide()
7831			.menu( "instance" );
7832
7833		this._addClass( this.menu.element, "ui-autocomplete", "ui-front" );
7834		this._on( this.menu.element, {
7835			mousedown: function( event ) {
7836				// prevent moving focus out of the text field
7837				event.preventDefault();
7838
7839				// IE doesn't prevent moving focus even with event.preventDefault()
7840				// so we set a flag to know when we should ignore the blur event
7841				this.cancelBlur = true;
7842				this._delay(function() {
7843					delete this.cancelBlur;
7844
7845					// Support: IE 8 only
7846					// Right clicking a menu item or selecting text from the menu items will
7847					// result in focus moving out of the input. However, we've already received
7848					// and ignored the blur event because of the cancelBlur flag set above. So
7849					// we restore focus to ensure that the menu closes properly based on the user's
7850					// next actions.
7851					if ( this.element[ 0 ] !== $.ui.safeActiveElement( this.document[ 0 ] ) ) {
7852						this.element.focus();
7853					}
7854				});
7855
7856				// clicking on the scrollbar causes focus to shift to the body
7857				// but we can't detect a mouseup or a click immediately afterward
7858				// so we have to track the next mousedown and close the menu if
7859				// the user clicks somewhere outside of the autocomplete
7860				var menuElement = this.menu.element[ 0 ];
7861				if ( !$( event.target ).closest( ".ui-menu-item" ).length ) {
7862					this._delay(function() {
7863						var that = this;
7864						this.document.one( "mousedown", function( event ) {
7865							if ( event.target !== that.element[ 0 ] &&
7866									event.target !== menuElement &&
7867									!$.contains( menuElement, event.target ) ) {
7868								that.close();
7869							}
7870						});
7871					});
7872				}
7873			},
7874			menufocus: function( event, ui ) {
7875				var label, item;
7876				// support: Firefox
7877				// Prevent accidental activation of menu items in Firefox (#7024 #9118)
7878				if ( this.isNewMenu ) {
7879					this.isNewMenu = false;
vendor: 4,080 bytes, lines 7880-8029
7880					if ( event.originalEvent && /^mouse/.test( event.originalEvent.type ) ) {
7881						this.menu.blur();
7882
7883						this.document.one( "mousemove", function() {
7884							$( event.target ).trigger( event.originalEvent );
7885						});
7886
7887						return;
7888					}
7889				}
7890
7891				item = ui.item.data( "ui-autocomplete-item" );
7892				if ( false !== this._trigger( "focus", event, { item: item } ) ) {
7893					// use value to match what will end up in the input, if it was a key event
7894					if ( event.originalEvent && /^key/.test( event.originalEvent.type ) ) {
7895						this._value( item.value );
7896					}
7897				}
7898
7899				// Announce the value in the liveRegion
7900				label = ui.item.attr( "aria-label" ) || item.value;
7901				if ( label && $.trim( label ).length ) {
7902					this.liveRegion.children().hide();
7903					$( "<div>" ).text( label ).appendTo( this.liveRegion );
7904				}
7905			},
7906			menuselect: function( event, ui ) {
7907				var item = ui.item.data( "ui-autocomplete-item" ),
7908					previous = this.previous;
7909
7910				// only trigger when focus was lost (click on menu)
7911				if ( this.element[ 0 ] !== $.ui.safeActiveElement( this.document[ 0 ] ) ) {
7912					this.element.focus();
7913					this.previous = previous;
7914					// #6109 - IE triggers two focus events and the second
7915					// is asynchronous, so we need to reset the previous
7916					// term synchronously and asynchronously :-(
7917					this._delay(function() {
7918						this.previous = previous;
7919						this.selectedItem = item;
7920					});
7921				}
7922
7923				if ( false !== this._trigger( "select", event, { item: item } ) ) {
7924					this._value( item.value );
7925				}
7926				// reset the term after the select event
7927				// this allows custom select handling to work properly
7928				this.term = this._value();
7929
7930				this.close( event );
7931				this.selectedItem = item;
7932			}
7933		});
7934
7935		this.liveRegion = $( "<span>", {
7936				role: "status",
7937				"aria-live": "assertive",
7938				"aria-relevant": "additions"
7939			})
7940			.appendTo( this.document[ 0 ].body );
7941
7942		this._addClass( this.liveRegion, null, "ui-helper-hidden-accessible" );
7943
7944		// turning off autocomplete prevents the browser from remembering the
7945		// value when navigating through history, so we re-enable autocomplete
7946		// if the page is unloaded before the widget is destroyed. #7790
7947		this._on( this.window, {
7948			beforeunload: function() {
7949				this.element.removeAttr( "autocomplete" );
7950			}
7951		});
7952	},
7953
7954	_destroy: function() {
7955		clearTimeout( this.searching );
7956		this.element.removeAttr( "autocomplete" );
7957		this.menu.element.remove();
7958		this.liveRegion.remove();
7959	},
7960
7961	_setOption: function( key, value ) {
7962		this._super( key, value );
7963		if ( key === "source" ) {
7964			this._initSource();
7965		}
7966		if ( key === "appendTo" ) {
7967			this.menu.element.appendTo( this._appendTo() );
7968		}
7969		if ( key === "disabled" && value && this.xhr ) {
7970			this.xhr.abort();
7971		}
7972	},
7973
7974	_appendTo: function() {
7975		var element = this.options.appendTo;
7976
7977		if ( element ) {
7978			element = element.jquery || element.nodeType ?
7979				$( element ) :
7980				this.document.find( element ).eq( 0 );
7981		}
7982
7983		if ( !element || !element[ 0 ] ) {
7984			element = this.element.closest( ".ui-front" );
7985		}
7986
7987		if ( !element.length ) {
7988			element = this.document[ 0 ].body;
7989		}
7990
7991		return element;
7992	},
7993
7994	_initSource: function() {
7995		var array, url,
7996			that = this;
7997		if ( $.isArray( this.options.source ) ) {
7998			array = this.options.source;
7999			this.source = function( request, response ) {
8000				response( $.ui.autocomplete.filter( array, request.term ) );
8001			};
8002		} else if ( typeof this.options.source === "string" ) {
8003			url = this.options.source;
8004			this.source = function( request, response ) {
8005				if ( that.xhr ) {
8006					that.xhr.abort();
8007				}
8008				that.xhr = $.ajax({
8009					url: url,
8010					data: request,
8011					dataType: "json",
8012					success: function( data ) {
8013						response( data );
8014					},
8015					error: function() {
8016						response([]);
8017					}
8018				});
8019			};
8020		} else {
8021			this.source = this.options.source;
8022		}
8023	},
8024
8025	_searchTimeout: function( event ) {
8026		clearTimeout( this.searching );
8027		this.searching = this._delay(function() {
8028
8029			// Search if the value has changed, or if the user retypes the same value (see #7434)
vendor: 7,059 bytes, lines 8030-8314
8030			var equalValues = this.term === this._value(),
8031				menuVisible = this.menu.element.is( ":visible" ),
8032				modifierKey = event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
8033
8034			if ( !equalValues || ( equalValues && !menuVisible && !modifierKey ) ) {
8035				this.selectedItem = null;
8036				this.search( null, event );
8037			}
8038		}, this.options.delay );
8039	},
8040
8041	search: function( value, event ) {
8042		value = value != null ? value : this._value();
8043
8044		// always save the actual value, not the one passed as an argument
8045		this.term = this._value();
8046
8047		if ( value.length < this.options.minLength ) {
8048			return this.close( event );
8049		}
8050
8051		if ( this._trigger( "search", event ) === false ) {
8052			return;
8053		}
8054
8055		return this._search( value );
8056	},
8057
8058	_search: function( value ) {
8059		this.pending++;
8060		this._addClass( "ui-autocomplete-loading" );
8061		this.cancelSearch = false;
8062
8063		this.source( { term: value }, this._response() );
8064	},
8065
8066	_response: function() {
8067		var index = ++this.requestIndex;
8068
8069		return $.proxy(function( content ) {
8070			if ( index === this.requestIndex ) {
8071				this.__response( content );
8072			}
8073
8074			this.pending--;
8075			if ( !this.pending ) {
8076				this._removeClass( "ui-autocomplete-loading" );
8077			}
8078		}, this );
8079	},
8080
8081	__response: function( content ) {
8082		if ( content ) {
8083			content = this._normalize( content );
8084		}
8085		this._trigger( "response", null, { content: content } );
8086		if ( !this.options.disabled && content && content.length && !this.cancelSearch ) {
8087			this._suggest( content );
8088			this._trigger( "open" );
8089		} else {
8090			// use ._close() instead of .close() so we don't cancel future searches
8091			this._close();
8092		}
8093	},
8094
8095	close: function( event ) {
8096		this.cancelSearch = true;
8097		this._close( event );
8098	},
8099
8100	_close: function( event ) {
8101		if ( this.menu.element.is( ":visible" ) ) {
8102			this.menu.element.hide();
8103			this.menu.blur();
8104			this.isNewMenu = true;
8105			this._trigger( "close", event );
8106		}
8107	},
8108
8109	_change: function( event ) {
8110		if ( this.previous !== this._value() ) {
8111			this._trigger( "change", event, { item: this.selectedItem } );
8112		}
8113	},
8114
8115	_normalize: function( items ) {
8116		// assume all items have the right format when the first item is complete
8117		if ( items.length && items[ 0 ].label && items[ 0 ].value ) {
8118			return items;
8119		}
8120		return $.map( items, function( item ) {
8121			if ( typeof item === "string" ) {
8122				return {
8123					label: item,
8124					value: item
8125				};
8126			}
8127			return $.extend( {}, item, {
8128				label: item.label || item.value,
8129				value: item.value || item.label
8130			});
8131		});
8132	},
8133
8134	_suggest: function( items ) {
8135		var ul = this.menu.element.empty();
8136		this._renderMenu( ul, items );
8137		this.isNewMenu = true;
8138		this.menu.refresh();
8139
8140		// size and position menu
8141		ul.show();
8142		this._resizeMenu();
8143		ul.position( $.extend({
8144			of: this.element
8145		}, this.options.position ) );
8146
8147		if ( this.options.autoFocus ) {
8148			this.menu.next();
8149		}
8150	},
8151
8152	_resizeMenu: function() {
8153		var ul = this.menu.element;
8154		ul.outerWidth( Math.max(
8155			// Firefox wraps long text (possibly a rounding bug)
8156			// so we add 1px to avoid the wrapping (#7513)
8157			ul.width( "" ).outerWidth() + 1,
8158			this.element.outerWidth()
8159		) );
8160	},
8161
8162	_renderMenu: function( ul, items ) {
8163		var that = this;
8164		$.each( items, function( index, item ) {
8165			that._renderItemData( ul, item );
8166		});
8167	},
8168
8169	_renderItemData: function( ul, item ) {
8170		return this._renderItem( ul, item ).data( "ui-autocomplete-item", item );
8171	},
8172
8173	_renderItem: function( ul, item ) {
8174		return $( "<li>" )
8175			.append( $( "<div>" ).text( item.label ) )
8176			.appendTo( ul );
8177	},
8178
8179	_move: function( direction, event ) {
8180		if ( !this.menu.element.is( ":visible" ) ) {
8181			this.search( null, event );
8182			return;
8183		}
8184		if ( this.menu.isFirstItem() && /^previous/.test( direction ) ||
8185				this.menu.isLastItem() && /^next/.test( direction ) ) {
8186
8187			if ( !this.isMultiLine ) {
8188				this._value( this.term );
8189			}
8190
8191			this.menu.blur();
8192			return;
8193		}
8194		this.menu[ direction ]( event );
8195	},
8196
8197	widget: function() {
8198		return this.menu.element;
8199	},
8200
8201	_value: function() {
8202		return this.valueMethod.apply( this.element, arguments );
8203	},
8204
8205	_keyEvent: function( keyEvent, event ) {
8206		if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
8207			this._move( keyEvent, event );
8208
8209			// prevents moving cursor to beginning/end of the text field in some browsers
8210			event.preventDefault();
8211		}
8212	}
8213});
8214
8215$.extend( $.ui.autocomplete, {
8216	escapeRegex: function( value ) {
8217		return value.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" );
8218	},
8219	filter: function( array, term ) {
8220		var matcher = new RegExp( $.ui.autocomplete.escapeRegex( term ), "i" );
8221		return $.grep( array, function( value ) {
8222			return matcher.test( value.label || value.value || value );
8223		});
8224	}
8225});
8226
8227// live region extension, adding a `messages` option
8228// NOTE: This is an experimental API. We are still investigating
8229// a full solution for string manipulation and internationalization.
8230$.widget( "ui.autocomplete", $.ui.autocomplete, {
8231	options: {
8232		messages: {
8233			noResults: "No search results.",
8234			results: function( amount ) {
8235				return amount + ( amount > 1 ? " results are" : " result is" ) +
8236					" available, use up and down arrow keys to navigate.";
8237			}
8238		}
8239	},
8240
8241	__response: function( content ) {
8242		var message;
8243		this._superApply( arguments );
8244		if ( this.options.disabled || this.cancelSearch ) {
8245			return;
8246		}
8247		if ( content && content.length ) {
8248			message = this.options.messages.results( content.length );
8249		} else {
8250			message = this.options.messages.noResults;
8251		}
8252		this.liveRegion.children().hide();
8253		$( "<div>" ).text( message ).appendTo( this.liveRegion );
8254	}
8255});
8256
8257return $.ui.autocomplete;
8258
8259}));
8260
8261//>>label: Button
8262//>>group: Widgets
8263//>>description: Enhances a form with themeable buttons.
8264//>>docs: http://api.jqueryui.com/button/
8265//>>demos: http://jqueryui.com/button/
8266//>>css.structure: ../themes/base/core.css
8267//>>css.structure: ../themes/base/button.css
8268//>>css.theme: ../themes/base/theme.css
8269
8270(function( factory ) {
8271	if ( typeof define === "function" && define.amd ) {
8272
8273		// AMD. Register as an anonymous module.
8274		define([
8275			"jquery",
8276			"./core",
8277			"./widget"
8278		], factory );
8279	} else {
8280
8281		// Browser globals
8282		factory( jQuery );
8283	}
8284}(function( $ ) {
8285
8286var lastActive,
8287	baseClasses = "ui-button ui-widget ui-state-default ui-corner-all",
8288	typeClasses = "ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only",
8289	formResetHandler = function() {
8290		var form = $( this );
8291		setTimeout(function() {
8292			form.find( ":ui-button" ).button( "refresh" );
8293		}, 1 );
8294	},
8295	radioGroup = function( radio ) {
8296		var name = radio.name,
8297			form = radio.form,
8298			radios = $( [] );
8299		if ( name ) {
8300			name = name.replace( /'/g, "\\'" );
8301			if ( form ) {
8302				radios = $( form ).find( "[name='" + name + "'][type=radio]" );
8303			} else {
8304				radios = $( "[name='" + name + "'][type=radio]", radio.ownerDocument )
8305					.filter(function() {
8306						return !this.form;
8307					});
8308			}
8309		}
8310		return radios;
8311	};
8312
8313$.widget( "ui.button", {
8314	version: "@VERSION
vendor: 8,572 bytes, lines 8314-8610
8314",
8315	defaultElement: "<button>",
8316	options: {
8317		disabled: null,
8318		text: true,
8319		label: null,
8320		icons: {
8321			primary: null,
8322			secondary: null
8323		}
8324	},
8325	_create: function() {
8326		this.element.closest( "form" )
8327			.unbind( "reset" + this.eventNamespace )
8328			.bind( "reset" + this.eventNamespace, formResetHandler );
8329
8330		if ( typeof this.options.disabled !== "boolean" ) {
8331			this.options.disabled = !!this.element.prop( "disabled" );
8332		} else {
8333			this.element.prop( "disabled", this.options.disabled );
8334		}
8335
8336		this._determineButtonType();
8337		this.hasTitle = !!this.buttonElement.attr( "title" );
8338
8339		var that = this,
8340			options = this.options,
8341			toggleButton = this.type === "checkbox" || this.type === "radio",
8342			activeClass = !toggleButton ? "ui-state-active" : "";
8343
8344		if ( options.label === null ) {
8345			options.label = (this.type === "input" ? this.buttonElement.val() : this.buttonElement.html());
8346		}
8347
8348		this._hoverable( this.buttonElement );
8349
8350		this.buttonElement
8351			.addClass( baseClasses )
8352			.attr( "role", "button" )
8353			.bind( "mouseenter" + this.eventNamespace, function() {
8354				if ( options.disabled ) {
8355					return;
8356				}
8357				if ( this === lastActive ) {
8358					$( this ).addClass( "ui-state-active" );
8359				}
8360			})
8361			.bind( "mouseleave" + this.eventNamespace, function() {
8362				if ( options.disabled ) {
8363					return;
8364				}
8365				$( this ).removeClass( activeClass );
8366			})
8367			.bind( "click" + this.eventNamespace, function( event ) {
8368				if ( options.disabled ) {
8369					event.preventDefault();
8370					event.stopImmediatePropagation();
8371				}
8372			});
8373
8374		// Can't use _focusable() because the element that receives focus
8375		// and the element that gets the ui-state-focus class are different
8376		this._on({
8377			focus: function() {
8378				this.buttonElement.addClass( "ui-state-focus" );
8379			},
8380			blur: function() {
8381				this.buttonElement.removeClass( "ui-state-focus" );
8382			}
8383		});
8384
8385		if ( toggleButton ) {
8386			this.element.bind( "change" + this.eventNamespace, function() {
8387				that.refresh();
8388			});
8389		}
8390
8391		if ( this.type === "checkbox" ) {
8392			this.buttonElement.bind( "click" + this.eventNamespace, function() {
8393				if ( options.disabled ) {
8394					return false;
8395				}
8396			});
8397		} else if ( this.type === "radio" ) {
8398			this.buttonElement.bind( "click" + this.eventNamespace, function() {
8399				if ( options.disabled ) {
8400					return false;
8401				}
8402				$( this ).addClass( "ui-state-active" );
8403				that.buttonElement.attr( "aria-pressed", "true" );
8404
8405				var radio = that.element[ 0 ];
8406				radioGroup( radio )
8407					.not( radio )
8408					.map(function() {
8409						return $( this ).button( "widget" )[ 0 ];
8410					})
8411					.removeClass( "ui-state-active" )
8412					.attr( "aria-pressed", "false" );
8413			});
8414		} else {
8415			this.buttonElement
8416				.bind( "mousedown" + this.eventNamespace, function() {
8417					if ( options.disabled ) {
8418						return false;
8419					}
8420					$( this ).addClass( "ui-state-active" );
8421					lastActive = this;
8422					that.document.one( "mouseup", function() {
8423						lastActive = null;
8424					});
8425				})
8426				.bind( "mouseup" + this.eventNamespace, function() {
8427					if ( options.disabled ) {
8428						return false;
8429					}
8430					$( this ).removeClass( "ui-state-active" );
8431				})
8432				.bind( "keydown" + this.eventNamespace, function(event) {
8433					if ( options.disabled ) {
8434						return false;
8435					}
8436					if ( event.keyCode === $.ui.keyCode.SPACE || event.keyCode === $.ui.keyCode.ENTER ) {
8437						$( this ).addClass( "ui-state-active" );
8438					}
8439				})
8440				// see #8559, we bind to blur here in case the button element loses
8441				// focus between keydown and keyup, it would be left in an "active" state
8442				.bind( "keyup" + this.eventNamespace + " blur" + this.eventNamespace, function() {
8443					$( this ).removeClass( "ui-state-active" );
8444				});
8445
8446			if ( this.buttonElement.is("a") ) {
8447				this.buttonElement.keyup(function(event) {
8448					if ( event.keyCode === $.ui.keyCode.SPACE ) {
8449						// TODO pass through original event correctly (just as 2nd argument doesn't work)
8450						$( this ).click();
8451					}
8452				});
8453			}
8454		}
8455
8456		this._setOption( "disabled", options.disabled );
8457		this._resetButton();
8458	},
8459
8460	_determineButtonType: function() {
8461		var ancestor, labelSelector, checked;
8462
8463		if ( this.element.is("[type=checkbox]") ) {
8464			this.type = "checkbox";
8465		} else if ( this.element.is("[type=radio]") ) {
8466			this.type = "radio";
8467		} else if ( this.element.is("input") ) {
8468			this.type = "input";
8469		} else {
8470			this.type = "button";
8471		}
8472
8473		if ( this.type === "checkbox" || this.type === "radio" ) {
8474			// we don't search against the document in case the element
8475			// is disconnected from the DOM
8476			ancestor = this.element.parents().last();
8477			labelSelector = "label[for='" + this.element.attr("id") + "']";
8478			this.buttonElement = ancestor.find( labelSelector );
8479			if ( !this.buttonElement.length ) {
8480				ancestor = ancestor.length ? ancestor.siblings() : this.element.siblings();
8481				this.buttonElement = ancestor.filter( labelSelector );
8482				if ( !this.buttonElement.length ) {
8483					this.buttonElement = ancestor.find( labelSelector );
8484				}
8485			}
8486			this.element.addClass( "ui-helper-hidden-accessible" );
8487
8488			checked = this.element.is( ":checked" );
8489			if ( checked ) {
8490				this.buttonElement.addClass( "ui-state-active" );
8491			}
8492			this.buttonElement.prop( "aria-pressed", checked );
8493		} else {
8494			this.buttonElement = this.element;
8495		}
8496	},
8497
8498	widget: function() {
8499		return this.buttonElement;
8500	},
8501
8502	_destroy: function() {
8503		this.element
8504			.removeClass( "ui-helper-hidden-accessible" );
8505		this.buttonElement
8506			.removeClass( baseClasses + " ui-state-active " + typeClasses )
8507			.removeAttr( "role" )
8508			.removeAttr( "aria-pressed" )
8509			.html( this.buttonElement.find(".ui-button-text").html() );
8510
8511		if ( !this.hasTitle ) {
8512			this.buttonElement.removeAttr( "title" );
8513		}
8514	},
8515
8516	_setOption: function( key, value ) {
8517		this._super( key, value );
8518		if ( key === "disabled" ) {
8519			this.widget().toggleClass( "ui-state-disabled", !!value );
8520			this.element.prop( "disabled", !!value );
8521			if ( value ) {
8522				if ( this.type === "checkbox" || this.type === "radio" ) {
8523					this.buttonElement.removeClass( "ui-state-focus" );
8524				} else {
8525					this.buttonElement.removeClass( "ui-state-focus ui-state-active" );
8526				}
8527			}
8528			return;
8529		}
8530		this._resetButton();
8531	},
8532
8533	refresh: function() {
8534		//See #8237 & #8828
8535		var isDisabled = this.element.is( "input, button" ) ? this.element.is( ":disabled" ) : this.element.hasClass( "ui-button-disabled" );
8536
8537		if ( isDisabled !== this.options.disabled ) {
8538			this._setOption( "disabled", isDisabled );
8539		}
8540		if ( this.type === "radio" ) {
8541			radioGroup( this.element[0] ).each(function() {
8542				if ( $( this ).is( ":checked" ) ) {
8543					$( this ).button( "widget" )
8544						.addClass( "ui-state-active" )
8545						.attr( "aria-pressed", "true" );
8546				} else {
8547					$( this ).button( "widget" )
8548						.removeClass( "ui-state-active" )
8549						.attr( "aria-pressed", "false" );
8550				}
8551			});
8552		} else if ( this.type === "checkbox" ) {
8553			if ( this.element.is( ":checked" ) ) {
8554				this.buttonElement
8555					.addClass( "ui-state-active" )
8556					.attr( "aria-pressed", "true" );
8557			} else {
8558				this.buttonElement
8559					.removeClass( "ui-state-active" )
8560					.attr( "aria-pressed", "false" );
8561			}
8562		}
8563	},
8564
8565	_resetButton: function() {
8566		if ( this.type === "input" ) {
8567			if ( this.options.label ) {
8568				this.element.val( this.options.label );
8569			}
8570			return;
8571		}
8572		var buttonElement = this.buttonElement.removeClass( typeClasses ),
8573			buttonText = $( "<span></span>", this.document[0] )
8574				.addClass( "ui-button-text" )
8575				.html( this.options.label )
8576				.appendTo( buttonElement.empty() )
8577				.text(),
8578			icons = this.options.icons,
8579			multipleIcons = icons.primary && icons.secondary,
8580			buttonClasses = [];
8581
8582		if ( icons.primary || icons.secondary ) {
8583			if ( this.options.text ) {
8584				buttonClasses.push( "ui-button-text-icon" + ( multipleIcons ? "s" : ( icons.primary ? "-primary" : "-secondary" ) ) );
8585			}
8586
8587			if ( icons.primary ) {
8588				buttonElement.prepend( "<span class='ui-button-icon-primary ui-icon " + icons.primary + "'></span>" );
8589			}
8590
8591			if ( icons.secondary ) {
8592				buttonElement.append( "<span class='ui-button-icon-secondary ui-icon " + icons.secondary + "'></span>" );
8593			}
8594
8595			if ( !this.options.text ) {
8596				buttonClasses.push( multipleIcons ? "ui-button-icons-only" : "ui-button-icon-only" );
8597
8598				if ( !this.hasTitle ) {
8599					buttonElement.attr( "title", $.trim( buttonText ) );
8600				}
8601			}
8602		} else {
8603			buttonClasses.push( "ui-button-text-only" );
8604		}
8605		buttonElement.addClass( buttonClasses.join( " " ) );
8606	}
8607});
8608
8609$.widget( "ui.buttonset", {
8610	version: "
vendor: 2,068 bytes, lines 8610-8696
8610@VERSION",
8611	options: {
8612		items: "button, input[type=button], input[type=submit], input[type=reset], input[type=checkbox], input[type=radio], a, :data(ui-button)"
8613	},
8614
8615	_create: function() {
8616		this.element.addClass( "ui-buttonset" );
8617	},
8618
8619	_init: function() {
8620		this.refresh();
8621	},
8622
8623	_setOption: function( key, value ) {
8624		if ( key === "disabled" ) {
8625			this.buttons.button( "option", key, value );
8626		}
8627
8628		this._super( key, value );
8629	},
8630
8631	refresh: function() {
8632		var rtl = this.element.css( "direction" ) === "rtl",
8633			allButtons = this.element.find( this.options.items ),
8634			existingButtons = allButtons.filter( ":ui-button" );
8635
8636		// Initialize new buttons
8637		allButtons.not( ":ui-button" ).button();
8638
8639		// Refresh existing buttons
8640		existingButtons.button( "refresh" );
8641
8642		this.buttons = allButtons
8643			.map(function() {
8644				return $( this ).button( "widget" )[ 0 ];
8645			})
8646				.removeClass( "ui-corner-all ui-corner-left ui-corner-right" )
8647				.filter( ":first" )
8648					.addClass( rtl ? "ui-corner-right" : "ui-corner-left" )
8649				.end()
8650				.filter( ":last" )
8651					.addClass( rtl ? "ui-corner-left" : "ui-corner-right" )
8652				.end()
8653			.end();
8654	},
8655
8656	_destroy: function() {
8657		this.element.removeClass( "ui-buttonset" );
8658		this.buttons
8659			.map(function() {
8660				return $( this ).button( "widget" )[ 0 ];
8661			})
8662				.removeClass( "ui-corner-left ui-corner-right" )
8663			.end()
8664			.button( "destroy" );
8665	}
8666});
8667
8668return $.ui.button;
8669
8670}));
8671
8672//>>label: Datepicker
8673//>>group: Widgets
8674//>>description: Displays a calendar from an input or inline for selecting dates.
8675//>>docs: http://api.jqueryui.com/datepicker/
8676//>>demos: http://jqueryui.com/datepicker/
8677//>>css.structure: ../themes/base/core.css
8678//>>css.structure: ../themes/base/datepicker.css
8679//>>css.theme: ../themes/base/theme.css
8680
8681(function( factory ) {
8682	if ( typeof define === "function" && define.amd ) {
8683
8684		// AMD. Register as an anonymous module.
8685		define([
8686			"jquery",
8687			"./core"
8688		], factory );
8689	} else {
8690
8691		// Browser globals
8692		factory( jQuery );
8693	}
8694}(function( $ ) {
8695
8696$.extend($.ui, { datepicker: { version: "@VERSION
vendor: 23,184 bytes, lines 8696-9263
8696" } });
8697
8698var datepicker_instActive;
8699
8700function datepicker_getZindex( elem ) {
8701	var position, value;
8702	while ( elem.length && elem[ 0 ] !== document ) {
8703		// Ignore z-index if position is set to a value where z-index is ignored by the browser
8704		// This makes behavior of this function consistent across browsers
8705		// WebKit always returns auto if the element is positioned
8706		position = elem.css( "position" );
8707		if ( position === "absolute" || position === "relative" || position === "fixed" ) {
8708			// IE returns 0 when zIndex is not specified
8709			// other browsers return a string
8710			// we ignore the case of nested elements with an explicit value of 0
8711			// <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
8712			value = parseInt( elem.css( "zIndex" ), 10 );
8713			if ( !isNaN( value ) && value !== 0 ) {
8714				return value;
8715			}
8716		}
8717		elem = elem.parent();
8718	}
8719
8720	return 0;
8721}
8722/* Date picker manager.
8723   Use the singleton instance of this class, $.datepicker, to interact with the date picker.
8724   Settings for (groups of) date pickers are maintained in an instance object,
8725   allowing multiple different settings on the same page. */
8726
8727function Datepicker() {
8728	this._curInst = null; // The current instance in use
8729	this._keyEvent = false; // If the last event was a key event
8730	this._disabledInputs = []; // List of date picker inputs that have been disabled
8731	this._datepickerShowing = false; // True if the popup picker is showing , false if not
8732	this._inDialog = false; // True if showing within a "dialog", false if not
8733	this._mainDivId = "ui-datepicker-div"; // The ID of the main datepicker division
8734	this._inlineClass = "ui-datepicker-inline"; // The name of the inline marker class
8735	this._appendClass = "ui-datepicker-append"; // The name of the append marker class
8736	this._triggerClass = "ui-datepicker-trigger"; // The name of the trigger marker class
8737	this._dialogClass = "ui-datepicker-dialog"; // The name of the dialog marker class
8738	this._disableClass = "ui-datepicker-disabled"; // The name of the disabled covering marker class
8739	this._unselectableClass = "ui-datepicker-unselectable"; // The name of the unselectable cell marker class
8740	this._currentClass = "ui-datepicker-current-day"; // The name of the current day marker class
8741	this._dayOverClass = "ui-datepicker-days-cell-over"; // The name of the day hover marker class
8742	this.regional = []; // Available regional settings, indexed by language code
8743	this.regional[""] = { // Default regional settings
8744		closeText: "Done", // Display text for close link
8745		prevText: "Prev", // Display text for previous month link
8746		nextText: "Next", // Display text for next month link
8747		currentText: "Today", // Display text for current month link
8748		monthNames: ["January","February","March","April","May","June",
8749			"July","August","September","October","November","December"], // Names of months for drop-down and formatting
8750		monthNamesShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], // For formatting
8751		dayNames: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], // For formatting
8752		dayNamesShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], // For formatting
8753		dayNamesMin: ["Su","Mo","Tu","We","Th","Fr","Sa"], // Column headings for days starting at Sunday
8754		weekHeader: "Wk", // Column header for week of the year
8755		dateFormat: "mm/dd/yy", // See format options on parseDate
8756		firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
8757		isRTL: false, // True if right-to-left language, false if left-to-right
8758		showMonthAfterYear: false, // True if the year select precedes month, false for month then year
8759		yearSuffix: "" // Additional text to append to the year in the month headers
8760	};
8761	this._defaults = { // Global defaults for all the date picker instances
8762		showOn: "focus", // "focus" for popup on focus,
8763			// "button" for trigger button, or "both" for either
8764		showAnim: "fadeIn", // Name of jQuery animation for popup
8765		showOptions: {}, // Options for enhanced animations
8766		defaultDate: null, // Used when field is blank: actual date,
8767			// +/-number for offset from today, null for today
8768		appendText: "", // Display text following the input box, e.g. showing the format
8769		buttonText: "...", // Text for trigger button
8770		buttonImage: "", // URL for trigger button image
8771		buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
8772		hideIfNoPrevNext: false, // True to hide next/previous month links
8773			// if not applicable, false to just disable them
8774		navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links
8775		gotoCurrent: false, // True if today link goes back to current selection instead
8776		changeMonth: false, // True if month can be selected directly, false if only prev/next
8777		changeYear: false, // True if year can be selected directly, false if only prev/next
8778		yearRange: "c-10:c+10", // Range of years to display in drop-down,
8779			// either relative to today's year (-nn:+nn), relative to currently displayed year
8780			// (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n)
8781		showOtherMonths: false, // True to show dates in other months, false to leave blank
8782		selectOtherMonths: false, // True to allow selection of dates in other months, false for unselectable
8783		showWeek: false, // True to show week of the year, false to not show it
8784		calculateWeek: this.iso8601Week, // How to calculate the week of the year,
8785			// takes a Date and returns the number of the week for it
8786		shortYearCutoff: "+10", // Short year values < this are in the current century,
8787			// > this are in the previous century,
8788			// string value starting with "+" for current year + value
8789		minDate: null, // The earliest selectable date, or null for no limit
8790		maxDate: null, // The latest selectable date, or null for no limit
8791		duration: "fast", // Duration of display/closure
8792		beforeShowDay: null, // Function that takes a date and returns an array with
8793			// [0] = true if selectable, false if not, [1] = custom CSS class name(s) or "",
8794			// [2] = cell title (optional), e.g. $.datepicker.noWeekends
8795		beforeShow: null, // Function that takes an input field and
8796			// returns a set of custom settings for the date picker
8797		onSelect: null, // Define a callback function when a date is selected
8798		onChangeMonthYear: null, // Define a callback function when the month or year is changed
8799		onClose: null, // Define a callback function when the datepicker is closed
8800		numberOfMonths: 1, // Number of months to show at a time
8801		showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0)
8802		stepMonths: 1, // Number of months to step back/forward
8803		stepBigMonths: 12, // Number of months to step back/forward for the big links
8804		altField: "", // Selector for an alternate field to store selected dates into
8805		altFormat: "", // The date format to use for the alternate field
8806		constrainInput: true, // The input is constrained by the current date format
8807		showButtonPanel: false, // True to show button panel, false to not show it
8808		autoSize: false, // True to size the input for the date format, false to leave as is
8809		disabled: false // The initial disabled state
8810	};
8811	$.extend(this._defaults, this.regional[""]);
8812	this.regional.en = $.extend( true, {}, this.regional[ "" ]);
8813	this.regional[ "en-US" ] = $.extend( true, {}, this.regional.en );
8814	this.dpDiv = datepicker_bindHover($("<div id='" + this._mainDivId + "' class='ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>"));
8815}
8816
8817$.extend(Datepicker.prototype, {
8818	/* Class name added to elements to indicate already configured with a date picker. */
8819	markerClassName: "hasDatepicker",
8820
8821	//Keep track of the maximum number of rows displayed (see #7043)
8822	maxRows: 4,
8823
8824	// TODO rename to "widget" when switching to widget factory
8825	_widgetDatepicker: function() {
8826		return this.dpDiv;
8827	},
8828
8829	/* Override the default settings for all instances of the date picker.
8830	 * @param  settings  object - the new settings to use as defaults (anonymous object)
8831	 * @return the manager object
8832	 */
8833	setDefaults: function(settings) {
8834		datepicker_extendRemove(this._defaults, settings || {});
8835		return this;
8836	},
8837
8838	/* Attach the date picker to a jQuery selection.
8839	 * @param  target	element - the target input field or division or span
8840	 * @param  settings  object - the new settings to use for this date picker instance (anonymous)
8841	 */
8842	_attachDatepicker: function(target, settings) {
8843		var nodeName, inline, inst;
8844		nodeName = target.nodeName.toLowerCase();
8845		inline = (nodeName === "div" || nodeName === "span");
8846		if (!target.id) {
8847			this.uuid += 1;
8848			target.id = "dp" + this.uuid;
8849		}
8850		inst = this._newInst($(target), inline);
8851		inst.settings = $.extend({}, settings || {});
8852		if (nodeName === "input") {
8853			this._connectDatepicker(target, inst);
8854		} else if (inline) {
8855			this._inlineDatepicker(target, inst);
8856		}
8857	},
8858
8859	/* Create a new instance object. */
8860	_newInst: function(target, inline) {
8861		var id = target[0].id.replace(/([^A-Za-z0-9_\-])/g, "\\\\$1"); // escape jQuery meta chars
8862		return {id: id, input: target, // associated target
8863			selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection
8864			drawMonth: 0, drawYear: 0, // month being drawn
8865			inline: inline, // is datepicker inline or not
8866			dpDiv: (!inline ? this.dpDiv : // presentation div
8867			datepicker_bindHover($("<div class='" + this._inlineClass + " ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>")))};
8868	},
8869
8870	/* Attach the date picker to an input field. */
8871	_connectDatepicker: function(target, inst) {
8872		var input = $(target);
8873		inst.append = $([]);
8874		inst.trigger = $([]);
8875		if (input.hasClass(this.markerClassName)) {
8876			return;
8877		}
8878		this._attachments(input, inst);
8879		input.addClass(this.markerClassName).keydown(this._doKeyDown).
8880			keypress(this._doKeyPress).keyup(this._doKeyUp);
8881		this._autoSize(inst);
8882		$.data(target, "datepicker", inst);
8883		//If disabled option is true, disable the datepicker once it has been attached to the input (see ticket #5665)
8884		if( inst.settings.disabled ) {
8885			this._disableDatepicker( target );
8886		}
8887	},
8888
8889	/* Make attachments based on settings. */
8890	_attachments: function(input, inst) {
8891		var showOn, buttonText, buttonImage,
8892			appendText = this._get(inst, "appendText"),
8893			isRTL = this._get(inst, "isRTL");
8894
8895		if (inst.append) {
8896			inst.append.remove();
8897		}
8898		if (appendText) {
8899			inst.append = $("<span class='" + this._appendClass + "'>" + appendText + "</span>");
8900			input[isRTL ? "before" : "after"](inst.append);
8901		}
8902
8903		input.unbind("focus", this._showDatepicker);
8904
8905		if (inst.trigger) {
8906			inst.trigger.remove();
8907		}
8908
8909		showOn = this._get(inst, "showOn");
8910		if (showOn === "focus" || showOn === "both") { // pop-up date picker when in the marked field
8911			input.focus(this._showDatepicker);
8912		}
8913		if (showOn === "button" || showOn === "both") { // pop-up date picker when button clicked
8914			buttonText = this._get(inst, "buttonText");
8915			buttonImage = this._get(inst, "buttonImage");
8916			inst.trigger = $(this._get(inst, "buttonImageOnly") ?
8917				$("<img/>").addClass(this._triggerClass).
8918					attr({ src: buttonImage, alt: buttonText, title: buttonText }) :
8919				$("<button type='button'></button>").addClass(this._triggerClass).
8920					html(!buttonImage ? buttonText : $("<img/>").attr(
8921					{ src:buttonImage, alt:buttonText, title:buttonText })));
8922			input[isRTL ? "before" : "after"](inst.trigger);
8923			inst.trigger.click(function() {
8924				if ($.datepicker._datepickerShowing && $.datepicker._lastInput === input[0]) {
8925					$.datepicker._hideDatepicker();
8926				} else if ($.datepicker._datepickerShowing && $.datepicker._lastInput !== input[0]) {
8927					$.datepicker._hideDatepicker();
8928					$.datepicker._showDatepicker(input[0]);
8929				} else {
8930					$.datepicker._showDatepicker(input[0]);
8931				}
8932				return false;
8933			});
8934		}
8935	},
8936
8937	/* Apply the maximum length for the date format. */
8938	_autoSize: function(inst) {
8939		if (this._get(inst, "autoSize") && !inst.inline) {
8940			var findMax, max, maxI, i,
8941				date = new Date(2009, 12 - 1, 20), // Ensure double digits
8942				dateFormat = this._get(inst, "dateFormat");
8943
8944			if (dateFormat.match(/[DM]/)) {
8945				findMax = function(names) {
8946					max = 0;
8947					maxI = 0;
8948					for (i = 0; i < names.length; i++) {
8949						if (names[i].length > max) {
8950							max = names[i].length;
8951							maxI = i;
8952						}
8953					}
8954					return maxI;
8955				};
8956				date.setMonth(findMax(this._get(inst, (dateFormat.match(/MM/) ?
8957					"monthNames" : "monthNamesShort"))));
8958				date.setDate(findMax(this._get(inst, (dateFormat.match(/DD/) ?
8959					"dayNames" : "dayNamesShort"))) + 20 - date.getDay());
8960			}
8961			inst.input.attr("size", this._formatDate(inst, date).length);
8962		}
8963	},
8964
8965	/* Attach an inline date picker to a div. */
8966	_inlineDatepicker: function(target, inst) {
8967		var divSpan = $(target);
8968		if (divSpan.hasClass(this.markerClassName)) {
8969			return;
8970		}
8971		divSpan.addClass(this.markerClassName).append(inst.dpDiv);
8972		$.data(target, "datepicker", inst);
8973		this._setDate(inst, this._getDefaultDate(inst), true);
8974		this._updateDatepicker(inst);
8975		this._updateAlternate(inst);
8976		//If disabled option is true, disable the datepicker before showing it (see ticket #5665)
8977		if( inst.settings.disabled ) {
8978			this._disableDatepicker( target );
8979		}
8980		// Set display:block in place of inst.dpDiv.show() which won't work on disconnected elements
8981		// http://bugs.jqueryui.com/ticket/7552 - A Datepicker created on a detached div has zero height
8982		inst.dpDiv.css( "display", "block" );
8983	},
8984
8985	/* Pop-up the date picker in a "dialog" box.
8986	 * @param  input element - ignored
8987	 * @param  date	string or Date - the initial date to display
8988	 * @param  onSelect  function - the function to call when a date is selected
8989	 * @param  settings  object - update the dialog date picker instance's settings (anonymous object)
8990	 * @param  pos int[2] - coordinates for the dialog's position within the screen or
8991	 *					event - with x/y coordinates or
8992	 *					leave empty for default (screen centre)
8993	 * @return the manager object
8994	 */
8995	_dialogDatepicker: function(input, date, onSelect, settings, pos) {
8996		var id, browserWidth, browserHeight, scrollX, scrollY,
8997			inst = this._dialogInst; // internal instance
8998
8999		if (!inst) {
9000			this.uuid += 1;
9001			id = "dp" + this.uuid;
9002			this._dialogInput = $("<input type='text' id='" + id +
9003				"' style='position: absolute; top: -100px; width: 0px;'/>");
9004			this._dialogInput.keydown(this._doKeyDown);
9005			$("body").append(this._dialogInput);
9006			inst = this._dialogInst = this._newInst(this._dialogInput, false);
9007			inst.settings = {};
9008			$.data(this._dialogInput[0], "datepicker", inst);
9009		}
9010		datepicker_extendRemove(inst.settings, settings || {});
9011		date = (date && date.constructor === Date ? this._formatDate(inst, date) : date);
9012		this._dialogInput.val(date);
9013
9014		this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null);
9015		if (!this._pos) {
9016			browserWidth = document.documentElement.clientWidth;
9017			browserHeight = document.documentElement.clientHeight;
9018			scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
9019			scrollY = document.documentElement.scrollTop || document.body.scrollTop;
9020			this._pos = // should use actual width/height below
9021				[(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY];
9022		}
9023
9024		// move input on screen for focus, but hidden behind dialog
9025		this._dialogInput.css("left", (this._pos[0] + 20) + "px").css("top", this._pos[1] + "px");
9026		inst.settings.onSelect = onSelect;
9027		this._inDialog = true;
9028		this.dpDiv.addClass(this._dialogClass);
9029		this._showDatepicker(this._dialogInput[0]);
9030		if ($.blockUI) {
9031			$.blockUI(this.dpDiv);
9032		}
9033		$.data(this._dialogInput[0], "datepicker", inst);
9034		return this;
9035	},
9036
9037	/* Detach a datepicker from its control.
9038	 * @param  target	element - the target input field or division or span
9039	 */
9040	_destroyDatepicker: function(target) {
9041		var nodeName,
9042			$target = $(target),
9043			inst = $.data(target, "datepicker");
9044
9045		if (!$target.hasClass(this.markerClassName)) {
9046			return;
9047		}
9048
9049		nodeName = target.nodeName.toLowerCase();
9050		$.removeData(target, "datepicker");
9051		if (nodeName === "input") {
9052			inst.append.remove();
9053			inst.trigger.remove();
9054			$target.removeClass(this.markerClassName).
9055				unbind("focus", this._showDatepicker).
9056				unbind("keydown", this._doKeyDown).
9057				unbind("keypress", this._doKeyPress).
9058				unbind("keyup", this._doKeyUp);
9059		} else if (nodeName === "div" || nodeName === "span") {
9060			$target.removeClass(this.markerClassName).empty();
9061		}
9062
9063		if ( datepicker_instActive === inst ) {
9064			datepicker_instActive = null;
9065		}
9066	},
9067
9068	/* Enable the date picker to a jQuery selection.
9069	 * @param  target	element - the target input field or division or span
9070	 */
9071	_enableDatepicker: function(target) {
9072		var nodeName, inline,
9073			$target = $(target),
9074			inst = $.data(target, "datepicker");
9075
9076		if (!$target.hasClass(this.markerClassName)) {
9077			return;
9078		}
9079
9080		nodeName = target.nodeName.toLowerCase();
9081		if (nodeName === "input") {
9082			target.disabled = false;
9083			inst.trigger.filter("button").
9084				each(function() { this.disabled = false; }).end().
9085				filter("img").css({opacity: "1.0", cursor: ""});
9086		} else if (nodeName === "div" || nodeName === "span") {
9087			inline = $target.children("." + this._inlineClass);
9088			inline.children().removeClass("ui-state-disabled");
9089			inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
9090				prop("disabled", false);
9091		}
9092		this._disabledInputs = $.map(this._disabledInputs,
9093			function(value) { return (value === target ? null : value); }); // delete entry
9094	},
9095
9096	/* Disable the date picker to a jQuery selection.
9097	 * @param  target	element - the target input field or division or span
9098	 */
9099	_disableDatepicker: function(target) {
9100		var nodeName, inline,
9101			$target = $(target),
9102			inst = $.data(target, "datepicker");
9103
9104		if (!$target.hasClass(this.markerClassName)) {
9105			return;
9106		}
9107
9108		nodeName = target.nodeName.toLowerCase();
9109		if (nodeName === "input") {
9110			target.disabled = true;
9111			inst.trigger.filter("button").
9112				each(function() { this.disabled = true; }).end().
9113				filter("img").css({opacity: "0.5", cursor: "default"});
9114		} else if (nodeName === "div" || nodeName === "span") {
9115			inline = $target.children("." + this._inlineClass);
9116			inline.children().addClass("ui-state-disabled");
9117			inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
9118				prop("disabled", true);
9119		}
9120		this._disabledInputs = $.map(this._disabledInputs,
9121			function(value) { return (value === target ? null : value); }); // delete entry
9122		this._disabledInputs[this._disabledInputs.length] = target;
9123	},
9124
9125	/* Is the first field in a jQuery collection disabled as a datepicker?
9126	 * @param  target	element - the target input field or division or span
9127	 * @return boolean - true if disabled, false if enabled
9128	 */
9129	_isDisabledDatepicker: function(target) {
9130		if (!target) {
9131			return false;
9132		}
9133		for (var i = 0; i < this._disabledInputs.length; i++) {
9134			if (this._disabledInputs[i] === target) {
9135				return true;
9136			}
9137		}
9138		return false;
9139	},
9140
9141	/* Retrieve the instance data for the target control.
9142	 * @param  target  element - the target input field or division or span
9143	 * @return  object - the associated instance data
9144	 * @throws  error if a jQuery problem getting data
9145	 */
9146	_getInst: function(target) {
9147		try {
9148			return $.data(target, "datepicker");
9149		}
9150		catch (err) {
9151			throw "Missing instance data for this datepicker";
9152		}
9153	},
9154
9155	/* Update or retrieve the settings for a date picker attached to an input field or division.
9156	 * @param  target  element - the target input field or division or span
9157	 * @param  name	object - the new settings to update or
9158	 *				string - the name of the setting to change or retrieve,
9159	 *				when retrieving also "all" for all instance settings or
9160	 *				"defaults" for all global defaults
9161	 * @param  value   any - the new value for the setting
9162	 *				(omit if above is an object or to retrieve a value)
9163	 */
9164	_optionDatepicker: function(target, name, value) {
9165		var settings, date, minDate, maxDate,
9166			inst = this._getInst(target);
9167
9168		if (arguments.length === 2 && typeof name === "string") {
9169			return (name === "defaults" ? $.extend({}, $.datepicker._defaults) :
9170				(inst ? (name === "all" ? $.extend({}, inst.settings) :
9171				this._get(inst, name)) : null));
9172		}
9173
9174		settings = name || {};
9175		if (typeof name === "string") {
9176			settings = {};
9177			settings[name] = value;
9178		}
9179
9180		if (inst) {
9181			if (this._curInst === inst) {
9182				this._hideDatepicker();
9183			}
9184
9185			date = this._getDateDatepicker(target, true);
9186			minDate = this._getMinMaxDate(inst, "min");
9187			maxDate = this._getMinMaxDate(inst, "max");
9188			datepicker_extendRemove(inst.settings, settings);
9189			// reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided
9190			if (minDate !== null && settings.dateFormat !== undefined && settings.minDate === undefined) {
9191				inst.settings.minDate = this._formatDate(inst, minDate);
9192			}
9193			if (maxDate !== null && settings.dateFormat !== undefined && settings.maxDate === undefined) {
9194				inst.settings.maxDate = this._formatDate(inst, maxDate);
9195			}
9196			if ( "disabled" in settings ) {
9197				if ( settings.disabled ) {
9198					this._disableDatepicker(target);
9199				} else {
9200					this._enableDatepicker(target);
9201				}
9202			}
9203			this._attachments($(target), inst);
9204			this._autoSize(inst);
9205			this._setDate(inst, date);
9206			this._updateAlternate(inst);
9207			this._updateDatepicker(inst);
9208		}
9209	},
9210
9211	// change method deprecated
9212	_changeDatepicker: function(target, name, value) {
9213		this._optionDatepicker(target, name, value);
9214	},
9215
9216	/* Redraw the date picker attached to an input field or division.
9217	 * @param  target  element - the target input field or division or span
9218	 */
9219	_refreshDatepicker: function(target) {
9220		var inst = this._getInst(target);
9221		if (inst) {
9222			this._updateDatepicker(inst);
9223		}
9224	},
9225
9226	/* Set the dates for a jQuery selection.
9227	 * @param  target element - the target input field or division or span
9228	 * @param  date	Date - the new date
9229	 */
9230	_setDateDatepicker: function(target, date) {
9231		var inst = this._getInst(target);
9232		if (inst) {
9233			this._setDate(inst, date);
9234			this._updateDatepicker(inst);
9235			this._updateAlternate(inst);
9236		}
9237	},
9238
9239	/* Get the date(s) for the first entry in a jQuery selection.
9240	 * @param  target element - the target input field or division or span
9241	 * @param  noDefault boolean - true if no default date is to be used
9242	 * @return Date - the current date
9243	 */
9244	_getDateDatepicker: function(target, noDefault) {
9245		var inst = this._getInst(target);
9246		if (inst && !inst.inline) {
9247			this._setDateFromField(inst, noDefault);
9248		}
9249		return (inst ? this._getDate(inst) : null);
9250	},
9251
9252	/* Handle keystrokes. */
9253	_doKeyDown: function(event) {
9254		var onSelect, dateStr, sel,
9255			inst = $.datepicker._getInst(event.target),
9256			handled = true,
9257			isRTL = inst.dpDiv.is(".ui-datepicker-rtl");
9258
9259		inst._keyEvent = true;
9260		if ($.datepicker._datepickerShowing) {
9261			switch (event.keyCode) {
9262				case 9: $.datepicker._hideDatepicker();
9263						handled = false;
vendor: 4,495 bytes, lines 9264-9387
9264						break; // hide on tab out
9265				case 13: sel = $("td." + $.datepicker._dayOverClass + ":not(." +
9266									$.datepicker._currentClass + ")", inst.dpDiv);
9267						if (sel[0]) {
9268							$.datepicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, sel[0]);
9269						}
9270
9271						onSelect = $.datepicker._get(inst, "onSelect");
9272						if (onSelect) {
9273							dateStr = $.datepicker._formatDate(inst);
9274
9275							// trigger custom callback
9276							onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]);
9277						} else {
9278							$.datepicker._hideDatepicker();
9279						}
9280
9281						return false; // don't submit the form
9282				case 27: $.datepicker._hideDatepicker();
9283						break; // hide on escape
9284				case 33: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
9285							-$.datepicker._get(inst, "stepBigMonths") :
9286							-$.datepicker._get(inst, "stepMonths")), "M");
9287						break; // previous month/year on page up/+ ctrl
9288				case 34: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
9289							+$.datepicker._get(inst, "stepBigMonths") :
9290							+$.datepicker._get(inst, "stepMonths")), "M");
9291						break; // next month/year on page down/+ ctrl
9292				case 35: if (event.ctrlKey || event.metaKey) {
9293							$.datepicker._clearDate(event.target);
9294						}
9295						handled = event.ctrlKey || event.metaKey;
9296						break; // clear on ctrl or command +end
9297				case 36: if (event.ctrlKey || event.metaKey) {
9298							$.datepicker._gotoToday(event.target);
9299						}
9300						handled = event.ctrlKey || event.metaKey;
9301						break; // current on ctrl or command +home
9302				case 37: if (event.ctrlKey || event.metaKey) {
9303							$.datepicker._adjustDate(event.target, (isRTL ? +1 : -1), "D");
9304						}
9305						handled = event.ctrlKey || event.metaKey;
9306						// -1 day on ctrl or command +left
9307						if (event.originalEvent.altKey) {
9308							$.datepicker._adjustDate(event.target, (event.ctrlKey ?
9309								-$.datepicker._get(inst, "stepBigMonths") :
9310								-$.datepicker._get(inst, "stepMonths")), "M");
9311						}
9312						// next month/year on alt +left on Mac
9313						break;
9314				case 38: if (event.ctrlKey || event.metaKey) {
9315							$.datepicker._adjustDate(event.target, -7, "D");
9316						}
9317						handled = event.ctrlKey || event.metaKey;
9318						break; // -1 week on ctrl or command +up
9319				case 39: if (event.ctrlKey || event.metaKey) {
9320							$.datepicker._adjustDate(event.target, (isRTL ? -1 : +1), "D");
9321						}
9322						handled = event.ctrlKey || event.metaKey;
9323						// +1 day on ctrl or command +right
9324						if (event.originalEvent.altKey) {
9325							$.datepicker._adjustDate(event.target, (event.ctrlKey ?
9326								+$.datepicker._get(inst, "stepBigMonths") :
9327								+$.datepicker._get(inst, "stepMonths")), "M");
9328						}
9329						// next month/year on alt +right
9330						break;
9331				case 40: if (event.ctrlKey || event.metaKey) {
9332							$.datepicker._adjustDate(event.target, +7, "D");
9333						}
9334						handled = event.ctrlKey || event.metaKey;
9335						break; // +1 week on ctrl or command +down
9336				default: handled = false;
9337			}
9338		} else if (event.keyCode === 36 && event.ctrlKey) { // display the date picker on ctrl+home
9339			$.datepicker._showDatepicker(this);
9340		} else {
9341			handled = false;
9342		}
9343
9344		if (handled) {
9345			event.preventDefault();
9346			event.stopPropagation();
9347		}
9348	},
9349
9350	/* Filter entered characters - based on date format. */
9351	_doKeyPress: function(event) {
9352		var chars, chr,
9353			inst = $.datepicker._getInst(event.target);
9354
9355		if ($.datepicker._get(inst, "constrainInput")) {
9356			chars = $.datepicker._possibleChars($.datepicker._get(inst, "dateFormat"));
9357			chr = String.fromCharCode(event.charCode == null ? event.keyCode : event.charCode);
9358			return event.ctrlKey || event.metaKey || (chr < " " || !chars || chars.indexOf(chr) > -1);
9359		}
9360	},
9361
9362	/* Synchronise manual entry and field/alternate field. */
9363	_doKeyUp: function(event) {
9364		var date,
9365			inst = $.datepicker._getInst(event.target);
9366
9367		if (inst.input.val() !== inst.lastVal) {
9368			try {
9369				date = $.datepicker.parseDate($.datepicker._get(inst, "dateFormat"),
9370					(inst.input ? inst.input.val() : null),
9371					$.datepicker._getFormatConfig(inst));
9372
9373				if (date) { // only if valid
9374					$.datepicker._setDateFromField(inst);
9375					$.datepicker._updateAlternate(inst);
9376					$.datepicker._updateDatepicker(inst);
9377				}
9378			}
9379			catch (err) {
9380			}
9381		}
9382		return true;
9383	},
9384
9385	/* Pop-up the date picker for a given input field.
9386	 * If false returned from beforeShow event handler do not show.
9387	 * @param  input  element - the input field attached to the date picker or
vendor: 9,398 bytes, lines 9388-9662
9388	 *					event - if triggered by focus
9389	 */
9390	_showDatepicker: function(input) {
9391		input = input.target || input;
9392		if (input.nodeName.toLowerCase() !== "input") { // find from button/image trigger
9393			input = $("input", input.parentNode)[0];
9394		}
9395
9396		if ($.datepicker._isDisabledDatepicker(input) || $.datepicker._lastInput === input) { // already here
9397			return;
9398		}
9399
9400		var inst, beforeShow, beforeShowSettings, isFixed,
9401			offset, showAnim, duration;
9402
9403		inst = $.datepicker._getInst(input);
9404		if ($.datepicker._curInst && $.datepicker._curInst !== inst) {
9405			$.datepicker._curInst.dpDiv.stop(true, true);
9406			if ( inst && $.datepicker._datepickerShowing ) {
9407				$.datepicker._hideDatepicker( $.datepicker._curInst.input[0] );
9408			}
9409		}
9410
9411		beforeShow = $.datepicker._get(inst, "beforeShow");
9412		beforeShowSettings = beforeShow ? beforeShow.apply(input, [input, inst]) : {};
9413		if(beforeShowSettings === false){
9414			return;
9415		}
9416		datepicker_extendRemove(inst.settings, beforeShowSettings);
9417
9418		inst.lastVal = null;
9419		$.datepicker._lastInput = input;
9420		$.datepicker._setDateFromField(inst);
9421
9422		if ($.datepicker._inDialog) { // hide cursor
9423			input.value = "";
9424		}
9425		if (!$.datepicker._pos) { // position below input
9426			$.datepicker._pos = $.datepicker._findPos(input);
9427			$.datepicker._pos[1] += input.offsetHeight; // add the height
9428		}
9429
9430		isFixed = false;
9431		$(input).parents().each(function() {
9432			isFixed |= $(this).css("position") === "fixed";
9433			return !isFixed;
9434		});
9435
9436		offset = {left: $.datepicker._pos[0], top: $.datepicker._pos[1]};
9437		$.datepicker._pos = null;
9438		//to avoid flashes on Firefox
9439		inst.dpDiv.empty();
9440		// determine sizing offscreen
9441		inst.dpDiv.css({position: "absolute", display: "block", top: "-1000px"});
9442		$.datepicker._updateDatepicker(inst);
9443		// fix width for dynamic number of date pickers
9444		// and adjust position before showing
9445		offset = $.datepicker._checkOffset(inst, offset, isFixed);
9446		inst.dpDiv.css({position: ($.datepicker._inDialog && $.blockUI ?
9447			"static" : (isFixed ? "fixed" : "absolute")), display: "none",
9448			left: offset.left + "px", top: offset.top + "px"});
9449
9450		if (!inst.inline) {
9451			showAnim = $.datepicker._get(inst, "showAnim");
9452			duration = $.datepicker._get(inst, "duration");
9453			inst.dpDiv.css( "z-index", datepicker_getZindex( $( input ) ) + 1 );
9454			$.datepicker._datepickerShowing = true;
9455
9456			if ( $.effects && $.effects.effect[ showAnim ] ) {
9457				inst.dpDiv.show(showAnim, $.datepicker._get(inst, "showOptions"), duration);
9458			} else {
9459				inst.dpDiv[showAnim || "show"](showAnim ? duration : null);
9460			}
9461
9462			if ( $.datepicker._shouldFocusInput( inst ) ) {
9463				inst.input.focus();
9464			}
9465
9466			$.datepicker._curInst = inst;
9467		}
9468	},
9469
9470	/* Generate the date picker content. */
9471	_updateDatepicker: function(inst) {
9472		this.maxRows = 4; //Reset the max number of rows being displayed (see #7043)
9473		datepicker_instActive = inst; // for delegate hover events
9474		inst.dpDiv.empty().append(this._generateHTML(inst));
9475		this._attachHandlers(inst);
9476
9477		var origyearshtml,
9478			numMonths = this._getNumberOfMonths(inst),
9479			cols = numMonths[1],
9480			width = 17,
9481			activeCell = inst.dpDiv.find( "." + this._dayOverClass + " a" );
9482
9483		if ( activeCell.length > 0 ) {
9484			datepicker_handleMouseover.apply( activeCell.get( 0 ) );
9485		}
9486
9487		inst.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width("");
9488		if (cols > 1) {
9489			inst.dpDiv.addClass("ui-datepicker-multi-" + cols).css("width", (width * cols) + "em");
9490		}
9491		inst.dpDiv[(numMonths[0] !== 1 || numMonths[1] !== 1 ? "add" : "remove") +
9492			"Class"]("ui-datepicker-multi");
9493		inst.dpDiv[(this._get(inst, "isRTL") ? "add" : "remove") +
9494			"Class"]("ui-datepicker-rtl");
9495
9496		if (inst === $.datepicker._curInst && $.datepicker._datepickerShowing && $.datepicker._shouldFocusInput( inst ) ) {
9497			inst.input.focus();
9498		}
9499
9500		// deffered render of the years select (to avoid flashes on Firefox)
9501		if( inst.yearshtml ){
9502			origyearshtml = inst.yearshtml;
9503			setTimeout(function(){
9504				//assure that inst.yearshtml didn't change.
9505				if( origyearshtml === inst.yearshtml && inst.yearshtml ){
9506					inst.dpDiv.find("select.ui-datepicker-year:first").replaceWith(inst.yearshtml);
9507				}
9508				origyearshtml = inst.yearshtml = null;
9509			}, 0);
9510		}
9511	},
9512
9513	// #6694 - don't focus the input if it's already focused
9514	// this breaks the change event in IE
9515	// Support: IE and jQuery <1.9
9516	_shouldFocusInput: function( inst ) {
9517		return inst.input && inst.input.is( ":visible" ) && !inst.input.is( ":disabled" ) && !inst.input.is( ":focus" );
9518	},
9519
9520	/* Check positioning to remain on screen. */
9521	_checkOffset: function(inst, offset, isFixed) {
9522		var dpWidth = inst.dpDiv.outerWidth(),
9523			dpHeight = inst.dpDiv.outerHeight(),
9524			inputWidth = inst.input ? inst.input.outerWidth() : 0,
9525			inputHeight = inst.input ? inst.input.outerHeight() : 0,
9526			viewWidth = document.documentElement.clientWidth + (isFixed ? 0 : $(document).scrollLeft()),
9527			viewHeight = document.documentElement.clientHeight + (isFixed ? 0 : $(document).scrollTop());
9528
9529		offset.left -= (this._get(inst, "isRTL") ? (dpWidth - inputWidth) : 0);
9530		offset.left -= (isFixed && offset.left === inst.input.offset().left) ? $(document).scrollLeft() : 0;
9531		offset.top -= (isFixed && offset.top === (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0;
9532
9533		// now check if datepicker is showing outside window viewport - move to a better place if so.
9534		offset.left -= Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ?
9535			Math.abs(offset.left + dpWidth - viewWidth) : 0);
9536		offset.top -= Math.min(offset.top, (offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ?
9537			Math.abs(dpHeight + inputHeight) : 0);
9538
9539		return offset;
9540	},
9541
9542	/* Find an object's position on the screen. */
9543	_findPos: function(obj) {
9544		var position,
9545			inst = this._getInst(obj),
9546			isRTL = this._get(inst, "isRTL");
9547
9548		while (obj && (obj.type === "hidden" || obj.nodeType !== 1 || $.expr.filters.hidden(obj))) {
9549			obj = obj[isRTL ? "previousSibling" : "nextSibling"];
9550		}
9551
9552		position = $(obj).offset();
9553		return [position.left, position.top];
9554	},
9555
9556	/* Hide the date picker from view.
9557	 * @param  input  element - the input field attached to the date picker
9558	 */
9559	_hideDatepicker: function(input) {
9560		var showAnim, duration, postProcess, onClose,
9561			inst = this._curInst;
9562
9563		if (!inst || (input && inst !== $.data(input, "datepicker"))) {
9564			return;
9565		}
9566
9567		if (this._datepickerShowing) {
9568			showAnim = this._get(inst, "showAnim");
9569			duration = this._get(inst, "duration");
9570			postProcess = function() {
9571				$.datepicker._tidyDialog(inst);
9572			};
9573
9574			// DEPRECATED: after BC for 1.8.x $.effects[ showAnim ] is not needed
9575			if ( $.effects && ( $.effects.effect[ showAnim ] || $.effects[ showAnim ] ) ) {
9576				inst.dpDiv.hide(showAnim, $.datepicker._get(inst, "showOptions"), duration, postProcess);
9577			} else {
9578				inst.dpDiv[(showAnim === "slideDown" ? "slideUp" :
9579					(showAnim === "fadeIn" ? "fadeOut" : "hide"))]((showAnim ? duration : null), postProcess);
9580			}
9581
9582			if (!showAnim) {
9583				postProcess();
9584			}
9585			this._datepickerShowing = false;
9586
9587			onClose = this._get(inst, "onClose");
9588			if (onClose) {
9589				onClose.apply((inst.input ? inst.input[0] : null), [(inst.input ? inst.input.val() : ""), inst]);
9590			}
9591
9592			this._lastInput = null;
9593			if (this._inDialog) {
9594				this._dialogInput.css({ position: "absolute", left: "0", top: "-100px" });
9595				if ($.blockUI) {
9596					$.unblockUI();
9597					$("body").append(this.dpDiv);
9598				}
9599			}
9600			this._inDialog = false;
9601		}
9602	},
9603
9604	/* Tidy up after a dialog display. */
9605	_tidyDialog: function(inst) {
9606		inst.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar");
9607	},
9608
9609	/* Close date picker if clicked elsewhere. */
9610	_checkExternalClick: function(event) {
9611		if (!$.datepicker._curInst) {
9612			return;
9613		}
9614
9615		var $target = $(event.target),
9616			inst = $.datepicker._getInst($target[0]);
9617
9618		if ( ( ( $target[0].id !== $.datepicker._mainDivId &&
9619				$target.parents("#" + $.datepicker._mainDivId).length === 0 &&
9620				!$target.hasClass($.datepicker.markerClassName) &&
9621				!$target.closest("." + $.datepicker._triggerClass).length &&
9622				$.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI) ) ) ||
9623			( $target.hasClass($.datepicker.markerClassName) && $.datepicker._curInst !== inst ) ) {
9624				$.datepicker._hideDatepicker();
9625		}
9626	},
9627
9628	/* Adjust one of the date sub-fields. */
9629	_adjustDate: function(id, offset, period) {
9630		var target = $(id),
9631			inst = this._getInst(target[0]);
9632
9633		if (this._isDisabledDatepicker(target[0])) {
9634			return;
9635		}
9636		this._adjustInstDate(inst, offset +
9637			(period === "M" ? this._get(inst, "showCurrentAtPos") : 0), // undo positioning
9638			period);
9639		this._updateDatepicker(inst);
9640	},
9641
9642	/* Action for current link. */
9643	_gotoToday: function(id) {
9644		var date,
9645			target = $(id),
9646			inst = this._getInst(target[0]);
9647
9648		if (this._get(inst, "gotoCurrent") && inst.currentDay) {
9649			inst.selectedDay = inst.currentDay;
9650			inst.drawMonth = inst.selectedMonth = inst.currentMonth;
9651			inst.drawYear = inst.selectedYear = inst.currentYear;
9652		} else {
9653			date = new Date();
9654			inst.selectedDay = date.getDate();
9655			inst.drawMonth = inst.selectedMonth = date.getMonth();
9656			inst.drawYear = inst.selectedYear = date.getFullYear();
9657		}
9658		this._notifyChange(inst);
9659		this._adjustDate(target);
9660	},
9661
9662	/* Action for selecting a new month/year. */
vendor: 4,436 bytes, lines 9663-9794
9663	_selectMonthYear: function(id, select, period) {
9664		var target = $(id),
9665			inst = this._getInst(target[0]);
9666
9667		inst["selected" + (period === "M" ? "Month" : "Year")] =
9668		inst["draw" + (period === "M" ? "Month" : "Year")] =
9669			parseInt(select.options[select.selectedIndex].value,10);
9670
9671		this._notifyChange(inst);
9672		this._adjustDate(target);
9673	},
9674
9675	/* Action for selecting a day. */
9676	_selectDay: function(id, month, year, td) {
9677		var inst,
9678			target = $(id);
9679
9680		if ($(td).hasClass(this._unselectableClass) || this._isDisabledDatepicker(target[0])) {
9681			return;
9682		}
9683
9684		inst = this._getInst(target[0]);
9685		inst.selectedDay = inst.currentDay = $("a", td).html();
9686		inst.selectedMonth = inst.currentMonth = month;
9687		inst.selectedYear = inst.currentYear = year;
9688		this._selectDate(id, this._formatDate(inst,
9689			inst.currentDay, inst.currentMonth, inst.currentYear));
9690	},
9691
9692	/* Erase the input field and hide the date picker. */
9693	_clearDate: function(id) {
9694		var target = $(id);
9695		this._selectDate(target, "");
9696	},
9697
9698	/* Update the input field with the selected date. */
9699	_selectDate: function(id, dateStr) {
9700		var onSelect,
9701			target = $(id),
9702			inst = this._getInst(target[0]);
9703
9704		dateStr = (dateStr != null ? dateStr : this._formatDate(inst));
9705		if (inst.input) {
9706			inst.input.val(dateStr);
9707		}
9708		this._updateAlternate(inst);
9709
9710		onSelect = this._get(inst, "onSelect");
9711		if (onSelect) {
9712			onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]);  // trigger custom callback
9713		} else if (inst.input) {
9714			inst.input.trigger("change"); // fire the change event
9715		}
9716
9717		if (inst.inline){
9718			this._updateDatepicker(inst);
9719		} else {
9720			this._hideDatepicker();
9721			this._lastInput = inst.input[0];
9722			if (typeof(inst.input[0]) !== "object") {
9723				inst.input.focus(); // restore focus
9724			}
9725			this._lastInput = null;
9726		}
9727	},
9728
9729	/* Update any alternate field to synchronise with the main field. */
9730	_updateAlternate: function(inst) {
9731		var altFormat, date, dateStr,
9732			altField = this._get(inst, "altField");
9733
9734		if (altField) { // update alternate field too
9735			altFormat = this._get(inst, "altFormat") || this._get(inst, "dateFormat");
9736			date = this._getDate(inst);
9737			dateStr = this.formatDate(altFormat, date, this._getFormatConfig(inst));
9738			$(altField).each(function() { $(this).val(dateStr); });
9739		}
9740	},
9741
9742	/* Set as beforeShowDay function to prevent selection of weekends.
9743	 * @param  date  Date - the date to customise
9744	 * @return [boolean, string] - is this date selectable?, what is its CSS class?
9745	 */
9746	noWeekends: function(date) {
9747		var day = date.getDay();
9748		return [(day > 0 && day < 6), ""];
9749	},
9750
9751	/* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
9752	 * @param  date  Date - the date to get the week for
9753	 * @return  number - the number of the week within the year that contains this date
9754	 */
9755	iso8601Week: function(date) {
9756		var time,
9757			checkDate = new Date(date.getTime());
9758
9759		// Find Thursday of this week starting on Monday
9760		checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7));
9761
9762		time = checkDate.getTime();
9763		checkDate.setMonth(0); // Compare with Jan 1
9764		checkDate.setDate(1);
9765		return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
9766	},
9767
9768	/* Parse a string value into a date object.
9769	 * See formatDate below for the possible formats.
9770	 *
9771	 * @param  format string - the expected format of the date
9772	 * @param  value string - the date in the above format
9773	 * @param  settings Object - attributes include:
9774	 *					shortYearCutoff  number - the cutoff year for determining the century (optional)
9775	 *					dayNamesShort	string[7] - abbreviated names of the days from Sunday (optional)
9776	 *					dayNames		string[7] - names of the days from Sunday (optional)
9777	 *					monthNamesShort string[12] - abbreviated names of the months (optional)
9778	 *					monthNames		string[12] - names of the months (optional)
9779	 * @return  Date - the extracted date value or null if value is blank
9780	 */
9781	parseDate: function (format, value, settings) {
9782		if (format == null || value == null) {
9783			throw "Invalid arguments";
9784		}
9785
9786		value = (typeof value === "object" ? value.toString() : value + "");
9787		if (value === "") {
9788			return null;
9789		}
9790
9791		var iFormat, dim, extra,
9792			iValue = 0,
9793			shortYearCutoffTemp = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff,
9794			shortYearCutoff = (typeof shortYearCutoffTemp !== "string" ? shortYearCutoffTemp 
vendor: 5,261 bytes, lines 9794-9980
9794:
9795				new Date().getFullYear() % 100 + parseInt(shortYearCutoffTemp, 10)),
9796			dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort,
9797			dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames,
9798			monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort,
9799			monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames,
9800			year = -1,
9801			month = -1,
9802			day = -1,
9803			doy = -1,
9804			literal = false,
9805			date,
9806			// Check whether a format character is doubled
9807			lookAhead = function(match) {
9808				var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
9809				if (matches) {
9810					iFormat++;
9811				}
9812				return matches;
9813			},
9814			// Extract a number from the string value
9815			getNumber = function(match) {
9816				var isDoubled = lookAhead(match),
9817					size = (match === "@" ? 14 : (match === "!" ? 20 :
9818					(match === "y" && isDoubled ? 4 : (match === "o" ? 3 : 2)))),
9819					minSize = (match === "y" ? size : 1),
9820					digits = new RegExp("^\\d{" + minSize + "," + size + "}"),
9821					num = value.substring(iValue).match(digits);
9822				if (!num) {
9823					throw "Missing number at position " + iValue;
9824				}
9825				iValue += num[0].length;
9826				return parseInt(num[0], 10);
9827			},
9828			// Extract a name from the string value and convert to an index
9829			getName = function(match, shortNames, longNames) {
9830				var index = -1,
9831					names = $.map(lookAhead(match) ? longNames : shortNames, function (v, k) {
9832						return [ [k, v] ];
9833					}).sort(function (a, b) {
9834						return -(a[1].length - b[1].length);
9835					});
9836
9837				$.each(names, function (i, pair) {
9838					var name = pair[1];
9839					if (value.substr(iValue, name.length).toLowerCase() === name.toLowerCase()) {
9840						index = pair[0];
9841						iValue += name.length;
9842						return false;
9843					}
9844				});
9845				if (index !== -1) {
9846					return index + 1;
9847				} else {
9848					throw "Unknown name at position " + iValue;
9849				}
9850			},
9851			// Confirm that a literal character matches the string value
9852			checkLiteral = function() {
9853				if (value.charAt(iValue) !== format.charAt(iFormat)) {
9854					throw "Unexpected literal at position " + iValue;
9855				}
9856				iValue++;
9857			};
9858
9859		for (iFormat = 0; iFormat < format.length; iFormat++) {
9860			if (literal) {
9861				if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
9862					literal = false;
9863				} else {
9864					checkLiteral();
9865				}
9866			} else {
9867				switch (format.charAt(iFormat)) {
9868					case "d":
9869						day = getNumber("d");
9870						break;
9871					case "D":
9872						getName("D", dayNamesShort, dayNames);
9873						break;
9874					case "o":
9875						doy = getNumber("o");
9876						break;
9877					case "m":
9878						month = getNumber("m");
9879						break;
9880					case "M":
9881						month = getName("M", monthNamesShort, monthNames);
9882						break;
9883					case "y":
9884						year = getNumber("y");
9885						break;
9886					case "@":
9887						date = new Date(getNumber("@"));
9888						year = date.getFullYear();
9889						month = date.getMonth() + 1;
9890						day = date.getDate();
9891						break;
9892					case "!":
9893						date = new Date((getNumber("!") - this._ticksTo1970) / 10000);
9894						year = date.getFullYear();
9895						month = date.getMonth() + 1;
9896						day = date.getDate();
9897						break;
9898					case "'":
9899						if (lookAhead("'")){
9900							checkLiteral();
9901						} else {
9902							literal = true;
9903						}
9904						break;
9905					default:
9906						checkLiteral();
9907				}
9908			}
9909		}
9910
9911		if (iValue < value.length){
9912			extra = value.substr(iValue);
9913			if (!/^\s+/.test(extra)) {
9914				throw "Extra/unparsed characters found in date: " + extra;
9915			}
9916		}
9917
9918		if (year === -1) {
9919			year = new Date().getFullYear();
9920		} else if (year < 100) {
9921			year += new Date().getFullYear() - new Date().getFullYear() % 100 +
9922				(year <= shortYearCutoff ? 0 : -100);
9923		}
9924
9925		if (doy > -1) {
9926			month = 1;
9927			day = doy;
9928			do {
9929				dim = this._getDaysInMonth(year, month - 1);
9930				if (day <= dim) {
9931					break;
9932				}
9933				month++;
9934				day -= dim;
9935			} while (true);
9936		}
9937
9938		date = this._daylightSavingAdjust(new Date(year, month - 1, day));
9939		if (date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day) {
9940			throw "Invalid date"; // E.g. 31/02/00
9941		}
9942		return date;
9943	},
9944
9945	/* Standard date formats. */
9946	ATOM: "yy-mm-dd", // RFC 3339 (ISO 8601)
9947	COOKIE: "D, dd M yy",
9948	ISO_8601: "yy-mm-dd",
9949	RFC_822: "D, d M y",
9950	RFC_850: "DD, dd-M-y",
9951	RFC_1036: "D, d M y",
9952	RFC_1123: "D, d M yy",
9953	RFC_2822: "D, d M yy",
9954	RSS: "D, d M y", // RFC 822
9955	TICKS: "!",
9956	TIMESTAMP: "@",
9957	W3C: "yy-mm-dd", // ISO 8601
9958
9959	_ticksTo1970: (((1970 - 1) * 365 + Math.floor(1970 / 4) - Math.floor(1970 / 100) +
9960		Math.floor(1970 / 400)) * 24 * 60 * 60 * 10000000),
9961
9962	/* Format a date object into a string value.
9963	 * The format can be combinations of the following:
9964	 * d  - day of month (no leading zero)
9965	 * dd - day of month (two digit)
9966	 * o  - day of year (no leading zeros)
9967	 * oo - day of year (three digit)
9968	 * D  - day name short
9969	 * DD - day name long
9970	 * m  - month of year (no leading zero)
9971	 * mm - month of year (two digit)
9972	 * M  - month name short
9973	 * MM - month name long
9974	 * y  - year (two digit)
9975	 * yy - year (four digit)
9976	 * @ - Unix timestamp (ms since 01/01/1970)
9977	 * ! - Windows ticks (100ns since 01/01/0001)
9978	 * "..." - literal text
9979	 * '' - single quote
9980	 *
vendor: 11,157 bytes, lines 9981-10314
9981	 * @param  format string - the desired format of the date
9982	 * @param  date Date - the date value to format
9983	 * @param  settings Object - attributes include:
9984	 *					dayNamesShort	string[7] - abbreviated names of the days from Sunday (optional)
9985	 *					dayNames		string[7] - names of the days from Sunday (optional)
9986	 *					monthNamesShort string[12] - abbreviated names of the months (optional)
9987	 *					monthNames		string[12] - names of the months (optional)
9988	 * @return  string - the date in the above format
9989	 */
9990	formatDate: function (format, date, settings) {
9991		if (!date) {
9992			return "";
9993		}
9994
9995		var iFormat,
9996			dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort,
9997			dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames,
9998			monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort,
9999			monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames,
10000			// Check whether a format character is doubled
10001			lookAhead = function(match) {
10002				var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
10003				if (matches) {
10004					iFormat++;
10005				}
10006				return matches;
10007			},
10008			// Format a number, with leading zero if necessary
10009			formatNumber = function(match, value, len) {
10010				var num = "" + value;
10011				if (lookAhead(match)) {
10012					while (num.length < len) {
10013						num = "0" + num;
10014					}
10015				}
10016				return num;
10017			},
10018			// Format a name, short or long as requested
10019			formatName = function(match, value, shortNames, longNames) {
10020				return (lookAhead(match) ? longNames[value] : shortNames[value]);
10021			},
10022			output = "",
10023			literal = false;
10024
10025		if (date) {
10026			for (iFormat = 0; iFormat < format.length; iFormat++) {
10027				if (literal) {
10028					if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
10029						literal = false;
10030					} else {
10031						output += format.charAt(iFormat);
10032					}
10033				} else {
10034					switch (format.charAt(iFormat)) {
10035						case "d":
10036							output += formatNumber("d", date.getDate(), 2);
10037							break;
10038						case "D":
10039							output += formatName("D", date.getDay(), dayNamesShort, dayNames);
10040							break;
10041						case "o":
10042							output += formatNumber("o",
10043								Math.round((new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000), 3);
10044							break;
10045						case "m":
10046							output += formatNumber("m", date.getMonth() + 1, 2);
10047							break;
10048						case "M":
10049							output += formatName("M", date.getMonth(), monthNamesShort, monthNames);
10050							break;
10051						case "y":
10052							output += (lookAhead("y") ? date.getFullYear() :
10053								(date.getYear() % 100 < 10 ? "0" : "") + date.getYear() % 100);
10054							break;
10055						case "@":
10056							output += date.getTime();
10057							break;
10058						case "!":
10059							output += date.getTime() * 10000 + this._ticksTo1970;
10060							break;
10061						case "'":
10062							if (lookAhead("'")) {
10063								output += "'";
10064							} else {
10065								literal = true;
10066							}
10067							break;
10068						default:
10069							output += format.charAt(iFormat);
10070					}
10071				}
10072			}
10073		}
10074		return output;
10075	},
10076
10077	/* Extract all possible characters from the date format. */
10078	_possibleChars: function (format) {
10079		var iFormat,
10080			chars = "",
10081			literal = false,
10082			// Check whether a format character is doubled
10083			lookAhead = function(match) {
10084				var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
10085				if (matches) {
10086					iFormat++;
10087				}
10088				return matches;
10089			};
10090
10091		for (iFormat = 0; iFormat < format.length; iFormat++) {
10092			if (literal) {
10093				if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
10094					literal = false;
10095				} else {
10096					chars += format.charAt(iFormat);
10097				}
10098			} else {
10099				switch (format.charAt(iFormat)) {
10100					case "d": case "m": case "y": case "@":
10101						chars += "0123456789";
10102						break;
10103					case "D": case "M":
10104						return null; // Accept anything
10105					case "'":
10106						if (lookAhead("'")) {
10107							chars += "'";
10108						} else {
10109							literal = true;
10110						}
10111						break;
10112					default:
10113						chars += format.charAt(iFormat);
10114				}
10115			}
10116		}
10117		return chars;
10118	},
10119
10120	/* Get a setting value, defaulting if necessary. */
10121	_get: function(inst, name) {
10122		return inst.settings[name] !== undefined ?
10123			inst.settings[name] : this._defaults[name];
10124	},
10125
10126	/* Parse existing date and initialise date picker. */
10127	_setDateFromField: function(inst, noDefault) {
10128		if (inst.input.val() === inst.lastVal) {
10129			return;
10130		}
10131
10132		var dateFormat = this._get(inst, "dateFormat"),
10133			dates = inst.lastVal = inst.input ? inst.input.val() : null,
10134			defaultDate = this._getDefaultDate(inst),
10135			date = defaultDate,
10136			settings = this._getFormatConfig(inst);
10137
10138		try {
10139			date = this.parseDate(dateFormat, dates, settings) || defaultDate;
10140		} catch (event) {
10141			dates = (noDefault ? "" : dates);
10142		}
10143		inst.selectedDay = date.getDate();
10144		inst.drawMonth = inst.selectedMonth = date.getMonth();
10145		inst.drawYear = inst.selectedYear = date.getFullYear();
10146		inst.currentDay = (dates ? date.getDate() : 0);
10147		inst.currentMonth = (dates ? date.getMonth() : 0);
10148		inst.currentYear = (dates ? date.getFullYear() : 0);
10149		this._adjustInstDate(inst);
10150	},
10151
10152	/* Retrieve the default date shown on opening. */
10153	_getDefaultDate: function(inst) {
10154		return this._restrictMinMax(inst,
10155			this._determineDate(inst, this._get(inst, "defaultDate"), new Date()));
10156	},
10157
10158	/* A date may be specified as an exact value or a relative one. */
10159	_determineDate: function(inst, date, defaultDate) {
10160		var offsetNumeric = function(offset) {
10161				var date = new Date();
10162				date.setDate(date.getDate() + offset);
10163				return date;
10164			},
10165			offsetString = function(offset) {
10166				try {
10167					return $.datepicker.parseDate($.datepicker._get(inst, "dateFormat"),
10168						offset, $.datepicker._getFormatConfig(inst));
10169				}
10170				catch (e) {
10171					// Ignore
10172				}
10173
10174				var date = (offset.toLowerCase().match(/^c/) ?
10175					$.datepicker._getDate(inst) : null) || new Date(),
10176					year = date.getFullYear(),
10177					month = date.getMonth(),
10178					day = date.getDate(),
10179					pattern = /([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,
10180					matches = pattern.exec(offset);
10181
10182				while (matches) {
10183					switch (matches[2] || "d") {
10184						case "d" : case "D" :
10185							day += parseInt(matches[1],10); break;
10186						case "w" : case "W" :
10187							day += parseInt(matches[1],10) * 7; break;
10188						case "m" : case "M" :
10189							month += parseInt(matches[1],10);
10190							day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
10191							break;
10192						case "y": case "Y" :
10193							year += parseInt(matches[1],10);
10194							day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
10195							break;
10196					}
10197					matches = pattern.exec(offset);
10198				}
10199				return new Date(year, month, day);
10200			},
10201			newDate = (date == null || date === "" ? defaultDate : (typeof date === "string" ? offsetString(date) :
10202				(typeof date === "number" ? (isNaN(date) ? defaultDate : offsetNumeric(date)) : new Date(date.getTime()))));
10203
10204		newDate = (newDate && newDate.toString() === "Invalid Date" ? defaultDate : newDate);
10205		if (newDate) {
10206			newDate.setHours(0);
10207			newDate.setMinutes(0);
10208			newDate.setSeconds(0);
10209			newDate.setMilliseconds(0);
10210		}
10211		return this._daylightSavingAdjust(newDate);
10212	},
10213
10214	/* Handle switch to/from daylight saving.
10215	 * Hours may be non-zero on daylight saving cut-over:
10216	 * > 12 when midnight changeover, but then cannot generate
10217	 * midnight datetime, so jump to 1AM, otherwise reset.
10218	 * @param  date  (Date) the date to check
10219	 * @return  (Date) the corrected date
10220	 */
10221	_daylightSavingAdjust: function(date) {
10222		if (!date) {
10223			return null;
10224		}
10225		date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0);
10226		return date;
10227	},
10228
10229	/* Set the date(s) directly. */
10230	_setDate: function(inst, date, noChange) {
10231		var clear = !date,
10232			origMonth = inst.selectedMonth,
10233			origYear = inst.selectedYear,
10234			newDate = this._restrictMinMax(inst, this._determineDate(inst, date, new Date()));
10235
10236		inst.selectedDay = inst.currentDay = newDate.getDate();
10237		inst.drawMonth = inst.selectedMonth = inst.currentMonth = newDate.getMonth();
10238		inst.drawYear = inst.selectedYear = inst.currentYear = newDate.getFullYear();
10239		if ((origMonth !== inst.selectedMonth || origYear !== inst.selectedYear) && !noChange) {
10240			this._notifyChange(inst);
10241		}
10242		this._adjustInstDate(inst);
10243		if (inst.input) {
10244			inst.input.val(clear ? "" : this._formatDate(inst));
10245		}
10246	},
10247
10248	/* Retrieve the date(s) directly. */
10249	_getDate: function(inst) {
10250		var startDate = (!inst.currentYear || (inst.input && inst.input.val() === "") ? null :
10251			this._daylightSavingAdjust(new Date(
10252			inst.currentYear, inst.currentMonth, inst.currentDay)));
10253			return startDate;
10254	},
10255
10256	/* Attach the onxxx handlers.  These are declared statically so
10257	 * they work with static code transformers like Caja.
10258	 */
10259	_attachHandlers: function(inst) {
10260		var stepMonths = this._get(inst, "stepMonths"),
10261			id = "#" + inst.id.replace( /\\\\/g, "\\" );
10262		inst.dpDiv.find("[data-handler]").map(function () {
10263			var handler = {
10264				prev: function () {
10265					$.datepicker._adjustDate(id, -stepMonths, "M");
10266				},
10267				next: function () {
10268					$.datepicker._adjustDate(id, +stepMonths, "M");
10269				},
10270				hide: function () {
10271					$.datepicker._hideDatepicker();
10272				},
10273				today: function () {
10274					$.datepicker._gotoToday(id);
10275				},
10276				selectDay: function () {
10277					$.datepicker._selectDay(id, +this.getAttribute("data-month"), +this.getAttribute("data-year"), this);
10278					return false;
10279				},
10280				selectMonth: function () {
10281					$.datepicker._selectMonthYear(id, this, "M");
10282					return false;
10283				},
10284				selectYear: function () {
10285					$.datepicker._selectMonthYear(id, this, "Y");
10286					return false;
10287				}
10288			};
10289			$(this).bind(this.getAttribute("data-event"), handler[this.getAttribute("data-handler")]);
10290		});
10291	},
10292
10293	/* Generate the HTML for the current state of the date picker. */
10294	_generateHTML: function(inst) {
10295		var maxDraw, prevText, prev, nextText, next, currentText, gotoDate,
10296			controls, buttonPanel, firstDay, showWeek, dayNames, dayNamesMin,
10297			monthNames, monthNamesShort, beforeShowDay, showOtherMonths,
10298			selectOtherMonths, defaultDate, html, dow, row, group, col, selectedDate,
10299			cornerClass, calender, thead, day, daysInMonth, leadDays, curRows, numRows,
10300			printDate, dRow, tbody, daySettings, otherMonth, unselectable,
10301			tempDate = new Date(),
10302			today = this._daylightSavingAdjust(
10303				new Date(tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate())), // clear time
10304			isRTL = this._get(inst, "isRTL"),
10305			showButtonPanel = this._get(inst, "showButtonPanel"),
10306			hideIfNoPrevNext = this._get(inst, "hideIfNoPrevNext"),
10307			navigationAsDateFormat = this._get(inst, "navigationAsDateFormat"),
10308			numMonths = this._getNumberOfMonths(inst),
10309			showCurrentAtPos = this._get(inst, "showCurrentAtPos"),
10310			stepMonths = this._get(inst, "stepMonths"),
10311			isMultiMonth = (numMonths[0] !== 1 || numMonths[1] !== 1),
10312			currentDate = this._daylightSavingAdjust((!inst.currentDay ? new Date(9999, 9, 9) :
10313				new Date(inst.currentYear, inst.currentMonth, inst.currentDay))),
10314			minDate = this._getMinMaxDate(inst, "min"),
vendor: 9,139 bytes, lines 10315-10473
10315			maxDate = this._getMinMaxDate(inst, "max"),
10316			drawMonth = inst.drawMonth - showCurrentAtPos,
10317			drawYear = inst.drawYear;
10318
10319		if (drawMonth < 0) {
10320			drawMonth += 12;
10321			drawYear--;
10322		}
10323		if (maxDate) {
10324			maxDraw = this._daylightSavingAdjust(new Date(maxDate.getFullYear(),
10325				maxDate.getMonth() - (numMonths[0] * numMonths[1]) + 1, maxDate.getDate()));
10326			maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw);
10327			while (this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1)) > maxDraw) {
10328				drawMonth--;
10329				if (drawMonth < 0) {
10330					drawMonth = 11;
10331					drawYear--;
10332				}
10333			}
10334		}
10335		inst.drawMonth = drawMonth;
10336		inst.drawYear = drawYear;
10337
10338		prevText = this._get(inst, "prevText");
10339		prevText = (!navigationAsDateFormat ? prevText : this.formatDate(prevText,
10340			this._daylightSavingAdjust(new Date(drawYear, drawMonth - stepMonths, 1)),
10341			this._getFormatConfig(inst)));
10342
10343		prev = (this._canAdjustMonth(inst, -1, drawYear, drawMonth) ?
10344			"<a class='ui-datepicker-prev ui-corner-all' data-handler='prev' data-event='click'" +
10345			" title='" + prevText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "e" : "w") + "'>" + prevText + "</span></a>" :
10346			(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>"));
10347
10348		nextText = this._get(inst, "nextText");
10349		nextText = (!navigationAsDateFormat ? nextText : this.formatDate(nextText,
10350			this._daylightSavingAdjust(new Date(drawYear, drawMonth + stepMonths, 1)),
10351			this._getFormatConfig(inst)));
10352
10353		next = (this._canAdjustMonth(inst, +1, drawYear, drawMonth) ?
10354			"<a class='ui-datepicker-next ui-corner-all' data-handler='next' data-event='click'" +
10355			" title='" + nextText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "w" : "e") + "'>" + nextText + "</span></a>" :
10356			(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>"));
10357
10358		currentText = this._get(inst, "currentText");
10359		gotoDate = (this._get(inst, "gotoCurrent") && inst.currentDay ? currentDate : today);
10360		currentText = (!navigationAsDateFormat ? currentText :
10361			this.formatDate(currentText, gotoDate, this._getFormatConfig(inst)));
10362
10363		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'>" +
10364			this._get(inst, "closeText") + "</button>" : "");
10365
10366		buttonPanel = (showButtonPanel) ? "<div class='ui-datepicker-buttonpane ui-widget-content'>" + (isRTL ? controls : "") +
10367			(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'" +
10368			">" + currentText + "</button>" : "") + (isRTL ? "" : controls) + "</div>" : "";
10369
10370		firstDay = parseInt(this._get(inst, "firstDay"),10);
10371		firstDay = (isNaN(firstDay) ? 0 : firstDay);
10372
10373		showWeek = this._get(inst, "showWeek");
10374		dayNames = this._get(inst, "dayNames");
10375		dayNamesMin = this._get(inst, "dayNamesMin");
10376		monthNames = this._get(inst, "monthNames");
10377		monthNamesShort = this._get(inst, "monthNamesShort");
10378		beforeShowDay = this._get(inst, "beforeShowDay");
10379		showOtherMonths = this._get(inst, "showOtherMonths");
10380		selectOtherMonths = this._get(inst, "selectOtherMonths");
10381		defaultDate = this._getDefaultDate(inst);
10382		html = "";
10383		dow;
10384		for (row = 0; row < numMonths[0]; row++) {
10385			group = "";
10386			this.maxRows = 4;
10387			for (col = 0; col < numMonths[1]; col++) {
10388				selectedDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, inst.selectedDay));
10389				cornerClass = " ui-corner-all";
10390				calender = "";
10391				if (isMultiMonth) {
10392					calender += "<div class='ui-datepicker-group";
10393					if (numMonths[1] > 1) {
10394						switch (col) {
10395							case 0: calender += " ui-datepicker-group-first";
10396								cornerClass = " ui-corner-" + (isRTL ? "right" : "left"); break;
10397							case numMonths[1]-1: calender += " ui-datepicker-group-last";
10398								cornerClass = " ui-corner-" + (isRTL ? "left" : "right"); break;
10399							default: calender += " ui-datepicker-group-middle"; cornerClass = ""; break;
10400						}
10401					}
10402					calender += "'>";
10403				}
10404				calender += "<div class='ui-datepicker-header ui-widget-header ui-helper-clearfix" + cornerClass + "'>" +
10405					(/all|left/.test(cornerClass) && row === 0 ? (isRTL ? next : prev) : "") +
10406					(/all|right/.test(cornerClass) && row === 0 ? (isRTL ? prev : next) : "") +
10407					this._generateMonthYearHeader(inst, drawMonth, drawYear, minDate, maxDate,
10408					row > 0 || col > 0, monthNames, monthNamesShort) + // draw month headers
10409					"</div><table class='ui-datepicker-calendar'><thead>" +
10410					"<tr>";
10411				thead = (showWeek ? "<th class='ui-datepicker-week-col'>" + this._get(inst, "weekHeader") + "</th>" : "");
10412				for (dow = 0; dow < 7; dow++) { // days of the week
10413					day = (dow + firstDay) % 7;
10414					thead += "<th scope='col'" + ((dow + firstDay + 6) % 7 >= 5 ? " class='ui-datepicker-week-end'" : "") + ">" +
10415						"<span title='" + dayNames[day] + "'>" + dayNamesMin[day] + "</span></th>";
10416				}
10417				calender += thead + "</tr></thead><tbody>";
10418				daysInMonth = this._getDaysInMonth(drawYear, drawMonth);
10419				if (drawYear === inst.selectedYear && drawMonth === inst.selectedMonth) {
10420					inst.selectedDay = Math.min(inst.selectedDay, daysInMonth);
10421				}
10422				leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7;
10423				curRows = Math.ceil((leadDays + daysInMonth) / 7); // calculate the number of rows to generate
10424				numRows = (isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows); //If multiple months, use the higher number of rows (see #7043)
10425				this.maxRows = numRows;
10426				printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays));
10427				for (dRow = 0; dRow < numRows; dRow++) { // create date picker rows
10428					calender += "<tr>";
10429					tbody = (!showWeek ? "" : "<td class='ui-datepicker-week-col'>" +
10430						this._get(inst, "calculateWeek")(printDate) + "</td>");
10431					for (dow = 0; dow < 7; dow++) { // create date picker days
10432						daySettings = (beforeShowDay ?
10433							beforeShowDay.apply((inst.input ? inst.input[0] : null), [printDate]) : [true, ""]);
10434						otherMonth = (printDate.getMonth() !== drawMonth);
10435						unselectable = (otherMonth && !selectOtherMonths) || !daySettings[0] ||
10436							(minDate && printDate < minDate) || (maxDate && printDate > maxDate);
10437						tbody += "<td class='" +
10438							((dow + firstDay + 6) % 7 >= 5 ? " ui-datepicker-week-end" : "") + // highlight weekends
10439							(otherMonth ? " ui-datepicker-other-month" : "") + // highlight days from other months
10440							((printDate.getTime() === selectedDate.getTime() && drawMonth === inst.selectedMonth && inst._keyEvent) || // user pressed key
10441							(defaultDate.getTime() === printDate.getTime() && defaultDate.getTime() === selectedDate.getTime()) ?
10442							// or defaultDate is current printedDate and defaultDate is selectedDate
10443							" " + this._dayOverClass : "") + // highlight selected day
10444							(unselectable ? " " + this._unselectableClass + " ui-state-disabled": "") +  // highlight unselectable days
10445							(otherMonth && !showOtherMonths ? "" : " " + daySettings[1] + // highlight custom dates
10446							(printDate.getTime() === currentDate.getTime() ? " " + this._currentClass : "") + // highlight selected day
10447							(printDate.getTime() === today.getTime() ? " ui-datepicker-today" : "")) + "'" + // highlight today (if different)
10448							((!otherMonth || showOtherMonths) && daySettings[2] ? " title='" + daySettings[2].replace(/'/g, "&#39;") + "'" : "") + // cell title
10449							(unselectable ? "" : " data-handler='selectDay' data-event='click' data-month='" + printDate.getMonth() + "' data-year='" + printDate.getFullYear() + "'") + ">" + // actions
10450							(otherMonth && !showOtherMonths ? "&#xa0;" : // display for other months
10451							(unselectable ? "<span class='ui-state-default'>" + printDate.getDate() + "</span>" : "<a class='ui-state-default" +
10452							(printDate.getTime() === today.getTime() ? " ui-state-highlight" : "") +
10453							(printDate.getTime() === currentDate.getTime() ? " ui-state-active" : "") + // highlight selected day
10454							(otherMonth ? " ui-priority-secondary" : "") + // distinguish dates from other months
10455							"' href='#'>" + printDate.getDate() + "</a>")) + "</td>"; // display selectable date
10456						printDate.setDate(printDate.getDate() + 1);
10457						printDate = this._daylightSavingAdjust(printDate);
10458					}
10459					calender += tbody + "</tr>";
10460				}
10461				drawMonth++;
10462				if (drawMonth > 11) {
10463					drawMonth = 0;
10464					drawYear++;
10465				}
10466				calender += "</tbody></table>" + (isMultiMonth ? "</div>" +
10467							((numMonths[0] > 0 && col === numMonths[1]-1) ? "<div class='ui-datepicker-row-break'></div>" : "") : "");
10468				group += calender;
10469			}
10470			html += group;
10471		}
10472		html += buttonPanel;
10473		inst._keyEvent = false;
vendor: 5,431 bytes, lines 10474-10617
10474		return html;
10475	},
10476
10477	/* Generate the month and year header. */
10478	_generateMonthYearHeader: function(inst, drawMonth, drawYear, minDate, maxDate,
10479			secondary, monthNames, monthNamesShort) {
10480
10481		var inMinYear, inMaxYear, month, years, thisYear, determineYear, year, endYear,
10482			changeMonth = this._get(inst, "changeMonth"),
10483			changeYear = this._get(inst, "changeYear"),
10484			showMonthAfterYear = this._get(inst, "showMonthAfterYear"),
10485			html = "<div class='ui-datepicker-title'>",
10486			monthHtml = "";
10487
10488		// month selection
10489		if (secondary || !changeMonth) {
10490			monthHtml += "<span class='ui-datepicker-month'>" + monthNames[drawMonth] + "</span>";
10491		} else {
10492			inMinYear = (minDate && minDate.getFullYear() === drawYear);
10493			inMaxYear = (maxDate && maxDate.getFullYear() === drawYear);
10494			monthHtml += "<select class='ui-datepicker-month' data-handler='selectMonth' data-event='change'>";
10495			for ( month = 0; month < 12; month++) {
10496				if ((!inMinYear || month >= minDate.getMonth()) && (!inMaxYear || month <= maxDate.getMonth())) {
10497					monthHtml += "<option value='" + month + "'" +
10498						(month === drawMonth ? " selected='selected'" : "") +
10499						">" + monthNamesShort[month] + "</option>";
10500				}
10501			}
10502			monthHtml += "</select>";
10503		}
10504
10505		if (!showMonthAfterYear) {
10506			html += monthHtml + (secondary || !(changeMonth && changeYear) ? "&#xa0;" : "");
10507		}
10508
10509		// year selection
10510		if ( !inst.yearshtml ) {
10511			inst.yearshtml = "";
10512			if (secondary || !changeYear) {
10513				html += "<span class='ui-datepicker-year'>" + drawYear + "</span>";
10514			} else {
10515				// determine range of years to display
10516				years = this._get(inst, "yearRange").split(":");
10517				thisYear = new Date().getFullYear();
10518				determineYear = function(value) {
10519					var year = (value.match(/c[+\-].*/) ? drawYear + parseInt(value.substring(1), 10) :
10520						(value.match(/[+\-].*/) ? thisYear + parseInt(value, 10) :
10521						parseInt(value, 10)));
10522					return (isNaN(year) ? thisYear : year);
10523				};
10524				year = determineYear(years[0]);
10525				endYear = Math.max(year, determineYear(years[1] || ""));
10526				year = (minDate ? Math.max(year, minDate.getFullYear()) : year);
10527				endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear);
10528				inst.yearshtml += "<select class='ui-datepicker-year' data-handler='selectYear' data-event='change'>";
10529				for (; year <= endYear; year++) {
10530					inst.yearshtml += "<option value='" + year + "'" +
10531						(year === drawYear ? " selected='selected'" : "") +
10532						">" + year + "</option>";
10533				}
10534				inst.yearshtml += "</select>";
10535
10536				html += inst.yearshtml;
10537				inst.yearshtml = null;
10538			}
10539		}
10540
10541		html += this._get(inst, "yearSuffix");
10542		if (showMonthAfterYear) {
10543			html += (secondary || !(changeMonth && changeYear) ? "&#xa0;" : "") + monthHtml;
10544		}
10545		html += "</div>"; // Close datepicker_header
10546		return html;
10547	},
10548
10549	/* Adjust one of the date sub-fields. */
10550	_adjustInstDate: function(inst, offset, period) {
10551		var year = inst.drawYear + (period === "Y" ? offset : 0),
10552			month = inst.drawMonth + (period === "M" ? offset : 0),
10553			day = Math.min(inst.selectedDay, this._getDaysInMonth(year, month)) + (period === "D" ? offset : 0),
10554			date = this._restrictMinMax(inst, this._daylightSavingAdjust(new Date(year, month, day)));
10555
10556		inst.selectedDay = date.getDate();
10557		inst.drawMonth = inst.selectedMonth = date.getMonth();
10558		inst.drawYear = inst.selectedYear = date.getFullYear();
10559		if (period === "M" || period === "Y") {
10560			this._notifyChange(inst);
10561		}
10562	},
10563
10564	/* Ensure a date is within any min/max bounds. */
10565	_restrictMinMax: function(inst, date) {
10566		var minDate = this._getMinMaxDate(inst, "min"),
10567			maxDate = this._getMinMaxDate(inst, "max"),
10568			newDate = (minDate && date < minDate ? minDate : date);
10569		return (maxDate && newDate > maxDate ? maxDate : newDate);
10570	},
10571
10572	/* Notify change of month/year. */
10573	_notifyChange: function(inst) {
10574		var onChange = this._get(inst, "onChangeMonthYear");
10575		if (onChange) {
10576			onChange.apply((inst.input ? inst.input[0] : null),
10577				[inst.selectedYear, inst.selectedMonth + 1, inst]);
10578		}
10579	},
10580
10581	/* Determine the number of months to show. */
10582	_getNumberOfMonths: function(inst) {
10583		var numMonths = this._get(inst, "numberOfMonths");
10584		return (numMonths == null ? [1, 1] : (typeof numMonths === "number" ? [1, numMonths] : numMonths));
10585	},
10586
10587	/* Determine the current maximum date - ensure no time components are set. */
10588	_getMinMaxDate: function(inst, minMax) {
10589		return this._determineDate(inst, this._get(inst, minMax + "Date"), null);
10590	},
10591
10592	/* Find the number of days in a given month. */
10593	_getDaysInMonth: function(year, month) {
10594		return 32 - this._daylightSavingAdjust(new Date(year, month, 32)).getDate();
10595	},
10596
10597	/* Find the day of the week of the first of a month. */
10598	_getFirstDayOfMonth: function(year, month) {
10599		return new Date(year, month, 1).getDay();
10600	},
10601
10602	/* Determines if we should allow a "next/prev" month display change. */
10603	_canAdjustMonth: function(inst, offset, curYear, curMonth) {
10604		var numMonths = this._getNumberOfMonths(inst),
10605			date = this._daylightSavingAdjust(new Date(curYear,
10606			curMonth + (offset < 0 ? offset : numMonths[0] * numMonths[1]), 1));
10607
10608		if (offset < 0) {
10609			date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth()));
10610		}
10611		return this._isInRange(inst, date);
10612	},
10613
10614	/* Is the given date in the accepted range? */
10615	_isInRange: function(inst, date) {
10616		var yearSplit, currentYear,
10617			minDate = this._getMinMaxDate(inst, "min"),
vendor: 4,916 bytes, lines 10618-10750
10618			maxDate = this._getMinMaxDate(inst, "max"),
10619			minYear = null,
10620			maxYear = null,
10621			years = this._get(inst, "yearRange");
10622			if (years){
10623				yearSplit = years.split(":");
10624				currentYear = new Date().getFullYear();
10625				minYear = parseInt(yearSplit[0], 10);
10626				maxYear = parseInt(yearSplit[1], 10);
10627				if ( yearSplit[0].match(/[+\-].*/) ) {
10628					minYear += currentYear;
10629				}
10630				if ( yearSplit[1].match(/[+\-].*/) ) {
10631					maxYear += currentYear;
10632				}
10633			}
10634
10635		return ((!minDate || date.getTime() >= minDate.getTime()) &&
10636			(!maxDate || date.getTime() <= maxDate.getTime()) &&
10637			(!minYear || date.getFullYear() >= minYear) &&
10638			(!maxYear || date.getFullYear() <= maxYear));
10639	},
10640
10641	/* Provide the configuration settings for formatting/parsing. */
10642	_getFormatConfig: function(inst) {
10643		var shortYearCutoff = this._get(inst, "shortYearCutoff");
10644		shortYearCutoff = (typeof shortYearCutoff !== "string" ? shortYearCutoff :
10645			new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
10646		return {shortYearCutoff: shortYearCutoff,
10647			dayNamesShort: this._get(inst, "dayNamesShort"), dayNames: this._get(inst, "dayNames"),
10648			monthNamesShort: this._get(inst, "monthNamesShort"), monthNames: this._get(inst, "monthNames")};
10649	},
10650
10651	/* Format the given date for display. */
10652	_formatDate: function(inst, day, month, year) {
10653		if (!day) {
10654			inst.currentDay = inst.selectedDay;
10655			inst.currentMonth = inst.selectedMonth;
10656			inst.currentYear = inst.selectedYear;
10657		}
10658		var date = (day ? (typeof day === "object" ? day :
10659			this._daylightSavingAdjust(new Date(year, month, day))) :
10660			this._daylightSavingAdjust(new Date(inst.currentYear, inst.currentMonth, inst.currentDay)));
10661		return this.formatDate(this._get(inst, "dateFormat"), date, this._getFormatConfig(inst));
10662	}
10663});
10664
10665/*
10666 * Bind hover events for datepicker elements.
10667 * Done via delegate so the binding only occurs once in the lifetime of the parent div.
10668 * Global datepicker_instActive, set by _updateDatepicker allows the handlers to find their way back to the active picker.
10669 */
10670function datepicker_bindHover(dpDiv) {
10671	var selector = "button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";
10672	return dpDiv.delegate(selector, "mouseout", function() {
10673			$(this).removeClass("ui-state-hover");
10674			if (this.className.indexOf("ui-datepicker-prev") !== -1) {
10675				$(this).removeClass("ui-datepicker-prev-hover");
10676			}
10677			if (this.className.indexOf("ui-datepicker-next") !== -1) {
10678				$(this).removeClass("ui-datepicker-next-hover");
10679			}
10680		})
10681		.delegate( selector, "mouseover", datepicker_handleMouseover );
10682}
10683
10684function datepicker_handleMouseover() {
10685	if (!$.datepicker._isDisabledDatepicker( datepicker_instActive.inline? datepicker_instActive.dpDiv.parent()[0] : datepicker_instActive.input[0])) {
10686		$(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover");
10687		$(this).addClass("ui-state-hover");
10688		if (this.className.indexOf("ui-datepicker-prev") !== -1) {
10689			$(this).addClass("ui-datepicker-prev-hover");
10690		}
10691		if (this.className.indexOf("ui-datepicker-next") !== -1) {
10692			$(this).addClass("ui-datepicker-next-hover");
10693		}
10694	}
10695}
10696
10697/* jQuery extend now ignores nulls! */
10698function datepicker_extendRemove(target, props) {
10699	$.extend(target, props);
10700	for (var name in props) {
10701		if (props[name] == null) {
10702			target[name] = props[name];
10703		}
10704	}
10705	return target;
10706}
10707
10708/* Invoke the datepicker functionality.
10709   @param  options  string - a command, optionally followed by additional parameters or
10710					Object - settings for attaching new datepicker functionality
10711   @return  jQuery object */
10712$.fn.datepicker = function(options){
10713
10714	/* Verify an empty collection wasn't passed - Fixes #6976 */
10715	if ( !this.length ) {
10716		return this;
10717	}
10718
10719	/* Initialise the date picker. */
10720	if (!$.datepicker.initialized) {
10721		$(document).mousedown($.datepicker._checkExternalClick);
10722		$.datepicker.initialized = true;
10723	}
10724
10725	/* Append datepicker main container to body if not exist. */
10726	if ($("#"+$.datepicker._mainDivId).length === 0) {
10727		$("body").append($.datepicker.dpDiv);
10728	}
10729
10730	var otherArgs = Array.prototype.slice.call(arguments, 1);
10731	if (typeof options === "string" && (options === "isDisabled" || options === "getDate" || options === "widget")) {
10732		return $.datepicker["_" + options + "Datepicker"].
10733			apply($.datepicker, [this[0]].concat(otherArgs));
10734	}
10735	if (options === "option" && arguments.length === 2 && typeof arguments[1] === "string") {
10736		return $.datepicker["_" + options + "Datepicker"].
10737			apply($.datepicker, [this[0]].concat(otherArgs));
10738	}
10739	return this.each(function() {
10740		typeof options === "string" ?
10741			$.datepicker["_" + options + "Datepicker"].
10742				apply($.datepicker, [this].concat(otherArgs)) :
10743			$.datepicker._attachDatepicker(this, options);
10744	});
10745};
10746
10747$.datepicker = new Datepicker(); // singleton instance
10748$.datepicker.initialized = false;
10749$.datepicker.uuid = new Date().getTime();
10750$.datepicker.version = "
vendor: 44,511 bytes, lines 10750-12539
10750@VERSION";
10751
10752return $.datepicker;
10753
10754}));
10755
10756//>>label: Dialog
10757//>>group: Widgets
10758//>>description: Displays customizable dialog windows.
10759//>>docs: http://api.jqueryui.com/dialog/
10760//>>demos: http://jqueryui.com/dialog/
10761//>>css.structure: ../themes/base/core.css
10762//>>css.structure: ../themes/base/dialog.css
10763//>>css.theme: ../themes/base/theme.css
10764
10765(function( factory ) {
10766	if ( typeof define === "function" && define.amd ) {
10767
10768		// AMD. Register as an anonymous module.
10769		define([
10770			"jquery",
10771			"./core",
10772			"./widget",
10773			"./button",
10774			"./draggable",
10775			"./mouse",
10776			"./position",
10777			"./resizable"
10778		], factory );
10779	} else {
10780
10781		// Browser globals
10782		factory( jQuery );
10783	}
10784}(function( $ ) {
10785
10786$.widget( "ui.dialog", {
10787	version: "@VERSION",
10788	options: {
10789		appendTo: "body",
10790		autoOpen: true,
10791		buttons: [],
10792		classes: {
10793			"ui-dialog": "ui-corner-all",
10794			"ui-dialog-titlebar": "ui-corner-all"
10795		},
10796		closeOnEscape: true,
10797		closeText: "Close",
10798		draggable: true,
10799		hide: null,
10800		height: "auto",
10801		maxHeight: null,
10802		maxWidth: null,
10803		minHeight: 150,
10804		minWidth: 150,
10805		modal: false,
10806		position: {
10807			my: "center",
10808			at: "center",
10809			of: window,
10810			collision: "fit",
10811			// Ensure the titlebar is always visible
10812			using: function( pos ) {
10813				var topOffset = $( this ).css( pos ).offset().top;
10814				if ( topOffset < 0 ) {
10815					$( this ).css( "top", pos.top - topOffset );
10816				}
10817			}
10818		},
10819		resizable: true,
10820		show: null,
10821		title: null,
10822		width: 300,
10823
10824		// callbacks
10825		beforeClose: null,
10826		close: null,
10827		drag: null,
10828		dragStart: null,
10829		dragStop: null,
10830		focus: null,
10831		open: null,
10832		resize: null,
10833		resizeStart: null,
10834		resizeStop: null
10835	},
10836
10837	sizeRelatedOptions: {
10838		buttons: true,
10839		height: true,
10840		maxHeight: true,
10841		maxWidth: true,
10842		minHeight: true,
10843		minWidth: true,
10844		width: true
10845	},
10846
10847	resizableRelatedOptions: {
10848		maxHeight: true,
10849		maxWidth: true,
10850		minHeight: true,
10851		minWidth: true
10852	},
10853
10854	_create: function() {
10855		this.originalCss = {
10856			display: this.element[ 0 ].style.display,
10857			width: this.element[ 0 ].style.width,
10858			minHeight: this.element[ 0 ].style.minHeight,
10859			maxHeight: this.element[ 0 ].style.maxHeight,
10860			height: this.element[ 0 ].style.height
10861		};
10862		this.originalPosition = {
10863			parent: this.element.parent(),
10864			index: this.element.parent().children().index( this.element )
10865		};
10866		this.originalTitle = this.element.attr( "title" );
10867		this.options.title = this.options.title || this.originalTitle;
10868
10869		this._createWrapper();
10870
10871		this.element
10872			.show()
10873			.removeAttr( "title" )
10874			.appendTo( this.uiDialog );
10875
10876		this._addClass( "ui-dialog-content", "ui-widget-content" );
10877
10878		this._createTitlebar();
10879		this._createButtonPane();
10880
10881		if ( this.options.draggable && $.fn.draggable ) {
10882			this._makeDraggable();
10883		}
10884		if ( this.options.resizable && $.fn.resizable ) {
10885			this._makeResizable();
10886		}
10887
10888		this._isOpen = false;
10889
10890		this._trackFocus();
10891	},
10892
10893	_init: function() {
10894		if ( this.options.autoOpen ) {
10895			this.open();
10896		}
10897	},
10898
10899	_appendTo: function() {
10900		var element = this.options.appendTo;
10901		if ( element && (element.jquery || element.nodeType) ) {
10902			return $( element );
10903		}
10904		return this.document.find( element || "body" ).eq( 0 );
10905	},
10906
10907	_destroy: function() {
10908		var next,
10909			originalPosition = this.originalPosition;
10910
10911		this._untrackInstance();
10912		this._destroyOverlay();
10913
10914		this.element
10915			.removeUniqueId()
10916			.css( this.originalCss )
10917			// Without detaching first, the following becomes really slow
10918			.detach();
10919
10920		this.uiDialog.remove();
10921
10922		if ( this.originalTitle ) {
10923			this.element.attr( "title", this.originalTitle );
10924		}
10925
10926		next = originalPosition.parent.children().eq( originalPosition.index );
10927		// Don't try to place the dialog next to itself (#8613)
10928		if ( next.length && next[ 0 ] !== this.element[ 0 ] ) {
10929			next.before( this.element );
10930		} else {
10931			originalPosition.parent.append( this.element );
10932		}
10933	},
10934
10935	widget: function() {
10936		return this.uiDialog;
10937	},
10938
10939	disable: $.noop,
10940	enable: $.noop,
10941
10942	close: function( event ) {
10943		var that = this;
10944
10945		if ( !this._isOpen || this._trigger( "beforeClose", event ) === false ) {
10946			return;
10947		}
10948
10949		this._isOpen = false;
10950		this._focusedElement = null;
10951		this._destroyOverlay();
10952		this._untrackInstance();
10953
10954		if ( !this.opener.filter( ":focusable" ).focus().length ) {
10955
10956			// Hiding a focused element doesn't trigger blur in WebKit
10957			// so in case we have nothing to focus on, explicitly blur the active element
10958			// https://bugs.webkit.org/show_bug.cgi?id=47182
10959			$.ui.safeBlur( $.ui.safeActiveElement( this.document[ 0 ] ) );
10960		}
10961
10962		this._hide( this.uiDialog, this.options.hide, function() {
10963			that._trigger( "close", event );
10964		});
10965	},
10966
10967	isOpen: function() {
10968		return this._isOpen;
10969	},
10970
10971	moveToTop: function() {
10972		this._moveToTop();
10973	},
10974
10975	_moveToTop: function( event, silent ) {
10976		var moved = false,
10977			zIndices = this.uiDialog.siblings( ".ui-front:visible" ).map(function() {
10978				return +$( this ).css( "z-index" );
10979			}).get(),
10980			zIndexMax = Math.max.apply( null, zIndices );
10981
10982		if ( zIndexMax >= +this.uiDialog.css( "z-index" ) ) {
10983			this.uiDialog.css( "z-index", zIndexMax + 1 );
10984			moved = true;
10985		}
10986
10987		if ( moved && !silent ) {
10988			this._trigger( "focus", event );
10989		}
10990		return moved;
10991	},
10992
10993	open: function() {
10994		var that = this;
10995		if ( this._isOpen ) {
10996			if ( this._moveToTop() ) {
10997				this._focusTabbable();
10998			}
10999			return;
11000		}
11001
11002		this._isOpen = true;
11003		this.opener = $( $.ui.safeActiveElement( this.document[ 0 ] ) );
11004
11005		this._size();
11006		this._position();
11007		this._createOverlay();
11008		this._moveToTop( null, true );
11009
11010		// Ensure the overlay is moved to the top with the dialog, but only when
11011		// opening. The overlay shouldn't move after the dialog is open so that
11012		// modeless dialogs opened after the modal dialog stack properly.
11013		if ( this.overlay ) {
11014			this.overlay.css( "z-index", this.uiDialog.css( "z-index" ) - 1 );
11015		}
11016
11017		this._show( this.uiDialog, this.options.show, function() {
11018			that._focusTabbable();
11019			that._trigger( "focus" );
11020		});
11021
11022		// Track the dialog immediately upon openening in case a focus event
11023		// somehow occurs outside of the dialog before an element inside the
11024		// dialog is focused (#10152)
11025		this._makeFocusTarget();
11026
11027		this._trigger( "open" );
11028	},
11029
11030	_focusTabbable: function() {
11031		// Set focus to the first match:
11032		// 1. An element that was focused previously
11033		// 2. First element inside the dialog matching [autofocus]
11034		// 3. Tabbable element inside the content element
11035		// 4. Tabbable element inside the buttonpane
11036		// 5. The close button
11037		// 6. The dialog itself
11038		var hasFocus = this._focusedElement;
11039		if ( !hasFocus ) {
11040			hasFocus = this.element.find( "[autofocus]" );
11041		}
11042		if ( !hasFocus.length ) {
11043			hasFocus = this.element.find( ":tabbable" );
11044		}
11045		if ( !hasFocus.length ) {
11046			hasFocus = this.uiDialogButtonPane.find( ":tabbable" );
11047		}
11048		if ( !hasFocus.length ) {
11049			hasFocus = this.uiDialogTitlebarClose.filter( ":tabbable" );
11050		}
11051		if ( !hasFocus.length ) {
11052			hasFocus = this.uiDialog;
11053		}
11054		hasFocus.eq( 0 ).focus();
11055	},
11056
11057	_keepFocus: function( event ) {
11058		function checkFocus() {
11059			var activeElement = $.ui.safeActiveElement( this.document[0] ),
11060				isActive = this.uiDialog[0] === activeElement ||
11061					$.contains( this.uiDialog[0], activeElement );
11062			if ( !isActive ) {
11063				this._focusTabbable();
11064			}
11065		}
11066		event.preventDefault();
11067		checkFocus.call( this );
11068		// support: IE
11069		// IE <= 8 doesn't prevent moving focus even with event.preventDefault()
11070		// so we check again later
11071		this._delay( checkFocus );
11072	},
11073
11074	_createWrapper: function() {
11075		this.uiDialog = $("<div>")
11076			.hide()
11077			.attr({
11078				// Setting tabIndex makes the div focusable
11079				tabIndex: -1,
11080				role: "dialog"
11081			})
11082			.appendTo( this._appendTo() );
11083
11084		this._addClass( this.uiDialog, "ui-dialog", "ui-widget ui-widget-content ui-front" );
11085		this._on( this.uiDialog, {
11086			keydown: function( event ) {
11087				if ( this.options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode &&
11088						event.keyCode === $.ui.keyCode.ESCAPE ) {
11089					event.preventDefault();
11090					this.close( event );
11091					return;
11092				}
11093
11094				// prevent tabbing out of dialogs
11095				if ( event.keyCode !== $.ui.keyCode.TAB || event.isDefaultPrevented() ) {
11096					return;
11097				}
11098				var tabbables = this.uiDialog.find( ":tabbable" ),
11099					first = tabbables.filter( ":first" ),
11100					last = tabbables.filter( ":last" );
11101
11102				if ( ( event.target === last[0] || event.target === this.uiDialog[0] ) && !event.shiftKey ) {
11103					this._delay(function() {
11104						first.focus();
11105					});
11106					event.preventDefault();
11107				} else if ( ( event.target === first[0] || event.target === this.uiDialog[0] ) && event.shiftKey ) {
11108					this._delay(function() {
11109						last.focus();
11110					});
11111					event.preventDefault();
11112				}
11113			},
11114			mousedown: function( event ) {
11115				if ( this._moveToTop( event ) ) {
11116					this._focusTabbable();
11117				}
11118			}
11119		});
11120
11121		// We assume that any existing aria-describedby attribute means
11122		// that the dialog content is marked up properly
11123		// otherwise we brute force the content as the description
11124		if ( !this.element.find( "[aria-describedby]" ).length ) {
11125			this.uiDialog.attr({
11126				"aria-describedby": this.element.uniqueId().attr( "id" )
11127			});
11128		}
11129	},
11130
11131	_createTitlebar: function() {
11132		var uiDialogTitle;
11133
11134		this.uiDialogTitlebar = $( "<div>" );
11135		this._addClass( this.uiDialogTitlebar,
11136			"ui-dialog-titlebar", "ui-widget-header ui-helper-clearfix" );
11137		this._on( this.uiDialogTitlebar, {
11138			mousedown: function( event ) {
11139				// Don't prevent click on close button (#8838)
11140				// Focusing a dialog that is partially scrolled out of view
11141				// causes the browser to scroll it into view, preventing the click event
11142				if ( !$( event.target ).closest( ".ui-dialog-titlebar-close" ) ) {
11143					// Dialog isn't getting focus when dragging (#8063)
11144					this.uiDialog.focus();
11145				}
11146			}
11147		});
11148
11149		// support: IE
11150		// Use type="button" to prevent enter keypresses in textboxes from closing the
11151		// dialog in IE (#9312)
11152		this.uiDialogTitlebarClose = $( "<button type='button'></button>" )
11153			.button({
11154				label: this.options.closeText,
11155				icons: {
11156					primary: "ui-icon-closethick"
11157				},
11158				text: false
11159			})
11160			.appendTo( this.uiDialogTitlebar );
11161
11162		this._addClass( this.uiDialogTitlebarClose, "ui-dialog-titlebar-close" );
11163		this._on( this.uiDialogTitlebarClose, {
11164			click: function( event ) {
11165				event.preventDefault();
11166				this.close( event );
11167			}
11168		});
11169
11170		uiDialogTitle = $( "<span>" ).uniqueId().prependTo( this.uiDialogTitlebar );
11171		this._addClass( uiDialogTitle, "ui-dialog-title" );
11172		this._title( uiDialogTitle );
11173
11174		this.uiDialogTitlebar.prependTo( this.uiDialog );
11175
11176		this.uiDialog.attr({
11177			"aria-labelledby": uiDialogTitle.attr( "id" )
11178		});
11179	},
11180
11181	_title: function( title ) {
11182		if ( !this.options.title ) {
11183			title.html( "&#160;" );
11184		}
11185		title.text( this.options.title );
11186	},
11187
11188	_createButtonPane: function() {
11189		this.uiDialogButtonPane = $( "<div>" );
11190		this._addClass( this.uiDialogButtonPane, "ui-dialog-buttonpane",
11191			"ui-widget-content ui-helper-clearfix" );
11192
11193		this.uiButtonSet = $( "<div>" )
11194			.appendTo( this.uiDialogButtonPane );
11195		this._addClass( this.uiButtonSet, "ui-dialog-buttonset" );
11196
11197		this._createButtons();
11198	},
11199
11200	_createButtons: function() {
11201		var that = this,
11202			buttons = this.options.buttons;
11203
11204		// if we already have a button pane, remove it
11205		this.uiDialogButtonPane.remove();
11206		this.uiButtonSet.empty();
11207
11208		if ( $.isEmptyObject( buttons ) || ($.isArray( buttons ) && !buttons.length) ) {
11209			this._removeClass( this.uiDialog, "ui-dialog-buttons" );
11210			return;
11211		}
11212
11213		$.each( buttons, function( name, props ) {
11214			var click, buttonOptions;
11215			props = $.isFunction( props ) ?
11216				{ click: props, text: name } :
11217				props;
11218			// Default to a non-submitting button
11219			props = $.extend( { type: "button" }, props );
11220			// Change the context for the click callback to be the main element
11221			click = props.click;
11222			props.click = function() {
11223				click.apply( that.element[ 0 ], arguments );
11224			};
11225			buttonOptions = {
11226				icons: props.icons,
11227				text: props.showText
11228			};
11229			delete props.icons;
11230			delete props.showText;
11231			$( "<button></button>", props )
11232				.button( buttonOptions )
11233				.appendTo( that.uiButtonSet );
11234		});
11235		this._addClass( this.uiDialog, "ui-dialog-buttons" );
11236		this.uiDialogButtonPane.appendTo( this.uiDialog );
11237	},
11238
11239	_makeDraggable: function() {
11240		var that = this,
11241			options = this.options;
11242
11243		function filteredUi( ui ) {
11244			return {
11245				position: ui.position,
11246				offset: ui.offset
11247			};
11248		}
11249
11250		this.uiDialog.draggable({
11251			cancel: ".ui-dialog-content, .ui-dialog-titlebar-close",
11252			handle: ".ui-dialog-titlebar",
11253			containment: "document",
11254			start: function( event, ui ) {
11255				that._addClass( $( this ), "ui-dialog-dragging" );
11256				that._blockFrames();
11257				that._trigger( "dragStart", event, filteredUi( ui ) );
11258			},
11259			drag: function( event, ui ) {
11260				that._trigger( "drag", event, filteredUi( ui ) );
11261			},
11262			stop: function( event, ui ) {
11263				var left = ui.offset.left - that.document.scrollLeft(),
11264					top = ui.offset.top - that.document.scrollTop();
11265
11266				options.position = {
11267					my: "left top",
11268					at: "left" + (left >= 0 ? "+" : "") + left + " " +
11269						"top" + (top >= 0 ? "+" : "") + top,
11270					of: that.window
11271				};
11272				that._removeClass( $( this ), "ui-dialog-dragging" );
11273				that._unblockFrames();
11274				that._trigger( "dragStop", event, filteredUi( ui ) );
11275			}
11276		});
11277	},
11278
11279	_makeResizable: function() {
11280		var that = this,
11281			options = this.options,
11282			handles = options.resizable,
11283			// .ui-resizable has position: relative defined in the stylesheet
11284			// but dialogs have to use absolute or fixed positioning
11285			position = this.uiDialog.css("position"),
11286			resizeHandles = typeof handles === "string" ?
11287				handles	:
11288				"n,e,s,w,se,sw,ne,nw";
11289
11290		function filteredUi( ui ) {
11291			return {
11292				originalPosition: ui.originalPosition,
11293				originalSize: ui.originalSize,
11294				position: ui.position,
11295				size: ui.size
11296			};
11297		}
11298
11299		this.uiDialog.resizable({
11300			cancel: ".ui-dialog-content",
11301			containment: "document",
11302			alsoResize: this.element,
11303			maxWidth: options.maxWidth,
11304			maxHeight: options.maxHeight,
11305			minWidth: options.minWidth,
11306			minHeight: this._minHeight(),
11307			handles: resizeHandles,
11308			start: function( event, ui ) {
11309				that._addClass( $( this ), "ui-dialog-resizing" );
11310				that._blockFrames();
11311				that._trigger( "resizeStart", event, filteredUi( ui ) );
11312			},
11313			resize: function( event, ui ) {
11314				that._trigger( "resize", event, filteredUi( ui ) );
11315			},
11316			stop: function( event, ui ) {
11317				var offset = that.uiDialog.offset(),
11318					left = offset.left - that.document.scrollLeft(),
11319					top = offset.top - that.document.scrollTop();
11320
11321				options.height = that.uiDialog.height();
11322				options.width = that.uiDialog.width();
11323				options.position = {
11324					my: "left top",
11325					at: "left" + (left >= 0 ? "+" : "") + left + " " +
11326						"top" + (top >= 0 ? "+" : "") + top,
11327					of: that.window
11328				};
11329				that._removeClass( $( this ), "ui-dialog-resizing" );
11330				that._unblockFrames();
11331				that._trigger( "resizeStop", event, filteredUi( ui ) );
11332			}
11333		})
11334		.css( "position", position );
11335	},
11336
11337	_trackFocus: function() {
11338		this._on( this.widget(), {
11339			focusin: function( event ) {
11340				this._makeFocusTarget();
11341				this._focusedElement = $( event.target );
11342			}
11343		});
11344	},
11345
11346	_makeFocusTarget: function() {
11347		this._untrackInstance();
11348		this._trackingInstances().unshift( this );
11349	},
11350
11351	_untrackInstance: function() {
11352		var instances = this._trackingInstances(),
11353			exists = $.inArray( this, instances );
11354		if ( exists !== -1 ) {
11355			instances.splice( exists, 1 );
11356		}
11357	},
11358
11359	_trackingInstances: function() {
11360		var instances = this.document.data( "ui-dialog-instances" );
11361		if ( !instances ) {
11362			instances = [];
11363			this.document.data( "ui-dialog-instances", instances );
11364		}
11365		return instances;
11366	},
11367
11368	_minHeight: function() {
11369		var options = this.options;
11370
11371		return options.height === "auto" ?
11372			options.minHeight :
11373			Math.min( options.minHeight, options.height );
11374	},
11375
11376	_position: function() {
11377		// Need to show the dialog to get the actual offset in the position plugin
11378		var isVisible = this.uiDialog.is( ":visible" );
11379		if ( !isVisible ) {
11380			this.uiDialog.show();
11381		}
11382		this.uiDialog.position( this.options.position );
11383		if ( !isVisible ) {
11384			this.uiDialog.hide();
11385		}
11386	},
11387
11388	_setOptions: function( options ) {
11389		var that = this,
11390			resize = false,
11391			resizableOptions = {};
11392
11393		$.each( options, function( key, value ) {
11394			that._setOption( key, value );
11395
11396			if ( key in that.sizeRelatedOptions ) {
11397				resize = true;
11398			}
11399			if ( key in that.resizableRelatedOptions ) {
11400				resizableOptions[ key ] = value;
11401			}
11402		});
11403
11404		if ( resize ) {
11405			this._size();
11406			this._position();
11407		}
11408		if ( this.uiDialog.is( ":data(ui-resizable)" ) ) {
11409			this.uiDialog.resizable( "option", resizableOptions );
11410		}
11411	},
11412
11413	_setOption: function( key, value ) {
11414		var isDraggable, isResizable,
11415			uiDialog = this.uiDialog;
11416
11417		if ( key === "disabled" ) {
11418			return;
11419		}
11420
11421		this._super( key, value );
11422
11423		if ( key === "appendTo" ) {
11424			this.uiDialog.appendTo( this._appendTo() );
11425		}
11426
11427		if ( key === "buttons" ) {
11428			this._createButtons();
11429		}
11430
11431		if ( key === "closeText" ) {
11432			this.uiDialogTitlebarClose.button({
11433				// Ensure that we always pass a string
11434				label: "" + value
11435			});
11436		}
11437
11438		if ( key === "draggable" ) {
11439			isDraggable = uiDialog.is( ":data(ui-draggable)" );
11440			if ( isDraggable && !value ) {
11441				uiDialog.draggable( "destroy" );
11442			}
11443
11444			if ( !isDraggable && value ) {
11445				this._makeDraggable();
11446			}
11447		}
11448
11449		if ( key === "position" ) {
11450			this._position();
11451		}
11452
11453		if ( key === "resizable" ) {
11454			// currently resizable, becoming non-resizable
11455			isResizable = uiDialog.is( ":data(ui-resizable)" );
11456			if ( isResizable && !value ) {
11457				uiDialog.resizable( "destroy" );
11458			}
11459
11460			// currently resizable, changing handles
11461			if ( isResizable && typeof value === "string" ) {
11462				uiDialog.resizable( "option", "handles", value );
11463			}
11464
11465			// currently non-resizable, becoming resizable
11466			if ( !isResizable && value !== false ) {
11467				this._makeResizable();
11468			}
11469		}
11470
11471		if ( key === "title" ) {
11472			this._title( this.uiDialogTitlebar.find( ".ui-dialog-title" ) );
11473		}
11474	},
11475
11476	_size: function() {
11477		// If the user has resized the dialog, the .ui-dialog and .ui-dialog-content
11478		// divs will both have width and height set, so we need to reset them
11479		var nonContentHeight, minContentHeight, maxContentHeight,
11480			options = this.options;
11481
11482		// Reset content sizing
11483		this.element.show().css({
11484			width: "auto",
11485			minHeight: 0,
11486			maxHeight: "none",
11487			height: 0
11488		});
11489
11490		if ( options.minWidth > options.width ) {
11491			options.width = options.minWidth;
11492		}
11493
11494		// reset wrapper sizing
11495		// determine the height of all the non-content elements
11496		nonContentHeight = this.uiDialog.css({
11497				height: "auto",
11498				width: options.width
11499			})
11500			.outerHeight();
11501		minContentHeight = Math.max( 0, options.minHeight - nonContentHeight );
11502		maxContentHeight = typeof options.maxHeight === "number" ?
11503			Math.max( 0, options.maxHeight - nonContentHeight ) :
11504			"none";
11505
11506		if ( options.height === "auto" ) {
11507			this.element.css({
11508				minHeight: minContentHeight,
11509				maxHeight: maxContentHeight,
11510				height: "auto"
11511			});
11512		} else {
11513			this.element.height( Math.max( 0, options.height - nonContentHeight ) );
11514		}
11515
11516		if ( this.uiDialog.is( ":data(ui-resizable)" ) ) {
11517			this.uiDialog.resizable( "option", "minHeight", this._minHeight() );
11518		}
11519	},
11520
11521	_blockFrames: function() {
11522		this.iframeBlocks = this.document.find( "iframe" ).map(function() {
11523			var iframe = $( this );
11524
11525			return $( "<div>" )
11526				.css({
11527					position: "absolute",
11528					width: iframe.outerWidth(),
11529					height: iframe.outerHeight()
11530				})
11531				.appendTo( iframe.parent() )
11532				.offset( iframe.offset() )[0];
11533		});
11534	},
11535
11536	_unblockFrames: function() {
11537		if ( this.iframeBlocks ) {
11538			this.iframeBlocks.remove();
11539			delete this.iframeBlocks;
11540		}
11541	},
11542
11543	_allowInteraction: function( event ) {
11544		if ( $( event.target ).closest( ".ui-dialog" ).length ) {
11545			return true;
11546		}
11547
11548		// TODO: Remove hack when datepicker implements
11549		// the .ui-front logic (#8989)
11550		return !!$( event.target ).closest( ".ui-datepicker" ).length;
11551	},
11552
11553	_createOverlay: function() {
11554		if ( !this.options.modal ) {
11555			return;
11556		}
11557
11558		// We use a delay in case the overlay is created from an
11559		// event that we're going to be cancelling (#2804)
11560		var isOpening = true;
11561		this._delay(function() {
11562			isOpening = false;
11563		});
11564
11565		if ( !this.document.data( "ui-dialog-overlays" ) ) {
11566
11567			// Prevent use of anchors and inputs
11568			// Using _on() for an event handler shared across many instances is
11569			// safe because the dialogs stack and must be closed in reverse order
11570			this._on( this.document, {
11571				focusin: function( event ) {
11572					if ( isOpening ) {
11573						return;
11574					}
11575
11576					if ( !this._allowInteraction( event ) ) {
11577						event.preventDefault();
11578						this._trackingInstances()[ 0 ]._focusTabbable();
11579					}
11580				}
11581			});
11582		}
11583
11584		this.overlay = $( "<div>" )
11585			.appendTo( this._appendTo() );
11586
11587		this._addClass( this.overlay, null, "ui-widget-overlay ui-front" );
11588		this._on( this.overlay, {
11589			mousedown: "_keepFocus"
11590		});
11591		this.document.data( "ui-dialog-overlays",
11592			(this.document.data( "ui-dialog-overlays" ) || 0) + 1 );
11593	},
11594
11595	_destroyOverlay: function() {
11596		if ( !this.options.modal ) {
11597			return;
11598		}
11599
11600		if ( this.overlay ) {
11601			var overlays = this.document.data( "ui-dialog-overlays" ) - 1;
11602
11603			if ( !overlays ) {
11604				this.document
11605					.unbind( "focusin" )
11606					.removeData( "ui-dialog-overlays" );
11607			} else {
11608				this.document.data( "ui-dialog-overlays", overlays );
11609			}
11610
11611			this.overlay.remove();
11612			this.overlay = null;
11613		}
11614	}
11615});
11616
11617// DEPRECATED
11618// TODO: switch return back to widget declaration at top of file when this is removed
11619if ( $.uiBackCompat !== false ) {
11620
11621	// Backcompat for dialogClass option
11622	$.widget( "ui.dialog", $.ui.dialog, {
11623		options: {
11624			dialogClass: ""
11625		},
11626		_createWrapper: function() {
11627			this._super();
11628			this.uiDialog.addClass( this.options.dialogClass );
11629		},
11630		_setOption: function( key, value ) {
11631			if ( key === "dialogClass" ) {
11632				this.uiDialog
11633					.removeClass( this.options.dialogClass )
11634					.addClass( value );
11635			}
11636			this._superApply( arguments );
11637		}
11638	});
11639}
11640
11641return $.ui.dialog;
11642
11643}));
11644
11645//>>label: Blind Effect
11646//>>group: Effects
11647//>>description: Blinds the element.
11648//>>docs: http://api.jqueryui.com/blind-effect/
11649//>>demos: http://jqueryui.com/effect/
11650
11651(function( factory ) {
11652	if ( typeof define === "function" && define.amd ) {
11653
11654		// AMD. Register as an anonymous module.
11655		define([
11656			"jquery",
11657			"./effect"
11658		], factory );
11659	} else {
11660
11661		// Browser globals
11662		factory( jQuery );
11663	}
11664}(function( $ ) {
11665
11666return $.effects.define( "blind", "hide", function( options, done ) {
11667	var map = {
11668			up: [ "bottom", "top" ],
11669			vertical: [ "bottom", "top" ],
11670			down: [ "top", "bottom" ],
11671			left: [ "right", "left" ],
11672			horizontal: [ "right", "left" ],
11673			right: [ "left", "right" ]
11674		},
11675		element = $( this ),
11676		direction = options.direction || "up",
11677		start = element.cssClip(),
11678		animate = { clip: $.extend( {}, start ) },
11679		placeholder = $.effects.createPlaceholder( element );
11680
11681	animate.clip[ map[ direction ][ 0 ] ] = animate.clip[ map[ direction ][ 1 ] ];
11682
11683	if ( options.mode === "show" ) {
11684		element.cssClip( animate.clip );
11685		if ( placeholder ) {
11686			placeholder.css( $.effects.clipToBox( animate ) );
11687		}
11688
11689		animate.clip = start;
11690	}
11691
11692	if ( placeholder ) {
11693		placeholder.animate( $.effects.clipToBox( animate ), options.duration, options.easing );
11694	}
11695
11696	element.animate( animate, {
11697		queue: false,
11698		duration: options.duration,
11699		easing: options.easing,
11700		complete: done
11701	});
11702});
11703
11704}));
11705
11706//>>label: Bounce Effect
11707//>>group: Effects
11708//>>description: Bounces an element horizontally or vertically n times.
11709//>>docs: http://api.jqueryui.com/bounce-effect/
11710//>>demos: http://jqueryui.com/effect/
11711
11712(function( factory ) {
11713	if ( typeof define === "function" && define.amd ) {
11714
11715		// AMD. Register as an anonymous module.
11716		define([
11717			"jquery",
11718			"./effect"
11719		], factory );
11720	} else {
11721
11722		// Browser globals
11723		factory( jQuery );
11724	}
11725}(function( $ ) {
11726
11727return $.effects.define( "bounce", function( options, done ) {
11728	var upAnim, downAnim, refValue,
11729		element = $( this ),
11730
11731		// defaults:
11732		mode = options.mode,
11733		hide = mode === "hide",
11734		show = mode === "show",
11735		direction = options.direction || "up",
11736		distance = options.distance,
11737		times = options.times || 5,
11738
11739		// number of internal animations
11740		anims = times * 2 + ( show || hide ? 1 : 0 ),
11741		speed = options.duration / anims,
11742		easing = options.easing,
11743
11744		// utility:
11745		ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
11746		motion = ( direction === "up" || direction === "left" ),
11747		i = 0,
11748
11749		queuelen = element.queue().length;
11750
11751	$.effects.createPlaceholder( element );
11752
11753	refValue = element.css( ref );
11754
11755	// default distance for the BIGGEST bounce is the outer Distance / 3
11756	if ( !distance ) {
11757		distance = element[ ref === "top" ? "outerHeight" : "outerWidth" ]() / 3;
11758	}
11759
11760	if ( show ) {
11761		downAnim = { opacity: 1 };
11762		downAnim[ ref ] = refValue;
11763
11764		// if we are showing, force opacity 0 and set the initial position
11765		// then do the "first" animation
11766		element
11767			.css( "opacity", 0 )
11768			.css( ref, motion ? -distance * 2 : distance * 2 )
11769			.animate( downAnim, speed, easing );
11770	}
11771
11772	// start at the smallest distance if we are hiding
11773	if ( hide ) {
11774		distance = distance / Math.pow( 2, times - 1 );
11775	}
11776
11777	downAnim = {};
11778	downAnim[ ref ] = refValue;
11779	// Bounces up/down/left/right then back to 0 -- times * 2 animations happen here
11780	for ( ; i < times; i++ ) {
11781		upAnim = {};
11782		upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance;
11783
11784		element
11785			.animate( upAnim, speed, easing )
11786			.animate( downAnim, speed, easing );
11787
11788		distance = hide ? distance * 2 : distance / 2;
11789	}
11790
11791	// Last Bounce when Hiding
11792	if ( hide ) {
11793		upAnim = { opacity: 0 };
11794		upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance;
11795
11796		element.animate( upAnim, speed, easing );
11797	}
11798
11799	element.queue( done );
11800
11801	$.effects.unshift( element, queuelen, anims + 1 );
11802});
11803
11804}));
11805
11806//>>label: Clip Effect
11807//>>group: Effects
11808//>>description: Clips the element on and off like an old TV.
11809//>>docs: http://api.jqueryui.com/clip-effect/
11810//>>demos: http://jqueryui.com/effect/
11811
11812(function( factory ) {
11813	if ( typeof define === "function" && define.amd ) {
11814
11815		// AMD. Register as an anonymous module.
11816		define([
11817			"jquery",
11818			"./effect"
11819		], factory );
11820	} else {
11821
11822		// Browser globals
11823		factory( jQuery );
11824	}
11825}(function( $ ) {
11826
11827return $.effects.define( "clip", "hide", function( options, done ) {
11828	var start,
11829		animate = {},
11830		element = $( this ),
11831		direction = options.direction || "vertical",
11832		both = direction === "both",
11833		horizontal = both || direction === "horizontal",
11834		vertical = both || direction === "vertical";
11835
11836	start = element.cssClip();
11837	animate.clip = {
11838		top: vertical ? ( start.bottom - start.top ) / 2 : start.top,
11839		right: horizontal ? ( start.right - start.left ) / 2 : start.right,
11840		bottom: vertical ? ( start.bottom - start.top ) / 2 : start.bottom,
11841		left: horizontal ? ( start.right - start.left ) / 2 : start.left
11842	};
11843
11844	$.effects.createPlaceholder( element );
11845
11846	if ( options.mode === "show" ) {
11847		element.cssClip( animate.clip );
11848		animate.clip = start;
11849	}
11850
11851	element.animate( animate, {
11852		queue: false,
11853		duration: options.duration,
11854		easing: options.easing,
11855		complete: done
11856	});
11857
11858});
11859
11860}));
11861
11862//>>label: Drop Effect
11863//>>group: Effects
11864//>>description: Moves an element in one direction and hides it at the same time.
11865//>>docs: http://api.jqueryui.com/drop-effect/
11866//>>demos: http://jqueryui.com/effect/
11867
11868(function( factory ) {
11869	if ( typeof define === "function" && define.amd ) {
11870
11871		// AMD. Register as an anonymous module.
11872		define([
11873			"jquery",
11874			"./effect"
11875		], factory );
11876	} else {
11877
11878		// Browser globals
11879		factory( jQuery );
11880	}
11881}(function( $ ) {
11882
11883return $.effects.define( "drop", "hide", function( options, done ) {
11884
11885	var distance,
11886		element = $( this ),
11887		mode = options.mode,
11888		show = mode === "show",
11889		direction = options.direction || "left",
11890		ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
11891		motion = ( direction === "up" || direction === "left" ) ? "-=" : "+=",
11892		oppositeMotion = ( motion === "+=" ) ? "-=" : "+=",
11893		animation = {
11894			opacity: 0
11895		};
11896
11897	$.effects.createPlaceholder( element );
11898
11899	distance = options.distance || element[ ref === "top" ? "outerHeight" : "outerWidth" ]( true ) / 2;
11900
11901	animation[ ref ] = motion + distance;
11902
11903	if ( show ) {
11904		element.css( animation );
11905
11906		animation[ ref ] = oppositeMotion + distance;
11907		animation.opacity = 1;
11908	}
11909
11910	// Animate
11911	element.animate( animation, {
11912		queue: false,
11913		duration: options.duration,
11914		easing: options.easing,
11915		complete: done
11916	});
11917});
11918
11919}));
11920
11921//>>label: Explode Effect
11922//>>group: Effects
11923//>>description: Explodes an element in all directions into n pieces. Implodes an element to its original wholeness.
11924//>>docs: http://api.jqueryui.com/explode-effect/
11925//>>demos: http://jqueryui.com/effect/
11926
11927(function( factory ) {
11928	if ( typeof define === "function" && define.amd ) {
11929
11930		// AMD. Register as an anonymous module.
11931		define([
11932			"jquery",
11933			"./effect"
11934		], factory );
11935	} else {
11936
11937		// Browser globals
11938		factory( jQuery );
11939	}
11940}(function( $ ) {
11941
11942return $.effects.define( "explode", "hide", function( options, done ) {
11943
11944	var i, j, left, top, mx, my,
11945		rows = options.pieces ? Math.round( Math.sqrt( options.pieces ) ) : 3,
11946		cells = rows,
11947		element = $( this ),
11948		mode = options.mode,
11949		show = mode === "show",
11950
11951		// show and then visibility:hidden the element before calculating offset
11952		offset = element.show().css( "visibility", "hidden" ).offset(),
11953
11954		// width and height of a piece
11955		width = Math.ceil( element.outerWidth() / cells ),
11956		height = Math.ceil( element.outerHeight() / rows ),
11957		pieces = [];
11958
11959	// children animate complete:
11960	function childComplete() {
11961		pieces.push( this );
11962		if ( pieces.length === rows * cells ) {
11963			animComplete();
11964		}
11965	}
11966
11967	// clone the element for each row and cell.
11968	for ( i = 0; i < rows ; i++ ) { // ===>
11969		top = offset.top + i * height;
11970		my = i - ( rows - 1 ) / 2 ;
11971
11972		for ( j = 0; j < cells ; j++ ) { // |||
11973			left = offset.left + j * width;
11974			mx = j - ( cells - 1 ) / 2 ;
11975
11976			// Create a clone of the now hidden main element that will be absolute positioned
11977			// within a wrapper div off the -left and -top equal to size of our pieces
11978			element
11979				.clone()
11980				.appendTo( "body" )
11981				.wrap( "<div></div>" )
11982				.css({
11983					position: "absolute",
11984					visibility: "visible",
11985					left: -j * width,
11986					top: -i * height
11987				})
11988
11989			// select the wrapper - make it overflow: hidden and absolute positioned based on
11990			// where the original was located +left and +top equal to the size of pieces
11991				.parent()
11992				.addClass( "ui-effects-explode" )
11993				.css({
11994					position: "absolute",
11995					overflow: "hidden",
11996					width: width,
11997					height: height,
11998					left: left + ( show ? mx * width : 0 ),
11999					top: top + ( show ? my * height : 0 ),
12000					opacity: show ? 0 : 1
12001				}).animate({
12002					left: left + ( show ? 0 : mx * width ),
12003					top: top + ( show ? 0 : my * height ),
12004					opacity: show ? 1 : 0
12005				}, options.duration || 500, options.easing, childComplete );
12006		}
12007	}
12008
12009	function animComplete() {
12010		element.css({
12011			visibility: "visible"
12012		});
12013		$( pieces ).remove();
12014		done();
12015	}
12016});
12017
12018}));
12019
12020//>>label: Fade Effect
12021//>>group: Effects
12022//>>description: Fades the element.
12023//>>docs: http://api.jqueryui.com/fade-effect/
12024//>>demos: http://jqueryui.com/effect/
12025
12026(function( factory ) {
12027	if ( typeof define === "function" && define.amd ) {
12028
12029		// AMD. Register as an anonymous module.
12030		define([
12031			"jquery",
12032			"./effect"
12033		], factory );
12034	} else {
12035
12036		// Browser globals
12037		factory( jQuery );
12038	}
12039}(function( $ ) {
12040
12041return $.effects.define( "fade", "toggle", function( options, done ) {
12042	var show = options.mode === "show";
12043
12044	$( this )
12045		.css( "opacity", show ? 0 : 1 )
12046		.animate({
12047			opacity: show ? 1 : 0
12048		}, {
12049			queue: false,
12050			duration: options.duration,
12051			easing: options.easing,
12052			complete: done
12053		});
12054});
12055
12056}));
12057
12058//>>label: Fold Effect
12059//>>group: Effects
12060//>>description: Folds an element first horizontally and then vertically.
12061//>>docs: http://api.jqueryui.com/fold-effect/
12062//>>demos: http://jqueryui.com/effect/
12063
12064(function( factory ) {
12065	if ( typeof define === "function" && define.amd ) {
12066
12067		// AMD. Register as an anonymous module.
12068		define([
12069			"jquery",
12070			"./effect"
12071		], factory );
12072	} else {
12073
12074		// Browser globals
12075		factory( jQuery );
12076	}
12077}(function( $ ) {
12078
12079return $.effects.define( "fold", "hide", function( options, done ) {
12080
12081	// Create element
12082	var element = $( this ),
12083		mode = options.mode,
12084		show = mode === "show",
12085		hide = mode === "hide",
12086		size = options.size || 15,
12087		percent = /([0-9]+)%/.exec( size ),
12088		horizFirst = !!options.horizFirst,
12089		ref = horizFirst ? [ "right", "bottom" ] : [ "bottom", "right" ],
12090		duration = options.duration / 2,
12091
12092		placeholder = $.effects.createPlaceholder( element ),
12093
12094		start = element.cssClip(),
12095		animation1 = { clip: $.extend( {}, start ) },
12096		animation2 = { clip: $.extend( {}, start ) },
12097
12098		distance = [ start[ ref[ 0 ] ], start[ ref[ 1 ] ] ],
12099
12100		queuelen = element.queue().length;
12101
12102	if ( percent ) {
12103		size = parseInt( percent[ 1 ], 10 ) / 100 * distance[ hide ? 0 : 1 ];
12104	}
12105	animation1.clip[ ref[ 0 ] ] = size;
12106	animation2.clip[ ref[ 0 ] ] = size;
12107	animation2.clip[ ref[ 1 ] ] = 0;
12108
12109	if ( show ) {
12110		element.cssClip( animation2.clip );
12111		if ( placeholder ) {
12112			placeholder.css( $.effects.clipToBox( animation2 ) );
12113		}
12114
12115		animation2.clip = start;
12116	}
12117
12118	// Animate
12119	element
12120		.queue(function( next ) {
12121			if ( placeholder ) {
12122				placeholder
12123					.animate( $.effects.clipToBox( animation1 ), duration, options.easing )
12124					.animate( $.effects.clipToBox( animation2 ), duration, options.easing );
12125			}
12126
12127			next();
12128		})
12129		.animate( animation1, duration, options.easing )
12130		.animate( animation2, duration, options.easing )
12131		.queue( done );
12132
12133	$.effects.unshift( element, queuelen, 4 );
12134});
12135
12136}));
12137
12138//>>label: Highlight Effect
12139//>>group: Effects
12140//>>description: Highlights the background of an element in a defined color for a custom duration.
12141//>>docs: http://api.jqueryui.com/highlight-effect/
12142//>>demos: http://jqueryui.com/effect/
12143
12144(function( factory ) {
12145	if ( typeof define === "function" && define.amd ) {
12146
12147		// AMD. Register as an anonymous module.
12148		define([
12149			"jquery",
12150			"./effect"
12151		], factory );
12152	} else {
12153
12154		// Browser globals
12155		factory( jQuery );
12156	}
12157}(function( $ ) {
12158
12159return $.effects.define( "highlight", "show", function( options, done ) {
12160	var element = $( this ),
12161		animation = {
12162			backgroundColor: element.css( "backgroundColor" )
12163		};
12164
12165	if ( options.mode === "hide" ) {
12166		animation.opacity = 0;
12167	}
12168
12169	$.effects.saveStyle( element );
12170
12171	element
12172		.css({
12173			backgroundImage: "none",
12174			backgroundColor: options.color || "#ffff99"
12175		})
12176		.animate( animation, {
12177			queue: false,
12178			duration: options.duration,
12179			easing: options.easing,
12180			complete: done
12181		});
12182});
12183
12184}));
12185
12186//>>label: Puff Effect
12187//>>group: Effects
12188//>>description: Creates a puff effect by scaling the element up and hiding it at the same time.
12189//>>docs: http://api.jqueryui.com/puff-effect/
12190//>>demos: http://jqueryui.com/effect/
12191
12192(function( factory ) {
12193	if ( typeof define === "function" && define.amd ) {
12194
12195		// AMD. Register as an anonymous module.
12196		define([
12197			"jquery",
12198			"./effect",
12199			"./effect-scale"
12200		], factory );
12201	} else {
12202
12203		// Browser globals
12204		factory( jQuery );
12205	}
12206}(function( $ ) {
12207
12208return $.effects.define( "puff", "hide", function( options, done ) {
12209	var newOptions = $.extend( true, {}, options, {
12210		fade: true,
12211		percent: parseInt( options.percent, 10 ) || 150
12212	});
12213
12214	$.effects.effect.scale.call( this, newOptions, done );
12215});
12216
12217}));
12218
12219//>>label: Pulsate Effect
12220//>>group: Effects
12221//>>description: Pulsates an element n times by changing the opacity to zero and back.
12222//>>docs: http://api.jqueryui.com/pulsate-effect/
12223//>>demos: http://jqueryui.com/effect/
12224
12225(function( factory ) {
12226	if ( typeof define === "function" && define.amd ) {
12227
12228		// AMD. Register as an anonymous module.
12229		define([
12230			"jquery",
12231			"./effect"
12232		], factory );
12233	} else {
12234
12235		// Browser globals
12236		factory( jQuery );
12237	}
12238}(function( $ ) {
12239
12240return $.effects.define( "pulsate", "show", function( options, done ) {
12241	var element = $( this ),
12242		mode = options.mode,
12243		show = mode === "show",
12244		hide = mode === "hide",
12245		showhide = show || hide,
12246
12247		// Showing or hiding leaves off the "last" animation
12248		anims = ( ( options.times || 5 ) * 2 ) + ( showhide ? 1 : 0 ),
12249		duration = options.duration / anims,
12250		animateTo = 0,
12251		i = 1,
12252		queuelen = element.queue().length;
12253
12254	if ( show || !element.is( ":visible" ) ) {
12255		element.css( "opacity", 0 ).show();
12256		animateTo = 1;
12257	}
12258
12259	// Anims - 1 opacity "toggles"
12260	for ( ; i < anims; i++ ) {
12261		element.animate( { opacity: animateTo }, duration, options.easing );
12262		animateTo = 1 - animateTo;
12263	}
12264
12265	element.animate( { opacity: animateTo }, duration, options.easing );
12266
12267	element.queue( done );
12268
12269	$.effects.unshift( element, queuelen, anims + 1 );
12270});
12271
12272}));
12273
12274//>>label: Scale Effect
12275//>>group: Effects
12276//>>description: Grows or shrinks an element and its content. Restores an element to its original size.
12277//>>docs: http://api.jqueryui.com/scale-effect/
12278//>>demos: http://jqueryui.com/effect/
12279
12280(function( factory ) {
12281	if ( typeof define === "function" && define.amd ) {
12282
12283		// AMD. Register as an anonymous module.
12284		define([
12285			"jquery",
12286			"./effect",
12287			"./effect-size"
12288		], factory );
12289	} else {
12290
12291		// Browser globals
12292		factory( jQuery );
12293	}
12294}(function( $ ) {
12295
12296return $.effects.define( "scale", function( options, done ) {
12297
12298	// Create element
12299	var el = $( this ),
12300		mode = options.mode,
12301		percent = parseInt( options.percent, 10 ) ||
12302			( parseInt( options.percent, 10 ) === 0 ? 0 : ( mode !== "effect" ? 0 : 100 ) ),
12303
12304		newOptions = $.extend( true, {
12305			from: $.effects.scaledDimensions( el ),
12306			to: $.effects.scaledDimensions( el, percent, options.direction || "both" ),
12307			origin: options.origin || [ "middle", "center" ]
12308		}, options );
12309
12310	// Fade option to support puff
12311	if ( options.fade ) {
12312		newOptions.from.opacity = 1;
12313		newOptions.to.opacity = 0;
12314	}
12315
12316	$.effects.effect.size.call( this, newOptions, done );
12317});
12318
12319}));
12320
12321//>>label: Shake Effect
12322//>>group: Effects
12323//>>description: Shakes an element horizontally or vertically n times.
12324//>>docs: http://api.jqueryui.com/shake-effect/
12325//>>demos: http://jqueryui.com/effect/
12326
12327(function( factory ) {
12328	if ( typeof define === "function" && define.amd ) {
12329
12330		// AMD. Register as an anonymous module.
12331		define([
12332			"jquery",
12333			"./effect"
12334		], factory );
12335	} else {
12336
12337		// Browser globals
12338		factory( jQuery );
12339	}
12340}(function( $ ) {
12341
12342return $.effects.define( "shake", function( options, done ) {
12343
12344	var i = 1,
12345		element = $( this ),
12346		direction = options.direction || "left",
12347		distance = options.distance || 20,
12348		times = options.times || 3,
12349		anims = times * 2 + 1,
12350		speed = Math.round( options.duration / anims ),
12351		ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
12352		positiveMotion = ( direction === "up" || direction === "left" ),
12353		animation = {},
12354		animation1 = {},
12355		animation2 = {},
12356
12357		queuelen = element.queue().length;
12358
12359	$.effects.createPlaceholder( element );
12360
12361	// Animation
12362	animation[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance;
12363	animation1[ ref ] = ( positiveMotion ? "+=" : "-=" ) + distance * 2;
12364	animation2[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance * 2;
12365
12366	// Animate
12367	element.animate( animation, speed, options.easing );
12368
12369	// Shakes
12370	for ( ; i < times; i++ ) {
12371		element.animate( animation1, speed, options.easing ).animate( animation2, speed, options.easing );
12372	}
12373
12374	element
12375		.animate( animation1, speed, options.easing )
12376		.animate( animation, speed / 2, options.easing )
12377		.queue( done );
12378
12379	$.effects.unshift( element, queuelen, anims + 1 );
12380});
12381
12382}));
12383
12384//>>label: Size Effect
12385//>>group: Effects
12386//>>description: Resize an element to a specified width and height.
12387//>>docs: http://api.jqueryui.com/size-effect/
12388//>>demos: http://jqueryui.com/effect/
12389
12390(function( factory ) {
12391	if ( typeof define === "function" && define.amd ) {
12392
12393		// AMD. Register as an anonymous module.
12394		define([
12395			"jquery",
12396			"./effect"
12397		], factory );
12398	} else {
12399
12400		// Browser globals
12401		factory( jQuery );
12402	}
12403}(function( $ ) {
12404
12405return $.effects.define( "size", function( options, done ) {
12406
12407	// Create element
12408	var baseline, factor, temp,
12409		element = $( this ),
12410
12411		// Copy for children
12412		cProps = [ "fontSize" ],
12413		vProps = [ "borderTopWidth", "borderBottomWidth", "paddingTop", "paddingBottom" ],
12414		hProps = [ "borderLeftWidth", "borderRightWidth", "paddingLeft", "paddingRight" ],
12415
12416		// Set options
12417		mode = options.mode,
12418		restore = mode !== "effect",
12419		scale = options.scale || "both",
12420		origin = options.origin || [ "middle", "center" ],
12421		position = element.css( "position" ),
12422		pos = element.position(),
12423		original = $.effects.scaledDimensions( element ),
12424		from = options.from || original,
12425		to = options.to || $.effects.scaledDimensions( element, 0 );
12426
12427	$.effects.createPlaceholder( element );
12428
12429	if ( mode === "show" ) {
12430		temp = from;
12431		from = to;
12432		to = temp;
12433	}
12434
12435	// Set scaling factor
12436	factor = {
12437		from: {
12438			y: from.height / original.height,
12439			x: from.width / original.width
12440		},
12441		to: {
12442			y: to.height / original.height,
12443			x: to.width / original.width
12444		}
12445	};
12446
12447	// Scale the css box
12448	if ( scale === "box" || scale === "both" ) {
12449
12450		// Vertical props scaling
12451		if ( factor.from.y !== factor.to.y ) {
12452			from = $.effects.setTransition( element, vProps, factor.from.y, from );
12453			to = $.effects.setTransition( element, vProps, factor.to.y, to );
12454		}
12455
12456		// Horizontal props scaling
12457		if ( factor.from.x !== factor.to.x ) {
12458			from = $.effects.setTransition( element, hProps, factor.from.x, from );
12459			to = $.effects.setTransition( element, hProps, factor.to.x, to );
12460		}
12461	}
12462
12463	// Scale the content
12464	if ( scale === "content" || scale === "both" ) {
12465
12466		// Vertical props scaling
12467		if ( factor.from.y !== factor.to.y ) {
12468			from = $.effects.setTransition( element, cProps, factor.from.y, from );
12469			to = $.effects.setTransition( element, cProps, factor.to.y, to );
12470		}
12471	}
12472
12473	// Adjust the position properties based on the provided origin points
12474	if ( origin ) {
12475		baseline = $.effects.getBaseline( origin, original );
12476		from.top = ( original.outerHeight - from.outerHeight ) * baseline.y + pos.top;
12477		from.left = ( original.outerWidth - from.outerWidth ) * baseline.x + pos.left;
12478		to.top = ( original.outerHeight - to.outerHeight ) * baseline.y + pos.top;
12479		to.left = ( original.outerWidth - to.outerWidth ) * baseline.x + pos.left;
12480	}
12481	element.css( from );
12482
12483	// Animate the children if desired
12484	if ( scale === "content" || scale === "both" ) {
12485
12486		vProps = vProps.concat([ "marginTop", "marginBottom" ]).concat( cProps );
12487		hProps = hProps.concat([ "marginLeft", "marginRight" ]);
12488
12489		// Only animate children with width attributes specified
12490		// TODO: is this right? should we include anything with css width specified as well
12491		element.find( "*[width]" ).each( function() {
12492			var child = $( this ),
12493				childOriginal = $.effects.scaledDimensions( child ),
12494				childFrom = {
12495					height: childOriginal.height * factor.from.y,
12496					width: childOriginal.width * factor.from.x,
12497					outerHeight: childOriginal.outerHeight * factor.from.y,
12498					outerWidth: childOriginal.outerWidth * factor.from.x
12499				},
12500				childTo = {
12501					height: childOriginal.height * factor.to.y,
12502					width: childOriginal.width * factor.to.x,
12503					outerHeight: childOriginal.height * factor.to.y,
12504					outerWidth: childOriginal.width * factor.to.x
12505				};
12506
12507			// Vertical props scaling
12508			if ( factor.from.y !== factor.to.y ) {
12509				childFrom = $.effects.setTransition( child, vProps, factor.from.y, childFrom );
12510				childTo = $.effects.setTransition( child, vProps, factor.to.y, childTo );
12511			}
12512
12513			// Horizontal props scaling
12514			if ( factor.from.x !== factor.to.x ) {
12515				childFrom = $.effects.setTransition( child, hProps, factor.from.x, childFrom );
12516				childTo = $.effects.setTransition( child, hProps, factor.to.x, childTo );
12517			}
12518
12519			if ( restore ) {
12520				$.effects.saveStyle( child );
12521			}
12522
12523			// Animate children
12524			child.css( childFrom );
12525			child.animate( childTo, options.duration, options.easing, function() {
12526
12527				// Restore children
12528				if ( restore ) {
12529					$.effects.restoreStyle( child );
12530				}
12531			});
12532		});
12533	}
12534
12535	// Animate
12536	element.animate( to, {
12537		queue: false,
12538		duration: options.duration,
12539		easing: options.easing,
12540		complete: function() {
12541
12542			var offset = element.offset();
12543
12544			if ( to.opacity === 0 ) {
12545				element.css( "opacity", from.opacity );
12546			}
12547
12548			if ( !restore ) {
12549				element
12550					.css( "position", position === "static" ? "relative" : position )
12551					.offset( offset );
12552
12553				// Need to save style here so that automatic style restoration
12554				// doesn't restore to the original styles from before the animation.
12555				$.effects.saveStyle( element );
12556			}
12557
12558			done();
12559		}
12560	});
12561
12562});
12563
12564}));
12565
12566//>>label: Slide Effect
12567//>>group: Effects
12568//>>description: Slides an element in and out of the viewport.
12569//>>docs: http://api.jqueryui.com/slide-effect/
12570//>>demos: http://jqueryui.com/effect/
12571
12572(function( factory ) {
12573	if ( typeof define === "function" && define.amd ) {
12574
12575		// AMD. Register as an anonymous module.
12576		define([
12577			"jquery",
12578			"./effect"
12579		], factory );
12580	} else {
12581
12582		// Browser globals
12583		factory( jQuery );
12584	}
12585}(function( $ ) {
12586
12587return $.effects.define( "slide", "show", function( options, done ) {
12588	var startClip, startRef,
12589		element = $( this ),
12590		map = {
12591			up: [ "bottom", "top" ],
12592			down: [ "top", "bottom" ],
12593			left: [ "right", "left" ],
12594			right: [ "left", "right" ]
12595		},
12596		mode = options.mode,
12597		direction = options.direction || "left",
12598		ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
12599		positiveMotion = ( direction === "up" || direction === "left" ),
12600		distance = options.distance || element[ ref === "top" ? "outerHeight" : "outerWidth" ]( true ),
12601		animation = {};
12602
12603	$.effects.createPlaceholder( element );
12604
12605	startClip = element.cssClip();
12606	startRef = element.position()[ ref ];
12607
12608	// Define hide animation
12609	animation[ ref ] = ( positiveMotion ? -1 : 1 ) * distance + startRef;
12610	animation.clip = element.cssClip();
12611	animation.clip[ map[ direction ][ 1 ] ] = animation.clip[ map[ direction ][ 0 ] ];
12612
12613	// Reverse the animation if we're showing
12614	if ( mode === "show" ) {
12615		element.cssClip( animation.clip );
12616		element.css( ref, animation[ ref ] );
12617		animation.clip = startClip;
12618		animation[ ref ] = startRef;
12619	}
12620
12621	// Actually animate
12622	element.animate( animation, {
12623		queue: false,
12624		duration: options.duration,
12625		easing: options.easing,
12626		complete: done
12627	});
12628});
12629
12630}));
12631
12632//>>label: Transfer Effect
12633//>>group: Effects
12634//>>description: Displays a transfer effect from one element to another.
12635//>>docs: http://api.jqueryui.com/transfer-effect/
12636//>>demos: http://jqueryui.com/effect/
12637
12638(function( factory ) {
12639	if ( typeof define === "function" && define.amd ) {
12640
12641		// AMD. Register as an anonymous module.
12642		define([
12643			"jquery",
12644			"./effect"
12645		], factory );
12646	} else {
12647
12648		// Browser globals
12649		factory( jQuery );
12650	}
12651}(function( $ ) {
12652
12653if ( $.uiBackCompat !== false ) {
12654	return $.effects.define( "transfer", function( options, done ) {
12655		$( this ).transfer( options, done );
12656	});
12657}
12658
12659}));
12660
12661//>>label: Menu
12662//>>group: Widgets
12663//>>description: Creates nestable menus.
12664//>>docs: http://api.jqueryui.com/menu/
12665//>>demos: http://jqueryui.com/menu/
12666//>>css.structure: ../themes/base/core.css
12667//>>css.structure: ../themes/base/menu.css
12668//>>css.theme: ../themes/base/theme.css
12669
12670(function( factory ) {
12671	if ( typeof define === "function" && define.amd ) {
12672
12673		// AMD. Register as an anonymous module.
12674		define([
12675			"jquery",
12676			"./core",
12677			"./widget",
12678			"./position"
12679		], factory );
12680	} else {
12681
12682		// Browser globals
12683		factory( jQuery );
12684	}
12685}(function( $ ) {
12686
12687return $.widget( "ui.menu", {
12688	version: "@VERSION",
12689	defaultElement: "<ul>",
12690	delay: 300,
12691	options: {
12692		classes: {},
12693		icons: {
12694			submenu: "ui-icon-caret-1-e"
12695		},
12696		items: "> *",
12697		menus: "ul",
12698		position: {
12699			my: "left top",
12700			at: "right top"
12701		},
12702		role: "menu",
12703
12704		// callbacks
12705		blur: null,
12706		focus: null,
12707		select: null
12708	},
12709
12710	_create: function() {
12711		this.activeMenu = this.element;
12712
12713		// Flag used to prevent firing of the click handler
12714		// as the event bubbles up through nested menus
12715		this.mouseHandled = false;
12716		this.element
12717			.uniqueId()
12718			.attr({
12719				role: this.options.role,
12720				tabIndex: 0
12721			});
12722
12723		if ( this.options.disabled ) {
12724			this._addClass( null, "ui-state-disabled" );
12725			this.element.attr( "aria-disabled", "true" );
12726		}
12727
12728		this._addClass( "ui-menu", "ui-widget ui-widget-content" );
12729		this._on({
12730
12731			// Prevent focus from sticking to links inside menu after clicking
12732			// them (focus should always stay on UL during navigation).
12733			"mousedown .ui-menu-item": function( event ) {
12734				event.preventDefault();
12735			},
12736			"click .ui-menu-item": function( event ) {
12737				var target = $( event.target );
12738				if ( !this.mouseHandled && target.not( ".ui-state-disabled" ).length ) {
12739					this.select( event );
12740
12741					// Only set the mouseHandled flag if the event will bubble, see #9469.
12742					if ( !event.isPropagationStopped() ) {
12743						this.mouseHandled = true;
12744					}
12745
12746					// Open submenu on click
12747					if ( target.has( ".ui-menu" ).length ) {
12748						this.expand( event );
12749					} else if ( !this.element.is( ":focus" ) && $( $.ui.safeActiveElement( this.document[ 0 ] ) ).closest( ".ui-menu" ).length ) {
12750
12751						// Redirect focus to the menu
12752						this.element.trigger( "focus", [ true ] );
12753
12754						// If the active item is on the top level, let it stay active.
12755						// Otherwise, blur the active item since it is no longer visible.
12756						if ( this.active && this.active.parents( ".ui-menu" ).length === 1 ) {
12757							clearTimeout( this.timer );
12758						}
12759					}
12760				}
12761			},
12762			"mouseenter .ui-menu-item": function( event ) {
12763				// Ignore mouse events while typeahead is active, see #10458.
12764				// Prevents focusing the wrong item when typeahead causes a scroll while the mouse
12765				// is over an item in the menu
12766				if ( this.previousFilter ) {
12767					return;
12768				}
12769				var target = $( event.currentTarget );
12770				// Remove ui-state-active class from siblings of the newly focused menu item
12771				// to avoid a jump caused by adjacent elements both having a class with a border
12772				this._removeClass( target.siblings().children( ".ui-state-active" ),
12773					null, "ui-state-active" );
12774				this.focus( event, target );
12775			},
12776			mouseleave: "collapseAll",
12777			"mouseleave .ui-menu": "collapseAll",
12778			focus: function( event, keepActiveItem ) {
12779				// If there's already an active item, keep it active
12780				// If not, activate the first item
12781				var item = this.active || this.element.find( this.options.items ).eq( 0 );
12782
12783				if ( !keepActiveItem ) {
12784					this.focus( event, item );
12785				}
12786			},
12787			blur: function( event ) {
12788				this._delay(function() {
12789					if ( !$.contains( this.element[0], $.ui.safeActiveElement( this.document[0] ) ) ) {
12790						this.collapseAll( event );
12791					}
12792				});
12793			},
12794			keydown: "_keydown"
12795		});
12796
12797		this.refresh();
12798
vendor: 2,013 bytes, lines 12799-12882
12799		// Clicks outside of a menu collapse any open menus
12800		this._on( this.document, {
12801			click: function( event ) {
12802				if ( this._closeOnDocumentClick( event ) ) {
12803					this.collapseAll( event );
12804				}
12805
12806				// Reset the mouseHandled flag
12807				this.mouseHandled = false;
12808			}
12809		});
12810	},
12811
12812	_destroy: function() {
12813		var items = this.element.find( ".ui-menu-item" )
12814				.removeAttr( "role" )
12815				.removeAttr( "aria-disabled" ),
12816			submenus = items.children( ".ui-menu-item-wrapper" )
12817				.removeUniqueId()
12818				.removeAttr( "tabIndex" )
12819				.removeAttr( "role" )
12820				.removeAttr( "aria-haspopup" );
12821
12822		// Destroy (sub)menus
12823		this.element
12824			.removeAttr( "aria-activedescendant" )
12825			.find( ".ui-menu" ).addBack()
12826				.removeAttr( "role" )
12827				.removeAttr( "tabIndex" )
12828				.removeAttr( "aria-labelledby" )
12829				.removeAttr( "aria-expanded" )
12830				.removeAttr( "aria-hidden" )
12831				.removeAttr( "aria-disabled" )
12832				.removeUniqueId()
12833				.show();
12834
12835		submenus.children().each(function() {
12836			var elem = $( this );
12837			if ( elem.data( "ui-menu-submenu-caret" ) ) {
12838				elem.remove();
12839			}
12840		});
12841	},
12842
12843	_keydown: function( event ) {
12844		var match, prev, character, skip,
12845			preventDefault = true;
12846
12847		switch ( event.keyCode ) {
12848		case $.ui.keyCode.PAGE_UP:
12849			this.previousPage( event );
12850			break;
12851		case $.ui.keyCode.PAGE_DOWN:
12852			this.nextPage( event );
12853			break;
12854		case $.ui.keyCode.HOME:
12855			this._move( "first", "first", event );
12856			break;
12857		case $.ui.keyCode.END:
12858			this._move( "last", "last", event );
12859			break;
12860		case $.ui.keyCode.UP:
12861			this.previous( event );
12862			break;
12863		case $.ui.keyCode.DOWN:
12864			this.next( event );
12865			break;
12866		case $.ui.keyCode.LEFT:
12867			this.collapse( event );
12868			break;
12869		case $.ui.keyCode.RIGHT:
12870			if ( this.active && !this.active.is( ".ui-state-disabled" ) ) {
12871				this.expand( event );
12872			}
12873			break;
12874		case $.ui.keyCode.ENTER:
12875		case $.ui.keyCode.SPACE:
12876			this._activate( event );
12877			break;
12878		case $.ui.keyCode.ESCAPE:
12879			this.collapse( event );
12880			break;
12881		default:
12882			preventDefault = false;
vendor: 6,731 bytes, lines 12883-13124
12883			prev = this.previousFilter || "";
12884			character = String.fromCharCode( event.keyCode );
12885			skip = false;
12886
12887			clearTimeout( this.filterTimer );
12888
12889			if ( character === prev ) {
12890				skip = true;
12891			} else {
12892				character = prev + character;
12893			}
12894
12895			match = this._filterMenuItems( character );
12896			match = skip && match.index( this.active.next() ) !== -1 ?
12897				this.active.nextAll( ".ui-menu-item" ) :
12898				match;
12899
12900			// If no matches on the current filter, reset to the last character pressed
12901			// to move down the menu to the first item that starts with that character
12902			if ( !match.length ) {
12903				character = String.fromCharCode( event.keyCode );
12904				match = this._filterMenuItems( character );
12905			}
12906
12907			if ( match.length ) {
12908				this.focus( event, match );
12909				this.previousFilter = character;
12910				this.filterTimer = this._delay(function() {
12911					delete this.previousFilter;
12912				}, 1000 );
12913			} else {
12914				delete this.previousFilter;
12915			}
12916		}
12917
12918		if ( preventDefault ) {
12919			event.preventDefault();
12920		}
12921	},
12922
12923	_activate: function( event ) {
12924		if ( !this.active.is( ".ui-state-disabled" ) ) {
12925			if ( this.active.children( "[aria-haspopup='true']" ).length ) {
12926				this.expand( event );
12927			} else {
12928				this.select( event );
12929			}
12930		}
12931	},
12932
12933	refresh: function() {
12934		var menus, items, newSubmenus, newItems, newWrappers,
12935			that = this,
12936			icon = this.options.icons.submenu,
12937			submenus = this.element.find( this.options.menus );
12938
12939		this._toggleClass( "ui-menu-icons", null, !!this.element.find( ".ui-icon" ).length );
12940
12941		// Initialize nested menus
12942		newSubmenus = submenus.filter( ":not(.ui-menu)" )
12943			.hide()
12944			.attr({
12945				role: this.options.role,
12946				"aria-hidden": "true",
12947				"aria-expanded": "false"
12948			})
12949			.each(function() {
12950				var menu = $( this ),
12951					item = menu.prev(),
12952					submenuCaret = $( "<span>" ).data( "ui-menu-submenu-caret", true );
12953
12954				that._addClass( submenuCaret, "ui-menu-icon", "ui-icon " + icon );
12955				item
12956					.attr( "aria-haspopup", "true" )
12957					.prepend( submenuCaret );
12958				menu.attr( "aria-labelledby", item.attr( "id" ) );
12959			});
12960
12961		this._addClass( newSubmenus, "ui-menu", "ui-widget ui-widget-content ui-front" );
12962
12963		menus = submenus.add( this.element );
12964		items = menus.find( this.options.items );
12965
12966		// Initialize menu-items containing spaces and/or dashes only as dividers
12967		items.not( ".ui-menu-item" ).each(function() {
12968			var item = $( this );
12969			if ( that._isDivider( item ) ) {
12970				that._addClass( item, "ui-menu-divider", "ui-widget-content" );
12971			}
12972		});
12973
12974		// Don't refresh list items that are already adapted
12975		newItems = items.not( ".ui-menu-item, .ui-menu-divider" );
12976		newWrappers = newItems.children()
12977			.not( ".ui-menu" )
12978				.uniqueId()
12979				.attr({
12980					tabIndex: -1,
12981					role: this._itemRole()
12982				});
12983		this._addClass( newItems, "ui-menu-item" )
12984			._addClass( newWrappers, "ui-menu-item-wrapper" );
12985
12986		// Add aria-disabled attribute to any disabled menu item
12987		items.filter( ".ui-state-disabled" ).attr( "aria-disabled", "true" );
12988
12989		// If the active item has been removed, blur the menu
12990		if ( this.active && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
12991			this.blur();
12992		}
12993	},
12994
12995	_itemRole: function() {
12996		return {
12997			menu: "menuitem",
12998			listbox: "option"
12999		}[ this.options.role ];
13000	},
13001
13002	_setOption: function( key, value ) {
13003		if ( key === "icons" ) {
13004			var icons = this.element.find( ".ui-menu-icon" );
13005			this._removeClass( icons, null, this.options.icons.submenu )
13006				._addClass( icons, null, value.submenu );
13007		}
13008		if ( key === "disabled" ) {
13009			this.element.attr( "aria-disabled", value );
13010			this._toggleClass( null, "ui-state-disabled", !!value );
13011		}
13012		this._super( key, value );
13013	},
13014
13015	focus: function( event, item ) {
13016		var nested, focused, activeParent;
13017		this.blur( event, event && event.type === "focus" );
13018
13019		this._scrollIntoView( item );
13020
13021		this.active = item.first();
13022
13023		focused = this.active.children( ".ui-menu-item-wrapper" );
13024		this._addClass( focused, null, "ui-state-active" );
13025
13026		// Only update aria-activedescendant if there's a role
13027		// otherwise we assume focus is managed elsewhere
13028		if ( this.options.role ) {
13029			this.element.attr( "aria-activedescendant", focused.attr( "id" ) );
13030		}
13031
13032		// Highlight active parent menu item, if any
13033		activeParent = this.active
13034			.parent()
13035				.closest( ".ui-menu-item" )
13036					.children( ".ui-menu-item-wrapper" );
13037		this._addClass( activeParent, null, "ui-state-active" );
13038
13039		if ( event && event.type === "keydown" ) {
13040			this._close();
13041		} else {
13042			this.timer = this._delay(function() {
13043				this._close();
13044			}, this.delay );
13045		}
13046
13047		nested = item.children( ".ui-menu" );
13048		if ( nested.length && event && ( /^mouse/.test( event.type ) ) ) {
13049			this._startOpening(nested);
13050		}
13051		this.activeMenu = item.parent();
13052
13053		this._trigger( "focus", event, { item: item } );
13054	},
13055
13056	_scrollIntoView: function( item ) {
13057		var borderTop, paddingTop, offset, scroll, elementHeight, itemHeight;
13058		if ( this._hasScroll() ) {
13059			borderTop = parseFloat( $.css( this.activeMenu[0], "borderTopWidth" ) ) || 0;
13060			paddingTop = parseFloat( $.css( this.activeMenu[0], "paddingTop" ) ) || 0;
13061			offset = item.offset().top - this.activeMenu.offset().top - borderTop - paddingTop;
13062			scroll = this.activeMenu.scrollTop();
13063			elementHeight = this.activeMenu.height();
13064			itemHeight = item.outerHeight();
13065
13066			if ( offset < 0 ) {
13067				this.activeMenu.scrollTop( scroll + offset );
13068			} else if ( offset + itemHeight > elementHeight ) {
13069				this.activeMenu.scrollTop( scroll + offset - elementHeight + itemHeight );
13070			}
13071		}
13072	},
13073
13074	blur: function( event, fromFocus ) {
13075		if ( !fromFocus ) {
13076			clearTimeout( this.timer );
13077		}
13078
13079		if ( !this.active ) {
13080			return;
13081		}
13082
13083		this._removeClass( this.active.children( ".ui-menu-item-wrapper" ),
13084			null, "ui-state-active" );
13085		this.active = null;
13086
13087		this._trigger( "blur", event, { item: this.active } );
13088	},
13089
13090	_startOpening: function( submenu ) {
13091		clearTimeout( this.timer );
13092
13093		// Don't open if already open fixes a Firefox bug that caused a .5 pixel
13094		// shift in the submenu position when mousing over the caret icon
13095		if ( submenu.attr( "aria-hidden" ) !== "true" ) {
13096			return;
13097		}
13098
13099		this.timer = this._delay(function() {
13100			this._close();
13101			this._open( submenu );
13102		}, this.delay );
13103	},
13104
13105	_open: function( submenu ) {
13106		var position = $.extend({
13107			of: this.active
13108		}, this.options.position );
13109
13110		clearTimeout( this.timer );
13111		this.element.find( ".ui-menu" ).not( submenu.parents( ".ui-menu" ) )
13112			.hide()
13113			.attr( "aria-hidden", "true" );
13114
13115		submenu
13116			.show()
13117			.removeAttr( "aria-hidden" )
13118			.attr( "aria-expanded", "true" )
13119			.position( position );
13120	},
13121
13122	collapseAll: function( event, all ) {
13123		clearTimeout( this.timer );
13124		this.timer = this._delay(function() {
vendor: 7,946 bytes, lines 13125-13428
13125			// If we were passed an event, look for the submenu that contains the event
13126			var currentMenu = all ? this.element :
13127				$( event && event.target ).closest( this.element.find( ".ui-menu" ) );
13128
13129			// If we found no valid submenu ancestor, use the main menu to close all sub menus anyway
13130			if ( !currentMenu.length ) {
13131				currentMenu = this.element;
13132			}
13133
13134			this._close( currentMenu );
13135
13136			this.blur( event );
13137			this.activeMenu = currentMenu;
13138		}, this.delay );
13139	},
13140
13141	// With no arguments, closes the currently active menu - if nothing is active
13142	// it closes all menus.  If passed an argument, it will search for menus BELOW
13143	_close: function( startMenu ) {
13144		if ( !startMenu ) {
13145			startMenu = this.active ? this.active.parent() : this.element;
13146		}
13147
13148		var active = startMenu
13149			.find( ".ui-menu" )
13150				.hide()
13151				.attr( "aria-hidden", "true" )
13152				.attr( "aria-expanded", "false" )
13153			.end()
13154			.find( ".ui-state-active" ).not( ".ui-menu-item-wrapper" );
13155		this._removeClass( active, null, "ui-state-active" );
13156	},
13157
13158	_closeOnDocumentClick: function( event ) {
13159		return !$( event.target ).closest( ".ui-menu" ).length;
13160	},
13161
13162	_isDivider: function( item ) {
13163
13164		// Match hyphen, em dash, en dash
13165		return !/[^\-\u2014\u2013\s]/.test( item.text() );
13166	},
13167
13168	collapse: function( event ) {
13169		var newItem = this.active &&
13170			this.active.parent().closest( ".ui-menu-item", this.element );
13171		if ( newItem && newItem.length ) {
13172			this._close();
13173			this.focus( event, newItem );
13174		}
13175	},
13176
13177	expand: function( event ) {
13178		var newItem = this.active &&
13179			this.active
13180				.children( ".ui-menu " )
13181				.find( this.options.items )
13182				.first();
13183
13184		if ( newItem && newItem.length ) {
13185			this._open( newItem.parent() );
13186
13187			// Delay so Firefox will not hide activedescendant change in expanding submenu from AT
13188			this._delay(function() {
13189				this.focus( event, newItem );
13190			});
13191		}
13192	},
13193
13194	next: function( event ) {
13195		this._move( "next", "first", event );
13196	},
13197
13198	previous: function( event ) {
13199		this._move( "prev", "last", event );
13200	},
13201
13202	isFirstItem: function() {
13203		return this.active && !this.active.prevAll( ".ui-menu-item" ).length;
13204	},
13205
13206	isLastItem: function() {
13207		return this.active && !this.active.nextAll( ".ui-menu-item" ).length;
13208	},
13209
13210	_move: function( direction, filter, event ) {
13211		var next;
13212		if ( this.active ) {
13213			if ( direction === "first" || direction === "last" ) {
13214				next = this.active
13215					[ direction === "first" ? "prevAll" : "nextAll" ]( ".ui-menu-item" )
13216					.eq( -1 );
13217			} else {
13218				next = this.active
13219					[ direction + "All" ]( ".ui-menu-item" )
13220					.eq( 0 );
13221			}
13222		}
13223		if ( !next || !next.length || !this.active ) {
13224			next = this.activeMenu.find( this.options.items )[ filter ]();
13225		}
13226
13227		this.focus( event, next );
13228	},
13229
13230	nextPage: function( event ) {
13231		var item, base, height;
13232
13233		if ( !this.active ) {
13234			this.next( event );
13235			return;
13236		}
13237		if ( this.isLastItem() ) {
13238			return;
13239		}
13240		if ( this._hasScroll() ) {
13241			base = this.active.offset().top;
13242			height = this.element.height();
13243			this.active.nextAll( ".ui-menu-item" ).each(function() {
13244				item = $( this );
13245				return item.offset().top - base - height < 0;
13246			});
13247
13248			this.focus( event, item );
13249		} else {
13250			this.focus( event, this.activeMenu.find( this.options.items )
13251				[ !this.active ? "first" : "last" ]() );
13252		}
13253	},
13254
13255	previousPage: function( event ) {
13256		var item, base, height;
13257		if ( !this.active ) {
13258			this.next( event );
13259			return;
13260		}
13261		if ( this.isFirstItem() ) {
13262			return;
13263		}
13264		if ( this._hasScroll() ) {
13265			base = this.active.offset().top;
13266			height = this.element.height();
13267			this.active.prevAll( ".ui-menu-item" ).each(function() {
13268				item = $( this );
13269				return item.offset().top - base + height > 0;
13270			});
13271
13272			this.focus( event, item );
13273		} else {
13274			this.focus( event, this.activeMenu.find( this.options.items ).first() );
13275		}
13276	},
13277
13278	_hasScroll: function() {
13279		return this.element.outerHeight() < this.element.prop( "scrollHeight" );
13280	},
13281
13282	select: function( event ) {
13283		// TODO: It should never be possible to not have an active item at this
13284		// point, but the tests don't trigger mouseenter before click.
13285		this.active = this.active || $( event.target ).closest( ".ui-menu-item" );
13286		var ui = { item: this.active };
13287		if ( !this.active.has( ".ui-menu" ).length ) {
13288			this.collapseAll( event, true );
13289		}
13290		this._trigger( "select", event, ui );
13291	},
13292
13293	_filterMenuItems: function( character ) {
13294		var escapedCharacter = character.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" ),
13295			regex = new RegExp( "^" + escapedCharacter, "i" );
13296
13297		return this.activeMenu
13298			.find( this.options.items )
13299
13300			// Only match on items, not dividers or other content (#10571)
13301			.filter( ".ui-menu-item" )
13302			.filter(function() {
13303				return regex.test(
13304					$.trim( $( this ).children( ".ui-menu-item-wrapper" ).text() ) );
13305			});
13306	}
13307});
13308
13309}));
13310
13311//>>label: Position
13312//>>group: UI Core
13313//>>description: Positions elements relative to other elements.
13314//>>docs: http://api.jqueryui.com/position/
13315//>>demos: http://jqueryui.com/position/
13316
13317(function( factory ) {
13318	if ( typeof define === "function" && define.amd ) {
13319
13320		// AMD. Register as an anonymous module.
13321		define( [ "jquery" ], factory );
13322	} else {
13323
13324		// Browser globals
13325		factory( jQuery );
13326	}
13327}(function( $ ) {
13328(function() {
13329
13330$.ui = $.ui || {};
13331
13332var cachedScrollbarWidth, supportsOffsetFractions,
13333	max = Math.max,
13334	abs = Math.abs,
13335	round = Math.round,
13336	rhorizontal = /left|center|right/,
13337	rvertical = /top|center|bottom/,
13338	roffset = /[\+\-]\d+(\.[\d]+)?%?/,
13339	rposition = /^\w+/,
13340	rpercent = /%$/,
13341	_position = $.fn.position;
13342
13343function getOffsets( offsets, width, height ) {
13344	return [
13345		parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ),
13346		parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 )
13347	];
13348}
13349
13350function parseCss( element, property ) {
13351	return parseInt( $.css( element, property ), 10 ) || 0;
13352}
13353
13354function getDimensions( elem ) {
13355	var raw = elem[0];
13356	if ( raw.nodeType === 9 ) {
13357		return {
13358			width: elem.width(),
13359			height: elem.height(),
13360			offset: { top: 0, left: 0 }
13361		};
13362	}
13363	if ( $.isWindow( raw ) ) {
13364		return {
13365			width: elem.width(),
13366			height: elem.height(),
13367			offset: { top: elem.scrollTop(), left: elem.scrollLeft() }
13368		};
13369	}
13370	if ( raw.preventDefault ) {
13371		return {
13372			width: 0,
13373			height: 0,
13374			offset: { top: raw.pageY, left: raw.pageX }
13375		};
13376	}
13377	return {
13378		width: elem.outerWidth(),
13379		height: elem.outerHeight(),
13380		offset: elem.offset()
13381	};
13382}
13383
13384$.position = {
13385	scrollbarWidth: function() {
13386		if ( cachedScrollbarWidth !== undefined ) {
13387			return cachedScrollbarWidth;
13388		}
13389		var w1, w2,
13390			div = $( "<div style='display:block;position:absolute;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>" ),
13391			innerDiv = div.children()[0];
13392
13393		$( "body" ).append( div );
13394		w1 = innerDiv.offsetWidth;
13395		div.css( "overflow", "scroll" );
13396
13397		w2 = innerDiv.offsetWidth;
13398
13399		if ( w1 === w2 ) {
13400			w2 = div[0].clientWidth;
13401		}
13402
13403		div.remove();
13404
13405		return (cachedScrollbarWidth = w1 - w2);
13406	},
13407	getScrollInfo: function( within ) {
13408		var overflowX = within.isWindow || within.isDocument ? "" :
13409				within.element.css( "overflow-x" ),
13410			overflowY = within.isWindow || within.isDocument ? "" :
13411				within.element.css( "overflow-y" ),
13412			hasOverflowX = overflowX === "scroll" ||
13413				( overflowX === "auto" && within.width < within.element[0].scrollWidth ),
13414			hasOverflowY = overflowY === "scroll" ||
13415				( overflowY === "auto" && within.height < within.element[0].scrollHeight );
13416		return {
13417			width: hasOverflowY ? $.position.scrollbarWidth() : 0,
13418			height: hasOverflowX ? $.position.scrollbarWidth() : 0
13419		};
13420	},
13421	getWithinInfo: function( element ) {
13422		var withinElement = $( element || window ),
13423			isWindow = $.isWindow( withinElement[0] ),
13424			isDocument = !!withinElement[ 0 ] && withinElement[ 0 ].nodeType === 9;
13425		return {
13426			element: withinElement,
13427			isWindow: isWindow,
13428			isDocument: isDocument,
vendor: 12,789 bytes, lines 13429-13824
13429			offset: withinElement.offset() || { left: 0, top: 0 },
13430			scrollLeft: withinElement.scrollLeft(),
13431			scrollTop: withinElement.scrollTop(),
13432			width: withinElement.outerWidth(),
13433			height: withinElement.outerHeight()
13434		};
13435	}
13436};
13437
13438$.fn.position = function( options ) {
13439	if ( !options || !options.of ) {
13440		return _position.apply( this, arguments );
13441	}
13442
13443	// make a copy, we don't want to modify arguments
13444	options = $.extend( {}, options );
13445
13446	var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions,
13447		target = $( options.of ),
13448		within = $.position.getWithinInfo( options.within ),
13449		scrollInfo = $.position.getScrollInfo( within ),
13450		collision = ( options.collision || "flip" ).split( " " ),
13451		offsets = {};
13452
13453	dimensions = getDimensions( target );
13454	if ( target[0].preventDefault ) {
13455		// force left top to allow flipping
13456		options.at = "left top";
13457	}
13458	targetWidth = dimensions.width;
13459	targetHeight = dimensions.height;
13460	targetOffset = dimensions.offset;
13461	// clone to reuse original targetOffset later
13462	basePosition = $.extend( {}, targetOffset );
13463
13464	// force my and at to have valid horizontal and vertical positions
13465	// if a value is missing or invalid, it will be converted to center
13466	$.each( [ "my", "at" ], function() {
13467		var pos = ( options[ this ] || "" ).split( " " ),
13468			horizontalOffset,
13469			verticalOffset;
13470
13471		if ( pos.length === 1) {
13472			pos = rhorizontal.test( pos[ 0 ] ) ?
13473				pos.concat( [ "center" ] ) :
13474				rvertical.test( pos[ 0 ] ) ?
13475					[ "center" ].concat( pos ) :
13476					[ "center", "center" ];
13477		}
13478		pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center";
13479		pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center";
13480
13481		// calculate offsets
13482		horizontalOffset = roffset.exec( pos[ 0 ] );
13483		verticalOffset = roffset.exec( pos[ 1 ] );
13484		offsets[ this ] = [
13485			horizontalOffset ? horizontalOffset[ 0 ] : 0,
13486			verticalOffset ? verticalOffset[ 0 ] : 0
13487		];
13488
13489		// reduce to just the positions without the offsets
13490		options[ this ] = [
13491			rposition.exec( pos[ 0 ] )[ 0 ],
13492			rposition.exec( pos[ 1 ] )[ 0 ]
13493		];
13494	});
13495
13496	// normalize collision option
13497	if ( collision.length === 1 ) {
13498		collision[ 1 ] = collision[ 0 ];
13499	}
13500
13501	if ( options.at[ 0 ] === "right" ) {
13502		basePosition.left += targetWidth;
13503	} else if ( options.at[ 0 ] === "center" ) {
13504		basePosition.left += targetWidth / 2;
13505	}
13506
13507	if ( options.at[ 1 ] === "bottom" ) {
13508		basePosition.top += targetHeight;
13509	} else if ( options.at[ 1 ] === "center" ) {
13510		basePosition.top += targetHeight / 2;
13511	}
13512
13513	atOffset = getOffsets( offsets.at, targetWidth, targetHeight );
13514	basePosition.left += atOffset[ 0 ];
13515	basePosition.top += atOffset[ 1 ];
13516
13517	return this.each(function() {
13518		var collisionPosition, using,
13519			elem = $( this ),
13520			elemWidth = elem.outerWidth(),
13521			elemHeight = elem.outerHeight(),
13522			marginLeft = parseCss( this, "marginLeft" ),
13523			marginTop = parseCss( this, "marginTop" ),
13524			collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) + scrollInfo.width,
13525			collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) + scrollInfo.height,
13526			position = $.extend( {}, basePosition ),
13527			myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() );
13528
13529		if ( options.my[ 0 ] === "right" ) {
13530			position.left -= elemWidth;
13531		} else if ( options.my[ 0 ] === "center" ) {
13532			position.left -= elemWidth / 2;
13533		}
13534
13535		if ( options.my[ 1 ] === "bottom" ) {
13536			position.top -= elemHeight;
13537		} else if ( options.my[ 1 ] === "center" ) {
13538			position.top -= elemHeight / 2;
13539		}
13540
13541		position.left += myOffset[ 0 ];
13542		position.top += myOffset[ 1 ];
13543
13544		// if the browser doesn't support fractions, then round for consistent results
13545		if ( !supportsOffsetFractions ) {
13546			position.left = round( position.left );
13547			position.top = round( position.top );
13548		}
13549
13550		collisionPosition = {
13551			marginLeft: marginLeft,
13552			marginTop: marginTop
13553		};
13554
13555		$.each( [ "left", "top" ], function( i, dir ) {
13556			if ( $.ui.position[ collision[ i ] ] ) {
13557				$.ui.position[ collision[ i ] ][ dir ]( position, {
13558					targetWidth: targetWidth,
13559					targetHeight: targetHeight,
13560					elemWidth: elemWidth,
13561					elemHeight: elemHeight,
13562					collisionPosition: collisionPosition,
13563					collisionWidth: collisionWidth,
13564					collisionHeight: collisionHeight,
13565					offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ],
13566					my: options.my,
13567					at: options.at,
13568					within: within,
13569					elem: elem
13570				});
13571			}
13572		});
13573
13574		if ( options.using ) {
13575			// adds feedback as second argument to using callback, if present
13576			using = function( props ) {
13577				var left = targetOffset.left - position.left,
13578					right = left + targetWidth - elemWidth,
13579					top = targetOffset.top - position.top,
13580					bottom = top + targetHeight - elemHeight,
13581					feedback = {
13582						target: {
13583							element: target,
13584							left: targetOffset.left,
13585							top: targetOffset.top,
13586							width: targetWidth,
13587							height: targetHeight
13588						},
13589						element: {
13590							element: elem,
13591							left: position.left,
13592							top: position.top,
13593							width: elemWidth,
13594							height: elemHeight
13595						},
13596						horizontal: right < 0 ? "left" : left > 0 ? "right" : "center",
13597						vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle"
13598					};
13599				if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) {
13600					feedback.horizontal = "center";
13601				}
13602				if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) {
13603					feedback.vertical = "middle";
13604				}
13605				if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) {
13606					feedback.important = "horizontal";
13607				} else {
13608					feedback.important = "vertical";
13609				}
13610				options.using.call( this, props, feedback );
13611			};
13612		}
13613
13614		elem.offset( $.extend( position, { using: using } ) );
13615	});
13616};
13617
13618$.ui.position = {
13619	fit: {
13620		left: function( position, data ) {
13621			var within = data.within,
13622				withinOffset = within.isWindow ? within.scrollLeft : within.offset.left,
13623				outerWidth = within.width,
13624				collisionPosLeft = position.left - data.collisionPosition.marginLeft,
13625				overLeft = withinOffset - collisionPosLeft,
13626				overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset,
13627				newOverRight;
13628
13629			// element is wider than within
13630			if ( data.collisionWidth > outerWidth ) {
13631				// element is initially over the left side of within
13632				if ( overLeft > 0 && overRight <= 0 ) {
13633					newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - withinOffset;
13634					position.left += overLeft - newOverRight;
13635				// element is initially over right side of within
13636				} else if ( overRight > 0 && overLeft <= 0 ) {
13637					position.left = withinOffset;
13638				// element is initially over both left and right sides of within
13639				} else {
13640					if ( overLeft > overRight ) {
13641						position.left = withinOffset + outerWidth - data.collisionWidth;
13642					} else {
13643						position.left = withinOffset;
13644					}
13645				}
13646			// too far left -> align with left edge
13647			} else if ( overLeft > 0 ) {
13648				position.left += overLeft;
13649			// too far right -> align with right edge
13650			} else if ( overRight > 0 ) {
13651				position.left -= overRight;
13652			// adjust based on position and margin
13653			} else {
13654				position.left = max( position.left - collisionPosLeft, position.left );
13655			}
13656		},
13657		top: function( position, data ) {
13658			var within = data.within,
13659				withinOffset = within.isWindow ? within.scrollTop : within.offset.top,
13660				outerHeight = data.within.height,
13661				collisionPosTop = position.top - data.collisionPosition.marginTop,
13662				overTop = withinOffset - collisionPosTop,
13663				overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset,
13664				newOverBottom;
13665
13666			// element is taller than within
13667			if ( data.collisionHeight > outerHeight ) {
13668				// element is initially over the top of within
13669				if ( overTop > 0 && overBottom <= 0 ) {
13670					newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - withinOffset;
13671					position.top += overTop - newOverBottom;
13672				// element is initially over bottom of within
13673				} else if ( overBottom > 0 && overTop <= 0 ) {
13674					position.top = withinOffset;
13675				// element is initially over both top and bottom of within
13676				} else {
13677					if ( overTop > overBottom ) {
13678						position.top = withinOffset + outerHeight - data.collisionHeight;
13679					} else {
13680						position.top = withinOffset;
13681					}
13682				}
13683			// too far up -> align with top
13684			} else if ( overTop > 0 ) {
13685				position.top += overTop;
13686			// too far down -> align with bottom edge
13687			} else if ( overBottom > 0 ) {
13688				position.top -= overBottom;
13689			// adjust based on position and margin
13690			} else {
13691				position.top = max( position.top - collisionPosTop, position.top );
13692			}
13693		}
13694	},
13695	flip: {
13696		left: function( position, data ) {
13697			var within = data.within,
13698				withinOffset = within.offset.left + within.scrollLeft,
13699				outerWidth = within.width,
13700				offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left,
13701				collisionPosLeft = position.left - data.collisionPosition.marginLeft,
13702				overLeft = collisionPosLeft - offsetLeft,
13703				overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft,
13704				myOffset = data.my[ 0 ] === "left" ?
13705					-data.elemWidth :
13706					data.my[ 0 ] === "right" ?
13707						data.elemWidth :
13708						0,
13709				atOffset = data.at[ 0 ] === "left" ?
13710					data.targetWidth :
13711					data.at[ 0 ] === "right" ?
13712						-data.targetWidth :
13713						0,
13714				offset = -2 * data.offset[ 0 ],
13715				newOverRight,
13716				newOverLeft;
13717
13718			if ( overLeft < 0 ) {
13719				newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - outerWidth - withinOffset;
13720				if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) {
13721					position.left += myOffset + atOffset + offset;
13722				}
13723			} else if ( overRight > 0 ) {
13724				newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + atOffset + offset - offsetLeft;
13725				if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) {
13726					position.left += myOffset + atOffset + offset;
13727				}
13728			}
13729		},
13730		top: function( position, data ) {
13731			var within = data.within,
13732				withinOffset = within.offset.top + within.scrollTop,
13733				outerHeight = within.height,
13734				offsetTop = within.isWindow ? within.scrollTop : within.offset.top,
13735				collisionPosTop = position.top - data.collisionPosition.marginTop,
13736				overTop = collisionPosTop - offsetTop,
13737				overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop,
13738				top = data.my[ 1 ] === "top",
13739				myOffset = top ?
13740					-data.elemHeight :
13741					data.my[ 1 ] === "bottom" ?
13742						data.elemHeight :
13743						0,
13744				atOffset = data.at[ 1 ] === "top" ?
13745					data.targetHeight :
13746					data.at[ 1 ] === "bottom" ?
13747						-data.targetHeight :
13748						0,
13749				offset = -2 * data.offset[ 1 ],
13750				newOverTop,
13751				newOverBottom;
13752			if ( overTop < 0 ) {
13753				newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - outerHeight - withinOffset;
13754				if ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) {
13755					position.top += myOffset + atOffset + offset;
13756				}
13757			} else if ( overBottom > 0 ) {
13758				newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + offset - offsetTop;
13759				if ( newOverTop > 0 || abs( newOverTop ) < overBottom ) {
13760					position.top += myOffset + atOffset + offset;
13761				}
13762			}
13763		}
13764	},
13765	flipfit: {
13766		left: function() {
13767			$.ui.position.flip.left.apply( this, arguments );
13768			$.ui.position.fit.left.apply( this, arguments );
13769		},
13770		top: function() {
13771			$.ui.position.flip.top.apply( this, arguments );
13772			$.ui.position.fit.top.apply( this, arguments );
13773		}
13774	}
13775};
13776
13777// fraction support test
13778(function() {
13779	var testElement, testElementParent, testElementStyle, offsetLeft, i,
13780		body = document.getElementsByTagName( "body" )[ 0 ],
13781		div = document.createElement( "div" );
13782
13783	//Create a "fake body" for testing based on method used in jQuery.support
13784	testElement = document.createElement( body ? "div" : "body" );
13785	testElementStyle = {
13786		visibility: "hidden",
13787		width: 0,
13788		height: 0,
13789		border: 0,
13790		margin: 0,
13791		background: "none"
13792	};
13793	if ( body ) {
13794		$.extend( testElementStyle, {
13795			position: "absolute",
13796			left: "-1000px",
13797			top: "-1000px"
13798		});
13799	}
13800	for ( i in testElementStyle ) {
13801		testElement.style[ i ] = testElementStyle[ i ];
13802	}
13803	testElement.appendChild( div );
13804	testElementParent = body || document.documentElement;
13805	testElementParent.insertBefore( testElement, testElementParent.firstChild );
13806
13807	div.style.cssText = "position: absolute; left: 10.7432222px;";
13808
13809	offsetLeft = $( div ).offset().left;
13810	supportsOffsetFractions = offsetLeft > 10 && offsetLeft < 11;
13811
13812	testElement.innerHTML = "";
13813	testElementParent.removeChild( testElement );
13814})();
13815
13816})();
13817
13818return $.ui.position;
13819
13820}));
13821
13822//>>label: Progressbar
13823//>>group: Widgets
13824//>>
vendor: 5,428 bytes, lines 13824-14053
13824description: Displays a status indicator for loading state, standard percentage, and other progress indicators.
13825//>>docs: http://api.jqueryui.com/progressbar/
13826//>>demos: http://jqueryui.com/progressbar/
13827//>>css.structure: ../themes/base/core.css
13828//>>css.structure: ../themes/base/progressbar.css
13829//>>css.theme: ../themes/base/theme.css
13830
13831(function( factory ) {
13832	if ( typeof define === "function" && define.amd ) {
13833
13834		// AMD. Register as an anonymous module.
13835		define([
13836			"jquery",
13837			"./core",
13838			"./widget"
13839		], factory );
13840	} else {
13841
13842		// Browser globals
13843		factory( jQuery );
13844	}
13845}(function( $ ) {
13846
13847return $.widget( "ui.progressbar", {
13848	version: "@VERSION",
13849	options: {
13850		classes: {
13851			"ui-progressbar": "ui-corner-all",
13852			"ui-progressbar-value": "ui-corner-left",
13853			"ui-progressbar-complete": "ui-corner-right"
13854		},
13855		max: 100,
13856		value: 0,
13857
13858		change: null,
13859		complete: null
13860	},
13861
13862	min: 0,
13863
13864	_create: function() {
13865
13866		// Constrain initial value
13867		this.oldValue = this.options.value = this._constrainedValue();
13868
13869		this.element.attr({
13870
13871			// Only set static values; aria-valuenow and aria-valuemax are
13872			// set inside _refreshValue()
13873			role: "progressbar",
13874			"aria-valuemin": this.min
13875		});
13876		this._addClass( "ui-progressbar", "ui-widget ui-widget-content" );
13877
13878		this.valueDiv = $( "<div>" ).appendTo( this.element );
13879		this._addClass( this.valueDiv, "ui-progressbar-value", "ui-widget-header" );
13880		this._refreshValue();
13881	},
13882
13883	_destroy: function() {
13884		this.element
13885			.removeAttr( "role" )
13886			.removeAttr( "aria-valuemin" )
13887			.removeAttr( "aria-valuemax" )
13888			.removeAttr( "aria-valuenow" );
13889
13890		this.valueDiv.remove();
13891	},
13892
13893	value: function( newValue ) {
13894		if ( newValue === undefined ) {
13895			return this.options.value;
13896		}
13897
13898		this.options.value = this._constrainedValue( newValue );
13899		this._refreshValue();
13900	},
13901
13902	_constrainedValue: function( newValue ) {
13903		if ( newValue === undefined ) {
13904			newValue = this.options.value;
13905		}
13906
13907		this.indeterminate = newValue === false;
13908
13909		// sanitize value
13910		if ( typeof newValue !== "number" ) {
13911			newValue = 0;
13912		}
13913
13914		return this.indeterminate ? false :
13915			Math.min( this.options.max, Math.max( this.min, newValue ) );
13916	},
13917
13918	_setOptions: function( options ) {
13919		// Ensure "value" option is set after other values (like max)
13920		var value = options.value;
13921		delete options.value;
13922
13923		this._super( options );
13924
13925		this.options.value = this._constrainedValue( value );
13926		this._refreshValue();
13927	},
13928
13929	_setOption: function( key, value ) {
13930		if ( key === "max" ) {
13931			// Don't allow a max less than min
13932			value = Math.max( this.min, value );
13933		}
13934		if ( key === "disabled" ) {
13935			this.element.attr( "aria-disabled", value );
13936			this._toggleClass( null, "ui-state-disabled", !!value );
13937		}
13938		this._super( key, value );
13939	},
13940
13941	_percentage: function() {
13942		return this.indeterminate ? 100 : 100 * ( this.options.value - this.min ) / ( this.options.max - this.min );
13943	},
13944
13945	_refreshValue: function() {
13946		var value = this.options.value,
13947			percentage = this._percentage();
13948
13949		this.valueDiv
13950			.toggle( this.indeterminate || value > this.min )
13951			.width( percentage.toFixed(0) + "%" );
13952
13953		this
13954			._toggleClass( this.valueDiv, "ui-progressbar-complete", null,
13955				value === this.options.max )
13956			._toggleClass( "ui-progressbar-indeterminate", null, this.indeterminate );
13957
13958		if ( this.indeterminate ) {
13959			this.element.removeAttr( "aria-valuenow" );
13960			if ( !this.overlayDiv ) {
13961				this.overlayDiv = $( "<div>" ).appendTo( this.valueDiv );
13962				this._addClass( this.overlayDiv, "ui-progressbar-overlay" );
13963			}
13964		} else {
13965			this.element.attr({
13966				"aria-valuemax": this.options.max,
13967				"aria-valuenow": value
13968			});
13969			if ( this.overlayDiv ) {
13970				this.overlayDiv.remove();
13971				this.overlayDiv = null;
13972			}
13973		}
13974
13975		if ( this.oldValue !== value ) {
13976			this.oldValue = value;
13977			this._trigger( "change" );
13978		}
13979		if ( value === this.options.max ) {
13980			this._trigger( "complete" );
13981		}
13982	}
13983});
13984
13985}));
13986
13987//>>label: Selectmenu
13988//>>group: Widgets
13989//>>description: Duplicates and extends the functionality of a native HTML select element, allowing it to be customizable in behavior and appearance far beyond the limitations of a native select.
13990//>>docs: http://api.jqueryui.com/selectmenu/
13991//>>demos: http://jqueryui.com/selectmenu/
13992//>>css.structure: ../themes/base/core.css
13993//>>css.structure: ../themes/base/selectmenu.css
13994//>>css.theme: ../themes/base/theme.css
13995
13996(function( factory ) {
13997	if ( typeof define === "function" && define.amd ) {
13998
13999		// AMD. Register as an anonymous module.
14000		define([
14001			"jquery",
14002			"./core",
14003			"./widget",
14004			"./position",
14005			"./menu"
14006		], factory );
14007	} else {
14008
14009		// Browser globals
14010		factory( jQuery );
14011	}
14012}(function( $ ) {
14013
14014return $.widget( "ui.selectmenu", {
14015	version: "@VERSION",
14016	defaultElement: "<select>",
14017	options: {
14018		appendTo: null,
14019		classes: {
14020			"ui-selectmenu-button-open": "ui-corner-top",
14021			"ui-selectmenu-button-closed": "ui-corner-all"
14022		},
14023		disabled: null,
14024		icons: {
14025			button: "ui-icon-triangle-1-s"
14026		},
14027		position: {
14028			my: "left top",
14029			at: "left bottom",
14030			collision: "none"
14031		},
14032		width: false,
14033
14034		// callbacks
14035		change: null,
14036		close: null,
14037		focus: null,
14038		open: null,
14039		select: null
14040	},
14041
14042	_create: function() {
14043		var selectmenuId = this.element.uniqueId().attr( "id" );
14044		this.ids = {
14045			element: selectmenuId,
14046			button: selectmenuId + "-button",
14047			menu: selectmenuId + "-menu"
14048		};
14049
14050		this._drawButton();
14051		this._drawMenu();
14052
14053		this._rendered = false;
vendor: 5,322 bytes, lines 14054-14267
14054		this.menuItems = $();
14055
14056		if ( this.options.disabled ) {
14057			this.disable();
14058		}
14059	},
14060
14061	_drawButton: function() {
14062		var icon,
14063			that = this,
14064			item = this._parseOption(
14065				this.element.find( "option:selected" ),
14066				this.element[ 0 ].selectedIndex
14067			);
14068
14069		// Associate existing label with the new button
14070		this.label = $( "label[for='" + this.ids.element + "']" ).attr( "for", this.ids.button );
14071		this._on( this.label, {
14072			click: function( event ) {
14073				this.button.focus();
14074				event.preventDefault();
14075			}
14076		});
14077
14078		// Hide original select element
14079		this.element.hide();
14080
14081		// Create button
14082		this.button = $( "<span>", {
14083			tabindex: this.options.disabled ? -1 : 0,
14084			id: this.ids.button,
14085			role: "combobox",
14086			"aria-expanded": "false",
14087			"aria-autocomplete": "list",
14088			"aria-owns": this.ids.menu,
14089			"aria-haspopup": "true",
14090			title: this.element.attr( "title" )
14091		})
14092			.insertAfter( this.element );
14093
14094		this._addClass( this.button, "ui-selectmenu-button ui-selectmenu-button-closed",
14095			"ui-widget ui-state-default" );
14096
14097		icon = $( "<span>" ).prependTo( this.button );
14098		this._addClass( icon, null, "ui-icon " + this.options.icons.button );
14099
14100		this.buttonItem = this._renderButtonItem( item )
14101			.appendTo( this.button );
14102
14103		if ( this.options.width !== false ) {
14104			this._resizeButton();
14105		}
14106
14107		this._on( this.button, this._buttonEvents );
14108		this.button.one( "focusin", function() {
14109
14110			// Delay rendering the menu items until the button receives focus.
14111			// The menu may have already been rendered via a programmatic open.
14112			if ( !that._rendered ) {
14113				that._refreshMenu();
14114			}
14115		});
14116		this._hoverable( this.button );
14117		this._focusable( this.button );
14118	},
14119
14120	_drawMenu: function() {
14121		var that = this;
14122
14123		// Create menu
14124		this.menu = $( "<ul>", {
14125			"aria-hidden": "true",
14126			"aria-labelledby": this.ids.button,
14127			id: this.ids.menu
14128		});
14129
14130		// Wrap menu
14131		this.menuWrap = $( "<div>" ).append( this.menu );
14132		this._addClass( this.menuWrap, "ui-selectmenu-menu", "ui-front" );
14133		this.menuWrap.appendTo( this._appendTo() );
14134
14135		// Initialize menu widget
14136		this.menuInstance = this.menu
14137			.menu({
14138				classes: {
14139					"ui-menu": "ui-corner-bottom"
14140				},
14141				role: "listbox",
14142				select: function( event, ui ) {
14143					event.preventDefault();
14144
14145					// support: IE8
14146					// If the item was selected via a click, the text selection
14147					// will be destroyed in IE
14148					that._setSelection();
14149
14150					that._select( ui.item.data( "ui-selectmenu-item" ), event );
14151				},
14152				focus: function( event, ui ) {
14153					var item = ui.item.data( "ui-selectmenu-item" );
14154
14155					// Prevent inital focus from firing and check if its a newly focused item
14156					if ( that.focusIndex != null && item.index !== that.focusIndex ) {
14157						that._trigger( "focus", event, { item: item } );
14158						if ( !that.isOpen ) {
14159							that._select( item, event );
14160						}
14161					}
14162					that.focusIndex = item.index;
14163
14164					that.button.attr( "aria-activedescendant",
14165						that.menuItems.eq( item.index ).attr( "id" ) );
14166				}
14167			})
14168			.menu( "instance" );
14169
14170		// Don't close the menu on mouseleave
14171		this.menuInstance._off( this.menu, "mouseleave" );
14172
14173		// Cancel the menu's collapseAll on document click
14174		this.menuInstance._closeOnDocumentClick = function() {
14175			return false;
14176		};
14177
14178		// Selects often contain empty items, but never contain dividers
14179		this.menuInstance._isDivider = function() {
14180			return false;
14181		};
14182	},
14183
14184	refresh: function() {
14185		this._refreshMenu();
14186		this.buttonItem.replaceWith(
14187			this.buttonItem = this._renderButtonItem(
14188
14189				// Fall back to an empty object in case there are no options
14190				this._getSelectedItem().data( "ui-selectmenu-item" ) || {}
14191			)
14192		);
14193		if ( this.options.width === null ) {
14194			this._resizeButton();
14195		}
14196	},
14197
14198	_refreshMenu: function() {
14199		var item,
14200			options = this.element.find( "option" );
14201
14202		this.menu.empty();
14203
14204		this._parseOptions( options );
14205		this._renderMenu( this.menu, this.items );
14206
14207		this.menuInstance.refresh();
14208		this.menuItems = this.menu.find( "li" )
14209			.not( ".ui-selectmenu-optgroup" )
14210				.find( ".ui-menu-item-wrapper" );
14211
14212		this._rendered = true;
14213
14214		if ( !options.length ) {
14215			return;
14216		}
14217
14218		item = this._getSelectedItem();
14219
14220		// Update the menu to have the correct item focused
14221		this.menuInstance.focus( null, item );
14222		this._setAria( item.data( "ui-selectmenu-item" ) );
14223
14224		// Set disabled state
14225		this._setOption( "disabled", this.element.prop( "disabled" ) );
14226	},
14227
14228	open: function( event ) {
14229		if ( this.options.disabled ) {
14230			return;
14231		}
14232
14233		// If this is the first time the menu is being opened, render the items
14234		if ( !this._rendered ) {
14235			this._refreshMenu();
14236		} else {
14237
14238			// Menu clears focus on close, reset focus to selected item
14239			this._removeClass( this.menu.find( ".ui-state-active" ), null, "ui-state-active" );
14240			this.menuInstance.focus( null, this._getSelectedItem() );
14241		}
14242
14243		// If there are no options, don't open the menu
14244		if ( !this.menuItems.length ) {
14245			return;
14246		}
14247
14248		this.isOpen = true;
14249		this._toggleAttr();
14250		this._resizeMenu();
14251		this._position();
14252
14253		this._on( this.document, this._documentClick );
14254
14255		this._trigger( "open", event );
14256	},
14257
14258	_position: function() {
14259		this.menuWrap.position( $.extend( { of: this.button }, this.options.position ) );
14260	},
14261
14262	close: function( event ) {
14263		if ( !this.isOpen ) {
14264			return;
14265		}
14266
14267		this.isOpen = false;
vendor: 10,337 bytes, lines 14268-14718
14268		this._toggleAttr();
14269
14270		this.range = null;
14271		this._off( this.document );
14272
14273		this._trigger( "close", event );
14274	},
14275
14276	widget: function() {
14277		return this.button;
14278	},
14279
14280	menuWidget: function() {
14281		return this.menu;
14282	},
14283
14284	_renderButtonItem: function( item ) {
14285		var buttonItem = $( "<span>" );
14286
14287		this._setText( buttonItem, item.label );
14288		this._addClass( buttonItem, "ui-selectmenu-text" );
14289
14290		return buttonItem;
14291	},
14292
14293	_renderMenu: function( ul, items ) {
14294		var that = this,
14295			currentOptgroup = "";
14296
14297		$.each( items, function( index, item ) {
14298			var li;
14299
14300			if ( item.optgroup !== currentOptgroup ) {
14301				li = $( "<li>", {
14302					text: item.optgroup
14303				});
14304				that._addClass( li, "ui-selectmenu-optgroup", "ui-menu-divider" +
14305					( item.element.parent( "optgroup" ).prop( "disabled" ) ?
14306						" ui-state-disabled" :
14307						"" ) );
14308
14309				li.appendTo( ul );
14310
14311				currentOptgroup = item.optgroup;
14312			}
14313
14314			that._renderItemData( ul, item );
14315		});
14316	},
14317
14318	_renderItemData: function( ul, item ) {
14319		return this._renderItem( ul, item ).data( "ui-selectmenu-item", item );
14320	},
14321
14322	_renderItem: function( ul, item ) {
14323		var li = $( "<li>" ),
14324			wrapper = $( "<div>", {
14325				title: item.element.attr( "title" )
14326			});
14327
14328		if ( item.disabled ) {
14329			this._addClass( li, null, "ui-state-disabled" );
14330		}
14331		this._setText( wrapper, item.label );
14332
14333		return li.append( wrapper ).appendTo( ul );
14334	},
14335
14336	_setText: function( element, value ) {
14337		if ( value ) {
14338			element.text( value );
14339		} else {
14340			element.html( "&#160;" );
14341		}
14342	},
14343
14344	_move: function( direction, event ) {
14345		var item, next,
14346			filter = ".ui-menu-item";
14347
14348		if ( this.isOpen ) {
14349			item = this.menuItems.eq( this.focusIndex ).parent( "li" );
14350		} else {
14351			item = this.menuItems.eq( this.element[ 0 ].selectedIndex ).parent( "li" );
14352			filter += ":not(.ui-state-disabled)";
14353		}
14354
14355		if ( direction === "first" || direction === "last" ) {
14356			next = item[ direction === "first" ? "prevAll" : "nextAll" ]( filter ).eq( -1 );
14357		} else {
14358			next = item[ direction + "All" ]( filter ).eq( 0 );
14359		}
14360
14361		if ( next.length ) {
14362			this.menuInstance.focus( event, next );
14363		}
14364	},
14365
14366	_getSelectedItem: function() {
14367		return this.menuItems.eq( this.element[ 0 ].selectedIndex ).parent( "li" );
14368	},
14369
14370	_toggle: function( event ) {
14371		this[ this.isOpen ? "close" : "open" ]( event );
14372	},
14373
14374	_setSelection: function() {
14375		var selection;
14376
14377		if ( !this.range ) {
14378			return;
14379		}
14380
14381		if ( window.getSelection ) {
14382			selection = window.getSelection();
14383			selection.removeAllRanges();
14384			selection.addRange( this.range );
14385
14386		// support: IE8
14387		} else {
14388			this.range.select();
14389		}
14390
14391		// support: IE
14392		// Setting the text selection kills the button focus in IE, but
14393		// restoring the focus doesn't kill the selection.
14394		this.button.focus();
14395	},
14396
14397	_documentClick: {
14398		mousedown: function( event ) {
14399			if ( !this.isOpen ) {
14400				return;
14401			}
14402
14403			if ( !$( event.target ).closest( ".ui-selectmenu-menu, #" + this.ids.button ).length ) {
14404				this.close( event );
14405			}
14406		}
14407	},
14408
14409	_buttonEvents: {
14410
14411		// Prevent text selection from being reset when interacting with the selectmenu (#10144)
14412		mousedown: function() {
14413			var selection;
14414
14415			if ( window.getSelection ) {
14416				selection = window.getSelection();
14417				if ( selection.rangeCount ) {
14418					this.range = selection.getRangeAt( 0 );
14419				}
14420
14421			// support: IE8
14422			} else {
14423				this.range = document.selection.createRange();
14424			}
14425		},
14426
14427		click: function( event ) {
14428			this._setSelection();
14429			this._toggle( event );
14430		},
14431
14432		keydown: function( event ) {
14433			var preventDefault = true;
14434			switch ( event.keyCode ) {
14435				case $.ui.keyCode.TAB:
14436				case $.ui.keyCode.ESCAPE:
14437					this.close( event );
14438					preventDefault = false;
14439					break;
14440				case $.ui.keyCode.ENTER:
14441					if ( this.isOpen ) {
14442						this._selectFocusedItem( event );
14443					}
14444					break;
14445				case $.ui.keyCode.UP:
14446					if ( event.altKey ) {
14447						this._toggle( event );
14448					} else {
14449						this._move( "prev", event );
14450					}
14451					break;
14452				case $.ui.keyCode.DOWN:
14453					if ( event.altKey ) {
14454						this._toggle( event );
14455					} else {
14456						this._move( "next", event );
14457					}
14458					break;
14459				case $.ui.keyCode.SPACE:
14460					if ( this.isOpen ) {
14461						this._selectFocusedItem( event );
14462					} else {
14463						this._toggle( event );
14464					}
14465					break;
14466				case $.ui.keyCode.LEFT:
14467					this._move( "prev", event );
14468					break;
14469				case $.ui.keyCode.RIGHT:
14470					this._move( "next", event );
14471					break;
14472				case $.ui.keyCode.HOME:
14473				case $.ui.keyCode.PAGE_UP:
14474					this._move( "first", event );
14475					break;
14476				case $.ui.keyCode.END:
14477				case $.ui.keyCode.PAGE_DOWN:
14478					this._move( "last", event );
14479					break;
14480				default:
14481					this.menu.trigger( event );
14482					preventDefault = false;
14483			}
14484
14485			if ( preventDefault ) {
14486				event.preventDefault();
14487			}
14488		}
14489	},
14490
14491	_selectFocusedItem: function( event ) {
14492		var item = this.menuItems.eq( this.focusIndex ).parent( "li" );
14493		if ( !item.hasClass( "ui-state-disabled" ) ) {
14494			this._select( item.data( "ui-selectmenu-item" ), event );
14495		}
14496	},
14497
14498	_select: function( item, event ) {
14499		var oldIndex = this.element[ 0 ].selectedIndex;
14500
14501		// Change native select element
14502		this.element[ 0 ].selectedIndex = item.index;
14503		this.buttonItem.replaceWith( this.buttonItem = this._renderButtonItem( item ) );
14504		this._setAria( item );
14505		this._trigger( "select", event, { item: item } );
14506
14507		if ( item.index !== oldIndex ) {
14508			this._trigger( "change", event, { item: item } );
14509		}
14510
14511		this.close( event );
14512	},
14513
14514	_setAria: function( item ) {
14515		var id = this.menuItems.eq( item.index ).attr( "id" );
14516
14517		this.button.attr({
14518			"aria-labelledby": id,
14519			"aria-activedescendant": id
14520		});
14521		this.menu.attr( "aria-activedescendant", id );
14522	},
14523
14524	_setOption: function( key, value ) {
14525		if ( key === "icons" ) {
14526			var icon = this.button.find( "span.ui-icon" );
14527			this._removeClass( icon, null, this.options.icons.button )
14528				._addClass( icon, null, value.button );
14529		}
14530
14531		this._super( key, value );
14532
14533		if ( key === "appendTo" ) {
14534			this.menuWrap.appendTo( this._appendTo() );
14535		}
14536
14537		if ( key === "disabled" ) {
14538			this.menuInstance.option( "disabled", value );
14539			this.button.attr( "aria-disabled", value );
14540			this._toggleClass( this.button, null, "ui-state-disabled", value );
14541
14542			this.element.prop( "disabled", value );
14543			if ( value ) {
14544				this.button.attr( "tabindex", -1 );
14545				this.close();
14546			} else {
14547				this.button.attr( "tabindex", 0 );
14548			}
14549		}
14550
14551		if ( key === "width" ) {
14552			this._resizeButton();
14553		}
14554	},
14555
14556	_appendTo: function() {
14557		var element = this.options.appendTo;
14558
14559		if ( element ) {
14560			element = element.jquery || element.nodeType ?
14561				$( element ) :
14562				this.document.find( element ).eq( 0 );
14563		}
14564
14565		if ( !element || !element[ 0 ] ) {
14566			element = this.element.closest( ".ui-front" );
14567		}
14568
14569		if ( !element.length ) {
14570			element = this.document[ 0 ].body;
14571		}
14572
14573		return element;
14574	},
14575
14576	_toggleAttr: function() {
14577		this.button.attr( "aria-expanded", this.isOpen );
14578
14579		// We can't use two _toggleClass() calls here, because we need to make sure
14580		// we always remove classes first and add them second, otherwise if both classes have the
14581		// same theme class, it will be removed after we add it.
14582		this._removeClass( this.button, "ui-selectmenu-button-" +
14583				( this.isOpen ? "closed" : "open" ) )
14584			._addClass( this.button, "ui-selectmenu-button-" +
14585				( this.isOpen ? "open" : "closed" ) )
14586			._toggleClass( this.menuWrap, "ui-selectmenu-open", null, this.isOpen );
14587
14588		this.menu.attr( "aria-hidden", !this.isOpen );
14589	},
14590
14591	_resizeButton: function() {
14592		var width = this.options.width;
14593
14594		// For `width: false`, just remove inline style and stop
14595		if ( width === false ) {
14596			this.button.css( "width", "" );
14597			return;
14598		}
14599
14600		// For `width: null`, match the width of the original element
14601		if ( width === null ) {
14602			width = this.element.show().outerWidth();
14603			this.element.hide();
14604		}
14605
14606		this.button.outerWidth( width );
14607	},
14608
14609	_resizeMenu: function() {
14610		this.menu.outerWidth( Math.max(
14611			this.button.outerWidth(),
14612
14613			// support: IE10
14614			// IE10 wraps long text (possibly a rounding bug)
14615			// so we add 1px to avoid the wrapping
14616			this.menu.width( "" ).outerWidth() + 1
14617		) );
14618	},
14619
14620	_getCreateOptions: function() {
14621		return { disabled: this.element.prop( "disabled" ) };
14622	},
14623
14624	_parseOptions: function( options ) {
14625		var that = this,
14626			data = [];
14627		options.each(function( index, item ) {
14628			data.push( that._parseOption( $( item ), index ) );
14629		});
14630		this.items = data;
14631	},
14632
14633	_parseOption: function( option, index ) {
14634		var optgroup = option.parent( "optgroup" );
14635
14636		return {
14637			element: option,
14638			index: index,
14639			value: option.val(),
14640			label: option.text(),
14641			optgroup: optgroup.attr( "label" ) || "",
14642			disabled: optgroup.prop( "disabled" ) || option.prop( "disabled" )
14643		};
14644	},
14645
14646	_destroy: function() {
14647		this.menuWrap.remove();
14648		this.button.remove();
14649		this.element.show();
14650		this.element.removeUniqueId();
14651		this.label.attr( "for", this.ids.element );
14652	}
14653});
14654
14655}));
14656
14657//>>label: Slider
14658//>>group: Widgets
14659//>>description: Displays a flexible slider with ranges and accessibility via keyboard.
14660//>>docs: http://api.jqueryui.com/slider/
14661//>>demos: http://jqueryui.com/slider/
14662//>>css.structure: ../themes/base/core.css
14663//>>css.structure: ../themes/base/slider.css
14664//>>css.theme: ../themes/base/theme.css
14665
14666(function( factory ) {
14667	if ( typeof define === "function" && define.amd ) {
14668
14669		// AMD. Register as an anonymous module.
14670		define([
14671			"jquery",
14672			"./core",
14673			"./mouse",
14674			"./widget"
14675		], factory );
14676	} else {
14677
14678		// Browser globals
14679		factory( jQuery );
14680	}
14681}(function( $ ) {
14682
14683return $.widget( "ui.slider", $.ui.mouse, {
14684	version: "@VERSION",
14685	widgetEventPrefix: "slide",
14686
14687	options: {
14688		animate: false,
14689		classes: {
14690			"ui-slider": "ui-corner-all",
14691			"ui-slider-handle": "ui-corner-all",
14692
14693			// Note: ui-widget-header isn't the most fittingly semantic framework class for this
14694			// element, but worked best visually with a variety of themes
14695			"ui-slider-range": "ui-corner-all ui-widget-header"
14696		},
14697		distance: 0,
14698		max: 100,
14699		min: 0,
14700		orientation: "horizontal",
14701		range: false,
14702		step: 1,
14703		value: 0,
14704		values: null,
14705
14706		// callbacks
14707		change: null,
14708		slide: null,
14709		start: null,
14710		stop: null
14711	},
14712
14713	// number of pages in a slider
14714	// (how many times can you page up/down to go through the whole range)
14715	numPages: 5,
14716
14717	_create: function() {
14718		this._keySliding = false;
vendor: 9,779 bytes, lines 14719-15098
14719		this._mouseSliding = false;
14720		this._animateOff = true;
14721		this._handleIndex = null;
14722		this._detectOrientation();
14723		this._mouseInit();
14724		this._calculateNewMax();
14725
14726		this._addClass( "ui-slider ui-slider-" + this.orientation,
14727			"ui-widget ui-widget-content" );
14728
14729		this._refresh();
14730		this._setOption( "disabled", this.options.disabled );
14731
14732		this._animateOff = false;
14733	},
14734
14735	_refresh: function() {
14736		this._createRange();
14737		this._createHandles();
14738		this._setupEvents();
14739		this._refreshValue();
14740	},
14741
14742	_createHandles: function() {
14743		var i, handleCount,
14744			options = this.options,
14745			existingHandles = this.element.find( ".ui-slider-handle" ),
14746			handle = "<span tabindex='0'></span>",
14747			handles = [];
14748
14749		handleCount = ( options.values && options.values.length ) || 1;
14750
14751		if ( existingHandles.length > handleCount ) {
14752			existingHandles.slice( handleCount ).remove();
14753			existingHandles = existingHandles.slice( 0, handleCount );
14754		}
14755
14756		for ( i = existingHandles.length; i < handleCount; i++ ) {
14757			handles.push( handle );
14758		}
14759
14760		this.handles = existingHandles.add( $( handles.join( "" ) ).appendTo( this.element ) );
14761
14762		this._addClass( this.handles, "ui-slider-handle", "ui-state-default" );
14763
14764		this.handle = this.handles.eq( 0 );
14765
14766		this.handles.each(function( i ) {
14767			$( this ).data( "ui-slider-handle-index", i );
14768		});
14769	},
14770
14771	_createRange: function() {
14772		var options = this.options;
14773
14774		if ( options.range ) {
14775			if ( options.range === true ) {
14776				if ( !options.values ) {
14777					options.values = [ this._valueMin(), this._valueMin() ];
14778				} else if ( options.values.length && options.values.length !== 2 ) {
14779					options.values = [ options.values[0], options.values[0] ];
14780				} else if ( $.isArray( options.values ) ) {
14781					options.values = options.values.slice(0);
14782				}
14783			}
14784
14785			if ( !this.range || !this.range.length ) {
14786				this.range = $( "<div>" )
14787					.appendTo( this.element );
14788
14789				this._addClass( this.range, "ui-slider-range" );
14790			} else {
14791				this._removeClass( this.range, "ui-slider-range-min ui-slider-range-max" );
14792
14793				// Handle range switching from true to min/max
14794				this.range.css({
14795					"left": "",
14796					"bottom": ""
14797				});
14798			}
14799			if ( options.range === "min" || options.range === "max" ) {
14800				this._addClass( this.range, "ui-slider-range-" + options.range );
14801			}
14802		} else {
14803			if ( this.range ) {
14804				this.range.remove();
14805			}
14806			this.range = null;
14807		}
14808	},
14809
14810	_setupEvents: function() {
14811		this._off( this.handles );
14812		this._on( this.handles, this._handleEvents );
14813		this._hoverable( this.handles );
14814		this._focusable( this.handles );
14815	},
14816
14817	_destroy: function() {
14818		this.handles.remove();
14819		if ( this.range ) {
14820			this.range.remove();
14821		}
14822
14823		this._mouseDestroy();
14824	},
14825
14826	_mouseCapture: function( event ) {
14827		var position, normValue, distance, closestHandle, index, allowed, offset, mouseOverHandle,
14828			that = this,
14829			o = this.options;
14830
14831		if ( o.disabled ) {
14832			return false;
14833		}
14834
14835		this.elementSize = {
14836			width: this.element.outerWidth(),
14837			height: this.element.outerHeight()
14838		};
14839		this.elementOffset = this.element.offset();
14840
14841		position = { x: event.pageX, y: event.pageY };
14842		normValue = this._normValueFromMouse( position );
14843		distance = this._valueMax() - this._valueMin() + 1;
14844		this.handles.each(function( i ) {
14845			var thisDistance = Math.abs( normValue - that.values(i) );
14846			if (( distance > thisDistance ) ||
14847				( distance === thisDistance &&
14848					(i === that._lastChangedValue || that.values(i) === o.min ))) {
14849				distance = thisDistance;
14850				closestHandle = $( this );
14851				index = i;
14852			}
14853		});
14854
14855		allowed = this._start( event, index );
14856		if ( allowed === false ) {
14857			return false;
14858		}
14859		this._mouseSliding = true;
14860
14861		this._handleIndex = index;
14862
14863		this._addClass( closestHandle, null, "ui-state-active" );
14864		closestHandle.focus();
14865
14866		offset = closestHandle.offset();
14867		mouseOverHandle = !$( event.target ).parents().addBack().is( ".ui-slider-handle" );
14868		this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : {
14869			left: event.pageX - offset.left - ( closestHandle.width() / 2 ),
14870			top: event.pageY - offset.top -
14871				( closestHandle.height() / 2 ) -
14872				( parseInt( closestHandle.css("borderTopWidth"), 10 ) || 0 ) -
14873				( parseInt( closestHandle.css("borderBottomWidth"), 10 ) || 0) +
14874				( parseInt( closestHandle.css("marginTop"), 10 ) || 0)
14875		};
14876
14877		if ( !this.handles.hasClass( "ui-state-hover" ) ) {
14878			this._slide( event, index, normValue );
14879		}
14880		this._animateOff = true;
14881		return true;
14882	},
14883
14884	_mouseStart: function() {
14885		return true;
14886	},
14887
14888	_mouseDrag: function( event ) {
14889		var position = { x: event.pageX, y: event.pageY },
14890			normValue = this._normValueFromMouse( position );
14891
14892		this._slide( event, this._handleIndex, normValue );
14893
14894		return false;
14895	},
14896
14897	_mouseStop: function( event ) {
14898		this._removeClass( this.handles, null, "ui-state-active" );
14899		this._mouseSliding = false;
14900
14901		this._stop( event, this._handleIndex );
14902		this._change( event, this._handleIndex );
14903
14904		this._handleIndex = null;
14905		this._clickOffset = null;
14906		this._animateOff = false;
14907
14908		return false;
14909	},
14910
14911	_detectOrientation: function() {
14912		this.orientation = ( this.options.orientation === "vertical" ) ? "vertical" : "horizontal";
14913	},
14914
14915	_normValueFromMouse: function( position ) {
14916		var pixelTotal,
14917			pixelMouse,
14918			percentMouse,
14919			valueTotal,
14920			valueMouse;
14921
14922		if ( this.orientation === "horizontal" ) {
14923			pixelTotal = this.elementSize.width;
14924			pixelMouse = position.x - this.elementOffset.left - ( this._clickOffset ? this._clickOffset.left : 0 );
14925		} else {
14926			pixelTotal = this.elementSize.height;
14927			pixelMouse = position.y - this.elementOffset.top - ( this._clickOffset ? this._clickOffset.top : 0 );
14928		}
14929
14930		percentMouse = ( pixelMouse / pixelTotal );
14931		if ( percentMouse > 1 ) {
14932			percentMouse = 1;
14933		}
14934		if ( percentMouse < 0 ) {
14935			percentMouse = 0;
14936		}
14937		if ( this.orientation === "vertical" ) {
14938			percentMouse = 1 - percentMouse;
14939		}
14940
14941		valueTotal = this._valueMax() - this._valueMin();
14942		valueMouse = this._valueMin() + percentMouse * valueTotal;
14943
14944		return this._trimAlignValue( valueMouse );
14945	},
14946
14947	_uiHash: function( index, value, values ) {
14948		var uiHash = {
14949			handle: this.handles[ index ],
14950			handleIndex: index,
14951			value: value || this.value()
14952		};
14953
14954		if ( this._hasMultipleValues() ) {
14955			uiHash.value = value || this.values( index );
14956			uiHash.values = values || this.values();
14957		}
14958
14959		return uiHash;
14960	},
14961
14962	_hasMultipleValues: function() {
14963		return this.options.values && this.options.values.length;
14964	},
14965
14966	_start: function( event, index ) {
14967		return this._trigger( "start", event, this._uiHash( index ) );
14968	},
14969
14970	_slide: function( event, index, newVal ) {
14971		var allowed, otherVal,
14972			currentValue = this.value(),
14973			newValues = this.values();
14974
14975		if ( this._hasMultipleValues() ) {
14976			otherVal = this.values( index ? 0 : 1 );
14977			currentValue = this.values( index );
14978
14979			if ( this.options.values.length === 2 && this.options.range === true ) {
14980				newVal =  index === 0 ? Math.min( otherVal, newVal ) : Math.max( otherVal, newVal );
14981			}
14982
14983			newValues[ index ] = newVal;
14984		}
14985
14986		if ( newVal === currentValue ) {
14987			return;
14988		}
14989
14990		allowed = this._trigger( "slide", event, this._uiHash( index, newVal, newValues ) );
14991
14992		// A slide can be canceled by returning false from the slide callback
14993		if ( allowed === false ) {
14994			return;
14995		}
14996
14997		if ( this._hasMultipleValues() ) {
14998			this.values( index, newVal );
14999		} else {
15000			this.value( newVal );
15001		}
15002	},
15003
15004	_stop: function( event, index ) {
15005		this._trigger( "stop", event, this._uiHash( index ) );
15006	},
15007
15008	_change: function( event, index ) {
15009		if ( !this._keySliding && !this._mouseSliding ) {
15010			//store the last changed value index for reference when handles overlap
15011			this._lastChangedValue = index;
15012			this._trigger( "change", event, this._uiHash( index ) );
15013		}
15014	},
15015
15016	value: function( newValue ) {
15017		if ( arguments.length ) {
15018			this.options.value = this._trimAlignValue( newValue );
15019			this._refreshValue();
15020			this._change( null, 0 );
15021			return;
15022		}
15023
15024		return this._value();
15025	},
15026
15027	values: function( index, newValue ) {
15028		var vals,
15029			newValues,
15030			i;
15031
15032		if ( arguments.length > 1 ) {
15033			this.options.values[ index ] = this._trimAlignValue( newValue );
15034			this._refreshValue();
15035			this._change( null, index );
15036			return;
15037		}
15038
15039		if ( arguments.length ) {
15040			if ( $.isArray( arguments[ 0 ] ) ) {
15041				vals = this.options.values;
15042				newValues = arguments[ 0 ];
15043				for ( i = 0; i < vals.length; i += 1 ) {
15044					vals[ i ] = this._trimAlignValue( newValues[ i ] );
15045					this._change( null, i );
15046				}
15047				this._refreshValue();
15048			} else {
15049				if ( this._hasMultipleValues() ) {
15050					return this._values( index );
15051				} else {
15052					return this.value();
15053				}
15054			}
15055		} else {
15056			return this._values();
15057		}
15058	},
15059
15060	_setOption: function( key, value ) {
15061		var i,
15062			valsLength = 0;
15063
15064		if ( key === "range" && this.options.range === true ) {
15065			if ( value === "min" ) {
15066				this.options.value = this._values( 0 );
15067				this.options.values = null;
15068			} else if ( value === "max" ) {
15069				this.options.value = this._values( this.options.values.length - 1 );
15070				this.options.values = null;
15071			}
15072		}
15073
15074		if ( $.isArray( this.options.values ) ) {
15075			valsLength = this.options.values.length;
15076		}
15077
15078		if ( key === "disabled" ) {
15079			this._toggleClass( null, "ui-state-disabled", !!value );
15080		}
15081
15082		this._super( key, value );
15083
15084		switch ( key ) {
15085			case "orientation":
15086				this._detectOrientation();
15087				this._removeClass( "ui-slider-horizontal ui-slider-vertical" )
15088					._addClass( "ui-slider-" + this.orientation );
15089				this._refreshValue();
15090
15091				// Reset positioning from previous orientation
15092				this.handles.css( value === "horizontal" ? "bottom" : "left", "" );
15093				break;
15094			case "value":
15095				this._animateOff = true;
15096				this._refreshValue();
15097				this._change( null, 0 );
15098				this._animateOff = false;
vendor: 4,188 bytes, lines 15099-15241
15099				break;
15100			case "values":
15101				this._animateOff = true;
15102				this._refreshValue();
15103				for ( i = 0; i < valsLength; i += 1 ) {
15104					this._change( null, i );
15105				}
15106				this._animateOff = false;
15107				break;
15108			case "step":
15109			case "min":
15110			case "max":
15111				this._animateOff = true;
15112				this._calculateNewMax();
15113				this._refreshValue();
15114				this._animateOff = false;
15115				break;
15116			case "range":
15117				this._animateOff = true;
15118				this._refresh();
15119				this._animateOff = false;
15120				break;
15121		}
15122	},
15123
15124	//internal value getter
15125	// _value() returns value trimmed by min and max, aligned by step
15126	_value: function() {
15127		var val = this.options.value;
15128		val = this._trimAlignValue( val );
15129
15130		return val;
15131	},
15132
15133	//internal values getter
15134	// _values() returns array of values trimmed by min and max, aligned by step
15135	// _values( index ) returns single value trimmed by min and max, aligned by step
15136	_values: function( index ) {
15137		var val,
15138			vals,
15139			i;
15140
15141		if ( arguments.length ) {
15142			val = this.options.values[ index ];
15143			val = this._trimAlignValue( val );
15144
15145			return val;
15146		} else if ( this._hasMultipleValues() ) {
15147			// .slice() creates a copy of the array
15148			// this copy gets trimmed by min and max and then returned
15149			vals = this.options.values.slice();
15150			for ( i = 0; i < vals.length; i += 1) {
15151				vals[ i ] = this._trimAlignValue( vals[ i ] );
15152			}
15153
15154			return vals;
15155		} else {
15156			return [];
15157		}
15158	},
15159
15160	// returns the step-aligned value that val is closest to, between (inclusive) min and max
15161	_trimAlignValue: function( val ) {
15162		if ( val <= this._valueMin() ) {
15163			return this._valueMin();
15164		}
15165		if ( val >= this._valueMax() ) {
15166			return this._valueMax();
15167		}
15168		var step = ( this.options.step > 0 ) ? this.options.step : 1,
15169			valModStep = (val - this._valueMin()) % step,
15170			alignValue = val - valModStep;
15171
15172		if ( Math.abs(valModStep) * 2 >= step ) {
15173			alignValue += ( valModStep > 0 ) ? step : ( -step );
15174		}
15175
15176		// Since JavaScript has problems with large floats, round
15177		// the final value to 5 digits after the decimal point (see #4124)
15178		return parseFloat( alignValue.toFixed(5) );
15179	},
15180
15181	_calculateNewMax: function() {
15182		var max = this.options.max,
15183			min = this._valueMin(),
15184			step = this.options.step,
15185			aboveMin = Math.floor( ( +( max - min ).toFixed( this._precision() ) ) / step ) * step;
15186		max = aboveMin + min;
15187		this.max = parseFloat( max.toFixed( this._precision() ) );
15188	},
15189
15190	_precision: function() {
15191		var precision = this._precisionOf( this.options.step );
15192		if ( this.options.min !== null ) {
15193			precision = Math.max( precision, this._precisionOf( this.options.min ) );
15194		}
15195		return precision;
15196	},
15197
15198	_precisionOf: function( num ) {
15199		var str = num.toString(),
15200			decimal = str.indexOf( "." );
15201		return decimal === -1 ? 0 : str.length - decimal - 1;
15202	},
15203
15204	_valueMin: function() {
15205		return this.options.min;
15206	},
15207
15208	_valueMax: function() {
15209		return this.max;
15210	},
15211
15212	_refreshValue: function() {
15213		var lastValPercent, valPercent, value, valueMin, valueMax,
15214			oRange = this.options.range,
15215			o = this.options,
15216			that = this,
15217			animate = ( !this._animateOff ) ? o.animate : false,
15218			_set = {};
15219
15220		if ( this._hasMultipleValues() ) {
15221			this.handles.each(function( i ) {
15222				valPercent = ( that.values(i) - that._valueMin() ) / ( that._valueMax() - that._valueMin() ) * 100;
15223				_set[ that.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
15224				$( this ).stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
15225				if ( that.options.range === true ) {
15226					if ( that.orientation === "horizontal" ) {
15227						if ( i === 0 ) {
15228							that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { left: valPercent + "%" }, o.animate );
15229						}
15230						if ( i === 1 ) {
15231							that.range[ animate ? "animate" : "css" ]( { width: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
15232						}
15233					} else {
15234						if ( i === 0 ) {
15235							that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { bottom: ( valPercent ) + "%" }, o.animate );
15236						}
15237						if ( i === 1 ) {
15238							that.range[ animate ? "animate" : "css" ]( { height: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
15239						}
15240					}
15241				}
vendor: 4,880 bytes, lines 15242-15422
15242				lastValPercent = valPercent;
15243			});
15244		} else {
15245			value = this.value();
15246			valueMin = this._valueMin();
15247			valueMax = this._valueMax();
15248			valPercent = ( valueMax !== valueMin ) ?
15249					( value - valueMin ) / ( valueMax - valueMin ) * 100 :
15250					0;
15251			_set[ this.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
15252			this.handle.stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
15253
15254			if ( oRange === "min" && this.orientation === "horizontal" ) {
15255				this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { width: valPercent + "%" }, o.animate );
15256			}
15257			if ( oRange === "max" && this.orientation === "horizontal" ) {
15258				this.range[ animate ? "animate" : "css" ]( { width: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
15259			}
15260			if ( oRange === "min" && this.orientation === "vertical" ) {
15261				this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { height: valPercent + "%" }, o.animate );
15262			}
15263			if ( oRange === "max" && this.orientation === "vertical" ) {
15264				this.range[ animate ? "animate" : "css" ]( { height: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
15265			}
15266		}
15267	},
15268
15269	_handleEvents: {
15270		keydown: function( event ) {
15271			var allowed, curVal, newVal, step,
15272				index = $( event.target ).data( "ui-slider-handle-index" );
15273
15274			switch ( event.keyCode ) {
15275				case $.ui.keyCode.HOME:
15276				case $.ui.keyCode.END:
15277				case $.ui.keyCode.PAGE_UP:
15278				case $.ui.keyCode.PAGE_DOWN:
15279				case $.ui.keyCode.UP:
15280				case $.ui.keyCode.RIGHT:
15281				case $.ui.keyCode.DOWN:
15282				case $.ui.keyCode.LEFT:
15283					event.preventDefault();
15284					if ( !this._keySliding ) {
15285						this._keySliding = true;
15286						this._addClass( $( event.target ), null, "ui-state-active" );
15287						allowed = this._start( event, index );
15288						if ( allowed === false ) {
15289							return;
15290						}
15291					}
15292					break;
15293			}
15294
15295			step = this.options.step;
15296			if ( this._hasMultipleValues() ) {
15297				curVal = newVal = this.values( index );
15298			} else {
15299				curVal = newVal = this.value();
15300			}
15301
15302			switch ( event.keyCode ) {
15303				case $.ui.keyCode.HOME:
15304					newVal = this._valueMin();
15305					break;
15306				case $.ui.keyCode.END:
15307					newVal = this._valueMax();
15308					break;
15309				case $.ui.keyCode.PAGE_UP:
15310					newVal = this._trimAlignValue(
15311						curVal + ( ( this._valueMax() - this._valueMin() ) / this.numPages )
15312					);
15313					break;
15314				case $.ui.keyCode.PAGE_DOWN:
15315					newVal = this._trimAlignValue(
15316						curVal - ( (this._valueMax() - this._valueMin()) / this.numPages ) );
15317					break;
15318				case $.ui.keyCode.UP:
15319				case $.ui.keyCode.RIGHT:
15320					if ( curVal === this._valueMax() ) {
15321						return;
15322					}
15323					newVal = this._trimAlignValue( curVal + step );
15324					break;
15325				case $.ui.keyCode.DOWN:
15326				case $.ui.keyCode.LEFT:
15327					if ( curVal === this._valueMin() ) {
15328						return;
15329					}
15330					newVal = this._trimAlignValue( curVal - step );
15331					break;
15332			}
15333
15334			this._slide( event, index, newVal );
15335		},
15336		keyup: function( event ) {
15337			var index = $( event.target ).data( "ui-slider-handle-index" );
15338
15339			if ( this._keySliding ) {
15340				this._keySliding = false;
15341				this._stop( event, index );
15342				this._change( event, index );
15343				this._removeClass( $( event.target ), null, "ui-state-active" );
15344			}
15345		}
15346	}
15347});
15348
15349}));
15350
15351//>>label: Spinner
15352//>>group: Widgets
15353//>>description: Displays buttons to easily input numbers via the keyboard or mouse.
15354//>>docs: http://api.jqueryui.com/spinner/
15355//>>demos: http://jqueryui.com/spinner/
15356//>>css.structure: ../themes/base/core.css
15357//>>css.structure: ../themes/base/spinner.css
15358//>>css.theme: ../themes/base/theme.css
15359
15360(function( factory ) {
15361	if ( typeof define === "function" && define.amd ) {
15362
15363		// AMD. Register as an anonymous module.
15364		define([
15365			"jquery",
15366			"./core",
15367			"./widget",
15368			"./button"
15369		], factory );
15370	} else {
15371
15372		// Browser globals
15373		factory( jQuery );
15374	}
15375}(function( $ ) {
15376
15377function spinner_modifier( fn ) {
15378	return function() {
15379		var previous = this.element.val();
15380		fn.apply( this, arguments );
15381		this._refresh();
15382		if ( previous !== this.element.val() ) {
15383			this._trigger( "change" );
15384		}
15385	};
15386}
15387
15388return $.widget( "ui.spinner", {
15389	version: "@VERSION",
15390	defaultElement: "<input>",
15391	widgetEventPrefix: "spin",
15392	options: {
15393		classes: {
15394			"ui-spinner": "ui-corner-all",
15395			"ui-spinner-down": "ui-corner-br",
15396			"ui-spinner-up": "ui-corner-tr"
15397		},
15398		culture: null,
15399		icons: {
15400			down: "ui-icon-triangle-1-s",
15401			up: "ui-icon-triangle-1-n"
15402		},
15403		incremental: true,
15404		max: null,
15405		min: null,
15406		numberFormat: null,
15407		page: 10,
15408		step: 1,
15409
15410		change: null,
15411		spin: null,
15412		start: null,
15413		stop: null
15414	},
15415
15416	_create: function() {
15417		// handle string values that need to be parsed
15418		this._setOption( "max", this.options.max );
15419		this._setOption( "min", this.options.min );
15420		this._setOption( "step", this.options.step );
15421
15422		// Only format if there is a value, prevents the field from being marked
vendor: 5,795 bytes, lines 15423-15618
15423		// as invalid in Firefox, see #9573.
15424		if ( this.value() !== "" ) {
15425			// Format the value, but don't constrain.
15426			this._value( this.element.val(), true );
15427		}
15428
15429		this._draw();
15430		this._on( this._events );
15431		this._refresh();
15432
15433		// turning off autocomplete prevents the browser from remembering the
15434		// value when navigating through history, so we re-enable autocomplete
15435		// if the page is unloaded before the widget is destroyed. #7790
15436		this._on( this.window, {
15437			beforeunload: function() {
15438				this.element.removeAttr( "autocomplete" );
15439			}
15440		});
15441	},
15442
15443	_getCreateOptions: function() {
15444		var options = {},
15445			element = this.element;
15446
15447		$.each( [ "min", "max", "step" ], function( i, option ) {
15448			var value = element.attr( option );
15449			if ( value !== undefined && value.length ) {
15450				options[ option ] = value;
15451			}
15452		});
15453
15454		return options;
15455	},
15456
15457	_events: {
15458		keydown: function( event ) {
15459			if ( this._start( event ) && this._keydown( event ) ) {
15460				event.preventDefault();
15461			}
15462		},
15463		keyup: "_stop",
15464		focus: function() {
15465			this.previous = this.element.val();
15466		},
15467		blur: function( event ) {
15468			if ( this.cancelBlur ) {
15469				delete this.cancelBlur;
15470				return;
15471			}
15472
15473			this._stop();
15474			this._refresh();
15475			if ( this.previous !== this.element.val() ) {
15476				this._trigger( "change", event );
15477			}
15478		},
15479		mousewheel: function( event, delta ) {
15480			if ( !delta ) {
15481				return;
15482			}
15483			if ( !this.spinning && !this._start( event ) ) {
15484				return false;
15485			}
15486
15487			this._spin( (delta > 0 ? 1 : -1) * this.options.step, event );
15488			clearTimeout( this.mousewheelTimer );
15489			this.mousewheelTimer = this._delay(function() {
15490				if ( this.spinning ) {
15491					this._stop( event );
15492				}
15493			}, 100 );
15494			event.preventDefault();
15495		},
15496		"mousedown .ui-spinner-button": function( event ) {
15497			var previous;
15498
15499			// We never want the buttons to have focus; whenever the user is
15500			// interacting with the spinner, the focus should be on the input.
15501			// If the input is focused then this.previous is properly set from
15502			// when the input first received focus. If the input is not focused
15503			// then we need to set this.previous based on the value before spinning.
15504			previous = this.element[0] === $.ui.safeActiveElement( this.document[0] ) ?
15505				this.previous : this.element.val();
15506			function checkFocus() {
15507				var isActive = this.element[0] === $.ui.safeActiveElement( this.document[0] );
15508				if ( !isActive ) {
15509					this.element.focus();
15510					this.previous = previous;
15511					// support: IE
15512					// IE sets focus asynchronously, so we need to check if focus
15513					// moved off of the input because the user clicked on the button.
15514					this._delay(function() {
15515						this.previous = previous;
15516					});
15517				}
15518			}
15519
15520			// ensure focus is on (or stays on) the text field
15521			event.preventDefault();
15522			checkFocus.call( this );
15523
15524			// support: IE
15525			// IE doesn't prevent moving focus even with event.preventDefault()
15526			// so we set a flag to know when we should ignore the blur event
15527			// and check (again) if focus moved off of the input.
15528			this.cancelBlur = true;
15529			this._delay(function() {
15530				delete this.cancelBlur;
15531				checkFocus.call( this );
15532			});
15533
15534			if ( this._start( event ) === false ) {
15535				return;
15536			}
15537
15538			this._repeat( null, $( event.currentTarget ).hasClass( "ui-spinner-up" ) ? 1 : -1, event );
15539		},
15540		"mouseup .ui-spinner-button": "_stop",
15541		"mouseenter .ui-spinner-button": function( event ) {
15542			// button will add ui-state-active if mouse was down while mouseleave and kept down
15543			if ( !$( event.currentTarget ).hasClass( "ui-state-active" ) ) {
15544				return;
15545			}
15546
15547			if ( this._start( event ) === false ) {
15548				return false;
15549			}
15550			this._repeat( null, $( event.currentTarget ).hasClass( "ui-spinner-up" ) ? 1 : -1, event );
15551		},
15552		// TODO: do we really want to consider this a stop?
15553		// shouldn't we just stop the repeater and wait until mouseup before
15554		// we trigger the stop event?
15555		"mouseleave .ui-spinner-button": "_stop"
15556	},
15557
15558	_draw: function() {
15559		var uiSpinner = this.uiSpinner = this.element
15560			.attr( "autocomplete", "off" )
15561			.wrap( this._uiSpinnerHtml() )
15562			.parent()
15563				// add buttons
15564				.append( this._buttonHtml() );
15565
15566		this._addClass( this.uiSpinner, "ui-spinner", "ui-widget ui-widget-content" );
15567		this._addClass( "ui-spinner-input" );
15568
15569		this.element.attr( "role", "spinbutton" );
15570
15571		// button bindings
15572		this.buttons = uiSpinner.children( "a" )
15573			.attr( "tabIndex", -1 )
15574			.button();
15575
15576		// TODO: Right now button does not support classes this is already updated in button PR
15577		this._removeClass( this.buttons, "ui-corner-all" );
15578
15579		this._addClass( this.buttons.first(), "ui-spinner-button ui-spinner-up" );
15580		this._addClass( this.buttons.last(), "ui-spinner-button ui-spinner-down" );
15581		this._addClass( this.buttons.first().find( ".ui-button-text span" ), null,
15582			"ui-icon " + this.options.icons.up );
15583		this._addClass( this.buttons.last().find( ".ui-button-text span" ), null,
15584			"ui-icon " + this.options.icons.down );
15585
15586		// IE 6 doesn't understand height: 50% for the buttons
15587		// unless the wrapper has an explicit height
15588		if ( this.buttons.height() > Math.ceil( uiSpinner.height() * 0.5 ) &&
15589				uiSpinner.height() > 0 ) {
15590			uiSpinner.height( uiSpinner.height() );
15591		}
15592
15593		// disable spinner if element was already disabled
15594		if ( this.options.disabled ) {
15595			this.disable();
15596		}
15597	},
15598
15599	_keydown: function( event ) {
15600		var options = this.options,
15601			keyCode = $.ui.keyCode;
15602
15603		switch ( event.keyCode ) {
15604		case keyCode.UP:
15605			this._repeat( null, 1, event );
15606			return true;
15607		case keyCode.DOWN:
15608			this._repeat( null, -1, event );
15609			return true;
15610		case keyCode.PAGE_UP:
15611			this._repeat( null, options.page, event );
15612			return true;
15613		case keyCode.PAGE_DOWN:
15614			this._repeat( null, -options.page, event );
15615			return true;
15616		}
15617
15618		return false;
vendor: 9,027 bytes, lines 15619-16000
15619	},
15620
15621	_uiSpinnerHtml: function() {
15622		return "<span>";
15623	},
15624
15625	_buttonHtml: function() {
15626		return "" +
15627			"<a>" +
15628				"<span>&#9650;</span>" +
15629			"</a>" +
15630			"<a>" +
15631				"<span>&#9660;</span>" +
15632			"</a>";
15633	},
15634
15635	_start: function( event ) {
15636		if ( !this.spinning && this._trigger( "start", event ) === false ) {
15637			return false;
15638		}
15639
15640		if ( !this.counter ) {
15641			this.counter = 1;
15642		}
15643		this.spinning = true;
15644		return true;
15645	},
15646
15647	_repeat: function( i, steps, event ) {
15648		i = i || 500;
15649
15650		clearTimeout( this.timer );
15651		this.timer = this._delay(function() {
15652			this._repeat( 40, steps, event );
15653		}, i );
15654
15655		this._spin( steps * this.options.step, event );
15656	},
15657
15658	_spin: function( step, event ) {
15659		var value = this.value() || 0;
15660
15661		if ( !this.counter ) {
15662			this.counter = 1;
15663		}
15664
15665		value = this._adjustValue( value + step * this._increment( this.counter ) );
15666
15667		if ( !this.spinning || this._trigger( "spin", event, { value: value } ) !== false) {
15668			this._value( value );
15669			this.counter++;
15670		}
15671	},
15672
15673	_increment: function( i ) {
15674		var incremental = this.options.incremental;
15675
15676		if ( incremental ) {
15677			return $.isFunction( incremental ) ?
15678				incremental( i ) :
15679				Math.floor( i * i * i / 50000 - i * i / 500 + 17 * i / 200 + 1 );
15680		}
15681
15682		return 1;
15683	},
15684
15685	_precision: function() {
15686		var precision = this._precisionOf( this.options.step );
15687		if ( this.options.min !== null ) {
15688			precision = Math.max( precision, this._precisionOf( this.options.min ) );
15689		}
15690		return precision;
15691	},
15692
15693	_precisionOf: function( num ) {
15694		var str = num.toString(),
15695			decimal = str.indexOf( "." );
15696		return decimal === -1 ? 0 : str.length - decimal - 1;
15697	},
15698
15699	_adjustValue: function( value ) {
15700		var base, aboveMin,
15701			options = this.options;
15702
15703		// make sure we're at a valid step
15704		// - find out where we are relative to the base (min or 0)
15705		base = options.min !== null ? options.min : 0;
15706		aboveMin = value - base;
15707		// - round to the nearest step
15708		aboveMin = Math.round(aboveMin / options.step) * options.step;
15709		// - rounding is based on 0, so adjust back to our base
15710		value = base + aboveMin;
15711
15712		// fix precision from bad JS floating point math
15713		value = parseFloat( value.toFixed( this._precision() ) );
15714
15715		// clamp the value
15716		if ( options.max !== null && value > options.max) {
15717			return options.max;
15718		}
15719		if ( options.min !== null && value < options.min ) {
15720			return options.min;
15721		}
15722
15723		return value;
15724	},
15725
15726	_stop: function( event ) {
15727		if ( !this.spinning ) {
15728			return;
15729		}
15730
15731		clearTimeout( this.timer );
15732		clearTimeout( this.mousewheelTimer );
15733		this.counter = 0;
15734		this.spinning = false;
15735		this._trigger( "stop", event );
15736	},
15737
15738	_setOption: function( key, value ) {
15739		var prevValue, first, last;
15740
15741		if ( key === "culture" || key === "numberFormat" ) {
15742			prevValue = this._parse( this.element.val() );
15743			this.options[ key ] = value;
15744			this.element.val( this._format( prevValue ) );
15745			return;
15746		}
15747
15748		if ( key === "max" || key === "min" || key === "step" ) {
15749			if ( typeof value === "string" ) {
15750				value = this._parse( value );
15751			}
15752		}
15753		if ( key === "icons" ) {
15754			first = this.buttons.first().find( ".ui-icon" );
15755			this._removeClass( first, null, this.options.icons.up );
15756			this._addClass( first, null, value.up );
15757			last = this.buttons.last().find( ".ui-icon" );
15758			this._removeClass( last, null, this.options.icons.down );
15759			this._addClass( last, null, value.down );
15760		}
15761
15762		this._super( key, value );
15763
15764		if ( key === "disabled" ) {
15765			this._toggleClass( this.uiSpinner, null, "ui-state-disabled", !!value );
15766			this.element.prop( "disabled", !!value );
15767			this.buttons.button( value ? "disable" : "enable" );
15768		}
15769	},
15770
15771	_setOptions: spinner_modifier(function( options ) {
15772		this._super( options );
15773	}),
15774
15775	_parse: function( val ) {
15776		if ( typeof val === "string" && val !== "" ) {
15777			val = window.Globalize && this.options.numberFormat ?
15778				Globalize.parseFloat( val, 10, this.options.culture ) : +val;
15779		}
15780		return val === "" || isNaN( val ) ? null : val;
15781	},
15782
15783	_format: function( value ) {
15784		if ( value === "" ) {
15785			return "";
15786		}
15787		return window.Globalize && this.options.numberFormat ?
15788			Globalize.format( value, this.options.numberFormat, this.options.culture ) :
15789			value;
15790	},
15791
15792	_refresh: function() {
15793		this.element.attr({
15794			"aria-valuemin": this.options.min,
15795			"aria-valuemax": this.options.max,
15796			// TODO: what should we do with values that can't be parsed?
15797			"aria-valuenow": this._parse( this.element.val() )
15798		});
15799	},
15800
15801	isValid: function() {
15802		var value = this.value();
15803
15804		// null is invalid
15805		if ( value === null ) {
15806			return false;
15807		}
15808
15809		// if value gets adjusted, it's invalid
15810		return value === this._adjustValue( value );
15811	},
15812
15813	// update the value without triggering change
15814	_value: function( value, allowAny ) {
15815		var parsed;
15816		if ( value !== "" ) {
15817			parsed = this._parse( value );
15818			if ( parsed !== null ) {
15819				if ( !allowAny ) {
15820					parsed = this._adjustValue( parsed );
15821				}
15822				value = this._format( parsed );
15823			}
15824		}
15825		this.element.val( value );
15826		this._refresh();
15827	},
15828
15829	_destroy: function() {
15830		this.element
15831			.prop( "disabled", false )
15832			.removeAttr( "autocomplete" )
15833			.removeAttr( "role" )
15834			.removeAttr( "aria-valuemin" )
15835			.removeAttr( "aria-valuemax" )
15836			.removeAttr( "aria-valuenow" );
15837
15838		this.uiSpinner.replaceWith( this.element );
15839	},
15840
15841	stepUp: spinner_modifier(function( steps ) {
15842		this._stepUp( steps );
15843	}),
15844	_stepUp: function( steps ) {
15845		if ( this._start() ) {
15846			this._spin( (steps || 1) * this.options.step );
15847			this._stop();
15848		}
15849	},
15850
15851	stepDown: spinner_modifier(function( steps ) {
15852		this._stepDown( steps );
15853	}),
15854	_stepDown: function( steps ) {
15855		if ( this._start() ) {
15856			this._spin( (steps || 1) * -this.options.step );
15857			this._stop();
15858		}
15859	},
15860
15861	pageUp: spinner_modifier(function( pages ) {
15862		this._stepUp( (pages || 1) * this.options.page );
15863	}),
15864
15865	pageDown: spinner_modifier(function( pages ) {
15866		this._stepDown( (pages || 1) * this.options.page );
15867	}),
15868
15869	value: function( newVal ) {
15870		if ( !arguments.length ) {
15871			return this._parse( this.element.val() );
15872		}
15873		spinner_modifier( this._value ).call( this, newVal );
15874	},
15875
15876	widget: function() {
15877		return this.uiSpinner;
15878	}
15879});
15880
15881}));
15882
15883//>>label: Tabs
15884//>>group: Widgets
15885//>>description: Transforms a set of container elements into a tab structure.
15886//>>docs: http://api.jqueryui.com/tabs/
15887//>>demos: http://jqueryui.com/tabs/
15888//>>css.structure: ../themes/base/core.css
15889//>>css.structure: ../themes/base/tabs.css
15890//>>css.theme: ../themes/base/theme.css
15891
15892(function( factory ) {
15893	if ( typeof define === "function" && define.amd ) {
15894
15895		// AMD. Register as an anonymous module.
15896		define([
15897			"jquery",
15898			"./core",
15899			"./widget"
15900		], factory );
15901	} else {
15902
15903		// Browser globals
15904		factory( jQuery );
15905	}
15906}(function( $ ) {
15907
15908return $.widget( "ui.tabs", {
15909	version: "@VERSION",
15910	delay: 300,
15911	options: {
15912		active: null,
15913		classes: {
15914			"ui-tabs": "ui-corner-all",
15915			"ui-tabs-nav": "ui-corner-all",
15916			"ui-tab": "ui-corner-top",
15917			"ui-tabs-panel": "ui-corner-bottom"
15918		},
15919		collapsible: false,
15920		event: "click",
15921		heightStyle: "content",
15922		hide: null,
15923		show: null,
15924
15925		// callbacks
15926		activate: null,
15927		beforeActivate: null,
15928		beforeLoad: null,
15929		load: null
15930	},
15931
15932	_isLocal: (function() {
15933		var rhash = /#.*$/;
15934
15935		return function( anchor ) {
15936			var anchorUrl, locationUrl;
15937
15938			anchorUrl = anchor.href.replace( rhash, "" );
15939			locationUrl = location.href.replace( rhash, "" );
15940
15941			// decoding may throw an error if the URL isn't UTF-8 (#9518)
15942			try {
15943				anchorUrl = decodeURIComponent( anchorUrl );
15944			} catch ( error ) {}
15945			try {
15946				locationUrl = decodeURIComponent( locationUrl );
15947			} catch ( error ) {}
15948
15949			return anchor.hash.length > 1 && anchorUrl === locationUrl;
15950		};
15951	})(),
15952
15953	_create: function() {
15954		var that = this,
15955			options = this.options;
15956
15957		this.running = false;
15958
15959		this._addClass( "ui-tabs", "ui-widget ui-widget-content" );
15960		this._toggleClass( "ui-tabs-collapsible", null, options.collapsible );
15961
15962		this._processTabs();
15963		options.active = this._initialActive();
15964
15965		// Take disabling tabs via class attribute from HTML
15966		// into account and update option properly.
15967		if ( $.isArray( options.disabled ) ) {
15968			options.disabled = $.unique( options.disabled.concat(
15969				$.map( this.tabs.filter( ".ui-state-disabled" ), function( li ) {
15970					return that.tabs.index( li );
15971				})
15972			) ).sort();
15973		}
15974
15975		// check for length avoids error when initializing empty list
15976		if ( this.options.active !== false && this.anchors.length ) {
15977			this.active = this._findActive( options.active );
15978		} else {
15979			this.active = $();
15980		}
15981
15982		this._refresh();
15983
15984		if ( this.active.length ) {
15985			this.load( options.active );
15986		}
15987	},
15988
15989	_initialActive: function() {
15990		var active = this.options.active,
15991			collapsible = this.options.collapsible,
15992			locationHash = location.hash.substring( 1 );
15993
15994		if ( active === null ) {
15995			// check the fragment identifier in the URL
15996			if ( locationHash ) {
15997				this.tabs.each(function( i, tab ) {
15998					if ( $( tab ).attr( "aria-controls" ) === locationHash ) {
15999						active = i;
16000						return false;
vendor: 5,218 bytes, lines 16001-16199
16001					}
16002				});
16003			}
16004
16005			// check for a tab marked active via a class
16006			if ( active === null ) {
16007				active = this.tabs.index( this.tabs.filter( ".ui-tabs-active" ) );
16008			}
16009
16010			// no active tab, set to false
16011			if ( active === null || active === -1 ) {
16012				active = this.tabs.length ? 0 : false;
16013			}
16014		}
16015
16016		// handle numbers: negative, out of range
16017		if ( active !== false ) {
16018			active = this.tabs.index( this.tabs.eq( active ) );
16019			if ( active === -1 ) {
16020				active = collapsible ? false : 0;
16021			}
16022		}
16023
16024		// don't allow collapsible: false and active: false
16025		if ( !collapsible && active === false && this.anchors.length ) {
16026			active = 0;
16027		}
16028
16029		return active;
16030	},
16031
16032	_getCreateEventData: function() {
16033		return {
16034			tab: this.active,
16035			panel: !this.active.length ? $() : this._getPanelForTab( this.active )
16036		};
16037	},
16038
16039	_tabKeydown: function( event ) {
16040		var focusedTab = $( $.ui.safeActiveElement( this.document[0] ) ).closest( "li" ),
16041			selectedIndex = this.tabs.index( focusedTab ),
16042			goingForward = true;
16043
16044		if ( this._handlePageNav( event ) ) {
16045			return;
16046		}
16047
16048		switch ( event.keyCode ) {
16049			case $.ui.keyCode.RIGHT:
16050			case $.ui.keyCode.DOWN:
16051				selectedIndex++;
16052				break;
16053			case $.ui.keyCode.UP:
16054			case $.ui.keyCode.LEFT:
16055				goingForward = false;
16056				selectedIndex--;
16057				break;
16058			case $.ui.keyCode.END:
16059				selectedIndex = this.anchors.length - 1;
16060				break;
16061			case $.ui.keyCode.HOME:
16062				selectedIndex = 0;
16063				break;
16064			case $.ui.keyCode.SPACE:
16065				// Activate only, no collapsing
16066				event.preventDefault();
16067				clearTimeout( this.activating );
16068				this._activate( selectedIndex );
16069				return;
16070			case $.ui.keyCode.ENTER:
16071				// Toggle (cancel delayed activation, allow collapsing)
16072				event.preventDefault();
16073				clearTimeout( this.activating );
16074				// Determine if we should collapse or activate
16075				this._activate( selectedIndex === this.options.active ? false : selectedIndex );
16076				return;
16077			default:
16078				return;
16079		}
16080
16081		// Focus the appropriate tab, based on which key was pressed
16082		event.preventDefault();
16083		clearTimeout( this.activating );
16084		selectedIndex = this._focusNextTab( selectedIndex, goingForward );
16085
16086		// Navigating with control/command key will prevent automatic activation
16087		if ( !event.ctrlKey && !event.metaKey ) {
16088
16089			// Update aria-selected immediately so that AT think the tab is already selected.
16090			// Otherwise AT may confuse the user by stating that they need to activate the tab,
16091			// but the tab will already be activated by the time the announcement finishes.
16092			focusedTab.attr( "aria-selected", "false" );
16093			this.tabs.eq( selectedIndex ).attr( "aria-selected", "true" );
16094
16095			this.activating = this._delay(function() {
16096				this.option( "active", selectedIndex );
16097			}, this.delay );
16098		}
16099	},
16100
16101	_panelKeydown: function( event ) {
16102		if ( this._handlePageNav( event ) ) {
16103			return;
16104		}
16105
16106		// Ctrl+up moves focus to the current tab
16107		if ( event.ctrlKey && event.keyCode === $.ui.keyCode.UP ) {
16108			event.preventDefault();
16109			this.active.focus();
16110		}
16111	},
16112
16113	// Alt+page up/down moves focus to the previous/next tab (and activates)
16114	_handlePageNav: function( event ) {
16115		if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_UP ) {
16116			this._activate( this._focusNextTab( this.options.active - 1, false ) );
16117			return true;
16118		}
16119		if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_DOWN ) {
16120			this._activate( this._focusNextTab( this.options.active + 1, true ) );
16121			return true;
16122		}
16123	},
16124
16125	_findNextTab: function( index, goingForward ) {
16126		var lastTabIndex = this.tabs.length - 1;
16127
16128		function constrain() {
16129			if ( index > lastTabIndex ) {
16130				index = 0;
16131			}
16132			if ( index < 0 ) {
16133				index = lastTabIndex;
16134			}
16135			return index;
16136		}
16137
16138		while ( $.inArray( constrain(), this.options.disabled ) !== -1 ) {
16139			index = goingForward ? index + 1 : index - 1;
16140		}
16141
16142		return index;
16143	},
16144
16145	_focusNextTab: function( index, goingForward ) {
16146		index = this._findNextTab( index, goingForward );
16147		this.tabs.eq( index ).focus();
16148		return index;
16149	},
16150
16151	_setOption: function( key, value ) {
16152		if ( key === "active" ) {
16153			// _activate() will handle invalid values and update this.options
16154			this._activate( value );
16155			return;
16156		}
16157
16158		if ( key === "disabled" ) {
16159			// don't use the widget factory's disabled handling
16160			this._setupDisabled( value );
16161			return;
16162		}
16163
16164		this._super( key, value);
16165
16166		if ( key === "collapsible" ) {
16167			this._toggleClass( "ui-tabs-collapsible", null, value );
16168
16169			// Setting collapsible: false while collapsed; open first panel
16170			if ( !value && this.options.active === false ) {
16171				this._activate( 0 );
16172			}
16173		}
16174
16175		if ( key === "event" ) {
16176			this._setupEvents( value );
16177		}
16178
16179		if ( key === "heightStyle" ) {
16180			this._setupHeightStyle( value );
16181		}
16182	},
16183
16184	_sanitizeSelector: function( hash ) {
16185		return hash ? hash.replace( /[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g, "\\$&" ) : "";
16186	},
16187
16188	refresh: function() {
16189		var options = this.options,
16190			lis = this.tablist.children( ":has(a[href])" );
16191
16192		// get disabled tabs from class attribute from HTML
16193		// this will get converted to a boolean if needed in _refresh()
16194		options.disabled = $.map( lis.filter( ".ui-state-disabled" ), function( tab ) {
16195			return lis.index( tab );
16196		});
16197
16198		this._processTabs();
16199
vendor: 4,101 bytes, lines 16200-16340
16200		// was collapsed or no tabs
16201		if ( options.active === false || !this.anchors.length ) {
16202			options.active = false;
16203			this.active = $();
16204		// was active, but active tab is gone
16205		} else if ( this.active.length && !$.contains( this.tablist[ 0 ], this.active[ 0 ] ) ) {
16206			// all remaining tabs are disabled
16207			if ( this.tabs.length === options.disabled.length ) {
16208				options.active = false;
16209				this.active = $();
16210			// activate previous tab
16211			} else {
16212				this._activate( this._findNextTab( Math.max( 0, options.active - 1 ), false ) );
16213			}
16214		// was active, active tab still exists
16215		} else {
16216			// make sure active index is correct
16217			options.active = this.tabs.index( this.active );
16218		}
16219
16220		this._refresh();
16221	},
16222
16223	_refresh: function() {
16224		this._setupDisabled( this.options.disabled );
16225		this._setupEvents( this.options.event );
16226		this._setupHeightStyle( this.options.heightStyle );
16227
16228		this.tabs.not( this.active ).attr({
16229			"aria-selected": "false",
16230			"aria-expanded": "false",
16231			tabIndex: -1
16232		});
16233		this.panels.not( this._getPanelForTab( this.active ) )
16234			.hide()
16235			.attr({
16236				"aria-hidden": "true"
16237			});
16238
16239		// Make sure one tab is in the tab order
16240		if ( !this.active.length ) {
16241			this.tabs.eq( 0 ).attr( "tabIndex", 0 );
16242		} else {
16243			this.active
16244				.attr({
16245					"aria-selected": "true",
16246					"aria-expanded": "true",
16247					tabIndex: 0
16248				});
16249			this._addClass( this.active, "ui-tabs-active", "ui-state-active" );
16250			this._getPanelForTab( this.active )
16251				.show()
16252				.attr({
16253					"aria-hidden": "false"
16254				});
16255		}
16256	},
16257
16258	_processTabs: function() {
16259		var that = this,
16260			prevTabs = this.tabs,
16261			prevAnchors = this.anchors,
16262			prevPanels = this.panels;
16263
16264		this.tablist = this._getList().attr( "role", "tablist" );
16265		this._addClass( this.tablist, "ui-tabs-nav",
16266			"ui-helper-reset ui-helper-clearfix ui-widget-header" );
16267
16268		// Prevent users from focusing disabled tabs via click
16269		this.tablist
16270			.delegate( "> li", "mousedown" + this.eventNamespace, function( event ) {
16271				if ( $( this ).is( ".ui-state-disabled" ) ) {
16272					event.preventDefault();
16273				}
16274			})
16275
16276			// support: IE <9
16277			// Preventing the default action in mousedown doesn't prevent IE
16278			// from focusing the element, so if the anchor gets focused, blur.
16279			// We don't have to worry about focusing the previously focused
16280			// element since clicking on a non-focusable element should focus
16281			// the body anyway.
16282			.delegate( ".ui-tabs-anchor", "focus" + this.eventNamespace, function() {
16283				if ( $( this ).closest( "li" ).is( ".ui-state-disabled" ) ) {
16284					this.blur();
16285				}
16286			});
16287
16288		this.tabs = this.tablist.find( "> li:has(a[href])" )
16289			.attr({
16290				role: "tab",
16291				tabIndex: -1
16292			});
16293		this._addClass( this.tabs, "ui-tab", "ui-state-default" );
16294
16295		this.anchors = this.tabs.map(function() {
16296				return $( "a", this )[ 0 ];
16297			})
16298			.attr({
16299				role: "presentation",
16300				tabIndex: -1
16301			});
16302		this._addClass( this.anchors, "ui-tabs-anchor" );
16303
16304		this.panels = $();
16305
16306		this.anchors.each(function( i, anchor ) {
16307			var selector, panel, panelId,
16308				anchorId = $( anchor ).uniqueId().attr( "id" ),
16309				tab = $( anchor ).closest( "li" ),
16310				originalAriaControls = tab.attr( "aria-controls" );
16311
16312			// inline tab
16313			if ( that._isLocal( anchor ) ) {
16314				selector = anchor.hash;
16315				panelId = selector.substring( 1 );
16316				panel = that.element.find( that._sanitizeSelector( selector ) );
16317			// remote tab
16318			} else {
16319				// If the tab doesn't already have aria-controls,
16320				// generate an id by using a throw-away element
16321				panelId = tab.attr( "aria-controls" ) || $( {} ).uniqueId()[ 0 ].id;
16322				selector = "#" + panelId;
16323				panel = that.element.find( selector );
16324				if ( !panel.length ) {
16325					panel = that._createPanel( panelId );
16326					panel.insertAfter( that.panels[ i - 1 ] || that.tablist );
16327				}
16328				panel.attr( "aria-live", "polite" );
16329			}
16330
16331			if ( panel.length) {
16332				that.panels = that.panels.add( panel );
16333			}
16334			if ( originalAriaControls ) {
16335				tab.data( "ui-tabs-aria-controls", originalAriaControls );
16336			}
16337			tab.attr({
16338				"aria-controls": panelId,
16339				"aria-labelledby": anchorId
16340			});
vendor: 9,124 bytes, lines 16341-16698
16341			panel.attr( "aria-labelledby", anchorId );
16342		});
16343
16344		this.panels.attr( "role", "tabpanel" );
16345		this._addClass( this.panels, "ui-tabs-panel", "ui-widget-content" );
16346
16347		// Avoid memory leaks (#10056)
16348		if ( prevTabs ) {
16349			this._off( prevTabs.not( this.tabs ) );
16350			this._off( prevAnchors.not( this.anchors ) );
16351			this._off( prevPanels.not( this.panels ) );
16352		}
16353	},
16354
16355	// allow overriding how to find the list for rare usage scenarios (#7715)
16356	_getList: function() {
16357		return this.tablist || this.element.find( "ol,ul" ).eq( 0 );
16358	},
16359
16360	_createPanel: function( id ) {
16361		return $( "<div>" )
16362			.attr( "id", id )
16363			.data( "ui-tabs-destroy", true );
16364	},
16365
16366	_setupDisabled: function( disabled ) {
16367		var currentItem, li, i;
16368
16369		if ( $.isArray( disabled ) ) {
16370			if ( !disabled.length ) {
16371				disabled = false;
16372			} else if ( disabled.length === this.anchors.length ) {
16373				disabled = true;
16374			}
16375		}
16376
16377		// disable tabs
16378		for ( i = 0; ( li = this.tabs[ i ] ); i++ ) {
16379			currentItem = $( li );
16380			if ( disabled === true || $.inArray( i, disabled ) !== -1 ) {
16381				currentItem.attr( "aria-disabled", "true" );
16382				this._addClass( currentItem, null, "ui-state-disabled" );
16383			} else {
16384				currentItem.removeAttr( "aria-disabled" );
16385				this._removeClass( currentItem, null, "ui-state-disabled" );
16386			}
16387		}
16388
16389		this.options.disabled = disabled;
16390	},
16391
16392	_setupEvents: function( event ) {
16393		var events = {};
16394		if ( event ) {
16395			$.each( event.split(" "), function( index, eventName ) {
16396				events[ eventName ] = "_eventHandler";
16397			});
16398		}
16399
16400		this._off( this.anchors.add( this.tabs ).add( this.panels ) );
16401		// Always prevent the default action, even when disabled
16402		this._on( true, this.anchors, {
16403			click: function( event ) {
16404				event.preventDefault();
16405			}
16406		});
16407		this._on( this.anchors, events );
16408		this._on( this.tabs, { keydown: "_tabKeydown" } );
16409		this._on( this.panels, { keydown: "_panelKeydown" } );
16410
16411		this._focusable( this.tabs );
16412		this._hoverable( this.tabs );
16413	},
16414
16415	_setupHeightStyle: function( heightStyle ) {
16416		var maxHeight,
16417			parent = this.element.parent();
16418
16419		if ( heightStyle === "fill" ) {
16420			maxHeight = parent.height();
16421			maxHeight -= this.element.outerHeight() - this.element.height();
16422
16423			this.element.siblings( ":visible" ).each(function() {
16424				var elem = $( this ),
16425					position = elem.css( "position" );
16426
16427				if ( position === "absolute" || position === "fixed" ) {
16428					return;
16429				}
16430				maxHeight -= elem.outerHeight( true );
16431			});
16432
16433			this.element.children().not( this.panels ).each(function() {
16434				maxHeight -= $( this ).outerHeight( true );
16435			});
16436
16437			this.panels.each(function() {
16438				$( this ).height( Math.max( 0, maxHeight -
16439					$( this ).innerHeight() + $( this ).height() ) );
16440			})
16441			.css( "overflow", "auto" );
16442		} else if ( heightStyle === "auto" ) {
16443			maxHeight = 0;
16444			this.panels.each(function() {
16445				maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() );
16446			}).height( maxHeight );
16447		}
16448	},
16449
16450	_eventHandler: function( event ) {
16451		var options = this.options,
16452			active = this.active,
16453			anchor = $( event.currentTarget ),
16454			tab = anchor.closest( "li" ),
16455			clickedIsActive = tab[ 0 ] === active[ 0 ],
16456			collapsing = clickedIsActive && options.collapsible,
16457			toShow = collapsing ? $() : this._getPanelForTab( tab ),
16458			toHide = !active.length ? $() : this._getPanelForTab( active ),
16459			eventData = {
16460				oldTab: active,
16461				oldPanel: toHide,
16462				newTab: collapsing ? $() : tab,
16463				newPanel: toShow
16464			};
16465
16466		event.preventDefault();
16467
16468		if ( tab.hasClass( "ui-state-disabled" ) ||
16469				// tab is already loading
16470				tab.hasClass( "ui-tabs-loading" ) ||
16471				// can't switch durning an animation
16472				this.running ||
16473				// click on active header, but not collapsible
16474				( clickedIsActive && !options.collapsible ) ||
16475				// allow canceling activation
16476				( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
16477			return;
16478		}
16479
16480		options.active = collapsing ? false : this.tabs.index( tab );
16481
16482		this.active = clickedIsActive ? $() : tab;
16483		if ( this.xhr ) {
16484			this.xhr.abort();
16485		}
16486
16487		if ( !toHide.length && !toShow.length ) {
16488			$.error( "jQuery UI Tabs: Mismatching fragment identifier." );
16489		}
16490
16491		if ( toShow.length ) {
16492			this.load( this.tabs.index( tab ), event );
16493		}
16494		this._toggle( event, eventData );
16495	},
16496
16497	// handles show/hide for selecting tabs
16498	_toggle: function( event, eventData ) {
16499		var that = this,
16500			toShow = eventData.newPanel,
16501			toHide = eventData.oldPanel;
16502
16503		this.running = true;
16504
16505		function complete() {
16506			that.running = false;
16507			that._trigger( "activate", event, eventData );
16508		}
16509
16510		function show() {
16511			that._addClass( eventData.newTab.closest( "li" ), "ui-tabs-active", "ui-state-active" );
16512
16513			if ( toShow.length && that.options.show ) {
16514				that._show( toShow, that.options.show, complete );
16515			} else {
16516				toShow.show();
16517				complete();
16518			}
16519		}
16520
16521		// start out by hiding, then showing, then completing
16522		if ( toHide.length && this.options.hide ) {
16523			this._hide( toHide, this.options.hide, function() {
16524				that._removeClass( eventData.oldTab.closest( "li" ),
16525					"ui-tabs-active", "ui-state-active" );
16526				show();
16527			});
16528		} else {
16529			this._removeClass( eventData.oldTab.closest( "li" ),
16530				"ui-tabs-active", "ui-state-active" );
16531			toHide.hide();
16532			show();
16533		}
16534
16535		toHide.attr( "aria-hidden", "true" );
16536		eventData.oldTab.attr({
16537			"aria-selected": "false",
16538			"aria-expanded": "false"
16539		});
16540		// If we're switching tabs, remove the old tab from the tab order.
16541		// If we're opening from collapsed state, remove the previous tab from the tab order.
16542		// If we're collapsing, then keep the collapsing tab in the tab order.
16543		if ( toShow.length && toHide.length ) {
16544			eventData.oldTab.attr( "tabIndex", -1 );
16545		} else if ( toShow.length ) {
16546			this.tabs.filter(function() {
16547				return $( this ).attr( "tabIndex" ) === 0;
16548			})
16549			.attr( "tabIndex", -1 );
16550		}
16551
16552		toShow.attr( "aria-hidden", "false" );
16553		eventData.newTab.attr({
16554			"aria-selected": "true",
16555			"aria-expanded": "true",
16556			tabIndex: 0
16557		});
16558	},
16559
16560	_activate: function( index ) {
16561		var anchor,
16562			active = this._findActive( index );
16563
16564		// trying to activate the already active panel
16565		if ( active[ 0 ] === this.active[ 0 ] ) {
16566			return;
16567		}
16568
16569		// trying to collapse, simulate a click on the current active header
16570		if ( !active.length ) {
16571			active = this.active;
16572		}
16573
16574		anchor = active.find( ".ui-tabs-anchor" )[ 0 ];
16575		this._eventHandler({
16576			target: anchor,
16577			currentTarget: anchor,
16578			preventDefault: $.noop
16579		});
16580	},
16581
16582	_findActive: function( index ) {
16583		return index === false ? $() : this.tabs.eq( index );
16584	},
16585
16586	_getIndex: function( index ) {
16587		// meta-function to give users option to provide a href string instead of a numerical index.
16588		if ( typeof index === "string" ) {
16589			index = this.anchors.index( this.anchors.filter( "[href$='" + index + "']" ) );
16590		}
16591
16592		return index;
16593	},
16594
16595	_destroy: function() {
16596		if ( this.xhr ) {
16597			this.xhr.abort();
16598		}
16599
16600		this.tablist
16601			.removeAttr( "role" )
16602			.unbind( this.eventNamespace );
16603
16604		this.anchors
16605			.removeAttr( "role" )
16606			.removeAttr( "tabIndex" )
16607			.removeUniqueId();
16608
16609		this.tabs.add( this.panels ).each(function() {
16610			if ( $.data( this, "ui-tabs-destroy" ) ) {
16611				$( this ).remove();
16612			} else {
16613				$( this )
16614					.removeAttr( "tabIndex" )
16615					.removeAttr( "aria-live" )
16616					.removeAttr( "aria-busy" )
16617					.removeAttr( "aria-selected" )
16618					.removeAttr( "aria-labelledby" )
16619					.removeAttr( "aria-hidden" )
16620					.removeAttr( "aria-expanded" )
16621					.removeAttr( "role" );
16622			}
16623		});
16624
16625		this.tabs.each(function() {
16626			var li = $( this ),
16627				prev = li.data( "ui-tabs-aria-controls" );
16628			if ( prev ) {
16629				li
16630					.attr( "aria-controls", prev )
16631					.removeData( "ui-tabs-aria-controls" );
16632			} else {
16633				li.removeAttr( "aria-controls" );
16634			}
16635		});
16636
16637		this.panels.show();
16638
16639		if ( this.options.heightStyle !== "content" ) {
16640			this.panels.css( "height", "" );
16641		}
16642	},
16643
16644	enable: function( index ) {
16645		var disabled = this.options.disabled;
16646		if ( disabled === false ) {
16647			return;
16648		}
16649
16650		if ( index === undefined ) {
16651			disabled = false;
16652		} else {
16653			index = this._getIndex( index );
16654			if ( $.isArray( disabled ) ) {
16655				disabled = $.map( disabled, function( num ) {
16656					return num !== index ? num : null;
16657				});
16658			} else {
16659				disabled = $.map( this.tabs, function( li, num ) {
16660					return num !== index ? num : null;
16661				});
16662			}
16663		}
16664		this._setupDisabled( disabled );
16665	},
16666
16667	disable: function( index ) {
16668		var disabled = this.options.disabled;
16669		if ( disabled === true ) {
16670			return;
16671		}
16672
16673		if ( index === undefined ) {
16674			disabled = true;
16675		} else {
16676			index = this._getIndex( index );
16677			if ( $.inArray( index, disabled ) !== -1 ) {
16678				return;
16679			}
16680			if ( $.isArray( disabled ) ) {
16681				disabled = $.merge( [ index ], disabled ).sort();
16682			} else {
16683				disabled = [ index ];
16684			}
16685		}
16686		this._setupDisabled( disabled );
16687	},
16688
16689	load: function( index, event ) {
16690		index = this._getIndex( index );
16691		var that = this,
16692			tab = this.tabs.eq( index ),
16693			anchor = tab.find( ".ui-tabs-anchor" ),
16694			panel = this._getPanelForTab( tab ),
16695			eventData = {
16696				tab: tab,
16697				panel: panel
16698			},
vendor: 4,230 bytes, lines 16699-16869
16699			complete = function( jqXHR, status ) {
16700				if ( status === "abort" ) {
16701					that.panels.stop( false, true );
16702				}
16703
16704				that._removeClass( tab, "ui-tabs-loading" );
16705				panel.removeAttr( "aria-busy" );
16706
16707				if ( jqXHR === that.xhr ) {
16708					delete that.xhr;
16709				}
16710			};
16711
16712		// not remote
16713		if ( this._isLocal( anchor[ 0 ] ) ) {
16714			return;
16715		}
16716
16717		this.xhr = $.ajax( this._ajaxSettings( anchor, event, eventData ) );
16718
16719		// support: jQuery <1.8
16720		// jQuery <1.8 returns false if the request is canceled in beforeSend,
16721		// but as of 1.8, $.ajax() always returns a jqXHR object.
16722		if ( this.xhr && this.xhr.statusText !== "canceled" ) {
16723			this._addClass( tab, "ui-tabs-loading" );
16724			panel.attr( "aria-busy", "true" );
16725
16726			this.xhr
16727				.done(function( response, status, jqXHR ) {
16728					// support: jQuery <1.8
16729					// http://bugs.jquery.com/ticket/11778
16730					setTimeout(function() {
16731						panel.html( response );
16732						that._trigger( "load", event, eventData );
16733
16734						complete( jqXHR, status );
16735					}, 1 );
16736				})
16737				.fail(function( jqXHR, status ) {
16738					// support: jQuery <1.8
16739					// http://bugs.jquery.com/ticket/11778
16740					setTimeout(function() {
16741						complete( jqXHR, status );
16742					}, 1 );
16743				});
16744		}
16745	},
16746
16747	_ajaxSettings: function( anchor, event, eventData ) {
16748		var that = this;
16749		return {
16750			url: anchor.attr( "href" ),
16751			beforeSend: function( jqXHR, settings ) {
16752				return that._trigger( "beforeLoad", event,
16753					$.extend( { jqXHR: jqXHR, ajaxSettings: settings }, eventData ) );
16754			}
16755		};
16756	},
16757
16758	_getPanelForTab: function( tab ) {
16759		var id = $( tab ).attr( "aria-controls" );
16760		return this.element.find( this._sanitizeSelector( "#" + id ) );
16761	}
16762});
16763
16764}));
16765
16766//>>label: Tooltip
16767//>>group: Widgets
16768//>>description: Shows additional information for any element on hover or focus.
16769//>>docs: http://api.jqueryui.com/tooltip/
16770//>>demos: http://jqueryui.com/tooltip/
16771//>>css.structure: ../themes/base/core.css
16772//>>css.structure: ../themes/base/tooltip.css
16773//>>css.theme: ../themes/base/theme.css
16774
16775(function( factory ) {
16776	if ( typeof define === "function" && define.amd ) {
16777
16778		// AMD. Register as an anonymous module.
16779		define([
16780			"jquery",
16781			"./core",
16782			"./widget",
16783			"./position"
16784		], factory );
16785	} else {
16786
16787		// Browser globals
16788		factory( jQuery );
16789	}
16790}(function( $ ) {
16791
16792$.widget( "ui.tooltip", {
16793	version: "@VERSION",
16794	options: {
16795		classes: {
16796			"ui-tooltip": "ui-corner-all ui-widget-shadow"
16797		},
16798		content: function() {
16799			// support: IE<9, Opera in jQuery <1.7
16800			// .text() can't accept undefined, so coerce to a string
16801			var title = $( this ).attr( "title" ) || "";
16802			// Escape title, since we're going from an attribute to raw HTML
16803			return $( "<a>" ).text( title ).html();
16804		},
16805		hide: true,
16806		// Disabled elements have inconsistent behavior across browsers (#8661)
16807		items: "[title]:not([disabled])",
16808		position: {
16809			my: "left top+15",
16810			at: "left bottom",
16811			collision: "flipfit flip"
16812		},
16813		show: true,
16814		track: false,
16815
16816		// callbacks
16817		close: null,
16818		open: null
16819	},
16820
16821	_addDescribedBy: function( elem, id ) {
16822		var describedby = (elem.attr( "aria-describedby" ) || "").split( /\s+/ );
16823		describedby.push( id );
16824		elem
16825			.data( "ui-tooltip-id", id )
16826			.attr( "aria-describedby", $.trim( describedby.join( " " ) ) );
16827	},
16828
16829	_removeDescribedBy: function( elem ) {
16830		var id = elem.data( "ui-tooltip-id" ),
16831			describedby = (elem.attr( "aria-describedby" ) || "").split( /\s+/ ),
16832			index = $.inArray( id, describedby );
16833
16834		if ( index !== -1 ) {
16835			describedby.splice( index, 1 );
16836		}
16837
16838		elem.removeData( "ui-tooltip-id" );
16839		describedby = $.trim( describedby.join( " " ) );
16840		if ( describedby ) {
16841			elem.attr( "aria-describedby", describedby );
16842		} else {
16843			elem.removeAttr( "aria-describedby" );
16844		}
16845	},
16846
16847	_create: function() {
16848		this._on({
16849			mouseover: "open",
16850			focusin: "open"
16851		});
16852
16853		// IDs of generated tooltips, needed for destroy
16854		this.tooltips = {};
16855
16856		// IDs of parent tooltips where we removed the title attribute
16857		this.parents = {};
16858
16859		if ( this.options.disabled ) {
16860			this._disable();
16861		}
16862
16863		// Append the aria-live region so tooltips announce correctly
16864		this.liveRegion = $( "<div>" )
16865			.attr({
16866				role: "log",
16867				"aria-live": "assertive",
16868				"aria-relevant": "additions"
16869			})
vendor: 4,763 bytes, lines 16870-17031
16870			.appendTo( this.document[ 0 ].body );
16871		this._addClass( this.liveRegion, null, "ui-helper-hidden-accessible" );
16872	},
16873
16874	_setOption: function( key, value ) {
16875		var that = this;
16876
16877		if ( key === "disabled" ) {
16878			this[ value ? "_disable" : "_enable" ]();
16879			this.options[ key ] = value;
16880			// disable element style changes
16881			return;
16882		}
16883
16884		this._super( key, value );
16885
16886		if ( key === "content" ) {
16887			$.each( this.tooltips, function( id, tooltipData ) {
16888				that._updateContent( tooltipData.element );
16889			});
16890		}
16891	},
16892
16893	_disable: function() {
16894		var that = this;
16895
16896		// close open tooltips
16897		$.each( this.tooltips, function( id, tooltipData ) {
16898			var event = $.Event( "blur" );
16899			event.target = event.currentTarget = tooltipData.element[ 0 ];
16900			that.close( event, true );
16901		});
16902
16903		// remove title attributes to prevent native tooltips
16904		this.element.find( this.options.items ).addBack().each(function() {
16905			var element = $( this );
16906			if ( element.is( "[title]" ) ) {
16907				element
16908					.data( "ui-tooltip-title", element.attr( "title" ) )
16909					.removeAttr( "title" );
16910			}
16911		});
16912	},
16913
16914	_enable: function() {
16915		// restore title attributes
16916		this.element.find( this.options.items ).addBack().each(function() {
16917			var element = $( this );
16918			if ( element.data( "ui-tooltip-title" ) ) {
16919				element.attr( "title", element.data( "ui-tooltip-title" ) );
16920			}
16921		});
16922	},
16923
16924	open: function( event ) {
16925		var that = this,
16926			target = $( event ? event.target : this.element )
16927				// we need closest here due to mouseover bubbling,
16928				// but always pointing at the same event target
16929				.closest( this.options.items );
16930
16931		// No element to show a tooltip for or the tooltip is already open
16932		if ( !target.length || target.data( "ui-tooltip-id" ) ) {
16933			return;
16934		}
16935
16936		if ( target.attr( "title" ) ) {
16937			target.data( "ui-tooltip-title", target.attr( "title" ) );
16938		}
16939
16940		target.data( "ui-tooltip-open", true );
16941
16942		// kill parent tooltips, custom or native, for hover
16943		if ( event && event.type === "mouseover" ) {
16944			target.parents().each(function() {
16945				var parent = $( this ),
16946					blurEvent;
16947				if ( parent.data( "ui-tooltip-open" ) ) {
16948					blurEvent = $.Event( "blur" );
16949					blurEvent.target = blurEvent.currentTarget = this;
16950					that.close( blurEvent, true );
16951				}
16952				if ( parent.attr( "title" ) ) {
16953					parent.uniqueId();
16954					that.parents[ this.id ] = {
16955						element: this,
16956						title: parent.attr( "title" )
16957					};
16958					parent.attr( "title", "" );
16959				}
16960			});
16961		}
16962
16963		this._registerCloseHandlers( event, target );
16964		this._updateContent( target, event );
16965	},
16966
16967	_updateContent: function( target, event ) {
16968		var content,
16969			contentOption = this.options.content,
16970			that = this,
16971			eventType = event ? event.type : null;
16972
16973		if ( typeof contentOption === "string" || contentOption.nodeType ||
16974				contentOption.jquery ) {
16975			return this._open( event, target, contentOption );
16976		}
16977
16978		content = contentOption.call( target[0], function( response ) {
16979
16980			// IE may instantly serve a cached response for ajax requests
16981			// delay this call to _open so the other call to _open runs first
16982			that._delay(function() {
16983
16984				// Ignore async response if tooltip was closed already
16985				if ( !target.data( "ui-tooltip-open" ) ) {
16986					return;
16987				}
16988
16989				// jQuery creates a special event for focusin when it doesn't
16990				// exist natively. To improve performance, the native event
16991				// object is reused and the type is changed. Therefore, we can't
16992				// rely on the type being correct after the event finished
16993				// bubbling, so we set it back to the previous value. (#8740)
16994				if ( event ) {
16995					event.type = eventType;
16996				}
16997				this._open( event, target, response );
16998			});
16999		});
17000		if ( content ) {
17001			this._open( event, target, content );
17002		}
17003	},
17004
17005	_open: function( event, target, content ) {
17006		var tooltipData, tooltip, delayedShow, a11yContent,
17007			positionOption = $.extend( {}, this.options.position );
17008
17009		if ( !content ) {
17010			return;
17011		}
17012
17013		// Content can be updated multiple times. If the tooltip already
17014		// exists, then just update the content and bail.
17015		tooltipData = this._find( target );
17016		if ( tooltipData ) {
17017			tooltipData.tooltip.find( ".ui-tooltip-content" ).html( content );
17018			return;
17019		}
17020
17021		// if we have a title, clear it to prevent the native tooltip
17022		// we have to check first to avoid defining a title if none exists
17023		// (we don't want to cause an element to start matching [title])
17024		//
17025		// We use removeAttr only for key events, to allow IE to export the correct
17026		// accessible attributes. For mouse events, set to empty string to avoid
17027		// native tooltip showing up (happens only when removing inside mouseover).
17028		if ( target.is( "[title]" ) ) {
17029			if ( event && event.type === "mouseover" ) {
17030				target.attr( "title", "" );
17031			} else {
vendor: 4,420 bytes, lines 17032-17175
17032				target.removeAttr( "title" );
17033			}
17034		}
17035
17036		tooltipData = this._tooltip( target );
17037		tooltip = tooltipData.tooltip;
17038		this._addDescribedBy( target, tooltip.attr( "id" ) );
17039		tooltip.find( ".ui-tooltip-content" ).html( content );
17040
17041		// Support: Voiceover on OS X, JAWS on IE <= 9
17042		// JAWS announces deletions even when aria-relevant="additions"
17043		// Voiceover will sometimes re-read the entire log region's contents from the beginning
17044		this.liveRegion.children().hide();
17045		a11yContent = $( "<div>" ).html( tooltip.find( ".ui-tooltip-content" ).html() );
17046		a11yContent.removeAttr( "id" ).find( "[id]" ).removeAttr( "id" );
17047		a11yContent.appendTo( this.liveRegion );
17048
17049		function position( event ) {
17050			positionOption.of = event;
17051			if ( tooltip.is( ":hidden" ) ) {
17052				return;
17053			}
17054			tooltip.position( positionOption );
17055		}
17056		if ( this.options.track && event && /^mouse/.test( event.type ) ) {
17057			this._on( this.document, {
17058				mousemove: position
17059			});
17060			// trigger once to override element-relative positioning
17061			position( event );
17062		} else {
17063			tooltip.position( $.extend({
17064				of: target
17065			}, this.options.position ) );
17066		}
17067
17068		tooltip.hide();
17069
17070		this._show( tooltip, this.options.show );
17071		// Handle tracking tooltips that are shown with a delay (#8644). As soon
17072		// as the tooltip is visible, position the tooltip using the most recent
17073		// event.
17074		if ( this.options.show && this.options.show.delay ) {
17075			delayedShow = this.delayedShow = setInterval(function() {
17076				if ( tooltip.is( ":visible" ) ) {
17077					position( positionOption.of );
17078					clearInterval( delayedShow );
17079				}
17080			}, $.fx.interval );
17081		}
17082
17083		this._trigger( "open", event, { tooltip: tooltip } );
17084	},
17085
17086	_registerCloseHandlers: function( event, target ) {
17087		var events = {
17088			keyup: function( event ) {
17089				if ( event.keyCode === $.ui.keyCode.ESCAPE ) {
17090					var fakeEvent = $.Event(event);
17091					fakeEvent.currentTarget = target[0];
17092					this.close( fakeEvent, true );
17093				}
17094			}
17095		};
17096
17097		// Only bind remove handler for delegated targets. Non-delegated
17098		// tooltips will handle this in destroy.
17099		if ( target[ 0 ] !== this.element[ 0 ] ) {
17100			events.remove = function() {
17101				this._removeTooltip( this._find( target ).tooltip );
17102			};
17103		}
17104
17105		if ( !event || event.type === "mouseover" ) {
17106			events.mouseleave = "close";
17107		}
17108		if ( !event || event.type === "focusin" ) {
17109			events.focusout = "close";
17110		}
17111		this._on( true, target, events );
17112	},
17113
17114	close: function( event ) {
17115		var tooltip,
17116			that = this,
17117			target = $( event ? event.currentTarget : this.element ),
17118			tooltipData = this._find( target );
17119
17120		// The tooltip may already be closed
17121		if ( !tooltipData ) {
17122
17123			// We set ui-tooltip-open immediately upon open (in open()), but only set the
17124			// additional data once there's actually content to show (in _open()). So even if the
17125			// tooltip doesn't have full data, we always remove ui-tooltip-open in case we're in
17126			// the period between open() and _open().
17127			target.removeData( "ui-tooltip-open" );
17128			return;
17129		}
17130
17131		tooltip = tooltipData.tooltip;
17132
17133		// disabling closes the tooltip, so we need to track when we're closing
17134		// to avoid an infinite loop in case the tooltip becomes disabled on close
17135		if ( tooltipData.closing ) {
17136			return;
17137		}
17138
17139		// Clear the interval for delayed tracking tooltips
17140		clearInterval( this.delayedShow );
17141
17142		// only set title if we had one before (see comment in _open())
17143		// If the title attribute has changed since open(), don't restore
17144		if ( target.data( "ui-tooltip-title" ) && !target.attr( "title" ) ) {
17145			target.attr( "title", target.data( "ui-tooltip-title" ) );
17146		}
17147
17148		this._removeDescribedBy( target );
17149
17150		tooltipData.hiding = true;
17151		tooltip.stop( true );
17152		this._hide( tooltip, this.options.hide, function() {
17153			that._removeTooltip( $( this ) );
17154		});
17155
17156		target.removeData( "ui-tooltip-open" );
17157		this._off( target, "mouseleave focusout keyup" );
17158
17159		// Remove 'remove' binding only on delegated targets
17160		if ( target[ 0 ] !== this.element[ 0 ] ) {
17161			this._off( target, "remove" );
17162		}
17163		this._off( this.document, "mousemove" );
17164
17165		if ( event && event.type === "mouseleave" ) {
17166			$.each( this.parents, function( id, parent ) {
17167				$( parent.element ).attr( "title", parent.title );
17168				delete that.parents[ id ];
17169			});
17170		}
17171
17172		tooltipData.closing = true;
17173		this._trigger( "close", event, { tooltip: tooltip } );
17174		if ( !tooltipData.hiding ) {
17175			tooltipData.closing = false;
17176		}
17177	},
17178
17179	_tooltip: function( element ) {
17180		var tooltip = $( "<div>" ).attr( "role", "tooltip" ),
17181			content = $( "<div>" ).appendTo( tooltip ),
17182			id = tooltip.uniqueId().attr( "id" );
17183
17184		this._addClass( content, "ui-tooltip-content" );
17185		this._addClass( tooltip, "ui-tooltip", "ui-widget ui-widget-content" );
17186
17187		tooltip.appendTo( this.document[0].body );
17188
17189		return this.tooltips[ id ] = {
17190			element: element,
17191			tooltip: tooltip
17192		};
17193	},
17194
17195	_find: function( target ) {
17196		var id = target.data( "ui-tooltip-id" );
17197		return id ? this.tooltips[ id ] : null;
17198	},
17199
17200	_removeTooltip: function( tooltip ) {
17201		tooltip.remove();
17202		delete this.tooltips[ tooltip.attr( "id" ) ];
17203	},
17204
17205	_destroy: function() {
17206		var that = this;
17207
17208		// close open tooltips
17209		$.each( this.tooltips, function( id, tooltipData ) {
17210			// Delegate to close method to handle common cleanup
17211			var event = $.Event( "blur" ),
17212				element = tooltipData.element;
17213			event.target = event.currentTarget = element[ 0 ];
17214			that.close( event, true );
17215
17216			// Remove immediately; destroying an open tooltip doesn't use the
17217			// hide animation
17218			$( "#" + id ).remove();
17219
17220			// Restore the title
17221			if ( element.data( "ui-tooltip-title" ) ) {
17222				// If the title attribute has changed since open(), don't restore
17223				if ( !element.attr( "title" ) ) {
17224					element.attr( "title", element.data( "ui-tooltip-title" ) );
17225				}
17226				element.removeData( "ui-tooltip-title" );
17227			}
17228		});
17229		this.liveRegion.remove();
17230	}
17231});
17232
17233// DEPRECATED
17234// TODO: Switch return back to widget declaration at top of file when this is removed
17235if ( $.uiBackCompat !== false ) {
17236
17237	// Backcompat for tooltipClass option
17238	$.widget( "ui.tooltip", $.ui.tooltip, {
17239		options: {
17240			tooltipClass: null
17241		},
17242		_tooltip: function() {
17243			var tooltipData = this._superApply( arguments );
17244			if ( this.options.tooltipClass ) {
17245				tooltipData.tooltip.addClass( this.options.tooltipClass );
17246			}
17247			return tooltipData;
17248		}
17249	});
17250}
17251
17252return $.ui.tooltip;
17253
17254}));
17255
17256/*!
17257 * Bootstrap v3.3.2 (http://getbootstrap.com)
17258 * Copyright 2011-2015 Twitter, Inc.
17259 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
17260 */
17261if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.2",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;
vendor: 4,123 bytes, line 17261
17261e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.2",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active"));a&&this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.2",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});
vendor: 5,084 bytes, line 17261
17261if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&"show"==b&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a(this.options.trigger).filter('[href="#'+b.id+'"], [data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.2",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0,trigger:'[data-toggle="collapse"]'},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":a.extend({},e.data(),{trigger:this});c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){b&&3===b.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=c(d),f={relatedTarget:this};e.hasClass("open")&&(e.trigger(b=a.Event("hide.bs.dropdown",f)),b.isDefaultPrevented()||(d.attr("aria-expanded","fal
17261se"),e.removeClass("open").trigger("hidden.bs.dropdown",f)))}))}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.2",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a('<div class="dropdown-backdrop"/>').insertAfter(a(this)).on("click",b);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger("shown.bs.dropdown",h)}return!1}},g.prototype.keydown=function(b){if(/(38|40|27|32)/.test(b.which)&&!/input|textarea/i.test(b.target.tagName)){var d=a(this);if(b.preventDefault(),b.stopPropagation(),!d.is(".disabled, :disabled")){var e=c(d),g=e.hasClass("open");if(!g&&27!=b.which||g&&27==b.which)return 27==b.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.divider):visible a",i=e.find('[role="menu"]'+h+', [role="listbox"]'+h);if(i.length){var j=i.index(b.target);38==b.which&&j>0&&j--,40==b.which&&j<i.length-1&&j++,~j||(j=0),i.eq(j).trigger("focus")}}}};var h=a.fn.dropdown;a.fn.dropdown=d,a.fn.dropdown.Constructor=g,a.fn.dropdown.noConflict=function(){return a.fn.dropdown=h,this},a(document).on("click.bs.dropdown.data-api",b).on("click.bs.dropdown.data-api",".dropdown form",function(a){a.stopPropagation()}).on("click.bs.dropdown.data-api",f,g.prototype.toggle).on("keydown.bs.dropdown.data-api",f,g.prototype.keydown).on("keydown.bs.dropdown.data-api",'[role="menu"]',g.prototype.keydown).on("keydown.bs.dropdown.data-api",'[role="listbox"]',g.prototype.keydown)}(jQuery),+function(a){"use strict";function b(b,d){return this.each(function(){var e=a(this),f=e.data("bs.modal"),g=a.extend({},c.DEFAULTS,e.data(),"object"==typeof b&&b);f||e.data("bs.modal",f=new c(this,g)),"string"==typeof b?f[b](d):g.show&&f.show(d)})}var c=function(b,c){this.options=c,this.$body=a(document.body),this.$element=a(b),this.$backdrop=this.isShown=null,this.scrollbarWidth=0,this.options.remote&&this.$element.find(".modal-content").load(this.options.remote,a.proxy(function(){this.$element.trigger("loaded.bs.modal")},this))};c.VERSION="3.3.2",c.TRANSITION_DURATION=300,c.BACKDROP_TRANSITION_DURATION=150,c.DEFAULTS={backdrop:!0,keyboard:!0,show:!0},c.prototype.toggle=function(a){return this.isShown?this.hide():this.show(a)},c.prototype.show=function(b){var d=this,e=a.Event("show.bs.modal",{relatedTarget:b});this.$element.trigger(e),this.isShown||e.isDefaultPrevented()||(this.isShown=!0,this.checkScrollbar(),this.setScrollbar(),this.$body.addClass("modal-open"),this.escape(),this.resize(),this.$element.on("click.dismiss.bs.modal",'[data-dismiss="modal"]',a.proxy(this.hide,this)),this.backdrop(function(){var e=a.support.transition&&d.$element.hasClass("fade");d.$element.parent().length||d.$element.appendTo(d.$body),d.$element.show().scrollTop(0),d.options.backdrop&&d.adjustBackdrop(),d.adjustDialog(),e&&d.$element[0].offsetWidth,d.$element.addClass("in").attr("aria-hidden",!1),d.enforceFocus();var f=a.Event("shown.bs.modal",{relatedTarget:b});e?d.$element.find(".modal-dialog").one("bsTransitionEnd",function(){d.$element.trigger("focus").trigger(f)}).emulateTransitionEnd(c.TRANSITION_DURATION):d.$element.trigger("focus").trigger(f)}))},c.prototype.hide=function(b){b&&b.preventDefault(),b=a.Event("hide.bs.modal"),this.$element.trigger(b),this.isShown&&!b.isDefaultPrevented()&&(this.isShown=!1,this.escape(),this.resize(),a(document).off("focusin.bs.modal"),this.$element.removeClass("in").attr("aria-hidden",!0).off("click.dismiss.bs.modal"),a.support.transition&&this.$element.hasClass("fade")?this.$element.one("bsTransitionEnd",a.proxy(this.hideModal,this)).emulateTransitionEnd(c.TRANSITION_DURATION):this.hideModal())},c.prototype.enforceFocus=function(){a(document).off("focusin.bs.modal").on("focusin.bs.modal",a.proxy(function(a){this.$element[0]===a.target||this.$element.has(a.target).length||this.$element.trigger("focus")},this))},c.prototype.escape=function(){this.isShown&&this.options.keyboard?this.$element.on("key
17261down.dismiss.bs.modal",a.proxy(function(a){27==a.which&&this.hide()},this)):this.isShown||this.$element.off("keydown.dismiss.bs.modal")},c.prototype.resize=function(){this.isShown?a(window).on("resize.bs.modal",a.proxy(this.handleUpdate,this)):a(window).off("resize.bs.modal")},c.prototype.hideModal=function(){var a=this;this.$element.hide(),this.backdrop(function(){a.$body.removeClass("modal-open"),a.resetAdjustments(),a.resetScrollbar(),a.$element.trigger("hidden.bs.modal")})},c.prototype.removeBackdrop=function(){this.$backdrop&&this.$backdrop.remove(),this.$backdrop=null},c.prototype.backdrop=function(b){var d=this,e=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var f=a.support.transition&&e;if(this.$backdrop=a('<div class="modal-backdrop '+e+'" />').prependTo(this.$element).on("click.dismiss.bs.modal",a.proxy(function(a){a.target===a.currentTarget&&("static"==this.options.backdrop?this.$element[0].focus.call(this.$element[0]):this.hide.call(this))},this)),f&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),!b)return;f?this.$backdrop.one("bsTransitionEnd",b).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION):b()}else if(!this.isShown&&this.$backdrop){this.$backdrop.removeClass("in");var g=function(){d.removeBackdrop(),b&&b()};a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one("bsTransitionEnd",g).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION):g()}else b&&b()},c.prototype.handleUpdate=function(){this.options.backdrop&&this.adjustBackdrop(),this.adjustDialog()},c.prototype.adjustBackdrop=function(){this.$backdrop.css("height",0).css("height",this.$element[0].scrollHeight)},c.prototype.adjustDialog=function(){var a=this.$element[0].scrollHeight>document.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){this.bodyIsOverflowing=document.body.scrollHeight>document.documentElement.clientHeight,this.scrollbarWidth=this.measureScrollbar()},c.prototype.setScrollbar=function(){var a=parseInt(this.$body.css("padding-right")||0,10);this.bodyIsOverflowing&&this.$body.css("padding-right",a+this.scrollbarWidth)},c.prototype.resetScrollbar=function(){this.$body.css("padding-right","")},c.prototype.measureScrollbar=function(){var a=document.createElement("div");a.className="modal-scrollbar-measure",this.$body.append(a);var b=a.offsetWidth-a.clientWidth;return this.$body[0].removeChild(a),b};var d=a.fn.modal;a.fn.modal=b,a.fn.modal.Constructor=c,a.fn.modal.noConflict=function(){return a.fn.modal=d,this},a(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',function(c){var d=a(this),e=d.attr("href"),f=a(d.attr("data-target")||e&&e.replace(/.*(?=#[^\s]+$)/,"")),g=f.data("bs.modal")?"toggle":a.extend({remote:!/#/.test(e)&&e},f.data(),d.data());d.is("a")&&c.preventDefault(),f.one("show.bs.modal",function(a){a.isDefaultPrevented()||f.one("hidden.bs.modal",function(){d.is(":visible")&&d.trigger("focus")})}),b.call(f,g,this)})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tooltip"),f="object"==typeof b&&b;(e||"destroy"!=b)&&(e||d.data("bs.tooltip",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.type=this.options=this.enabled=this.timeout=this.hoverState=this.$element=null,this.init("tooltip",a,b)};c.VERSION="3.3.2",c.TRANSITION_DURATION=150,c.DEFAULTS={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(this.options.viewport.selector||this.options.viewport);for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c&&c.$tip&&c.$tip.is(":visible")?void(c.hoverState="in"):(c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.options.container?a(this.options.container):this.$element.parent(),p=this.getPosition(o);h="bottom"==h&&k.bottom+m>p.bottom?"top":"top"==h&&k.top-m<p.top?"bottom":"right"==h&&k.right+l>p.width?"left":"left"==h&&k.left-l<p.left?"right":h,f.removeClass(n).addClass(h)}var q=this.getCalculatedOffset(h,k,l,m);this.applyPlacement(q,h);var r=function(){var a=e.hoverState;e.$element.trigger("shown.bs."+e.type),e.hoverState=null,"out"==a&&e.leave(e)};a.support.transition&&this.$tip.hasClass("fade")?f.one("bsTransitionEnd",r).emulateTransitionEnd(c.TRANSITION_DURATION):r()}},c.prototype.applyPlacement=function(b,c){var d=this.tip(),e=d[0].offsetWidth,f=d[0].offsetHeight,g=parseInt(d.css("margin-top"),10),h=
vendor: 9,757 bytes, lines 17261-17262
17261parseInt(d.css("margin-left"),10);isNaN(g)&&(g=0),isNaN(h)&&(h=0),b.top=b.top+g,b.left=b.left+h,a.offset.setOffset(d[0],a.extend({using:function(a){d.css({top:Math.round(a.top),left:Math.round(a.left)})}},b),0),d.addClass("in");var i=d[0].offsetWidth,j=d[0].offsetHeight;"top"==c&&j!=f&&(b.top=b.top+f-j);var k=this.getViewportAdjustedDelta(c,b,i,j);k.left?b.left+=k.left:b.top+=k.top;var l=/top|bottom/.test(c),m=l?2*k.left-e+i:2*k.top-f+j,n=l?"offsetWidth":"offsetHeight";d.offset(b),this.replaceArrow(m,d[0][n],l)},c.prototype.replaceArrow=function(a,b,c){this.arrow().css(c?"left":"top",50*(1-a/b)+"%").css(c?"top":"left","")},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle();a.find(".tooltip-inner")[this.options.html?"html":"text"](b),a.removeClass("fade in top bottom left right")},c.prototype.hide=function(b){function d(){"in"!=e.hoverState&&f.detach(),e.$element.removeAttr("aria-describedby").trigger("hidden.bs."+e.type),b&&b()}var e=this,f=this.tip(),g=a.Event("hide.bs."+this.type);return this.$element.trigger(g),g.isDefaultPrevented()?void 0:(f.removeClass("in"),a.support.transition&&this.$tip.hasClass("fade")?f.one("bsTransitionEnd",d).emulateTransitionEnd(c.TRANSITION_DURATION):d(),this.hoverState=null,this)},c.prototype.fixTitle=function(){var a=this.$element;(a.attr("title")||"string"!=typeof a.attr("data-original-title"))&&a.attr("data-original-title",a.attr("title")||"").attr("title","")},c.prototype.hasContent=function(){return this.getTitle()},c.prototype.getPosition=function(b){b=b||this.$element;var c=b[0],d="BODY"==c.tagName,e=c.getBoundingClientRect();null==e.width&&(e=a.extend({},e,{width:e.right-e.left,height:e.bottom-e.top}));var f=d?{top:0,left:0}:b.offset(),g={scroll:d?document.documentElement.scrollTop||document.body.scrollTop:b.scrollTop()},h=d?{width:a(window).width(),height:a(window).height()}:null;return a.extend({},e,g,h,f)},c.prototype.getCalculatedOffset=function(a,b,c,d){return"bottom"==a?{top:b.top+b.height,left:b.left+b.width/2-c/2}:"top"==a?{top:b.top-d,left:b.left+b.width/2-c/2}:"left"==a?{top:b.top+b.height/2-d/2,left:b.left-c}:{top:b.top+b.height/2-d/2,left:b.left+b.width}},c.prototype.getViewportAdjustedDelta=function(a,b,c,d){var e={top:0,left:0};if(!this.$viewport)return e;var f=this.options.viewport&&this.options.viewport.padding||0,g=this.getPosition(this.$viewport);if(/right|left/.test(a)){var h=b.top-f-g.scroll,i=b.top+f-g.scroll+d;h<g.top?e.top=g.top-h:i>g.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;j<g.left?e.left=g.left-j:k>g.width&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){return this.$tip=this.$tip||a(this.options.template)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type)})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||"destroy"!=b)&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.2",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:'<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},c.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){var e=a.proxy(this.process,this);this.$body=a("body"),this.$scrollElement=a(a(c).is("body")?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",e),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.2",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b="offset",c=0;a.isWindow(this.$scrollElement[0])||(b="position",c=this.$scrollElement.scrollTop()),this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight();var d=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[b]().top+c,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){d.offsets.push(this[0]),d.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b<e[0])return this.activeTarget=null,this.clear();for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,this.clear();var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate.bs.scrollspy")},b.prototype.clear=function(){a(this.selector).parentsUntil(this.options.target,".active").removeClass("active")};var d=a.fn.scrollspy;a.fn.scrollspy=c,a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=d,this},a(window).on("load.bs.scrollspy.data-api",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);c.call(b,b.data())})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new c(this)),"string"==typeof b&&e[b]()})}var c=function(b){this.element=a(b)};c.VERSION="3.3.2",c.TRANSITION_DURATION=150,c.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a"),f=a.Event("hide.bs.tab",{relatedTarget:b[0]}),g=a.Event("show.bs.tab",{relatedTarget:e[0]});if(e.trigger(f),b.trigger(g),!g.isDefaultPrevented()&&!f.isDefaultPrevented()){var h=a(d);this.activate(b.closest("li"),c),this.activate(h,h.parent(),function(){e.trigger({type:"hidden.bs.tab",relatedTarget:b[0]}),b.trigger({type:"shown.bs.tab",relatedTarget:e[0]})})}}},c.prototype.activate=function(b,d,e){function f(){g.removeClass("active").find("> .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()
17262}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;
vendor: 4,667 bytes, lines 17262-17325
17262e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=this.unpin=this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.2",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=a("body").height();"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery);
17263/**
17264 * @license AngularJS v1.4.7
17265 * (c) 2010-2015 Google, Inc. http://angularjs.org
17266 * License: MIT
17267 */
17268(function(window, document, undefined) {'use strict';
17269
17270/**
17271 * @description
17272 *
17273 * This object provides a utility for producing rich Error messages within
17274 * Angular. It can be called as follows:
17275 *
17276 * var exampleMinErr = minErr('example');
17277 * throw exampleMinErr('one', 'This {0} is {1}', foo, bar);
17278 *
17279 * The above creates an instance of minErr in the example namespace. The
17280 * resulting error will have a namespaced error code of example.one.  The
17281 * resulting error will replace {0} with the value of foo, and {1} with the
17282 * value of bar. The object is not restricted in the number of arguments it can
17283 * take.
17284 *
17285 * If fewer arguments are specified than necessary for interpolation, the extra
17286 * interpolation markers will be preserved in the final string.
17287 *
17288 * Since data will be parsed statically during a build step, some restrictions
17289 * are applied with respect to how minErr instances are created and called.
17290 * Instances should have names of the form namespaceMinErr for a minErr created
17291 * using minErr('namespace') . Error codes, namespaces and template strings
17292 * should all be static strings, not variables or general expressions.
17293 *
17294 * @param {string} module The namespace to use for the new minErr instance.
17295 * @param {function} ErrorConstructor Custom error constructor to be instantiated when returning
17296 *   error from returned function, for cases when a particular type of error is useful.
17297 * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance
17298 */
17299
17300function minErr(module, ErrorConstructor) {
17301  ErrorConstructor = ErrorConstructor || Error;
17302  return function() {
17303    var SKIP_INDEXES = 2;
17304
17305    var templateArgs = arguments,
17306      code = templateArgs[0],
17307      message = '[' + (module ? module + ':' : '') + code + '] ',
17308      template = templateArgs[1],
17309      paramPrefix, i;
17310
17311    message += template.replace(/\{\d+\}/g, function(match) {
17312      var index = +match.slice(1, -1),
17313        shiftedIndex = index + SKIP_INDEXES;
17314
17315      if (shiftedIndex < templateArgs.length) {
17316        return toDebugString(templateArgs[shiftedIndex]);
17317      }
17318
17319      return match;
17320    });
17321
17322    message += '\nhttp://errors.angularjs.org/1.4.7/' +
17323      (module ? module + '/' : '') + code;
17324
17325    for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
17326      message += paramPrefix + 'p' + (i - SKIP_INDEXES) + '=' +
17327        encodeURIComponent(toDebugString(templateArgs[i]));
17328    }
17329
17330    return new ErrorConstructor(message);
17331  };
17332}
17333
17334/* We need to tell jshint what variables are being exported */
17335/* global angular: true,
17336  msie: true,
17337  jqLite: true,
17338  jQuery: true,
17339  slice: true,
17340  splice: true,
17341  push: true,
17342  toString: true,
17343  ngMinErr: true,
17344  angularModule: true,
17345  uid: true,
17346  REGEX_STRING_REGEXP: true,
17347  VALIDITY_STATE_PROPERTY: true,
17348
17349  lowercase: true,
17350  uppercase: true,
17351  manualLowercase: true,
17352  manualUppercase: true,
17353  nodeName_: true,
17354  isArrayLike: true,
17355  forEach: true,
17356  forEachSorted: true,
17357  reverseParams: true,
17358  nextUid: true,
17359  setHashKey: true,
17360  extend: true,
17361  toInt: true,
17362  inherit: true,
17363  merge: true,
17364  noop: true,
17365  identity: true,
17366  valueFn: true,
17367  isUndefined: true,
17368  isDefined: true,
17369  isObject: true,
17370  isBlankObject: true,
17371  isString: true,
17372  isNumber: true,
17373  isDate: true,
17374  isArray: true,
17375  isFunction: true,
17376  isRegExp: true,
17377  isWindow: true,
17378  isScope: true,
17379  isFile: true,
17380  isFormData: true,
17381  isBlob: true,
17382  isBoolean: true,
17383  isPromiseLike: true,
17384  trim: true,
17385  escapeForRegexp: true,
17386  isElement: true,
17387  makeMap: true,
17388  includes: true,
17389  arrayRemove: true,
17390  copy: true,
17391  shallowCopy: true,
17392  equals: true,
17393  csp: true,
17394  jq: true,
17395  concat: true,
17396  sliceArgs: true,
17397  bind: true,
17398  toJsonReplacer: true,
17399  toJson: true,
17400  fromJson: true,
17401  convertTimezoneToLocal: true,
17402  timezoneToOffset: true,
17403  startingTag: true,
17404  tryDecodeURIComponent: true,
17405  parseKeyValue: true,
17406  toKeyValue: true,
17407  encodeUriSegment: true,
17408  encodeUriQuery: true,
17409  angularInit: true,
17410  bootstrap: true,
17411  getTestability: true,
17412  snake_case: true,
17413  bindJQuery: true,
17414  assertArg: true,
17415  assertArgFn: true,
17416  assertNotHasOwnProperty: true,
17417  getter: true,
17418  getBlockNodes: true,
17419  hasOwnProperty: true,
17420  createMap: true,
17421
17422  NODE_TYPE_ELEMENT: true,
17423  NODE_TYPE_ATTRIBUTE: true,
17424  NODE_TYPE_TEXT: true,
17425  NODE_TYPE_COMMENT: true,
17426  NODE_TYPE_DOCUMENT: true,
17427  NODE_TYPE_DOCUMENT_FRAGMENT: true,
17428*/
17429
17430////////////////////////////////////
17431
17432/**
17433 * @ngdoc module
17434 * @name ng
17435 * @module ng
17436 * @description
17437 *
17438 * # ng (core module)
17439 * The ng module is loaded by default when an AngularJS application is started. The module itself
17440 * contains the essential components for an AngularJS application to function. The table below
17441 * lists a high level breakdown of each of the services/factories, filters, directives and testing
17442 * components available within this core module.
17443 *
17444 * <div doc-module-components="ng"></div>
17445 */
17446
17447var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/;
17448
17449// The name of a form control's ValidityState property.
17450// This is used so that it's possible for internal tests to create mock ValidityStates.
17451var VALIDITY_STATE_PROPERTY = 'validity';
17452
17453/**
17454 * @ngdoc function
17455 * @name angular.lowercase
17456 * @module ng
17457 * @kind function
17458 *
17459 * @description Converts the specified string to lowercase.
17460 * @param {string} string String to be converted to lowercase.
17461 * @returns {string} Lowercased string.
17462 */
17463var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
17464var hasOwnProperty = Object.prototype.hasOwnProperty;
17465
17466/**
17467 * @ngdoc function
17468 * @name angular.uppercase
17469 * @module ng
17470 * @kind function
17471 *
17472 * @description Converts the specified string to uppercase.
17473 * @param {string} string String to be converted to uppercase.
17474 * @returns {string} Uppercased string.
17475 */
17476var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
17477
17478
17479var manualLowercase = function(s) {
17480  /* jshint bitwise: false */
17481  return isString(s)
17482      ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
17483      : s;
17484};
17485var manualUppercase = function(s) {
17486  /* jshint bitwise: false */
17487  return isString(s)
17488      ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
17489      : s;
17490};
17491
17492
17493// String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
17494// locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
17495// with correct but slower alternatives.
17496if ('i' !== 'I'.toLowerCase()) {
17497  lowercase = manualLowercase;
17498  uppercase = manualUppercase;
17499}
17500
17501
17502var
17503    msie,             // holds major version number for IE, or NaN if UA is not IE.
17504    jqLite,           // delay binding since jQuery could be loaded after us.
17505    jQuery,           // delay binding
17506    slice             = [].slice,
17507    splice            = [].splice,
17508    push              = [].push,
17509    toString          = Object.prototype.toString,
17510    getPrototypeOf    = Object.getPrototypeOf,
17511    ngMinErr          = minErr('ng'),
17512
17513    /** @name angular */
17514    angular           = window.angular || (window.angular = {}),
17515    angularModule,
17516    uid               = 0;
17517
17518/**
17519 * documentMode is an IE-only property
17520 * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
17521 */
17522msie = document.documentMode;
17523
17524
17525/**
17526 * @private
17527 * @param {*} obj
17528 * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,
17529 *                   String ...)
17530 */
17531function isArrayLike(obj) {
17532  if (obj == null || isWindow(obj)) {
17533    return false;
17534  }
17535
17536  // Support: iOS 8.2 (not reproducible in simulator)
17537  // "length" in obj used to prevent JIT error (gh-11508)
17538  var length = "length" in Object(obj) && obj.length;
17539
17540  if (obj.nodeType === NODE_TYPE_ELEMENT && length) {
17541    return true;
17542  }
17543
17544  return isString(obj) || isArray(obj) || length === 0 ||
17545         typeof length === 'number' && length > 0 && (length - 1) in obj;
17546}
17547
17548/**
17549 * @ngdoc function
17550 * @name angular.forEach
17551 * @module ng
17552 * @kind function
17553 *
17554 * @description
17555 * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
17556 * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value`
17557 * is the value of an object property or an array element, `key` is the object property key or
17558 * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional.
17559 *
17560 * It is worth noting that `.forEach` does not iterate over inherited properties because it filters
17561 * using the `hasOwnProperty` method.
17562 *
17563 * Unlike ES262's
17564 * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
17565 * Providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
17566 * return the value provided.
17567 *
17568   ```js
17569     var values = {name: 'misko', gender: 'male'};
17570     var log = [];
17571     angular.forEach(values, function(value, key) {
17572       this.push(key + ': ' + value);
17573     }, log);
17574     expect(log).toEqual(['name: misko', 'gender: male']);
17575   ```
17576 *
17577 * @param {Object|Array} obj Object to iterate over.
17578 * @param {Function} iterator Iterator function.
17579 * @param {Object=} context Object to become context (`this`) for the iterator function.
17580 * @returns {Object|Array} Reference to `obj`.
17581 */
17582
17583function forEach(obj, iterator, context) {
17584  var key, length;
17585  if (obj) {
17586    if (isFunction(obj)) {
17587      for (key in obj) {
17588        // Need to check if hasOwnProperty exists,
17589        // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
17590        if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
17591          iterator.call(context, obj[key], key, obj);
17592        }
17593      }
17594    } else if (isArray(obj) || isArrayLike(obj)) {
17595      var isPrimitive = typeof obj !== 'object';
17596      for (key = 0, length = obj.length; key < length; key++) {
17597        if (isPrimitive || key in obj) {
17598          iterator.call(context, obj[key], key, obj);
17599        }
17600      }
17601    } else if (obj.forEach && obj.forEach !== forEach) {
17602        obj.forEach(iterator, context, obj);
17603    } else if (isBlankObject(obj)) {
17604      // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
17605      for (key in obj) {
17606        iterator.call(context, obj[key], key, obj);
17607      }
17608    } else if (typeof obj.hasOwnProperty === 'function') {
17609      // Slow path for objects inheriting Object.prototype, hasOwnProperty check needed
17610      for (key in obj) {
17611        if (obj.hasOwnProperty(key)) {
17612          iterator.call(context, obj[key], key, obj);
17613        }
17614      }
17615    } else {
17616      // Slow path for objects which do not have a method `hasOwnProperty`
17617      for (key in obj) {
17618        if (hasOwnProperty.call(obj, key)) {
17619          iterator.call(context, obj[key], key, obj);
17620        }
17621      }
17622    }
17623  }
17624  return obj;
17625}
17626
17627function forEachSorted(obj, iterator, context) {
17628  var keys = Object.keys(obj).sort();
17629  for (var i = 0; i < keys.length; i++) {
17630    iterator.call(context, obj[keys[i]], keys[i]);
17631  }
17632  return keys;
17633}
17634
17635
17636/**
17637 * when using forEach the params are value, key, but it is often useful to have key, value.
17638 * @param {function(string, *)} iteratorFn
17639 * @returns {function(*, string)}
17640 */
17641function reverseParams(iteratorFn) {
17642  return function(value, key) { iteratorFn(key, value); };
17643}
17644
17645/**
17646 * A consistent way of creating unique IDs in angular.
17647 *
17648 * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before
17649 * we hit number precision issues in JavaScript.
17650 *
17651 * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M
17652 *
17653 * @returns {number}
vendor: 9,733 bytes, lines 17653-18077
17653 an unique alpha-numeric string
17654 */
17655function nextUid() {
17656  return ++uid;
17657}
17658
17659
17660/**
17661 * Set or clear the hashkey for an object.
17662 * @param obj object
17663 * @param h the hashkey (!truthy to delete the hashkey)
17664 */
17665function setHashKey(obj, h) {
17666  if (h) {
17667    obj.$$hashKey = h;
17668  } else {
17669    delete obj.$$hashKey;
17670  }
17671}
17672
17673
17674function baseExtend(dst, objs, deep) {
17675  var h = dst.$$hashKey;
17676
17677  for (var i = 0, ii = objs.length; i < ii; ++i) {
17678    var obj = objs[i];
17679    if (!isObject(obj) && !isFunction(obj)) continue;
17680    var keys = Object.keys(obj);
17681    for (var j = 0, jj = keys.length; j < jj; j++) {
17682      var key = keys[j];
17683      var src = obj[key];
17684
17685      if (deep && isObject(src)) {
17686        if (isDate(src)) {
17687          dst[key] = new Date(src.valueOf());
17688        } else if (isRegExp(src)) {
17689          dst[key] = new RegExp(src);
17690        } else {
17691          if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
17692          baseExtend(dst[key], [src], true);
17693        }
17694      } else {
17695        dst[key] = src;
17696      }
17697    }
17698  }
17699
17700  setHashKey(dst, h);
17701  return dst;
17702}
17703
17704/**
17705 * @ngdoc function
17706 * @name angular.extend
17707 * @module ng
17708 * @kind function
17709 *
17710 * @description
17711 * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
17712 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
17713 * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`.
17714 *
17715 * **Note:** Keep in mind that `angular.extend` does not support recursive merge (deep copy). Use
17716 * {@link angular.merge} for this.
17717 *
17718 * @param {Object} dst Destination object.
17719 * @param {...Object} src Source object(s).
17720 * @returns {Object} Reference to `dst`.
17721 */
17722function extend(dst) {
17723  return baseExtend(dst, slice.call(arguments, 1), false);
17724}
17725
17726
17727/**
17728* @ngdoc function
17729* @name angular.merge
17730* @module ng
17731* @kind function
17732*
17733* @description
17734* Deeply extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
17735* to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
17736* by passing an empty object as the target: `var object = angular.merge({}, object1, object2)`.
17737*
17738* Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source
17739* objects, performing a deep copy.
17740*
17741* @param {Object} dst Destination object.
17742* @param {...Object} src Source object(s).
17743* @returns {Object} Reference to `dst`.
17744*/
17745function merge(dst) {
17746  return baseExtend(dst, slice.call(arguments, 1), true);
17747}
17748
17749
17750
17751function toInt(str) {
17752  return parseInt(str, 10);
17753}
17754
17755
17756function inherit(parent, extra) {
17757  return extend(Object.create(parent), extra);
17758}
17759
17760/**
17761 * @ngdoc function
17762 * @name angular.noop
17763 * @module ng
17764 * @kind function
17765 *
17766 * @description
17767 * A function that performs no operations. This function can be useful when writing code in the
17768 * functional style.
17769   ```js
17770     function foo(callback) {
17771       var result = calculateResult();
17772       (callback || angular.noop)(result);
17773     }
17774   ```
17775 */
17776function noop() {}
17777noop.$inject = [];
17778
17779
17780/**
17781 * @ngdoc function
17782 * @name angular.identity
17783 * @module ng
17784 * @kind function
17785 *
17786 * @description
17787 * A function that returns its first argument. This function is useful when writing code in the
17788 * functional style.
17789 *
17790   ```js
17791     function transformer(transformationFn, value) {
17792       return (transformationFn || angular.identity)(value);
17793     };
17794   ```
17795  * @param {*} value to be returned.
17796  * @returns {*} the value passed in.
17797 */
17798function identity($) {return $;}
17799identity.$inject = [];
17800
17801
17802function valueFn(value) {return function() {return value;};}
17803
17804function hasCustomToString(obj) {
17805  return isFunction(obj.toString) && obj.toString !== Object.prototype.toString;
17806}
17807
17808
17809/**
17810 * @ngdoc function
17811 * @name angular.isUndefined
17812 * @module ng
17813 * @kind function
17814 *
17815 * @description
17816 * Determines if a reference is undefined.
17817 *
17818 * @param {*} value Reference to check.
17819 * @returns {boolean} True if `value` is undefined.
17820 */
17821function isUndefined(value) {return typeof value === 'undefined';}
17822
17823
17824/**
17825 * @ngdoc function
17826 * @name angular.isDefined
17827 * @module ng
17828 * @kind function
17829 *
17830 * @description
17831 * Determines if a reference is defined.
17832 *
17833 * @param {*} value Reference to check.
17834 * @returns {boolean} True if `value` is defined.
17835 */
17836function isDefined(value) {return typeof value !== 'undefined';}
17837
17838
17839/**
17840 * @ngdoc function
17841 * @name angular.isObject
17842 * @module ng
17843 * @kind function
17844 *
17845 * @description
17846 * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
17847 * considered to be objects. Note that JavaScript arrays are objects.
17848 *
17849 * @param {*} value Reference to check.
17850 * @returns {boolean} True if `value` is an `Object` but not `null`.
17851 */
17852function isObject(value) {
17853  // http://jsperf.com/isobject4
17854  return value !== null && typeof value === 'object';
17855}
17856
17857
17858/**
17859 * Determine if a value is an object with a null prototype
17860 *
17861 * @returns {boolean} True if `value` is an `Object` with a null prototype
17862 */
17863function isBlankObject(value) {
17864  return value !== null && typeof value === 'object' && !getPrototypeOf(value);
17865}
17866
17867
17868/**
17869 * @ngdoc function
17870 * @name angular.isString
17871 * @module ng
17872 * @kind function
17873 *
17874 * @description
17875 * Determines if a reference is a `String`.
17876 *
17877 * @param {*} value Reference to check.
17878 * @returns {boolean} True if `value` is a `String`.
17879 */
17880function isString(value) {return typeof value === 'string';}
17881
17882
17883/**
17884 * @ngdoc function
17885 * @name angular.isNumber
17886 * @module ng
17887 * @kind function
17888 *
17889 * @description
17890 * Determines if a reference is a `Number`.
17891 *
17892 * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`.
17893 *
17894 * If you wish to exclude these then you can use the native
17895 * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite)
17896 * method.
17897 *
17898 * @param {*} value Reference to check.
17899 * @returns {boolean} True if `value` is a `Number`.
17900 */
17901function isNumber(value) {return typeof value === 'number';}
17902
17903
17904/**
17905 * @ngdoc function
17906 * @name angular.isDate
17907 * @module ng
17908 * @kind function
17909 *
17910 * @description
17911 * Determines if a value is a date.
17912 *
17913 * @param {*} value Reference to check.
17914 * @returns {boolean} True if `value` is a `Date`.
17915 */
17916function isDate(value) {
17917  return toString.call(value) === '[object Date]';
17918}
17919
17920
17921/**
17922 * @ngdoc function
17923 * @name angular.isArray
17924 * @module ng
17925 * @kind function
17926 *
17927 * @description
17928 * Determines if a reference is an `Array`.
17929 *
17930 * @param {*} value Reference to check.
17931 * @returns {boolean} True if `value` is an `Array`.
17932 */
17933var isArray = Array.isArray;
17934
17935/**
17936 * @ngdoc function
17937 * @name angular.isFunction
17938 * @module ng
17939 * @kind function
17940 *
17941 * @description
17942 * Determines if a reference is a `Function`.
17943 *
17944 * @param {*} value Reference to check.
17945 * @returns {boolean} True if `value` is a `Function`.
17946 */
17947function isFunction(value) {return typeof value === 'function';}
17948
17949
17950/**
17951 * Determines if a value is a regular expression object.
17952 *
17953 * @private
17954 * @param {*} value Reference to check.
17955 * @returns {boolean} True if `value` is a `RegExp`.
17956 */
17957function isRegExp(value) {
17958  return toString.call(value) === '[object RegExp]';
17959}
17960
17961
17962/**
17963 * Checks if `obj` is a window object.
17964 *
17965 * @private
17966 * @param {*} obj Object to check
17967 * @returns {boolean} True if `obj` is a window obj.
17968 */
17969function isWindow(obj) {
17970  return obj && obj.window === obj;
17971}
17972
17973
17974function isScope(obj) {
17975  return obj && obj.$evalAsync && obj.$watch;
17976}
17977
17978
17979function isFile(obj) {
17980  return toString.call(obj) === '[object File]';
17981}
17982
17983
17984function isFormData(obj) {
17985  return toString.call(obj) === '[object FormData]';
17986}
17987
17988
17989function isBlob(obj) {
17990  return toString.call(obj) === '[object Blob]';
17991}
17992
17993
17994function isBoolean(value) {
17995  return typeof value === 'boolean';
17996}
17997
17998
17999function isPromiseLike(obj) {
18000  return obj && isFunction(obj.then);
18001}
18002
18003
18004var TYPED_ARRAY_REGEXP = /^\[object (Uint8(Clamped)?)|(Uint16)|(Uint32)|(Int8)|(Int16)|(Int32)|(Float(32)|(64))Array\]$/;
18005function isTypedArray(value) {
18006  return TYPED_ARRAY_REGEXP.test(toString.call(value));
18007}
18008
18009
18010var trim = function(value) {
18011  return isString(value) ? value.trim() : value;
18012};
18013
18014// Copied from:
18015// http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021
18016// Prereq: s is a string.
18017var escapeForRegexp = function(s) {
18018  return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
18019           replace(/\x08/g, '\\x08');
18020};
18021
18022
18023/**
18024 * @ngdoc function
18025 * @name angular.isElement
18026 * @module ng
18027 * @kind function
18028 *
18029 * @description
18030 * Determines if a reference is a DOM element (or wrapped jQuery element).
18031 *
18032 * @param {*} value Reference to check.
18033 * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
18034 */
18035function isElement(node) {
18036  return !!(node &&
18037    (node.nodeName  // we are a direct element
18038    || (node.prop && node.attr && node.find)));  // we have an on and find method part of jQuery API
18039}
18040
18041/**
18042 * @param str 'key1,key2,...'
18043 * @returns {object} in the form of {key1:true, key2:true, ...}
18044 */
18045function makeMap(str) {
18046  var obj = {}, items = str.split(","), i;
18047  for (i = 0; i < items.length; i++) {
18048    obj[items[i]] = true;
18049  }
18050  return obj;
18051}
18052
18053
18054function nodeName_(element) {
18055  return lowercase(element.nodeName || (element[0] && element[0].nodeName));
18056}
18057
18058function includes(array, obj) {
18059  return Array.prototype.indexOf.call(array, obj) != -1;
18060}
18061
18062function arrayRemove(array, value) {
18063  var index = array.indexOf(value);
18064  if (index >= 0) {
18065    array.splice(index, 1);
18066  }
18067  return index;
18068}
18069
18070/**
18071 * @ngdoc function
18072 * @name angular.copy
18073 * @module ng
18074 * @kind function
18075 *
18076 * @description
18077 * Creates a deep copy of `source`, which should be an object or an array.
18078 *
18079 * * If no destination is supplied, a copy of the object or array is created.
18080 * * If a destination is provided, all of its elements (for arrays) or properties (for objects)
18081 *   are deleted and then all elements/properties from the source are copied to it.
18082 * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
18083 * * If `source` is identical to 'destination' an exception will be thrown.
18084 *
18085 * @param {*} source The source that will be used to make a copy.
18086 *                   Can be any type, including primitives, `null`, and `undefined`.
18087 * @param {(Object|Array)=} destination Destination into which the source is copied. If
18088 *     provided, must be of the same type as `source`.
18089 * @returns {*} The copy or updated `destination`, if `destination` was specified.
18090 *
18091 * @example
18092 <example module="copyExample">
18093 <file name="index.html">
18094 <div ng-controller="ExampleController">
18095 <form novalidate class="simple-form">
18096 Name: <input type="text" ng-model="user.name" /><br />
18097 E-mail: <input type="email" ng-model="user.email" /><br />
18098 Gender: <input type="radio" ng-model="user.gender" value="male" />male
18099 <input type="radio" ng-model="user.gender" value="female" />female<br />
18100 <button ng-click="reset()">RESET</button>
18101 <button ng-click="update(user)">SAVE</button>
18102 </form>
18103 <pre>form = {{user | json}}</pre>
18104 <pre>master = {{master | json}}</pre>
18105 </div>
18106
18107 <script>
18108  angular.module('copyExample', [])
18109    .controller('ExampleController', ['$scope', function($scope) {
18110      $scope.master= {};
18111
18112      $scope.update = function(user) {
18113        // Example with 1 argument
18114        $scope.master= angular.copy(user);
18115      };
18116
18117      $scope.reset = function() {
18118        // Example with 2 arguments
18119        angular.copy($scope.master, $scope.user);
18120      };
18121
18122      $scope.reset();
18123    }]);
18124 </script>
18125 </file>
18126 </example>
18127 */
18128function copy(source, destination, stackSource, stackDest) {
18129  if (isWindow(source) || isScope(source)) {
18130    throw ngMinErr('cpws',
18131      "Can't copy! Making copies of Window or Scope instances is not supported.");
18132  }
18133  if (isTypedArray(destination)) {
18134    throw ngMinErr('cpta',
18135      "Can't copy! TypedArray destination cannot be mutated.");
18136  }
18137
18138  if (!destination) {
18139    destination = source;
18140    if (isObject(source)) {
18141      var index;
18142      if (stackSource && (index = stackSource.indexOf(source)) !== -1) {
18143        return stackDest[index];
18144      }
18145
18146      // TypedArray, Date and RegExp have specific copy functionality and must be
18147      // pushed onto the stack before returning.
18148      // Array and other objects create the base object and recurse to copy child
18149      // objects. The array/object will be pushed onto the stack when recursed.
18150      if (isArray(source)) {
18151        return copy(source, [], stackSource, stackDest);
18152      } else if (isTypedArray(source)) {
18153        destination = new source.constructor(source);
18154      } else if (isDate(source)) {
18155        destination = new Date(source.getTime());
18156      } else if (isRegExp(source)) {
18157        destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
18158        destination.lastIndex = source.lastIndex;
18159      } else if (isFunction(source.cloneNode)) {
18160          destination = source.cloneNode(true);
18161      } else {
18162        var emptyObject = Object.create(getPrototypeOf(source));
18163        return copy(source, emptyObject, stackSource, stackDest);
18164      }
18165
18166      if (stackDest) {
18167        stackSource.push(source);
18168        stackDest.push(destination);
18169      }
18170    }
18171  } else {
18172    if (source === destination) throw ngMinErr('cpi',
18173      "Can't copy! Source and destination are identical.");
18174
18175    stackSource = stackSource || [];
18176    stackDest = stackDest || [];
18177
18178    if (isObject(source)) {
18179      stackSource.push(source);
18180      stackDest.push(destination);
18181    }
18182
18183    var result, key;
18184    if (isArray(source)) {
18185      destination.length = 0;
18186      for (var i = 0; i < source.length; i++) {
18187        destination.push(copy(source[i], null, stackSource, stackDest));
18188      }
18189    } else {
18190      var h = destination.$$hashKey;
18191      if (isArray(destination)) {
18192        destination.length = 0;
18193      } else {
18194        forEach(destination, function(value, key) {
18195          delete destination[key];
18196        });
18197      }
18198      if (isBlankObject(source)) {
18199        // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
18200        for (key in source) {
18201          destination[key] = copy(source[key], null, stackSource, stackDest);
18202        }
18203      } else if (source && typeof source.hasOwnProperty === 'function') {
18204        // Slow path, which must rely on hasOwnProperty
18205        for (key in source) {
18206          if (source.hasOwnProperty(key)) {
18207            destination[key] = copy(source[key], null, stackSource, stackDest);
18208          }
18209        }
18210      } else {
18211        // Slowest path --- hasOwnProperty can't be called as a method
18212        for (key in source) {
18213          if (hasOwnProperty.call(source, key)) {
18214            destination[key] = copy(source[key], null, stackSource, stackDest);
18215          }
18216        }
18217      }
18218      setHashKey(destination,h);
18219    }
18220  }
18221  return destination;
18222}
18223
18224/**
18225 * Creates a shallow copy of an object, an array or a primitive.
18226 *
18227 * Assumes that there are no proto properties for objects.
18228 */
18229function shallowCopy(src, dst) {
18230  if (isArray(src)) {
18231    dst = dst || [];
18232
18233    for (var i = 0, ii = src.length; i < ii; i++) {
18234      dst[i] = src[i];
18235    }
18236  } else if (isObject(src)) {
18237    dst = dst || {};
18238
18239    for (var key in src) {
18240      if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
18241        dst[key] = src[key];
18242      }
18243    }
18244  }
18245
18246  return dst || src;
18247}
18248
18249
18250/**
18251 * @ngdoc function
18252 * @name angular.equals
18253 * @module ng
18254 * @kind function
18255 *
18256 * @description
18257 * Determines if two objects or two values are equivalent. Supports value types, regular
18258 * expressions, arrays and objects.
18259 *
18260 * Two objects or values are considered equivalent if at least one of the following is true:
18261 *
18262 * * Both objects or values pass `===` comparison.
18263 * * Both objects or values are of the same type and all of their properties are equal by
18264 *   comparing them with `angular.equals`.
18265 * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
18266 * * Both values represent the same regular expression (In JavaScript,
18267 *   /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
18268 *   representation matches).
18269 *
18270 * During a property comparison, properties of `function` type and properties with names
18271 * that begin with `$` are ignored.
18272 *
18273 * Scope and DOMWindow objects are being compared only by identify (`===`).
18274 *
18275 * @param {*} o1 Object or value to compare.
18276 * @param {*} o2 Object or value to compare.
18277 * @returns {boolean} True if arguments are equal.
18278 */
18279function equals(o1, o2) {
18280  if (o1 === o2) return true;
18281  if (o1 === null || o2 === null) return false;
18282  if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
18283  var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
18284  if (t1 == t2) {
18285    if (t1 == 'object') {
18286      if (isArray(o1)) {
18287        if (!isArray(o2)) return false;
18288        if ((length = o1.length) == o2.length) {
18289          for (key = 0; key < length; key++) {
18290            if (!equals(o1[key], o2[key])) return false;
18291          }
18292          return true;
18293        }
18294      } else if (isDate(o1)) {
18295        if (!isDate(o2)) return false;
18296        return equals(o1.getTime(), o2.getTime());
18297      } else if (isRegExp(o1)) {
18298        return isRegExp(o2) ? o1.toString() == o2.toString() : false;
18299      } else {
18300        if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
18301          isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
18302        keySet = createMap();
18303        for (key in o1) {
18304          if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
18305          if (!equals(o1[key], o2[key])) return false;
18306          keySet[key] = true;
18307        }
18308        for (key in o2) {
18309          if (!(key in keySet) &&
18310              key.charAt(0) !== '$' &&
18311              isDefined(o2[key]) &&
18312              !isFunction(o2[key])) return false;
18313        }
18314        return true;
18315      }
18316    }
18317  }
18318  return false;
18319}
18320
18321var csp = function() {
vendor: 4,669 bytes, lines 18322-18487
18322  if (!isDefined(csp.rules)) {
18323
18324
18325    var ngCspElement = (document.querySelector('[ng-csp]') ||
18326                    document.querySelector('[data-ng-csp]'));
18327
18328    if (ngCspElement) {
18329      var ngCspAttribute = ngCspElement.getAttribute('ng-csp') ||
18330                    ngCspElement.getAttribute('data-ng-csp');
18331      csp.rules = {
18332        noUnsafeEval: !ngCspAttribute || (ngCspAttribute.indexOf('no-unsafe-eval') !== -1),
18333        noInlineStyle: !ngCspAttribute || (ngCspAttribute.indexOf('no-inline-style') !== -1)
18334      };
18335    } else {
18336      csp.rules = {
18337        noUnsafeEval: noUnsafeEval(),
18338        noInlineStyle: false
18339      };
18340    }
18341  }
18342
18343  return csp.rules;
18344
18345  function noUnsafeEval() {
18346    try {
18347      /* jshint -W031, -W054 */
18348      new Function('');
18349      /* jshint +W031, +W054 */
18350      return false;
18351    } catch (e) {
18352      return true;
18353    }
18354  }
18355};
18356
18357/**
18358 * @ngdoc directive
18359 * @module ng
18360 * @name ngJq
18361 *
18362 * @element ANY
18363 * @param {string=} ngJq the name of the library available under `window`
18364 * to be used for angular.element
18365 * @description
18366 * Use this directive to force the angular.element library.  This should be
18367 * used to force either jqLite by leaving ng-jq blank or setting the name of
18368 * the jquery variable under window (eg. jQuery).
18369 *
18370 * Since angular looks for this directive when it is loaded (doesn't wait for the
18371 * DOMContentLoaded event), it must be placed on an element that comes before the script
18372 * which loads angular. Also, only the first instance of `ng-jq` will be used and all
18373 * others ignored.
18374 *
18375 * @example
18376 * This example shows how to force jqLite using the `ngJq` directive to the `html` tag.
18377 ```html
18378 <!doctype html>
18379 <html ng-app ng-jq>
18380 ...
18381 ...
18382 </html>
18383 ```
18384 * @example
18385 * This example shows how to use a jQuery based library of a different name.
18386 * The library name must be available at the top most 'window'.
18387 ```html
18388 <!doctype html>
18389 <html ng-app ng-jq="jQueryLib">
18390 ...
18391 ...
18392 </html>
18393 ```
18394 */
18395var jq = function() {
18396  if (isDefined(jq.name_)) return jq.name_;
18397  var el;
18398  var i, ii = ngAttrPrefixes.length, prefix, name;
18399  for (i = 0; i < ii; ++i) {
18400    prefix = ngAttrPrefixes[i];
18401    if (el = document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) {
18402      name = el.getAttribute(prefix + 'jq');
18403      break;
18404    }
18405  }
18406
18407  return (jq.name_ = name);
18408};
18409
18410function concat(array1, array2, index) {
18411  return array1.concat(slice.call(array2, index));
18412}
18413
18414function sliceArgs(args, startIndex) {
18415  return slice.call(args, startIndex || 0);
18416}
18417
18418
18419/* jshint -W101 */
18420/**
18421 * @ngdoc function
18422 * @name angular.bind
18423 * @module ng
18424 * @kind function
18425 *
18426 * @description
18427 * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
18428 * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
18429 * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
18430 * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
18431 *
18432 * @param {Object} self Context which `fn` should be evaluated in.
18433 * @param {function()} fn Function to be bound.
18434 * @param {...*} args Optional arguments to be prebound to the `fn` function call.
18435 * @returns {function()} Function that wraps the `fn` with all the specified bindings.
18436 */
18437/* jshint +W101 */
18438function bind(self, fn) {
18439  var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
18440  if (isFunction(fn) && !(fn instanceof RegExp)) {
18441    return curryArgs.length
18442      ? function() {
18443          return arguments.length
18444            ? fn.apply(self, concat(curryArgs, arguments, 0))
18445            : fn.apply(self, curryArgs);
18446        }
18447      : function() {
18448          return arguments.length
18449            ? fn.apply(self, arguments)
18450            : fn.call(self);
18451        };
18452  } else {
18453    // in IE, native methods are not functions so they cannot be bound (note: they don't need to be)
18454    return fn;
18455  }
18456}
18457
18458
18459function toJsonReplacer(key, value) {
18460  var val = value;
18461
18462  if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
18463    val = undefined;
18464  } else if (isWindow(value)) {
18465    val = '$WINDOW';
18466  } else if (value &&  document === value) {
18467    val = '$DOCUMENT';
18468  } else if (isScope(value)) {
18469    val = '$SCOPE';
18470  }
18471
18472  return val;
18473}
18474
18475
18476/**
18477 * @ngdoc function
18478 * @name angular.toJson
18479 * @module ng
18480 * @kind function
18481 *
18482 * @description
18483 * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
18484 * stripped since angular uses this notation internally.
18485 *
18486 * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
18487 * @param {boolean|number}
18487 [pretty=2] If set to true, the JSON output will contain newlines and whitespace.
18488 *    If set to an integer, the JSON output will contain that many spaces per indentation.
18489 * @returns {string|undefined} JSON-ified string representing `obj`.
18490 */
18491function toJson(obj, pretty) {
18492  if (typeof obj === 'undefined') return undefined;
18493  if (!isNumber(pretty)) {
18494    pretty = pretty ? 2 : null;
18495  }
18496  return JSON.stringify(obj, toJsonReplacer, pretty);
18497}
18498
18499
18500/**
18501 * @ngdoc function
18502 * @name angular.fromJson
18503 * @module ng
18504 * @kind function
18505 *
18506 * @description
18507 * Deserializes a JSON string.
18508 *
18509 * @param {string} json JSON string to deserialize.
18510 * @returns {Object|Array|string|number} Deserialized JSON string.
18511 */
18512function fromJson(json) {
18513  return isString(json)
18514      ? JSON.parse(json)
18515      : json;
18516}
18517
18518
18519function timezoneToOffset(timezone, fallback) {
18520  var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
18521  return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
18522}
18523
18524
18525function addDateMinutes(date, minutes) {
18526  date = new Date(date.getTime());
18527  date.setMinutes(date.getMinutes() + minutes);
18528  return date;
18529}
18530
18531
18532function convertTimezoneToLocal(date, timezone, reverse) {
18533  reverse = reverse ? -1 : 1;
18534  var timezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
18535  return addDateMinutes(date, reverse * (timezoneOffset - date.getTimezoneOffset()));
18536}
18537
18538
18539/**
18540 * @returns {string} Returns the string representation of the element.
18541 */
18542function startingTag(element) {
18543  element = jqLite(element).clone();
18544  try {
18545    // turns out IE does not let you set .html() on elements which
18546    // are not allowed to have children. So we just ignore it.
18547    element.empty();
18548  } catch (e) {}
18549  var elemHtml = jqLite('<div>').append(element).html();
18550  try {
18551    return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
18552        elemHtml.
18553          match(/^(<[^>]+>)/)[1].
18554          replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
18555  } catch (e) {
18556    return lowercase(elemHtml);
18557  }
18558
18559}
18560
18561
18562/////////////////////////////////////////////////
18563
18564/**
18565 * Tries to decode the URI component without throwing an exception.
18566 *
18567 * @private
18568 * @param str value potential URI component to check.
18569 * @returns {boolean} True if `value` can be decoded
18570 * with the decodeURIComponent function.
18571 */
18572function tryDecodeURIComponent(value) {
18573  try {
18574    return decodeURIComponent(value);
18575  } catch (e) {
18576    // Ignore any invalid uri component
18577  }
18578}
18579
18580
18581/**
18582 * Parses an escaped url query string into key-value pairs.
18583 * @returns {Object.<string,boolean|Array>}
18584 */
18585function parseKeyValue(/**string*/keyValue) {
18586  var obj = {};
18587  forEach((keyValue || "").split('&'), function(keyValue) {
18588    var splitPoint, key, val;
18589    if (keyValue) {
18590      key = keyValue = keyValue.replace(/\+/g,'%20');
18591      splitPoint = keyValue.indexOf('=');
18592      if (splitPoint !== -1) {
18593        key = keyValue.substring(0, splitPoint);
18594        val = keyValue.substring(splitPoint + 1);
18595      }
18596      key = tryDecodeURIComponent(key);
18597      if (isDefined(key)) {
18598        val = isDefined(val) ? tryDecodeURIComponent(val) : true;
18599        if (!hasOwnProperty.call(obj, key)) {
18600          obj[key] = val;
18601        } else if (isArray(obj[key])) {
18602          obj[key].push(val);
18603        } else {
18604          obj[key] = [obj[key],val];
18605        }
18606      }
18607    }
18608  });
18609  return obj;
18610}
18611
18612function toKeyValue(obj) {
18613  var parts = [];
18614  forEach(obj, function(value, key) {
18615    if (isArray(value)) {
18616      forEach(value, function(arrayValue) {
18617        parts.push(encodeUriQuery(key, true) +
18618                   (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
18619      });
18620    } else {
18621    parts.push(encodeUriQuery(key, true) +
18622               (value === true ? '' : '=' + encodeUriQuery(value, true)));
18623    }
18624  });
18625  return parts.length ? parts.join('&') : '';
18626}
18627
18628
18629/**
18630 * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
18631 * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
18632 * segments:
18633 *    segment       = *pchar
18634 *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
18635 *    pct-encoded   = "%" HEXDIG HEXDIG
18636 *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
18637 *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
18638 *                     / "*" / "+" / "," / ";" / "="
18639 */
18640function encodeUriSegment(val) {
18641  return encodeUriQuery(val, true).
18642             replace(/%26/gi, '&').
18643             replace(/%3D/gi, '=').
18644             replace(/%2B/gi, '+');
18645}
18646
18647
18648/**
18649 * This method is intended for encoding *key* or *value* parts of query component. We need a custom
18650 * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
18651 * encoded per http://tools.ietf.org/html/rfc3986:
18652 *    query       = *( pchar / "/" / "?" )
18653 *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
18654 *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
18655 *    pct-encoded   = "%" HEXDIG HEXDIG
18656 *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
18657 *                     / "*" / "+" / "," / ";" / "="
18658 */
18659function encodeUriQuery(val, pctEncodeSpaces) {
18660  return encodeURIComponent(val).
18661             replace(/%40/gi, '@').
18662             replace(/%3A/gi, ':').
18663             replace(/%24/g, '$').
18664             replace(/%2C/gi, ',').
18665             replace(/%3B/gi, ';').
18666             replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
18667}
18668
18669var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
18670
18671function getNgAttribute(element, ngAttr) {
18672  var attr, i, ii = ngAttrPrefixes.length;
18673  for (i = 0; i < ii; ++i) {
18674    attr = ngAttrPrefixes[i] + ngAttr;
18675    if (isString(attr = element.getAttribute(attr))) {
18676      return attr;
18677    }
18678  }
18679  return null;
18680}
18681
18682/**
18683 * @ngdoc directive
18684 * @name ngApp
18685 * @module ng
18686 *
18687 * @element ANY
18688 * @param {angular.Module} ngApp an optional application
18689 *   {@link angular.module module} name to load.
18690 * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be
18691 *   created in "strict-di" mode. This means that the application will fail to invoke functions which
18692 *   do not use explicit function annotation (and are thus unsuitable for minification), as de
18692scribed
18693 *   in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in
18694 *   tracking down the root of these bugs.
18695 *
18696 * @description
18697 *
18698 * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
18699 * designates the **root element** of the application and is typically placed near the root element
18700 * of the page - e.g. on the `<body>` or `<html>` tags.
18701 *
18702 * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
18703 * found in the document will be used to define the root element to auto-bootstrap as an
18704 * application. To run multiple applications in an HTML document you must manually bootstrap them using
18705 * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other.
18706 *
18707 * You can specify an **AngularJS module** to be used as the root module for the application.  This
18708 * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It
18709 * should contain the application code needed or have dependencies on other modules that will
18710 * contain the code. See {@link angular.module} for more information.
18711 *
18712 * In the example below if the `ngApp` directive were not placed on the `html` element then the
18713 * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
18714 * would not be resolved to `3`.
18715 *
18716 * `ngApp` is the easiest, and most common way to bootstrap an application.
18717 *
18718 <example module="ngAppDemo">
18719   <file name="index.html">
18720   <div ng-controller="ngAppDemoController">
18721     I can add: {{a}} + {{b}} =  {{ a+b }}
18722   </div>
18723   </file>
18724   <file name="script.js">
18725   angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
18726     $scope.a = 1;
18727     $scope.b = 2;
18728   });
18729   </file>
18730 </example>
18731 *
18732 * Using `ngStrictDi`, you would see something like this:
18733 *
18734 <example ng-app-included="true">
18735   <file name="index.html">
18736   <div ng-app="ngAppStrictDemo" ng-strict-di>
18737       <div ng-controller="GoodController1">
18738           I can add: {{a}} + {{b}} =  {{ a+b }}
18739
18740           <p>This renders because the controller does not fail to
18741              instantiate, by using explicit annotation style (see
18742              script.js for details)
18743           </p>
18744       </div>
18745
18746       <div ng-controller="GoodController2">
18747           Name: <input ng-model="name"><br />
18748           Hello, {{name}}!
18749
18750           <p>This renders because the controller does not fail to
18751              instantiate, by using explicit annotation style
18752              (see script.js for details)
18753           </p>
18754       </div>
18755
18756       <div ng-controller="BadController">
18757           I can add: {{a}} + {{b}} =  {{ a+b }}
18758
18759           <p>The controller could not be instantiated, due to relying
18760              on automatic function annotations (which are disabled in
18761              strict mode). As such, the content of this section is not
18762              interpolated, and there should be an error in your web console.
18763           </p>
18764       </div>
18765   </div>
18766   </file>
18767   <file name="script.js">
18768   angular.module('ngAppStrictDemo', [])
18769     // BadController will fail to instantiate, due to relying on automatic function annotation,
18770     // rather than an explicit annotation
18771     .controller('BadController', function($scope) {
18772       $scope.a = 1;
18773       $scope.b = 2;
18774     })
18775     // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated,
18776     // due to using explicit annotations using the array style and $inject property, respectively.
18777     .controller('GoodController1', ['$scope', function($scope) {
18778       $scope.a = 1;
18779       $scope.b = 2;
18780     }])
18781     .controller('GoodController2', GoodController2);
18782     function GoodController2($scope) {
18783       $scope.name = "World";
18784     }
18785     GoodController2.$inject = ['$scope'];
18786   </file>
18787   <file name="style.css">
18788   div[ng-controller] {
18789       margin-bottom: 1em;
18790       -webkit-border-radius: 4px;
18791       border-radius: 4px;
18792       border: 1px solid;
18793       padding: .5em;
18794   }
18795   div[ng-controller^=Good] {
18796       border-color: #d6e9c6;
18797       background-color: #dff0d8;
18798       color: #3c763d;
18799   }
18800   div[ng-controller^=Bad] {
18801       border-color: #ebccd1;
18802       background-color: #f2dede;
18803       color: #a94442;
18804       margin-bottom: 0;
18805   }
18806   </file>
18807 </example>
18808 */
18809function angularInit(element, bootstrap) {
18810  var appElement,
18811      module,
18812      config = {};
18813
18814  // The element `element` has priority over any other element
18815  forEach(ngAttrPrefixes, function(prefix) {
18816    var name = prefix + 'app';
18817
18818    if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
18819      appElement = element;
18820      module = element.getAttribute(name);
18821    }
18822  });
18823  forEach(ngAttrPrefixes, function(prefix) {
18824    var name = prefix + 'app';
18825    var candidate;
18826
18827    if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
18828      appElement = candidate;
18829      module = candidate.getAttribute(name);
18830    }
18831  });
18832  if (appElement) {
18833    config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
18834    bootstrap(appElement, module ? [module] : [], config);
18835  }
18836}
18837
18838/**
18839 * @ngdoc function
18840 * @name angular.bootstrap
18841 * @module ng
18842 * @description
18843 * Use this function to manually start up angular application.
18844 *
18845 * See: {@link guide/bootstrap Bootstrap}
18846 *
18847 * Note that Protractor based end-to-end tests cannot use this function to bootstrap manually.
18848 * They must use {@link ng.directive:ngApp ngApp}.
18849 *
18850 * Angular will detect if it has been loaded into the browser more than once and only allow the
18851 * first loaded script to be bootstrapped and will report a warning to the browser console for
18852 * each of the subsequent scripts. This prevents strange results in applications, where otherwise
18853 * multiple instances of Angular try to work on the DOM.
18854 *
18855 * ```html
18856 * <!doctype html>
18857 * <html>
18858 * <body>
18859 * <div ng-controller="WelcomeController">
18860 *   {{greeting}}
18861 * </div>
18862 *
18863 * <script src="angular.js"></script>
18864 * <script>
18865 *   var app = angular.module('demo', [])
18866 *   .controller('WelcomeController', function($scope) {
18867 *       $scope.greeting = 'Welcome!';
18868 *   });
18869 *   angular.bootstrap(document, ['demo']);
18870 * </script>
18871 * </body>
18872 * </html>
18873 * ```
18874 *
18875 * @param {DOMElement} element DOM element which is the root of angular application.
18876 * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
18877 *     Each item in the array should be the name of a predefined module or a (DI annotated)
18878 *     function that will be invoked by the injector as a `config` block.
18879 *     See: {@link angular.module modules}
18880 * @param {Object=} config an object for defining configuration options for the application. The
18881 *     following keys are supported:
18882 *
18883 * * `strictDi` - disable automatic function annotation for the application. This is meant to
18884 *   assist in finding bugs which break minified code. Defaults to `false`.
18885 *
18886 * @returns {auto.$injector} Returns the newly created injector for this app.
18887 */
18888function bootstrap(element, modules, config) {
18889  if (!isObject(config)) config = {};
18890  var defaultConfig = {
18891    strictDi: false
18892  };
18893  config = extend(defaultConfig, config);
18894  var doBootstrap = function() {
18895    element = jqLite(element);
18896
18897    if (element.injector()) {
18898      var tag = (element[0] === document) ? 'document' : startingTag(element);
18899      //Encode angle brackets to prevent input from being sanitized to empty string #8683
18900      throw ngMinErr(
18901          'btstrpd',
18902          "App Already Bootstrapped with this Element '{0}'",
18903          tag.replace(/</,'&lt;').replace(/>/,'&gt;'));
18904    }
18905
18906    modules = modules || [];
18907    modules.unshift(['$provide', function($provide) {
18908      $provide.value('$rootElement', element);
18909    }]);
18910
18911    if (config.debugInfoEnabled) {
18912      // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
18913      modules.push(['$compileProvider', function($compileProvider) {
18914        $compileProvider.debugInfoEnabled(true);
18915      }]);
18916    }
18917
18918    modules.unshift('ng');
18919    var injector = createInjector(modules, config.strictDi);
18920    injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
18921       function bootstrapApply(scope, element, compile, injector) {
18922        scope.$apply(function() {
18923          element.data('$injector', injector);
18924          compile(element)(scope);
18925        });
18926      }]
18927    );
18928    return injector;
18929  };
18930
18931  var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
18932  var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
18933
18934  if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
18935    config.debugInfoEnabled = true;
18936    window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
18937  }
18938
18939  if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
18940    return doBootstrap();
18941  }
18942
18943  window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
18944  angular.resumeBootstrap = function(extraModules) {
18945    forEach(extraModules, function(module) {
18946      modules.push(module);
18947    });
18948    return doBootstrap();
18949  };
18950
18951  if (isFunction(angular.resumeDeferredBootstrap)) {
18952    angular.resumeDeferredBootstrap();
18953  }
18954}
18955
18956/**
18957 * @ngdoc function
18958 * @name angular.reloadWithDebugInfo
18959 * @module ng
18960 * @description
18961 * Use this function to reload the current application with debug information turned on.
18962 * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`.
18963 *
18964 * See {@link ng.$compileProvider#debugInfoEnabled} for more.
18965 */
18966function reloadWithDebugInfo() {
18967  window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;
18968  window.location.reload();
18969}
18970
18971/**
18972 * @name angular.getTestability
18973 * @module ng
18974 * @description
18975 * Get the testability service for the instance of Angular on the given
18976 * element.
18977 * @param {DOMElement} element DOM element which is the root of angular application.
18978 */
18979function getTestability(rootElement) {
18980  var injector = angular.element(rootElement).injector();
18981  if (!injector) {
18982    throw ngMinErr('test',
18983      'no injector found for element argument to getTestability');
18984  }
18985  return injector.get('$$testability');
18986}
18987
18988var SNAKE_CASE_REGEXP = /[A-Z]/g;
18989function snake_case(name, separator) {
18990  separator = separator || '_';
18991  return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
18992    return (pos ? separator : '') + letter.toLowerCase();
18993  });
18994}
18995
18996var bindJQueryFired = false;
vendor: 9,099 bytes, lines 18997-19286
18997var skipDestroyOnNextJQueryCleanData;
18998function bindJQuery() {
18999  var originalCleanData;
19000
19001  if (bindJQueryFired) {
19002    return;
19003  }
19004
19005  // bind to jQuery if present;
19006  var jqName = jq();
19007  jQuery = isUndefined(jqName) ? window.jQuery :   // use jQuery (if present)
19008           !jqName             ? undefined     :   // use jqLite
19009                                 window[jqName];   // use jQuery specified by `ngJq`
19010
19011  // Use jQuery if it exists with proper functionality, otherwise default to us.
19012  // Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
19013  // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
19014  // versions. It will not work for sure with jQuery <1.7, though.
19015  if (jQuery && jQuery.fn.on) {
19016    jqLite = jQuery;
19017    extend(jQuery.fn, {
19018      scope: JQLitePrototype.scope,
19019      isolateScope: JQLitePrototype.isolateScope,
19020      controller: JQLitePrototype.controller,
19021      injector: JQLitePrototype.injector,
19022      inheritedData: JQLitePrototype.inheritedData
19023    });
19024
19025    // All nodes removed from the DOM via various jQuery APIs like .remove()
19026    // are passed through jQuery.cleanData. Monkey-patch this method to fire
19027    // the $destroy event on all removed nodes.
19028    originalCleanData = jQuery.cleanData;
19029    jQuery.cleanData = function(elems) {
19030      var events;
19031      if (!skipDestroyOnNextJQueryCleanData) {
19032        for (var i = 0, elem; (elem = elems[i]) != null; i++) {
19033          events = jQuery._data(elem, "events");
19034          if (events && events.$destroy) {
19035            jQuery(elem).triggerHandler('$destroy');
19036          }
19037        }
19038      } else {
19039        skipDestroyOnNextJQueryCleanData = false;
19040      }
19041      originalCleanData(elems);
19042    };
19043  } else {
19044    jqLite = JQLite;
19045  }
19046
19047  angular.element = jqLite;
19048
19049  // Prevent double-proxying.
19050  bindJQueryFired = true;
19051}
19052
19053/**
19054 * throw error if the argument is falsy.
19055 */
19056function assertArg(arg, name, reason) {
19057  if (!arg) {
19058    throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
19059  }
19060  return arg;
19061}
19062
19063function assertArgFn(arg, name, acceptArrayAnnotation) {
19064  if (acceptArrayAnnotation && isArray(arg)) {
19065      arg = arg[arg.length - 1];
19066  }
19067
19068  assertArg(isFunction(arg), name, 'not a function, got ' +
19069      (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));
19070  return arg;
19071}
19072
19073/**
19074 * throw error if the name given is hasOwnProperty
19075 * @param  {String} name    the name to test
19076 * @param  {String} context the context in which the name is used, such as module or directive
19077 */
19078function assertNotHasOwnProperty(name, context) {
19079  if (name === 'hasOwnProperty') {
19080    throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context);
19081  }
19082}
19083
19084/**
19085 * Return the value accessible from the object by path. Any undefined traversals are ignored
19086 * @param {Object} obj starting object
19087 * @param {String} path path to traverse
19088 * @param {boolean} [bindFnToScope=true]
19089 * @returns {Object} value as accessible by path
19090 */
19091//TODO(misko): this function needs to be removed
19092function getter(obj, path, bindFnToScope) {
19093  if (!path) return obj;
19094  var keys = path.split('.');
19095  var key;
19096  var lastInstance = obj;
19097  var len = keys.length;
19098
19099  for (var i = 0; i < len; i++) {
19100    key = keys[i];
19101    if (obj) {
19102      obj = (lastInstance = obj)[key];
19103    }
19104  }
19105  if (!bindFnToScope && isFunction(obj)) {
19106    return bind(lastInstance, obj);
19107  }
19108  return obj;
19109}
19110
19111/**
19112 * Return the DOM siblings between the first and last node in the given array.
19113 * @param {Array} array like object
19114 * @returns {Array} the inputted object or a jqLite collection containing the nodes
19115 */
19116function getBlockNodes(nodes) {
19117  // TODO(perf): update `nodes` instead of creating a new object?
19118  var node = nodes[0];
19119  var endNode = nodes[nodes.length - 1];
19120  var blockNodes;
19121
19122  for (var i = 1; node !== endNode && (node = node.nextSibling); i++) {
19123    if (blockNodes || nodes[i] !== node) {
19124      if (!blockNodes) {
19125        blockNodes = jqLite(slice.call(nodes, 0, i));
19126      }
19127      blockNodes.push(node);
19128    }
19129  }
19130
19131  return blockNodes || nodes;
19132}
19133
19134
19135/**
19136 * Creates a new object without a prototype. This object is useful for lookup without having to
19137 * guard against prototypically inherited properties via hasOwnProperty.
19138 *
19139 * Related micro-benchmarks:
19140 * - http://jsperf.com/object-create2
19141 * - http://jsperf.com/proto-map-lookup/2
19142 * - http://jsperf.com/for-in-vs-object-keys2
19143 *
19144 * @returns {Object}
19145 */
19146function createMap() {
19147  return Object.create(null);
19148}
19149
19150var NODE_TYPE_ELEMENT = 1;
19151var NODE_TYPE_ATTRIBUTE = 2;
19152var NODE_TYPE_TEXT = 3;
19153var NODE_TYPE_COMMENT = 8;
19154var NODE_TYPE_DOCUMENT = 9;
19155var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
19156
19157/**
19158 * @ngdoc type
19159 * @name angular.Module
19160 * @module ng
19161 * @description
19162 *
19163 * Interface for configuring angular {@link angular.module modules}.
19164 */
19165
19166function setupModuleLoader(window) {
19167
19168  var $injectorMinErr = minErr('$injector');
19169  var ngMinErr = minErr('ng');
19170
19171  function ensure(obj, name, factory) {
19172    return obj[name] || (obj[name] = factory());
19173  }
19174
19175  var angular = ensure(window, 'angular', Object);
19176
19177  // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
19178  angular.$$minErr = angular.$$minErr || minErr;
19179
19180  return ensure(angular, 'module', function() {
19181    /** @type {Object.<string, angular.Module>} */
19182    var modules = {};
19183
19184    /**
19185     * @ngdoc function
19186     * @name angular.module
19187     * @module ng
19188     * @description
19189     *
19190     * The `angular.module` is a global place for creating, registering and retrieving Angular
19191     * modules.
19192     * All modules (angular core or 3rd party) that should be available to an application must be
19193     * registered using this mechanism.
19194     *
19195     * Passing one argument retrieves an existing {@link angular.Module},
19196     * whereas passing more than one argument creates a new {@link angular.Module}
19197     *
19198     *
19199     * # Module
19200     *
19201     * A module is a collection of services, directives, controllers, filters, and configuration information.
19202     * `angular.module` is used to configure the {@link auto.$injector $injector}.
19203     *
19204     * ```js
19205     * // Create a new module
19206     * var myModule = angular.module('myModule', []);
19207     *
19208     * // register a new service
19209     * myModule.value('appName', 'MyCoolApp');
19210     *
19211     * // configure existing services inside initialization blocks.
19212     * myModule.config(['$locationProvider', function($locationProvider) {
19213     *   // Configure existing providers
19214     *   $locationProvider.hashPrefix('!');
19215     * }]);
19216     * ```
19217     *
19218     * Then you can create an injector and load your modules like this:
19219     *
19220     * ```js
19221     * var injector = angular.injector(['ng', 'myModule'])
19222     * ```
19223     *
19224     * However it's more likely that you'll just use
19225     * {@link ng.directive:ngApp ngApp} or
19226     * {@link angular.bootstrap} to simplify this process for you.
19227     *
19228     * @param {!string} name The name of the module to create or retrieve.
19229     * @param {!Array.<string>=} requires If specified then new module is being created. If
19230     *        unspecified then the module is being retrieved for further configuration.
19231     * @param {Function=} configFn Optional configuration function for the module. Same as
19232     *        {@link angular.Module#config Module#config()}.
19233     * @returns {module} new module with the {@link angular.Module} api.
19234     */
19235    return function module(name, requires, configFn) {
19236      var assertNotHasOwnProperty = function(name, context) {
19237        if (name === 'hasOwnProperty') {
19238          throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
19239        }
19240      };
19241
19242      assertNotHasOwnProperty(name, 'module');
19243      if (requires && modules.hasOwnProperty(name)) {
19244        modules[name] = null;
19245      }
19246      return ensure(modules, name, function() {
19247        if (!requires) {
19248          throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
19249             "the module name or forgot to load it. If registering a module ensure that you " +
19250             "specify the dependencies as the second argument.", name);
19251        }
19252
19253        /** @type {!Array.<Array.<*>>} */
19254        var invokeQueue = [];
19255
19256        /** @type {!Array.<Function>} */
19257        var configBlocks = [];
19258
19259        /** @type {!Array.<Function>} */
19260        var runBlocks = [];
19261
19262        var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
19263
19264        /** @type {angular.Module} */
19265        var moduleInstance = {
19266          // Private state
19267          _invokeQueue: invokeQueue,
19268          _configBlocks: configBlocks,
19269          _runBlocks: runBlocks,
19270
19271          /**
19272           * @ngdoc property
19273           * @name angular.Module#requires
19274           * @module ng
19275           *
19276           * @description
19277           * Holds the list of modules which the injector will load before the current module is
19278           * loaded.
19279           */
19280          requires: requires,
19281
19282          /**
19283           * @ngdoc property
19284           * @name angular.Module#name
19285           * @module ng
19286           *
19287           * @description
19288           * Name of the module.
19289           */
19290          name: name,
19291
19292
19293          /**
19294           * @ngdoc method
19295           * @name angular.Module#provider
19296           * @module ng
19297           * @param {string} name service name
19298           * @param {Function} providerType Construction function for creating new instance of the
19299           *                                service.
19300           * @description
19301           * See {@link auto.$provide#provider $provide.provider()}.
19302           */
19303          provider: invokeLaterAndSetModuleName('$provide', 'provider'),
19304
19305          /**
19306           * @ngdoc method
19307           * @name angular.Module#factory
19308           * @module ng
19309           * @param {string} name service name
19310           * @param {Function} providerFunction Function for creating new instance of the service.
19311           * @description
19312           * See {@link auto.$provide#factory $provide.factory()}.
19313           */
19314          factory: invokeLaterAndSetModuleName('$provide', 'factory'),
19315
19316          /**
19317           * @ngdoc method
19318           * @name angular.Module#service
19319           * @module ng
19320           * @param {string} name service name
19321           * @param {Function} constructor A constructor function that will be instantiated.
19322           * @description
19323           * See {@link auto.$provide#service $provide.service()}.
19324           */
19325          service: invokeLaterAndSetModuleName('$provide', 'service'),
19326
19327          /**
19328           * @ngdoc method
19329           * @name angular.Module#value
19330           * @module ng
19331           * @param {string} name service name
19332           * @param {*} object Service instance object.
19333           * @description
19334           * See {@link auto.$provide#value $provide.value()}.
19335           */
19336          value: invokeLater('$provide', 'value'),
19337
19338          /**
19339           * @ngdoc method
19340           * @name angular.Module#constant
19341           * @module ng
19342           * @param {string} name constant name
19343           * @param {*} object Constant value.
19344           * @description
19345           * Because the constant are fixed, they get applied before other provide methods.
19346           * See {@link auto.$provide#constant $provide.constant()}.
19347           */
19348          constant: invokeLater('$provide', 'constant', 'unshift'),
19349
19350           /**
19351           * @ngdoc method
19352           * @name angular.Module#decorator
19353           * @module ng
19354           * @param {string} The name of the service to decorate.
19355           * @param {Function} This function will be invoked when the service needs to be
19356           *                                    instantiated and should return the decorated service instance.
19357           * @description
19358           * See {@link auto.$provide#decorator $provide.decorator()}.
19359           */
19360          decorator: invokeLaterAndSetModuleName('$provide', 'decorator'),
19361
19362          /**
19363           * @ngdoc method
19364           * @name angular.Module#animation
19365           * @module ng
19366           * @param {string} name animation name
19367           * @param {Function} animationFactory Factory function for creating new instance of an
19368           *                                    animation.
19369           * @description
19370           *
19371           * **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
19372           *
19373           *
19374           * Defines an animation hook that can be later used with
19375           * {@link $animate $animate} service and directives that use this service.
19376           *
19377           * ```js
19378           * module.animation('.animation-name', function($inject1, $inject2) {
19379           *   return {
19380           *     eventName : function(element, done) {
19381           *       //code to run the animation
19382           *       //once complete, then run done()
19383           *       return function cancellationFunction(element) {
19384           *         //code to cancel the animation
19385           *       }
19386           *     }
19387           *   }
19388           * })
19389           * ```
19390           *
19391           * See {@link ng.$animateProvider#register $animateProvider.register()} and
19392           * {@link ngAnimate ngAnimate module} for more information.
19393           */
19394          animation: invokeLaterAndSetModuleName('$animateProvider', 'register'),
19395
19396          /**
19397           * @ngdoc method
19398           * @name angular.Module#filter
19399           * @module ng
19400           * @param {string} name Filter name - this must be a valid angular expression identifier
19401           * @param {Function} filterFactory Factory function for creating new instance of filter.
19402           * @description
19403           * See {@link ng.$filterProvider#register $filterProvider.register()}.
19404           *
19405           * <div class="alert alert-warning">
19406           * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
19407           * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
19408           * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
19409           * (`myapp_subsection_filterx`).
19410           * </div>
19411           */
19412          filter: invokeLaterAndSetModuleName('$filterProvider', 'register'),
19413
19414          /**
19415           * @ngdoc method
19416           * @name angular.Module#controller
19417           * @module ng
19418           * @param {string|Object} name Controller name, or an object map of controllers where the
19419           *    keys are the names and the values are the constructors.
19420           * @param {Function} constructor Controller constructor function.
19421           * @description
19422           * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
19423           */
19424          controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'),
19425
19426          /**
19427           * @ngdoc method
19428           * @name angular.Module#directive
19429           * @module ng
19430           * @param {string|Object} name Directive name, or an object map of directives where the
19431           *    keys are the names and the values are the factories.
19432           * @param {Function} directiveFactory Factory function for creating new instance of
19433           * directives.
19434           * @description
19435           * See {@link ng.$compileProvider#directive $compileProvider.directive()}.
19436           */
19437          directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
19438
19439          /**
19440           * @ngdoc method
19441           * @name angular.Module#config
19442           * @module ng
19443           * @param {Function} configFn Execute this function on module load. Useful for service
19444           *    configuration.
19445           * @description
19446           * Use this method to register work which needs to be performed on module loading.
19447           * For more about how to configure services, see
19448           * {@link providers#provider-recipe Provider Recipe}.
19449           */
19450          config: config,
19451
19452          /**
19453           * @ngdoc method
19454           * @name angular.Module#run
19455           * @module ng
19456           * @param {Function} initializationFn Execute this function after injector creation.
19457           *    Useful for application initialization.
19458           * @description
19459           * Use this method to register work which should be performed when the injector is done
19460           * loading all modules.
19461           */
19462          run: function(block) {
19463            runBlocks.push(block);
19464            return this;
19465          }
19466        };
19467
19468        if (configFn) {
19469          config(configFn);
19470        }
19471
19472        return moduleInstance;
19473
19474        /**
19475         * @param {string} provider
19476         * @param {string} method
19477         * @param {String=} insertMethod
19478         * @returns {angular.Module}
19479         */
19480        function invokeLater(provider, method, insertMethod, queue) {
19481          if (!queue) queue = invokeQueue;
19482          return function() {
19483            queue[insertMethod || 'push']([provider, method, arguments]);
19484            return moduleInstance;
19485          };
19486        }
19487
19488        /**
19489         * @param {string} provider
19490         * @param {string} method
19491         * @returns {angular.Module}
19492         */
19493        function invokeLaterAndSetModuleName(provider, method) {
19494          return function(recipeName, factoryFunction) {
19495            if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
19496            invokeQueue.push([provider, method, arguments]);
19497            return moduleInstance;
19498          };
19499        }
19500      });
19501    };
19502  });
19503
19504}
19505
19506/* global: toDebugString: true */
19507
19508function serializeObject(obj) {
19509  var seen = [];
19510
19511  return JSON.stringify(obj, function(key, val) {
19512    val = toJsonReplacer(key, val);
19513    if (isObject(val)) {
19514
19515      if (seen.indexOf(val) >= 0) return '...';
19516
19517      seen.push(val);
19518    }
19519    return val;
19520  });
19521}
19522
19523function toDebugString(obj) {
19524  if (typeof obj === 'function') {
19525    return obj.toString().replace(/ \{[\s\S]*$/, '');
19526  } else if (isUndefined(obj)) {
19527    return 'undefined';
19528  } else if (typeof obj !== 'string') {
19529    return serializeObject(obj);
19530  }
19531  return obj;
19532}
19533
19534/* global angularModule: true,
19535  version: true,
19536
19537  $CompileProvider,
19538
19539  htmlAnchorDirective,
19540  inputDirective,
19541  inputDirective,
19542  formDirective,
19543  scriptDirective,
19544  selectDirective,
19545  styleDirective,
19546  optionDirective,
19547  ngBindDirective,
19548  ngBindHtmlDirective,
19549  ngBindTemplateDirective,
19550  ngClassDirective,
19551  ngClassEvenDirective,
19552  ngClassOddDirective,
19553  ngCloakDirective,
19554  ngControllerDirective,
19555  ngFormDirective,
19556  ngHideDirective,
19557  ngIfDirective,
19558  ngIncludeDirective,
19559  ngIncludeFillContentDirective,
19560  ngInitDirective,
19561  ngNonBindableDirective,
19562  ngPluralizeDirective,
19563  ngRepeatDirective,
19564  ngShowDirective,
19565  ngStyleDirective,
19566  ngSwitchDirective,
19567  ngSwitchWhenDirective,
19568  ngSwitchDefaultDirective,
19569  ngOptionsDirective,
19570  ngTranscludeDirective,
19571  ngModelDirective,
19572  ngListDirective,
19573  ngChangeDirective,
19574  patternDirective,
19575  patternDirective,
19576  requiredDirective,
19577  requiredDirective,
19578  minlengthDirective,
19579  minlengthDirective,
19580  maxlengthDirective,
19581  maxlengthDirective,
19582  ngValueDirective,
19583  ngModelOptionsDirective,
19584  ngAttributeAliasDirectives,
19585  ngEventDirectives,
19586
19587  $AnchorScrollProvider,
19588  $AnimateProvider,
19589  $CoreAnimateCssProvider,
19590  $$CoreAnimateQueueProvider,
19591  $$CoreAnimateRunnerProvider,
19592  $BrowserProvider,
19593  $CacheFactoryProvider,
19594  $ControllerProvider,
19595  $DocumentProvider,
19596  $ExceptionHandlerProvider,
19597  $FilterProvider,
19598  $$ForceReflowProvider,
19599  $InterpolateProvider,
19600  $IntervalProvider,
19601  $$HashMapProvider,
19602  $HttpProvider,
19603  $HttpParamSerializerProvider,
19604  $HttpParamSerializerJQLikeProvider,
19605  $HttpBackendProvider,
19606  $xhrFactoryProvider,
19607  $LocationProvider,
19608  $LogProvider,
19609  $ParseProvider,
19610  $RootScopeProvider,
19611  $QProvider,
19612  $$QProvider,
19613  $$SanitizeUriProvider,
19614  $SceProvider,
19615  $SceDelegateProvider,
19616  $SnifferProvider,
19617  $TemplateCacheProvider,
19618  $TemplateRequestProvider,
19619  $$TestabilityProvider,
19620  $TimeoutProvider,
19621  $$RAFProvider,
19622  $WindowProvider,
19623  $$jqLiteProvider,
19624  $$CookieReaderProvider
19625*/
19626
19627
19628/**
19629 * @ngdoc object
19630 * @name angular.version
19631 * @module ng
19632 * @description
19633 * An object that contains information about the current AngularJS version.
19634 *
19635 * This object has the following properties:
19636 *
19637 * - `full` – `{string}` – Full version string, such as "0.9.18".
19638 * - `major` – `{number}` – Major version number, such as "0".
19639 * - `minor` – `{number}` – Minor version number, such as "9".
19640 * - `dot` – `{number}` – Dot version number, such as "18".
19641 * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
19642 */
19643var version = {
19644  full: '1.4.7',    // all of these placeholder strings will be replaced by grunt's
19645  major: 1,    // package task
19646  minor: 4,
19647  dot: 7,
19648  codeName: 'dark-luminescence'
19649};
19650
19651
19652function publishExternalAPI(angular) {
19653  extend(angular, {
19654    'bootstrap': bootstrap,
19655    'copy': copy,
19656    'extend': extend,
19657    'merge': merge,
19658    'equals': equals,
19659    'element': jqLite,
19660    'forEach': forEach,
19661    'injector': createInjector,
19662    'noop': noop,
19663    'bind': bind,
19664    'toJson': toJson,
19665    'fromJson': fromJson,
19666    'identity': identity,
19667    'isUndefined': isUndefined,
19668    'isDefined': isDefined,
19669    'isString': isString,
19670    'isFunction': isFunction,
19671    'isObject': isObject,
19672    'isNumber': isNumber,
19673    'isElement': isElement,
19674    'isArray': isArray,
19675    'version': version,
19676    'isDate': isDate,
19677    'lowercase': lowercase,
19678    'uppercase': uppercase,
19679    'callbacks': {counter: 0},
19680    'getTestability': getTestability,
19681    '$$minErr': minErr,
19682    '$$csp': csp,
19683    'reloadWithDebugInfo': reloadWithDebugInfo
19684  });
19685
19686  angularModule = setupModuleLoader(window);
19687
19688  angularModule('ng', ['ngLocale'], ['$provide',
19689    function ngModule($provide) {
19690      // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
19691      $provide.provider({
19692        $$sanitizeUri: $$SanitizeUriProvider
19693      });
19694      $provide.provider('$compile', $CompileProvider).
19695        directive({
19696            a: htmlAnchorDirective,
19697            input: inputDirective,
19698            textarea: inputDirective,
19699            form: formDirective,
19700            script: scriptDirective,
19701            select: selectDirective,
19702            style: styleDirective,
19703            option: optionDirective,
19704            ngBind: ngBindDirective,
19705            ngBindHtml: ngBindHtmlDirective,
19706            ngBindTemplate: ngBindTemplateDirective,
19707            ngClass: ngClassDirective,
19708            ngClassEven: ngClassEvenDirective,
19709            ngClassOdd: ngClassOddDirective,
19710            ngCloak: ngCloakDirective,
19711            ngController: ngControllerDirective,
19712            ngForm: ngFormDirective,
19713            ngHide: ngHideDirective,
19714            ngIf: ngIfDirective,
19715            ngInclude: ngIncludeDirective,
19716            ngInit: ngInitDirective,
19717            ngNonBindable: ngNonBindableDirective,
19718            ngPluralize: ngPluralizeDirective,
19719            ngRepeat: ngRepeatDirective,
19720            ngShow: ngShowDirective,
19721            ngStyle: ngStyleDirective,
19722            ngSwitch: ngSwitchDirective,
19723            ngSwitchWhen: ngSwitchWhenDirective,
19724            ngSwitchDefault: ngSwitchDefaultDirective,
19725            ngOptions: ngOptionsDirective,
19726            ngTransclude: ngTranscludeDirective,
19727            ngModel: ngModelDirective,
19728            ngList: ngListDirective,
19729            ngChange: ngChangeDirective,
19730            pattern: patternDirective,
19731            ngPattern: patternDirective,
19732            required: requiredDirective,
19733            ngRequired: requiredDirective,
19734            minlength: minlengthDirective,
19735            ngMinlength: minlengthDirective,
19736            maxlength: maxlengthDirective,
19737            ngMaxlength: maxlengthDirective,
19738            ngValue: ngValueDirective,
19739            ngModelOptions: ngModelOptionsDirective
19740        }).
19741        directive({
19742          ngInclude: ngIncludeFillContentDirective
19743        }).
19744        directive(ngAttributeAliasDirectives).
19745        directive(ngEventDirectives);
19746      $provide.provider({
19747        $anchorScroll: $AnchorScrollProvider,
19748        $animate: $AnimateProvider,
19749        $animateCss: $CoreAnimateCssProvider,
19750        $$animateQueue: $$CoreAnimateQueueProvider,
19751        $$AnimateRunner: $$CoreAnimateRunnerProvider,
19752        $browser: $BrowserProvider,
19753        $cacheFactory: $CacheFactoryProvider,
19754        $controller: $ControllerProvider,
19755        $document: $DocumentProvider,
19756        $exceptionHandler: $ExceptionHandlerProvider,
19757        $filter: $FilterProvider,
19758        $$forceReflow: $$ForceReflowProvider,
19759        $interpolate: $InterpolateProvider,
19760        $interval: $IntervalProvider,
19761        $http: $HttpProvider,
19762        $httpParamSerializer: $HttpParamSerializerProvider,
19763        $httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
19764        $httpBackend: $HttpBackendProvider,
19765        $xhrFactory: $xhrFactoryProvider,
19766        $location: $LocationProvider,
19767        $log: $LogProvider,
19768        $parse: $ParseProvider,
19769        $rootScope: $RootScopeProvider,
19770        $q: $QProvider,
19771        $$q: $$QProvider,
19772        $sce: $SceProvider,
19773        $sceDelegate: $SceDelegateProvider,
19774        $sniffer: $SnifferProvider,
19775        $templateCache: $TemplateCacheProvider,
19776        $templateRequest: $TemplateRequestProvider,
19777        $$testability: $$TestabilityProvider,
19778        $timeout: $TimeoutProvider,
19779        $window: $WindowProvider,
19780        $$rAF: $$RAFProvider,
19781        $$jqLite: $$jqLiteProvider,
19782        $$HashMap: $$HashMapProvider,
19783        $$cookieReader: $$CookieReaderProvider
19784      });
19785    }
19786  ]);
19787}
19788
19789/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19790 *     Any commits to this file should be reviewed with security in mind.  *
19791 *   Changes to this file can potentially create security vulnerabilities. *
19792 *          An approval from 2 Core members with history of modifying      *
19793 *                         this file is required.                          *
19794 *                                                                         *
19795 *  Does the change somehow allow for arbitrary javascript to be executed? *
19796 *    Or allows for someone to change the prototype of built-in objects?   *
19797 *     Or gives undesired access to variables likes document or window?    *
19798 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
19799
19800/* global JQLitePrototype: true,
19801  addEventListenerFn: true,
19802  removeEventListenerFn: true,
19803  BOOLEAN_ATTR: true,
19804  ALIASED_ATTR: true,
19805*/
19806
19807//////////////////////////////////
19808//JQLite
19809//////////////////////////////////
19810
19811/**
19812 * @ngdoc function
19813 * @name angular.element
19814 * @module ng
19815 * @kind function
19816 *
19817 * @description
19818 * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
19819 *
19820 * If jQuery is available, `angular.element` is an alias for the
19821 * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
19822 * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite."
19823 *
19824 * <div class="alert alert-success">jqLite is a tiny, API-compatible subset of jQuery that allows
19825 * Angular to manipulate the DOM in a cross-browser compatible way. **jqLite** implements only the most
19826 * commonly needed functionality with the goal of having a very small footprint.</div>
19827 *
19828 * To use `jQuery`, simply ensure it is loaded before the `angular.js` file.
19829 *
19830 * <div class="alert">**Note:** all element references in Angular are always wrapped with jQuery or
19831 * jqLite; they are never raw DOM references.</div>
19832 *
19833 * ## Angular's jqLite
19834 * jqLite provides only the following jQuery methods:
19835 *
19836 * - [`addClass()`](http://api.jquery.com/addClass/)
19837 * - [`after()`](http://api.jquery.com/after/)
19838 * - [`append()`](http://api.jquery.com/append/)
19839 * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
19840 * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
19841 * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
19842 * - [`clone()`](http://api.jquery.com/clone/)
19843 * - [`contents()`](http://api.jquery.com/contents/)
19844 * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`. As a setter, does not convert numbers to strings or append 'px'.
19845 * - [`data()`](http://api.jquery.com/data/)
19846 * - [`detach()`](http://api.jquery.com/detach/)
19847 * - [`empty()`](http://api.jquery.com/empty/)
19848 * - [`eq()`](http://api.jquery.com/eq/)
19849 * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
19850 * - [`hasClass()`](http://api.jquery.com/hasClass/)
19851 * - [`html()`](http://api.jquery.com/html/)
19852 * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
19853 * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
19854 * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces, selectors or event object as parameter
19855 * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
19856 * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
19857 * - [`prepend()`](http://api.jquery.com/prepend/)
19858 * - [`prop()`](http://api.jquery.com/prop/)
19859 * - [`ready()`](http://api.jquery.com/ready/)
19860 * - [`remove()`](http://api.jquery.com/remove/)
19861 * - [`removeAttr()`](http://api.jquery.com/removeAttr/)
19862 * - [`removeClass()`](http://api.jquery.com/removeClass/)
19863 * - [`removeData()`](http://api.jquery.com/removeData/)
19864 * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
19865 * - [`text()`](http://api.jquery.com/text/)
19866 * - [`toggleClass()`](http://api.jquery.com/toggleClass/)
19867 * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
19868 * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces or event object as parameter
19869 * - [`val()`](http://api.jquery.com/val/)
19870 * - [`wrap()`](http://api.jquery.com/wrap/)
19871 *
19872 * ## jQuery/jqLite Extras
19873 * Angular also provides the following additional methods and events to both jQuery and jqLite:
19874 *
19875 * ### Events
19876 * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction a
19876pis and fires this event
19877 *    on all DOM nodes being removed.  This can be used to clean up any 3rd party bindings to the DOM
19878 *    element before it is removed.
19879 *
19880 * ### Methods
19881 * - `controller(name)` - retrieves the controller of the current element or its parent. By default
19882 *   retrieves controller associated with the `ngController` directive. If `name` is provided as
19883 *   camelCase directive name, then the controller for this directive will be retrieved (e.g.
19884 *   `'ngModel'`).
19885 * - `injector()` - retrieves the injector of the current element or its parent.
19886 * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
19887 *   element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to
19888 *   be enabled.
19889 * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
19890 *   current element. This getter should be used only on elements that contain a directive which starts a new isolate
19891 *   scope. Calling `scope()` on this element always returns the original non-isolate scope.
19892 *   Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
19893 * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
19894 *   parent element is reached.
19895 *
19896 * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
19897 * @returns {Object} jQuery object.
19898 */
19899
19900JQLite.expando = 'ng339';
19901
19902var jqCache = JQLite.cache = {},
19903    jqId = 1,
19904    addEventListenerFn = function(element, type, fn) {
19905      element.addEventListener(type, fn, false);
19906    },
19907    removeEventListenerFn = function(element, type, fn) {
19908      element.removeEventListener(type, fn, false);
19909    };
19910
19911/*
19912 * !!! This is an undocumented "private" function !!!
19913 */
19914JQLite._data = function(node) {
19915  //jQuery always returns an object on cache miss
19916  return this.cache[node[this.expando]] || {};
19917};
19918
19919function jqNextId() { return ++jqId; }
19920
19921
19922var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
19923var MOZ_HACK_REGEXP = /^moz([A-Z])/;
19924var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"};
19925var jqLiteMinErr = minErr('jqLite');
19926
19927/**
19928 * Converts snake_case to camelCase.
19929 * Also there is special case for Moz prefix starting with upper case letter.
19930 * @param name Name to normalize
19931 */
19932function camelCase(name) {
19933  return name.
19934    replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
19935      return offset ? letter.toUpperCase() : letter;
19936    }).
19937    replace(MOZ_HACK_REGEXP, 'Moz$1');
19938}
19939
19940var SINGLE_TAG_REGEXP = /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/;
19941var HTML_REGEXP = /<|&#?\w+;/;
19942var TAG_NAME_REGEXP = /<([\w:-]+)/;
19943var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi;
19944
19945var wrapMap = {
19946  'option': [1, '<select multiple="multiple">', '</select>'],
19947
19948  'thead': [1, '<table>', '</table>'],
19949  'col': [2, '<table><colgroup>', '</colgroup></table>'],
19950  'tr': [2, '<table><tbody>', '</tbody></table>'],
19951  'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],
19952  '_default': [0, "", ""]
19953};
19954
19955wrapMap.optgroup = wrapMap.option;
19956wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
19957wrapMap.th = wrapMap.td;
19958
19959
19960function jqLiteIsTextNode(html) {
19961  return !HTML_REGEXP.test(html);
19962}
19963
19964function jqLiteAcceptsData(node) {
19965  // The window object can accept data but has no nodeType
19966  // Otherwise we are only interested in elements (1) and documents (9)
19967  var nodeType = node.nodeType;
19968  return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
19969}
19970
19971function jqLiteHasData(node) {
19972  for (var key in jqCache[node.ng339]) {
19973    return true;
19974  }
19975  return false;
19976}
19977
19978function jqLiteBuildFragment(html, context) {
19979  var tmp, tag, wrap,
19980      fragment = context.createDocumentFragment(),
19981      nodes = [], i;
19982
19983  if (jqLiteIsTextNode(html)) {
19984    // Convert non-html into a text node
19985    nodes.push(context.createTextNode(html));
19986  } else {
19987    // Convert html into DOM nodes
19988    tmp = tmp || fragment.appendChild(context.createElement("div"));
19989    tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase();
19990    wrap = wrapMap[tag] || wrapMap._default;
19991    tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
19992
19993    // Descend through wrappers to the right content
19994    i = wrap[0];
19995    while (i--) {
19996      tmp = tmp.lastChild;
19997    }
19998
19999    nodes = concat(nodes, tmp.childNodes);
20000
20001    tmp = fragment.firstChild;
20002    tmp.textContent = "";
20003  }
20004
20005  // Remove wrapper from fragment
20006  fragment.textContent = "";
20007  fragment.innerHTML = ""; // Clear inner HTML
20008  forEach(nodes, function(node) {
20009    fragment.appendChild(node);
20010  });
20011
20012  return fragment;
20013}
20014
20015function jqLiteParseHTML(html, context) {
20016  context = context || document;
20017  var parsed;
20018
20019  if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
20020    return [context.createElement(parsed[1])];
20021  }
20022
20023  if ((parsed = jqLiteBuildFragment(html, context))) {
20024    return parsed.childNodes;
20025  }
20026
20027  return [];
20028}
20029
20030/////////////////////////////////////////////
20031function JQLite(element) {
20032  if (element instanceof JQLite) {
20033    return element;
20034  }
20035
20036  var argIsString;
20037
20038  if (isString(element)) {
20039    element = trim(element);
20040    argIsString = true;
20041  }
20042  if (!(this instanceof JQLite)) {
20043    if (argIsString && element.charAt(0) != '<') {
20044      throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
20045    }
20046    return new JQLite(element);
20047  }
20048
20049  if (argIsString) {
20050    jqLiteAddNodes(this, jqLiteParseHTML(element));
20051  } else {
20052    jqLiteAddNodes(this, element);
20053  }
20054}
20055
20056function jqLiteClone(element) {
20057  return element.cloneNode(true);
20058}
20059
20060function jqLiteDealoc(element, onlyDescendants) {
20061  if (!onlyDescendants) jqLiteRemoveData(element);
20062
20063  if (element.querySelectorAll) {
20064    var descendants = element.querySelectorAll('*');
20065    for (var i = 0, l = descendants.length; i < l; i++) {
20066      jqLiteRemoveData(descendants[i]);
20067    }
20068  }
20069}
20070
20071function jqLiteOff(element, type, fn, unsupported) {
20072  if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
20073
20074  var expandoStore = jqLiteExpandoStore(element);
20075  var events = expandoStore && expandoStore.events;
20076  var handle = expandoStore && expandoStore.handle;
20077
20078  if (!handle) return; //no listeners registered
20079
20080  if (!type) {
20081    for (type in events) {
20082      if (type !== '$destroy') {
20083        removeEventListenerFn(element, type, handle);
20084      }
20085      delete events[type];
20086    }
20087  } else {
20088    forEach(type.split(' '), function(type) {
20089      if (isDefined(fn)) {
20090        var listenerFns = events[type];
20091        arrayRemove(listenerFns || [], fn);
20092        if (listenerFns && listenerFns.length > 0) {
20093          return;
20094        }
20095      }
20096
20097      removeEventListenerFn(element, type, handle);
20098      delete events[type];
20099    });
20100  }
20101}
20102
20103function jqLiteRemoveData(element, name) {
20104  var expandoId = element.ng339;
20105  var expandoStore = expandoId && jqCache[expandoId];
20106
20107  if (expandoStore) {
20108    if (name) {
20109      delete expandoStore.data[name];
20110      return;
20111    }
20112
20113    if (expandoStore.handle) {
20114      if (expandoStore.events.$destroy) {
20115        expandoStore.handle({}, '$destroy');
20116      }
20117      jqLiteOff(element);
20118    }
20119    delete jqCache[expandoId];
20120    element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
20121  }
20122}
20123
20124
20125function jqLiteExpandoStore(element, createIfNecessary) {
20126  var expandoId = element.ng339,
20127      expandoStore = expandoId && jqCache[expandoId];
20128
20129  if (createIfNecessary && !expandoStore) {
20130    element.ng339 = expandoId = jqNextId();
20131    expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined};
20132  }
20133
20134  return expandoStore;
20135}
20136
20137
20138function jqLiteData(element, key, value) {
20139  if (jqLiteAcceptsData(element)) {
20140
20141    var isSimpleSetter = isDefined(value);
20142    var isSimpleGetter = !isSimpleSetter && key && !isObject(key);
20143    var massGetter = !key;
20144    var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter);
20145    var data = expandoStore && expandoStore.data;
20146
20147    if (isSimpleSetter) { // data('key', value)
20148      data[key] = value;
20149    } else {
20150      if (massGetter) {  // data()
20151        return data;
20152      } else {
20153        if (isSimpleGetter) { // data('key')
20154          // don't force creation of expandoStore if it doesn't exist yet
20155          return data && data[key];
20156        } else { // mass-setter: data({key1: val1, key2: val2})
20157          extend(data, key);
20158        }
20159      }
20160    }
20161  }
20162}
20163
20164function jqLiteHasClass(element, selector) {
20165  if (!element.getAttribute) return false;
20166  return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " ").
20167      indexOf(" " + selector + " ") > -1);
20168}
20169
20170function jqLiteRemoveClass(element, cssClasses) {
20171  if (cssClasses && element.setAttribute) {
20172    forEach(cssClasses.split(' '), function(cssClass) {
20173      element.setAttribute('class', trim(
20174          (" " + (element.getAttribute('class') || '') + " ")
20175          .replace(/[\n\t]/g, " ")
20176          .replace(" " + trim(cssClass) + " ", " "))
20177      );
20178    });
20179  }
20180}
20181
20182function jqLiteAddClass(element, cssClasses) {
20183  if (cssClasses && element.setAttribute) {
20184    var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
20185                            .replace(/[\n\t]/g, " ");
20186
20187    forEach(cssClasses.split(' '), function(cssClass) {
20188      cssClass = trim(cssClass);
20189      if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
20190        existingClasses += cssClass + ' ';
20191      }
20192    });
20193
20194    element.setAttribute('class', trim(existingClasses));
20195  }
20196}
20197
20198
20199function jqLiteAddNodes(root, elements) {
20200  // THIS CODE IS VERY HOT. Don't make changes without benchmarking.
20201
20202  if (elements) {
20203
20204    // if a Node (the most common case)
20205    if (elements.nodeType) {
20206      root[root.length++] = elements;
20207    } else {
20208      var length = elements.length;
20209
20210      // if an Array or NodeList and not a Window
20211      if (typeof length === 'number' && elements.window !== elements) {
20212        if (length) {
20213          for (var i = 0; i < length; i++) {
20214            root[root.length++] = elements[i];
20215          }
20216        }
20217      } else {
20218        root[root.length++] = elements;
20219      }
20220    }
20221  }
20222}
20223
20224
20225function jqLiteController(element, name) {
20226  return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller');
20227}
20228
20229function jqLiteInheritedData(element, name, value) {
20230  // if element is the document object work with the html element instead
20231  // this makes $(document).scope() possible
20232  if (element.nodeType == NODE_TYPE_DOCUMENT) {
20233    element = element.documentElement;
20234  }
20235  var names = isArray(name) ? name : [name];
20236
20237  while (element) {
20238    for (var i = 0, ii = names.length; i < ii; i++) {
20239      if (isDefined(value = jqLite.data(element, names[i]))) return value;
20240    }
20241
20242    // If dealing with a document fragment node with a host element, and no parent, use the host
20243    // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
20244    // to lookup parent controllers.
20245    element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
20246  }
20247}
20248
20249function jqLiteEmpty(element) {
20250  jqLiteDealoc(element, true);
20251  while (element.firstChild) {
20252    element.removeChild(element.firstChild);
20253  }
20254}
20255
20256function jqLiteRemove(element, keepData) {
20257  if (!keepData) jqLiteDealoc(element);
20258  var parent = element.parentNode;
20259  if (parent) parent.removeChild(element);
20260}
20261
20262
20263function jqLiteDocumentLoaded(action, win) {
20264  win = win || window;
20265  if (win.document.readyState === 'complete') {
20266    // Force the action to be run async for consistent behaviour
20267    // from the action's point of view
20268    // i.e. it will definitely not be in a $apply
20269    win.setTimeout(action);
20270  } else {
20271    // No need to unbind this handler as load is only ever called once
20272    jqLite(win).on('load', action);
20273  }
20274}
20275
20276//////////////////////////////////////////
20277// Functions which are declared directly.
20278//////////////////////////////////////////
20279var JQLitePrototype = JQLite.prototype = {
20280  ready: function(fn) {
20281    var fired = false;
20282
20283    function trigger() {
20284      if (fired) return;
20285      fired = true;
20286      fn();
20287    }
20288
20289    // check if document is already loaded
20290    if (document.readyState === 'complete') {
20291      setTimeout(trigger);
20292    } else {
20293      this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
20294      // we can not use jqLite since we are not done loading and jQuery could be loaded later.
20295      // jshint -W064
20296      JQLite(window).on('load', trigger); // fallback to window.onload for others
20297      // jshint +W064
20298    }
20299  },
20300  toString: function() {
20301    var value = [];
20302    forEach(this, function(e) { value.push('' + e);});
20303    return '[' + value.join(', ') + ']';
20304  },
20305
20306  eq: function(index) {
20307      return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
20308  },
20309
20310  length: 0,
20311  push: push,
20312  sort: [].sort,
20313  splice: [].splice
20314};
20315
20316//////////////////////////////////////////
20317// Functions iterating getter/setters.
20318// these functions return self on setter and
20319// value on get.
20320//////////////////////////////////////////
20321var BOOLEAN_ATTR = {};
20322forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
20323  BOOLEAN_ATTR[lowercase(value)] = value;
20324});
20325var BOOLEAN_ELEMENTS = {};
20326forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
20327  BOOLEAN_ELEMENTS[value] = true;
20328});
20329var ALIASED_ATTR = {
20330  'ngMinlength': 'minlength',
20331  'ngMaxlength': 'maxlength',
20332  'ngMin': 'min',
20333  'ngMax': 'max',
20334  'ngPattern': 'pattern'
20335};
20336
20337function getBooleanAttrName(element, name) {
20338  // check dom last since we will most likely fail on name
20339  var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
20340
20341  // booleanAttr is here twice to minimize DOM access
20342  return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
20343}
20344
20345function getAliasedAttrName(name) {
20346  return ALIASED_ATTR[name];
20347}
20348
20349forEach({
20350  data: jqLiteData,
20351  removeData: jqLiteRemoveData,
20352  hasData: jqLiteHasData
20353}, function(fn, name) {
20354  JQLite[name] = fn;
20355});
20356
20357forEach({
20358  data: jqLiteData,
20359  inheritedData: jqLiteInheritedData,
20360
20361  scope: function(element) {
20362    // Can't use jqLiteData here directly so we stay compatible with jQuery!
20363    return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
20364  },
20365
20366  isolateScope: function(element) {
20367    // Can't use jqLiteData here directly so we stay compatible with jQuery!
20368    return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
20369  },
20370
20371  controller: jqLiteController,
20372
20373  injector: function(element) {
20374    return jqLiteInheritedData(element, '$injector');
20375  },
20376
20377  removeAttr: function(element, name) {
20378    element.removeAttribute(name);
20379  },
20380
20381  hasClass: jqLiteHasClass,
20382
20383  css: function(element, name, value) {
20384    name = camelCase(name);
20385
20386    if (isDefined(value)) {
20387      element.style[name] = value;
20388    } else {
20389      return element.style[name];
20390    }
20391  },
20392
20393  attr: function(element, name, value) {
20394    var nodeType = element.nodeType;
20395    if (nodeType === NODE_TYPE_TEXT || nodeType === NODE_TYPE_ATTRIBUTE || nodeType === NODE_TYPE_COMMENT) {
20396      return;
20397    }
20398    var lowercasedName = lowercase(name);
20399    if (BOOLEAN_ATTR[lowercasedName]) {
20400      if (isDefined(value)) {
20401        if (!!value) {
20402          element[name] = true;
20403          element.setAttribute(name, lowercasedName);
20404        } else {
20405          element[name] = false;
20406          element.removeAttribute(lowercasedName);
20407        }
20408      } else {
20409        return (element[name] ||
20410                 (element.attributes.getNamedItem(name) || noop).specified)
20411               ? lowercasedName
20412               : undefined;
20413      }
20414    } else if (isDefined(value)) {
20415      element.setAttribute(name, value);
20416    } else if (element.getAttribute) {
20417      // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
20418      // some elements (e.g. Document) don't have get attribute, so return undefined
20419      var ret = element.getAttribute(name, 2);
20420      // normalize non-existing attributes to undefined (as jQuery)
20421      return ret === null ? undefined : ret;
20422    }
20423  },
20424
20425  prop: function(element, name, value) {
20426    if (isDefined(value)) {
20427      element[name] = value;
20428    } else {
20429      return element[name];
20430    }
20431  },
20432
20433  text: (function() {
20434    getText.$dv = '';
20435    return getText;
20436
20437    function getText(element, value) {
20438      if (isUndefined(value)) {
20439        var nodeType = element.nodeType;
20440        return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
20441      }
20442      element.textContent = value;
20443    }
20444  })(),
20445
20446  val: function(element, value) {
20447    if (isUndefined(value)) {
20448      if (element.multiple && nodeName_(element) === 'select') {
20449        var result = [];
vendor: 3,896 bytes, lines 20450-20579
20450        forEach(element.options, function(option) {
20451          if (option.selected) {
20452            result.push(option.value || option.text);
20453          }
20454        });
20455        return result.length === 0 ? null : result;
20456      }
20457      return element.value;
20458    }
20459    element.value = value;
20460  },
20461
20462  html: function(element, value) {
20463    if (isUndefined(value)) {
20464      return element.innerHTML;
20465    }
20466    jqLiteDealoc(element, true);
20467    element.innerHTML = value;
20468  },
20469
20470  empty: jqLiteEmpty
20471}, function(fn, name) {
20472  /**
20473   * Properties: writes return selection, reads return first value
20474   */
20475  JQLite.prototype[name] = function(arg1, arg2) {
20476    var i, key;
20477    var nodeCount = this.length;
20478
20479    // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
20480    // in a way that survives minification.
20481    // jqLiteEmpty takes no arguments but is a setter.
20482    if (fn !== jqLiteEmpty &&
20483        (isUndefined((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2))) {
20484      if (isObject(arg1)) {
20485
20486        // we are a write, but the object properties are the key/values
20487        for (i = 0; i < nodeCount; i++) {
20488          if (fn === jqLiteData) {
20489            // data() takes the whole object in jQuery
20490            fn(this[i], arg1);
20491          } else {
20492            for (key in arg1) {
20493              fn(this[i], key, arg1[key]);
20494            }
20495          }
20496        }
20497        // return self for chaining
20498        return this;
20499      } else {
20500        // we are a read, so read the first child.
20501        // TODO: do we still need this?
20502        var value = fn.$dv;
20503        // Only if we have $dv do we iterate over all, otherwise it is just the first element.
20504        var jj = (isUndefined(value)) ? Math.min(nodeCount, 1) : nodeCount;
20505        for (var j = 0; j < jj; j++) {
20506          var nodeValue = fn(this[j], arg1, arg2);
20507          value = value ? value + nodeValue : nodeValue;
20508        }
20509        return value;
20510      }
20511    } else {
20512      // we are a write, so apply to all children
20513      for (i = 0; i < nodeCount; i++) {
20514        fn(this[i], arg1, arg2);
20515      }
20516      // return self for chaining
20517      return this;
20518    }
20519  };
20520});
20521
20522function createEventHandler(element, events) {
20523  var eventHandler = function(event, type) {
20524    // jQuery specific api
20525    event.isDefaultPrevented = function() {
20526      return event.defaultPrevented;
20527    };
20528
20529    var eventFns = events[type || event.type];
20530    var eventFnsLength = eventFns ? eventFns.length : 0;
20531
20532    if (!eventFnsLength) return;
20533
20534    if (isUndefined(event.immediatePropagationStopped)) {
20535      var originalStopImmediatePropagation = event.stopImmediatePropagation;
20536      event.stopImmediatePropagation = function() {
20537        event.immediatePropagationStopped = true;
20538
20539        if (event.stopPropagation) {
20540          event.stopPropagation();
20541        }
20542
20543        if (originalStopImmediatePropagation) {
20544          originalStopImmediatePropagation.call(event);
20545        }
20546      };
20547    }
20548
20549    event.isImmediatePropagationStopped = function() {
20550      return event.immediatePropagationStopped === true;
20551    };
20552
20553    // Copy event handlers in case event handlers array is modified during execution.
20554    if ((eventFnsLength > 1)) {
20555      eventFns = shallowCopy(eventFns);
20556    }
20557
20558    for (var i = 0; i < eventFnsLength; i++) {
20559      if (!event.isImmediatePropagationStopped()) {
20560        eventFns[i].call(element, event);
20561      }
20562    }
20563  };
20564
20565  // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all
20566  //       events on `element`
20567  eventHandler.elem = element;
20568  return eventHandler;
20569}
20570
20571//////////////////////////////////////////
20572// Functions iterating traversal.
20573// These functions chain results into a single
20574// selector.
20575//////////////////////////////////////////
20576forEach({
20577  removeData: jqLiteRemoveData,
20578
20579  on: function jqLiteOn(element, type, fn, unsupported) {
20580    if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
20581
20582    // Do not add event handlers to non-elements because they will not be cleaned up.
20583    if (!jqLiteAcceptsData(element)) {
20584      return;
20585    }
20586
20587    var expandoStore = jqLiteExpandoStore(element, true);
20588    var events = expandoStore.events;
20589    var handle = expandoStore.handle;
20590
20591    if (!handle) {
20592      handle = expandoStore.handle = createEventHandler(element, events);
20593    }
20594
20595    // http://jsperf.com/string-indexof-vs-split
20596    var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
20597    var i = types.length;
20598
20599    while (i--) {
20600      type = types[i];
20601      var eventFns = events[type];
20602
20603      if (!eventFns) {
20604        events[type] = [];
20605
20606        if (type === 'mouseenter' || type === 'mouseleave') {
20607          // Refer to jQuery's implementation of mouseenter & mouseleave
20608          // Read about mouseenter and mouseleave:
20609          // http://www.quirksmode.org/js/events_mouse.html#link8
20610
20611          jqLiteOn(element, MOUSE_EVENT_MAP[type], function(event) {
20612            var target = this, related = event.relatedTarget;
20613            // For mousenter/leave call the handler if related is outside the target.
20614            // NB: No relatedTarget if the mouse left/entered the browser window
20615            if (!related || (related !== target && !target.contains(related))) {
20616              handle(event, type);
20617            }
20618          });
20619
20620        } else {
20621          if (type !== '$destroy') {
20622            addEventListenerFn(element, type, handle);
20623          }
20624        }
20625        eventFns = events[type];
20626      }
20627      eventFns.push(fn);
20628    }
20629  },
20630
20631  off: jqLiteOff,
20632
20633  one: function(element, type, fn) {
20634    element = jqLite(element);
20635
20636    //add the listener twice so that when it is called
20637    //you can remove the original function and still be
20638    //able to call element.off(ev, fn) normally
20639    element.on(type, function onFn() {
20640      element.off(type, fn);
20641      element.off(type, onFn);
20642    });
20643    element.on(type, fn);
20644  },
20645
20646  replaceWith: function(element, replaceNode) {
20647    var index, parent = element.parentNode;
20648    jqLiteDealoc(element);
20649    forEach(new JQLite(replaceNode), function(node) {
20650      if (index) {
20651        parent.insertBefore(node, index.nextSibling);
20652      } else {
20653        parent.replaceChild(node, element);
20654      }
20655      index = node;
20656    });
20657  },
20658
20659  children: function(element) {
20660    var children = [];
20661    forEach(element.childNodes, function(element) {
20662      if (element.nodeType === NODE_TYPE_ELEMENT) {
20663        children.push(element);
20664      }
20665    });
20666    return children;
20667  },
20668
20669  contents: function(element) {
20670    return element.contentDocument || element.childNodes || [];
20671  },
20672
20673  append: function(element, node) {
20674    var nodeType = element.nodeType;
20675    if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
20676
20677    node = new JQLite(node);
20678
20679    for (var i = 0, ii = node.length; i < ii; i++) {
20680      var child = node[i];
20681      element.appendChild(child);
20682    }
20683  },
20684
20685  prepend: function(element, node) {
20686    if (element.nodeType === NODE_TYPE_ELEMENT) {
20687      var index = element.firstChild;
20688      forEach(new JQLite(node), function(child) {
20689        element.insertBefore(child, index);
20690      });
20691    }
20692  },
20693
20694  wrap: function(element, wrapNode) {
20695    wrapNode = jqLite(wrapNode).eq(0).clone()[0];
20696    var parent = element.parentNode;
20697    if (parent) {
20698      parent.replaceChild(wrapNode, element);
20699    }
20700    wrapNode.appendChild(element);
20701  },
20702
20703  remove: jqLiteRemove,
20704
20705  detach: function(element) {
20706    jqLiteRemove(element, true);
20707  },
20708
20709  after: function(element, newElement) {
20710    var index = element, parent = element.parentNode;
20711    newElement = new JQLite(newElement);
20712
20713    for (var i = 0, ii = newElement.length; i < ii; i++) {
20714      var node = newElement[i];
20715      parent.insertBefore(node, index.nextSibling);
20716      index = node;
20717    }
20718  },
20719
20720  addClass: jqLiteAddClass,
20721  removeClass: jqLiteRemoveClass,
20722
20723  toggleClass: function(element, selector, condition) {
20724    if (selector) {
20725      forEach(selector.split(' '), function(className) {
20726        var classCondition = condition;
20727        if (isUndefined(classCondition)) {
20728          classCondition = !jqLiteHasClass(element, className);
20729        }
20730        (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);
20731      });
20732    }
20733  },
20734
20735  parent: function(element) {
20736    var parent = element.parentNode;
20737    return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;
20738  },
20739
20740  next: function(element) {
20741    return element.nextElementSibling;
20742  },
20743
20744  find: function(element, selector) {
20745    if (element.getElementsByTagName) {
20746      return element.getElementsByTagName(selector);
20747    } else {
20748      return [];
20749    }
20750  },
20751
20752  clone: jqLiteClone,
20753
20754  triggerHandler: function(element, event, extraParameters) {
20755
20756    var dummyEvent, eventFnsCopy, handlerArgs;
20757    var eventName = event.type || event;
20758    var expandoStore = jqLiteExpandoStore(element);
20759    var events = expandoStore && expandoStore.events;
20760    var eventFns = events && events[eventName];
20761
20762    if (eventFns) {
20763      // Create a dummy event to pass to the handlers
20764      dummyEvent = {
20765        preventDefault: function() { this.defaultPrevented = true; },
20766        isDefaultPrevented: function() { return this.defaultPrevented === true; },
20767        stopImmediatePropagation: function() { this.immediatePropagationStopped = true; },
20768        isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; },
20769        stopPropagation: noop,
20770        type: eventName,
20771        target: element
20772      };
20773
20774      // If a custom event was provided then extend our dummy event with it
20775      if (event.type) {
20776        dummyEvent = extend(dummyEvent, event);
20777      }
20778
20779      // Copy event handlers in case event handlers array is modified during execution.
20780      eventFnsCopy = shallowCopy(eventFns);
20781      handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
20782
20783      forEach(eventFnsCopy, function(fn) {
20784        if (!dummyEvent.isImmediatePropagationStopped()) {
20785          fn.apply(element, handlerArgs);
20786        }
20787      });
20788    }
20789  }
20790}, function(fn, name) {
20791  /**
20792   * chaining functions
20793   */
20794  JQLite.prototype[name] = function(arg1, arg2, arg3) {
20795    var value;
20796
20797    for (var i = 0, ii = this.length; i < ii; i++) {
20798      if (isUndefined(value)) {
20799        value = fn(this[i], arg1, arg2, arg3);
20800        if (isDefined(value)) {
20801          // any function which returns a value needs to be wrapped
20802          value = jqLite(value);
20803        }
20804      } else {
20805        jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
20806      }
20807    }
20808    return isDefined(value) ? value : this;
20809  };
20810
20811  // bind legacy bind/unbind to on/off
20812  JQLite.prototype.bind = JQLite.prototype.on;
20813  JQLite.prototype.unbind = JQLite.prototype.off;
20814});
20815
20816
20817// Provider for private $$jqLite service
20818function $$jqLiteProvider() {
20819  this.$get = function $$jqLite() {
20820    return extend(JQLite, {
20821      hasClass: function(node, classes) {
20822        if (node.attr) node = node[0];
20823        return jqLiteHasClass(node, classes);
20824      },
20825      addClass: function(node, classes) {
20826        if (node.attr) node = node[0];
20827        return jqLiteAddClass(node, classes);
20828      },
20829      removeClass: function(node, classes) {
20830        if (node.attr) node = node[0];
20831        return jqLiteRemoveClass(node, classes);
20832      }
20833    });
20834  };
20835}
20836
20837/**
20838 * Computes a hash of an 'obj'.
20839 * Hash of a:
20840 *  string is string
20841 *  number is number as string
20842 *  object is either result of calling $$hashKey function on the object or uniquely generated id,
20843 *         that is also assigned to the $$hashKey property of the object.
20844 *
20845 * @param obj
20846 * @returns {string} hash string such that the same input will have the same hash string.
20847 *         The resulting string key is in 'type:hashKey' format.
20848 */
20849function hashKey(obj, nextUidFn) {
20850  var key = obj && obj.$$hashKey;
20851
20852  if (key) {
20853    if (typeof key === 'function') {
20854      key = obj.$$hashKey();
20855    }
20856    return key;
20857  }
20858
20859  var objType = typeof obj;
20860  if (objType == 'function' || (objType == 'object' && obj !== null)) {
20861    key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)();
20862  } else {
20863    key = objType + ':' + obj;
20864  }
20865
20866  return key;
20867}
20868
20869/**
20870 * HashMap which can use objects as keys
20871 */
20872function HashMap(array, isolatedUid) {
20873  if (isolatedUid) {
20874    var uid = 0;
20875    this.nextUid = function() {
20876      return ++uid;
20877    };
20878  }
20879  forEach(array, this.put, this);
20880}
20881HashMap.prototype = {
20882  /**
20883   * Store key value pair
20884   * @param key key to store can be any type
20885   * @param value value to store can be any type
20886   */
20887  put: function(key, value) {
20888    this[hashKey(key, this.nextUid)] = value;
20889  },
20890
20891  /**
20892   * @param key
20893   * @returns {Object} the value for the key
20894   */
20895  get: function(key) {
20896    return this[hashKey(key, this.nextUid)];
20897  },
20898
20899  /**
20900   * Remove the key/value pair
20901   * @param key
20902   */
20903  remove: function(key) {
20904    var value = this[key = hashKey(key, this.nextUid)];
20905    delete this[key];
20906    return value;
20907  }
20908};
20909
20910var $$HashMapProvider = [function() {
20911  this.$get = [function() {
20912    return HashMap;
20913  }];
20914}];
20915
20916/**
20917 * @ngdoc function
20918 * @module ng
20919 * @name angular.injector
20920 * @kind function
20921 *
20922 * @description
20923 * Creates an injector object that can be used for retrieving services as well as for
20924 * dependency injection (see {@link guide/di dependency injection}).
20925 *
20926 * @param {Array.<string|Function>}
20926 modules A list of module functions or their aliases. See
20927 *     {@link angular.module}. The `ng` module must be explicitly added.
20928 * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which
20929 *     disallows argument name annotation inference.
20930 * @returns {injector} Injector object. See {@link auto.$injector $injector}.
20931 *
20932 * @example
20933 * Typical usage
20934 * ```js
20935 *   // create an injector
20936 *   var $injector = angular.injector(['ng']);
20937 *
20938 *   // use the injector to kick off your application
20939 *   // use the type inference to auto inject arguments, or use implicit injection
20940 *   $injector.invoke(function($rootScope, $compile, $document) {
20941 *     $compile($document)($rootScope);
20942 *     $rootScope.$digest();
20943 *   });
20944 * ```
20945 *
20946 * Sometimes you want to get access to the injector of a currently running Angular app
20947 * from outside Angular. Perhaps, you want to inject and compile some markup after the
20948 * application has been bootstrapped. You can do this using the extra `injector()` added
20949 * to JQuery/jqLite elements. See {@link angular.element}.
20950 *
20951 * *This is fairly rare but could be the case if a third party library is injecting the
20952 * markup.*
20953 *
20954 * In the following example a new block of HTML containing a `ng-controller`
20955 * directive is added to the end of the document body by JQuery. We then compile and link
20956 * it into the current AngularJS scope.
20957 *
20958 * ```js
20959 * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
20960 * $(document.body).append($div);
20961 *
20962 * angular.element(document).injector().invoke(function($compile) {
20963 *   var scope = angular.element($div).scope();
20964 *   $compile($div)(scope);
20965 * });
20966 * ```
20967 */
20968
20969
20970/**
20971 * @ngdoc module
20972 * @name auto
20973 * @description
20974 *
20975 * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
20976 */
20977
20978var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m;
20979var FN_ARG_SPLIT = /,/;
20980var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
20981var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
20982var $injectorMinErr = minErr('$injector');
20983
20984function anonFn(fn) {
20985  // For anonymous functions, showing at the very least the function signature can help in
20986  // debugging.
20987  var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
20988      args = fnText.match(FN_ARGS);
20989  if (args) {
20990    return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
20991  }
20992  return 'fn';
20993}
20994
20995function annotate(fn, strictDi, name) {
20996  var $inject,
20997      fnText,
20998      argDecl,
20999      last;
21000
21001  if (typeof fn === 'function') {
21002    if (!($inject = fn.$inject)) {
21003      $inject = [];
21004      if (fn.length) {
21005        if (strictDi) {
21006          if (!isString(name) || !name) {
21007            name = fn.name || anonFn(fn);
21008          }
21009          throw $injectorMinErr('strictdi',
21010            '{0} is not using explicit annotation and cannot be invoked in strict mode', name);
21011        }
21012        fnText = fn.toString().replace(STRIP_COMMENTS, '');
21013        argDecl = fnText.match(FN_ARGS);
21014        forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
21015          arg.replace(FN_ARG, function(all, underscore, name) {
21016            $inject.push(name);
21017          });
21018        });
21019      }
21020      fn.$inject = $inject;
21021    }
21022  } else if (isArray(fn)) {
21023    last = fn.length - 1;
21024    assertArgFn(fn[last], 'fn');
21025    $inject = fn.slice(0, last);
21026  } else {
21027    assertArgFn(fn, 'fn', true);
21028  }
21029  return $inject;
21030}
21031
21032///////////////////////////////////////
21033
21034/**
21035 * @ngdoc service
21036 * @name $injector
21037 *
21038 * @description
21039 *
21040 * `$injector` is used to retrieve object instances as defined by
21041 * {@link auto.$provide provider}, instantiate types, invoke methods,
21042 * and load modules.
21043 *
21044 * The following always holds true:
21045 *
21046 * ```js
21047 *   var $injector = angular.injector();
21048 *   expect($injector.get('$injector')).toBe($injector);
21049 *   expect($injector.invoke(function($injector) {
21050 *     return $injector;
21051 *   })).toBe($injector);
21052 * ```
21053 *
21054 * # Injection Function Annotation
21055 *
21056 * JavaScript does not have annotations, and annotations are needed for dependency injection. The
21057 * following are all valid ways of annotating function with injection arguments and are equivalent.
21058 *
21059 * ```js
21060 *   // inferred (only works if code not minified/obfuscated)
21061 *   $injector.invoke(function(serviceA){});
21062 *
21063 *   // annotated
21064 *   function explicit(serviceA) {};
21065 *   explicit.$inject = ['serviceA'];
21066 *   $injector.invoke(explicit);
21067 *
21068 *   // inline
21069 *   $injector.invoke(['serviceA', function(serviceA){}]);
21070 * ```
21071 *
21072 * ## Inference
21073 *
21074 * In JavaScript calling `toString()` on a function returns the function definition. The definition
21075 * can then be parsed and the function arguments can be extracted. This method of discovering
21076 * annotations is disallowed when the injector is in strict mode.
21077 * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the
21078 * argument names.
21079 *
21080 * ## `$inject` Annotation
21081 * By adding an `$inject` property onto a function the injection parameters can be specified.
21082 *
21083 * ## Inline
21084 * As an array of injection names, where the last item in the array is the function to call.
21085 */
21086
21087/**
21088 * @ngdoc method
21089 * @name $injector#get
21090 *
21091 * @description
21092 * Return an instance of the service.
21093 *
21094 * @param {string} name The name of the instance to retrieve.
21095 * @param {string=} caller An optional string to provide the origin of the function call for error messages.
21096 * @return {*} The instance.
21097 */
21098
21099/**
21100 * @ngdoc method
21101 * @name $injector#invoke
21102 *
21103 * @description
21104 * Invoke the method and supply the method arguments from the `$injector`.
21105 *
21106 * @param {Function|Array.<string|Function>} fn The injectable function to invoke. Function parameters are
21107 *   injected according to the {@link guide/di $inject Annotation} rules.
21108 * @param {Object=} self The `this` for the invoked method.
21109 * @param {Object=} locals Optional object. If preset then any argument names are read from this
21110 *                         object first, before the `$injector` is consulted.
21111 * @returns {*} the value returned by the invoked `fn` function.
21112 */
21113
21114/**
21115 * @ngdoc method
21116 * @name $injector#has
21117 *
21118 * @description
21119 * Allows the user to query if the particular service exists.
21120 *
21121 * @param {string} name Name of the service to query.
21122 * @returns {boolean} `true` if injector has given service.
21123 */
21124
21125/**
21126 * @ngdoc method
21127 * @name $injector#instantiate
21128 * @description
21129 * Create a new instance of JS type. The method takes a constructor function, invokes the new
21130 * operator, and supplies all of the arguments to the constructor function as specified by the
21131 * constructor annotation.
21132 *
21133 * @param {Function} Type Annotated constructor function.
21134 * @param {Object=} locals Optional object. If preset then any argument names are read from this
21135 * object first, before the `$injector` is consulted.
21136 * @returns {Object} new instance of `Type`.
21137 */
21138
21139/**
21140 * @ngdoc method
21141 * @name $injector#annotate
21142 *
21143 * @description
21144 * Returns an array of service names which the function is requesting for injection. This API is
21145 * used by the injector to determine which services need to be injected into the function when the
21146 * function is invoked. There are three ways in which the function can be annotated with the needed
21147 * dependencies.
21148 *
21149 * # Argument names
21150 *
21151 * The simplest form is to extract the dependencies from the arguments of the function. This is done
21152 * by converting the function into a string using `toString()` method and extracting the argument
21153 * names.
21154 * ```js
21155 *   // Given
21156 *   function MyController($scope, $route) {
21157 *     // ...
21158 *   }
21159 *
21160 *   // Then
21161 *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
21162 * ```
21163 *
21164 * You can disallow this method by using strict injection mode.
21165 *
21166 * This method does not work with code minification / obfuscation. For this reason the following
21167 * annotation strategies are supported.
21168 *
21169 * # The `$inject` property
21170 *
21171 * If a function has an `$inject` property and its value is an array of strings, then the strings
21172 * represent names of services to be injected into the function.
21173 * ```js
21174 *   // Given
21175 *   var MyController = function(obfuscatedScope, obfuscatedRoute) {
21176 *     // ...
21177 *   }
21178 *   // Define function dependencies
21179 *   MyController['$inject'] = ['$scope', '$route'];
21180 *
21181 *   // Then
21182 *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
21183 * ```
21184 *
21185 * # The array notation
21186 *
21187 * It is often desirable to inline Injected functions and that's when setting the `$inject` property
21188 * is very inconvenient. In these situations using the array notation to specify the dependencies in
21189 * a way that survives minification is a better choice:
21190 *
21191 * ```js
21192 *   // We wish to write this (not minification / obfuscation safe)
21193 *   injector.invoke(function($compile, $rootScope) {
21194 *     // ...
21195 *   });
21196 *
21197 *   // We are forced to write break inlining
21198 *   var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
21199 *     // ...
21200 *   };
21201 *   tmpFn.$inject = ['$compile', '$rootScope'];
21202 *   injector.invoke(tmpFn);
21203 *
21204 *   // To better support inline function the inline annotation is supported
21205 *   injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
21206 *     // ...
21207 *   }]);
21208 *
21209 *   // Therefore
21210 *   expect(injector.annotate(
21211 *      ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
21212 *    ).toEqual(['$compile', '$rootScope']);
21213 * ```
21214 *
21215 * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
21216 * be retrieved as described above.
21217 *
21218 * @param {boolean=} [strictDi=false] Disallow argument name annotation inference.
21219 *
21220 * @returns {Array.<string>} The names of the services which the function requires.
21221 */
21222
21223
21224
21225
21226/**
21227 * @ngdoc service
21228 * @name $provide
21229 *
21230 * @description
21231 *
21232 * The {@link auto.$provide $provide} service has a number of methods for registering components
21233 * with the {@link auto.$injector $injector}. Many of these functions are also exposed on
21234 * {@link angular.Module}.
21235 *
21236 * An Angular **service** is a singleton object created by a **service factory**.  These **service
21237 * factories** are functions which, in turn, are created by a **service provider**.
21238 * The **service providers** are constructor functions. When instantiated they must contain a
21239 * property called `$get`, which holds the **service factory** function.
21240 *
21241 * When you request a service, the {@link auto.$injector $injector} is responsible for finding the
21242 * correct **service provider**, instantiating it and then calling its `$get` **service factory**
21243 * function to get the instance of the **service**.
21244 *
21245 * Often services have no configuration options and there is no need to add methods to the service
21246 * provider.  The provider will be no more than a constructor function with a `$get` property. For
21247 * these cases the {@link auto.$provide $provide} service has additional helper methods to register
21248 * services without specifying a provider.
21249 *
21250 * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the
21251 *     {@link auto.$injector $injector}
21252 * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by
21253 *     providers and services.
21254 * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by
21255 *     services, not providers.
21256 * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`,
21257 *     that will be wrapped in a **service provider** object, whose `$get` property will contain the
21258 *     given factory function.
21259 * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class`
21260 *     that will be wrapped in a **service provider** object, whose `$get` property will instantiate
21261 *      a new object using the given constructor function.
21262 *
21263 * See the individual methods for more information and examples.
21264 */
21265
21266/**
21267 * @ngdoc method
21268 * @name $provide#provider
21269 * @description
21270 *
21271 * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions
21272 * are constructor functions, whose instances are responsible for "providing" a factory for a
21273 * service.
21274 *
21275 * Service provider names start with the name of the service they provide followed by `Provider`.
21276 * For example, the {@link ng.$log $log} service has a provider called
21277 * {@link ng.$logProvider $logProvider}.
21278 *
21279 * Service provider objects can have additional methods which allow configuration of the provider
21280 * and its service. Importantly, you can configure what kind of service is created by the `$get`
21281 * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
21282 * method {@link ng.$logProvider#debugEnabled debugEnabled}
21283 * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
21284 * console or not.
21285 *
21286 * @param {string} name The name of the instance. NOTE: the provider will be available under `name +
21287                        'Provider'` key.
21288 * @param {(Object|function())} provider If the provider is:
21289 *
21290 *   - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
21291 *     {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.
21292 *   - `Constructor`: a new instance of the provider will be created using
21293 *     {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.
21294 *
21295 * @returns {Object} registered provider instance
21296
21297 * @example
21298 *
21299 * The following example shows how to create a simple event tracking service and register it using
21300 * {@link auto.$provide#provider $provide.provider()}.
21301 *
21302 * ```js
21303 *  // Define the eventTracker provider
21304 *  function EventTrackerProvider() {
21305 *    var trackingUrl = '/track';
21306 *
21307 *    // A provider method for configuring where the tracked events should been saved
21308 *    this.setTrackingUrl = function(url) {
21309 *      trackingUrl = url;
21310 *    };
21311 *
21312 *    // The service factory function
21313 *    this.$get = ['$http', function($http) {
21314 *      var trackedEvents = {};
21315 *      return {
21316 *        // Call this to track an event
21317 *        event: function(event) {
21318 *          var count = trackedEvents[event] || 0;
21319 *          count += 1;
21320 *          trackedEvents[event] = count;
21321 *          return count;
21322 *        },
21323 *        // Call this to save the tracked events to the trackingUrl
21324 *        save: function() {
21325 *          $http.post(trackingUrl, trackedEvents);
21326 *        }
21327 *      };
21328 *    }];
21329 *  }
21330 *
21331 *  describe('eventTracker', function() {
21332 *    var postSpy;
21333 *
21334 *    beforeEach(module(function($provide) {
21335 *      // Register the eventTracker provider
21336 *      $provide.provider('eventTracker', EventTrackerProvider);
21337 *    }));
21338 *
21339 *    beforeEach(module(function(eventTrackerProvider) {
21340 *      // Configure eventTracker provider
21341 *      eventTrackerProvider.setTrackingUrl('/custom-track');
21342 *    }));
21343 *
21344 *    it('tracks events', inject(function(eventTracker) {
21345 *      expect(eventTracker.event('login')).toEqual(1);
21346 *      expect(eventTracker.event('login')).toEqual(2);
21347 *    }));
21348 *
21349 *    it('saves to the tracking url', inject(function(eventTracker, $http) {
21350 *      postSpy = spyOn($http, 'post');
21351 *      eventTracker.event('login');
21352 *      eventTracker.save();
21353 *      expect(postSpy).toHaveBeenCalled();
21354 *      expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
21355 *      expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
21356 *      expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
21357 *    }));
21358 *  });
21359 * ```
21360 */
21361
21362/**
21363 * @ngdoc method
21364 * @name $provide#factory
21365 * @description
21366 *
21367 * Register a **service factory**, which will be called to return the service instance.
21368 * This is short for registering a service where its provider consists of only a `$get` property,
21369 * which is the given service factory function.
21370 * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to
21371 * configure your service in a provider.
21372 *
21373 * @param {string} name The name of the instance.
21374 * @param {Function|Array.<string|Function>} $getFn The injectable $getFn for the instance creation.
21375 *                      Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`.
21376 * @returns {Object} registered provider instance
21377 *
21378 * @example
21379 * Here is an example of registering a service
21380 * ```js
21381 *   $provide.factory('ping', ['$http', function($http) {
21382 *     return function ping() {
21383 *       return $http.send('/ping');
21384 *     };
21385 *   }]);
21386 * ```
21387 * You would then inject and use this service like this:
21388 * ```js
21389 *   someModule.controller('Ctrl', ['ping', function(ping) {
21390 *     ping();
21391 *   }]);
21392 * ```
21393 */
21394
21395
21396/**
21397 * @ngdoc method
21398 * @name $provide#service
21399 * @description
21400 *
21401 * Register a **service constructor**, which will be invoked with `new` to create the service
21402 * instance.
21403 * This is short for registering a service where its provider's `$get` property is the service
21404 * constructor function that will be used to instantiate the service instance.
21405 *
21406 * You should use {@link auto.$provide#service $provide.service(class)} if you define your service
21407 * as a type/class.
21408 *
21409 * @param {string} name The name of the instance.
21410 * @param {Function|Array.<string|Function>} constructor An injectable class (constructor function)
21411 *     that will be instantiated.
21412 * @returns {Object} registered provider instance
21413 *
21414 * @example
21415 * Here is an example of registering a service using
21416 * {@link auto.$provide#service $provide.service(class)}.
21417 * ```js
21418 *   var Ping = function($http) {
21419 *     this.$http = $http;
21420 *   };
21421 *
21422 *   Ping.$inject = ['$http'];
21423 *
21424 *   Ping.prototype.send = function() {
21425 *     return this.$http.get('/ping');
21426 *   };
21427 *   $provide.service('ping', Ping);
21428 * ```
21429 * You would then inject and use this service like this:
21430 * ```js
21431 *   someModule.controller('Ctrl', ['ping', function(ping) {
21432 *     ping.send();
21433 *   }]);
21434 * ```
21435 */
21436
21437
21438/**
21439 * @ngdoc method
21440 * @name $provide#value
21441 * @description
21442 *
21443 * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
21444 * number, an array, an object or a function.  This is short for registering a service where its
21445 * provider's `$get` property is a factory function that takes no arguments and returns the **value
21446 * service**.
21447 *
21448 * Value services are similar to constant services, except that they cannot be injected into a
21449 * module configuration function (see {@link angular.Module#config}) but they can be overridden by
21450 * an Angular
21451 * {@link auto.$provide#decorator decorator}.
21452 *
21453 * @param {string} name The name of the instance.
21454 * @param {*} value The value.
21455 * @returns {Object} registered provider instance
21456 *
21457 * @example
21458 * Here are some examples of creating value services.
21459 * ```js
21460 *   $provide.value('ADMIN_USER', 'admin');
21461 *
21462 *   $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
21463 *
21464 *   $provide.value('halfOf', function(value) {
21465 *     return value / 2;
21466 *   });
21467 * ```
21468 */
21469
21470
21471/**
21472 * @ngdoc method
21473 * @name $provide#constant
21474 * @description
21475 *
21476 * Register a **constant service**, such as a string, a number, an array, an object or a function,
21477 * with the {@link auto.$injector $injector}. Unlike {@link auto.$provide#value value} it can be
21478 * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
21479 * be overridden by an Angular {@link auto.$provide#decorator decorator}.
21480 *
21481 * @param {string} name The name of the constant.
21482 * @param {*} value The constant value.
21483 * @returns {Object} registered instance
21484 *
21485 * @example
21486 * Here a some examples of creating constants:
21487 * ```js
21488 *   $provide.constant('SHARD_HEIGHT', 306);
21489 *
21490 *   $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
21491 *
21492 *   $provide.constant('double', function(value) {
21493 *     return value * 2;
21494 *   });
21495 * ```
21496 */
21497
21498
21499/**
21500 * @ngdoc method
21501 * @name $provide#decorator
21502 * @description
21503 *
21504 * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator
21505 * intercepts the creation of a service, allowing it to override or modify the behaviour of the
21506 * service. The object returned by the decorator may be the original service, or a new service
21507 * object which replaces or wraps and delegates to the original service.
21508 *
21509 * @param {string} name The name of the service to decorate.
21510 * @param {Function|Array.<string|Function>} decorator This function will be invoked when the service needs to be
21511 *    instantiated and should return the decorated service instance. The function is called using
21512 *    the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.
21513 *    Local injection arguments:
21514 *
21515 *    * `$delegate` - The original service instance, which can be monkey patched, configured,
21516 *      decorated or delegated to.
21517 *
21518 * @example
21519 * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
21520 * calls to {@link ng.$log#error $log.warn()}.
21521 * ```js
21522 *   $provide.decorator('$log', ['$delegate', function($delegate) {
21523 *     $delegate.warn = $delegate.error;
21524 *     return $delegate;
21525 *   }]);
21526 * ```
21527 */
21528
21529
21530function createInjector(modulesToLoad, strictDi) {
21531  strictDi = (strictDi === true);
21532  var INSTANTIATING = {},
21533      providerSuffix = 'Provider',
21534      path = [],
21535      loadedModules = new HashMap([], true),
21536      providerCache = {
21537        $provide: {
21538            provider: supportObject(provider),
21539            factory: supportObject(factory),
21540            service: supportObject(service),
21541            value: supportObject(value),
21542            constant: supportObject(constant),
21543            decorator: decorator
21544          }
21545      },
21546      providerInjector = (providerCache.$injector =
21547          createInternalInjector(providerCache, function(serviceName, caller) {
21548            if (angular.isString(caller)) {
21549              path.push(caller);
21550            }
21551            throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
21552          })),
21553      instanceCache = {},
21554      instanceInjector = (instanceCache.$injector =
21555          createInternalInjector(instanceCache, function(serviceName, caller) {
21556            var provider = providerInjector.get(serviceName + providerSuffix, caller);
21557            return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);
21558          }));
21559
21560
21561  forEach(loadModules(modulesToLoad), function(fn) { if (fn) instanceInjector.invoke(fn); });
21562
21563  return instanceInjector;
21564
21565  ////////////////////////////////////
21566  // $provider
21567  ////////////////////////////////////
21568
21569  function supportObject(delegate) {
21570    return function(key, value) {
21571      if (isObject(key)) {
21572        forEach(key, reverseParams(delegate));
21573      } else {
21574        return delegate(key, value);
21575      }
21576    };
21577  }
21578
21579  function provider(name, provider_) {
21580    assertNotHasOwnProperty(name, 'service');
21581    if (isFunction(provider_) || isArray(provider_)) {
21582      provider_ = providerInjector.instantiate(provider_);
21583    }
21584    if (!provider_.$get) {
21585      throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
21586    }
21587    return providerCache[name + providerSuffix] = provider_;
21588  }
21589
21590  function enforceReturnValue(name, factory) {
21591    return function enforcedReturnValue() {
21592      var result = instanceInjector.invoke(factory, this);
21593      if (isUndefined(result)) {
21594        throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
21595      }
21596      return result;
21597    };
21598  }
21599
21600  function factory(name, factoryFn, enforce) {
21601    return provider(name, {
21602      $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
21603    });
21604  }
21605
21606  function service(name, constructor) {
21607    return factory(name, ['$injector', function($injector) {
21608      return $injector.instantiate(constructor);
21609    }]);
21610  }
21611
21612  function value(name, val) { return factory(name, valueFn(val), false); }
21613
21614  function constant(name, value) {
21615    assertNotHasOwnProperty(name, 'constant');
21616    providerCache[name] = value;
21617    instanceCache[name] = value;
21618  }
21619
21620  function decorator(serviceName, decorFn) {
21621    var origProvider = providerInjector.get(serviceName + providerSuffix),
21622        orig$get = origProvider.$get;
21623
21624    origProvider.$get = function() {
21625      var origInstance = instanceInjector.invoke(orig$get, origProvider);
21626      return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
21627    };
21628  }
21629
21630  ////////////////////////////////////
21631  // Module Loading
21632  ////////////////////////////////////
21633  function loadModules(modulesToLoad) {
21634    assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array');
21635    var runBlocks = [], moduleFn;
21636    forEach(modulesToLoad, function(module) {
21637      if (loadedModules.get(module)) return;
21638      loadedModules.put(module, true);
21639
21640      function runInvokeQueue(queue) {
21641        var i, ii;
21642        for (i = 0, ii = queue.length; i < ii; i++) {
21643          var invokeArgs = queue[i],
21644              provider = providerInjector.get(invokeArgs[0]);
21645
21646          provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
21647        }
21648      }
21649
21650      try {
21651        if (isString(module)) {
21652          moduleFn = angularModule(module);
21653          runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
21654          runInvokeQueue(moduleFn._invokeQueue);
21655          runInvokeQueue(moduleFn._configBlocks);
21656        } else if (isFunction(module)) {
21657            runBlocks.push(providerInjector.invoke(module));
21658        } else if (isArray(module)) {
21659            runBlocks.push(providerInjector.invoke(module));
21660        } else {
21661          assertArgFn(module, 'module');
21662        }
21663      } catch (e) {
21664        if (isArray(module)) {
21665          module = module[module.length - 1];
21666        }
21667        if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
21668          // Safari & FF's stack traces don't contain error.message content
21669          // unlike those of Chrome and IE
21670          // So if stack doesn't contain message, we create a new string that contains both.
21671          // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
21672          /* jshint -W022 */
21673          e = e.message + '\n' + e.stack;
21674        }
21675        throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
21676                  module, e.stack || e.message || e);
21677      }
21678    });
21679    return runBlocks;
21680  }
21681
21682  ////////////////////////////////////
21683  // internal Injector
21684  ////////////////////////////////////
21685
21686  function createInternalInjector(cache, factory) {
21687
21688    function getService(serviceName, caller) {
21689      if (cache.hasOwnProperty(serviceName)) {
21690        if (cache[serviceName] === INSTANTIATING) {
21691          throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
21692                    serviceName + ' <- ' + path.join(' <- '));
21693        }
21694        return cache[serviceName];
21695      } else {
21696        try {
21697          path.unshift(serviceName);
21698          cache[serviceName] = INSTANTIATING;
21699          return cache[serviceName] = factory(serviceName, caller);
21700        } catch (err) {
21701          if (cache[serviceName] === INSTANTIATING) {
21702            delete cache[serviceName];
21703          }
21704          throw err;
21705        } finally {
21706          path.shift();
21707        }
21708      }
21709    }
21710
21711    function invoke(fn, self, locals, serviceName) {
21712      if (typeof locals === 'string') {
21713        serviceName = locals;
21714        locals = null;
21715      }
21716
21717      var args = [],
21718          $inject = createInjector.$$annotate(fn, strictDi, serviceName),
21719          length, i,
21720          key;
21721
21722      for (i = 0, length = $inject.length; i < length; i++) {
21723        key = $inject[i];
21724        if (typeof key !== 'string') {
21725          throw $injectorMinErr('itkn',
21726                  'Incorrect injection token! Expected service name as string, got {0}', key);
21727        }
21728        args.push(
21729          locals && locals.hasOwnProperty(key)
21730          ? locals[key]
21731          : getService(key, serviceName)
21732        );
21733      }
21734      if (isArray(fn)) {
21735        fn = fn[length];
21736      }
21737
21738      // http://jsperf.com/angularjs-invoke-apply-vs-switch
21739      // #5388
21740      return fn.apply(self, args);
21741    }
21742
21743    function instantiate(Type, locals, serviceName) {
21744      // Check if Type is annotated and use just the given function at n-1 as parameter
21745      // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
21746      // Object creation: http://jsperf.com/create-constructor/2
21747      var instance = Object.create((isArray(Type) ? Type[Type.length - 1] : Type).prototype || null);
21748      var returnedValue = invoke(Type, instance, locals, serviceName);
21749
21750      return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
21751    }
21752
21753    return {
21754      invoke: invoke,
21755      instantiate: instantiate,
21756      get: getService,
21757      annotate: createInjector.$$annotate,
21758      has: function(name) {
21759        return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
21760      }
21761    };
21762  }
21763}
21764
21765createInjector.$$annotate = annotate;
21766
21767/**
21768 * @ngdoc provider
21769 * @name $anchorScrollProvider
21770 *
21771 * @description
21772 * Use `$anchorScrollProvider` to disable automatic scrolling whenever
21773 * {@link ng.$location#hash $location.hash()} changes.
21774 */
21775function $AnchorScrollProvider() {
21776
21777  var autoScrollingEnabled = true;
21778
21779  /**
21780   * @ngdoc method
21781   * @name $anchorScrollProvider#disableAutoScrolling
21782   *
21783   * @description
21784   * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
21785   * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
21786   * Use this method to disable automatic scrolling.
21787   *
21788   * If automatic scrolling is disabled, one must explicitly call
21789   * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
21790   * current hash.
21791   */
21792  this.disableAutoScrolling = function() {
21793    autoScrollingEnabled = false;
21794  };
21795
21796  /**
21797   * @ngdoc service
21798   * @name $anchorScroll
21799   * @kind function
21800   * @requires $window
21801   * @requires $location
21802   * @requires $rootScope
21803   *
21804   * @description
21805   * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the
21806   * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified
21807   * in the
21808   * [HTML5 spec](http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document).
21809   *
21810   * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
21811   * match any anchor whenever it changes. This can be disabled by calling
21812   * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.
21813   *
21814   * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
21815   * vertical scroll-offset (either fixed or dynamic).
21816   *
21817   * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of
21818   *                       {@link ng.$location#hash $location.hash()} will be used.
21819   *
21820   * @property {(number|function|jqLite)} yOffset
21821   * If set, specifies a vertical scroll-offset. This is often useful when there are fixed
21822   * positioned elements at the top of the page, such as navbars, headers etc.
21823   *
21824   * `yOffset` can be specified in various ways:
21825   * - **number**: A fixed number of pixels to be used as offset.<br /><br />
21826   * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return
21827   *   a number representing the offset (in pixels).<br /><br />
21828   * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from
21829   *   the top of the page to the element's bottom will be used as offset.<br />
21830   *   **Note**: The element will be taken into account only as long as its `position` is set to
21831   *   `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust
21832   *   their height and/or positioning according to the viewport's size.
21833   *
21834   * <br />
21835   * <div class="alert alert-warning">
21836   * In order for `yOffset` to work properly, scrolling should take place on the document's root and
21837   * not some child element.
21838   * </div>
21839   *
21840   * @example
21841     <example module="anchorScrollExample">
21842       <file name="index.html">
21843         <div id="scrollArea" ng-controller="ScrollController">
21844           <a ng-click="gotoBottom()">Go to bottom</a>
21845           <a id="bottom"></a> You're at the bottom!
21846         </div>
21847       </file>
21848       <file name="script.js">
21849         angular.module('anchorScrollExample', [])
21850           .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
21851             function ($scope, $location, $anchorScroll) {
21852               $scope.gotoBottom = function() {
21853                 // set the location.hash to the id of
21854                 // the element you wish to scroll to.
21855                 $location.hash('bottom');
21856
21857                 // call $anchorScroll()
21858                 $anchorScroll();
21859               };
21860             }]);
21861       </file>
21862       <file name="style.css">
21863         #scrollArea {
21864           height: 280px;
21865           overflow: auto;
21866         }
21867
21868         #bottom {
21869           display: block;
21870           margin-top: 2000px;
21871         }
21872       </file>
21873     </example>
21874   *
21875   * <hr />
21876   * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).
21877   * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.
21878   *
21879   * @example
21880     <example module="anchorScrollOffsetExample">
21881       <file name="index.html">
21882         <div class="fixed-header" ng-controller="headerCtrl">
21883           <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]">
21884             Go to anchor {{x}}
21885           </a>
21886         </div>
21887         <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]">
21888           Anchor {{x}} of 5
21889         </div>
21890       </file>
21891       <file name="script.js">
21892         angular.module('anchorScrollOffsetExample', [])
21893           .run(['$anchorScroll', function($anchorScroll) {
21894             $anchorScroll.yOffset = 50;   // always scroll by 50 extra pixels
21895           }])
21896           .controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
21897             function ($anchorScroll, $location, $scope) {
21898               $scope.gotoAnchor = function(x) {
21899                 var newHash = 'anchor' + x;
21900                 if ($location.hash() !== newHash) {
21901                   // set the $location.hash to `newHash` and
21902                   // $anchorScroll will automatically scroll to it
21903                   $location.hash('anchor' + x);
21904                 } else {
21905                   // call $anchorScroll() explicitly,
21906                   // since $location.hash hasn't changed
21907                   $anchorScroll();
21908                 }
21909               };
21910             }
21911           ]);
21912       </file>
21913       <file name="style.css">
21914         body {
21915           padding-top: 50px;
21916         }
21917
21918         .anchor {
21919           border: 2px dashed DarkOrchid;
21920           padding: 10px 10px 200px 10px;
21921         }
21922
21923         .fixed-header {
21924           background-color: rgba(0, 0, 0, 0.2);
21925           height: 50px;
21926           position: fixed;
21927           top: 0; left: 0; right: 0;
21928         }
21929
21930         .fixed-header > a {
21931           display: inline-block;
21932           margin: 5px 15px;
21933         }
21934       </file>
21935     </example>
21936   */
21937  this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
21938    var document = $window.document;
21939
21940    // Helper function to get first anchor from a NodeList
21941    // (using `Array#some()` instead of `angular#forEach()` since it's more performant
21942    //  and working in all supported browsers.)
21943    function getFirstAnchor(list) {
21944      var result = null;
21945      Array.prototype.some.call(list, function(element) {
21946        if (nodeName_(element) === 'a') {
21947          result = element;
21948          return true;
21949        }
21950      });
21951      return result;
21952    }
21953
21954    function getYOffset() {
21955
21956      var offset = scroll.yOffset;
21957
21958      if (isFunction(offset)) {
21959        offset = offset();
21960      } else if (isElement(offset)) {
21961        var elem = offset[0];
21962        var style = $window.getComputedStyle(elem);
21963        if (style.position !== 'fixed') {
21964          offset = 0;
21965        } else {
21966          offset = elem.getBoundingClientRect().bottom;
21967        }
21968      } else if (!isNumber(offset)) {
21969        offset = 0;
21970      }
21971
21972      return offset;
21973    }
21974
21975    function scrollTo(elem) {
21976      if (elem) {
21977        elem.scrollIntoView();
21978
21979        var offset = getYOffset();
21980
21981        if (offset) {
21982          // `offset` is the number of pixels we should scroll UP in order to align `elem` properly.
21983          // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the
21984          // top of the viewport.
21985          //
21986          // IF the number of pixels from the top of `elem` to the end of the page's content is less
21987          // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some
21988          // way down the page.
21989          //
21990          // This is often the case for elements near the bottom of the page.
21991          //
21992          // In such cases we do not need to scroll the whole `offset` up, just the difference between
21993          // the top of the element and the offset, which is enough to align the top of `elem` at the
21994          // desired position.
21995          var elemTop = elem.getBoundingClientRect().top;
21996          $window.scrollBy(0, elemTop - offset);
21997        }
21998      } else {
21999        $window.scrollTo(0, 0);
22000      }
22001    }
22002
22003    function scroll(hash) {
22004      hash = isString(hash) ? hash : $location.hash();
22005      var elm;
22006
22007      // empty hash, scroll to the top of the page
22008      if (!hash) scrollTo(null);
22009
22010      // element with given id
22011      else if ((elm = document.getElementById(hash))) scrollTo(elm);
22012
22013      // first anchor with given name :-D
22014      else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);
22015
22016      // no element and hash == 'top', scroll to the top of the page
22017      else if (hash === 'top') scrollTo(null);
22018    }
22019
22020    // does not scroll when user clicks on anchor link that is currently on
22021    // (no url change, no $location.hash() change), browser native does scroll
22022    if (autoScrollingEnabled) {
22023      $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
22024        function autoScrollWatchAction(newVal, oldVal) {
22025          // skip the initial scroll if $location.hash is empty
22026          if (newVal === oldVal && newVal === '') return;
22027
22028          jqLiteDocumentLoaded(function() {
22029            $rootScope.$evalAsync(scroll);
22030          });
22031        });
22032    }
22033
22034    return scroll;
22035  }];
22036}
22037
22038var $animateMinErr = minErr('$animate');
22039var ELEMENT_NODE = 1;
22040var NG_ANIMATE_CLASSNAME = 'ng-animate';
22041
22042function mergeClasses(a,b) {
22043  if (!a && !b) return '';
22044  if (!a) return b;
22045  if (!b) return a;
22046  if (isArray(a)) a = a.join(' ');
22047  if (isArray(b)) b = b.join(' ');
22048  return a + ' ' + b;
22049}
22050
22051function extractElementNode(element) {
22052  for (var i = 0; i < element.length; i++) {
22053    var elm = element[i];
22054    if (elm.nodeType === ELEMENT_NODE) {
22055      return elm;
22056    }
22057  }
22058}
22059
22060function splitClasses(classes) {
22061  if (isString(classes)) {
22062    classes = classes.split(' ');
22063  }
22064
22065  // Use createMap() to prevent class assumptions involving property names in
22066  // Object.prototype
22067  var obj = createMap();
22068  forEach(classes, function(klass) {
22069    // sometimes the split leaves empty string values
22070    // incase extra spaces were applied to the options
22071    if (klass.length) {
22072      obj[klass] = true;
22073    }
22074  });
22075  return obj;
22076}
22077
22078// if any other type of options value besides an Object value is
22079// passed into the $animate.method() animation then this helper code
22080// will be run which will ignore it. While this patch is not the
22081// greatest solution to this, a lot of existing plugins depend on
22082// $animate to either call the callback (< 1.2) or return a promise
22083// that can be changed. This helper function ensures that the options
22084// are wiped clean incase a callback function is provided.
22085function prepareAnimateOptions(options) {
22086  return isObject(options)
22087      ? options
22088      : {};
22089}
22090
22091var $$CoreAnimateRunnerProvider = function() {
22092  this.$get = ['$q', '$$rAF', function($q, $$rAF) {
22093    function AnimateRunner() {}
22094    AnimateRunner.all = noop;
22095    AnimateRunner.chain = noop;
22096    AnimateRunner.prototype = {
22097      end: noop,
22098      cancel: noop,
22099      resume: noop,
22100      pause: noop,
22101      complete: noop,
22102      then: function(pass, fail) {
22103        return $q(function(resolve) {
22104          $$rAF(function() {
22105            resolve();
22106          });
22107        }).then(pass, fail);
22108      }
22109    };
22110    return AnimateRunner;
22111  }];
22112};
22113
22114// this is prefixed with Core since it conflicts with
22115// the animateQueueProvider defined in ngAnimate/animateQueue.js
22116var $$CoreAnimateQueueProvider = function() {
22117  var postDigestQueue = new HashMap();
22118  var postDigestElements = [];
22119
22120  this.$get = ['$$AnimateRunner', '$rootScope',
22121       function($$AnimateRunner,   $rootScope) {
22122    return {
22123      enabled: noop,
22124      on: noop,
22125      off: noop,
22126      pin: noop,
22127
22128      push: function(element, event, options, domOperation) {
22129        domOperation        && domOperation();
22130
22131        options = options || {};
22132        options.from        && element.css(options.from);
22133        options.to          && element.css(options.to);
22134
22135        if (options.addClass || options.removeClass) {
22136          addRemoveClassesPostDigest(element, options.addClass, options.removeClass);
22137        }
22138
22139        return new $$AnimateRunner(); // jshint ignore:line
22140      }
22141    };
22142
22143
22144    function updateData(data, classes, value) {
22145      var changed = false;
22146      if (classes) {
22147        classes = isString(classes) ? classes.split(' ') :
22148                  isArray(classes) ? classes : [];
22149        forEach(classes, function(className) {
22150          if (className) {
22151            changed = true;
22152            data[className] = value;
22153          }
22154        });
22155      }
22156      return changed;
22157    }
22158
22159    function handleCSSClassChanges() {
22160      forEach(postDigestElements, function(element) {
22161        var data = postDigestQueue.get(element);
22162        if (data) {
22163          var existing = splitClasses(element.attr('class'));
22164          var toAdd = '';
22165          var toRemove = '';
22166          forEach(data, function(status, className) {
22167            var hasClass = !!existing[className];
22168            if (status !== hasClass) {
22169              if (status) {
22170                toAdd += (toAdd.length ? ' ' : '') + className;
22171              } else {
22172                toRemove += (toRemove.length ? ' ' : '') + className;
22173              }
22174            }
22175          });
22176
22177          forEach(element, function(elm) {
22178            toAdd    && jqLiteAddClass(elm, toAdd);
22179            toRemove && jqLiteRemoveClass(elm, toRemove);
22180          });
22181          postDigestQueue.remove(element);
22182        }
22183      });
22184      postDigestElements.length = 0;
22185    }
22186
22187
22188    function addRemoveClassesPostDigest(element, add, remove) {
22189      var data = postDigestQueue.get(element) || {};
22190
22191      var classesAdded = updateData(data, add, true);
22192      var classesRemoved = updateData(data, remove, false);
22193
22194      if (classesAdded || classesRemoved) {
22195
22196        postDigestQueue.put(element, data);
22197        postDigestElements.push(element);
22198
22199        if (postDigestElements.length === 1) {
22200          $rootScope.$$postDigest(handleCSSClassChanges);
22201        }
22202      }
22203    }
22204  }];
22205};
22206
22207/**
22208 * @ngdoc provider
22209 * @name $animateProvider
22210 *
22211 * @description
22212 * Default implementation of $animate that doesn't perform any animations, instead just
22213 * synchronously performs DOM updates and resolves the returned runner promise.
22214 *
22215 * In order to enable animations the `ngAnimate` module has to be loaded.
22216 *
22217 * To see the functional implementation check out `src/ngAnimate/animate.js`.
22218 */
22219var $AnimateProvider = ['$provide', function($provide) {
22220  var provider = this;
22221
22222  this.$$registeredAnimations = Object.create(null);
22223
22224   /**
22225   * @ngdoc method
22226   * @name $animateProvider#register
22227   *
22228   * @description
22229   * Registers a new injectable animation factory function. The factory function produces the
22230   * animation object which contains callback functions for each event that is expected to be
22231   * animated.
22232   *
22233   *   * `eventFn`: `function(element, ... , doneFunction, options)`
22234   *   The element to animate, the `doneFunction` and the options fed into the animation. Depending
22235   *   on the type of animation additional arguments will be injected into the animation function. The
22236   *   list below explains the function signatures for the different animation methods:
22237   *
22238   *   - setClass: function(element, addedClasses, removedClasses, doneFunction, options)
22239   *   - addClass: function(element, addedClasses, doneFunction, options)
22240   *   - removeClass: function(element, removedClasses, doneFunction, options)
22241   *   - enter, leave, move: function(element, doneFunction, options)
22242   *   - animate: function(element, fromStyles, toStyles, doneFunction, options)
22243   *
22244   *   Make sure to trigger the `doneFunction` once the animation is fully complete.
22245   *
22246   * ```js
22247   *   return {
22248   *     //enter, leave, move signature
22249   *     eventFn : function(element, done, options) {
22250   *       //code to run the animation
22251   *       //once complete, then run done()
22252   *       return function endFunction(wasCancelled) {
22253   *         //code to cancel the animation
22254   *       }
22255   *     }
22256   *   }
22257   * ```
22258   *
22259   * @param {string} name The name of the animation (this is what the class-based CSS value will be compared to).
22260   * @param {Function} factory The factory function that will be executed to return the animation
22261   *                           object.
22262   */
22263  this.register = function(name, factory) {
22264    if (name && name.charAt(0) !== '.') {
22265      throw $animateMinErr('notcsel', "Expecting class selector starting with '.' got '{0}'.", name);
22266    }
22267
22268    var key = name + '-animation';
22269    provider.$$registeredAnimations[name.substr(1)] = key;
22270    $provide.factory(key, factory);
22271  };
22272
22273  /**
22274   * @ngdoc method
22275   * @name $animateProvider#classNameFilter
22276   *
22277   * @description
22278   * Sets and/or returns the CSS class regular expression that is checked when performing
22279   * an animation. Upon bootstrap the classNameFilter value is not set at all and will
22280   * therefore enable $animate to attempt to perform an animation on any element that is triggered.
22281   * When setting the `classNameFilter` value, animations will only be performed on elements
22282   * that successfully match the filter expression. This in turn can boost performance
22283   * for low-powered devices as well as applications containing a lot of structural operations.
22284   * @param {RegExp=} expression The className expression which will be checked against all animations
22285   * @return {RegExp} The current CSS className expression value. If null then there is no expression value
22286   */
22287  this.classNameFilter = function(expression) {
22288    if (arguments.length === 1) {
22289      this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
22290      if (this.$$classNameFilter) {
22291        var reservedRegex = new RegExp("(\\s+|\\/)" + NG_ANIMATE_CLASSNAME + "(\\s+|\\/)");
22292        if (reservedRegex.test(this.$$classNameFilter.toString())) {
22293          throw $animateMinErr('nongcls','$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME);
22294
22295        }
22296      }
22297    }
22298    return this.$$classNameFilter;
22299  };
22300
22301  this.$get = ['$$animateQueue', function($$animateQueue) {
22302    function domInsert(element, parentElement, afterElement) {
22303      // if for some reason the previous element was removed
22304      // from the dom sometime before this code runs then let's
22305      // just stick to using the parent element as the anchor
22306      if (afterElement) {
22307        var afterNode = extractElementNode(afterElement);
22308        if (afterNode && !afterNode.parentNode && !afterNode.previousElementSibling) {
22309          afterElement = null;
22310        }
22311      }
22312      afterElement ? afterElement.after(element) : parentElement.prepend(element);
22313    }
22314
22315    /**
22316     * @ngdoc service
22317     * @name $animate
22318     * @description The $animate service exposes a series of DOM utility methods that provide support
22319     * for animation hooks. The default behavior is the application of DOM operations, however,
22320     * when an animation is detected (and animations are enabled), $animate will do the heavy lifting
22321     * to ensure that animation runs with the triggered DOM operation.
22322     *
22323     * By default $animate doesn't trigger an animations. This is because the `ngAnimate` module isn't
22324     * included and only when it is active then the animation hooks that `$animate` triggers will be
22325     * functional. Once active then all structural `ng-` directives will trigger animations as they perform
22326     * their DOM-related operations (enter, leave and move). Other directives such as `ngClass`,
22327     * `ngShow`, `ngHide` and `ngMessages` also provide support for animations.
22328     *
22329     * It is recommended that the`$animate` service is always used when executing DOM-related procedures within directives.
22330     *
22331     * To learn more about enabling animation support, click here to visit the
22332     * {@link ngAnimate ngAnimate module page}.
22333     */
22334    return {
22335      // we don't call it directly since non-existant arguments may
22336      // be interpreted as null within the sub enabled function
22337
22338      /**
22339       *
22340       * @ngdoc method
22341       * @name $animate#on
22342       * @kind function
22343       * @description Sets up an event listener to fire whenever the animation event (enter, leave, move, etc...)
22344       *    has fired on the given element or among any of its children. Once the listener is fired, the provided callback
22345       *    is fired with the following params:
22346       *
22347       * ```js
22348       * $animate.on('enter', container,
22349       *    function callback(element, phase) {
22350       *      // cool we detected an enter animation within the container
22351       *    }
22352       * );
22353       * ```
22354       *
22355       * @param {string} event the animation event that will be captured (e.g. enter, leave, move, addClass, removeClass, etc...)
22356       * @param {DOMElement} container the container element that will capture each of the animation events that are fired on itself
22357       *     as well as among its children
22358       * @param {Function} callback the callback function that will be fired when the listener is triggered
22359       *
22360       * The arguments present in the callback function are:
22361       * * `element` - The captured DOM element that the animation was fired on.
22362       * * `phase` - The phase of the animation. The two possible phases are **start** (when the animation starts) and **close** (when it ends).
22363       */
22364      on: $$animateQueue.on,
22365
22366      /**
22367       *
22368       * @ngdoc method
22369       * @name $animate#off
22370       * @kind function
22371       * @description Deregisters an event listener based on the event which has been associated with the provided element. This method
22372       * can be used in three different ways depending on the arguments:
22373       *
22374       * ```js
22375       * // remove all the animation event listeners listening for `enter`
22376       * $animate.off('enter');
22377       *
22378       * // remove all the animation event listeners listening for `enter` on the given element and its children
22379       * $animate.off('enter', container);
22380       *
22381       * // remove the event listener function provided by `listenerFn` that is set
22382       * // to listen for `enter` on the given `element` as well as its children
22383       * $animate.off('enter', container, callback);
22384       * ```
22385       *
22386       * @param {string} event the animation event (e.g. enter, leave, move, addClass, removeClass, etc...)
22387       * @param {DOMElement=} container the container element the event listener was placed on
22388       * @param {Function=} callback the callback function that was registered as the listener
22389       */
22390      off: $$animateQueue.off,
22391
22392      /**
22393       * @ngdoc method
22394       * @name $animate#pin
22395       * @kind function
22396       * @description Associates the provided element with a host parent element to allow the element to be animated even if it exists
22397       *    outside of the DOM structure of the Angular application. By doing so, any animation triggered via `$animate` can be issued on the
22398       *    element despite being outside the realm of the application or within another application. Say for example if the application
22399       *    was bootstrapped on an element that is somewhere inside of the `<body>` tag, but we wanted to allow for an element to be situated
22400       *    as a direct child of `document.body`, then this can be achieved by pinning the element via `$animate.pin(element)`. Keep in mind
22401       *    that calling `$animate.pin(element, parentElement)` will not actually insert into the DOM anywhere; it will just create the association.
22402       *
22403       *    Note that this feature is only active when the `ngAnimate` module is used.
22404       *
22405       * @param {DOMElement} element the external element that will be pinned
22406       * @param {DOMElement} parentElement the host parent element that will be associated with the external element
22407       */
22408      pin: $$animateQueue.pin,
22409
22410      /**
22411       *
22412       * @ngdoc method
22413       * @name $animate#enabled
22414       * @kind function
22415       * @description Used to get and set whether animations are enabled or not on the entire application or on an element and its children. This
22416       * function can be called in four ways:
22417       *
22418       * ```js
22419       * // returns true or false
22420       * $animate.enabled();
22421       *
22422       * // changes the enabled state for all animations
22423       * $animate.enabled(false);
22424       * $animate.enabled(true);
22425       *
22426       * // returns true or false if animations are enabled for an element
22427       * $animate.enabled(element);
22428       *
22429       * // changes the enabled state for an element and its children
22430       * $animate.enabled(element, true);
22431       * $animate.enabled(element, false);
22432       * ```
22433       *
22434       * @param {DOMElement=} element the element that will be considered for checking/setting the enabled state
22435       * @param {boolean=} enabled whether or not the animations will be enabled for the element
22436       *
22437       * @return {boolean} whether or not animations are enabled
22438       */
22439      enabled: $$animateQueue.enabled,
22440
22441      /**
22442       * @ngdoc method
22443       * @name $animate#cancel
22444       * @kind function
22445       * @description Cancels the provided animation.
22446       *
22447       * @param {Promise} animationPromise The animation promise that is returned when an animation is started.
22448       */
22449      cancel: function(runner) {
22450        runner.end && runner.end();
22451      },
22452
22453      /**
22454       *
22455       * @ngdoc method
22456       * @name $animate#enter
22457       * @kind function
22458       * @description Inserts the element into the DOM either after the `after` element (if provided) or
22459       *   as the first child within the `parent` element and then triggers an animation.
22460       *   A promise is returned that will be resolved during the next digest once the animation
22461       *   has completed.
22462       *
22463       * @param {DOMElement} element the element which will be inserted into the DOM
22464       * @param {DOMElement} parent the parent element which will append the element as
22465       *   a child (so long as the after element is not present)
22466       * @param {DOMElement=} after the sibling element after which the element will be appended
22467       * @param {object=} options an optional collection of options/styles that will be applied to the element
22468       *
22469       * @return {Promise} the animation callback promise
22470       */
22471      enter: function(element, parent, after, options) {
22472        parent = parent && jqLite(parent);
22473        after = after && jqLite(after);
22474        parent = parent || after.parent();
22475        domInsert(element, parent, after);
22476        return $$animateQueue.push(element, 'enter', prepareAnimateOptions(options));
22477      },
22478
22479      /**
22480       *
22481       * @ngdoc method
22482       * @name $animate#move
22483       * @kind function
22484       * @description Inserts (moves) the element into its new position in the DOM either after
22485       *   the `after` element (if provided) or as the first child within the `parent` element
22486       *   and then triggers an animation. A promise is returned that will be resolved
22487       *   during the next digest once the animation has completed.
22488       *
22489       * @param {DOMElement} element the element which will be moved into the new DOM position
22490       * @param {DOMElement} parent the parent element which will append the element as
22491       *   a child (so long as the after element is not present)
22492       * @param {DOMElement=} after the sibling element after which the element will be appended
22493       * @param {object=} options an optional collection of options/styles that will be applied to the element
22494       *
22495       * @return {Promise} the animation callback promise
22496       */
22497      move: function(element, parent, after, options) {
22498        parent = parent && jqLite(parent);
22499        after = after && jqLite(after);
22500        parent = parent || after.parent();
22501        domInsert(element, parent, after);
22502        return $$animateQueue.push(element, 'move', prepareAnimateOptions(options));
22503      },
22504
22505      /**
22506       * @ngdoc method
22507       * @name $animate#leave
22508       * @kind function
22509       * @description Triggers an animation and then removes the element from the DOM.
22510       * When the function is called a promise is returned that will be resolved during the next
22511       * digest once the animation has completed.
22512       *
22513       * @param {DOMElement} element the element which will be removed from the DOM
22514       * @param {object=} options an optional collection of options/styles that will be applied to the element
22515       *
22516       * @return {Promise} the animation callback promise
22517       */
22518      leave: function(element, options) {
22519        return $$animateQueue.push(element, 'leave', prepareAnimateOptions(options), function() {
22520          element.remove();
22521        });
22522      },
22523
22524      /**
22525       * @ngdoc method
22526       * @name $animate#addClass
22527       * @kind function
22528       *
22529       * @description Triggers an addClass animation surrounding the addition of the provided CSS class(es). Upon
22530       *   execution, the addClass operation will only be handled after the next digest and it will not trigger an
22531       *   animation if element already contains the CSS class or if the class is removed at a later step.
22532       *   Note that class-based animations are treated differently compared to structural animations
22533       *   (like enter, move and leave) since the CSS classes may be added/removed at different points
22534       *   depending if CSS or JavaScript animations are used.
22535       *
22536       * @param {DOMElement} element the element which the CSS classes will be applied to
22537       * @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces)
22538       * @param {object=} options an optional collection of options/styles that will be applied to the element
22539       *
22540       * @return {Promise} the animation callback promise
22541       */
22542      addClass: function(element, className, options) {
22543        options = prepareAnimateOptions(options);
22544        options.addClass = mergeClasses(options.addclass, className);
22545        return $$animateQueue.push(element, 'addClass', options);
22546      },
22547
22548      /**
22549       * @ngdoc method
22550       * @name $animate#removeClass
22551       * @kind function
22552       *
22553       * @description Triggers a removeClass animation surrounding the removal of the provided CSS class(es). Upon
22554       *   execution, the removeClass operation will only be handled after the next digest and it will not trigger an
22555       *   animation if element does not contain the CSS class or if the class is added at a later step.
22556       *   Note that class-based animations are treated differently compared to structural animations
22557       *   (like enter, move and leave) since the CSS classes may be added/removed at different points
22558       *   depending if CSS or JavaScript animations are used.
22559       *
22560       * @param {DOMElement} element the element which the CSS classes will be applied to
22561       * @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces)
22562       * @param {object=} options an optional collection of options/styles that will be applied to the element
22563       *
22564       * @return {Promise} the animation callback promise
22565       */
22566      removeClass: function(element, className, options) {
22567        options = prepareAnimateOptions(options);
22568        options.removeClass = mergeClasses(options.removeClass, className);
22569        return $$animateQueue.push(element, 'removeClass', options);
22570      },
22571
22572      /**
22573       * @ngdoc method
22574       * @name $animate#setClass
22575       * @kind function
22576       *
22577       * @description Performs both the addition and removal of a CSS classes on an element and (during the process)
22578       *    triggers an animation surrounding the class addition/removal. Much like `$animate.addClass` and
22579       *    `$animate.removeClass`, `setClass` will only evaluate the classes being added/removed once a digest has
22580       *    passed. Note that class-based animations are treated differently compared to structural animations
22581       *    (like enter, move and leave) since the CSS classes may be added/removed at different points
22582       *    depending if CSS or JavaScript animations are used.
22583       *
22584       * @param {DOMElement} element the element which the CSS classes will be applied to
22585       * @param {string} add the CSS class(es) that will be added (multiple classes are separated via spaces)
22586       * @param {string} remove the CSS class(es) that will be removed (multiple classes are separated via spaces)
22587       * @param {object=} options an optional collection of options/styles that will be applied to the element
22588       *
22589       * @return {Promise} the animation callback promise
22590       */
22591      setClass: function(element, add, remove, options) {
22592        options = prepareAnimateOptions(options);
22593        options.addClass = mergeClasses(options.addClass, add);
22594        options.removeClass = mergeClasses(options.removeClass, remove);
22595        return $$animateQueue.push(element, 'setClass', options);
22596      },
22597
22598      /**
22599       * @ngdoc method
22600       * @name $animate#animate
22601       * @kind function
22602       *
22603       * @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element.
22604       * If any detected CSS transition, keyframe or JavaScript matches the provided className value then the animation will take
22605       * on the provided styles. For example, if a transition animation is set for the given className then the provided from and
22606       * to styles will be applied alongside the given transition. If a JavaScript animation is detected then the provided styles
22607       * will be given in as function paramters into the `animate` method (or as apart of the `options` parameter).
22608       *
22609       * @param {DOMElement} element the element which the CSS styles will be applied to
22610       * @param {object} from the from (starting) CSS styles that will be applied to the element and across the animation.
22611       * @param {object} to the to (destination) CSS styles that will be applied to the element and across the animation.
22612       * @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If
22613       *    this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element.
22614       *    (Note that if no animation is detected then this value will not be appplied to the element.)
22615       * @param {object=} options an optional collection of options/styles that will be applied to the element
22616       *
22617       * @return {Promise} the animation callback promise
22618       */
22619      animate: function(element, from, to, className, options) {
22620        options = prepareAnimateOptions(options);
22621        options.from = options.from ? extend(options.from, from) : from;
22622        options.to   = options.to   ? extend(options.to, to)     : to;
22623
22624        className = className || 'ng-inline-animate';
22625        options.tempClasses = mergeClasses(options.tempClasses, className);
22626        return $$animateQueue.push(element, 'animate', options);
22627      }
22628    };
22629  }];
22630}];
22631
22632/**
22633 * @ngdoc service
22634 * @name $animateCss
22635 * @kind object
22636 *
22637 * @description
22638 * This is the core version of `$animateCss`. By default, only when the `ngAnimate` is included,
22639 * then the `$animateCss` service will actually perform animations.
22640 *
22641 * Click here {@link ngAnimate.$animateCss to read the documentation for $animateCss}.
22642 */
22643var $CoreAnimateCssProvider = function() {
22644  this.$get = ['$$rAF', '$q', function($$rAF, $q) {
22645
22646    var RAFPromise = function() {};
22647    RAFPromise.prototype = {
22648      done: function(cancel) {
22649        this.defer && this.defer[cancel === true ? 'reject' : 'resolve']();
22650      },
22651      end: function() {
22652        this.done();
22653      },
22654      cancel: function() {
22655        this.done(true);
22656      },
22657      getPromise: function() {
22658        if (!this.defer) {
22659          this.defer = $q.defer();
22660        }
22661        return this.defer.promise;
22662      },
22663      then: function(f1,f2) {
22664        return this.getPromise().then(f1,f2);
22665      },
22666      'catch': function(f1) {
22667        return this.getPromise()['catch'](f1);
22668      },
22669      'finally': function(f1) {
22670        return this.getPromise()['finally'](f1);
22671      }
22672    };
22673
22674    return function(element, options) {
22675      // there is no point in applying the styles since
22676      // there is no animation that goes on at all in
22677      // this version of $animateCss.
22678      if (options.cleanupStyles) {
22679        options.from = options.to = null;
22680      }
22681
22682      if (options.from) {
22683        element.css(options.from);
22684        options.from = null;
22685      }
22686
22687      var closed, runner = new RAFPromise();
22688      return {
22689        start: run,
22690        end: run
22691      };
22692
22693      function run() {
22694        $$rAF(function() {
22695          close();
22696          if (!closed) {
22697            runner.done();
22698          }
22699          closed = true;
22700        });
22701        return runner;
22702      }
22703
22704      function close() {
22705        if (options.addClass) {
22706          element.addClass(options.addClass);
22707          options.addClass = null;
22708        }
22709        if (options.removeClass) {
22710          element.removeClass(options.removeClass);
22711          options.removeClass = null;
22712        }
22713        if (options.to) {
22714          element.css(options.to);
22715          options.to = null;
22716        }
22717      }
22718    };
22719  }];
22720};
22721
22722/* global stripHash: true */
22723
22724/**
22725 * ! This is a private undocumented service !
22726 *
22727 * @name $browser
22728 * @requires $log
22729 * @description
22730 * This object has two goals:
22731 *
22732 * - hide all the global state in the browser caused by the window object
22733 * - abstract away all the browser specific features and inconsistencies
22734 *
22735 * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
22736 * service, which can be used for convenient testing of the application without the interaction with
22737 * the real browser apis.
22738 */
22739/**
22740 * @param {object} window The global window object.
22741 * @param {object} document jQuery wrapped document.
22742 * @param {object} $log window.console or an object with the same interface.
22743 * @param {object} $sniffer $sniffer service
22744 */
22745function Browser(window, document, $log, $sniffer) {
22746  var self = this,
22747      rawDocument = document[0],
22748      location = window.location,
22749      history = window.history,
22750      setTimeout = window.setTimeout,
22751      clearTimeout = window.clearTimeout,
22752      pendingDeferIds = {};
22753
22754  self.isMock = false;
22755
22756  var outstandingRequestCount = 0;
22757  var outstandingRequestCallbacks = [];
22758
22759  // TODO(vojta): remove this temporary api
22760  self.$$completeOutstandingRequest = completeOutstandingRequest;
22761  self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
22762
22763  /**
22764   * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
22765   * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
22766   */
22767  function completeOutstandingRequest(fn) {
22768    try {
22769      fn.apply(null, sliceArgs(arguments, 1));
22770    } finally {
22771      outstandingRequestCount--;
22772      if (outstandingRequestCount === 0) {
22773        while (outstandingRequestCallbacks.length) {
22774          try {
22775            outstandingRequestCallbacks.pop()();
22776          } catch (e) {
22777            $log.error(e);
22778          }
22779        }
22780      }
22781    }
22782  }
22783
22784  function getHash(url) {
22785    var index = url.indexOf('#');
22786    return index === -1 ? '' : url.substr(index);
22787  }
22788
22789  /**
22790   * @private
22791   * Note: this method is used only by scenario runner
22792   * TODO(vojta): prefix this method with $$ ?
22793   * @param {function()} callback Function that will be called when no outstanding request
22794   */
22795  self.notifyWhenNoOutstandingRequests = function(callback) {
22796    if (outstandingRequestCount === 0) {
22797      callback();
22798    } else {
22799      outstandingRequestCallbacks.push(callback);
22800    }
22801  };
22802
22803  //////////////////////////////////////////////////////////////
22804  // URL API
22805  //////////////////////////////////////////////////////////////
22806
22807  var cachedState, lastHistoryState,
22808      lastBrowserUrl = location.href,
22809      baseElement = document.find('base'),
22810      pendingLocation = null;
22811
22812  cacheState();
22813  lastHistoryState = cachedState;
22814
22815  /**
22816   * @name $browser#url
22817   *
22818   * @description
22819   * GETTER:
22820   * Without any argument, this method just returns current value of location.href.
22821   *
22822   * SETTER:
22823   * With at least one argument, this method sets url to new value.
22824   * If html5 history api supported, pushState/replaceState is used, otherwise
22825   * location.href/location.replace is used.
22826   * Returns its own instance to allow chaining
22827   *
22828   * NOTE: this api is intended for use only by the $location service. Please use the
22829   * {@link ng.$location $location service} to change url.
22830   *
22831   * @param {string} url New url (when used as setter)
22832   * @param {boolean=} replace Should new url replace current history record?
22833   * @param {object=} state object to use with pushState/replaceState
22834   */
22835  self.url = function(url, replace, state) {
22836    // In modern browsers `history.state` is `null` by default; treating it separately
22837    // from `undefined` would cause `$browser.url('/foo')` to change `history.state`
22838    // to undefined via `pushState`. Instead, let's change `undefined` to `null` here.
22839    if (isUndefined(state)) {
22840      state = null;
22841    }
22842
22843    // Android Browser BFCache causes location, history reference to become stale.
22844    if (location !== window.location) location = window.location;
22845    if (history !== window.history) history = window.history;
22846
22847    // setter
22848    if (url) {
22849      var sameState = lastHistoryState === state;
22850
22851      // Don't change anything if previous and current URLs and states match. This also prevents
22852      // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
22853      // See https://github.com/angular/angular.js/commit/ffb2701
22854      if (lastBrowserUrl === url && (!$sniffer.history || sameState)) {
22855        return self;
22856      }
22857      var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
22858      lastBrowserUrl = url;
22859      lastHistoryState = state;
22860      // Don't use history API if only the hash changed
22861      // due to a bug in IE10/IE11 which leads
22862      // to not firing a `hashchange` nor `popstate` event
22863      // in some cases (see #9143).
22864      if ($sniffer.history && (!sameBase || !sameState)) {
22865        history[replace ? 'replaceState' : 'pushState'](state, '', url);
22866        cacheState();
22867        // Do the assignment again so that those two variables are referentially identical.
22868        lastHistoryState = cachedState;
22869      } else {
22870        if (!sameBase || pendingLocation) {
22871          pendingLocation = url;
22872        }
22873        if (replace) {
22874          location.replace(url);
22875        } else if (!sameBase) {
22876          location.href = url;
22877        } else {
22878          location.hash = getHash(url);
22879        }
22880        if (location.href !== url) {
22881          pendingLocation = url;
22882        }
22883      }
22884      return self;
22885    // getter
22886    } else {
22887      // - pendingLocation is needed as browsers don't allow to read out
22888      //   the new location.href if a reload happened or if there is a bug like in iOS 9 (see
22889      //   https://openradar.appspot.com/22186109).
22890      // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
22891      return pendingLocation || location.href.replace(/%27/g,"'");
22892    }
22893  };
22894
22895  /**
22896   * @name $browser#state
22897   *
22898   * @description
22899   * This method is a getter.
22900   *
22901   * Return history.state or null if history.state is undefined.
22902   *
22903   * @returns {object} state
22904   */
22905  self.state = function() {
22906    return cachedState;
22907  };
22908
22909  var urlChangeListeners = [],
22910      urlChangeInit = false;
22911
22912  function cacheStateAndFireUrlChange() {
22913    pendingLocation = null;
22914    cacheState();
22915    fireUrlChange();
22916  }
22917
22918  function getCurrentState() {
22919    try {
22920      return history.state;
22921    } catch (e) {
22922      // MSIE can reportedly throw when there is no state (UNCONFIRMED).
22923    }
22924  }
22925
22926  // This variable should be used *only* inside the cacheState function.
22927  var lastCachedState = null;
22928  function cacheState() {
22929    // This should be the only place in $browser where `history.state` is read.
22930    cachedState = getCurrentState();
22931    cachedState = isUndefined(cachedState) ? null : cachedState;
22932
22933    // Prevent callbacks fo fire twice if both hashchange & popstate were fired.
22934    if (equals(cachedState, lastCachedState)) {
22935      cachedState = lastCachedState;
22936    }
22937    lastCachedState = cachedState;
22938  }
22939
22940  function fireUrlChange() {
22941    if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) {
22942      return;
22943    }
22944
22945    lastBrowserUrl = self.url();
22946    lastHistoryState = cachedState;
22947    forEach(urlChangeListeners, function(listener) {
22948      listener(self.url(), cachedState);
22949    });
22950  }
22951
22952  /**
22953   * @name $browser#onUrlChange
22954   *
22955   * @description
22956   * Register callback function that will be called, when url changes.
22957   *
22958   * It's only called when the url is changed from outside of angular:
22959   * - user types different url into address bar
22960   * - user clicks on history (forward/back) button
22961   * - user clicks on a link
22962   *
22963   * It's not called when url is changed by $browser.url() method
22964   *
22965   * The listener gets called with new url as parameter.
22966   *
22967   * NOTE: this api is intended for use only by the $location service. Please use the
22968   * {@link ng.$location $location service} to monitor url changes in angular apps.
22969   *
22970   * @param {function(string)} listener Listener function to be called when url changes.
22971   * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
22972   */
22973  self.onUrlChange = function(callback) {
22974    // TODO(vojta): refactor to use node's syntax for events
22975    if (!urlChangeInit) {
22976      // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
22977      // don't fire popstate when user change the address bar and don't fire hashchange when url
22978      // changed by push/replaceState
22979
22980      // html5 history api - popstate event
22981      if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange);
22982      // hashchange event
22983      jqLite(window).on('hashchange', cacheStateAndFireUrlChange);
22984
22985      urlChangeInit = true;
22986    }
22987
22988    urlChangeListeners.push(callback);
22989    return callback;
22990  };
22991
22992  /**
22993   * @private
22994   * Remove popstate and hashchange handler from window.
22995   *
22996   * NOTE: this api is intended for use only by $rootScope.
22997   */
22998  self.$$applicationDestroyed = function() {
22999    jqLite(window).off('hashchange popstate', cacheStateAndFireUrlChange);
23000  };
23001
23002  /**
23003   * Checks whether the url has changed outside of Angular.
23004   * Needs to be exported to be able to check for changes that have been done in sync,
23005   * as hashchange/popstate events fire in async.
23006   */
23007  self.$$checkUrlChange = fireUrlChange;
23008
23009  //////////////////////////////////////////////////////////////
23010  // Misc API
23011  //////////////////////////////////////////////////////////////
23012
23013  /**
23014   * @name $browser#baseHref
23015   *
23016   * @description
23017   * Returns current <base href>
23018   * (always relative - without domain)
23019   *
23020   * @returns {string} The current base href
23021   */
23022  self.baseHref = function() {
23023    var href = baseElement.attr('href');
23024    return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : '';
23025  };
23026
23027  /**
23028   * @name $browser#defer
23029   * @param {function()} fn A function, who's execution should be deferred.
23030   * @param {number=} [delay=0] of milliseconds to defer the function execution.
23031   * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
23032   *
23033   * @description
23034   * Executes a fn asynchronously via `setTimeout(fn, delay)`.
23035   *
23036   * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
23037   * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
23038   * via `$browser.defer.flush()`.
23039   *
23040   */
23041  self.defer = function(fn, delay) {
23042    var timeoutId;
23043    outstandingRequestCount++;
23044    timeoutId = setTimeout(function() {
23045      delete pendingDeferIds[timeoutId];
23046      completeOutstandingRequest(fn);
23047    }, delay || 0);
23048    pendingDeferIds[timeoutId] = true;
23049    return timeoutId;
23050  };
23051
23052
23053  /**
23054   * @name $browser#defer.cancel
23055   *
23056   * @description
23057   * Cancels a deferred task identified with `deferId`.
23058   *
23059   * @param {*} deferId Token returned by the `$browser.defer` function.
23060   * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
23061   *                    canceled.
23062   */
23063  self.defer.cancel = function(deferId) {
23064    if (pendingDeferIds[deferId]) {
23065      delete pendingDeferIds[deferId];
23066      clearTimeout(deferId);
23067      completeOutstandingRequest(noop);
23068      return true;
23069    }
23070    return false;
23071  };
23072
23073}
23074
23075function $BrowserProvider() {
23076  this.$get = ['$window', '$log', '$sniffer', '$document',
23077      function($window, $log, $sniffer, $document) {
23078        return new Browser($window, $document, $log, $sniffer);
23079      }];
23080}
23081
23082/**
23083 * @ngdoc service
23084 * @name $cacheFactory
23085 *
23086 * @description
23087 * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
23088 * them.
23089 *
23090 * ```js
23091 *
23092 *  var cache = $cacheFactory('cacheId');
23093 *  expect($cacheFactory.get('cacheId')).toBe(cache);
23094 *  expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
23095 *
23096 *  cache.put("key", "value");
23097 *  cache.put("another key", "another value");
23098 *
23099 *  // We've specified no options on creation
23100 *  expect(cache.info()).toEqual({id: 'cacheId', size: 2});
23101 *
23102 * ```
23103 *
23104 *
23105 * @param {string} cacheId Name or id of the newly created cache.
23106 * @param {object=} options Options object that specifies the cache behavior. Properties:
23107 *
23108 *   - `{number=}` `capacity` — turns the cache into LRU cache.
23109 *
23110 * @returns {object} Newly created cache object with the following set of methods:
23111 *
23112 * - `{object}` `info()` — Returns id, size, and options of cache.
23113 * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns
23114 *   it.
23115 * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
23116 * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
23117 * - `{void}` `removeAll()` — Removes all cached values.
23118 * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
23119 *
23120 * @example
23121   <example module="cacheExampleApp">
23122     <file name="index.html">
23123       <div ng-controller="CacheController">
23124         <input ng-model="newCacheKey" placeholder="Key">
23125         <input ng-model="newCacheValue" placeholder="Value">
23126         <button ng-click="put(newCacheKey, newCacheValue)">Cache</button>
23127
23128         <p ng-if="keys.length">Cached Values</p>
23129         <div ng-repeat="key in keys">
23130           <span ng-bind="key"></span>
23131           <span>: </span>
23132           <b ng-bind="cache.get(key)"></b>
23133         </div>
23134
23135         <p>Cache Info</p>
23136         <div ng-repeat="(key, value) in cache.info()">
23137           <span ng-bind="key"></span>
23138           <span>: </span>
23139           <b ng-bind="value"></b>
23140         </div>
23141       </div>
23142     </file>
23143     <file name="script.js">
23144       angular.module('cacheExampleApp', []).
23145         controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {
23146           $scope.keys = [];
23147           $scope.cache = $cacheFactory('cacheId');
23148           $scope.put = function(key, value) {
23149             if (angular.isUndefined($scope.cache.get(key))) {
23150               $scope.keys.push(key);
23151             }
23152             $scope.cache.put(key, angular.isUndefined(value) ? null : value);
23153           };
23154         }]);
23155     </file>
23156     <file name="style.css">
23157       p {
23158         margin: 10px 0 3px;
23159       }
23160     </file>
23161   </example>
23162 */
23163function $CacheFactoryProvider() {
23164
23165  this.$get = function() {
23166    var caches = {};
23167
23168    function cacheFactory(cacheId, options) {
23169      if (cacheId in caches) {
23170        throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId);
23171      }
23172
23173      var size = 0,
23174          stats = extend({}, options, {id: cacheId}),
23175          data = {},
23176          capacity = (options && options.capacity) || Number.MAX_VALUE,
23177          lruHash = {},
23178          freshEnd = null,
23179          staleEnd = null;
23180
23181      /**
23182       * @ngdoc type
23183       * @name $cacheFactory.Cache
23184       *
23185       * @description
23186       * A cache object used to store and retrieve data, primarily used by
23187       * {@link $http $http} and the {@link ng.directive:script script} directive to cache
23188       * templates and other data.
23189       *
23190       * ```js
23191       *  angular.module('superCache')
23192       *    .factory('superCache', ['$cacheFactory', function($cacheFactory) {
23193       *      return $cacheFactory('super-cache');
23194       *    }]);
23195       * ```
23196       *
23197       * Example test:
23198       *
23199       * ```js
23200       *  it('should behave like a cache', inject(function(superCache) {
23201       *    superCache.put('key', 'value');
23202       *    superCache.put('another key', 'another value');
23203       *
23204       *    expect(superCache.info()).toEqual({
23205       *      id: 'super-cache',
23206       *      size: 2
23207       *    });
23208       *
23209       *    superCache.remove('another key');
23210       *    expect(superCache.get('another key')).toBeUndefined();
23211       *
23212       *    superCache.removeAll();
23213       *    expect(superCache.info()).toEqual({
23214       *      id: 'super-cache',
23215       *      size: 0
23216       *    });
23217       *  }));
23218       * ```
23219       */
23220      return caches[cacheId] = {
23221
23222        /**
23223         * @ngdoc method
23224         * @name $cacheFactory.Cache#put
23225         * @kind function
23226         *
23227         * @description
23228         * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be
23229         * retrieved later, and incrementing the size of the cache if the key was not already
23230         * present in the cache. If behaving like an LRU cache, it will also remove stale
23231         * entries from the set.
23232         *
23233         * It will not insert undefined values into the cache.
23234         *
23235         * @param {string} key the key under which the cached data is stored.
23236         * @param {*} value the value to store alongside the key. If it is undefined, the key
23237         *    will not be stored.
23238         * @returns {*} the value stored.
23239         */
23240        put: function(key, value) {
23241          if (isUndefined(value)) return;
23242          if (capacity < Number.MAX_VALUE) {
23243            var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
23244
23245            refresh(lruEntry);
23246          }
23247
23248          if (!(key in data)) size++;
23249          data[key] = value;
23250
23251          if (size > capacity) {
23252            this.remove(staleEnd.key);
23253          }
23254
23255          return value;
23256        },
23257
23258        /**
23259         * @ngdoc method
23260         * @name $cacheFactory.Cache#get
23261         * @kind function
23262         *
23263         * @description
23264         * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.
23265         *
23266         * @param {string} key the key of the data to be retrieved
23267         * @returns {*} the value stored.
23268         */
23269        get: function(key) {
23270          if (capacity < Number.MAX_VALUE) {
23271            var lruEntry = lruHash[key];
23272
23273            if (!lruEntry) return;
23274
23275            refresh(lruEntry);
23276          }
23277
23278          return data[key];
23279        },
23280
23281
23282        /**
23283         * @ngdoc method
23284         * @name $cacheFactory.Cache#remove
23285         * @kind function
23286         *
23287         * @description
23288         * Removes an entry from the {@link $cacheFactory.Cache Cache} object.
23289         *
23290         * @param {string} key the key of the entry to be removed
23291         */
23292        remove: function(key) {
23293          if (capacity < Number.MAX_VALUE) {
23294            var lruEntry = lruHash[key];
23295
23296            if (!lruEntry) return;
23297
23298            if (lruEntry == freshEnd) freshEnd = lruEntry.p;
23299            if (lruEntry == staleEnd) staleEnd = lruEntry.n;
23300            link(lruEntry.n,lruEntry.p);
23301
23302            delete lruHash[key];
23303          }
23304
23305          delete data[key];
23306          size--;
23307        },
23308
23309
23310        /**
23311         * @ngdoc method
23312         * @name $cacheFactory.Cache#removeAll
23313         * @kind function
23314         *
23315         * @description
23316         * Clears the cache object of any entries.
23317         */
23318        removeAll: function() {
23319          data = {};
23320          size = 0;
23321          lruHash = {};
23322          freshEnd = staleEnd = null;
23323        },
23324
23325
23326        /**
23327         * @ngdoc method
23328         * @name $cacheFactory.Cache#destroy
23329         * @kind function
23330         *
23331         * @description
23332         * Destroys the {@link $cacheFactory.Cache Cache} object entirely,
23333         * removing it from the {@link $cacheFactory $cacheFactory} set.
23334         */
23335        destroy: function() {
23336          data = null;
23337          stats = null;
23338          lruHash = null;
23339          delete caches[cacheId];
23340        },
23341
23342
23343        /**
23344         * @ngdoc method
23345         * @name $cacheFactory.Cache#info
23346         * @kind function
23347         *
23348         * @description
23349         * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.
23350         *
23351         * @returns {object} an object with the following properties:
23352         *   <ul>
23353         *     <li>**id**: the id of the cache instance</li>
23354         *     <li>**size**: the number of entries kept in the cache instance</li>
23355         *     <li>**...**: any additional properties from the options object when creating the
23356         *       cache.</li>
23357         *   </ul>
23358         */
23359        info: function() {
23360          return extend({}, stats, {size: size});
23361        }
23362      };
23363
23364
23365      /**
23366       * makes the `entry` the freshEnd of the LRU linked list
23367       */
23368      function refresh(entry) {
23369        if (entry != freshEnd) {
23370          if (!staleEnd) {
23371            staleEnd = entry;
23372          } else if (staleEnd == entry) {
23373            staleEnd = entry.n;
23374          }
23375
23376          link(entry.n, entry.p);
23377          link(entry, freshEnd);
23378          freshEnd = entry;
23379          freshEnd.n = null;
23380        }
23381      }
23382
23383
23384      /**
23385       * bidirectionally links two entries of the LRU linked list
23386       */
23387      function link(nextEntry, prevEntry) {
23388        if (nextEntry != prevEntry) {
23389          if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
23390          if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
23391        }
23392      }
23393    }
23394
23395
23396  /**
23397   * @ngdoc method
23398   * @name $cacheFactory#info
23399   *
23400   * @description
23401   * Get information about all the caches that have been created
23402   *
23403   * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
23404   */
23405    cacheFactory.info = function() {
23406      var info = {};
23407      forEach(caches, function(cache, cacheId) {
23408        info[cacheId] = cache.info();
23409      });
23410      return info;
23411    };
23412
23413
23414  /**
23415   * @ngdoc method
23416   * @name $cacheFactory#get
23417   *
23418   * @description
23419   * Get access to a cache object by the `cacheId` used when it was created.
23420   *
23421   * @param {string} cacheId Name or id of a cache to access.
23422   * @returns {object} Cache object identified by the cacheId or undefined if no such cache.
23423   */
23424    cacheFactory.get = function(cacheId) {
23425      return caches[cacheId];
23426    };
23427
23428
23429    return cacheFactory;
23430  };
23431}
23432
23433/**
23434 * @ngdoc service
23435 * @name $templateCache
23436 *
23437 * @description
23438 * The first time a template is used, it is loaded in the template cache for quick retrieval. You
23439 * can load templates directly into the cache in a `script` tag, or by consuming the
23440 * `$templateCache` service directly.
23441 *
23442 * Adding via the `script` tag:
23443 *
23444 * ```html
23445 *   <script type="text/ng-template" id="templateId.html">
23446 *     <p>This is the content of the template</p>
23447 *   </script>
23448 * ```
23449 *
23450 * **Note:** the `script` tag containing the template does not need to be included in the `head` of
23451 * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE,
23452 * element with ng-app attribute), otherwise the template will be ignored.
23453 *
23454 * Adding via the `$templateCache` service:
23455 *
23456 * ```js
23457 * var myApp = angular.module('myApp', []);
23458 * myApp.run(function($templateCache) {
23459 *   $templateCache.put('templateId.html', 'This is the content of the template');
23460 * });
23461 * ```
23462 *
23463 * To retrieve the template later, simply use it in your HTML:
23464 * ```html
23465 * <div ng-include=" 'templateId.html' "></div>
23466 * ```
23467 *
23468 * or get it via Javascript:
23469 * ```js
23470 * $templateCache.get('templateId.html')
23471 * ```
23472 *
23473 * See {@link ng.$cacheFactory $cacheFactory}.
23474 *
23475 */
23476function $TemplateCacheProvider() {
23477  this.$get = ['$cacheFactory', function($cacheFactory) {
23478    return $cacheFactory('templates');
23479  }];
23480}
23481
23482/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23483 *     Any commits to this file should be reviewed with security in mind.  *
23484 *   Changes to this file can potentially create security vulnerabilities. *
23485 *          An approval from 2 Core members with history of modifying      *
23486 *                         this file is required.                          *
23487 *                                                                         *
23488 *  Does the change somehow allow for arbitrary javascript to be executed? *
23489 *    Or allows for someone to change the prototype of built-in objects?   *
23490 *     Or gives undesired access to variables likes document or window?    *
23491 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
23492
23493/* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
23494 *
23495 * DOM-related variables:
23496 *
23497 * - "node" - DOM Node
23498 * - "element" - DOM Element or Node
23499 * - "$node" or "$element" - jqLite-wrapped node or element
23500 *
23501 *
23502 * Compiler related stuff:
23503 *
23504 * - "linkFn" - linking fn of a single directive
23505 * - "nodeLinkFn" - function that aggregates all linking fns for a particular node
23506 * - "childLinkFn" -  function that aggregates all linking fns for child nodes of a particular node
23507 * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
23508 */
23509
23510
23511/**
23512 * @ngdoc service
23513 * @name $compile
23514 * @kind function
23515 *
23516 * @description
23517 * Compiles an HTML string or DOM into a template and produces a template function, which
23518 * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
23519 *
23520 * The compilation is a process of walking the DOM tree and matching DOM elements to
23521 * {@link ng.$compileProvider#directive directives}.
23522 *
23523 * <div class="alert alert-warning">
23524 * **Note:** This document is an in-depth reference of all directive options.
23525 * For a gentle introduction to directives with examples of common use cases,
23526 * see the {@link guide/directive directive guide}.
23527 * </div>
23528 *
23529 * ## Comprehensive Directive API
23530 *
23531 * There are many different options for a directive.
23532 *
23533 * The difference resides in the return value of the factory function.
23534 * You can either return a "Directive Definition Object" (see below) that defines the directive properties,
23535 * or just the `postLink` function (all other properties will have the default values).
23536 *
23537 * <div class="alert alert-success">
23538 * **Best Practice:** It's recommended to use the "directive definition object" form.
23539 * </div>
23540 *
23541 * Here's an example directive declared with a Directive Definition Object:
23542 *
23543 * ```js
23544 *   var myModule = angular.module(...);
23545 *
23546 *   myModule.directive('directiveName', function factory(injectables) {
23547 *     var directiveDefinitionObject = {
23548 *       priority: 0,
23549 *       template: '<div></div>
23549', // or // function(tElement, tAttrs) { ... },
23550 *       // or
23551 *       // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
23552 *       transclude: false,
23553 *       restrict: 'A',
23554 *       templateNamespace: 'html',
23555 *       scope: false,
23556 *       controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
23557 *       controllerAs: 'stringIdentifier',
23558 *       bindToController: false,
23559 *       require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
23560 *       compile: function compile(tElement, tAttrs, transclude) {
23561 *         return {
23562 *           pre: function preLink(scope, iElement, iAttrs, controller) { ... },
23563 *           post: function postLink(scope, iElement, iAttrs, controller) { ... }
23564 *         }
23565 *         // or
23566 *         // return function postLink( ... ) { ... }
23567 *       },
23568 *       // or
23569 *       // link: {
23570 *       //  pre: function preLink(scope, iElement, iAttrs, controller) { ... },
23571 *       //  post: function postLink(scope, iElement, iAttrs, controller) { ... }
23572 *       // }
23573 *       // or
23574 *       // link: function postLink( ... ) { ... }
23575 *     };
23576 *     return directiveDefinitionObject;
23577 *   });
23578 * ```
23579 *
23580 * <div class="alert alert-warning">
23581 * **Note:** Any unspecified options will use the default value. You can see the default values below.
23582 * </div>
23583 *
23584 * Therefore the above can be simplified as:
23585 *
23586 * ```js
23587 *   var myModule = angular.module(...);
23588 *
23589 *   myModule.directive('directiveName', function factory(injectables) {
23590 *     var directiveDefinitionObject = {
23591 *       link: function postLink(scope, iElement, iAttrs) { ... }
23592 *     };
23593 *     return directiveDefinitionObject;
23594 *     // or
23595 *     // return function postLink(scope, iElement, iAttrs) { ... }
23596 *   });
23597 * ```
23598 *
23599 *
23600 *
23601 * ### Directive Definition Object
23602 *
23603 * The directive definition object provides instructions to the {@link ng.$compile
23604 * compiler}. The attributes are:
23605 *
23606 * #### `multiElement`
23607 * When this property is set to true, the HTML compiler will collect DOM nodes between
23608 * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
23609 * together as the directive elements. It is recommended that this feature be used on directives
23610 * which are not strictly behavioural (such as {@link ngClick}), and which
23611 * do not manipulate or replace child nodes (such as {@link ngInclude}).
23612 *
23613 * #### `priority`
23614 * When there are multiple directives defined on a single DOM element, sometimes it
23615 * is necessary to specify the order in which the directives are applied. The `priority` is used
23616 * to sort the directives before their `compile` functions get called. Priority is defined as a
23617 * number. Directives with greater numerical `priority` are compiled first. Pre-link functions
23618 * are also run in priority order, but post-link functions are run in reverse order. The order
23619 * of directives with the same priority is undefined. The default priority is `0`.
23620 *
23621 * #### `terminal`
23622 * If set to true then the current `priority` will be the last set of directives
23623 * which will execute (any directives at the current priority will still execute
23624 * as the order of execution on same `priority` is undefined). Note that expressions
23625 * and other directives used in the directive's template will also be excluded from execution.
23626 *
23627 * #### `scope`
23628 * The scope property can be `true`, an object or a falsy value:
23629 *
23630 * * **falsy:** No scope will be created for the directive. The directive will use its parent's scope.
23631 *
23632 * * **`true`:** A new child scope that prototypically inherits from its parent will be created for
23633 * the directive's element. If multiple directives on the same element request a new scope,
23634 * only one new scope is created. The new scope rule does not apply for the root of the template
23635 * since the root of the template always gets a new scope.
23636 *
23637 * * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's element. The
23638 * 'isolate' scope differs from normal scope in that it does not prototypically inherit from its parent
23639 * scope. This is useful when creating reusable components, which should not accidentally read or modify
23640 * data in the parent scope.
23641 *
23642 * The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the
23643 * directive's element. These local properties are useful for aliasing values for templates. The keys in
23644 * the object hash map to the name of the property on the isolate scope; the values define how the property
23645 * is bound to the parent scope, via matching attributes on the directive's element:
23646 *
23647 * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
23648 *   always a string since DOM attributes are strings. If no `attr` name is specified  then the
23649 *   attribute name is assumed to be the same as the local name.
23650 *   Given `<widget my-attr="hello {{name}}">` and widget definition
23651 *   of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect
23652 *   the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the
23653 *   `localName` property on the widget scope. The `name` is read from the parent scope (not
23654 *   component scope).
23655 *
23656 * * `=` or `=attr` - set up bi-directional binding between a local scope property and the
23657 *   parent scope property of name defined via the value of the `attr` attribute. If no `attr`
23658 *   name is specified then the attribute name is assumed to be the same as the local name.
23659 *   Given `<widget my-attr="parentModel">` and widget definition of
23660 *   `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the
23661 *   value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
23662 *   in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent
23663 *   scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You
23664 *   can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional. If
23665 *   you want to shallow watch for changes (i.e. $watchCollection instead of $watch) you can use
23666 *   `=*` or `=*attr` (`=*?` or `=*?attr` if the property is optional).
23667 *
23668 * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope.
23669 *   If no `attr` name is specified then the attribute name is assumed to be the same as the
23670 *   local name. Given `<widget my-attr="count = count + value">` and widget definition of
23671 *   `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to
23672 *   a function wrapper for the `count = count + value` expression. Often it's desirable to
23673 *   pass data from the isolated scope via an expression to the parent scope, this can be
23674 *   done by passing a map of local variable names and values into the expression wrapper fn.
23675 *   For example, if the expression is `increment(amount)` then we can specify the amount value
23676 *   by calling the `localFn` as `localFn({amount: 22})`.
23677 *
23678 * In general it's possible to apply more than one directive to one element, but there might be limitations
23679 * depending on the type of scope required by the directives. The following points will help explain these limitations.
23680 * For simplicity only two directives are taken into account, but it is also applicable for several directives:
23681 *
23682 * * **no scope** + **no scope** => Two directives which don't require their own scope will use their parent's scope
23683 * * **child scope** + **no scope** =>  Both directives will share one single child scope
23684 * * **child scope** + **child scope** =>  Both directives will share one single child scope
23685 * * **isolated scope** + **no scope** =>  The isolated directive will use it's own created isolated scope. The other directive will use
23686 * its parent's scope
23687 * * **isolated scope** + **child scope** =>  **Won't work!** Only one scope can be related to one element. Therefore these directives cannot
23688 * be applied to the same element.
23689 * * **isolated scope** + **isolated scope**  =>  **Won't work!** Only one scope can be related to one element. Therefore these directives
23690 * cannot be applied to the same element.
23691 *
23692 *
23693 * #### `bindToController`
23694 * When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController: true` will
23695 * allow a component to have its properties bound to the controller, rather than to scope. When the controller
23696 * is instantiated, the initial values of the isolate scope bindings are already available.
23697 *
23698 * #### `controller`
23699 * Controller constructor function. The controller is instantiated before the
23700 * pre-linking phase and can be accessed by other directives (see
23701 * `require` attribute). This allows the directives to communicate with each other and augment
23702 * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
23703 *
23704 * * `$scope` - Current scope associated with the element
23705 * * `$element` - Current element
23706 * * `$attrs` - Current attributes object for the element
23707 * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
23708 *   `function([scope], cloneLinkingFn, futureParentElement)`.
23709 *    * `scope`: optional argument to override the scope.
23710 *    * `cloneLinkingFn`: optional argument to create clones of the original transcluded content.
23711 *    * `futureParentElement`:
23712 *        * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
23713 *        * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
23714 *        * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
23715 *          and when the `cloneLinkinFn` is passed,
23716 *          as those elements need to created and cloned in a special way when they are defined outside their
23717 *          usual containers (e.g. like `<svg>`).
23718 *        * See also the `directive.templateNamespace` property.
23719 *
23720 *
23721 * #### `require`
23722 * Require another directive and inject its controller as the fourth argument to the linking function. The
23723 * `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the
23724 * injected argument will be an array in corresponding order. If no such directive can be
23725 * found, or if the directive does not have a controller, then an error is raised (unless no link function
23726 * is specified, in which case error checking is skipped). The name can be prefixed with:
23727 *
23728 * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
23729 * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
23730 * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
23731 * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
23732 * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
23733 *   `null` to the `link` fn if not found.
23734 * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
23735 *   `null` to the `link` fn if not found.
23736 *
23737 *
23738 * #### `controllerAs`
23739 * Identifier name for a reference to the controller in the directive's scope.
23740 * This allows the controller to be referenced from the directive template. This is especially
23741 * useful when a directive is used as component, i.e. with an `isolate` scope. It's also possible
23742 * to use it in a directive without an `isolate` / `new` scope, but you need to be aware that the
23743 * `controllerAs` reference might overwrite a property that already exists on the parent scope.
23744 *
23745 *
23746 * #### `restrict`
23747 * String of subset of `EACM` which restricts the directive to a specific directive
23748 * declaration style. If omitted, the defaults (elements and attributes) are used.
23749 *
23750 * * `E` - Element name (default): `<my-directive></my-directive>`
23751 * * `A` - Attribute (default): `<div my-directive="exp"></div>`
23752 * * `C` - Class: `<div class="my-directive: exp;"></div>`
23753 * * `M` - Comment: `<!-- directive: my-directive exp -->`
23754 *
23755 *
23756 * #### `templateNamespace`
23757 * String representing the document type used by the markup in the template.
23758 * AngularJS needs this information as those elements need to be created and cloned
23759 * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`.
23760 *
23761 * * `html` - All root nodes in the template are HTML. Root nodes may also be
23762 *   top-level elements such as `<svg>` or `<math>`.
23763 * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`).
23764 * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`).
23765 *
23766 * If no `templateNamespace` is specified, then the namespace is considered to be `html`.
23767 *
23768 * #### `template`
23769 * HTML markup that may:
23770 * * Replace the contents of the directive's element (default).
23771 * * Replace the directive's element itself (if `replace` is true - DEPRECATED).
23772 * * Wrap the contents of the directive's element (if `transclude` is true).
23773 *
23774 * Value may be:
23775 *
23776 * * A string. For example `<div red-on-hover>{{delete_str}}</div>`.
23777 * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile`
23778 *   function api below) and returns a string value.
23779 *
23780 *
23781 * #### `templateUrl`
23782 * This is similar to `template` but the template is loaded from the specified URL, asynchronously.
23783 *
23784 * Because template loading is asynchronous the compiler will suspend compilation of directives on that element
23785 * for later when the template has been resolved.  In the meantime it will continue to compile and link
23786 * sibling and parent elements as though this element had not contained any directives.
23787 *
23788 * The compiler does not suspend the entire compilation to wait for templates to be loaded because this
23789 * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the
23790 * case when only one deeply nested directive has `templateUrl`.
23791 *
23792 * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache}
23793 *
23794 * You can specify `templateUrl` as a string representing the URL or as a function which takes two
23795 * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
23796 * a string value representing the url.  In either case, the template URL is passed through {@link
23797 * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
23798 *
23799 *
23800 * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0)
23801 * specify what the template should replace. Defaults to `false`.
23802 *
23803 * * `true` - the template will replace the directive's element.
23804 * * `false` - the template will replace the contents of the directive's element.
23805 *
23806 * The replacement process migrates all of the attributes / classes from the old element to the new
23807 * one. See the {@link guide/directive#template-expanding-directive
23808 * Directives Guide} for an example.
23809 *
23810 * There are very few scenarios where element replacement is required for the application function,
23811 * the main one being reusable custom components that are used within SVG contexts
23812 * (because SVG doesn't work with custom elements in the DOM tree).
23813 *
23814 * #### `transclude`
23815 * Extract the contents of the element where the directive appears and make it available to the directive.
23816 * The contents are compiled and provided to the directive as a **transclusion function**. See the
23817 * {@link $compile#transclusion Transclusion} section below.
23818 *
23819 * There are two kinds of transclusion depending upon whether you want to transclude just the contents of the
23820 * directive's element or the entire element:
23821 *
23822 * * `true` - transclude the content (i.e. the child nodes) of the directive's element.
23823 * * `'element'` - transclude the whole of the directive's element including any directives on this
23824 *   element that defined at a lower priority than this directive. When used, the `template`
23825 *   property is ignored.
23826 *
23827 *
23828 * #### `compile`
23829 *
23830 * ```js
23831 *   function compile(tElement, tAttrs, transclude) { ... }
23832 * ```
23833 *
23834 * The compile function deals with transforming the template DOM. Since most directives do not do
23835 * template transformation, it is not used often. The compile function takes the following arguments:
23836 *
23837 *   * `tElement` - template element - The element where the directive has been declared. It is
23838 *     safe to do template transformation on the element and child elements only.
23839 *
23840 *   * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
23841 *     between all directive compile functions.
23842 *
23843 *   * `transclude` -  [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
23844 *
23845 * <div class="alert alert-warning">
23846 * **Note:** The template instance and the link instance may be different objects if the template has
23847 * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
23848 * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
23849 * should be done in a linking function rather than in a compile function.
23850 * </div>
23851
23852 * <div class="alert alert-warning">
23853 * **Note:** The compile function cannot handle directives that recursively use themselves in their
23854 * own templates or compile functions. Compiling these directives results in an infinite loop and a
23855 * stack overflow errors.
23856 *
23857 * This can be avoided by manually using $compile in the postLink function to imperatively compile
23858 * a directive's template instead of relying on automatic template compilation via `template` or
23859 * `templateUrl` declaration or manual compilation inside the compile function.
23860 * </div>
23861 *
23862 * <div class="alert alert-danger">
23863 * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
23864 *   e.g. does not know about the right outer scope. Please use the transclude function that is passed
23865 *   to the link function instead.
23866 * </div>
23867
23868 * A compile function can have a return value which can be either a function or an object.
23869 *
23870 * * returning a (post-link) function - is equivalent to registering the linking function via the
23871 *   `link` property of the config object when the compile function is empty.
23872 *
23873 * * returning an object with function(s) registered via `pre` and `post` properties - allows you to
23874 *   control when a linking function should be called during the linking phase. See info about
23875 *   pre-linking and post-linking functions below.
23876 *
23877 *
23878 * #### `link`
23879 * This property is used only if the `compile` property is not defined.
23880 *
23881 * ```js
23882 *   function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
23883 * ```
23884 *
23885 * The link function is responsible for registering DOM listeners as well as updating the DOM. It is
23886 * executed after the template has been cloned. This is where most of the directive logic will be
23887 * put.
23888 *
23889 *   * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the
23890 *     directive for registering {@link ng.$rootScope.Scope#$watch w
23890atches}.
23891 *
23892 *   * `iElement` - instance element - The element where the directive is to be used. It is safe to
23893 *     manipulate the children of the element only in `postLink` function since the children have
23894 *     already been linked.
23895 *
23896 *   * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
23897 *     between all directive linking functions.
23898 *
23899 *   * `controller` - the directive's required controller instance(s) - Instances are shared
23900 *     among all directives, which allows the directives to use the controllers as a communication
23901 *     channel. The exact value depends on the directive's `require` property:
23902 *       * no controller(s) required: the directive's own controller, or `undefined` if it doesn't have one
23903 *       * `string`: the controller instance
23904 *       * `array`: array of controller instances
23905 *
23906 *     If a required controller cannot be found, and it is optional, the instance is `null`,
23907 *     otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown.
23908 *
23909 *     Note that you can also require the directive's own controller - it will be made available like
23910 *     any other controller.
23911 *
23912 *   * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
23913 *     This is the same as the `$transclude`
23914 *     parameter of directive controllers, see there for details.
23915 *     `function([scope], cloneLinkingFn, futureParentElement)`.
23916 *
23917 * #### Pre-linking function
23918 *
23919 * Executed before the child elements are linked. Not safe to do DOM transformation since the
23920 * compiler linking function will fail to locate the correct elements for linking.
23921 *
23922 * #### Post-linking function
23923 *
23924 * Executed after the child elements are linked.
23925 *
23926 * Note that child elements that contain `templateUrl` directives will not have been compiled
23927 * and linked since they are waiting for their template to load asynchronously and their own
23928 * compilation and linking has been suspended until that occurs.
23929 *
23930 * It is safe to do DOM transformation in the post-linking function on elements that are not waiting
23931 * for their async templates to be resolved.
23932 *
23933 *
23934 * ### Transclusion
23935 *
23936 * Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and
23937 * copying them to another part of the DOM, while maintaining their connection to the original AngularJS
23938 * scope from where they were taken.
23939 *
23940 * Transclusion is used (often with {@link ngTransclude}) to insert the
23941 * original contents of a directive's element into a specified place in the template of the directive.
23942 * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded
23943 * content has access to the properties on the scope from which it was taken, even if the directive
23944 * has isolated scope.
23945 * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}.
23946 *
23947 * This makes it possible for the widget to have private state for its template, while the transcluded
23948 * content has access to its originating scope.
23949 *
23950 * <div class="alert alert-warning">
23951 * **Note:** When testing an element transclude directive you must not place the directive at the root of the
23952 * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
23953 * Testing Transclusion Directives}.
23954 * </div>
23955 *
23956 * #### Transclusion Functions
23957 *
23958 * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
23959 * function** to the directive's `link` function and `controller`. This transclusion function is a special
23960 * **linking function** that will return the compiled contents linked to a new transclusion scope.
23961 *
23962 * <div class="alert alert-info">
23963 * If you are just using {@link ngTransclude} then you don't need to worry about this function, since
23964 * ngTransclude will deal with it for us.
23965 * </div>
23966 *
23967 * If you want to manually control the insertion and removal of the transcluded content in your directive
23968 * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery
23969 * object that contains the compiled DOM, which is linked to the correct transclusion scope.
23970 *
23971 * When you call a transclusion function you can pass in a **clone attach function**. This function accepts
23972 * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded
23973 * content and the `scope` is the newly created transclusion scope, to which the clone is bound.
23974 *
23975 * <div class="alert alert-info">
23976 * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a translude function
23977 * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
23978 * </div>
23979 *
23980 * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone
23981 * attach function**:
23982 *
23983 * ```js
23984 * var transcludedContent, transclusionScope;
23985 *
23986 * $transclude(function(clone, scope) {
23987 *   element.append(clone);
23988 *   transcludedContent = clone;
23989 *   transclusionScope = scope;
23990 * });
23991 * ```
23992 *
23993 * Later, if you want to remove the transcluded content from your DOM then you should also destroy the
23994 * associated transclusion scope:
23995 *
23996 * ```js
23997 * transcludedContent.remove();
23998 * transclusionScope.$destroy();
23999 * ```
24000 *
24001 * <div class="alert alert-info">
24002 * **Best Practice**: if you intend to add and remove transcluded content manually in your directive
24003 * (by calling the transclude function to get the DOM and calling `element.remove()` to remove it),
24004 * then you are also responsible for calling `$destroy` on the transclusion scope.
24005 * </div>
24006 *
24007 * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
24008 * automatically destroy their transluded clones as necessary so you do not need to worry about this if
24009 * you are simply using {@link ngTransclude} to inject the transclusion into your directive.
24010 *
24011 *
24012 * #### Transclusion Scopes
24013 *
24014 * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion
24015 * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed
24016 * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it
24017 * was taken.
24018 *
24019 * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look
24020 * like this:
24021 *
24022 * ```html
24023 * <div ng-app>
24024 *   <div isolate>
24025 *     <div transclusion>
24026 *     </div>
24027 *   </div>
24028 * </div>
24029 * ```
24030 *
24031 * The `$parent` scope hierarchy will look like this:
24032 *
24033 * ```
24034 * - $rootScope
24035 *   - isolate
24036 *     - transclusion
24037 * ```
24038 *
24039 * but the scopes will inherit prototypically from different scopes to their `$parent`.
24040 *
24041 * ```
24042 * - $rootScope
24043 *   - transclusion
24044 * - isolate
24045 * ```
24046 *
24047 *
24048 * ### Attributes
24049 *
24050 * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
24051 * `link()` or `compile()` functions. It has a variety of uses.
24052 *
24053 * accessing *Normalized attribute names:*
24054 * Directives like 'ngBind' can be expressed in many ways: 'ng:bind', `data-ng-bind`, or 'x-ng-bind'.
24055 * the attributes object allows for normalized access to
24056 *   the attributes.
24057 *
24058 * * *Directive inter-communication:* All directives share the same instance of the attributes
24059 *   object which allows the directives to use the attributes object as inter directive
24060 *   communication.
24061 *
24062 * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
24063 *   allowing other directives to read the interpolated value.
24064 *
24065 * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
24066 *   that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
24067 *   the only way to easily get the actual value because during the linking phase the interpolation
24068 *   hasn't been evaluated yet and so the value is at this time set to `undefined`.
24069 *
24070 * ```js
24071 * function linkingFn(scope, elm, attrs, ctrl) {
24072 *   // get the attribute value
24073 *   console.log(attrs.ngModel);
24074 *
24075 *   // change the attribute
24076 *   attrs.$set('ngModel', 'new value');
24077 *
24078 *   // observe changes to interpolated attribute
24079 *   attrs.$observe('ngModel', function(value) {
24080 *     console.log('ngModel has changed value to ' + value);
24081 *   });
24082 * }
24083 * ```
24084 *
24085 * ## Example
24086 *
24087 * <div class="alert alert-warning">
24088 * **Note**: Typically directives are registered with `module.directive`. The example below is
24089 * to illustrate how `$compile` works.
24090 * </div>
24091 *
24092 <example module="compileExample">
24093   <file name="index.html">
24094    <script>
24095      angular.module('compileExample', [], function($compileProvider) {
24096        // configure new 'compile' directive by passing a directive
24097        // factory function. The factory function injects the '$compile'
24098        $compileProvider.directive('compile', function($compile) {
24099          // directive factory creates a link function
24100          return function(scope, element, attrs) {
24101            scope.$watch(
24102              function(scope) {
24103                 // watch the 'compile' expression for changes
24104                return scope.$eval(attrs.compile);
24105              },
24106              function(value) {
24107                // when the 'compile' expression changes
24108                // assign it into the current DOM
24109                element.html(value);
24110
24111                // compile the new DOM and link it to the current
24112                // scope.
24113                // NOTE: we only compile .childNodes so that
24114                // we don't get into infinite loop compiling ourselves
24115                $compile(element.contents())(scope);
24116              }
24117            );
24118          };
24119        });
24120      })
24121      .controller('GreeterController', ['$scope', function($scope) {
24122        $scope.name = 'Angular';
24123        $scope.html = 'Hello {{name}}';
24124      }]);
24125    </script>
24126    <div ng-controller="GreeterController">
24127      <input ng-model="name"> <br/>
24128      <textarea ng-model="html"></textarea> <br/>
24129      <div compile="html"></div>
24130    </div>
24131   </file>
24132   <file name="protractor.js" type="protractor">
24133     it('should auto compile', function() {
24134       var textarea = $('textarea');
24135       var output = $('div[compile]');
24136       // The initial state reads 'Hello Angular'.
24137       expect(output.getText()).toBe('Hello Angular');
24138       textarea.clear();
24139       textarea.sendKeys('{{name}}!');
24140       expect(output.getText()).toBe('Angular!');
24141     });
24142   </file>
24143 </example>
24144
24145 *
24146 *
24147 * @param {string|DOMElement} element Element or HTML string to compile into a template function.
24148 * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED.
24149 *
24150 * <div class="alert alert-danger">
24151 * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it
24152 *   e.g. will not use the right outer scope. Please pass the transclude function as a
24153 *   `parentBoundTranscludeFn` to the link function instead.
24154 * </div>
24155 *
24156 * @param {number} maxPriority only apply directives lower than given priority (Only effects the
24157 *                 root element(s), not their children)
24158 * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template
24159 * (a DOM element/tree) to a scope. Where:
24160 *
24161 *  * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
24162 *  * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
24163 *  `template` and call the `cloneAttachFn` function allowing the caller to attach the
24164 *  cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
24165 *  called as: <br/> `cloneAttachFn(clonedElement, scope)` where:
24166 *
24167 *      * `clonedElement` - is a clone of the original `element` passed into the compiler.
24168 *      * `scope` - is the current scope with which the linking function is working with.
24169 *
24170 *  * `options` - An optional object hash with linking options. If `options` is provided, then the following
24171 *  keys may be used to control linking behavior:
24172 *
24173 *      * `parentBoundTranscludeFn` - the transclude function made available to
24174 *        directives; if given, it will be passed through to the link functions of
24175 *        directives found in `element` during compilation.
24176 *      * `transcludeControllers` - an object hash with keys that map controller names
24177 *        to controller instances; if given, it will make the controllers
24178 *        available to directives.
24179 *      * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
24180 *        the cloned elements; only needed for transcludes that are allowed to contain non html
24181 *        elements (e.g. SVG elements). See also the directive.controller property.
24182 *
24183 * Calling the linking function returns the element of the template. It is either the original
24184 * element passed in, or the clone of the element if the `cloneAttachFn` is provided.
24185 *
24186 * After linking the view is not updated until after a call to $digest which typically is done by
24187 * Angular automatically.
24188 *
24189 * If you need access to the bound view, there are two ways to do it:
24190 *
24191 * - If you are not asking the linking function to clone the template, create the DOM element(s)
24192 *   before you send them to the compiler and keep this reference around.
24193 *   ```js
24194 *     var element = $compile('<p>{{total}}</p>')(scope);
24195 *   ```
24196 *
24197 * - if on the other hand, you need the element to be cloned, the view reference from the original
24198 *   example would not point to the clone, but rather to the original template that was cloned. In
24199 *   this case, you can access the clone via the cloneAttachFn:
24200 *   ```js
24201 *     var templateElement = angular.element('<p>{{total}}</p>'),
24202 *         scope = ....;
24203 *
24204 *     var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
24205 *       //attach the clone to DOM document at the right place
24206 *     });
24207 *
24208 *     //now we have reference to the cloned DOM via `clonedElement`
24209 *   ```
24210 *
24211 *
24212 * For information on how the compiler works, see the
24213 * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
24214 */
24215
24216var $compileMinErr = minErr('$compile');
24217
24218/**
24219 * @ngdoc provider
24220 * @name $compileProvider
24221 *
24222 * @description
24223 */
24224$CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
24225function $CompileProvider($provide, $$sanitizeUriProvider) {
24226  var hasDirectives = {},
24227      Suffix = 'Directive',
24228      COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\w\-]+)\s+(.*)$/,
24229      CLASS_DIRECTIVE_REGEXP = /(([\w\-]+)(?:\:([^;]+))?;?)/,
24230      ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
24231      REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
24232
24233  // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
24234  // The assumption is that future DOM event attribute names will begin with
24235  // 'on' and be composed of only English letters.
24236  var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
24237
24238  function parseIsolateBindings(scope, directiveName, isController) {
24239    var LOCAL_REGEXP = /^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/;
24240
24241    var bindings = {};
24242
24243    forEach(scope, function(definition, scopeName) {
24244      var match = definition.match(LOCAL_REGEXP);
24245
24246      if (!match) {
24247        throw $compileMinErr('iscp',
24248            "Invalid {3} for directive '{0}'." +
24249            " Definition: {... {1}: '{2}' ...}",
24250            directiveName, scopeName, definition,
24251            (isController ? "controller bindings definition" :
24252            "isolate scope definition"));
24253      }
24254
24255      bindings[scopeName] = {
24256        mode: match[1][0],
24257        collection: match[2] === '*',
24258        optional: match[3] === '?',
24259        attrName: match[4] || scopeName
24260      };
24261    });
24262
24263    return bindings;
24264  }
24265
24266  function parseDirectiveBindings(directive, directiveName) {
24267    var bindings = {
24268      isolateScope: null,
24269      bindToController: null
24270    };
24271    if (isObject(directive.scope)) {
24272      if (directive.bindToController === true) {
24273        bindings.bindToController = parseIsolateBindings(directive.scope,
24274                                                         directiveName, true);
24275        bindings.isolateScope = {};
24276      } else {
24277        bindings.isolateScope = parseIsolateBindings(directive.scope,
24278                                                     directiveName, false);
24279      }
24280    }
24281    if (isObject(directive.bindToController)) {
24282      bindings.bindToController =
24283          parseIsolateBindings(directive.bindToController, directiveName, true);
24284    }
24285    if (isObject(bindings.bindToController)) {
24286      var controller = directive.controller;
24287      var controllerAs = directive.controllerAs;
24288      if (!controller) {
24289        // There is no controller, there may or may not be a controllerAs property
24290        throw $compileMinErr('noctrl',
24291              "Cannot bind to controller without directive '{0}'s controller.",
24292              directiveName);
24293      } else if (!identifierForController(controller, controllerAs)) {
24294        // There is a controller, but no identifier or controllerAs property
24295        throw $compileMinErr('noident',
24296              "Cannot bind to controller without identifier for directive '{0}'.",
24297              directiveName);
24298      }
24299    }
24300    return bindings;
24301  }
24302
24303  function assertValidDirectiveName(name) {
24304    var letter = name.charAt(0);
24305    if (!letter || letter !== lowercase(letter)) {
24306      throw $compileMinErr('baddir', "Directive name '{0}
vendor: 4,630 bytes, lines 24306-24418
24306' is invalid. The first character must be a lowercase letter", name);
24307    }
24308    if (name !== name.trim()) {
24309      throw $compileMinErr('baddir',
24310            "Directive name '{0}' is invalid. The name should not contain leading or trailing whitespaces",
24311            name);
24312    }
24313  }
24314
24315  /**
24316   * @ngdoc method
24317   * @name $compileProvider#directive
24318   * @kind function
24319   *
24320   * @description
24321   * Register a new directive with the compiler.
24322   *
24323   * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
24324   *    will match as <code>ng-bind</code>), or an object map of directives where the keys are the
24325   *    names and the values are the factories.
24326   * @param {Function|Array} directiveFactory An injectable directive factory function. See
24327   *    {@link guide/directive} for more info.
24328   * @returns {ng.$compileProvider} Self for chaining.
24329   */
24330   this.directive = function registerDirective(name, directiveFactory) {
24331    assertNotHasOwnProperty(name, 'directive');
24332    if (isString(name)) {
24333      assertValidDirectiveName(name);
24334      assertArg(directiveFactory, 'directiveFactory');
24335      if (!hasDirectives.hasOwnProperty(name)) {
24336        hasDirectives[name] = [];
24337        $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
24338          function($injector, $exceptionHandler) {
24339            var directives = [];
24340            forEach(hasDirectives[name], function(directiveFactory, index) {
24341              try {
24342                var directive = $injector.invoke(directiveFactory);
24343                if (isFunction(directive)) {
24344                  directive = { compile: valueFn(directive) };
24345                } else if (!directive.compile && directive.link) {
24346                  directive.compile = valueFn(directive.link);
24347                }
24348                directive.priority = directive.priority || 0;
24349                directive.index = index;
24350                directive.name = directive.name || name;
24351                directive.require = directive.require || (directive.controller && directive.name);
24352                directive.restrict = directive.restrict || 'EA';
24353                var bindings = directive.$$bindings =
24354                    parseDirectiveBindings(directive, directive.name);
24355                if (isObject(bindings.isolateScope)) {
24356                  directive.$$isolateBindings = bindings.isolateScope;
24357                }
24358                directive.$$moduleName = directiveFactory.$$moduleName;
24359                directives.push(directive);
24360              } catch (e) {
24361                $exceptionHandler(e);
24362              }
24363            });
24364            return directives;
24365          }]);
24366      }
24367      hasDirectives[name].push(directiveFactory);
24368    } else {
24369      forEach(name, reverseParams(registerDirective));
24370    }
24371    return this;
24372  };
24373
24374
24375  /**
24376   * @ngdoc method
24377   * @name $compileProvider#aHrefSanitizationWhitelist
24378   * @kind function
24379   *
24380   * @description
24381   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
24382   * urls during a[href] sanitization.
24383   *
24384   * The sanitization is a security measure aimed at preventing XSS attacks via html links.
24385   *
24386   * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
24387   * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
24388   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
24389   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
24390   *
24391   * @param {RegExp=} regexp New regexp to whitelist urls with.
24392   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
24393   *    chaining otherwise.
24394   */
24395  this.aHrefSanitizationWhitelist = function(regexp) {
24396    if (isDefined(regexp)) {
24397      $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
24398      return this;
24399    } else {
24400      return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
24401    }
24402  };
24403
24404
24405  /**
24406   * @ngdoc method
24407   * @name $compileProvider#imgSrcSanitizationWhitelist
24408   * @kind function
24409   *
24410   * @description
24411   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
24412   * urls during img[src] sanitization.
24413   *
24414   * The sanitization is a security measure aimed at prevent XSS attacks via html links.
24415   *
24416   * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
24417   * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
24418   * regular expression. If a match is found, the original url is 
24418written into the dom. Otherwise,
24419   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
24420   *
24421   * @param {RegExp=} regexp New regexp to whitelist urls with.
24422   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
24423   *    chaining otherwise.
24424   */
24425  this.imgSrcSanitizationWhitelist = function(regexp) {
24426    if (isDefined(regexp)) {
24427      $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
24428      return this;
24429    } else {
24430      return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
24431    }
24432  };
24433
24434  /**
24435   * @ngdoc method
24436   * @name  $compileProvider#debugInfoEnabled
24437   *
24438   * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the
24439   * current debugInfoEnabled state
24440   * @returns {*} current value if used as getter or itself (chaining) if used as setter
24441   *
24442   * @kind function
24443   *
24444   * @description
24445   * Call this method to enable/disable various debug runtime information in the compiler such as adding
24446   * binding information and a reference to the current scope on to DOM elements.
24447   * If enabled, the compiler will add the following to DOM elements that have been bound to the scope
24448   * * `ng-binding` CSS class
24449   * * `$binding` data property containing an array of the binding expressions
24450   *
24451   * You may want to disable this in production for a significant performance boost. See
24452   * {@link guide/production#disabling-debug-data Disabling Debug Data} for more.
24453   *
24454   * The default value is true.
24455   */
24456  var debugInfoEnabled = true;
24457  this.debugInfoEnabled = function(enabled) {
24458    if (isDefined(enabled)) {
24459      debugInfoEnabled = enabled;
24460      return this;
24461    }
24462    return debugInfoEnabled;
24463  };
24464
24465  this.$get = [
24466            '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
24467            '$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri',
24468    function($injector,   $interpolate,   $exceptionHandler,   $templateRequest,   $parse,
24469             $controller,   $rootScope,   $document,   $sce,   $animate,   $$sanitizeUri) {
24470
24471    var Attributes = function(element, attributesToCopy) {
24472      if (attributesToCopy) {
24473        var keys = Object.keys(attributesToCopy);
24474        var i, l, key;
24475
24476        for (i = 0, l = keys.length; i < l; i++) {
24477          key = keys[i];
24478          this[key] = attributesToCopy[key];
24479        }
24480      } else {
24481        this.$attr = {};
24482      }
24483
24484      this.$$element = element;
24485    };
24486
24487    Attributes.prototype = {
24488      /**
24489       * @ngdoc method
24490       * @name $compile.directive.Attributes#$normalize
24491       * @kind function
24492       *
24493       * @description
24494       * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or
24495       * `data-`) to its normalized, camelCase form.
24496       *
24497       * Also there is special case for Moz prefix starting with upper case letter.
24498       *
24499       * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
24500       *
24501       * @param {string} name Name to normalize
24502       */
24503      $normalize: directiveNormalize,
24504
24505
24506      /**
24507       * @ngdoc method
24508       * @name $compile.directive.Attributes#$addClass
24509       * @kind function
24510       *
24511       * @description
24512       * Adds the CSS class value specified by the classVal parameter to the element. If animations
24513       * are enabled then an animation will be triggered for the class addition.
24514       *
24515       * @param {string} classVal The className value that will be added to the element
24516       */
24517      $addClass: function(classVal) {
24518        if (classVal && classVal.length > 0) {
24519          $animate.addClass(this.$$element, classVal);
24520        }
24521      },
24522
24523      /**
24524       * @ngdoc method
24525       * @name $compile.directive.Attributes#$removeClass
24526       * @kind function
24527       *
24528       * @description
24529       * Removes the CSS class value specified by the classVal parameter from the element. If
24530       * animations are enabled then an animation will be triggered for the class removal.
24531       *
24532       * @param {string} classVal The className value that will be removed from the element
24533       */
24534      $removeClass: function(classVal) {
24535        if (classVal && classVal.length > 0) {
24536          $animate.removeClass(this.$$element, classVal);
24537        }
24538      },
24539
24540      /**
24541       * @ngdoc method
24542       * @name $compile.directive.Attributes#$updateClass
24543       * @kind function
24544       *
24545       * @description
24546       * Adds and removes the appropriate CSS class values to the element based on the difference
24547       * between the new and old CSS class values (specified as newClasses and oldClasses).
24548       *
24549       * @param {string} newClasses The current CSS className value
24550       * @param {string} oldClasses The former CSS className value
24551       */
24552      $updateClass: function(newClasses, oldClasses) {
24553        var toAdd = tokenDifference(newClasses, oldClasses);
24554        if (toAdd && toAdd.length) {
24555          $animate.addClass(this.$$element, toAdd);
24556        }
24557
24558        var toRemove = tokenDifference(oldClasses, newClasses);
24559        if (toRemove && toRemove.length) {
24560          $animate.removeClass(this.$$element, toRemove);
24561        }
24562      },
24563
24564      /**
24565       * Set a normalized attribute on the element in a way such that all directives
24566       * can share the attribute. This function properly handles boolean attributes.
24567       * @param {string} key Normalized key. (ie ngAttribute)
24568       * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
24569       * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
24570       *     Defaults to true.
24571       * @param {string=} attrName Optional none normalized name. Defaults to key.
24572       */
24573      $set: function(key, value, writeAttr, attrName) {
24574        // TODO: decide whether or not to throw an error if "class"
24575        //is set through this function since it may cause $updateClass to
24576        //become unstable.
24577
24578        var node = this.$$element[0],
24579            booleanKey = getBooleanAttrName(node, key),
24580            aliasedKey = getAliasedAttrName(key),
24581            observer = key,
24582            nodeName;
24583
24584        if (booleanKey) {
24585          this.$$element.prop(key, value);
24586          attrName = booleanKey;
24587        } else if (aliasedKey) {
24588          this[aliasedKey] = value;
24589          observer = aliasedKey;
24590        }
24591
24592        this[key] = value;
24593
24594        // translate normalized key to actual key
24595        if (attrName) {
24596          this.$attr[key] = attrName;
24597        } else {
24598          attrName = this.$attr[key];
24599          if (!attrName) {
24600            this.$attr[key] = attrName = snake_case(key, '-');
24601          }
24602        }
24603
24604        nodeName = nodeName_(this.$$element);
24605
24606        if ((nodeName === 'a' && key === 'href') ||
24607            (nodeName === 'img' && key === 'src')) {
24608          // sanitize a[href] and img[src] values
24609          this[key] = value = $$sanitizeUri(value, key === 'src');
24610        } else if (nodeName === 'img' && key === 'srcset') {
24611          // sanitize img[srcset] values
24612          var result = "";
24613
24614          // first check if there are spaces because it's not the same pattern
24615          var trimmedSrcset = trim(value);
24616          //                (   999x   ,|   999w   ,|   ,|,   )
24617          var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;
24618          var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/;
24619
24620          // split srcset into tuple of uri and descriptor except for the last item
24621          var rawUris = trimmedSrcset.split(pattern);
24622
24623          // for each tuples
24624          var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
24625          for (var i = 0; i < nbrUrisWith2parts; i++) {
24626            var innerIdx = i * 2;
24627            // sanitize the uri
24628            result += $$sanitizeUri(trim(rawUris[innerIdx]), true);
24629            // add the descriptor
24630            result += (" " + trim(rawUris[innerIdx + 1]));
24631          }
24632
24633          // split the last item into uri and descriptor
24634          var lastTuple = trim(rawUris[i * 2]).split(/\s/);
24635
24636          // sanitize the last uri
24637          result += $$sanitizeUri(trim(lastTuple[0]), true);
24638
24639          // and add the last descriptor if any
24640          if (lastTuple.length === 2) {
24641            result += (" " + trim(lastTuple[1]));
24642          }
24643          this[key] = value = result;
24644        }
24645
24646        if (writeAttr !== false) {
24647          if (value === null || isUndefined(value)) {
24648            this.$$element.removeAttr(attrName);
24649          } else {
24650            this.$$element.attr(attrName, value);
24651          }
24652        }
24653
24654        // fire observers
24655        var $$observers = this.$$observers;
24656        $$observers && forEach($$observers[observer], function(fn) {
24657          try {
24658            fn(value);
24659          } catch (e) {
24660            $exceptionHandler(e);
24661          }
24662        });
24663      },
24664
24665
24666      /**
24667       * @ngdoc method
24668       * @name $compile.directive.Attributes#$observe
24669       * @kind function
24670       *
24671       * @description
24672       * Observes an interpolated attribute.
24673       *
24674       * The observer function will be invoked once during the next `$digest` following
24675       * compilation. The observer is then invoked whenever the interpolated value
24676       * changes.
24677       *
24678       * @param {string} key Normalized key. (ie ngAttribute) .
24679       * @param {function(interpolatedValue)} fn Function that will be called whenever
24680                the interpolated value of the attribute changes.
24681       *        See the {@link guide/directive#text-and-attribute-bindings Directives} guide for more info.
24682       * @returns {function()} Returns a deregistration function for this observer.
24683       */
24684      $observe: function(key, fn) {
24685        var attrs = this,
24686            $$observers = (attrs.$$observers || (attrs.$$observers = createMap())),
24687            listeners = ($$observers[key] || ($$observers[key] = []));
24688
24689        listeners.push(fn);
24690        $rootScope.$evalAsync(function() {
24691          if (!listeners.$$inter && attrs.hasOwnProperty(key) && !isUndefined(attrs[key])) {
24692            // no one registered attribute interpolation function, so lets call it manually
24693            fn(attrs[key]);
24694          }
24695        });
24696
24697        return function() {
24698          arrayRemove(listeners, fn);
24699        };
24700      }
24701    };
24702
24703
24704    function safeAddClass($element, className) {
24705      try {
24706        $element.addClass(className);
24707      } catch (e) {
24708        // ignore, since it means that we are trying to set class on
24709        // SVG element, where class name is read-only.
24710      }
24711    }
24712
24713
24714    var startSymbol = $interpolate.startSymbol(),
24715        endSymbol = $interpolate.endSymbol(),
24716        denormalizeTemplate = (startSymbol == '{{' || endSymbol  == '}}')
24717            ? identity
24718            : function denormalizeTemplate(template) {
24719              return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
24720        },
24721        NG_ATTR_BINDING = /^ngAttr[A-Z]/;
24722
24723    compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {
24724      var bindings = $element.data('$binding') || [];
24725
24726      if (isArray(binding)) {
24727        bindings = bindings.concat(binding);
24728      } else {
24729        bindings.push(binding);
24730      }
24731
24732      $element.data('$binding', bindings);
24733    } : noop;
24734
24735    compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) {
24736      safeAddClass($element, 'ng-binding');
24737    } : noop;
24738
24739    compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) {
24740      var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope';
24741      $element.data(dataName, scope);
24742    } : noop;
24743
24744    compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) {
24745      safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
24746    } : noop;
24747
24748    return compile;
24749
24750    //================================
24751
24752    function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,
24753                        previousCompileContext) {
24754      if (!($compileNodes instanceof jqLite)) {
24755        // jquery always rewraps, whereas we need to preserve the original selector so that we can
24756        // modify it.
24757        $compileNodes = jqLite($compileNodes);
24758      }
24759      // We can not compile top level text elements since text nodes can be merged and we will
24760      // not be able to attach scope data to them, so we will wrap them in <span>
24761      forEach($compileNodes, function(node, index) {
24762        if (node.nodeType == NODE_TYPE_TEXT && node.nodeValue.match(/\S+/) /* non-empty */ ) {
24763          $compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0];
24764        }
24765      });
24766      var compositeLinkFn =
24767              compileNodes($compileNodes, transcludeFn, $compileNodes,
24768                           maxPriority, ignoreDirective, previousCompileContext);
24769      compile.$$addScopeClass($compileNodes);
24770      var namespace = null;
24771      return function publicLinkFn(scope, cloneConnectFn, options) {
24772        assertArg(scope, 'scope');
24773
24774        options = options || {};
24775        var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
24776          transcludeControllers = options.transcludeControllers,
24777          futureParentElement = options.futureParentElement;
24778
24779        // When `parentBoundTranscludeFn` is passed, it is a
24780        // `controllersBoundTransclude` function (it was previously passed
24781        // as `transclude` to directive.link) so we must unwrap it to get
24782        // its `boundTranscludeFn`
24783        if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) {
24784          parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude;
24785        }
24786
24787        if (!namespace) {
24788          namespace = detectNamespaceForChildElements(futureParentElement);
24789        }
24790        var $linkNode;
24791        if (namespace !== 'html') {
24792          // When using a directive with replace:true and templateUrl the $compileNodes
24793          // (or a child element inside of them)
24794          // might change, so we need to recreate the namespace adapted compileNodes
24795          // for call to the link function.
24796          // Note: This will already clone the nodes...
24797          $linkNode = jqLite(
24798            wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html())
24799          );
24800        } else if (cloneConnectFn) {
24801          // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
vendor: 7,988 bytes, lines 24802-24996
24802          // and sometimes changes the structure of the DOM.
24803          $linkNode = JQLitePrototype.clone.call($compileNodes);
24804        } else {
24805          $linkNode = $compileNodes;
24806        }
24807
24808        if (transcludeControllers) {
24809          for (var controllerName in transcludeControllers) {
24810            $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance);
24811          }
24812        }
24813
24814        compile.$$addScopeInfo($linkNode, scope);
24815
24816        if (cloneConnectFn) cloneConnectFn($linkNode, scope);
24817        if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
24818        return $linkNode;
24819      };
24820    }
24821
24822    function detectNamespaceForChildElements(parentElement) {
24823      // TODO: Make this detect MathML as well...
24824      var node = parentElement && parentElement[0];
24825      if (!node) {
24826        return 'html';
24827      } else {
24828        return nodeName_(node) !== 'foreignobject' && node.toString().match(/SVG/) ? 'svg' : 'html';
24829      }
24830    }
24831
24832    /**
24833     * Compile function matches each node in nodeList against the directives. Once all directives
24834     * for a particular node are collected their compile functions are executed. The compile
24835     * functions return values - the linking functions - are combined into a composite linking
24836     * function, which is the a linking function for the node.
24837     *
24838     * @param {NodeList} nodeList an array of nodes or NodeList to compile
24839     * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
24840     *        scope argument is auto-generated to the new child of the transcluded parent scope.
24841     * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
24842     *        the rootElement must be set the jqLite collection of the compile root. This is
24843     *        needed so that the jqLite collection items can be replaced with widgets.
24844     * @param {number=} maxPriority Max directive priority.
24845     * @returns {Function} A composite linking function of all of the matched directives or null.
24846     */
24847    function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
24848                            previousCompileContext) {
24849      var linkFns = [],
24850          attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound;
24851
24852      for (var i = 0; i < nodeList.length; i++) {
24853        attrs = new Attributes();
24854
24855        // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
24856        directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,
24857                                        ignoreDirective);
24858
24859        nodeLinkFn = (directives.length)
24860            ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,
24861                                      null, [], [], previousCompileContext)
24862            : null;
24863
24864        if (nodeLinkFn && nodeLinkFn.scope) {
24865          compile.$$addScopeClass(attrs.$$element);
24866        }
24867
24868        childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
24869                      !(childNodes = nodeList[i].childNodes) ||
24870                      !childNodes.length)
24871            ? null
24872            : compileNodes(childNodes,
24873                 nodeLinkFn ? (
24874                  (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement)
24875                     && nodeLinkFn.transclude) : transcludeFn);
24876
24877        if (nodeLinkFn || childLinkFn) {
24878          linkFns.push(i, nodeLinkFn, childLinkFn);
24879          linkFnFound = true;
24880          nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn;
24881        }
24882
24883        //use the previous context only for the first element in the virtual group
24884        previousCompileContext = null;
24885      }
24886
24887      // return a linking function if we have found anything, null otherwise
24888      return linkFnFound ? compositeLinkFn : null;
24889
24890      function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
24891        var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn;
24892        var stableNodeList;
24893
24894
24895        if (nodeLinkFnFound) {
24896          // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our
24897          // offsets don't get screwed up
24898          var nodeListLength = nodeList.length;
24899          stableNodeList = new Array(nodeListLength);
24900
24901          // create a sparse array by only copying the elements which have a linkFn
24902          for (i = 0; i < linkFns.length; i+=3) {
24903            idx = linkFns[i];
24904            stableNodeList[idx] = nodeList[idx];
24905          }
24906        } else {
24907          stableNodeList = nodeList;
24908        }
24909
24910        for (i = 0, ii = linkFns.length; i < ii;) {
24911          node = stableNodeList[linkFns[i++]];
24912          nodeLinkFn = linkFns[i++];
24913          childLinkFn = linkFns[i++];
24914
24915          if (nodeLinkFn) {
24916            if (nodeLinkFn.scope) {
24917              childScope = scope.$new();
24918              compile.$$addScopeInfo(jqLite(node), childScope);
24919              var destroyBindings = nodeLinkFn.$$destroyBindings;
24920              if (destroyBindings) {
24921                nodeLinkFn.$$destroyBindings = null;
24922                childScope.$on('$destroyed', destroyBindings);
24923              }
24924            } else {
24925              childScope = scope;
24926            }
24927
24928            if (nodeLinkFn.transcludeOnThisElement) {
24929              childBoundTranscludeFn = createBoundTranscludeFn(
24930                  scope, nodeLinkFn.transclude, parentBoundTranscludeFn);
24931
24932            } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
24933              childBoundTranscludeFn = parentBoundTranscludeFn;
24934
24935            } else if (!parentBoundTranscludeFn && transcludeFn) {
24936              childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn);
24937
24938            } else {
24939              childBoundTranscludeFn = null;
24940            }
24941
24942            nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn,
24943                       nodeLinkFn);
24944
24945          } else if (childLinkFn) {
24946            childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
24947          }
24948        }
24949      }
24950    }
24951
24952    function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {
24953
24954      var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
24955
24956        if (!transcludedScope) {
24957          transcludedScope = scope.$new(false, containingScope);
24958          transcludedScope.$$transcluded = true;
24959        }
24960
24961        return transcludeFn(transcludedScope, cloneFn, {
24962          parentBoundTranscludeFn: previousBoundTranscludeFn,
24963          transcludeControllers: controllers,
24964          futureParentElement: futureParentElement
24965        });
24966      };
24967
24968      return boundTranscludeFn;
24969    }
24970
24971    /**
24972     * Looks for directives on the given node and adds them to the directive collection which is
24973     * sorted.
24974     *
24975     * @param node Node to search.
24976     * @param directives An array to which the directives are added to. This array is sorted before
24977     *        the function returns.
24978     * @param attrs The shared attrs object which is used to populate the normalized attributes.
24979     * @param {number=} maxPriority Max directive priority.
24980     */
24981    function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
24982      var nodeType = node.nodeType,
24983          attrsMap = attrs.$attr,
24984          match,
24985          className;
24986
24987      switch (nodeType) {
24988        case NODE_TYPE_ELEMENT: /* Element */
24989          // use the node name: <directive>
24990          addDirective(directives,
24991              directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective);
24992
24993          // iterate over the attributes
24994          for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
24995                   j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
24996            var attrStartName = false;
24997            var attrEndName = false;
24998
24999            attr = nAttrs[j];
25000            name = attr.name;
25001            value = trim(attr.value);
25002
25003            // support ngAttr attribute binding
25004            ngAttrName = directiveNormalize(name);
25005            if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
25006              name = name.replace(PREFIX_REGEXP, '')
25007                .substr(8).replace(/_(.)/g, function(match, letter) {
25008                  return letter.toUpperCase();
25009                });
25010            }
25011
25012            var directiveNName = ngAttrName.replace(/(Start|End)$/, '');
25013            if (directiveIsMultiElement(directiveNName)) {
25014              if (ngAttrName === directiveNName + 'Start') {
25015                attrStartName = name;
25016                attrEndName = name.substr(0, name.length - 5) + 'end';
25017                name = name.substr(0, name.length - 6);
25018              }
25019            }
25020
25021            nName = directiveNormalize(name.toLowerCase());
25022            attrsMap[nName] = name;
25023            if (isNgAttr || !attrs.hasOwnProperty(nName)) {
25024                attrs[nName] = value;
25025                if (getBooleanAttrName(node, nName)) {
25026                  attrs[nName] = true; // presence means true
25027                }
25028            }
25029            addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
25030            addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
25031                          attrEndName);
25032          }
25033
25034          // use class as directive
25035          className = node.className;
25036          if (isObject(className)) {
25037              // Maybe SVGAnimatedString
25038              className = className.animVal;
25039          }
25040          if (isString(className) && className !== '') {
25041            while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
25042              nName = directiveNormalize(match[2]);
25043              if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {
25044                attrs[nName] = trim(match[3]);
25045              }
25046              className = className.substr(match.index + match[0].length);
25047            }
25048          }
25049          break;
25050        case NODE_TYPE_TEXT: /* Text Node */
25051          if (msie === 11) {
25052            // Workaround for #11781
25053            while (node.parentNode && node.nextSibling && node.nextSibling.nodeType === NODE_TYPE_TEXT) {
25054              node.nodeValue = node.nodeValue + node.nextSibling.nodeValue;
25055              node.parentNode.removeChild(node.nextSibling);
25056            }
25057          }
25058          addTextInterpolateDirective(directives, node.nodeValue);
25059          break;
25060        case NODE_TYPE_COMMENT: /* Comment */
25061          try {
25062            match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
25063            if (match) {
25064              nName = directiveNormalize(match[1]);
25065              if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
25066                attrs[nName] = trim(match[2]);
25067              }
25068            }
25069          } catch (e) {
25070            // turns out that under some circumstances IE9 throws errors when one attempts to read
25071            // comment's node value.
25072            // Just ignore it and continue. (Can't seem to reproduce in test case.)
25073          }
25074          break;
25075      }
25076
25077      directives.sort(byPriority);
25078      return directives;
25079    }
25080
25081    /**
25082     * Given a node with an directive-start it collects all of the siblings until it finds
25083     * directive-end.
25084     * @param node
25085     * @param attrStart
25086     * @param attrEnd
25087     * @returns {*}
25088     */
25089    function groupScan(node, attrStart, attrEnd) {
25090      var nodes = [];
25091      var depth = 0;
25092      if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
25093        do {
25094          if (!node) {
25095            throw $compileMinErr('uterdir',
25096                      "Unterminated attribute, found '{0}' but no matching '{1}' found.",
25097                      attrStart, attrEnd);
25098          }
25099          if (node.nodeType == NODE_TYPE_ELEMENT) {
25100            if (node.hasAttribute(attrStart)) depth++;
25101            if (node.hasAttribute(attrEnd)) depth--;
25102          }
25103          nodes.push(node);
25104          node = node.nextSibling;
25105        } while (depth > 0);
25106      } else {
25107        nodes.push(node);
25108      }
25109
25110      return jqLite(nodes);
25111    }
25112
25113    /**
25114     * Wrapper for linking function which converts normal linking function into a grouped
25115     * linking function.
25116     * @param linkFn
25117     * @param attrStart
25118     * @param attrEnd
25119     * @returns {Function}
25120     */
25121    function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
25122      return function(scope, element, attrs, controllers, transcludeFn) {
25123        element = groupScan(element[0], attrStart, attrEnd);
25124        return linkFn(scope, element, attrs, controllers, transcludeFn);
25125      };
25126    }
25127
25128    /**
25129     * Once the directives have been collected, their compile functions are executed. This method
25130     * is responsible for inlining directive templates as well as terminating the application
25131     * of the directives if the terminal directive has been reached.
25132     *
25133     * @param {Array} directives Array of collected directives to execute their compile function.
25134     *        this needs to be pre-sorted by priority order.
25135     * @param {Node} compileNode The raw DOM node to apply the compile functions to
25136     * @param {Object} templateAttrs The shared attribute function
25137     * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
25138     *                                                  scope argument is auto-generated to the new
25139     *                                                  child of the transcluded parent scope.
25140     * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
25141     *                              argument has the root jqLite array so that we can replace nodes
25142     *                              on it.
25143     * @param {Object=} originalReplaceDirective An optional directive that will be ignored when
25144     *                                           compiling the transclusion.
25145     * @param {Array.<Function>} preLinkFns
25146     * @param {Array.<Function>} postLinkFns
25147     * @param {Object} previousCompileContext Context used for previous compilation of the current
25148     *                                        node
25149     * @returns {Function} linkFn
25150     */
25151    function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
25152                                   jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,
25153                                   previousCompileContext) {
25154      previousCompileContext = previousCompileContext || {};
25155
25156      var terminalPriority = -Number.MAX_VALUE,
25157          newScopeDirective = previousCompileContext.newScopeDirective,
25158          controllerDirectives = previousCompileContext.controllerDirectives,
25159          newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
25160          templateDirective = previousCompileContext.templateDirective,
25161          nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
25162          hasTranscludeDirective = false,
25163          hasTemplate = false,
25164          hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
25165          $compileNode = templateAttrs.$$element = jqLite(compileNode),
25166          directive,
25167          directiveName,
25168          $template,
25169          replaceDirective = originalReplaceDirective,
25170          childTranscludeFn = transcludeFn,
25171          linkFn,
25172          directiveValue;
25173
25174      // executes all directives on the current element
25175      for (var i = 0, ii = directives.length; i < ii; i++) {
25176        directive = directives[i];
25177        var attrStart = directive.$$start;
25178        var attrEnd = directive.$$end;
25179
25180        // collect multiblock sections
25181        if (attrStart) {
25182          $compileNode = groupScan(compileNode, attrStart, attrEnd);
25183        }
25184        $template = undefined;
25185
25186        if (terminalPriority > directive.priority) {
25187          break; // prevent further processing of directives
25188        }
25189
25190        if (directiveValue = directive.scope) {
25191
25192          // skip the check for directives with async templates, we'll check the derived sync
25193          // directive when the template arrives
25194          if (!directive.templateUrl) {
25195            if (isObject(directiveValue)) {
25196              // This directive is trying to add an isolated scope.
25197              // Check that there is no scope of any kind already
25198              assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective,
25199                                directive, $compileNode);
25200              newIsolateScopeDirective = directive;
25201            } else {
25202              // This directive is trying to add a child scope.
25203              // Check that there is no isolated scope already
25204              assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,
25205                                $compileNode);
25206            }
25207          }
25208
25209          newScopeDirective = newScopeDirective || directive;
25210        }
25211
25212        directiveName = directive.name;
25213
25214        if (!directive.templateUrl && directive.controller) {
25215          directiveValue = directive.controller;
25216          controllerDirectives = controllerDirectives || createMap();
25217          assertNoDuplicate("'" + directiveName + "' controller",
25218              controllerDirectives[directiveName], directive, $compileNode);
25219          controllerDirectives[directiveName] = directive;
25220        }
25221
25222        if (directiveValue = directive.transclude) {
25223          hasTranscludeDirective = true;
25224
25225          // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
25226          // This option should only be used by directives that know how to safely handle element transclusion,
25227          // where the transcluded nodes are added or replaced after linking.
25228          if (!directive.$$tlb) {
25229            assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
25230            nonTlbTranscludeDirective = directive;
25231          }
25232
25233          if (directiveValue == 'element') {
25234            hasElementTranscludeDirective = true;
25235            terminalPriority = directive.priority;
25236            $template = $compileNode;
25237            $compileNode = templateAttrs.$$element =
25238                jqLite(document.createComment(' ' + directiveName + ': ' +
25239                                              templateAttrs[directiveName] + ' '));
25240            compileNode = $compileNode[0];
25241            replaceWith(jqCollection, sliceArgs($template), compileNode);
25242
25243            childTranscludeFn = compile($template, transcludeFn, terminalPriority,
25244                                        replaceDirective && replaceDirective.name, {
25245                                          // Don't pass in:
25246                                          // - controllerDirectives - otherwise we'll create duplicates controllers
25247                                          // - newIsolateScopeDirective or templateDirective - comb
25247ining templates with
25248                                          //   element transclusion doesn't make sense.
25249                                          //
25250                                          // We need only nonTlbTranscludeDirective so that we prevent putting transclusion
25251                                          // on the same element more than once.
25252                                          nonTlbTranscludeDirective: nonTlbTranscludeDirective
25253                                        });
25254          } else {
25255            $template = jqLite(jqLiteClone(compileNode)).contents();
25256            $compileNode.empty(); // clear contents
25257            childTranscludeFn = compile($template, transcludeFn);
25258          }
25259        }
25260
25261        if (directive.template) {
25262          hasTemplate = true;
25263          assertNoDuplicate('template', templateDirective, directive, $compileNode);
25264          templateDirective = directive;
25265
25266          directiveValue = (isFunction(directive.template))
25267              ? directive.template($compileNode, templateAttrs)
25268              : directive.template;
25269
25270          directiveValue = denormalizeTemplate(directiveValue);
25271
25272          if (directive.replace) {
25273            replaceDirective = directive;
25274            if (jqLiteIsTextNode(directiveValue)) {
25275              $template = [];
25276            } else {
25277              $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
25278            }
25279            compileNode = $template[0];
25280
25281            if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
25282              throw $compileMinErr('tplrt',
25283                  "Template for directive '{0}' must have exactly one root element. {1}",
25284                  directiveName, '');
25285            }
25286
25287            replaceWith(jqCollection, $compileNode, compileNode);
25288
25289            var newTemplateAttrs = {$attr: {}};
25290
25291            // combine directives from the original node and from the template:
25292            // - take the array of directives for this element
25293            // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)
25294            // - collect directives from the template and sort them by priority
25295            // - combine directives as: processed + template + unprocessed
25296            var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
25297            var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
25298
25299            if (newIsolateScopeDirective) {
25300              markDirectivesAsIsolate(templateDirectives);
25301            }
25302            directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
25303            mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
25304
25305            ii = directives.length;
25306          } else {
25307            $compileNode.html(directiveValue);
25308          }
25309        }
25310
25311        if (directive.templateUrl) {
25312          hasTemplate = true;
25313          assertNoDuplicate('template', templateDirective, directive, $compileNode);
25314          templateDirective = directive;
25315
25316          if (directive.replace) {
25317            replaceDirective = directive;
25318          }
25319
25320          nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
25321              templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
25322                controllerDirectives: controllerDirectives,
25323                newScopeDirective: (newScopeDirective !== directive) && newScopeDirective,
25324                newIsolateScopeDirective: newIsolateScopeDirective,
25325                templateDirective: templateDirective,
25326                nonTlbTranscludeDirective: nonTlbTranscludeDirective
25327              });
25328          ii = directives.length;
25329        } else if (directive.compile) {
25330          try {
25331            linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
25332            if (isFunction(linkFn)) {
25333              addLinkFns(null, linkFn, attrStart, attrEnd);
25334            } else if (linkFn) {
25335              addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd);
25336            }
25337          } catch (e) {
25338            $exceptionHandler(e, startingTag($compileNode));
25339          }
25340        }
25341
25342        if (directive.terminal) {
25343          nodeLinkFn.terminal = true;
25344          terminalPriority = Math.max(terminalPriority, directive.priority);
25345        }
25346
25347      }
25348
25349      nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
25350      nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
25351      nodeLinkFn.templateOnThisElement = hasTemplate;
25352      nodeLinkFn.transclude = childTranscludeFn;
25353
25354      previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
25355
25356      // might be normal or delayed nodeLinkFn depending on if templateUrl is present
25357      return nodeLinkFn;
25358
25359      ////////////////////
25360
25361      function addLinkFns(pre, post, attrStart, attrEnd) {
25362        if (pre) {
25363          if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
25364          pre.require = directive.require;
25365          pre.directiveName = directiveName;
25366          if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
25367            pre = cloneAndAnnotateFn(pre, {isolateScope: true});
25368          }
25369          preLinkFns.push(pre);
25370        }
25371        if (post) {
25372          if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
25373          post.require = directive.require;
25374          post.directiveName = directiveName;
25375          if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
25376            post = cloneAndAnnotateFn(post, {isolateScope: true});
25377          }
25378          postLinkFns.push(post);
25379        }
25380      }
25381
25382
25383      function getControllers(directiveName, require, $element, elementControllers) {
25384        var value;
25385
25386        if (isString(require)) {
25387          var match = require.match(REQUIRE_PREFIX_REGEXP);
25388          var name = require.substring(match[0].length);
25389          var inheritType = match[1] || match[3];
25390          var optional = match[2] === '?';
25391
25392          //If only parents then start at the parent element
25393          if (inheritType === '^^') {
25394            $element = $element.parent();
25395          //Otherwise attempt getting the controller from elementControllers in case
25396          //the element is transcluded (and has no data) and to avoid .data if possible
25397          } else {
25398            value = elementControllers && elementControllers[name];
25399            value = value && value.instance;
25400          }
25401
25402          if (!value) {
25403            var dataName = '$' + name + 'Controller';
25404            value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName);
25405          }
25406
25407          if (!value && !optional) {
25408            throw $compileMinErr('ctreq',
25409                "Controller '{0}', required by directive '{1}', can't be found!",
25410                name, directiveName);
25411          }
25412        } else if (isArray(require)) {
25413          value = [];
25414          for (var i = 0, ii = require.length; i < ii; i++) {
25415            value[i] = getControllers(directiveName, require[i], $element, elementControllers);
25416          }
25417        }
25418
25419        return value || null;
25420      }
25421
25422      function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope) {
25423        var elementControllers = createMap();
25424        for (var controllerKey in controllerDirectives) {
25425          var directive = controllerDirectives[controllerKey];
25426          var locals = {
25427            $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
25428            $element: $element,
25429            $attrs: attrs,
25430            $transclude: transcludeFn
25431          };
25432
25433          var controller = directive.controller;
25434          if (controller == '@') {
25435            controller = attrs[directive.name];
25436          }
25437
25438          var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
25439
25440          // For directives with element transclusion the element is a comment,
25441          // but jQuery .data doesn't support attaching data to comment nodes as it's hard to
25442          // clean up (http://bugs.jquery.com/ticket/8335).
25443          // Instead, we save the controllers for the element in a local hash and attach to .data
25444          // later, once we have the actual element.
25445          elementControllers[directive.name] = controllerInstance;
25446          if (!hasElementTranscludeDirective) {
25447            $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
25448          }
25449        }
25450        return elementControllers;
25451      }
25452
25453      function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn,
25454                          thisLinkFn) {
25455        var i, ii, linkFn, controller, isolateScope, elementControllers, transcludeFn, $element,
25456            attrs;
25457
25458        if (compileNode === linkNode) {
25459          attrs = templateAttrs;
25460          $element = templateAttrs.$$element;
25461        } else {
25462          $element = jqLite(linkNode);
25463          attrs = new Attributes($element, templateAttrs);
25464        }
25465
25466        if (newIsolateScopeDirective) {
25467          isolateScope = scope.$new(true);
25468        }
25469
25470        if (boundTranscludeFn) {
25471          // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn`
25472          // is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
25473          transcludeFn = controllersBoundTransclude;
25474          transcludeFn.$$boundTransclude = boundTranscludeFn;
25475        }
25476
25477        if (controllerDirectives) {
25478          elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope);
25479        }
25480
25481        if (newIsolateScopeDirective) {
25482          // Initialize isolate scope bindings for new isolate scope directive.
25483          compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||
25484              templateDirective === newIsolateScopeDirective.$$originalDirective)));
25485          compile.$$addScopeClass($element, true);
25486          isolateScope.$$isolateBindings =
25487              newIsolateScopeDirective.$$isolateBindings;
25488          initializeDirectiveBindings(scope, attrs, isolateScope,
25489                                      isolateScope.$$isolateBindings,
25490                                      newIsolateScopeDirective, isolateScope);
25491        }
25492        if (elementControllers) {
25493          // Initialize bindToController bindings for new/isolate scopes
25494          var scopeDirective = newIsolateScopeDirective || newScopeDirective;
25495          var bindings;
25496          var controllerForBindings;
25497          if (scopeDirective && elementControllers[scopeDirective.name]) {
25498            bindings = scopeDirective.$$bindings.bindToController;
25499            controller = elementControllers[scopeDirective.name];
25500
25501            if (controller && controller.identifier && bindings) {
25502              controllerForBindings = controller;
25503              thisLinkFn.$$destroyBindings =
25504                  initializeDirectiveBindings(scope, attrs, controller.instance,
25505                                              bindings, scopeDirective);
25506            }
25507          }
25508          for (i in elementControllers) {
25509            controller = elementControllers[i];
25510            var controllerResult = controller();
25511
25512            if (controllerResult !== controller.instance) {
25513              // If the controller constructor has a return value, overwrite the instance
25514              // from setupControllers and update the element data
25515              controller.instance = controllerResult;
25516              $element.data('$' + i + 'Controller', controllerResult);
25517              if (controller === controllerForBindings) {
25518                // Remove and re-install bindToController bindings
25519                thisLinkFn.$$destroyBindings();
25520                thisLinkFn.$$destroyBindings =
25521                  initializeDirectiveBindings(scope, attrs, controllerResult, bindings, scopeDirective);
25522              }
25523            }
25524          }
25525        }
25526
25527        // PRELINKING
25528        for (i = 0, ii = preLinkFns.length; i < ii; i++) {
25529          linkFn = preLinkFns[i];
25530          invokeLinkFn(linkFn,
25531              linkFn.isolateScope ? isolateScope : scope,
25532              $element,
25533              attrs,
25534              linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
25535              transcludeFn
25536          );
25537        }
25538
25539        // RECURSION
25540        // We only pass the isolate scope, if the isolate directive has a template,
25541        // otherwise the child elements do not belong to the isolate directive.
25542        var scopeToChild = scope;
25543        if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {
25544          scopeToChild = isolateScope;
25545        }
25546        childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
25547
25548        // POSTLINKING
25549        for (i = postLinkFns.length - 1; i >= 0; i--) {
25550          linkFn = postLinkFns[i];
25551          invokeLinkFn(linkFn,
25552              linkFn.isolateScope ? isolateScope : scope,
25553              $element,
25554              attrs,
25555              linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
25556              transcludeFn
25557          );
25558        }
25559
25560        // This is the function that is injected as `$transclude`.
25561        // Note: all arguments are optional!
25562        function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement) {
25563          var transcludeControllers;
25564
25565          // No scope passed in:
25566          if (!isScope(scope)) {
25567            futureParentElement = cloneAttachFn;
25568            cloneAttachFn = scope;
25569            scope = undefined;
25570          }
25571
25572          if (hasElementTranscludeDirective) {
25573            transcludeControllers = elementControllers;
25574          }
25575          if (!futureParentElement) {
25576            futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
25577          }
25578          return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
25579        }
25580      }
25581    }
25582
25583    function markDirectivesAsIsolate(directives) {
25584      // mark all directives as needing isolate scope.
25585      for (var j = 0, jj = directives.length; j < jj; j++) {
25586        directives[j] = inherit(directives[j], {$$isolateScope: true});
25587      }
25588    }
25589
25590    /**
25591     * looks up the directive and decorates it with exception handling and proper parameters. We
25592     * call this the boundDirective.
25593     *
25594     * @param {string} name name of the directive to look up.
25595     * @param {string} location The directive must be found in specific format.
25596     *   String containing any of theses characters:
25597     *
25598     *   * `E`: element name
25599     *   * `A': attribute
25600     *   * `C`: class
25601     *   * `M`: comment
25602     * @returns {boolean} true if directive was added.
25603     */
25604    function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,
25605                          endAttrName) {
25606      if (name === ignoreDirective) return null;
25607      var match = null;
25608      if (hasDirectives.hasOwnProperty(name)) {
25609        for (var directive, directives = $injector.get(name + Suffix),
25610            i = 0, ii = directives.length; i < ii; i++) {
25611          try {
25612            directive = directives[i];
25613            if ((isUndefined(maxPriority) || maxPriority > directive.priority) &&
25614                 directive.restrict.indexOf(location) != -1) {
25615              if (startAttrName) {
25616                directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
25617              }
25618              tDirectives.push(directive);
25619              match = directive;
25620            }
25621          } catch (e) { $exceptionHandler(e); }
25622        }
25623      }
25624      return match;
25625    }
25626
25627
25628    /**
25629     * looks up the directive and returns true if it is a multi-element directive,
25630     * and therefore requires DOM nodes between -start and -end markers to be grouped
25631     * together.
25632     *
25633     * @param {string} name name of the directive to look up.
25634     * @returns true if directive was registered as multi-element.
25635     */
25636    function directiveIsMultiElement(name) {
25637      if (hasDirectives.hasOwnProperty(name)) {
25638        for (var directive, directives = $injector.get(name + Suffix),
25639            i = 0, ii = directives.length; i < ii; i++) {
25640          directive = directives[i];
25641          if (directive.multiElement) {
25642            return true;
25643          }
25644        }
25645      }
25646      return false;
25647    }
25648
25649    /**
25650     * When the element is replaced with HTML template then the new attributes
25651     * on the template need to be merged with the existing attributes in the DOM.
25652     * The desired effect is to have both of the attributes present.
25653     *
25654     * @param {object} dst destination attributes (original DOM)
25655     * @param {object} src source attributes (from the directive template)
25656     */
25657    function mergeTemplateAttributes(dst, src) {
25658      var srcAttr = src.$attr,
25659          dstAttr = dst.$attr,
25660          $element = dst.$$element;
25661
25662      // reapply the old attributes to the new element
25663      forEach(dst, function(value, key) {
25664        if (key.charAt(0) != '$') {
25665          if (src[key] && src[key] !== value) {
25666            value += (key === 'style' ? ';' : ' ') + src[key];
25667          }
25668          dst.$set(key, value, true, srcAttr[key]);
25669        }
25670      });
25671
25672      // copy the new attributes on the old attrs object
25673      forEach(src, function(value, key) {
25674        if (key == 'class') {
25675          safeAddClass($element, value);
25676          dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
25677        } else if (key == 'style') {
25678          $element.attr('style', $element.attr('style') + ';' + value);
25679          dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value;
25680          // `dst` will never contain hasOwnProperty as DOM parser won't let it.
25681          // You will get an "InvalidCharacterError: DOM Exception 5" error if you
25682          // have an attribute like "has-own-property" or "data-has-own-property", etc.
25683        } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
25684          dst[key] = value;
25685          dstAttr[key] = srcAttr[key];
25686        }
25687      });
25688    }
25689
25690
25691    function compileTemplateUrl(directives, $compileNode, tAttrs,
25692        $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
25693      var linkQueue = [],
25694          afterTemplateNodeLinkFn,
25695          afterTemplateChildLinkFn,
25696          beforeTemplateCompileNode = $compileNode[0],
25697          origAsyncDirective = directives.shift(),
25698          derivedSyncDirective = inherit(origAsyncDirective, {
25699            templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
25700          }),
25701          templateUrl = (isFunction(origAsyncDirective.templateUrl))
25702              ? origAsyncDirective.templateUrl($compileNode, tAttrs)
25703              : origAsyncDirective.templateUrl,
25704          templateNamespace = origAsyncDirective.templateNamespace;
25705
25706      $compileNode.empty();
25707
25708      $templateRequest(templateUrl)
25709        .then(function(content) {
25710          var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
25711
25712          content = denormalizeTemplate(content);
25713
25714          if (origAsyncDirective.replace) {
25715            if (jqLiteIsTextNode(content)) {
25716              $template = [];
25717            } else {
25718              $template = removeComments(wrapTemplate(templateNamespace, trim(content)));
25719            }
25720            compileNode = $template[0];
25721
25722            if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
25723              throw $compileMinErr('tplrt',
25724                  "Template for directive '{0}' must have exactly one root element. {1}",
25725                  origAsyncDirective.name, templateUrl);
25726            }
25727
25728            tempTemplateAttrs = {$attr: {}};
25729            replaceWith($rootElement, $compileNode, compileNode);
25730            var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
25731
25732            if (isObject(origAsyncDirective.scope)) {
25733              markDirectivesAsIsolate(templateDirectives);
25734            }
25735            directives = templateDirectives.concat(directives);
25736            mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
25737          } else {
25738            compileNode = beforeTemplateCompileNode;
25739            $compileNode.html(content);
25740          }
25741
25742          directives.unshift(derivedSyncDirective);
25743
25744          afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
25745              childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
25746              previousCompileContext);
25747          forEach($rootElement, function(node, i) {
25748            if (node == compileNode) {
25749              $rootElement[i] = $compileNode[0];
25750            }
25751          });
25752          afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
25753
25754          while (linkQueue.length) {
25755            var scope = linkQueue.shift(),
25756                beforeTemplateLinkNode = linkQueue.shift(),
25757                linkRootElement = linkQueue.shift(),
25758                boundTranscludeFn = linkQueue.shift(),
25759                linkNode = $compileNode[0];
25760
25761            if (scope.$$destroyed) continue;
25762
25763            if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
25764              var oldClasses = beforeTemplateLinkNode.className;
25765
25766              if (!(previousCompileContext.hasElementTranscludeDirective &&
25767                  origAsyncDirective.replace)) {
25768                // it was cloned therefore we have to clone as well.
25769                linkNode = jqLiteClone(compileNode);
25770              }
25771              replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
25772
25773              // Copy in CSS classes from original node
25774              safeAddClass(jqLite(linkNode), oldClasses);
25775            }
25776            if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
25777              childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
25778            } else {
25779              childBoundTranscludeFn = boundTranscludeFn;
25780            }
25781            afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
25782              childBoundTranscludeFn, afterTemplateNodeLinkFn);
25783          }
25784          linkQueue = null;
25785        });
25786
25787      return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
25788        var childBoundTranscludeFn = boundTranscludeFn;
25789        if (scope.$$destroyed) return;
25790        if (linkQueue) {
25791          linkQueue.push(scope,
25792                         node,
25793                         rootElement,
25794                         childBoundTranscludeFn);
25795        } else {
25796          if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
25797            childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
25798          }
25799          afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn,
25800                                  afterTemplateNodeLinkFn);
25801        }
25802      };
25803    }
25804
25805
25806    /**
25807     * Sorting function for bound directives.
25808     */
25809    function byPriority(a, b) {
25810      var diff = b.priority - a.priority;
25811      if (diff !== 0) return diff;
25812      if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;
25813      return a.index - b.index;
25814    }
25815
25816    function assertNoDuplicate(what, previousDirective, directive, element) {
25817
25818      function wrapModuleNameIfDefined(moduleName) {
25819        return moduleName ?
25820          (' (module: ' + moduleName + ')') :
25821          '';
25822      }
25823
25824      if (previousDirective) {
25825        throw $compileMinErr('multidir', 'Multiple directives [{0}{1}, {2}{3}] asking for {4} on: {5}',
25826            previousDirective.name, wrapModuleNameIfDefined(previousDirective.$$moduleName),
25827            directive.name, wrapModuleNameIfDefined(directive.$$moduleName), what, startingTag(element));
25828      }
25829    }
25830
25831
25832    function addTextInterpolateDirective(directives, text) {
25833      var interpolateFn = $interpolate(text, true);
25834      if (interpolateFn) {
25835        directives.push({
25836          priority: 0,
25837          compile: function textInterpolateCompileFn(templateNode) {
25838            var templateNodeParent = templateNode.parent(),
25839                hasCompileParent = !!templateNodeParent.length;
25840
25841            // When transcluding a template that has bindings in the root
25842            // we don't have a parent and thus need to add the class during linking fn.
25843            if (hasCompileParent) compile.$$addBindingClass(templateNodeParent);
25844
25845            return function textInterpolateLinkFn(scope, node) {
25846              var parent = node.parent();
25847              if (!hasCompileParent) compile.$$addBindingClass(parent);
25848              compile.$$addBindingInfo(parent, interpolateFn.expressions);
25849              scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
25850                node[0].nodeValue = value;
25851              });
25852            };
25853          }
25854        });
25855      }
25856    }
25857
25858
25859    function wrapTemplate(type, template) {
25860      type = lowercase(type || 'html');
25861      switch (type) {
25862      case 'svg':
25863      case 'math':
25864        var wrapper = document.createElement('div');
25865        wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
25866        return wrapper.childNodes[0].childNodes;
25867      default:
25868        return template;
25869      }
25870    }
25871
25872
25873    function getTrustedContext(node, attrNormalizedName) {
25874      if (attrNormalizedName == "srcdoc") {
25875        return $sce.HTML;
25876      }
25877      var tag = nodeName_(node);
25878      // maction[xlink:href] can source SVG.  It's not limited to <maction>.
25879      if (attrNormalizedName == "xlinkHref" ||
25880          (tag == "form" && attrNormalizedName == "action") ||
25881          (tag != "img" && (attrNormalizedName == "src" ||
25882                            attrNormalizedName == "ngSrc"))) {
25883        return $sce.RESOURCE_URL;
25884      }
25885    }
25886
25887
25888    function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) {
25889      var trustedContext = getTrustedContext(node, name);
25890      allOrNothing = ALL_OR_NOTHING_ATTRS[name] || allOrNothing;
25891
25892      var interpolateFn = $interpolate(value, true, trustedContext, allOrNothing);
25893
25894      // no interpolation found -> ignore
25895      if (!interpolateFn) return;
25896
25897
25898      if (name === "multiple" && nodeName_(node) === "select") {
25899        throw $compileMinErr("selmulti",
25900            "Binding to the 'multiple' attribute is not supported. Element: {0}",
25901            startingTag(node));
25902      }
25903
25904      directives.push({
25905        priority: 100,
25906        compile: function() {
25907            return {
25908              pre: function attrInterpolatePreLinkFn(scope, element, attr) {
25909                var $$observers = (attr.$$observers || (attr.$$observers = createMap())
25909);
25910
25911                if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
25912                  throw $compileMinErr('nodomevents',
25913                      "Interpolations for HTML DOM event attributes are disallowed.  Please use the " +
25914                          "ng- versions (such as ng-click instead of onclick) instead.");
25915                }
25916
25917                // If the attribute has changed since last $interpolate()ed
25918                var newValue = attr[name];
25919                if (newValue !== value) {
25920                  // we need to interpolate again since the attribute value has been updated
25921                  // (e.g. by another directive's compile function)
25922                  // ensure unset/empty values make interpolateFn falsy
25923                  interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing);
25924                  value = newValue;
25925                }
25926
25927                // if attribute was updated so that there is no interpolation going on we don't want to
25928                // register any observers
25929                if (!interpolateFn) return;
25930
25931                // initialize attr object so that it's ready in case we need the value for isolate
25932                // scope initialization, otherwise the value would not be available from isolate
25933                // directive's linking fn during linking phase
25934                attr[name] = interpolateFn(scope);
25935
25936                ($$observers[name] || ($$observers[name] = [])).$$inter = true;
25937                (attr.$$observers && attr.$$observers[name].$$scope || scope).
25938                  $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
25939                    //special case for class attribute addition + removal
25940                    //so that class changes can tap into the animation
25941                    //hooks provided by the $animate service. Be sure to
25942                    //skip animations when the first digest occurs (when
25943                    //both the new and the old values are the same) since
25944                    //the CSS classes are the non-interpolated values
25945                    if (name === 'class' && newValue != oldValue) {
25946                      attr.$updateClass(newValue, oldValue);
25947                    } else {
25948                      attr.$set(name, newValue);
25949                    }
25950                  });
25951              }
25952            };
25953          }
25954      });
25955    }
25956
25957
25958    /**
25959     * This is a special jqLite.replaceWith, which can replace items which
25960     * have no parents, provided that the containing jqLite collection is provided.
25961     *
25962     * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
25963     *                               in the root of the tree.
25964     * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep
25965     *                                  the shell, but replace its DOM node reference.
25966     * @param {Node} newNode The new DOM node.
25967     */
25968    function replaceWith($rootElement, elementsToRemove, newNode) {
25969      var firstElementToRemove = elementsToRemove[0],
25970          removeCount = elementsToRemove.length,
25971          parent = firstElementToRemove.parentNode,
25972          i, ii;
25973
25974      if ($rootElement) {
25975        for (i = 0, ii = $rootElement.length; i < ii; i++) {
25976          if ($rootElement[i] == firstElementToRemove) {
25977            $rootElement[i++] = newNode;
25978            for (var j = i, j2 = j + removeCount - 1,
25979                     jj = $rootElement.length;
25980                 j < jj; j++, j2++) {
25981              if (j2 < jj) {
25982                $rootElement[j] = $rootElement[j2];
25983              } else {
25984                delete $rootElement[j];
25985              }
25986            }
25987            $rootElement.length -= removeCount - 1;
25988
25989            // If the replaced element is also the jQuery .context then replace it
25990            // .context is a deprecated jQuery api, so we should set it only when jQuery set it
25991            // http://api.jquery.com/context/
25992            if ($rootElement.context === firstElementToRemove) {
25993              $rootElement.context = newNode;
25994            }
25995            break;
25996          }
25997        }
25998      }
25999
26000      if (parent) {
26001        parent.replaceChild(newNode, firstElementToRemove);
26002      }
26003
26004      // TODO(perf): what's this document fragment for? is it needed? can we at least reuse it?
26005      var fragment = document.createDocumentFragment();
26006      fragment.appendChild(firstElementToRemove);
26007
26008      if (jqLite.hasData(firstElementToRemove)) {
26009        // Copy over user data (that includes Angular's $scope etc.). Don't copy private
26010        // data here because there's no public interface in jQuery to do that and copying over
26011        // event listeners (which is the main use of private data) wouldn't work anyway.
26012        jqLite(newNode).data(jqLite(firstElementToRemove).data());
26013
26014        // Remove data of the replaced element. We cannot just call .remove()
26015        // on the element it since that would deallocate scope that is needed
26016        // for the new node. Instead, remove the data "manually".
26017        if (!jQuery) {
26018          delete jqLite.cache[firstElementToRemove[jqLite.expando]];
26019        } else {
26020          // jQuery 2.x doesn't expose the data storage. Use jQuery.cleanData to clean up after
26021          // the replaced element. The cleanData version monkey-patched by Angular would cause
26022          // the scope to be trashed and we do need the very same scope to work with the new
26023          // element. However, we cannot just cache the non-patched version and use it here as
26024          // that would break if another library patches the method after Angular does (one
26025          // example is jQuery UI). Instead, set a flag indicating scope destroying should be
26026          // skipped this one time.
26027          skipDestroyOnNextJQueryCleanData = true;
26028          jQuery.cleanData([firstElementToRemove]);
26029        }
26030      }
26031
26032      for (var k = 1, kk = elementsToRemove.length; k < kk; k++) {
26033        var element = elementsToRemove[k];
26034        jqLite(element).remove(); // must do this way to clean up expando
26035        fragment.appendChild(element);
26036        delete elementsToRemove[k];
26037      }
26038
26039      elementsToRemove[0] = newNode;
26040      elementsToRemove.length = 1;
26041    }
26042
26043
26044    function cloneAndAnnotateFn(fn, annotation) {
26045      return extend(function() { return fn.apply(null, arguments); }, fn, annotation);
26046    }
26047
26048
26049    function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) {
26050      try {
26051        linkFn(scope, $element, attrs, controllers, transcludeFn);
26052      } catch (e) {
26053        $exceptionHandler(e, startingTag($element));
26054      }
26055    }
26056
26057
26058    // Set up $watches for isolate scope and controller bindings. This process
26059    // only occurs for isolate scopes and new scopes with controllerAs.
26060    function initializeDirectiveBindings(scope, attrs, destination, bindings,
26061                                         directive, newScope) {
26062      var onNewScopeDestroyed;
26063      forEach(bindings, function(definition, scopeName) {
26064        var attrName = definition.attrName,
26065        optional = definition.optional,
26066        mode = definition.mode, // @, =, or &
26067        lastValue,
26068        parentGet, parentSet, compare;
26069
26070        switch (mode) {
26071
26072          case '@':
26073            if (!optional && !hasOwnProperty.call(attrs, attrName)) {
26074              destination[scopeName] = attrs[attrName] = void 0;
26075            }
26076            attrs.$observe(attrName, function(value) {
26077              if (isString(value)) {
26078                destination[scopeName] = value;
26079              }
26080            });
26081            attrs.$$observers[attrName].$$scope = scope;
26082            if (isString(attrs[attrName])) {
26083              // If the attribute has been provided then we trigger an interpolation to ensure
26084              // the value is there for use in the link fn
26085              destination[scopeName] = $interpolate(attrs[attrName])(scope);
26086            }
26087            break;
26088
26089          case '=':
26090            if (!hasOwnProperty.call(attrs, attrName)) {
26091              if (optional) break;
26092              attrs[attrName] = void 0;
26093            }
26094            if (optional && !attrs[attrName]) break;
26095
26096            parentGet = $parse(attrs[attrName]);
26097            if (parentGet.literal) {
26098              compare = equals;
26099            } else {
26100              compare = function(a, b) { return a === b || (a !== a && b !== b); };
26101            }
26102            parentSet = parentGet.assign || function() {
26103              // reset the change, or we will throw this exception on every $digest
26104              lastValue = destination[scopeName] = parentGet(scope);
26105              throw $compileMinErr('nonassign',
26106                  "Expression '{0}' used with directive '{1}' is non-assignable!",
26107                  attrs[attrName], directive.name);
26108            };
26109            lastValue = destination[scopeName] = parentGet(scope);
26110            var parentValueWatch = function parentValueWatch(parentValue) {
26111              if (!compare(parentValue, destination[scopeName])) {
26112                // we are out of sync and need to copy
26113                if (!compare(parentValue, lastValue)) {
26114                  // parent changed and it has precedence
26115                  destination[scopeName] = parentValue;
26116                } else {
26117                  // if the parent can be assigned then do so
26118                  parentSet(scope, parentValue = destination[scopeName]);
26119                }
26120              }
26121              return lastValue = parentValue;
26122            };
26123            parentValueWatch.$stateful = true;
26124            var unwatch;
26125            if (definition.collection) {
26126              unwatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
26127            } else {
26128              unwatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
26129            }
26130            onNewScopeDestroyed = (onNewScopeDestroyed || []);
26131            onNewScopeDestroyed.push(unwatch);
26132            break;
26133
26134          case '&':
26135            // Don't assign Object.prototype method to scope
26136            parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop;
26137
26138            // Don't assign noop to destination if expression is not val
26138id
26139            if (parentGet === noop && optional) break;
26140
26141            destination[scopeName] = function(locals) {
26142              return parentGet(scope, locals);
26143            };
26144            break;
26145        }
26146      });
26147      var destroyBindings = onNewScopeDestroyed ? function destroyBindings() {
26148        for (var i = 0, ii = onNewScopeDestroyed.length; i < ii; ++i) {
26149          onNewScopeDestroyed[i]();
26150        }
26151      } : noop;
26152      if (newScope && destroyBindings !== noop) {
26153        newScope.$on('$destroy', destroyBindings);
26154        return noop;
26155      }
26156      return destroyBindings;
26157    }
26158  }];
26159}
26160
26161var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
26162/**
26163 * Converts all accepted directives format into proper directive name.
26164 * @param name Name to normalize
26165 */
26166function directiveNormalize(name) {
26167  return camelCase(name.replace(PREFIX_REGEXP, ''));
26168}
26169
26170/**
26171 * @ngdoc type
26172 * @name $compile.directive.Attributes
26173 *
26174 * @description
26175 * A shared object between directive compile / linking functions which contains normalized DOM
26176 * element attributes. The values reflect current binding state `{{ }}`. The normalization is
26177 * needed since all of these are treated as equivalent in Angular:
26178 *
26179 * ```
26180 *    <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
26181 * ```
26182 */
26183
26184/**
26185 * @ngdoc property
26186 * @name $compile.directive.Attributes#$attr
26187 *
26188 * @description
26189 * A map of DOM element attribute names to the normalized name. This is
26190 * needed to do reverse lookup from normalized name back to actual name.
26191 */
26192
26193
26194/**
26195 * @ngdoc method
26196 * @name $compile.directive.Attributes#$set
26197 * @kind function
26198 *
26199 * @description
26200 * Set DOM element attribute value.
26201 *
26202 *
26203 * @param {string} name Normalized element attribute name of the property to modify. The name is
26204 *          reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
26205 *          property to the original name.
26206 * @param {string} value Value to set the attribute to. The value can be an interpolated string.
26207 */
26208
26209
26210
26211/**
26212 * Closure compiler type information
26213 */
26214
26215function nodesetLinkingFn(
26216  /* angular.Scope */ scope,
26217  /* NodeList */ nodeList,
26218  /* Element */ rootElement,
26219  /* function(Function) */ boundTranscludeFn
26220) {}
26221
26222function directiveLinkingFn(
26223  /* nodesetLinkingFn */ nodesetLinkingFn,
26224  /* angular.Scope */ scope,
26225  /* Node */ node,
26226  /* Element */ rootElement,
26227  /* function(Function) */ boundTranscludeFn
26228) {}
26229
26230function tokenDifference(str1, str2) {
26231  var values = '',
26232      tokens1 = str1.split(/\s+/),
26233      tokens2 = str2.split(/\s+/);
26234
26235  outer:
26236  for (var i = 0; i < tokens1.length; i++) {
26237    var token = tokens1[i];
26238    for (var j = 0; j < tokens2.length; j++) {
26239      if (token == tokens2[j]) continue outer;
26240    }
26241    values += (values.length > 0 ? ' ' : '') + token;
26242  }
26243  return values;
26244}
26245
26246function removeComments(jqNodes) {
26247  jqNodes = jqLite(jqNodes);
26248  var i = jqNodes.length;
26249
26250  if (i <= 1) {
26251    return jqNodes;
26252  }
26253
26254  while (i--) {
26255    var node = jqNodes[i];
26256    if (node.nodeType === NODE_TYPE_COMMENT) {
26257      splice.call(jqNodes, i, 1);
26258    }
26259  }
26260  return jqNodes;
26261}
26262
26263var $controllerMinErr = minErr('$controller');
26264
26265
26266var CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/;
26267function identifierForController(controller, ident) {
26268  if (ident && isString(ident)) return ident;
26269  if (isString(controller)) {
26270    var match = CNTRL_REG.exec(controller);
26271    if (match) return match[3];
26272  }
26273}
26274
26275
26276/**
26277 * @ngdoc provider
26278 * @name $controllerProvider
26279 * @description
26280 * The {@link ng.$controller $controller service} is used by Angular to create new
26281 * controllers.
26282 *
26283 * This provider allows controller registration via the
26284 * {@link ng.$controllerProvider#register register} method.
26285 */
26286function $ControllerProvider() {
26287  var controllers = {},
26288      globals = false;
26289
26290  /**
26291   * @ngdoc method
26292   * @name $controllerProvider#register
26293   * @param {string|Object} name Controller name, or an object map of controllers where the keys are
26294   *    the names and the values are the constructors.
26295   * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
26296   *    annotations in the array notation).
26297   */
26298  this.register = function(name, constructor) {
26299    assertNotHasOwnProperty(name, 'controller');
26300    if (isObject(name)) {
26301      extend(controllers, name);
26302    } else {
26303      controllers[name] = constructor;
26304    }
26305  };
26306
26307  /**
26308   * @ngdoc method
26309   * @name $controllerProvider#allowGlobals
26310   * @description If called, allows `$controller` to find controller constructors on `window`
26311   */
26312  this.allowGlobals = function() {
26313    globals = true;
26314  };
26315
26316
26317  this.$get = ['$injector', '$window', function($injector, $window) {
26318
26319    /**
26320     * @ngdoc service
26321     * @name $controller
26322     * @requires $injector
26323     *
26324     * @param {Function|string} constructor If called with a function then it's considered to be the
26325     *    controller constructor function. Otherwise it's considered to be a string which is used
26326     *    to retrieve the controller constructor using the following steps:
26327     *
26328     *    * check if a controller with given name is registered via `$controllerProvider`
26329     *    * check if evaluating the string on the current scope returns a constructor
26330     *    * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
26331     *      `window` object (not recommended)
26332     *
26333     *    The string can use the `controller as property` syntax, where the controller instance is published
26334     *    as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
26335     *    to work correctly.
26336     *
26337     * @param {Object} locals Injection locals for Controller.
26338     * @return {Object} Instance of given controller.
26339     *
26340     * @description
26341     * `$controller` service is responsible for instantiating controllers.
26342     *
26343     * It's just a simple call to {@link auto.$injector $injector}, but extracted into
26344     * a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
26345     */
26346    return function(expression, locals, later, ident) {
26347      // PRIVATE API:
26348      //   param `later` --- indicates that the controller's constructor is invoked at a later time.
26349      //                     If true, $controller will allocate the object with the correct
26350      //                     prototype chain, but will not invoke the controller until a returned
26351      //                     callback is invoked.
26352      //   param `ident` --- An optional label which overrides the label parsed from the controller
26353      //                     expression, if any.
26354      var instance, match, constructor, identifier;
26355      later = later === true;
26356      if (ident && isString(ident)) {
26357        identifier = ident;
26358      }
26359
26360      if (isString(expression)) {
26361        match = expression.match(CNTRL_REG);
26362        if (!match) {
26363          throw $controllerMinErr('ctrlfmt',
26364            "Badly formed controller string '{0}'. " +
26365            "Must match `__name__ as __id__` or `__name__`.", expression);
26366        }
26367        constructor = match[1],
26368        identifier = identifier || match[3];
26369        expression = controllers.hasOwnProperty(constructor)
26370            ? controllers[constructor]
26371            : getter(locals.$scope, constructor, true) ||
26372                (globals ? getter($window, constructor, true) : undefined);
26373
26374        assertArgFn(expression, constructor, true);
26375      }
26376
26377      if (later) {
26378        // Instantiate controller later:
26379        // This machinery is used to create an instance of the object before calling the
26380        // controller's constructor itself.
26381        //
26382        // This allows properties to be added to the controller before the constructor is
26383        // invoked. Primarily, this is used for isolate scope bindings in $compile.
26384        //
26385        // This feature is not intended for use by applications, and is thus not documented
26386        // publicly.
26387        // Object creation: http://jsperf.com/create-constructor/2
26388        var controllerPrototype = (isArray(expression) ?
26389          expression[expression.length - 1] : expression).prototype;
26390        instance = Object.create(controllerPrototype || null);
26391
26392        if (identifier) {
26393          addIdentifier(locals, identifier, instance, constructor || expression.name);
26394        }
26395
26396        var instantiate;
26397        return instantiate = extend(function() {
26398          var result = $injector.invoke(expression, instance, locals, constructor);
26399          if (result !== instance && (isObject(result) || isFunction(result))) {
26400            instance = result;
26401            if (identifier) {
26402              // If result changed, re-assign controllerAs value to scope.
26403              addIdentifier(locals, identifier, instance, constructor || expression.name);
26404            }
26405          }
26406          return instance;
26407        }, {
26408          instance: instance,
26409          identifier: identifier
26410        });
26411      }
26412
26413      instance = $injector.instantiate(expression, locals, constructor);
26414
26415      if (identifier) {
26416        addIdentifier(locals, identifier, instance, constructor || expression.name);
26417      }
26418
26419      return instance;
26420    };
26421
26422    function addIdentifier(locals, identifier, instance, name) {
26423      if (!(locals && isObject(locals.$scope))) {
26424        throw minErr('$controller')('noscp',
26425          "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.",
26426          name, identifier);
26427      }
26428
26429      locals.$scope[identifier] = instance;
26430    }
26431  }];
26432}
26433
26434/**
26435 * @ngdoc service
26436 * @name $document
26437 * @requires $window
26438 *
26439 * @description
26440 * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
26441 *
26442 * @example
26443   <example module="documentExample">
26444     <file name="index.html">
26445       <div ng-controller="ExampleController">
26446         <p>$document title: <b ng-bind="title"></b></p>
26447         <p>window.document title: <b ng-bind="windowTitle"></b></p>
26448       </div>
26449     </file>
26450     <file name="script.js">
26451       angular.module('documentExample', [])
26452         .controller('ExampleController', ['$scope', '$document', function($scope, $document) {
26453           $scope.title = $document[0].title;
26454           $scope.windowTitle = angular.element(window.document)[0].title;
26455         }]);
26456     </file>
26457   </example>
26458 */
26459function $DocumentProvider() {
26460  this.$get = ['$window', function(window) {
26461    return jqLite(window.document);
26462  }];
26463}
26464
26465/**
26466 * @ngdoc service
26467 * @name $exceptionHandler
26468 * @requires ng.$log
26469 *
26470 * @description
26471 * Any uncaught exception in angular expressions is delegated to this service.
26472 * The default implementation simply delegates to `$log.error` which logs it into
26473 * the browser console.
26474 *
26475 * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
26476 * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
26477 *
26478 * ## Example:
26479 *
26480 * ```js
26481 *   angular.module('exceptionOverride', []).factory('$exceptionHandler', function() {
26482 *     return function(exception, cause) {
26483 *       exception.message += ' (caused by "' + cause + '")';
26484 *       throw exception;
26485 *     };
26486 *   });
26487 * ```
26488 *
26489 * This example will override the normal action of `$exceptionHandler`, to make angular
26490 * exceptions fail hard when they happen, instead of just logging to the console.
26491 *
26492 * <hr />
26493 * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind`
26494 * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler}
26495 * (unless executed during a digest).
26496 *
26497 * If you wish, you can manually delegate exceptions, e.g.
26498 * `try { ... } catch(e) { $exceptionHandler(e); }`
26499 *
26500 * @param {Error} exception Exception associated with the error.
26501 * @param {string=} cause optional information about the context in which
26502 *       the error was thrown.
26503 *
26504 */
26505function $ExceptionHandlerProvider() {
26506  this.$get = ['$log', function($log) {
26507    return function(exception, cause) {
26508      $log.error.apply($log, arguments);
26509    };
26510  }];
26511}
26512
26513var $$ForceReflowProvider = function() {
26514  this.$get = ['$document', function($document) {
26515    return function(domNode) {
26516      //the line below will force the browser to perform a repaint so
26517      //that all the animated elements within the animation frame will
26518      //be properly updated and drawn on screen. This is required to
26519      //ensure that the preparation animation is properly flushed so that
26520      //the active state picks up from there. DO NOT REMOVE THIS LINE.
26521      //DO NOT OPTIMIZE THIS LINE. THE MINIFIER WILL REMOVE IT OTHERWISE WHICH
26522      //WILL RESULT IN AN UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND
26523      //WILL TAKE YEARS AWAY FROM YOUR LIFE.
26524      if (domNode) {
26525        if (!domNode.nodeType && domNode instanceof jqLite) {
26526          domNode = domNode[0];
26527        }
26528      } else {
26529        domNode = $document[0].body;
26530      }
26531      return domNode.offsetWidth + 1;
26532    };
26533  }];
26534};
26535
26536var APPLICATION_JSON = 'application/json';
26537var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
26538var JSON_START = /^\[|^\{(?!\{)/;
26539var JSON_ENDS = {
26540  '[': /]$/,
26541  '{': /}$/
26542};
26543var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/;
26544var $httpMinErr = minErr('$http');
26545var $httpMinErrLegacyFn = function(method) {
26546  return function() {
26547    throw $httpMinErr('legacy', 'The method `{0}` on the promise returned from `$http` has been disabled.', method);
26548  };
26549};
26550
26551function serializeValue(v) {
26552  if (isObject(v)) {
26553    return isDate(v) ? v.toISOString() : toJson(v);
26554  }
26555  return v;
26556}
26557
26558
26559function $HttpParamSerializerProvider() {
26560  /**
26561   * @ngdoc service
26562   * @name $httpParamSerializer
26563   * @description
26564   *
26565   * Default {@link $http `$http`} params serializer that converts objects to strings
26566   * according to the following rules:
26567   *
26568   * * `{'foo': 'bar'}` results in `foo=bar`
26569   * * `{'foo': Date.now()}` results in `foo=2015-04-01T09%3A50%3A49.262Z` (`toISOString()` and encoded representation of a Date object)
26570   * * `{'foo': ['bar', 'baz']}` results in `foo=bar&foo=baz` (repeated key for each array element)
26571   * * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D"` (stringified and encoded representation of an object)
26572   *
26573   * Note that serializer will sort the request parameters alphabetically.
26574   * */
26575
26576  this.$get = function() {
26577    return function ngParamSerializer(params) {
26578      if (!params) return '';
26579      var parts = [];
vendor: 3,682 bytes, lines 26580-26706
26580      forEachSorted(params, function(value, key) {
26581        if (value === null || isUndefined(value)) return;
26582        if (isArray(value)) {
26583          forEach(value, function(v, k) {
26584            parts.push(encodeUriQuery(key)  + '=' + encodeUriQuery(serializeValue(v)));
26585          });
26586        } else {
26587          parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(value)));
26588        }
26589      });
26590
26591      return parts.join('&');
26592    };
26593  };
26594}
26595
26596function $HttpParamSerializerJQLikeProvider() {
26597  /**
26598   * @ngdoc service
26599   * @name $httpParamSerializerJQLike
26600   * @description
26601   *
26602   * Alternative {@link $http `$http`} params serializer that follows
26603   * jQuery's [`param()`](http://api.jquery.com/jquery.param/) method logic.
26604   * The serializer will also sort the params alphabetically.
26605   *
26606   * To use it for serializing `$http` request parameters, set it as the `paramSerializer` property:
26607   *
26608   * ```js
26609   * $http({
26610   *   url: myUrl,
26611   *   method: 'GET',
26612   *   params: myParams,
26613   *   paramSerializer: '$httpParamSerializerJQLike'
26614   * });
26615   * ```
26616   *
26617   * It is also possible to set it as the default `paramSerializer` in the
26618   * {@link $httpProvider#defaults `$httpProvider`}.
26619   *
26620   * Additionally, you can inject the serializer and use it explicitly, for example to serialize
26621   * form data for submission:
26622   *
26623   * ```js
26624   * .controller(function($http, $httpParamSerializerJQLike) {
26625   *   //...
26626   *
26627   *   $http({
26628   *     url: myUrl,
26629   *     method: 'POST',
26630   *     data: $httpParamSerializerJQLike(myData),
26631   *     headers: {
26632   *       'Content-Type': 'application/x-www-form-urlencoded'
26633   *     }
26634   *   });
26635   *
26636   * });
26637   * ```
26638   *
26639   * */
26640  this.$get = function() {
26641    return function jQueryLikeParamSerializer(params) {
26642      if (!params) return '';
26643      var parts = [];
26644      serialize(params, '', true);
26645      return parts.join('&');
26646
26647      function serialize(toSerialize, prefix, topLevel) {
26648        if (toSerialize === null || isUndefined(toSerialize)) return;
26649        if (isArray(toSerialize)) {
26650          forEach(toSerialize, function(value, index) {
26651            serialize(value, prefix + '[' + (isObject(value) ? index : '') + ']');
26652          });
26653        } else if (isObject(toSerialize) && !isDate(toSerialize)) {
26654          forEachSorted(toSerialize, function(value, key) {
26655            serialize(value, prefix +
26656                (topLevel ? '' : '[') +
26657                key +
26658                (topLevel ? '' : ']'));
26659          });
26660        } else {
26661          parts.push(encodeUriQuery(prefix) + '=' + encodeUriQuery(serializeValue(toSerialize)));
26662        }
26663      }
26664    };
26665  };
26666}
26667
26668function defaultHttpResponseTransform(data, headers) {
26669  if (isString(data)) {
26670    // Strip json vulnerability protection prefix and trim whitespace
26671    var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim();
26672
26673    if (tempData) {
26674      var contentType = headers('Content-Type');
26675      if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
26676        data = fromJson(tempData);
26677      }
26678    }
26679  }
26680
26681  return data;
26682}
26683
26684function isJsonLike(str) {
26685    var jsonStart = str.match(JSON_START);
26686    return jsonStart && JSON_ENDS[jsonStart[0]].test(str);
26687}
26688
26689/**
26690 * Parse headers into key value object
26691 *
26692 * @param {string} headers Raw headers as a string
26693 * @returns {Object} Parsed headers as key value object
26694 */
26695function parseHeaders(headers) {
26696  var parsed = createMap(), i;
26697
26698  function fillInParsed(key, val) {
26699    if (key) {
26700      parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
26701    }
26702  }
26703
26704  if (isString(headers)) {
26705    forEach(headers.split('\n'), function(line) {
26706      i = line.indexOf(':');
vendor: 4,257 bytes, lines 26707-26844
26707      fillInParsed(lowercase(trim(line.substr(0, i))), trim(line.substr(i + 1)));
26708    });
26709  } else if (isObject(headers)) {
26710    forEach(headers, function(headerVal, headerKey) {
26711      fillInParsed(lowercase(headerKey), trim(headerVal));
26712    });
26713  }
26714
26715  return parsed;
26716}
26717
26718
26719/**
26720 * Returns a function that provides access to parsed headers.
26721 *
26722 * Headers are lazy parsed when first requested.
26723 * @see parseHeaders
26724 *
26725 * @param {(string|Object)} headers Headers to provide access to.
26726 * @returns {function(string=)} Returns a getter function which if called with:
26727 *
26728 *   - if called with single an argument returns a single header value or null
26729 *   - if called with no arguments returns an object containing all headers.
26730 */
26731function headersGetter(headers) {
26732  var headersObj;
26733
26734  return function(name) {
26735    if (!headersObj) headersObj =  parseHeaders(headers);
26736
26737    if (name) {
26738      var value = headersObj[lowercase(name)];
26739      if (value === void 0) {
26740        value = null;
26741      }
26742      return value;
26743    }
26744
26745    return headersObj;
26746  };
26747}
26748
26749
26750/**
26751 * Chain all given functions
26752 *
26753 * This function is used for both request and response transforming
26754 *
26755 * @param {*} data Data to transform.
26756 * @param {function(string=)} headers HTTP headers getter fn.
26757 * @param {number} status HTTP status code of the response.
26758 * @param {(Function|Array.<Function>)} fns Function or an array of functions.
26759 * @returns {*} Transformed data.
26760 */
26761function transformData(data, headers, status, fns) {
26762  if (isFunction(fns)) {
26763    return fns(data, headers, status);
26764  }
26765
26766  forEach(fns, function(fn) {
26767    data = fn(data, headers, status);
26768  });
26769
26770  return data;
26771}
26772
26773
26774function isSuccess(status) {
26775  return 200 <= status && status < 300;
26776}
26777
26778
26779/**
26780 * @ngdoc provider
26781 * @name $httpProvider
26782 * @description
26783 * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service.
26784 * */
26785function $HttpProvider() {
26786  /**
26787   * @ngdoc property
26788   * @name $httpProvider#defaults
26789   * @description
26790   *
26791   * Object containing default values for all {@link ng.$http $http} requests.
26792   *
26793   * - **`defaults.cache`** - {Object} - an object built with {@link ng.$cacheFactory `$cacheFactory`}
26794   * that will provide the cache for all requests who set their `cache` property to `true`.
26795   * If you set the `defaults.cache = false` then only requests that specify their own custom
26796   * cache object will be cached. See {@link $http#caching $http Caching} for more information.
26797   *
26798   * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
26799   * Defaults value is `'XSRF-TOKEN'`.
26800   *
26801   * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
26802   * XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
26803   *
26804   * - **`defaults.headers`** - {Object} - Default headers for all $http requests.
26805   * Refer to {@link ng.$http#setting-http-headers $http} for documentation on
26806   * setting default headers.
26807   *     - **`defaults.headers.common`**
26808   *     - **`defaults.headers.post`**
26809   *     - **`defaults.headers.put`**
26810   *     - **`defaults.headers.patch`**
26811   *
26812   *
26813   * - **`defaults.paramSerializer`** - `{string|function(Object<string,string>):string}` - A function
26814   *  used to the prepare string representation of request parameters (specified as an object).
26815   *  If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}.
26816   *  Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}.
26817   *
26818   **/
26819  var defaults = this.defaults = {
26820    // transform incoming response data
26821    transformResponse: [defaultHttpResponseTransform],
26822
26823    // transform outgoing request data
26824    transformRequest: [function(d) {
26825      return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d;
26826    }],
26827
26828    // default headers
26829    headers: {
26830      common: {
26831        'Accept': 'application/json, text/plain, */*'
26832      },
26833      post:   shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
26834      put:    shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
26835      patch:  shallowCopy(CONTENT_TYPE_APPLICATION_JSON)
26836    },
26837
26838    xsrfCookieName: 'XSRF-TOKEN',
26839    xsrfHeaderName: 'X-XSRF-TOKEN',
26840
26841    paramSerializer: '$httpParamSerializer'
26842  };
26843
26844  var useApplyAsync = false;
vendor: 4,300 bytes, lines 26845-26955
26845  /**
26846   * @ngdoc method
26847   * @name $httpProvider#useApplyAsync
26848   * @description
26849   *
26850   * Configure $http service to combine processing of multiple http responses received at around
26851   * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in
26852   * significant performance improvement for bigger applications that make many HTTP requests
26853   * concurrently (common during application bootstrap).
26854   *
26855   * Defaults to false. If no value is specified, returns the current configured value.
26856   *
26857   * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred
26858   *    "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window
26859   *    to load and share the same digest cycle.
26860   *
26861   * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
26862   *    otherwise, returns the current configured value.
26863   **/
26864  this.useApplyAsync = function(value) {
26865    if (isDefined(value)) {
26866      useApplyAsync = !!value;
26867      return this;
26868    }
26869    return useApplyAsync;
26870  };
26871
26872  var useLegacyPromise = true;
26873  /**
26874   * @ngdoc method
26875   * @name $httpProvider#useLegacyPromiseExtensions
26876   * @description
26877   *
26878   * Configure `$http` service to return promises without the shorthand methods `success` and `error`.
26879   * This should be used to make sure that applications work without these methods.
26880   *
26881   * Defaults to false. If no value is specified, returns the current configured value.
26882   *
26883   * @param {boolean=} value If true, `$http` will return a normal promise without the `success` and `error` methods.
26884   *
26885   * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
26886   *    otherwise, returns the current configured value.
26887   **/
26888  this.useLegacyPromiseExtensions = function(value) {
26889    if (isDefined(value)) {
26890      useLegacyPromise = !!value;
26891      return this;
26892    }
26893    return useLegacyPromise;
26894  };
26895
26896  /**
26897   * @ngdoc property
26898   * @name $httpProvider#interceptors
26899   * @description
26900   *
26901   * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http}
26902   * pre-processing of request or postprocessing of responses.
26903   *
26904   * These service factories are ordered by request, i.e. they are applied in the same order as the
26905   * array, on request, but reverse order, on response.
26906   *
26907   * {@link ng.$http#interceptors Interceptors detailed info}
26908   **/
26909  var interceptorFactories = this.interceptors = [];
26910
26911  this.$get = ['$httpBackend', '$$cookieReader', '$cacheFactory', '$rootScope', '$q', '$injector',
26912      function($httpBackend, $$cookieReader, $cacheFactory, $rootScope, $q, $injector) {
26913
26914    var defaultCache = $cacheFactory('$http');
26915
26916    /**
26917     * Make sure that default param serializer is exposed as a function
26918     */
26919    defaults.paramSerializer = isString(defaults.paramSerializer) ?
26920      $injector.get(defaults.paramSerializer) : defaults.paramSerializer;
26921
26922    /**
26923     * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
26924     * The reversal is needed so that we can build up the interception chain around the
26925     * server request.
26926     */
26927    var reversedInterceptors = [];
26928
26929    forEach(interceptorFactories, function(interceptorFactory) {
26930      reversedInterceptors.unshift(isString(interceptorFactory)
26931          ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
26932    });
26933
26934    /**
26935     * @ngdoc service
26936     * @kind function
26937     * @name $http
26938     * @requires ng.$httpBackend
26939     * @requires $cacheFactory
26940     * @requires $rootScope
26941     * @requires $q
26942     * @requires $injector
26943     *
26944     * @description
26945     * The `$http` service is a core Angular service that facilitates communication with the remote
26946     * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest)
26947     * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP).
26948     *
26949     * For unit testing applications that use `$http` service, see
26950     * {@link ngMock.$httpBackend $httpBackend mock}.
26951     *
26952     * For a higher level of abstraction, please check out the {@link ngResource.$resource
26953     * $resource} service.
26954     *
26955     * The $http API is based on the {@link ng.$q deferred/promise APIs}
26955 exposed by
26956     * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
26957     * it is important to familiarize yourself with these APIs and the guarantees they provide.
26958     *
26959     *
26960     * ## General usage
26961     * The `$http` service is a function which takes a single argument — a {@link $http#usage configuration object} —
26962     * that is used to generate an HTTP request and returns  a {@link ng.$q promise}.
26963     *
26964     * ```js
26965     *   // Simple GET request example:
26966     *   $http({
26967     *     method: 'GET',
26968     *     url: '/someUrl'
26969     *   }).then(function successCallback(response) {
26970     *       // this callback will be called asynchronously
26971     *       // when the response is available
26972     *     }, function errorCallback(response) {
26973     *       // called asynchronously if an error occurs
26974     *       // or server returns response with an error status.
26975     *     });
26976     * ```
26977     *
26978     * The response object has these properties:
26979     *
26980     *   - **data** – `{string|Object}` – The response body transformed with the transform
26981     *     functions.
26982     *   - **status** – `{number}` – HTTP status code of the response.
26983     *   - **headers** – `{function([headerName])}` – Header getter function.
26984     *   - **config** – `{Object}` – The configuration object that was used to generate the request.
26985     *   - **statusText** – `{string}` – HTTP status text of the response.
26986     *
26987     * A response status code between 200 and 299 is considered a success status and
26988     * will result in the success callback being called. Note that if the response is a redirect,
26989     * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
26990     * called for such responses.
26991     *
26992     *
26993     * ## Shortcut methods
26994     *
26995     * Shortcut methods are also available. All shortcut methods require passing in the URL, and
26996     * request data must be passed in for POST/PUT requests. An optional config can be passed as the
26997     * last argument.
26998     *
26999     * ```js
27000     *   $http.get('/someUrl', config).then(successCallback, errorCallback);
27001     *   $http.post('/someUrl', data, config).then(successCallback, errorCallback);
27002     * ```
27003     *
27004     * Complete list of shortcut methods:
27005     *
27006     * - {@link ng.$http#get $http.get}
27007     * - {@link ng.$http#head $http.head}
27008     * - {@link ng.$http#post $http.post}
27009     * - {@link ng.$http#put $http.put}
27010     * - {@link ng.$http#delete $http.delete}
27011     * - {@link ng.$http#jsonp $http.jsonp}
27012     * - {@link ng.$http#patch $http.patch}
27013     *
27014     *
27015     * ## Writing Unit Tests that use $http
27016     * When unit testing (using {@link ngMock ngMock}), it is necessary to call
27017     * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
27018     * request using trained responses.
27019     *
27020     * ```
27021     * $httpBackend.expectGET(...);
27022     * $http.get(...);
27023     * $httpBackend.flush();
27024     * ```
27025     *
27026     * ## Deprecation Notice
27027     * <div class="alert alert-danger">
27028     *   The `$http` legacy promise methods `success` and `error` have been deprecated.
27029     *   Use the standard `then` method instead.
27030     *   If {@link $httpProvider#useLegacyPromiseExtensions `$httpProvider.useLegacyPromiseExtensions`} is set to
27031     *   `false` then these methods will throw {@link $http:legacy `$http/legacy`} error.
27032     * </div>
27033     *
27034     * ## Setting HTTP Headers
27035     *
27036     * The $http service will automatically add certain HTTP headers to all requests. These defaults
27037     * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
27038     * object, which currently contains this default configuration:
27039     *
27040     * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
27041     *   - `Accept: application/json, text/plain, * / *`
27042     * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
27043     *   - `Content-Type: application/json`
27044     * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
27045     *   - `Content-Type: application/json`
27046     *
27047     * To add or overwrite these defaults, simply add or remove a property from these configuration
27048     * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
27049     * with the lowercased HTTP method name as the key, e.g.
27050     * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }`.
27051     *
27052     * The defaults can also be set at runtime via the `$http.defaults` object in the same
27053     * fashion. For example:
27054     *
27055     * ```
27056     * module.run(function($http) {
27057     *   $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w'
27058     * });
27059     * ```
27060     *
27061     * In addition, you can supply a `headers` property in the config object passed when
27062     * calling `$http(config)`, which overrides the defaults without changing them globally.
27063     *
27064     * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,
27065     * Use the `headers` property, setting the desired header to `undefined`. For example:
27066     *
27067     * ```js
27068     * var req = {
27069     *  method: 'POST',
27070     *  url: 'http://example.com',
27071     *  headers: {
27072     *    'Content-Type': undefined
27073     *  },
27074     *  data: { test: 'test' }
27075     * }
27076     *
27077     * $http(req).then(function(){...}, function(){...});
27078     * ```
27079     *
27080     * ## Transforming Requests and Responses
27081     *
27082     * Both requests and responses can be transformed using transformation functions: `transformRequest`
27083     * and `transformResponse`. These properties can be a single function that returns
27084     * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions,
27085     * which allows you to `push` or `unshift` a new transformation function into the transformation chain.
27086     *
27087     * ### Default Transformations
27088     *
27089     * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
27090     * `defaults.transformResponse` properties. If a request does not provide its own transformations
27091     * then these will be applied.
27092     *
27093     * You can augment or replace the default transformations by modifying these properties by adding to or
27094     * replacing the array.
27095     *
27096     * Angular provides the following default transformations:
27097     *
27098     * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`):
27099     *
27100     * - If the `data` property of the request configuration object contains an object, serialize it
27101     *   into JSON format.
27102     *
27103     * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`):
27104     *
27105     *  - If XSRF prefix is detected, strip it (see Security Considerations section below).
27106     *  - If JSON response is detected, deserialize it using a JSON parser.
27107     *
27108     *
27109     * ### Overriding the Default Transformations Per Request
27110     *
27111     * If you wish override the request/response transformations only for a single request then provide
27112     * `transformRequest` and/or `transformResponse` properties on the configuration object passed
27113     * into `$http`.
27114     *
27115     * Note that if you provide these properties on the config object the default transformations will be
27116     * overwritten. If you wish to augment the default transformations then you must include them in your
27117     * local transformation array.
27118     *
27119     * The following code demonstrates adding a new response transformation to be run after the default response
27120     * transformations have been run.
27121     *
27122     * ```js
27123     * function appendTransform(defaults, transform) {
27124     *
27125     *   // We can't guarantee that the default transformation is an array
27126     *   defaults = angular.isArray(defaults) ? defaults : [defaults];
27127     *
27128     *   // Append the new transformation to the defaults
27129     *   return defaults.concat(transform);
27130     * }
27131     *
27132     * $http({
27133     *   url: '...',
27134     *   method: 'GET',
27135     *   transformResponse: appendTransform($http.defaults.transformResponse, function(value) {
27136     *     return doTransform(value);
27137     *   })
27138     * });
27139     * ```
27140     *
27141     *
27142     * ## Caching
27143     *
27144     * To enable caching, set the request configuration `cache` property to `true` (to use default
27145     * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}).
27146     * When the cache is enabled, `$http` stores the response from the server in the specified
27147     * cache. The next time the same request is made, the response is served from the cache without
27148     * sending a request to the server.
27149     *
27150     * Note that even if the response is served from cache, delivery of the data is asynchronous in
27151     * the same way that real requests are.
27152     *
27153     * If there are multiple GET requests for the same URL that should be cached using the same
27154     * cache, but the cache is not populated yet, only one request to the server will be made and
27155     * the remaining requests will be fulfilled using the response from the first request.
27156     *
27157     * You can change the default cache to a new object (built with
27158     * {@link ng.$cacheFactory `$cacheFactory`}) by updating the
27159     * {@link ng.$http#defaults `$http.defaults.cache`} property. All requests who set
27160     * their `cache` property to `true` will now use this cache object.
27161     *
27162     * If you set the default cache to `false` then only requests that specify their own custom
27163     * cache object will be cached.
27164     *
27165     * ## Interceptors
27166     *
27167     * Before you start creating interceptors, be sure to understand the
27168     * {@link ng.$q $q and deferred/promise APIs}.
27169     *
27170     * For purposes of global error handling, authentication, or any kind of synchronous or
27171     * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
27172     * able to intercept requests before they are handed to the server and
27173     * responses before they are handed over to the application code that
27174     * initiated these requests. The interceptors leverage the {@link ng.$q
27175     * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
27176     *
27177     * The interceptors are service factories that are registered with the `$httpProvider` by
27178     * adding them to the `$httpProvider.interceptors` array. The factory is called and
27179     * injected with dependencies (if specified) and returns the interceptor.
27180     *
27181     * There are two kinds of interceptors (and two kinds of rejection interceptors):
27182     *
27183     *   * `request`: interceptors get called with a http {@link $http#usage config} object. The function is free to
27184     *     modify the `config` object or create a new one. The function needs to return the `config`
27185     *     object directly, or a promise containing the `config` or a new `config` object.
27186     *   * `requestError`: interceptor gets called when a previous interceptor threw an error or
27187     *     resolved with a rejection.
27188     *   * `response`: interceptors get called with http `response` object. The function is free to
27189     *     modify the `response` object or create a new one. The function needs to return the `response`
27190     *     object directly, or as a promise containing the `response` or a new `response` object.
27191     *   * `responseError`: interceptor gets called when a previous interceptor threw an error or
27192     *     resolved with a rejection.
27193     *
27194     *
27195     * ```js
27196     *   // register the interceptor as a service
27197     *   $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
27198     *     return {
27199     *       // optional method
27200     *       'request': function(config) {
27201     *         // do something on success
27202     *         return config;
27203     *       },
27204     *
27205     *       // optional method
27206     *      'requestError': function(rejection) {
27207     *         // do something on error
27208     *         if (canRecover(rejection)) {
27209     *           return responseOrNewPromise
27210     *         }
27211     *         return $q.reject(rejection);
27212     *       },
27213     *
27214     *
27215     *
27216     *       // optional method
27217     *       'response': function(response) {
27218     *         // do something on success
27219     *         return response;
27220     *       },
27221     *
27222     *       // optional method
27223     *      'responseError': function(rejection) {
27224     *         // do something on error
27225     *         if (canRecover(rejection)) {
27226     *           return responseOrNewPromise
27227     *         }
27228     *         return $q.reject(rejection);
27229     *       }
27230     *     };
27231     *   });
27232     *
27233     *   $httpProvider.interceptors.push('myHttpInterceptor');
27234     *
27235     *
27236     *   // alternatively, register the interceptor via an anonymous factory
27237     *   $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
27238     *     return {
27239     *      'request': function(config) {
27240     *          // same as above
27241     *       },
27242     *
27243     *       'response': function(response) {
27244     *          // same as above
27245     *       }
27246     *     };
27247     *   });
27248     * ```
27249     *
27250     * ## Security Considerations
27251     *
27252     * When designing web applications, consider security threats from:
27253     *
27254     * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
27255     * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)
27256     *
27257     * Both server and the client must cooperate in order to eliminate these threats. Angular comes
27258     * pre-configured with strategies that address these issues, but for this to work backend server
27259     * cooperation is required.
27260     *
27261     * ### JSON Vulnerability Protection
27262     *
27263     * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
27264     * allows third party website to turn your JSON resource URL into
27265     * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To
27266     * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
27267     * Angular will automatically strip the prefix before processing it as JSON.
27268     *
27269     * For example if your server needs to return:
27270     * ```js
27271     * ['one','two']
27272     * ```
27273     *
27274     * which is vulnerable to attack, your server can return:
27275     * ```js
27276     * )]}',
27277     * ['one','two']
27278     * ```
27279     *
27280     * Angular will strip the prefix, before processing the JSON.
27281     *
27282     *
27283     * ### Cross Site Request Forgery (XSRF) Protection
27284     *
27285     * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is a technique by which
27286     * an unauthorized site can gain your user's private data. Angular provides a mechanism
27287     * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
27288     * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only
27289     * JavaScript that runs on your domain could read the cookie, your server can be assured that
27290     * the XHR came from JavaScript running on your domain. The header will not be set for
27291     * cross-domain requests.
27292     *
27293     * To take advantage of this, your server needs to set a token in a JavaScript readable session
27294     * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
27295     * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
27296     * that only JavaScript running on your domain could have sent the request. The token must be
27297     * unique for each user and must be verifiable by the server (to prevent the JavaScript from
27298     * making up its own tokens). We recommend that the token is a digest of your site's
27299     * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography&#41;)
27300     * for added security.
27301     *
27302     * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
27303     * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
27304     * or the per-request config object.
27305     *
27306     * In order to prevent collisions in environments where multiple Angular apps share the
27307     * same domain or subdomain, we recommend that each application uses unique cookie name.
27308     *
27309     * @param {object} config Object describing the request to be made and how it should be
27310     *    processed. The object has following properties:
27311     *
27312     *    - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
27313     *    - **url** – `{string}` – Absolute or relative URL of the resource that is being requested.
27314     *    - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be serialized
27315     *      with the `paramSerializer` and appended as GET parameters.
27316     *    - **data** – `{string|Object}` – Data to be sent as the request message data.
27317     *    - **headers** – `{Object}` – Map of strings or functions which return strings representing
27318     *      HTTP headers to send to the server. If the return value of a function is null, the
27319     *      header will not be sent. Functions accept a config object as an argument.
27320     *    - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
27321     *    - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
27322     *    - **transformRequest** –
27323     *      `{function(data, headersGetter)|Array.<function(data, headersGetter)>
27323}` –
27324     *      transform function or an array of such functions. The transform function takes the http
27325     *      request body and headers and returns its transformed (typically serialized) version.
27326     *      See {@link ng.$http#overriding-the-default-transformations-per-request
27327     *      Overriding the Default Transformations}
27328     *    - **transformResponse** –
27329     *      `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` –
27330     *      transform function or an array of such functions. The transform function takes the http
27331     *      response body, headers and status and returns its transformed (typically deserialized) version.
27332     *      See {@link ng.$http#overriding-the-default-transformations-per-request
27333     *      Overriding the Default TransformationjqLiks}
27334     *    - **paramSerializer** - `{string|function(Object<string,string>):string}` - A function used to
27335     *      prepare the string representation of request parameters (specified as an object).
27336     *      If specified as string, it is interpreted as function registered with the
27337     *      {@link $injector $injector}, which means you can create your own serializer
27338     *      by registering it as a {@link auto.$provide#service service}.
27339     *      The default serializer is the {@link $httpParamSerializer $httpParamSerializer};
27340     *      alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike}
27341     *    - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
27342     *      GET request, otherwise if a cache instance built with
27343     *      {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
27344     *      caching.
27345     *    - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
27346     *      that should abort the request when resolved.
27347     *    - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
27348     *      XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
27349     *      for more information.
27350     *    - **responseType** - `{string}` - see
27351     *      [XMLHttpRequest.responseType](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#xmlhttprequest-responsetype).
27352     *
27353     * @returns {HttpPromise} Returns a {@link ng.$q `Promise}` that will be resolved to a response object
27354     *                        when the request succeeds or fails.
27355     *
27356     *
27357     * @property {Array.<Object>} pendingRequests Array of config objects for currently pending
27358     *   requests. This is primarily meant to be used for debugging purposes.
27359     *
27360     *
27361     * @example
27362<example module="httpExample">
27363<file name="index.html">
27364  <div ng-controller="FetchController">
27365    <select ng-model="method" aria-label="Request method">
27366      <option>GET</option>
27367      <option>JSONP</option>
27368    </select>
27369    <input type="text" ng-model="url" size="80" aria-label="URL" />
27370    <button id="fetchbtn" ng-click="fetch()">fetch</button><br>
27371    <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
27372    <button id="samplejsonpbtn"
27373      ng-click="updateModel('JSONP',
27374                    'https://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">
27375      Sample JSONP
27376    </button>
27377    <button id="invalidjsonpbtn"
27378      ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist&callback=JSON_CALLBACK')">
27379        Invalid JSONP
27380      </button>
27381    <pre>http status code: {{status}}</pre>
27382    <pre>http response data: {{data}}</pre>
27383  </div>
27384</file>
27385<file name="script.js">
27386  angular.module('httpExample', [])
27387    .controller('FetchController', ['$scope', '$http', '$templateCache',
27388      function($scope, $http, $templateCache) {
27389        $scope.method = 'GET';
27390        $scope.url = 'http-hello.html';
27391
27392        $scope.fetch = function() {
27393          $scope.code = null;
27394          $scope.response = null;
27395
27396          $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
27397            then(function(response) {
27398              $scope.status = response.status;
27399              $scope.data = response.data;
27400            }, function(response) {
27401              $scope.data = response.data || "Request failed";
27402              $scope.status = response.status;
27403          });
27404        };
27405
27406        $scope.updateModel = function(method, url) {
27407          $scope.method = method;
27408          $scope.url = url;
27409        };
27410      }]);
27411</file>
27412<file name="http-hello.html">
27413  Hello, $http!
27414</file>
27415<file name="protractor.js" type="protractor">
27416  var status = element(by.binding('status'));
27417  var data = element(by.binding('data'));
27418  var fetchBtn = element(by.id('fetchbtn'));
27419  var sampleGetBtn = element(by.id('samplegetbtn'));
27420  var sampleJsonpBtn = element(by.id('samplejsonpbtn'));
27421  var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));
27422
27423  it('should make an xhr GET request', function() {
27424    sampleGetBtn.click();
27425    fetchBtn.click();
27426    expect(status.getText()).toMatch('200');
27427    expect(data.getText()).toMatch(/Hello, \$http!/);
27428  });
27429
27430// Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185
27431// it('should make a JSONP request to angularjs.org', function() {
27432//   sampleJsonpBtn.click();
27433//   fetchBtn.click();
27434//   expect(status.getText()).toMatch('200');
27435//   expect(data.getText()).toMatch(/Super Hero!/);
27436// });
27437
27438  it('should make JSONP request to invalid URL and invoke the error handler',
27439      function() {
27440    invalidJsonpBtn.click();
27441    fetchBtn.click();
27442    expect(status.getText()).toMatch('0');
27443    expect(data.getText()).toMatch('Request failed');
27444  });
27445</file>
27446</example>
27447     */
27448    function $http(requestConfig) {
27449
27450      if (!angular.isObject(requestConfig)) {
27451        throw minErr('$http')('badreq', 'Http request configuration must be an object.  Received: {0}', requestConfig);
27452      }
27453
27454      var config = extend({
27455        method: 'get',
27456        transformRequest: defaults.transformRequest,
27457        transformResponse: defaults.transformResponse,
27458        paramSerializer: defaults.paramSerializer
27459      }, requestConfig);
27460
27461      config.headers = mergeHeaders(requestConfig);
27462      config.method = uppercase(config.method);
27463      config.paramSerializer = isString(config.paramSerializer) ?
27464        $injector.get(config.paramSerializer) : config.paramSerializer;
27465
27466      var serverRequest = function(config) {
27467        var headers = config.headers;
27468        var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
27469
27470        // strip content-type if data is undefined
27471        if (isUndefined(reqData)) {
27472          forEach(headers, function(value, header) {
27473            if (lowercase(header) === 'content-type') {
27474                delete headers[header];
27475            }
27476          });
27477        }
27478
27479        if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
27480          config.withCredentials = defaults.withCredentials;
27481        }
27482
27483        // send request
27484        return sendReq(config, reqData).then(transformResponse, transformResponse);
27485      };
27486
27487      var chain = [serverRequest, undefined];
27488      var promise = $q.when(config);
27489
27490      // apply interceptors
27491      forEach(reversedInterceptors, function(interceptor) {
27492        if (interceptor.request || interceptor.requestError) {
27493          chain.unshift(interceptor.request, interceptor.requestError);
27494        }
27495        if (interceptor.response || interceptor.responseError) {
27496          chain.push(interceptor.response, interceptor.responseError);
27497        }
27498      });
27499
27500      while (chain.length) {
27501        var thenFn = chain.shift();
27502        var rejectFn = chain.shift();
27503
27504        promise = promise.then(thenFn, rejectFn);
27505      }
27506
27507      if (useLegacyPromise) {
27508        promise.success = function(fn) {
27509          assertArgFn(fn, 'fn');
27510
27511          promise.then(function(response) {
27512            fn(response.data, response.status, response.headers, config);
27513          });
27514          return promise;
27515        };
27516
27517        promise.error = function(fn) {
27518          assertArgFn(fn, 'fn');
27519
27520          promise.then(null, function(response) {
27521            fn(response.data, response.status, response.headers, config);
27522          });
27523          return promise;
27524        };
27525      } else {
27526        promise.success = $httpMinErrLegacyFn('success');
27527        promise.error = $httpMinErrLegacyFn('error');
27528      }
27529
27530      return promise;
27531
27532      function transformResponse(response) {
27533        // make a copy since the response must be cacheable
27534        var resp = extend({}, response);
27535        if (!response.data) {
27536          resp.data = response.data;
27537        } else {
27538          resp.data = transformData(response.data, response.headers, response.status, config.transformResponse);
27539        }
27540        return (isSuccess(response.status))
27541          ? resp
27542          : $q.reject(resp);
27543      }
27544
27545      function executeHeaderFns(headers, config) {
27546        var headerContent, processedHeaders = {};
27547
27548        forEach(headers, function(headerFn, header) {
27549          if (isFunction(headerFn)) {
27550            headerContent = headerFn(config);
27551            if (headerContent != null) {
27552              processedHeaders[header] = headerContent;
27553            }
27554          } else {
27555            processedHeaders[header] = headerFn;
27556          }
27557        });
27558
27559        return processedHeaders;
27560      }
27561
27562      function mergeHeaders(config) {
27563        var defHeaders = defaults.headers,
27564            reqHeaders = extend({}, config.headers),
27565            defHeaderName, lowercaseDefHeaderName, reqHeaderName;
27566
27567        defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
27568
vendor: 9,098 bytes, lines 27569-27869
27569        // using for-in instead of forEach to avoid unecessary iteration after header has been found
27570        defaultHeadersIteration:
27571        for (defHeaderName in defHeaders) {
27572          lowercaseDefHeaderName = lowercase(defHeaderName);
27573
27574          for (reqHeaderName in reqHeaders) {
27575            if (lowercase(reqHeaderName) === lowercaseDefHeaderName) {
27576              continue defaultHeadersIteration;
27577            }
27578          }
27579
27580          reqHeaders[defHeaderName] = defHeaders[defHeaderName];
27581        }
27582
27583        // execute if header value is a function for merged headers
27584        return executeHeaderFns(reqHeaders, shallowCopy(config));
27585      }
27586    }
27587
27588    $http.pendingRequests = [];
27589
27590    /**
27591     * @ngdoc method
27592     * @name $http#get
27593     *
27594     * @description
27595     * Shortcut method to perform `GET` request.
27596     *
27597     * @param {string} url Relative or absolute URL specifying the destination of the request
27598     * @param {Object=} config Optional configuration object
27599     * @returns {HttpPromise} Future object
27600     */
27601
27602    /**
27603     * @ngdoc method
27604     * @name $http#delete
27605     *
27606     * @description
27607     * Shortcut method to perform `DELETE` request.
27608     *
27609     * @param {string} url Relative or absolute URL specifying the destination of the request
27610     * @param {Object=} config Optional configuration object
27611     * @returns {HttpPromise} Future object
27612     */
27613
27614    /**
27615     * @ngdoc method
27616     * @name $http#head
27617     *
27618     * @description
27619     * Shortcut method to perform `HEAD` request.
27620     *
27621     * @param {string} url Relative or absolute URL specifying the destination of the request
27622     * @param {Object=} config Optional configuration object
27623     * @returns {HttpPromise} Future object
27624     */
27625
27626    /**
27627     * @ngdoc method
27628     * @name $http#jsonp
27629     *
27630     * @description
27631     * Shortcut method to perform `JSONP` request.
27632     *
27633     * @param {string} url Relative or absolute URL specifying the destination of the request.
27634     *                     The name of the callback should be the string `JSON_CALLBACK`.
27635     * @param {Object=} config Optional configuration object
27636     * @returns {HttpPromise} Future object
27637     */
27638    createShortMethods('get', 'delete', 'head', 'jsonp');
27639
27640    /**
27641     * @ngdoc method
27642     * @name $http#post
27643     *
27644     * @description
27645     * Shortcut method to perform `POST` request.
27646     *
27647     * @param {string} url Relative or absolute URL specifying the destination of the request
27648     * @param {*} data Request content
27649     * @param {Object=} config Optional configuration object
27650     * @returns {HttpPromise} Future object
27651     */
27652
27653    /**
27654     * @ngdoc method
27655     * @name $http#put
27656     *
27657     * @description
27658     * Shortcut method to perform `PUT` request.
27659     *
27660     * @param {string} url Relative or absolute URL specifying the destination of the request
27661     * @param {*} data Request content
27662     * @param {Object=} config Optional configuration object
27663     * @returns {HttpPromise} Future object
27664     */
27665
27666     /**
27667      * @ngdoc method
27668      * @name $http#patch
27669      *
27670      * @description
27671      * Shortcut method to perform `PATCH` request.
27672      *
27673      * @param {string} url Relative or absolute URL specifying the destination of the request
27674      * @param {*} data Request content
27675      * @param {Object=} config Optional configuration object
27676      * @returns {HttpPromise} Future object
27677      */
27678    createShortMethodsWithData('post', 'put', 'patch');
27679
27680        /**
27681         * @ngdoc property
27682         * @name $http#defaults
27683         *
27684         * @description
27685         * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
27686         * default headers, withCredentials as well as request and response transformations.
27687         *
27688         * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
27689         */
27690    $http.defaults = defaults;
27691
27692
27693    return $http;
27694
27695
27696    function createShortMethods(names) {
27697      forEach(arguments, function(name) {
27698        $http[name] = function(url, config) {
27699          return $http(extend({}, config || {}, {
27700            method: name,
27701            url: url
27702          }));
27703        };
27704      });
27705    }
27706
27707
27708    function createShortMethodsWithData(name) {
27709      forEach(arguments, function(name) {
27710        $http[name] = function(url, data, config) {
27711          return $http(extend({}, config || {}, {
27712            method: name,
27713            url: url,
27714            data: data
27715          }));
27716        };
27717      });
27718    }
27719
27720
27721    /**
27722     * Makes the request.
27723     *
27724     * !!! ACCESSES CLOSURE VARS:
27725     * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
27726     */
27727    function sendReq(config, reqData) {
27728      var deferred = $q.defer(),
27729          promise = deferred.promise,
27730          cache,
27731          cachedResp,
27732          reqHeaders = config.headers,
27733          url = buildUrl(config.url, config.paramSerializer(config.params));
27734
27735      $http.pendingRequests.push(config);
27736      promise.then(removePendingReq, removePendingReq);
27737
27738
27739      if ((config.cache || defaults.cache) && config.cache !== false &&
27740          (config.method === 'GET' || config.method === 'JSONP')) {
27741        cache = isObject(config.cache) ? config.cache
27742              : isObject(defaults.cache) ? defaults.cache
27743              : defaultCache;
27744      }
27745
27746      if (cache) {
27747        cachedResp = cache.get(url);
27748        if (isDefined(cachedResp)) {
27749          if (isPromiseLike(cachedResp)) {
27750            // cached request has already been sent, but there is no response yet
27751            cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult);
27752          } else {
27753            // serving from cache
27754            if (isArray(cachedResp)) {
27755              resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
27756            } else {
27757              resolvePromise(cachedResp, 200, {}, 'OK');
27758            }
27759          }
27760        } else {
27761          // put the promise for the non-transformed response into cache as a placeholder
27762          cache.put(url, promise);
27763        }
27764      }
27765
27766
27767      // if we won't have the response in cache, set the xsrf headers and
27768      // send the request to the backend
27769      if (isUndefined(cachedResp)) {
27770        var xsrfValue = urlIsSameOrigin(config.url)
27771            ? $$cookieReader()[config.xsrfCookieName || defaults.xsrfCookieName]
27772            : undefined;
27773        if (xsrfValue) {
27774          reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
27775        }
27776
27777        $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
27778            config.withCredentials, config.responseType);
27779      }
27780
27781      return promise;
27782
27783
27784      /**
27785       * Callback registered to $httpBackend():
27786       *  - caches the response if desired
27787       *  - resolves the raw $http promise
27788       *  - calls $apply
27789       */
27790      function done(status, response, headersString, statusText) {
27791        if (cache) {
27792          if (isSuccess(status)) {
27793            cache.put(url, [status, response, parseHeaders(headersString), statusText]);
27794          } else {
27795            // remove promise from the cache
27796            cache.remove(url);
27797          }
27798        }
27799
27800        function resolveHttpPromise() {
27801          resolvePromise(response, status, headersString, statusText);
27802        }
27803
27804        if (useApplyAsync) {
27805          $rootScope.$applyAsync(resolveHttpPromise);
27806        } else {
27807          resolveHttpPromise();
27808          if (!$rootScope.$$phase) $rootScope.$apply();
27809        }
27810      }
27811
27812
27813      /**
27814       * Resolves the raw $http promise.
27815       */
27816      function resolvePromise(response, status, headers, statusText) {
27817        //status: HTTP response status code, 0, -1 (aborted by timeout / promise)
27818        status = status >= -1 ? status : 0;
27819
27820        (isSuccess(status) ? deferred.resolve : deferred.reject)({
27821          data: response,
27822          status: status,
27823          headers: headersGetter(headers),
27824          config: config,
27825          statusText: statusText
27826        });
27827      }
27828
27829      function resolvePromiseWithResult(result) {
27830        resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText);
27831      }
27832
27833      function removePendingReq() {
27834        var idx = $http.pendingRequests.indexOf(config);
27835        if (idx !== -1) $http.pendingRequests.splice(idx, 1);
27836      }
27837    }
27838
27839
27840    function buildUrl(url, serializedParams) {
27841      if (serializedParams.length > 0) {
27842        url += ((url.indexOf('?') == -1) ? '?' : '&') + serializedParams;
27843      }
27844      return url;
27845    }
27846  }];
27847}
27848
27849/**
27850 * @ngdoc service
27851 * @name $xhrFactory
27852 *
27853 * @description
27854 * Factory function used to create XMLHttpRequest objects.
27855 *
27856 * Replace or decorate this service to create your own custom XMLHttpRequest objects.
27857 *
27858 * ```
27859 * angular.module('myApp', [])
27860 * .factory('$xhrFactory', function() {
27861 *   return function createXhr(method, url) {
27862 *     return new window.XMLHttpRequest({mozSystem: true});
27863 *   };
27864 * });
27865 * ```
27866 *
27867 * @param {string} method HTTP method of the request (GET, POST, PUT, ..)
27868 * @param {string} url URL of the request.
27869 */
27870function $xhrFactoryProvider() {
27871  this.$get = function() {
27872    return function createXhr() {
27873      return new window.XMLHttpRequest();
27874    };
27875  };
27876}
27877
27878/**
27879 * @ngdoc service
27880 * @name $httpBackend
27881 * @requires $window
27882 * @requires $document
27883 * @requires $xhrFactory
27884 *
27885 * @description
27886 * HTTP backend used by the {@link ng.$http service} that delegates to
27887 * XMLHttpRequest object or JSONP and deals with browser incompatibilities.
27888 *
27889 * You should never need to use this service directly, instead use the higher-level abstractions:
27890 * {@link ng.$http $http} or {@link ngResource.$resource $resource}.
27891 *
27892 * During testing this implementation is swapped with {@link ngMock.$httpBackend mock
27893 * $httpBackend} which can be trained with responses.
27894 */
27895function $HttpBackendProvider() {
27896  this.$get = ['$browser', '$window', '$document', '$xhrFactory', function($browser, $window, $document, $xhrFactory) {
27897    return createHttpBackend($browser, $xhrFactory, $browser.defer, $window.angular.callbacks, $document[0]);
27898  }];
27899}
27900
27901function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
27902  // TODO(vojta): fix the signature
27903  return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
27904    $browser.$$incOutstandingRequestCount();
27905    url = url || $browser.url();
27906
27907    if (lowercase(method) == 'jsonp') {
27908      var callbackId = '_' + (callbacks.counter++).toString(36);
27909      callbacks[callbackId] = function(data) {
27910        callbacks[callbackId].data = data;
27911        callbacks[callbackId].called = true;
27912      };
27913
27914      var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
27915          callbackId, function(status, text) {
27916        completeRequest(callback, status, callbacks[callbackId].data, "", text);
27917        callbacks[callbackId] = noop;
27918      });
27919    } else {
27920
27921      var xhr = createXhr(method, url);
27922
27923      xhr.open(method, url, true);
27924      forEach(headers, function(value, key) {
27925        if (isDefined(value)) {
27926            xhr.setRequestHeader(key, value);
27927        }
27928      });
27929
27930      xhr.onload = function requestLoaded() {
27931        var statusText = xhr.statusText || '';
27932
27933        // responseText is the old-school way of retrieving response (supported by IE9)
27934        // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
27935        var response = ('response' in xhr) ? xhr.response : xhr.responseText;
27936
27937        // normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
27938        var status = xhr.status === 1223 ? 204 : xhr.status;
27939
27940        // fix status code when it is 0 (0 status is undocumented).
27941        // Occurs when accessing file resources or on Android 4.1 stock browser
27942        // while retrieving files from application cache.
27943        if (status === 0) {
27944          status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
27945        }
27946
27947        completeRequest(callback,
27948            status,
27949            response,
27950            xhr.getAllResponseHeaders(),
27951            statusText);
27952      };
27953
27954      var requestError = function() {
27955        // The response is always empty
27956        // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
27957        completeRequest(callback, -1, null, null, '');
27958      };
27959
27960      xhr.onerror = requestError;
27961      xhr.onabort = requestError;
27962
27963      if (withCredentials) {
27964        xhr.withCredentials = true;
27965      }
27966
27967      if (responseType) {
27968        try {
27969          xhr.responseType = responseType;
27970        } catch (e) {
27971          // WebKit added support for the json responseType value on 09/03/2013
27972          // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
27973          // known to throw when setting the value "json" as the response type. Other older
27974          // browsers implementing the responseType
27975          //
27976          // The json response type can be ignored if not supported, because JSON payloads are
27977          // parsed on the client-side regardless.
27978          if (responseType !== 'json') {
27979            throw e;
27980          }
27981        }
27982      }
27983
27984      xhr.send(isUndefined(post) ? null : post);
27985    }
27986
27987    if (timeout > 0) {
27988      var timeoutId = $browserDefer(timeoutRequest, timeout);
27989    } else if (isPromiseLike(timeout)) {
27990      timeout.then(timeoutRequest);
27991    }
27992
27993
27994    function timeoutRequest() {
27995      jsonpDone && jsonpDone();
27996      xhr && xhr.abort();
27997    }
27998
27999    function completeRequest(callback, status, response, headersString, statusText) {
28000      // cancel timeout and subsequent timeout promise resolution
28001      if (isDefined(timeoutId)) {
28002        $browserDefer.cancel(timeoutId);
28003      }
28004      jsonpDone = xhr = null;
28005
28006      callback(status, response, headersString, statusText);
28007      $browser.$$completeOutstandingRequest(noop);
28008    }
28009  };
28010
28011  function jsonpReq(url, callbackId, done) {
28012    // we can't use jQuery/jqLite here because jQuery does crazy stuff with script elements, e.g.:
28013    // - fetches local scripts via XHR and evals them
28014    // - adds and immediately removes script elements from the document
28015    var script = rawDocument.createElement('script'), callback = null;
28016    script.type = "text/javascript";
28017    script.src = url;
28018    script.async = true;
28019
28020    callback = function(event) {
28021      removeEventListenerFn(script, "load", callback);
28022      removeEventListenerFn(script, "error", callback);
28023      rawDocument.body.removeChild(script);
28024      script = null;
28025      var status = -1;
28026      var text = "unknown";
28027
28028      if (event) {
28029        if (event.type === "load" && !callbacks[callbackId].called) {
28030          event = { type: "error" };
28031        }
28032        text = event.type;
28033        status = event.type === "error" ? 404 : 200;
28034      }
28035
28036      if (done) {
28037        done(status, text);
28038      }
28039    };
28040
28041    addEventListenerFn(script, "load", callback);
28042    addEventListenerFn(script, "error", callback);
28043    rawDocument.body.appendChild(script);
28044    return callback;
28045  }
28046}
28047
28048var $interpolateMinErr = angular.$interpolateMinErr = minErr('$interpolate');
28049$interpolateMinErr.throwNoconcat = function(text) {
28050  throw $interpolateMinErr('noconcat',
28051      "Error while interpolating: {0}\nStrict Contextual Escaping disallows " +
28052      "interpolations that concatenate multiple expressions when a trusted value is " +
28053      "required.  See http://docs.angularjs.org/api/ng.$sce", text);
28054};
28055
28056$interpolateMinErr.interr = function(text, err) {
28057  return $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, err.toString());
28058};
28059
28060/**
28061 * @ngdoc provider
28062 * @name $interpolateProvider
28063 *
28064 * @description
28065 *
28066 * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
28067 *
28068 * @example
28069<example module="customInterpolationApp">
28070<file name="index.html">
28071<script>
28072  var customInterpolationApp = angular.module('customInterpolationApp', []);
28073
28074  customInterpolationApp.config(function($interpolateProvider) {
28075    $interpolateProvider.startSymbol('//');
28076    $interpolateProvider.endSymbol('//');
28077  });
28078
28079
28080  customInterpolationApp.controller('DemoController', function() {
28081      this.label = "This binding is brought you by // interpolation symbols.";
28082  });
28083</script>
28084<div ng-app="App" ng-controller="DemoController as demo">
28085    //demo.label//
28086</div>
28087</file>
28088<file name="protractor.js" type="protractor">
28089  it('should interpolate binding with custom symbols', function() {
28090    expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
28091  });
28092</file>
28093</example>
28094 */
28095function $InterpolateProvider() {
28096  var startSymbol = '{{';
28097  var endSymbol = '}}';
28098
28099  /**
28100   * @ngdoc method
28101   * @name $interpolateProvider#startSymbol
28102   * @description
28103   * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
28104   *
28105   * @param {string=} value new value to set the starting symbol to.
28106   * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
28107   */
28108  this.startSymbol = function(value) {
28109    if (value) {
28110      startSymbol = value;
28111      return this;
28112    } else {
28113      return startSymbol;
28114    }
28115  };
28116
28117  /**
28118   * @ngdoc method
28119   * @name $interpolateProvider#endSymbol
28120   * @description
28121   * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
28122   *
28123   * @param {string=} value new value to set the ending symbol to.
28124   * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
28125   */
28126  this.endSymbol = function(value) {
28127    if (value) {
28128      endSymbol = value;
28129      return this;
28130    } else {
28131      return endSymbol;
28132    }
28133  };
28134
28135
28136  this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
28137    var startSymbolLength = startSymbol.length,
28138        endSymbolLength = endSymbol.length,
28139        escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'),
28140        escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g');
28141
28142    function escape(ch) {
28143      return '\\\\\\' + ch;
28144    }
28145
28146    function unescapeText(text) {
28147      return text.replace(escapedStartRegexp, startSymbol).
28148        replace(escapedEndRegexp, endSymbol);
28149    }
28150
28151    function stringify(value) {
28152      if (value == null) { // null || undefined
28153        return '';
28154      }
28155      switch (typeof value) {
28156        case 'string':
28157          break;
28158        case 'number':
28159          value = '' + value;
28160          break;
28161        default:
28162          value = toJson(value);
28163      }
28164
28165      return value;
28166    }
28167
28168    /**
28169     * @ngdoc service
28170     * @name $interpolate
28171     * @kind function
28172     *
28173     * @requires $parse
28174     * @requires $sce
28175     *
28176     * @description
28177     *
28178     * Compiles a string with markup into an interpolation function. This service is used by the
28179     * HTML {@link ng.$compile $compile} service for data binding. See
28180     * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
28181     * interpolation markup.
28182     *
28183     *
28184     * ```js
28185     *   var $interpolate = ...; // injected
28186     *   var exp = $interpolate('Hello {{name | uppercase}}!');
28187     *   expect(exp({name:'Angular'})).toEqual('Hello ANGULAR!');
28188     * ```
28189     *
28190     * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
28191     * `true`, the interpolation function will return `undefined` unless all embedded expressions
28192     * evaluate to a value other than `undefined`.
28193     *
28194     * ```js
28195     *   var $interpolate = ...; // injected
28196     *   var context = {greeting: 'Hello', name: undefined };
28197     *
28198     *   // default "forgiving" mode
28199     *   var exp = $interpolate('{{greeting}} {{name}}!');
28200     *   expect(exp(context)).toEqual('Hello !');
28201     *
28202     *   // "allOrNothing" mode
28203     *   exp = $interpolate('{{greeting}} {{name}}!', false, null, true);
28204     *   expect(exp(context)).toBeUndefined();
28205     *   context.name = 'Angular';
28206     *   expect(exp(context)).toEqual('Hello Angular!');
28207     * ```
28208     *
28209     * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
28210     *
28211     * ####Escaped Interpolation
28212     * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
28213     * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
28214     * It will be rendered as a regular start/end marker, and will not be interpreted as an expression
28215     * or binding.
28216     *
28217     * This enables web-servers to prevent script injection attacks and defacing attacks, to some
28218     * degree, while also enabling code examples to work without relying on the
28219     * {@link ng.directive:ngNonBindable ngNonBindable} directive.
28220     *
28221     * **For security purposes, it is strongly encouraged that web servers escape user-supplied data,
28222     * replacing angle brackets (&lt;, &gt;) with &amp;lt; and &amp;gt; respectively, and replacing all
28223     * interpolation start/end markers with their escaped counterparts.**
28224     *
28225     * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered
28226     * output when the $interpolate service processes the text. So, for HTML elements interpolated
28227     * by {@link ng.$compile $compile}
28227, or otherwise interpolated with the `mustHaveExpression` parameter
28228     * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such,
28229     * this is typically useful only when user-data is used in rendering a template from the server, or
28230     * when otherwise untrusted data is used by a directive.
28231     *
28232     * <example>
28233     *  <file name="index.html">
28234     *    <div ng-init="username='A user'">
28235     *      <p ng-init="apptitle='Escaping demo'">{{apptitle}}: \{\{ username = "defaced value"; \}\}
28236     *        </p>
28237     *      <p><strong>{{username}}</strong> attempts to inject code which will deface the
28238     *        application, but fails to accomplish their task, because the server has correctly
28239     *        escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash)
28240     *        characters.</p>
28241     *      <p>Instead, the result of the attempted script injection is visible, and can be removed
28242     *        from the database by an administrator.</p>
28243     *    </div>
28244     *  </file>
28245     * </example>
28246     *
28247     * @param {string} text The text with markup to interpolate.
28248     * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
28249     *    embedded expression in order to return an interpolation function. Strings with no
28250     *    embedded expression will return null for the interpolation function.
28251     * @param {string=} trustedContext when provided, the returned function passes the interpolated
28252     *    result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
28253     *    trustedContext)} before returning it.  Refer to the {@link ng.$sce $sce} service that
28254     *    provides Strict Contextual Escaping for details.
28255     * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined
28256     *    unless all embedded expressions evaluate to a value other than `undefined`.
28257     * @returns {function(context)} an interpolation function which is used to compute the
28258     *    interpolated string. The function has these parameters:
28259     *
28260     * - `context`: evaluation context for all expressions embedded in the interpolated text
28261     */
28262    function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
28263      allOrNothing = !!allOrNothing;
28264      var startIndex,
28265          endIndex,
28266          index = 0,
28267          expressions = [],
28268          parseFns = [],
28269          textLength = text.length,
28270          exp,
28271          concat = [],
28272          expressionPositions = [];
28273
28274      while (index < textLength) {
28275        if (((startIndex = text.indexOf(startSymbol, index)) != -1) &&
28276             ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) {
28277          if (index !== startIndex) {
28278            concat.push(unescapeText(text.substring(index, startIndex)));
28279          }
28280          exp = text.substring(startIndex + startSymbolLength, endIndex);
28281          expressions.push(exp);
28282          parseFns.push($parse(exp, parseStringifyInterceptor));
28283          index = endIndex + endSymbolLength;
28284          expressionPositions.push(concat.length);
28285          concat.push('');
28286        } else {
28287          // we did not find an interpolation, so we have to add the remainder to the separators array
28288          if (index !== textLength) {
28289            concat.push(unescapeText(text.substring(index)));
28290          }
28291          break;
28292        }
28293      }
28294
28295      // Concatenating expressions makes it hard to reason about whether some combination of
28296      // concatenated values are unsafe to use and could easily lead to XSS.  By requiring that a
28297      // single expression be used for iframe[src], object[src], etc., we ensure that the value
28298      // that's used is assigned or constructed by some JS code somewhere that is more testable or
28299      // make it obvious that you bound the value to some user controlled value.  This helps reduce
28300      // the load when auditing for XSS issues.
28301      if (trustedContext && concat.length > 1) {
28302          $interpolateMinErr.throwNoconcat(text);
28303      }
28304
28305      if (!mustHaveExpression || expressions.length) {
28306        var compute = function(values) {
28307          for (var i = 0, ii = expressions.length; i < ii; i++) {
28308            if (allOrNothing && isUndefined(values[i])) return;
28309            concat[expressionPositions[i]] = values[i];
28310          }
28311          return concat.join('');
28312        };
28313
28314        var getValue = function(value) {
28315          return trustedContext ?
28316            $sce.getTrusted(trustedContext, value) :
28317            $sce.valueOf(value);
28318        };
28319
28320        return extend(function interpolationFn(context) {
28321            var i = 0;
28322            var ii = expressions.length;
28323            var values = new Array(ii);
28324
28325            try {
28326              for (; i < ii; i++) {
28327                values[i] = parseFns[i](context);
28328              }
28329
28330              return compute(values);
28331            } catch (err) {
28332              $exceptionHandler($interpolateMinErr.interr(text, err));
28333            }
28334
28335          }, {
28336          // all of these properties are undocumented for now
28337          exp: text, //just for compatibility with regular watchers created via $watch
28338          expressions: expressions,
28339          $$watchDelegate: function(scope, listener) {
28340            var lastValue;
28341            return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {
28342              var currValue = compute(values);
28343              if (isFunction(listener)) {
28344                listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
28345              }
28346              lastValue = currValue;
28347            });
28348          }
28349        });
28350      }
28351
28352      function parseStringifyInterceptor(value) {
28353        try {
28354          value = getValue(value);
28355          return allOrNothing && !isDefined(value) ? value : stringify(value);
28356        } catch (err) {
28357          $exceptionHandler($interpolateMinErr.interr(text, err));
28358        }
28359      }
28360    }
28361
28362
28363    /**
28364     * @ngdoc method
28365     * @name $interpolate#startSymbol
28366     * @description
28367     * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
28368     *
28369     * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
28370     * the symbol.
28371     *
28372     * @returns {string} start symbol.
28373     */
28374    $interpolate.startSymbol = function() {
28375      return startSymbol;
28376    };
28377
28378
28379    /**
28380     * @ngdoc method
28381     * @name $interpolate#endSymbol
28382     * @description
28383     * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
28384     *
28385     * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
28386     * the symbol.
28387     *
28388     * @returns {string} end symbol.
28389     */
28390    $interpolate.endSymbol = function() {
28391      return endSymbol;
28392    };
28393
28394    return $interpolate;
28395  }];
28396}
28397
28398function $IntervalProvider() {
28399  this.$get = ['$rootScope', '$window', '$q', '$$q',
28400       function($rootScope,   $window,   $q,   $$q) {
28401    var intervals = {};
28402
28403
28404     /**
28405      * @ngdoc service
28406      * @name $interval
28407      *
28408      * @description
28409      * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
28410      * milliseconds.
28411      *
28412      * The return value of registering an interval function is a promise. This promise will be
28413      * notified upon each tick of the interval, and will be resolved after `count` iterations, or
28414      * run indefinitely if `count` is not defined. The value of the notification will be the
28415      * number of iterations that have run.
28416      * To cancel an interval, call `$interval.cancel(promise)`.
28417      *
28418      * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
28419      * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
28420      * time.
28421      *
28422      * <div class="alert alert-warning">
28423      * **Note**: Intervals created by this service must be explicitly destroyed when you are finished
28424      * with them.  In particular they are not automatically destroyed when a controller's scope or a
28425      * directive's element are destroyed.
28426      * You should take this into consideration and make sure to always cancel the interval at the
28427      * appropriate moment.  See the example below for more details on how and when to do this.
28428      * </div>
28429      *
28430      * @param {function()} fn A function that should be called repeatedly.
28431      * @param {number} delay Number of milliseconds between each function call.
28432      * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
28433      *   indefinitely.
28434      * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
28435      *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
28436      * @param {...*=} Pass additional parameters to the executed function.
28437      * @returns {promise} A promise which will be notified on each iteration.
28438      *
28439      * @example
28440      * <example module="intervalExample">
28441      * <file name="index.html">
28442      *   <script>
28443      *     angular.module('intervalExample', [])
28444      *       .controller('ExampleController', ['$scope', '$interval',
28445      *         function($scope, $interval) {
28446      *           $scope.format = 'M/d/yy h:mm:ss a';
28447      *           $scope.blood_1 = 100;
28448      *           $scope.blood_2 = 120;
28449      *
28450      *           var stop;
28451      *           $scope.fight = function() {
28452      *             // Don't start a new fight if we are already fighting
28453      *             if ( angular.isDefined(stop) ) return;
28454      *
28455      *             stop = $interval(function() {
28456      *               if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
28457      *                 $scope.blood_1 = $scope.blood_1 - 3;
28458      *                 $scope.blood_2 = $scope.blood_2 - 4;
28459      *               } else {
28460      *                 $scope.stopFight();
28461      *               }
28462      *             }, 100);
28463      *           };
28464      *
28465      *           $scope.stopFight = function() {
28466      *             if (angular.isDefined(stop)) {
28467      *               $interval.cancel(stop);
28468      *               stop = undefined;
28469      *             }
28470      *           };
28471      *
28472      *           $scope.resetFight = function() {
28473      *             $scope.blood_1 = 100;
28474      *             $scope.blood_2 = 120;
28475      *           };
28476      *
28477      *           $scope.$on('$destroy', function() {
28478      *             // Make sure that the interval is destroyed too
28479      *             $scope.stopFight();
28480      *           });
28481      *         }])
28482      *       // Register the 'myCurrentTime' directive factory method.
28483      *       // We inject $interval and dateFilter service since the factory method is DI.
28484      *       .directive('myCurrentTime', ['$interval', 'dateFilter',
28485      *         function($interval, dateFilter) {
28486      *           // return the directive link function. (compile function not needed)
28487      *           return function(scope, element, attrs) {
28488      *             var format,  // date format
28489      *                 stopTime; // so that we can cancel the time updates
28490      *
28491      *             // used to update the UI
28492      *             function updateTime() {
28493      *               element.text(dateFilter(new Date(), format));
28494      *             }
28495      *
28496      *             // watch the expression, and update the UI on change.
28497      *             scope.$watch(attrs.myCurrentTime, function(value) {
28498      *               format = value;
28499      *               updateTime();
28500      *             });
28501      *
28502      *             stopTime = $interval(updateTime, 1000);
28503      *
28504      *             // listen on DOM destroy (removal) event, and cancel the next UI update
28505      *             // to prevent updating time after the DOM element was removed.
28506      *             element.on('$destroy', function() {
28507      *               $interval.cancel(stopTime);
28508      *             });
28509      *           }
28510      *         }]);
28511      *   </script>
28512      *
28513      *   <div>
28514      *     <div ng-controller="ExampleController">
28515      *       <label>Date format: <input ng-model="format"></label> <hr/>
28516      *       Current time is: <span my-current-time="format"></span>
28517      *       <hr/>
28518      *       Blood 1 : <font color='red'>{{blood_1}}</font>
28519      *       Blood 2 : <font color='red'>{{blood_2}}</font>
28520      *       <button type="button" data-ng-click="fight()">Fight</button>
28521      *       <button type="button" data-ng-click="stopFight()">StopFight</button>
28522      *       <button type="button" data-ng-click="resetFight()">resetFight</button>
28523      *     </div>
28524      *   </div>
28525      *
28526      * </file>
28527      * </example>
28528      */
28529    function interval(fn, delay, count, invokeApply) {
28530      var hasParams = arguments.length > 4,
28531          args = hasParams ? sliceArgs(arguments, 4) : [],
28532          setInterval = $window.setInterval,
28533          clearInterval = $window.clearInterval,
28534          iteration = 0,
28535          skipApply = (isDefined(invokeApply) && !invokeApply),
28536          deferred = (skipApply ? $$q : $q).defer(),
28537          promise = deferred.promise;
28538
28539      count = isDefined(count) ? count : 0;
28540
28541      promise.then(null, null, (!hasParams) ? fn : function() {
28542        fn.apply(null, args);
28543      });
28544
28545      promise.$$intervalId = setInterval(function tick() {
28546        deferred.notify(iteration++);
28547
28548        if (count > 0 && iteration >= count) {
28549          deferred.resolve(iteration);
28550          clearInterval(promise.$$intervalId);
28551          delete intervals[promise.$$intervalId];
28552        }
28553
28554        if (!skipApply) $rootScope.$apply();
28555
28556      }, delay);
28557
28558      intervals[promise.$$intervalId] = deferred;
28559
28560      return promise;
28561    }
28562
28563
28564     /**
28565      * @ngdoc method
28566      * @name $interval#cancel
28567      *
28568      * @description
28569      * Cancels a task associated with the `promise`.
28570      *
28571      * @param {Promise=} promise returned by the `$interval` function.
28572      * @returns {boolean} Returns `true` if the task was successfully canceled.
28573      */
28574    interval.cancel = function(promise) {
28575      if (promise && promise.$$intervalId in intervals) {
28576        intervals[promise.$$intervalId].reject('canceled');
28577        $window.clearInterval(promise.$$intervalId);
28578        delete intervals[promise.$$intervalId];
28579        return true;
28580      }
28581      return false;
28582    };
28583
28584    return interval;
28585  }];
28586}
28587
28588/**
28589 * @ngdoc service
28590 * @name $locale
28591 *
28592 * @description
28593 * $locale service provides localization rules for various Angular components. As of right now the
28594 * only public api is:
28595 *
28596 * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
28597 */
28598
28599var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/,
28600    DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
28601var $locationMinErr = minErr('$location');
28602
28603
28604/**
28605 * Encode path using encodeUriSegment, ignoring forward slashes
28606 *
28607 * @param {string} path Path to encode
28608 * @returns {string}
28609 */
28610function encodePath(path) {
28611  var segments = path.split('/'),
28612      i = segments.length;
28613
28614  while (i--) {
28615    segments[i] = encodeUriSegment(segments[i]);
28616  }
28617
28618  return segments.join('/');
28619}
28620
28621function parseAbsoluteUrl(absoluteUrl, locationObj) {
28622  var parsedUrl = urlResolve(absoluteUrl);
28623
28624  locationObj.$$protocol = parsedUrl.protocol;
28625  locationObj.$$host = parsedUrl.hostname;
28626  locationObj.$$port = toInt(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
28627}
28628
28629
28630function parseAppUrl(relativeUrl, locationObj) {
28631  var prefixed = (relativeUrl.charAt(0) !== '/');
28632  if (prefixed) {
28633    relativeUrl = '/' + relativeUrl;
28634  }
28635  var match = urlResolve(relativeUrl);
28636  locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
28637      match.pathname.substring(1) : match.pathname);
28638  locationObj.$$search = parseKeyValue(match.search);
28639  locationObj.$$hash = decodeURIComponent(match.hash);
28640
28641  // make sure path starts with '/';
28642  if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') {
28643    locationObj.$$path = '/' + locationObj.$$path;
28644  }
28645}
28646
28647
28648/**
28649 *
28650 * @param {string} begin
28651 * @param {string} whole
28652 * @returns {string} returns text from whole after begin or undefined if it does not begin with
28653 *                   expected string.
28654 */
28655function beginsWith(begin, whole) {
28656  if (whole.indexOf(begin) === 0) {
28657    return whole.substr(begin.length);
28658  }
28659}
28660
28661
28662function stripHash(url) {
28663  var index = url.indexOf('#');
28664  return index == -1 ? url : url.substr(0, index);
28665}
28666
28667function trimEmptyHash(url) {
28668  return url.replace(/(#.+)|#$/, '$1');
28669}
28670
28671
28672function stripFile(url) {
28673  return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
28674}
28675
28676/* return the server only (scheme://host:port) */
28677function serverBase(url) {
28678  return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
28679}
28680
28681
28682/**
28683 * LocationHtml5Url represents an url
28684 * This object is exposed as $location service when HTML5 mode is enabled and supported
28685 *
28686 * @constructor
28687 * @param {string} appBase application base URL
28688 * @param {string} appBaseNoFile application base URL stripped of any filename
28689 * @param {string} basePrefix url path prefix
28690 */
28691function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
28692  this.$$html5 = true;
28693  basePrefix = basePrefix || '';
28694  parseAbsoluteUrl(appBase, this);
28695
28696
28697  /**
28698   * Parse given html5 (regular) url string into properties
28699   * @param {string} url HTML5 url
28700   * @private
28701   */
28702  this.$$parse = function(url) {
28703    var pathUrl = beginsWith(appBaseNoFile, url);
28704    if (!isString(pathUrl)) {
28705      throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url,
28706          appBaseNoFile);
28707    }
28708
28709    parseAppUrl(pathUrl, this);
28710
28711    if (!this.$$path) {
28712      this.$$path = '/';
28713    }
28714
28715    this.$$compose();
28716  };
28717
28718  /**
28719   * Compose url and update `absUrl` property
28720   * @private
28721   */
28722  this.$$compose = function() {
28723    var search = toKeyValue(this.$$search),
28724        hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
28725
28726    this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
28727    this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
28728  };
28729
28730  this.$$parseLinkUrl = function(url, relHref) {
28731    if (relHref && relHref[0] === '#') {
28732      // special case for links to hash fragments:
28733      // keep the old url and only replace the hash fragment
28734      this.hash(relHref.slice(1));
28735      return true;
28736    }
28737    var appUrl, prevAppUrl;
28738    var rewrittenUrl;
28739
28740    if (isDefined(appUrl = beginsWith(appBase, url))) {
28741      prevAppUrl = appUrl;
28742      if (isDefined(appUrl = beginsWith(basePrefix, appUrl))) {
28743        rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
28744      } else {
28745        rewrittenUrl = appBase + prevAppUrl;
28746      }
28747    } else if (isDefined(appUrl = beginsWith(appBaseNoFile, url))) {
28748      rewrittenUrl = appBaseNoFile + appUrl;
28749    } else if (appBaseNoFile == url + '/') {
28750      rewrittenUrl = appBaseNoFile;
28751    }
28752    if (rewrittenUrl) {
28753      this.$$parse(rewrittenUrl);
28754    }
28755    return !!rewrittenUrl;
28756  };
28757}
28758
28759
28760/**
28761 * LocationHashbangUrl represents url
28762 * This object is exposed as $location service when developer doesn't opt into html5 mode.
28763 * It also serves as the base class for html5 mode fallback on legacy browsers.
28764 *
28765 * @constructor
28766 * @param {string} appBase application base URL
28767 * @param {string} appBaseNoFile application base URL stripped of any filename
28768 * @param {string} hashPrefix hashbang prefix
28769 */
28770function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
28771
28772  parseAbsoluteUrl(appBase, this);
28773
28774
28775  /**
28776   * Parse given hashbang url into properties
28777   * @param {string} url Hashbang url
28778   * @private
28779   */
28780  this.$$parse = function(url) {
28781    var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
28782    var withoutHashUrl;
28783
28784    if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === '#') {
28785
28786      // The rest of the url starts with a hash so we have
28787      // got either a hashbang path or a plain hash fragment
28788      withoutHashUrl = beginsWith(hashPrefix, withoutBaseUrl);
28789      if (isUndefined(withoutHashUrl)) {
28790        // There was no hashbang prefix so we just have a hash fragment
28791        withoutHashUrl = withoutBaseUrl;
28792      }
28793
28794    } else {
28795      // There was no hashbang path nor hash fragment:
28796      // If we are in HTML5 mode we use what is left as the path;
28797      // Otherwise we ignore what is left
28798      if (this.$$html5) {
28799        withoutHashUrl = withoutBaseUrl;
28800      } else {
28801        withoutHashUrl = '';
28802        if (isUndefined(withoutBaseUrl)) {
28803          appBase = url;
28804          this.replace();
28805        }
28806      }
28807    }
28808
28809    parseAppUrl(withoutHashUrl, this);
28810
28811    this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
28812
28813    this.$$compose();
28814
28815    /*
28816     * In Windows, on an anchor node on documents loaded from
28817     * the filesystem, the browser will return a pathname
28818     * prefixed with the drive name ('/C:/path') when a
28819     * pathname without a drive is set:
28820     *  * a.setAttribute('href', '/foo')
28821     *   * a.pathname === '/C:/foo' //true
28822     *
28823     * Inside of Angular, we're always using pathnames that
28824     * do not include drive names for routing.
28825     */
28826    function removeWindowsDriveName(path, url, base) {
28827      /*
28828      Matches paths for file protocol on windows,
28829      such as /C:/foo/bar, and captures only /foo/bar.
28830      */
28831      var windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
28832
28833      var firstPathSegmentMatch;
28834
28835      //Get the relative path from the input URL.
28836      if (url.indexOf(base) === 0) {
28837        url = url.replace(base, '');
28838      }
28839
28840      // The input URL intentionally contains a first path segment that ends with a colon.
28841      if (windowsFilePathExp.exec(url)) {
28842        return path;
28843      }
28844
28845      firstPathSegmentMatch = windowsFilePathExp.exec(path);
28846      return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
28847    }
28848  };
28849
28850  /**
28851   * Compose hashbang url and update `absUrl` property
28852   * @private
28853   */
28854  this.$$compose = function() {
28855    var search = toKeyValue(this.$$search),
28856        hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
28857
28858    this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
28859    this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
28860  };
28861
28862  this.$$parseLinkUrl = function(url, relHref) {
28863    if (stripHash(appBase) == stripHash(url)) {
28864      this.$$parse(url);
28865      return true;
28866    }
28867    return false;
28868  };
28869}
28870
28871
28872/**
28873 * LocationHashbangUrl represents url
28874 * This object is exposed as $location service when html5 history api is enabled but the browser
28875 * does not support it.
28876 *
28877 * @constructor
28878 * @param {string} appBase application base URL
28879 * @param {string} appBaseNoFile application base URL stripped of any filename
28880 * @param {string} hashPrefix hashbang prefix
28881 */
28882function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
28883  this.$$html5 = true;
28884  LocationHashbangUrl.apply(this, arguments);
28885
28886  this.$$parseLinkUrl = function(url, relHref) {
28887    if (relHref && relHref[0] === '#') {
28888      // special case for links to hash fragments:
28889      // keep the old url and only replace the hash fragment
28890      this.hash(relHref.slice(1));
28891      return true;
28892    }
28893
28894    var rewrittenUrl;
28895    var appUrl;
28896
28897    if (appBase == stripHash(url)) {
28898      rewrittenUrl = url;
28899    } else if ((appUrl = beginsWith(appBaseNoFile, url))) {
28900      rewrittenUrl = appBase + hashPrefix + appUrl;
28901    } else if (appBaseNoFile === url + '/') {
28902      rewrittenUrl = appBaseNoFile;
28903    }
28904    if (rewrittenUrl) {
28905      this.$$parse(rewrittenUrl);
28906    }
28907    return !!rewrittenUrl;
28908  };
28909
28910  this.$$compose = function() {
28911    var search = toKeyValue(this.$$search),
28912        hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
28913
28914    this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
28915    // include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#'
28916    this.$$absUrl = appBase + hashPrefix + this.$$url;
28917  };
28918
28919}
28920
28921
28922var locationPrototype = {
28923
28924  /**
28925   * Are we in html5 mode?
28926   * @private
28927   */
28928  $$html5: false,
28929
28930  /**
28931   * Has any change been replacing?
28932   * @private
28933   */
28934  $$replace: false,
28935
28936  /**
28937   * @ngdoc method
28938   * @name $location#absUrl
28939   *
28940   * @description
28941   * This method is getter only.
28942   *
28943   * Return full url representation with all segments encoded according to rules specified in
28944   * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
28945   *
28946   *
28947   * ```js
28948   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
28949   * var absUrl = $location.absUrl();
28950   * // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
28951   * ```
28952   *
28953   * @return {string} full url
28954   */
28955  absUrl: locationGetter('$$absUrl'),
28956
28957  /**
28958   * @ngdoc method
28959   * @name $location#url
28960   *
28961   * @description
28962   * This method is getter / setter.
28963   *
28964   * Return url (e.g. `/path?a=b#hash`) when called without any parameter.
28965   *
28966   * Change path, search and hash, when called with parameter and return `$location`.
28967   *
28968   *
28969   * ```js
28970   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
28971   * var url = $location.url();
28972   * // => "/some/path?foo=bar&baz=xoxo"
28973   * ```
28974   *
28975   * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
28976   * @return {string} url
28977   */
28978  url: function(url) {
28979    if (isUndefined(url)) {
28980      return this.$$url;
28981    }
28982
28983    var match = PATH_MATCH.exec(url);
28984    if (match[1] || url === '') this.path(decodeURIComponent(match[1]));
28985    if (match[2] || match[1] || url === '') this.search(match[3] || '');
28986    this.hash(match[5] || '');
28987
28988    return this;
28989  },
28990
28991  /**
28992   * @ngdoc method
28993   * @name $location#protocol
28994   *
28995   * @description
28996   * This method is getter only.
28997   *
28998   * Return protocol of current url.
28999   *
29000   *
29001   * ```js
29002   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
29003   * var protocol = $location.protocol();
29004   * // => "http"
29005   * ```
29006   *
29007   * @return {string} protocol of current url
29008   */
29009  protocol: locationGetter('$$protocol'),
29010
29011  /**
29012   * @ngdoc method
29013   * @name $location#host
29014   *
29015   * @description
29016   * This method is getter only.
29017   *
29018   * Return host of current url.
29019   *
29020   * Note: compared to the non-angular version `location.host` which returns `hostname:port`, this returns the `hostname` portion only.
29021   *
29022   *
29023   * ```js
29024   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
29025   * var host = $location.host();
29026   * // => "example.com"
29027   *
29028   * // given url http://user:[email protected]:8080/#/some/path?foo=bar&baz=xoxo
29029   * host = $location.host();
29030   * // => "example.com"
29031   * host = location.host;
29032   * // => "example.com:8080"
29033   * ```
29034   *
29035   * @return {string} host of current url.
29036   */
29037  host: locationGetter('$$host'),
29038
29039  /**
29040   * @ngdoc method
29041   * @name $location#port
29042   *
29043   * @description
29044   * This method is getter only.
29045   *
29046   * Return port of current url.
29047   *
29048   *
29049   * ```js
29050   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
29051   * var port = $location.port();
29052   * // => 80
29053   * ```
29054   *
29055   * @return {Number} port
29056   */
29057  port: locationGetter('$$port'),
29058
29059  /**
29060   * @ngdoc method
29061   * @name $location#path
29062   *
29063   * @description
29064   * This method is getter / setter.
29065   *
29066   * Return path of current url when called without any parameter.
29067   *
29068   * Change path when called with parameter and return `$location`.
29069   *
29070   * Note: Path should always begin with forward slash (/), this method will add the forward slash
29071   * if it is missing.
29072   *
29073   *
29074   * ```js
29075   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
29076   * var path = $location.path();
29077   * // => "/some/path"
29078   * ```
29079   *
29080   * @param {(string|number)=} path New path
29081   * @return {string} path
29082   */
29083  path: locationGetterSetter('$$path', function(path) {
29084    path = path !== null ? path.toString() : '';
29085    return path.charAt(0) == '/' ? path : '/' + path;
29086  }),
29087
29088  /**
29089   * @ngdoc method
29090   * @name $location#search
29091   *
29092   * @description
29093   * This method is getter / setter.
29094   *
29095   * Return search part (as object) of current url when called without any parameter.
29096   *
29097   * Change search part when called with parameter and return `$location`.
29098   *
29099   *
29100   * ```js
29101   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
29102   * var searchObject = $location.search();
29103   * // => {foo: 'bar', baz: 'xoxo'}
29104   *
29105   * // set foo to 'yipee'
29106   * $location.search('foo', 'yipee');
29107   * // $location.search() => {foo: 'yipee', baz: 'xoxo'}
29108   * ```
29109   *
29110   * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
29111   * hash object.
29112   *
29113   * When called with a single argument the method acts as a setter, setting the `search` component
29114   * of `$location` to the specified value.
29115   *
29116   * If the argument is a hash object containing an array of values, these values will be encoded
29117   * as duplicate search parameters in the url.
29118   *
29119   * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`
29120   * will override only a single search property.
29121   *
29122   * If `paramValue` is an array, it will override the property of the `search` component of
29123   * `$location` specified via the first argument.
29124   *
29125   * If `paramValue` is `null`, the property specified via the first argument will be deleted.
29126   *
29127   * If `paramValue` is `true`, the property specified via the first argument will be added with no
29128   * value nor trailing equal sign.
29129   *
29130   * @return {Object} If called with no arguments returns the parsed `search` object. If called with
29131   * one or more arguments returns `$location` object itself.
29132   */
29133  search: function(search, paramValue) {
29134    switch (arguments.length) {
29135      case 0:
29136        return this.$$search;
29137      case 1:
29138        if (isString(search) || isNumber(search)) {
29139          search = search.toString();
29140          this.$$search = parseKeyValue(search);
29141        } else if (isObject(search)) {
29142          search = copy(search, {});
29143          // remove object undefined or null properties
29144          forEach(search, function(value, key) {
29145            if (value == null) delete search[key];
29146          });
29147
29148          this.$$search = search;
29149        } else {
29150          throw $locationMinErr('isrcharg',
29151              'The first argument of the `$location#search()` call must be a string or an object.');
29152        }
29153        break;
29154      default:
29155        if (isUndefined(paramValue) || paramValue === null) {
29156          delete this.$$search[search];
29157        } else {
29158          this.$$search[search] = paramValue;
29159        }
29160    }
29161
29162    this.$$compose();
29163    return this;
29164  },
29165
29166  /**
29167   * @ngdoc method
29168   * @name $location#hash
29169   *
29170   * @description
29171   * This method is getter / setter.
29172   *
29173   * Return hash fragment when called without any parameter.
29174   *
29175   * Change hash fragment when called with parameter and return `$location`.
29176   *
29177   *
29178   * ```js
29179   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue
29180   * var hash = $location.hash();
29181   * // => "hashValue"
29182   * ```
29183   *
29184   * @param {(string|number)=} hash New hash fragment
29185   * @return {string} hash
29186   */
29187  hash: locationGetterSetter('$$hash', function(hash) {
29188    return hash !== null ? hash.toString() : '';
29189  }),
29190
29191  /**
29192   * @ngdoc method
29193   * @name $location#replace
29194   *
29195   * @description
29196   * If called, all changes to $location during current `$digest` will be replacing current history
29197   * record, instead of adding new one.
29198   */
29199  replace: function() {
29200    this.$$replace = true;
29201    return this;
29202  }
29203};
29204
29205forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) {
29206  Location.prototype = Object.create(locationPrototype);
29207
29208  /**
29209   * @ngdoc method
29210   * @name $location#state
29211   *
29212   * @description
29213   * This method is getter / setter.
29214   *
29215   * Return the history state object when called without any parameter.
29216   *
29217   * Change the history state object when called with one parameter and return `$location`.
29218   * The state object is later passed to `pushState` or `replaceState`.
29219   *
29220   * NOTE: This method is supported only in HTML5 mode and only in browsers supporting
29221   * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support
29222   * older browsers (like IE9 or Android < 4.0), don't use this method.
29223   *
29224   * @param {object=} state State object for pushState or replaceState
29225   * @return {object} state
29226   */
29227  Location.prototype.state = function(state) {
29228    if (!arguments.length) {
29229      return this.$$state;
29230    }
29231
29232    if (Location !== LocationHtml5Url || !this.$$html5) {
29233      throw $locationMinErr('nostate', 'History API state support is available only ' +
29234        'in HTML5 mode and only in browsers supporting HTML5 History API');
29235    }
29236    // The user might modify `stateObject` after invoking `$location.state(stateObject)`
29237    // but we're changing the $$state reference to $browser.state() during the $digest
29238    // so the modification window is narrow.
29239    this.$$state = isUndefined(state) ? null : state;
29240
29241    return this;
29242  };
29243});
29244
29245
29246function locationGetter(property) {
29247  return function() {
29248    return this[property];
29249  };
29250}
29251
29252
29253function locationGetterSetter(property, preprocess) {
29254  return function(value) {
29255    if (isUndefined(value)) {
29256      return this[property];
29257    }
29258
29259    this[property] = preprocess(value);
29260    this.$$compose();
29261
29262    return this;
29263  };
29264}
29265
29266
29267/**
29268 * @ngdoc service
29269 * @name $location
29270 *
29271 * @requires $rootElement
29272 *
29273 * @description
29274 * The $location service parses the URL in the browser address bar (based on the
29275 * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL
29276 * available to your application. Changes to the URL in the address bar are reflected into
29277 * $location service and changes to $location are reflected into the browser address bar.
29278 *
29279 * **The $location service:**
29280 *
29281 * - Exposes the current URL in the browser address bar, so you can
29282 *   - Watch and observe the URL.
29283 *   - Change the URL.
29284 * - Synchronizes the URL with the browser when the user
29285 *   - Changes the address bar.
29286 *   - Clicks the back or forward button (or clicks a History link).
29287 *   - Clicks on a link.
29288 * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
29289 *
29290 * For more information see {@link guide/$location Developer Guide: Using $location}
29291 */
29292
29293/**
29294 * @ngdoc provider
29295 * @name $locationProvider
29296 * @description
29297 * Use the `$locationProvider` to configure how the application deep linking paths are stored.
29298 */
29299function $LocationProvider() {
29300  var hashPrefix = '',
29301      html5Mode = {
29302        enabled: false,
29303        requireBase: true,
29304        rewriteLinks: true
29305      };
29306
29307  /**
29308   * @ngdoc method
29309   * @name $locationProvider#hashPrefix
29310   * @description
29311   * @param {string=} prefix Prefix for hash part (containing path and search)
29312   * @returns {*} current value if used as getter or itself (chaining) if used as setter
29313   */
29314  this.hashPrefix = function(prefix) {
29315    if (isDefined(prefix)) {
29316      hashPrefix = prefix;
29317      return this;
29318    } else {
29319      return hashPrefix;
29320    }
29321  };
29322
29323  /**
29324   * @ngdoc method
29325   * @name $locationProvider#html5Mode
29326   * @description
29327   * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.
29328   *   If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported
29329   *   properties:
29330   *   - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to
29331   *     change urls where supported. Will fall back to hash-prefixed paths in browsers that do not
29332   *     support `pushState`.
29333   *   - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies
29334   *     whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are
29335   *     true, and a base tag is not present, an error will be thrown when `$location` is injected.
29336   *     See the {@link guide/$location $location guide for more information}
29337   *   - **rewriteLinks** - `{boolean}` - (default: `true`) When html5Mode is enabled,
29338   *     enables/disables url rewriting for relative links.
29339   *
29340   * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
29341   */
29342  this.html5Mode = function(mode) {
29343    if (isBoolean(mode)) {
29344      html5Mode.enabled = mode;
29345      return this;
29346    } else if (isObject(mode)) {
29347
29348      if (isBoolean(mode.enabled)) {
29349        html5Mode.enabled = mode.enabled;
29350      }
29351
29352      if (isBoolean(mode.requireBase)) {
29353        html5Mode.requireBase = mode.requireBase;
29354      }
29355
29356      if (isBoolean(mode.rewriteLinks)) {
29357        html5Mode.rewriteLinks = mode.rewriteLinks;
29358      }
29359
29360      return this;
29361    } else {
29362      return html5Mode;
29363    }
29364  };
29365
29366  /**
29367   * @ngdoc event
29368   * @name $location#$locationChangeStart
29369   * @eventType broadcast on root scope
29370   * @description
29371   * Broadcasted before a URL will change.
29372   *
29373   * This change can be prevented by calling
29374   * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
29375   * details about event object. Upon successful change
29376   * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired.
29377   *
29378   * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
29379   * the browser supports the HTML5 History API.
29380   *
29381   * @param {Object} angularEvent Synthetic event object.
29382   * @param {string} newUrl New URL
29383   * @param {string=} oldUrl URL that was before it was changed.
29384   * @param {string=} newState New history state object
29385   * @param {string=} oldState History state object that was before it was changed.
29386   */
29387
29388  /**
29389   * @ngdoc event
29390   * @name $location#$locationChangeSuccess
29391   * @eventType broadcast on root scope
29392   * @description
29393   * Broadcasted after a URL was changed.
29394   *
29395   * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
29396   * the browser supports the HTML5 History API.
29397   *
29398   * @param {Object} angularEvent Synthetic event object.
29399   * @param {string} newUrl New URL
29400   * @param {string=} oldUrl URL that was before it was changed.
29401   * @param {string=} newState New history state object
29402   * @param {string=} oldState History state object that was before it was changed.
29403   */
29404
29405  this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window',
29406      function($rootScope, $browser, $sniffer, $rootElement, $window) {
29407    var $location,
29408        LocationMode,
29409        baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
29410        initialUrl = $browser.url(),
29411        appBase;
29412
29413    if (html5Mode.enabled) {
29414      if (!baseHref && html5Mode.requireBase) {
29415        throw $locationMinErr('nobase',
29416          "$location in HTML5 mode requires a <base> tag to be present!");
29417      }
29418      appBase = serverBase(initialUrl) + (baseHref || '/');
29419      LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
29420    } else {
29421      appBase = stripHash(initialUrl);
29422      LocationMode = LocationHashbangUrl;
29423    }
29424    var appBaseNoFile = stripFile(appBase);
29425
29426    $location = new LocationMode(appBase, appBaseNoFile, '#' + hashPrefix);
29427    $location.$$parseLinkUrl(initialUrl, initialUrl);
29428
29429    $location.$$state = $browser.state();
29430
29431    var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
29432
29433    function setBrowserUrlWithFallback(url, replace, state) {
29434      var oldUrl = $location.url();
29435      var oldState = $location.$$state;
29436      try {
29437        $browser.url(url, replace, state);
29438
29439        // Make sure $location.state() returns referentially identical (not just deeply equal)
29440        // state object; this makes possible quick checking if the state changed in the digest
29441        // loop. Checking deep equality would be too expensive.
29442        $location.$$state = $browser.state();
29443      } catch (e) {
29444        // Restore old values if pushState fails
29445        $location.url(oldUrl);
29446        $location.$$state = oldState;
29447
29448        throw e;
29449      }
29450    }
29451
29452    $rootElement.on('click', function(event) {
29453      // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
29454      // currently we open nice url link and redirect then
29455
29456      if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which == 2 || event.button == 2) return;
29457
29458      var elm = jqLite(event.target);
29459
29460      // traverse the DOM up to find first A tag
29461      while (nodeName_(elm[0]) !== 'a') {
29462        // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
29463        if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
29464      }
29465
29466      var absHref = elm.prop('href');
29467      // get the actual href attribute - see
29468      // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
29469      var relHref = elm.attr('href') || elm.attr('xlink:href');
29470
29471      if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
29472        // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
29473        // an animation.
29474        absHref = urlResolve(absHref.animVal).href;
29475      }
29476
29477      // Ignore when url is started with javascript: or mailto:
29478      if (IGNORE_URI_REGEXP.test(absHref)) return;
29479
29480      if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
29481        if ($location.$$parseLinkUrl(absHref, relHref)) {
29482          // We do a preventDefault for all urls that are part of the angular application,
29483          // in html5mode and also without, so that we are able to abort navigation without
29484          // getting double entries in the location history.
29485          event.preventDefault();
29486          // update location manually
29487          if ($location.absUrl() != $browser.url()) {
29488            $rootScope.$apply();
29489            // hack to work around FF6 bug 684208 when scenario runner clicks on links
29490            $window.angular['ff-684208-preventDefault'] = true;
29491          }
29492        }
29493      }
29494    });
29495
29496
29497    // rewrite hashbang url <> html5 url
29498    if (trimEmptyHash($location.absUrl()) != trimEmptyHash(initialUrl)) {
29499      $browser.url($location.absUrl(), true);
29500    }
29501
29502    var initializing = true;
29503
29504    // update $location when $browser url changes
29505    $browser.onUrlChange(function(newUrl, newState) {
29506
29507      if (isUndefined(beginsWith(appBaseNoFile, newUrl))) {
29508        // If we are navigating outside of the app then force a reload
29509        $window.location.href = newUrl;
29510        return;
29511      }
29512
29513      $rootScope.$evalAsync(function() {
29514        var oldUrl = $location.absUrl();
29515        var oldState = $location.$$state;
29516        var defaultPrevented;
29517
29518        $location.$$parse(newUrl);
29519        $location.$$state = newState;
29520
29521        defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
29522            newState, oldState).defaultPrevented;
29523
29524        // if the location was changed by a `$locationChangeStart` handler then stop
29525        // processing this location change
29526        if ($location.absUrl() !== newUrl) return;
29527
29528        if (defaultPrevented) {
29529          $location.$$parse(oldUrl);
29530          $location.$$state = oldState;
29531          setBrowserUrlWithFallback(oldUrl, false, oldState);
29532        } else {
29533          initializing = false;
29534          afterLocationChange(oldUrl, oldState);
29535        }
29536      });
29537      if (!$rootScope.$$phase) $rootScope.$digest();
29538    });
29539
29540    // update browser
29541    $rootScope.$watch(function $locationWatch() {
29542      var oldUrl = trimEmptyHash($browser.url());
29543      var newUrl = trimEmptyHash($location.absUrl());
29544      var oldState = $browser.state();
29545      var currentReplace = $location.$$replace;
29546      var urlOrStateChanged = oldUrl !== newUrl ||
29547        ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
29548
29549      if (initializing || urlOrStateChanged) {
29550        initializing = false;
29551
29552        $rootScope.$evalAsync(function() {
29553          var newUrl = $location.absUrl();
29554          var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
29555              $location.$$state, oldState).defaultPrevented;
29556
29557          // if the location was changed by a `$locationChangeStart` handler then stop
29558          // processing this location change
29559          if ($location.absUrl() !== newUrl) return;
29560
29561          if (defaultPrevented) {
29562            $location.$$parse(oldUrl);
29563            $location.$$state = oldState;
29564          } else {
29565            if (urlOrStateChanged) {
29566              setBrowserUrlWithFallback(newUrl, currentReplace,
29567                                        oldState === $location.$$state ? null : $location.$$state);
29568            }
29569            afterLocationChange(oldUrl, oldState);
29570          }
29571        });
29572      }
29573
29574      $location.$$replace = false;
29575
29576      // we don't need to return anything because $evalAsync will make the digest loop dirty when
29577      // there is a change
29578    });
29579
29580    return $location;
29581
29582    function afterLocationChange(oldUrl, oldState) {
29583      $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl,
29584        $location.$$state, oldState);
29585    }
29586}];
29587}
29588
29589/**
29590 * @ngdoc service
29591 * @name $log
29592 * @requires $window
29593 *
29594 * @description
29595 * Simple service for logging. Default implementation safely writes the message
29596 * into the browser's console (if present).
29597 *
29598 * The main purpose of this service is to simplify debugging and troubleshooting.
29599 *
29600 * The default is to log `debug` messages. You can use
29601 * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
29602 *
29603 * @example
29604   <example module="logExample">
29605     <file name="script.js">
29606       angular.module('logExample', [])
29607         .controller('LogController', ['$scope', '$log', function($scope, $log) {
29608           $scope.$log = $log;
29609           $scope.message = 'Hello World!';
29610         }]);
29611     </file>
29612     <file name="index.html">
29613       <div ng-controller="LogController">
29614         <p>Reload this page with open console, enter text and hit the log button...</p>
29615         <label>Message:
29616         <input type="text" ng-model="message" /></label>
29617         <button ng-click="$log.log(message)">log</button>
29618         <button ng-click="$log.warn(message)">warn</button>
29619         <button ng-click="$log.info(message)">info</button>
29620         <button ng-click="$log.error(message)">error</button>
29621         <button ng-click="$log.debug(message)">debug</button>
29622       </div>
29623     </file>
29624   </example>
29625 */
29626
29627/**
29628 * @ngdoc provider
29629 * @name $logProvider
29630 * @description
29631 * Use the `$logProvider` to configure how the application logs messages
29632 */
29633function $LogProvider() {
29634  var debug = true,
29635      self = this;
29636
29637  /**
29638   * @ngdoc method
29639   * @name $logProvider#debugEnabled
29640   * @description
29641   * @param {boolean=} flag enable or disable debug level messages
29642   * @returns {*} current value if used as getter or itself (chaining) if used as setter
29643   */
29644  this.debugEnabled = function(flag) {
29645    if (isDefined(flag)) {
29646      debug = flag;
29647    return this;
29648    } else {
29649      return debug;
29650    }
29651  };
29652
29653  this.$get = ['$window', function($window) {
29654    return {
29655      /**
29656       * @ngdoc method
29657       * @name $log#log
29658       *
29659       * @description
29660       * Write a log message
29661       */
29662      log: consoleLog('log'),
29663
29664      /**
29665       * @ngdoc method
29666       * @name $log#info
29667       *
29668       * @description
29669       * Write an information message
29670       */
29671      info: consoleLog('info'),
29672
29673      /**
29674       * @ngdoc method
29675       * @name $log#warn
29676       *
29677       * @description
29678       * Write a warning message
29679       */
29680      warn: consoleLog('warn'),
29681
29682      /**
29683       * @ngdoc method
29684       * @name $log#error
29685       *
29686       * @description
29687       * Write an error message
29688       */
29689      error: consoleLog('error'),
29690
29691      /**
29692       * @ngdoc method
29693       * @name $log#debug
29694       *
29695       * @description
29696       * Write a debug message
29697       */
29698      debug: (function() {
29699        var fn = consoleLog('debug');
29700
29701        return function() {
29702          if (debug) {
29703            fn.apply(self, arguments);
29704          }
29705        };
29706      }())
29707    };
29708
29709    function formatError(arg) {
29710      if (arg instanceof Error) {
29711        if (arg.stack) {
29712          arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
29713              ? 'Error: ' + arg.message + '\n' + arg.stack
29714              : arg.stack;
29715        } else if (arg.sourceURL) {
29716          arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
29717        }
29718      }
29719      return arg;
29720    }
29721
29722    function consoleLog(type) {
29723      var console = $window.console || {},
29724          logFn = console[type] || console.log || noop,
29725          hasApply = false;
29726
29727      // Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
29728      // The reason behind this is that console.log has type "object" in IE8...
29729      try {
29730        hasApply = !!logFn.apply;
29731      } catch (e) {}
29732
29733      if (hasApply) {
29734        return function() {
29735          var args = [];
29736          forEach(arguments, function(arg) {
29737            args.push(formatError(arg));
29738          });
29739          return logFn.apply(console, args);
29740        };
29741      }
29742
29743      // we are IE which either doesn't have window.console => this is noop and we do nothing,
29744      // or we are IE where console.log doesn't have apply so we log at least first 2 args
29745      return function(arg1, arg2) {
29746        logFn(arg1, arg2 == null ? '' : arg2);
29747      };
29748    }
29749  }];
29750}
29751
29752/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
29753 *     Any commits to this file should be reviewed with security in mind.  *
29754 *   Changes to this file can potentially create security vulnerabilities. *
29755 *          An approval from 2 Core members with history of modifying      *
29756 *                         this file is required.                          *
29757 *                                                                         *
29758 *  Does the change somehow allow for arbitrary javascript to be executed? *
29759 *    Or allows for someone to change the prototype of built-in objects?   *
29760 *     Or gives undesired access to variables likes document or window?    *
29761 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
29762
29763var $parseMinErr = minErr('$parse');
29764
29765// Sandboxing Angular Expressions
29766// ------------------------------
29767// Angular expressions are generally considered safe because these expressions only have direct
29768// access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by
29769// obtaining a reference to native JS functions such as the Function constructor.
29770//
29771// As an example, consider the following Angular expression:
29772//
29773//   {}.toString.constructor('alert("evil JS code")')
29774//
29775// This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
29776// against the expression language, but not to prevent exploits that were enabled by exposing
29777// sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good
29778// practice and therefore we are not even trying to protect against interaction with an object
29779// explicitly exposed in this way.
29780//
29781// In general, it is not possible to access a Window object from an angular expression unless a
29782// window or some DOM object that has a reference to window is published onto a Scope.
29783// Similarly we prevent invocations of function known to be dangerous, as well as assignments to
29784// native objects.
29785//
29786// See https://docs.angularjs.org/guide/security
29787
29788
29789function ensureSafeMemberName(name, fullExpression) {
29790  if (name === "__defineGetter__" || name === "__defineSetter__"
29791      || name === "__lookupGetter__" || name === "__lookupSetter__"
29792      || name === "__proto__") {
29793    throw $parseMinErr('isecfld',
29794        'Attempting to access a disallowed field in Angular expressions! '
29795        + 'Expression: {0}', fullExpression);
29796  }
29797  return name;
29798}
29799
29800function getStringValue(name, fullExpression) {
29801  // From the JavaScript docs:
29802  // Property names must be strings. This means that non-string objects cannot be used
29803  // as keys in an object. Any non-string object, including a number, is typecasted
29804  // into a string via the toString method.
29805  //
29806  // So, to ensure that we are checking the same `name` that JavaScript would use,
29807  // we cast it to a string, if possible.
29808  // Doing `name + ''` can cause a repl error if the result to `toString` is not a string,
29809  // this is, this will handle objects that misbehave.
29810  name = name + '';
29811  if (!isString(name)) {
29812    throw $parseMinErr('iseccst',
29813        'Cannot convert object to primitive value! '
29814        + 'Expression: {0}', fullExpression);
29815  }
29816  return name;
29817}
29818
29819function ensureSafeObject(obj, fullExpression) {
29820  // nifty check if obj is Function that is fast and works across iframes and other contexts
29821  if (obj) {
29822    if (obj.constructor === obj) {
29823      throw $parseMinErr('isecfn',
29824          'Referencing Function in Angular expressions is disallowed! Expression: {0}',
29825          fullExpression);
29826    } else if (// isWindow(obj)
29827        obj.window === obj) {
29828      throw $parseMinErr('isecwindow',
29829          'Referencing the Window in Angular expressions is disallowed! Expression: {0}',
29830          fullExpression);
29831    } else if (// isElement(obj)
29832        obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) {
29833      throw $parseMinErr('isecdom',
29834          'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
29835          fullExpression);
29836    } else if (// block Object so that we can't get hold of dangerous Object.* methods
29837        obj === Object) {
29838      throw $parseMinErr('isecobj',
29839          'Referencing Object in Angular expressions is disallowed! Expression: {0}',
29840          fullExpression);
29841    }
29842  }
29843  return obj;
29844}
29845
29846var CALL = Function.prototype.call;
29847var APPLY = Function.prototype.apply;
29848var BIND = Function.prototype.bind;
29849
29850function ensureSafeFunction(obj, fullExpression) {
29851  if (obj) {
29852    if (obj.constructor === obj) {
29853      throw $parseMinErr('isecfn',
29854        'Referencing Function in Angular expressions is disallowed! Expression: {0}',
29855        fullExpression);
29856    } else if (obj === CALL || obj === APPLY || obj === BIND) {
29857      throw $parseMinErr('isecff',
29858        'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}',
29859        fullExpression);
29860    }
29861  }
29862}
29863
29864function ensureSafeAssignContext(obj, fullExpression) {
29865  if (obj) {
29866    if (obj === (0).constructor || obj === (false).constructor || obj === ''.constructor ||
29867        obj === {}.constructor || obj === [].constructor || obj === Function.constructor) {
29868      throw $parseMinErr('isecaf',
29869        'Assigning to a constructor is disallowed! Expression: {0}', fullExpression);
29870    }
29871  }
29872}
29873
29874var OPERATORS = createMap();
29875forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; });
29876var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
29877
29878
vendor: 37,280 bytes, lines 29879-31106
29879/////////////////////////////////////////
29880
29881
29882/**
29883 * @constructor
29884 */
29885var Lexer = function(options) {
29886  this.options = options;
29887};
29888
29889Lexer.prototype = {
29890  constructor: Lexer,
29891
29892  lex: function(text) {
29893    this.text = text;
29894    this.index = 0;
29895    this.tokens = [];
29896
29897    while (this.index < this.text.length) {
29898      var ch = this.text.charAt(this.index);
29899      if (ch === '"' || ch === "'") {
29900        this.readString(ch);
29901      } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
29902        this.readNumber();
29903      } else if (this.isIdent(ch)) {
29904        this.readIdent();
29905      } else if (this.is(ch, '(){}[].,;:?')) {
29906        this.tokens.push({index: this.index, text: ch});
29907        this.index++;
29908      } else if (this.isWhitespace(ch)) {
29909        this.index++;
29910      } else {
29911        var ch2 = ch + this.peek();
29912        var ch3 = ch2 + this.peek(2);
29913        var op1 = OPERATORS[ch];
29914        var op2 = OPERATORS[ch2];
29915        var op3 = OPERATORS[ch3];
29916        if (op1 || op2 || op3) {
29917          var token = op3 ? ch3 : (op2 ? ch2 : ch);
29918          this.tokens.push({index: this.index, text: token, operator: true});
29919          this.index += token.length;
29920        } else {
29921          this.throwError('Unexpected next character ', this.index, this.index + 1);
29922        }
29923      }
29924    }
29925    return this.tokens;
29926  },
29927
29928  is: function(ch, chars) {
29929    return chars.indexOf(ch) !== -1;
29930  },
29931
29932  peek: function(i) {
29933    var num = i || 1;
29934    return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;
29935  },
29936
29937  isNumber: function(ch) {
29938    return ('0' <= ch && ch <= '9') && typeof ch === "string";
29939  },
29940
29941  isWhitespace: function(ch) {
29942    // IE treats non-breaking space as \u00A0
29943    return (ch === ' ' || ch === '\r' || ch === '\t' ||
29944            ch === '\n' || ch === '\v' || ch === '\u00A0');
29945  },
29946
29947  isIdent: function(ch) {
29948    return ('a' <= ch && ch <= 'z' ||
29949            'A' <= ch && ch <= 'Z' ||
29950            '_' === ch || ch === '$');
29951  },
29952
29953  isExpOperator: function(ch) {
29954    return (ch === '-' || ch === '+' || this.isNumber(ch));
29955  },
29956
29957  throwError: function(error, start, end) {
29958    end = end || this.index;
29959    var colStr = (isDefined(start)
29960            ? 's ' + start +  '-' + this.index + ' [' + this.text.substring(start, end) + ']'
29961            : ' ' + end);
29962    throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',
29963        error, colStr, this.text);
29964  },
29965
29966  readNumber: function() {
29967    var number = '';
29968    var start = this.index;
29969    while (this.index < this.text.length) {
29970      var ch = lowercase(this.text.charAt(this.index));
29971      if (ch == '.' || this.isNumber(ch)) {
29972        number += ch;
29973      } else {
29974        var peekCh = this.peek();
29975        if (ch == 'e' && this.isExpOperator(peekCh)) {
29976          number += ch;
29977        } else if (this.isExpOperator(ch) &&
29978            peekCh && this.isNumber(peekCh) &&
29979            number.charAt(number.length - 1) == 'e') {
29980          number += ch;
29981        } else if (this.isExpOperator(ch) &&
29982            (!peekCh || !this.isNumber(peekCh)) &&
29983            number.charAt(number.length - 1) == 'e') {
29984          this.throwError('Invalid exponent');
29985        } else {
29986          break;
29987        }
29988      }
29989      this.index++;
29990    }
29991    this.tokens.push({
29992      index: start,
29993      text: number,
29994      constant: true,
29995      value: Number(number)
29996    });
29997  },
29998
29999  readIdent: function() {
30000    var start = this.index;
30001    while (this.index < this.text.length) {
30002      var ch = this.text.charAt(this.index);
30003      if (!(this.isIdent(ch) || this.isNumber(ch))) {
30004        break;
30005      }
30006      this.index++;
30007    }
30008    this.tokens.push({
30009      index: start,
30010      text: this.text.slice(start, this.index),
30011      identifier: true
30012    });
30013  },
30014
30015  readString: function(quote) {
30016    var start = this.index;
30017    this.index++;
30018    var string = '';
30019    var rawString = quote;
30020    var escape = false;
30021    while (this.index < this.text.length) {
30022      var ch = this.text.charAt(this.index);
30023      rawString += ch;
30024      if (escape) {
30025        if (ch === 'u') {
30026          var hex = this.text.substring(this.index + 1, this.index + 5);
30027          if (!hex.match(/[\da-f]{4}/i)) {
30028            this.throwError('Invalid unicode escape [\\u' + hex + ']');
30029          }
30030          this.index += 4;
30031          string += String.fromCharCode(parseInt(hex, 16));
30032        } else {
30033          var rep = ESCAPE[ch];
30034          string = string + (rep || ch);
30035        }
30036        escape = false;
30037      } else if (ch === '\\') {
30038        escape = true;
30039      } else if (ch === quote) {
30040        this.index++;
30041        this.tokens.push({
30042          index: start,
30043          text: rawString,
30044          constant: true,
30045          value: string
30046        });
30047        return;
30048      } else {
30049        string += ch;
30050      }
30051      this.index++;
30052    }
30053    this.throwError('Unterminated quote', start);
30054  }
30055};
30056
30057var AST = function(lexer, options) {
30058  this.lexer = lexer;
30059  this.options = options;
30060};
30061
30062AST.Program = 'Program';
30063AST.ExpressionStatement = 'ExpressionStatement';
30064AST.AssignmentExpression = 'AssignmentExpression';
30065AST.ConditionalExpression = 'ConditionalExpression';
30066AST.LogicalExpression = 'LogicalExpression';
30067AST.BinaryExpression = 'BinaryExpression';
30068AST.UnaryExpression = 'UnaryExpression';
30069AST.CallExpression = 'CallExpression';
30070AST.MemberExpression = 'MemberExpression';
30071AST.Identifier = 'Identifier';
30072AST.Literal = 'Literal';
30073AST.ArrayExpression = 'ArrayExpression';
30074AST.Property = 'Property';
30075AST.ObjectExpression = 'ObjectExpression';
30076AST.ThisExpression = 'ThisExpression';
30077
30078// Internal use only
30079AST.NGValueParameter = 'NGValueParameter';
30080
30081AST.prototype = {
30082  ast: function(text) {
30083    this.text = text;
30084    this.tokens = this.lexer.lex(text);
30085
30086    var value = this.program();
30087
30088    if (this.tokens.length !== 0) {
30089      this.throwError('is an unexpected token', this.tokens[0]);
30090    }
30091
30092    return value;
30093  },
30094
30095  program: function() {
30096    var body = [];
30097    while (true) {
30098      if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
30099        body.push(this.expressionStatement());
30100      if (!this.expect(';')) {
30101        return { type: AST.Program, body: body};
30102      }
30103    }
30104  },
30105
30106  expressionStatement: function() {
30107    return { type: AST.ExpressionStatement, expression: this.filterChain() };
30108  },
30109
30110  filterChain: function() {
30111    var left = this.expression();
30112    var token;
30113    while ((token = this.expect('|'))) {
30114      left = this.filter(left);
30115    }
30116    return left;
30117  },
30118
30119  expression: function() {
30120    return this.assignment();
30121  },
30122
30123  assignment: function() {
30124    var result = this.ternary();
30125    if (this.expect('=')) {
30126      result = { type: AST.AssignmentExpression, left: result, right: this.assignment(), operator: '='};
30127    }
30128    return result;
30129  },
30130
30131  ternary: function() {
30132    var test = this.logicalOR();
30133    var alternate;
30134    var consequent;
30135    if (this.expect('?')) {
30136      alternate = this.expression();
30137      if (this.consume(':')) {
30138        consequent = this.expression();
30139        return { type: AST.ConditionalExpression, test: test, alternate: alternate, consequent: consequent};
30140      }
30141    }
30142    return test;
30143  },
30144
30145  logicalOR: function() {
30146    var left = this.logicalAND();
30147    while (this.expect('||')) {
30148      left = { type: AST.LogicalExpression, operator: '||', left: left, right: this.logicalAND() };
30149    }
30150    return left;
30151  },
30152
30153  logicalAND: function() {
30154    var left = this.equality();
30155    while (this.expect('&&')) {
30156      left = { type: AST.LogicalExpression, operator: '&&', left: left, right: this.equality()};
30157    }
30158    return left;
30159  },
30160
30161  equality: function() {
30162    var left = this.relational();
30163    var token;
30164    while ((token = this.expect('==','!=','===','!=='))) {
30165      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.relational() };
30166    }
30167    return left;
30168  },
30169
30170  relational: function() {
30171    var left = this.additive();
30172    var token;
30173    while ((token = this.expect('<', '>', '<=', '>='))) {
30174      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.additive() };
30175    }
30176    return left;
30177  },
30178
30179  additive: function() {
30180    var left = this.multiplicative();
30181    var token;
30182    while ((token = this.expect('+','-'))) {
30183      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.multiplicative() };
30184    }
30185    return left;
30186  },
30187
30188  multiplicative: function() {
30189    var left = this.unary();
30190    var token;
30191    while ((token = this.expect('*','/','%'))) {
30192      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.unary() };
30193    }
30194    return left;
30195  },
30196
30197  unary: function() {
30198    var token;
30199    if ((token = this.expect('+', '-', '!'))) {
30200      return { type: AST.UnaryExpression, operator: token.text, prefix: true, argument: this.unary() };
30201    } else {
30202      return this.primary();
30203    }
30204  },
30205
30206  primary: function() {
30207    var primary;
30208    if (this.expect('(')) {
30209      primary = this.filterChain();
30210      this.consume(')');
30211    } else if (this.expect('[')) {
30212      primary = this.arrayDeclaration();
30213    } else if (this.expect('{')) {
30214      primary = this.object();
30215    } else if (this.constants.hasOwnProperty(this.peek().text)) {
30216      primary = copy(this.constants[this.consume().text]);
30217    } else if (this.peek().identifier) {
30218      primary = this.identifier();
30219    } else if (this.peek().constant) {
30220      primary = this.constant();
30221    } else {
30222      this.throwError('not a primary expression', this.peek());
30223    }
30224
30225    var next;
30226    while ((next = this.expect('(', '[', '.'))) {
30227      if (next.text === '(') {
30228        primary = {type: AST.CallExpression, callee: primary, arguments: this.parseArguments() };
30229        this.consume(')');
30230      } else if (next.text === '[') {
30231        primary = { type: AST.MemberExpression, object: primary, property: this.expression(), computed: true };
30232        this.consume(']');
30233      } else if (next.text === '.') {
30234        primary = { type: AST.MemberExpression, object: primary, property: this.identifier(), computed: false };
30235      } else {
30236        this.throwError('IMPOSSIBLE');
30237      }
30238    }
30239    return primary;
30240  },
30241
30242  filter: function(baseExpression) {
30243    var args = [baseExpression];
30244    var result = {type: AST.CallExpression, callee: this.identifier(), arguments: args, filter: true};
30245
30246    while (this.expect(':')) {
30247      args.push(this.expression());
30248    }
30249
30250    return result;
30251  },
30252
30253  parseArguments: function() {
30254    var args = [];
30255    if (this.peekToken().text !== ')') {
30256      do {
30257        args.push(this.expression());
30258      } while (this.expect(','));
30259    }
30260    return args;
30261  },
30262
30263  identifier: function() {
30264    var token = this.consume();
30265    if (!token.identifier) {
30266      this.throwError('is not a valid identifier', token);
30267    }
30268    return { type: AST.Identifier, name: token.text };
30269  },
30270
30271  constant: function() {
30272    // TODO check that it is a constant
30273    return { type: AST.Literal, value: this.consume().value };
30274  },
30275
30276  arrayDeclaration: function() {
30277    var elements = [];
30278    if (this.peekToken().text !== ']') {
30279      do {
30280        if (this.peek(']')) {
30281          // Support trailing commas per ES5.1.
30282          break;
30283        }
30284        elements.push(this.expression());
30285      } while (this.expect(','));
30286    }
30287    this.consume(']');
30288
30289    return { type: AST.ArrayExpression, elements: elements };
30290  },
30291
30292  object: function() {
30293    var properties = [], property;
30294    if (this.peekToken().text !== '}') {
30295      do {
30296        if (this.peek('}')) {
30297          // Support trailing commas per ES5.1.
30298          break;
30299        }
30300        property = {type: AST.Property, kind: 'init'};
30301        if (this.peek().constant) {
30302          property.key = this.constant();
30303        } else if (this.peek().identifier) {
30304          property.key = this.identifier();
30305        } else {
30306          this.throwError("invalid key", this.peek());
30307        }
30308        this.consume(':');
30309        property.value = this.expression();
30310        properties.push(property);
30311      } while (this.expect(','));
30312    }
30313    this.consume('}');
30314
30315    return {type: AST.ObjectExpression, properties: properties };
30316  },
30317
30318  throwError: function(msg, token) {
30319    throw $parseMinErr('syntax',
30320        'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].',
30321          token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));
30322  },
30323
30324  consume: function(e1) {
30325    if (this.tokens.length === 0) {
30326      throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
30327    }
30328
30329    var token = this.expect(e1);
30330    if (!token) {
30331      this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
30332    }
30333    return token;
30334  },
30335
30336  peekToken: function() {
30337    if (this.tokens.length === 0) {
30338      throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
30339    }
30340    return this.tokens[0];
30341  },
30342
30343  peek: function(e1, e2, e3, e4) {
30344    return this.peekAhead(0, e1, e2, e3, e4);
30345  },
30346
30347  peekAhead: function(i, e1, e2, e3, e4) {
30348    if (this.tokens.length > i) {
30349      var token = this.tokens[i];
30350      var t = token.text;
30351      if (t === e1 || t === e2 || t === e3 || t === e4 ||
30352          (!e1 && !e2 && !e3 && !e4)) {
30353        return token;
30354      }
30355    }
30356    return false;
30357  },
30358
30359  expect: function(e1, e2, e3, e4) {
30360    var token = this.peek(e1, e2, e3, e4);
30361    if (token) {
30362      this.tokens.shift();
30363      return token;
30364    }
30365    return false;
30366  },
30367
30368
30369  /* `undefined` is not a constant, it is an identifier,
30370   * but using it as an identifier is not supported
30371   */
30372  constants: {
30373    'true': { type: AST.Literal, value: true },
30374    'false': { type: AST.Literal, value: false },
30375    'null': { type: AST.Literal, value: null },
30376    'undefined': {type: AST.Literal, value: undefined },
30377    'this': {type: AST.ThisExpression }
30378  }
30379};
30380
30381function ifDefined(v, d) {
30382  return typeof v !== 'undefined' ? v : d;
30383}
30384
30385function plusFn(l, r) {
30386  if (typeof l === 'undefined') return r;
30387  if (typeof r === 'undefined') return l;
30388  return l + r;
30389}
30390
30391function isStateless($filter, filterName) {
30392  var fn = $filter(filterName);
30393  return !fn.$stateful;
30394}
30395
30396function findConstantAndWatchExpressions(ast, $filter) {
30397  var allConstants;
30398  var argsToWatch;
30399  switch (ast.type) {
30400  case AST.Program:
30401    allConstants = true;
30402    forEach(ast.body, function(expr) {
30403      findConstantAndWatchExpressions(expr.expression, $filter);
30404      allConstants = allConstants && expr.expression.constant;
30405    });
30406    ast.constant = allConstants;
30407    break;
30408  case AST.Literal:
30409    ast.constant = true;
30410    ast.toWatch = [];
30411    break;
30412  case AST.UnaryExpression:
30413    findConstantAndWatchExpressions(ast.argument, $filter);
30414    ast.constant = ast.argument.constant;
30415    ast.toWatch = ast.argument.toWatch;
30416    break;
30417  case AST.BinaryExpression:
30418    findConstantAndWatchExpressions(ast.left, $filter);
30419    findConstantAndWatchExpressions(ast.right, $filter);
30420    ast.constant = ast.left.constant && ast.right.constant;
30421    ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch);
30422    break;
30423  case AST.LogicalExpression:
30424    findConstantAndWatchExpressions(ast.left, $filter);
30425    findConstantAndWatchExpressions(ast.right, $filter);
30426    ast.constant = ast.left.constant && ast.right.constant;
30427    ast.toWatch = ast.constant ? [] : [ast];
30428    break;
30429  case AST.ConditionalExpression:
30430    findConstantAndWatchExpressions(ast.test, $filter);
30431    findConstantAndWatchExpressions(ast.alternate, $filter);
30432    findConstantAndWatchExpressions(ast.consequent, $filter);
30433    ast.constant = ast.test.constant && ast.alternate.constant && ast.consequent.constant;
30434    ast.toWatch = ast.constant ? [] : [ast];
30435    break;
30436  case AST.Identifier:
30437    ast.constant = false;
30438    ast.toWatch = [ast];
30439    break;
30440  case AST.MemberExpression:
30441    findConstantAndWatchExpressions(ast.object, $filter);
30442    if (ast.computed) {
30443      findConstantAndWatchExpressions(ast.property, $filter);
30444    }
30445    ast.constant = ast.object.constant && (!ast.computed || ast.property.constant);
30446    ast.toWatch = [ast];
30447    break;
30448  case AST.CallExpression:
30449    allConstants = ast.filter ? isStateless($filter, ast.callee.name) : false;
30450    argsToWatch = [];
30451    forEach(ast.arguments, function(expr) {
30452      findConstantAndWatchExpressions(expr, $filter);
30453      allConstants = allConstants && expr.constant;
30454      if (!expr.constant) {
30455        argsToWatch.push.apply(argsToWatch, expr.toWatch);
30456      }
30457    });
30458    ast.constant = allConstants;
30459    ast.toWatch = ast.filter && isStateless($filter, ast.callee.name) ? argsToWatch : [ast];
30460    break;
30461  case AST.AssignmentExpression:
30462    findConstantAndWatchExpressions(ast.left, $filter);
30463    findConstantAndWatchExpressions(ast.right, $filter);
30464    ast.constant = ast.left.constant && ast.right.constant;
30465    ast.toWatch = [ast];
30466    break;
30467  case AST.ArrayExpression:
30468    allConstants = true;
30469    argsToWatch = [];
30470    forEach(ast.elements, function(expr) {
30471      findConstantAndWatchExpressions(expr, $filter);
30472      allConstants = allConstants && expr.constant;
30473      if (!expr.constant) {
30474        argsToWatch.push.apply(argsToWatch, expr.toWatch);
30475      }
30476    });
30477    ast.constant = allConstants;
30478    ast.toWatch = argsToWatch;
30479    break;
30480  case AST.ObjectExpression:
30481    allConstants = true;
30482    argsToWatch = [];
30483    forEach(ast.properties, function(property) {
30484      findConstantAndWatchExpressions(property.value, $filter);
30485      allConstants = allConstants && property.value.constant;
30486      if (!property.value.constant) {
30487        argsToWatch.push.apply(argsToWatch, property.value.toWatch);
30488      }
30489    });
30490    ast.constant = allConstants;
30491    ast.toWatch = argsToWatch;
30492    break;
30493  case AST.ThisExpression:
30494    ast.constant = false;
30495    ast.toWatch = [];
30496    break;
30497  }
30498}
30499
30500function getInputs(body) {
30501  if (body.length != 1) return;
30502  var lastExpression = body[0].expression;
30503  var candidate = lastExpression.toWatch;
30504  if (candidate.length !== 1) return candidate;
30505  return candidate[0] !== lastExpression ? candidate : undefined;
30506}
30507
30508function isAssignable(ast) {
30509  return ast.type === AST.Identifier || ast.type === AST.MemberExpression;
30510}
30511
30512function assignableAST(ast) {
30513  if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) {
30514    return {type: AST.AssignmentExpression, left: ast.body[0].expression, right: {type: AST.NGValueParameter}, operator: '='};
30515  }
30516}
30517
30518function isLiteral(ast) {
30519  return ast.body.length === 0 ||
30520      ast.body.length === 1 && (
30521      ast.body[0].expression.type === AST.Literal ||
30522      ast.body[0].expression.type === AST.ArrayExpression ||
30523      ast.body[0].expression.type === AST.ObjectExpression);
30524}
30525
30526function isConstant(ast) {
30527  return ast.constant;
30528}
30529
30530function ASTCompiler(astBuilder, $filter) {
30531  this.astBuilder = astBuilder;
30532  this.$filter = $filter;
30533}
30534
30535ASTCompiler.prototype = {
30536  compile: function(expression, expensiveChecks) {
30537    var self = this;
30538    var ast = this.astBuilder.ast(expression);
30539    this.state = {
30540      nextId: 0,
30541      filters: {},
30542      expensiveChecks: expensiveChecks,
30543      fn: {vars: [], body: [], own: {}},
30544      assign: {vars: [], body: [], own: {}},
30545      inputs: []
30546    };
30547    findConstantAndWatchExpressions(ast, self.$filter);
30548    var extra = '';
30549    var assignable;
30550    this.stage = 'assign';
30551    if ((assignable = assignableAST(ast))) {
30552      this.state.computing = 'assign';
30553      var result = this.nextId();
30554      this.recurse(assignable, result);
30555      this.return_(result);
30556      extra = 'fn.assign=' + this.generateFunction('assign', 's,v,l');
30557    }
30558    var toWatch = getInputs(ast.body);
30559    self.stage = 'inputs';
30560    forEach(toWatch, function(watch, key) {
30561      var fnKey = 'fn' + key;
30562      self.state[fnKey] = {vars: [], body: [], own: {}};
30563      self.state.computing = fnKey;
30564      var intoId = self.nextId();
30565      self.recurse(watch, intoId);
30566      self.return_(intoId);
30567      self.state.inputs.push(fnKey);
30568      watch.watchId = key;
30569    });
30570    this.state.computing = 'fn';
30571    this.stage = 'main';
30572    this.recurse(ast);
30573    var fnString =
30574      // The build and minification steps remove the string "use strict" from the code, but this is done using a regex.
30575      // This is a workaround for this until we do a better job at only removing the prefix only when we should.
30576      '"' + this.USE + ' ' + this.STRICT + '";\n' +
30577      this.filterPrefix() +
30578      'var fn=' + this.generateFunction('fn', 's,l,a,i') +
30579      extra +
30580      this.watchFns() +
30581      'return fn;';
30582
30583    /* jshint -W054 */
30584    var fn = (new Function('$filter',
30585        'ensureSafeMemberName',
30586        'ensureSafeObject',
30587        'ensureSafeFunction',
30588        'getStringValue',
30589        'ensureSafeAssignContext',
30590        'ifDefined',
30591        'plus',
30592        'text',
30593        fnString))(
30594          this.$filter,
30595          ensureSafeMemberName,
30596          ensureSafeObject,
30597          ensureSafeFunction,
30598          getStringValue,
30599          ensureSafeAssignContext,
30600          ifDefined,
30601          plusFn,
30602          expression);
30603    /* jshint +W054 */
30604    this.state = this.stage = undefined;
30605    fn.literal = isLiteral(ast);
30606    fn.constant = isConstant(ast);
30607    return fn;
30608  },
30609
30610  USE: 'use',
30611
30612  STRICT: 'strict',
30613
30614  watchFns: function() {
30615    var result = [];
30616    var fns = this.state.inputs;
30617    var self = this;
30618    forEach(fns, function(name) {
30619      result.push('var ' + name + '=' + self.generateFunction(name, 's'));
30620    });
30621    if (fns.length) {
30622      result.push('fn.inputs=[' + fns.join(',') + '];');
30623    }
30624    return result.join('');
30625  },
30626
30627  generateFunction: function(name, params) {
30628    return 'function(' + params + '){' +
30629        this.varsPrefix(name) +
30630        this.body(name) +
30631        '};';
30632  },
30633
30634  filterPrefix: function() {
30635    var parts = [];
30636    var self = this;
30637    forEach(this.state.filters, function(id, filter) {
30638      parts.push(id + '=$filter(' + self.escape(filter) + ')');
30639    });
30640    if (parts.length) return 'var ' + parts.join(',') + ';';
30641    return '';
30642  },
30643
30644  varsPrefix: function(section) {
30645    return this.state[section].vars.length ? 'var ' + this.state[section].vars.join(',') + ';' : '';
30646  },
30647
30648  body: function(section) {
30649    return this.state[section].body.join('');
30650  },
30651
30652  recurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
30653    var left, right, self = this, args, expression;
30654    recursionFn = recursionFn || noop;
30655    if (!skipWatchIdCheck && isDefined(ast.watchId)) {
30656      intoId = intoId || this.nextId();
30657      this.if_('i',
30658        this.lazyAssign(intoId, this.computedMember('i', ast.watchId)),
30659        this.lazyRecurse(ast, intoId, nameId, recursionFn, create, true)
30660      );
30661      return;
30662    }
30663    switch (ast.type) {
30664    case AST.Program:
30665      forEach(ast.body, function(expression, pos) {
30666        self.recurse(expression.expression, undefined, undefined, function(expr) { right = expr; });
30667        if (pos !== ast.body.length - 1) {
30668          self.current().body.push(right, ';');
30669        } else {
30670          self.return_(right);
30671        }
30672      });
30673      break;
30674    case AST.Literal:
30675      expression = this.escape(ast.value);
30676      this.assign(intoId, expression);
30677      recursionFn(expression);
30678      break;
30679    case AST.UnaryExpression:
30680      this.recurse(ast.argument, undefined, undefined, function(expr) { right = expr; });
30681      expression = ast.operator + '(' + this.ifDefined(right, 0) + ')';
30682      this.assign(intoId, expression);
30683      recursionFn(expression);
30684      break;
30685    case AST.BinaryExpression:
30686      this.recurse(ast.left, undefined, undefined, function(expr) { left = expr; });
30687      this.recurse(ast.right, undefined, undefined, function(expr) { right = expr; });
30688      if (ast.operator === '+') {
30689        expression = this.plus(left, right);
30690      } else if (ast.operator === '-') {
30691        expression = this.ifDefined(left, 0) + ast.operator + this.ifDefined(right, 0);
30692      } else {
30693        expression = '(' + left + ')' + ast.operator + '(' + right + ')';
30694      }
30695      this.assign(intoId, expression);
30696      recursionFn(expression);
30697      break;
30698    case AST.LogicalExpression:
30699      intoId = intoId || this.nextId();
30700      self.recurse(ast.left, intoId);
30701      self.if_(ast.operator === '&&' ? intoId : self.not(intoId), self.lazyRecurse(ast.right, intoId));
30702      recursionFn(intoId);
30703      break;
30704    case AST.ConditionalExpression:
30705      intoId = intoId || this.nextId();
30706      self.recurse(ast.test, intoId);
30707      self.if_(intoId, self.lazyRecurse(ast.alternate, intoId), self.lazyRecurse(ast.consequent, intoId));
30708      recursionFn(intoId);
30709      break;
30710    case AST.Identifier:
30711      intoId = intoId || this.nextId();
30712      if (nameId) {
30713        nameId.context = self.stage === 'inputs' ? 's' : this.assign(this.nextId(), this.getHasOwnProperty('l', ast.name) + '?l:s');
30714        nameId.computed = false;
30715        nameId.name = ast.name;
30716      }
30717      ensureSafeMemberName(ast.name);
30718      self.if_(self.stage === 'inputs' || self.not(self.getHasOwnProperty('l', ast.name)),
30719        function() {
30720          self.if_(self.stage === 'inputs' || 's', function() {
30721            if (create && create !== 1) {
30722              self.if_(
30723                self.not(self.nonComputedMember('s', ast.name)),
30724                self.lazyAssign(self.nonComputedMember('s', ast.name), '{}'));
30725            }
30726            self.assign(intoId, self.nonComputedMember('s', ast.name));
30727          });
30728        }, intoId && self.lazyAssign(intoId, self.nonComputedMember('l', ast.name))
30729        );
30730      if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.name)) {
30731        self.addEnsureSafeObject(intoId);
30732      }
30733      recursionFn(intoId);
30734      break;
30735    case AST.MemberExpression:
30736      left = nameId && (nameId.context = this.nextId()) || this.nextId();
30737      intoId = intoId || this.nextId();
30738      self.recurse(ast.object, left, undefined, function() {
30739        self.if_(self.notNull(left), function() {
30740          if (ast.computed) {
30741            right = self.nextId();
30742            self.recurse(ast.property, right);
30743            self.getStringValue(right);
30744            self.addEnsureSafeMemberName(right);
30745            if (create && create !== 1) {
30746              self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}'));
30747            }
30748            expression = self.ensureSafeObject(self.computedMember(left, right));
30749            self.assign(intoId, expression);
30750            if (nameId) {
30751              nameId.computed = true;
30752              nameId.name = right;
30753            }
30754          } else {
30755            ensureSafeMemberName(ast.property.name);
30756            if (create && create !== 1) {
30757              self.if_(self.not(self.nonComputedMember(left, ast.property.name)), self.lazyAssign(self.nonComputedMember(left, ast.property.name), '{}'));
30758            }
30759            expression = self.nonComputedMember(left, ast.property.name);
30760            if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.property.name)) {
30761              expression = self.ensureSafeObject(expression);
30762            }
30763            self.assign(intoId, expression);
30764            if (nameId) {
30765              nameId.computed = false;
30766              nameId.name = ast.property.name;
30767            }
30768          }
30769        }, function() {
30770          self.assign(intoId, 'undefined');
30771        });
30772        recursionFn(intoId);
30773      }, !!create);
30774      break;
30775    case AST.CallExpression:
30776      intoId = intoId || this.nextId();
30777      if (ast.filter) {
30778        right = self.filter(ast.callee.name);
30779        args = [];
30780        forEach(ast.arguments, function(expr) {
30781          var argument = self.nextId();
30782          self.recurse(expr, argument);
30783          args.push(argument);
30784        });
30785        expression = right + '(' + args.join(',') + ')';
30786        self.assign(intoId, expression);
30787        recursionFn(intoId);
30788      } else {
30789        right = self.nextId();
30790        left = {};
30791        args = [];
30792        self.recurse(ast.callee, right, left, function() {
30793          self.if_(self.notNull(right), function() {
30794            self.addEnsureSafeFunction(right);
30795            forEach(ast.arguments, function(expr) {
30796              self.recurse(expr, self.nextId(), undefined, function(argument) {
30797                args.push(self.ensureSafeObject(argument));
30798              });
30799            });
30800            if (left.name) {
30801              if (!self.state.expensiveChecks) {
30802                self.addEnsureSafeObject(left.context);
30803              }
30804              expression = self.member(left.context, left.name, left.computed) + '(' + args.join(',') + ')';
30805            } else {
30806              expression = right + '(' + args.join(',') + ')';
30807            }
30808            expression = self.ensureSafeObject(expression);
30809            self.assign(intoId, expression);
30810          }, function() {
30811            self.assign(intoId, 'undefined');
30812          });
30813          recursionFn(intoId);
30814        });
30815      }
30816      break;
30817    case AST.AssignmentExpression:
30818      right = this.nextId();
30819      left = {};
30820      if (!isAssignable(ast.left)) {
30821        throw $parseMinErr('lval', 'Trying to assing a value to a non l-value');
30822      }
30823      this.recurse(ast.left, undefined, left, function() {
30824        self.if_(self.notNull(left.context), function() {
30825          self.recurse(ast.right, right);
30826          self.addEnsureSafeObject(self.member(left.context, left.name, left.computed));
30827          self.addEnsureSafeAssignContext(left.context);
30828          expression = self.member(left.context, left.name, left.computed) + ast.operator + right;
30829          self.assign(intoId, expression);
30830          recursionFn(intoId || expression);
30831        });
30832      }, 1);
30833      break;
30834    case AST.ArrayExpression:
30835      args = [];
30836      forEach(ast.elements, function(expr) {
30837        self.recurse(expr, self.nextId(), undefined, function(argument) {
30838          args.push(argument);
30839        });
30840      });
30841      expression = '[' + args.join(',') + ']';
30842      this.assign(intoId, expression);
30843      recursionFn(expression);
30844      break;
30845    case AST.ObjectExpression:
30846      args = [];
30847      forEach(ast.properties, function(property) {
30848        self.recurse(property.value, self.nextId(), undefined, function(expr) {
30849          args.push(self.escape(
30850              property.key.type === AST.Identifier ? property.key.name :
30851                ('' + property.key.value)) +
30852              ':' + expr);
30853        });
30854      });
30855      expression = '{' + args.join(',') + '}';
30856      this.assign(intoId, expression);
30857      recursionFn(expression);
30858      break;
30859    case AST.ThisExpression:
30860      this.assign(intoId, 's');
30861      recursionFn('s');
30862      break;
30863    case AST.NGValueParameter:
30864      this.assign(intoId, 'v');
30865      recursionFn('v');
30866      break;
30867    }
30868  },
30869
30870  getHasOwnProperty: function(element, property) {
30871    var key = element + '.' + property;
30872    var own = this.current().own;
30873    if (!own.hasOwnProperty(key)) {
30874      own[key] = this.nextId(false, element + '&&(' + this.escape(property) + ' in ' + element + ')');
30875    }
30876    return own[key];
30877  },
30878
30879  assign: function(id, value) {
30880    if (!id) return;
30881    this.current().body.push(id, '=', value, ';');
30882    return id;
30883  },
30884
30885  filter: function(filterName) {
30886    if (!this.state.filters.hasOwnProperty(filterName)) {
30887      this.state.filters[filterName] = this.nextId(true);
30888    }
30889    return this.state.filters[filterName];
30890  },
30891
30892  ifDefined: function(id, defaultValue) {
30893    return 'ifDefined(' + id + ',' + this.escape(defaultValue) + ')';
30894  },
30895
30896  plus: function(left, right) {
30897    return 'plus(' + left + ',' + right + ')';
30898  },
30899
30900  return_: function(id) {
30901    this.current().body.push('return ', id, ';');
30902  },
30903
30904  if_: function(test, alternate, consequent) {
30905    if (test === true) {
30906      alternate();
30907    } else {
30908      var body = this.current().body;
30909      body.push('if(', test, '){');
30910      alternate();
30911      body.push('}');
30912      if (consequent) {
30913        body.push('else{');
30914        consequent();
30915        body.push('}');
30916      }
30917    }
30918  },
30919
30920  not: function(expression) {
30921    return '!(' + expression + ')';
30922  },
30923
30924  notNull: function(expression) {
30925    return expression + '!=null';
30926  },
30927
30928  nonComputedMember: function(left, right) {
30929    return left + '.' + right;
30930  },
30931
30932  computedMember: function(left, right) {
30933    return left + '[' + right + ']';
30934  },
30935
30936  member: function(left, right, computed) {
30937    if (computed) return this.computedMember(left, right);
30938    return this.nonComputedMember(left, right);
30939  },
30940
30941  addEnsureSafeObject: function(item) {
30942    this.current().body.push(this.ensureSafeObject(item), ';');
30943  },
30944
30945  addEnsureSafeMemberName: function(item) {
30946    this.current().body.push(this.ensureSafeMemberName(item), ';');
30947  },
30948
30949  addEnsureSafeFunction: function(item) {
30950    this.current().body.push(this.ensureSafeFunction(item), ';');
30951  },
30952
30953  addEnsureSafeAssignContext: function(item) {
30954    this.current().body.push(this.ensureSafeAssignContext(item), ';');
30955  },
30956
30957  ensureSafeObject: function(item) {
30958    return 'ensureSafeObject(' + item + ',text)';
30959  },
30960
30961  ensureSafeMemberName: function(item) {
30962    return 'ensureSafeMemberName(' + item + ',text)';
30963  },
30964
30965  ensureSafeFunction: function(item) {
30966    return 'ensureSafeFunction(' + item + ',text)';
30967  },
30968
30969  getStringValue: function(item) {
30970    this.assign(item, 'getStringValue(' + item + ',text)');
30971  },
30972
30973  ensureSafeAssignContext: function(item) {
30974    return 'ensureSafeAssignContext(' + item + ',text)';
30975  },
30976
30977  lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
30978    var self = this;
30979    return function() {
30980      self.recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck);
30981    };
30982  },
30983
30984  lazyAssign: function(id, value) {
30985    var self = this;
30986    return function() {
30987      self.assign(id, value);
30988    };
30989  },
30990
30991  stringEscapeRegex: /[^ a-zA-Z0-9]/g,
30992
30993  stringEscapeFn: function(c) {
30994    return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4);
30995  },
30996
30997  escape: function(value) {
30998    if (isString(value)) return "'" + value.replace(this.stringEscapeRegex, this.stringEscapeFn) + "'";
30999    if (isNumber(value)) return value.toString();
31000    if (value === true) return 'true';
31001    if (value === false) return 'false';
31002    if (value === null) return 'null';
31003    if (typeof value === 'undefined') return 'undefined';
31004
31005    throw $parseMinErr('esc', 'IMPOSSIBLE');
31006  },
31007
31008  nextId: function(skip, init) {
31009    var id = 'v' + (this.state.nextId++);
31010    if (!skip) {
31011      this.current().vars.push(id + (init ? '=' + init : ''));
31012    }
31013    return id;
31014  },
31015
31016  current: function() {
31017    return this.state[this.state.computing];
31018  }
31019};
31020
31021
31022function ASTInterpreter(astBuilder, $filter) {
31023  this.astBuilder = astBuilder;
31024  this.$filter = $filter;
31025}
31026
31027ASTInterpreter.prototype = {
31028  compile: function(expression, expensiveChecks) {
31029    var self = this;
31030    var ast = this.astBuilder.ast(expression);
31031    this.expression = expression;
31032    this.expensiveChecks = expensiveChecks;
31033    findConstantAndWatchExpressions(ast, self.$filter);
31034    var assignable;
31035    var assign;
31036    if ((assignable = assignableAST(ast))) {
31037      assign = this.recurse(assignable);
31038    }
31039    var toWatch = getInputs(ast.body);
31040    var inputs;
31041    if (toWatch) {
31042      inputs = [];
31043      forEach(toWatch, function(watch, key) {
31044        var input = self.recurse(watch);
31045        watch.input = input;
31046        inputs.push(input);
31047        watch.watchId = key;
31048      });
31049    }
31050    var expressions = [];
31051    forEach(ast.body, function(expression) {
31052      expressions.push(self.recurse(expression.expression));
31053    });
31054    var fn = ast.body.length === 0 ? function() {} :
31055             ast.body.length === 1 ? expressions[0] :
31056             function(scope, locals) {
31057               var lastValue;
31058               forEach(expressions, function(exp) {
31059                 lastValue = exp(scope, locals);
31060               });
31061               return lastValue;
31062             };
31063    if (assign) {
31064      fn.assign = function(scope, value, locals) {
31065        return assign(scope, locals, value);
31066      };
31067    }
31068    if (inputs) {
31069      fn.inputs = inputs;
31070    }
31071    fn.literal = isLiteral(ast);
31072    fn.constant = isConstant(ast);
31073    return fn;
31074  },
31075
31076  recurse: function(ast, context, create) {
31077    var left, right, self = this, args, expression;
31078    if (ast.input) {
31079      return this.inputs(ast.input, ast.watchId);
31080    }
31081    switch (ast.type) {
31082    case AST.Literal:
31083      return this.value(ast.value, context);
31084    case AST.UnaryExpression:
31085      right = this.recurse(ast.argument);
31086      return this['unary' + ast.operator](right, context);
31087    case AST.BinaryExpression:
31088      left = this.recurse(ast.left);
31089      right = this.recurse(ast.right);
31090      return this['binary' + ast.operator](left, right, context);
31091    case AST.LogicalExpression:
31092      left = this.recurse(ast.left);
31093      right = this.recurse(ast.right);
31094      return this['binary' + ast.operator](left, right, context);
31095    case AST.ConditionalExpression:
31096      return this['ternary?:'](
31097        this.recurse(ast.test),
31098        this.recurse(ast.alternate),
31099        this.recurse(ast.consequent),
31100        context
31101      );
31102    case AST.Identifier:
31103      ensureSafeMemberName(ast.name, self.expression);
31104      return self.identifier(ast.name,
31105                             self.expensiveChecks || isPossiblyDangerousMemberName(ast.name),
31106                             context, create, self.expression);
31107    case AST.MemberExpression:
31108      left = this.recurse(ast.object, false, !!create);
31109      if (!ast.computed) {
31110        ensureSafeMemberName(ast.property.name, self.expression);
31111        right = ast.property.name;
31112      }
31113      if (ast.computed) right = this.recurse(ast.property);
31114      return ast.computed ?
31115        this.computedMember(left, right, context, create, self.expression) :
31116        this.nonComputedMember(left, right, self.expensiveChecks, context, create, self.expression);
31117    case AST.CallExpression:
31118      args = [];
31119      forEach(ast.arguments, function(expr) {
31120        args.push(self.recurse(expr));
31121      });
31122      if (ast.filter) right = this.$filter(ast.callee.name);
31123      if (!ast.filter) right = this.recurse(ast.callee, true);
31124      return ast.filter ?
31125        function(scope, locals, assign, inputs) {
31126          var values = [];
31127          for (var i = 0; i < args.length; ++i) {
31128            values.push(args[i](scope, locals, assign, inputs));
31129          }
31130          var value = right.apply(undefined, values, inputs);
31131          return context ? {context: undefined, name: undefined, value: value} : value;
31132        } :
31133        function(scope, locals, assign, inputs) {
31134          var rhs = right(scope, locals, assign, inputs);
31135          var value;
31136          if (rhs.value != null) {
31137            ensureSafeObject(rhs.context, self.expression);
31138            ensureSafeFunction(rhs.value, self.expression);
31139            var values = [];
31140            for (var i = 0; i < args.length; ++i) {
31141              values.push(ensureSafeObject(args[i](scope, locals, assign, inputs), self.expression));
31142            }
31143            value = ensureSafeObject(rhs.value.apply(rhs.context, values), self.expression);
31144          }
31145          return context ? {value: value} : value;
31146        };
31147    case AST.AssignmentExpression:
31148      left = this.recurse(ast.left, true, 1);
31149      right = this.recurse(ast.right);
31150      return function(scope, locals, assign, inputs) {
31151        var lhs = left(scope, locals, assign, inputs);
31152        var rhs = right(scope, locals, assign, inputs);
31153        ensureSafeObject(lhs.value, self.expression);
31154        ensureSafeAssignContext(lhs.context);
31155        lhs.context[lhs.name] = rhs;
31156        return context ? {value: rhs} : rhs;
31157      };
31158    case AST.ArrayExpression:
31159      args = [];
31160      forEach(ast.elements, function(expr) {
31161        args.push(self.recurse(expr));
31162      });
31163      return function(scope, locals, assign, inputs) {
31164        var value = [];
31165        for (var i = 0; i < args.length; ++i) {
31166          value.push(args[i](scope, locals, assign, inputs));
31167        }
31168        return context ? {value: value} : value;
31169      };
31170    case AST.ObjectExpression:
31171      args = [];
31172      forEach(ast.properties, function(property) {
31173        args.push({key: property.key.type === AST.Identifier ?
31174                        property.key.name :
31175                        ('' + property.key.value),
31176                   value: self.recurse(property.value)
31177        });
31178      });
31179      return function(scope, locals, assign, inputs) {
31180        var value = {};
31181        for (var i = 0; i < args.length; ++i) {
31182          value[args[i].key] = args[i].value(scope, locals, assign, inputs);
31183        }
31184        return context ? {value: value} : value;
31185      };
31186    case AST.ThisExpression:
31187      return function(scope) {
31188        return context ? {value: scope} : scope;
31189      };
31190    case AST.NGValueParameter:
31191      return function(scope, locals, assign, inputs) {
31192        return context ? {value: assign} : assign;
31193      };
31194    }
31195  },
31196
31197  'unary+': function(argument, context) {
31198    return function(scope, locals, assign, inputs) {
31199      var arg = argument(scope, locals, assign, inputs);
31200      if (isDefined(arg)) {
31201        arg = +arg;
31202      } else {
31203        arg = 0;
31204      }
31205      return context ? {value: arg} : arg;
31206    };
31207  },
31208  'unary-': function(argument, context) {
31209    return function(scope, locals, assign, inputs) {
31210      var arg = argument(scope, locals, assign, inputs);
31211      if (isDefined(arg)) {
31212        arg = -arg;
31213      } else {
31214        arg = 0;
31215      }
31216      return context ? {value: arg} : arg;
31217    };
31218  },
31219  'unary!': function(argument, context) {
31220    return function(scope, locals, assign, inputs) {
31221      var arg = !argument(scope, locals, assign, inputs);
31222      return context ? {value: arg} : arg;
31223    };
31224  },
31225  'binary+': function(left, right, context) {
31226    return function(scope, locals, assign, inputs) {
31227      var lhs = left(scope, locals, assign, inputs);
31228      var rhs = right(scope, locals, assign, inputs);
31229      var arg = plusFn(lhs, rhs);
31230      return context ? {value: arg} : arg;
31231    };
31232  },
31233  'binary-': function(left, right, context) {
31234    return function(scope, locals, assign, inputs) {
31235      var lhs = left(scope, locals, assign, inputs);
31236      var rhs = right(scope, locals, assign, inputs);
31237      var arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0);
31238      return context ? {value: arg} : arg;
31239    };
31240  },
31241  'binary*': function(left, right, context) {
31242    return function(scope, locals, assign, inputs) {
31243      var arg = left(scope, locals, assign, inputs) * right(scope, locals, assign, inputs);
31244      return context ? {value: arg} : arg;
31245    };
31246  },
31247  'binary/': function(left, right, context) {
31248    return function(scope, locals, assign, inputs) {
31249      var arg = left(scope, locals, assign, inputs) / right(scope, locals, assign, inputs);
31250      return context ? {value: arg} : arg;
31251    };
31252  },
31253  'binary%': function(left, right, context) {
31254    return function(scope, locals, assign, inputs) {
31255      var arg = left(scope, locals, assign, inputs) % right(scope, locals, assign, inputs);
31256      return context ? {value: arg} : arg;
31257    };
31258  },
31259  'binary===': function(left, right, context) {
31260    return function(scope, locals, assign, inputs) {
31261      var arg = left(scope, locals, assign, inputs) === right(scope, locals, assign, inputs);
31262      return context ? {value: arg} : arg;
31263    };
31264  },
31265  'binary!==': function(left, right, context) {
31266    return function(scope, locals, assign, inputs) {
31267      var arg = left(scope, locals, assign, inputs) !== right(scope, locals, assign, inputs);
31268      return context ? {value: arg} : arg;
31269    };
31270  },
31271  'binary==': function(left, right, context) {
31272    return function(scope, locals, assign, inputs) {
31273      var arg = left(scope, locals, assign, inputs) == right(scope, locals, assign, inputs);
31274      return context ? {value: arg} : arg;
31275    };
31276  },
31277  'binary!=': function(left, right, context) {
31278    return function(scope, locals, assign, inputs) {
31279      var arg = left(scope, locals, assign, inputs) != right(scope, locals, assign, inputs);
31280      return context ? {value: arg} : arg;
31281    };
31282  },
31283  'binary<': function(left, right, context) {
31284    return function(scope, locals, assign, inputs) {
31285      var arg = left(scope, locals, assign, inputs) < right(scope, locals, assign, inputs);
31286      return context ? {value: arg} : arg;
31287    };
31288  },
31289  'binary>': function(left, right, context) {
31290    return function(scope, locals, assign, inputs) {
31291      var arg = left(scope, locals, assign, inputs) > right(scope, locals, assign, inputs);
31292      return context ? {value: arg} : arg;
31293    };
31294  },
31295  'binary<=': function(left, right, context) {
31296    return function(scope, locals, assign, inputs) {
31297      var arg = left(scope, locals, assign, inputs) <= right(scope, locals, assign, inputs);
31298      return context ? {value: arg} : arg;
31299    };
31300  },
31301  'binary>=': function(left, right, context) {
31302    return function(scope, locals, assign, inputs) {
31303      var arg = left(scope, locals, assign, inputs) >= right(scope, locals, assign, inputs);
31304      return context ? {value: arg} : arg;
31305    };
31306  },
31307  'binary&&': function(left, right, context) {
31308    return function(scope, locals, assign, inputs) {
31309      var arg = left(scope, locals, assign, inputs) && right(scope, locals, assign, inputs);
31310      return context ? {value: arg} : arg;
31311    };
31312  },
31313  'binary||': function(left, right, context) {
31314    return function(scope, locals, assign, inputs) {
31315      var arg = left(scope, locals, assign, inputs) || right(scope, locals, assign, inputs);
31316      return context ? {value: arg} : arg;
31317    };
31318  },
31319  'ternary?:': function(test, alternate, consequent, context) {
31320    return function(scope, locals, assign, inputs) {
31321      var arg = test(scope, locals, assign, inputs) ? alternate(scope, locals, assign, inputs) : consequent(scope, locals, assign, inputs);
31322      return context ? {value: arg} : arg;
31323    };
31324  },
31325  value: function(value, context) {
31326    return function() { return context ? {context: undefined, name: undefined, value: value} : value; };
31327  },
31328  identifier: function(name, expensiveChecks, context, create, expression) {
31329    return function(scope, locals, assign, inputs) {
31330      var base = locals && (name in locals) ? locals : scope;
31331      if (create && create !== 1 && base && !(base[name])) {
31332        base[name] = {};
31333      }
31334      var value = base ? base[name] : undefined;
31335      if (expensiveChecks) {
31336        ensureSafeObject(value, expression);
31337      }
31338      if (context) {
31339        return {context: base, name: name, value: value};
31340      } else {
31341        return value;
31342      }
31343    };
31344  },
31345  computedMember: function(left, right, context, create, expression) {
31346    return function(scope, locals, assign, inputs) {
31347      var lhs = left(scope, locals, assign, inputs);
31348      var rhs;
31349      var value;
31350      if (lhs != null) {
31351        rhs = right(scope, locals, assign, inputs);
31352        rhs = getStringValue(rhs);
31353        ensureSafeMemberName(rhs, expression);
31354        if (create && create !== 1 && lhs && !(lhs[rhs])) {
31355          lhs[rhs] = {};
31356        }
31357        value = lhs[rhs];
31358        ensureSafeObject(value, expression);
31359      }
31360      if (context) {
31361        return {context: lhs, name: rhs, value: value};
31362      } else {
31363        return value;
31364      }
31365    };
31366  },
31367  nonComputedMember: function(left, right, expensiveChecks, context, create, expression) {
31368    return function(scope, locals, assign, inputs) {
31369      var lhs = left(scope, locals, assign, inputs);
31370      if (create && create !== 1 && lhs && !(lhs[right])) {
31371        lhs[right] = {};
31372      }
31373      var value = lhs != null ? lhs[right] : undefined;
31374      if (expensiveChecks || isPossiblyDangerousMemberName(right)) {
31375        ensureSafeObject(value, expression);
31376      }
31377      if (context) {
31378        return {context: lhs, name: right, value: value};
31379      } else {
31380        return value;
31381      }
31382    };
31383  },
31384  inputs: function(input, watchId) {
31385    return function(scope, value, locals, inputs) {
31386      if (inputs) return inputs[watchId];
31387      return input(scope, value, locals);
31388    };
31389  }
31390};
31391
31392/**
31393 * @constructor
31394 */
31395var Parser = function(lexer, $filter, options) {
31396  this.lexer = lexer;
31397  this.$filter = $filter;
31398  this.options = options;
31399  this.ast = new AST(this.lexer);
31400  this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) :
31401                                   new ASTCompiler(this.ast, $filter);
31402};
31403
31404Parser.prototype = {
31405  constructor: Parser,
31406
31407  parse: function(text) {
31408    return this.astCompiler.compile(text, this.options.expensiveChecks);
31409  }
31410};
31411
31412var getterFnCacheDefault = createMap();
31413var getterFnCacheExpensive = createMap();
31414
31415function isPossiblyDangerousMemberName(name) {
31416  return name == 'constructor';
31417}
31418
31419var objectValueOf = Object.prototype.valueOf;
31420
31421function getValueOf(value) {
31422  return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value);
31423}
31424
31425///////////////////////////////////
31426
31427/**
31428 * @ngdoc service
31429 * @name $parse
31430 * @kind function
31431 *
31432 * @description
31433 *
31434 * Converts Angular {@link guide/expression expression} into a function.
31435 *
31436 * ```js
31437 *   var getter = $parse('user.name');
31438 *   var setter = getter.assign;
31439 *   var context = {user:{name:'angular'}};
31440 *   var locals = {user:{name:'local'}};
31441 *
31442 *   expect(getter(context)).toEqual('angular');
31443 *   setter(context, 'newValue');
31444 *   expect(context.user.name).toEqual('newValue');
31445 *   expect(getter(context, locals)).toEqual('local');
31446 * ```
31447 *
31448 *
31449 * @param {string} expression String expression to compile.
31450 * @returns {function(context, locals)} a function which represents the compiled expression:
31451 *
31452 *    * `context` – `{object}` – an object against which any expressions embedded in the strings
31453 *      are evaluated against (typically a scope object).
31454 *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
31455 *      `context`.
31456 *
31457 *    The returned function also has the following properties:
31458 *      * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
31459 *        literal.
31460 *      * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
31461 *        constant literals.
31462 *      * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
31463 *        set to a function to change its value on the given context.
31464 *
31465 */
31466
31467
31468/**
31469 * @ngdoc provider
31470 * @name $parseProvider
31471 *
31472 * @description
31473 * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
31474 *  service.
31475 */
31476function $ParseProvider() {
31477  var cacheDefault = createMap();
31478  var cacheExpensive = createMap();
31479
31480  this.$get = ['$filter', function($filter) {
31481    var noUnsafeEval = csp().noUnsafeEval;
31482    var $parseOptions = {
31483          csp: noUnsafeEval,
31484          expensiveChecks: false
31485        },
31486        $parseOptionsExpensive = {
31487          csp: noUnsafeEval,
31488          expensiveChecks: true
31489        };
31490
31491    return function $parse(exp, interceptorFn, expensiveChecks) {
31492      var parsedExpression, oneTime, cacheKey;
31493
31494      switch (typeof exp) {
31495        case 'string':
31496          exp = exp.trim();
31497          cacheKey = exp;
31498
31499          var cache = (expensiveChecks ? cacheExpensive : cacheDefault);
31500          parsedExpression = cache[cacheKey];
31501
31502          if (!parsedExpression) {
31503            if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
31504              oneTime = true;
31505              exp = exp.substring(2);
31506            }
31507            var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions;
31508            var lexer = new Lexer(parseOptions);
31509            var parser = new Parser(lexer, $filter, parseOptions);
31510            parsedExpression = parser.parse(exp);
31511            if (parsedExpression.constant) {
31512              parsedExpression.$$watchDelegate = constantWatchDelegate;
31513            } else if (oneTime) {
31514              parsedExpression.$$watchDelegate = parsedExpression.literal ?
31515                  oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
31516            } else if (parsedExpression.inputs) {
31517              parsedExpression.$$watchDelegate = inputsWatchDelegate;
31518            }
31519            cache[cacheKey] = parsedExpression;
31520          }
31521          return addInterceptor(parsedExpression, interceptorFn);
31522
31523        case 'function':
31524          return addInterceptor(exp, interceptorFn);
31525
31526        default:
31527          return noop;
31528      }
31529    };
31530
31531    function expressionInputDirtyCheck(newValue, oldValueOfValue) {
31532
31533      if (newValue == null || oldValueOfValue == null) { // null/undefined
31534        return newValue === oldValueOfValue;
31535      }
31536
31537      if (typeof newValue === 'object') {
31538
31539        // attempt to convert the value to a primitive type
31540        // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
31541        //             be cheaply dirty-checked
31542        newValue = getValueOf(newValue);
31543
31544        if (typeof newValue === 'object') {
31545          // objects/arrays are not supported - deep-watching them would be too expensive
31546          return false;
31547        }
31548
31549        // fall-through to the primitive equality check
31550      }
31551
31552      //Primitive or NaN
31553      return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue);
31554    }
31555
31556    function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) {
31557      var inputExpressions = parsedExpression.inputs;
31558      var lastResult;
31559
31560      if (inputExpressions.length === 1) {
31561        var oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails
31562        inputExpressions = inputExpressions[0];
31563        return scope.$watch(function expressionInputWatch(scope) {
31564          var newInputValue = inputExpressions(scope);
31565          if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf)) {
31566            lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]);
31567            oldInputValueOf = newInputValue && getValueOf(newInputValue);
31568          }
31569          return lastResult;
31570        }, listener, objectEquality, prettyPrintExpression);
31571      }
31572
31573      var oldInputValueOfValues = [];
31574      var oldInputValues = [];
31575      for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
31576        oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails
31577        oldInputValues[i] = null;
31578      }
31579
31580      return scope.$watch(function expressionInputsWatch(scope) {
31581        var changed = false;
31582
31583        for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
31584          var newInputValue = inputExpressions[i](scope);
31585          if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) {
31586            oldInputValues[i] = newInputValue;
31587            oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
31588          }
31589        }
31590
31591        if (changed) {
31592          lastResult = parsedExpression(scope, undefined, undefined, oldInputValues);
31593        }
31594
31595        return lastResult;
31596      }, listener, objectEquality, prettyPrintExpression);
31597    }
31598
31599    function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression) {
31600      var unwatch, lastValue;
31601      return unwatch = scope.$watch(function oneTimeWatch(scope) {
31602        return parsedExpression(scope);
31603      }, function oneTimeListener(value, old, scope) {
31604        lastValue = value;
31605        if (isFunction(listener)) {
31606          listener.apply(this, arguments);
31607        }
31608        if (isDefined(value)) {
31609          scope.$$postDigest(function() {
31610            if (isDefined(lastValue)) {
31611              unwatch();
31612            }
31613          });
31614        }
31615      }, objectEquality);
31616    }
31617
31618    function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) {
31619      var unwatch, lastValue;
31620      return unwatch = scope.$watch(function oneTimeWatch(scope) {
31621        return parsedExpression(scope);
31622      }, function oneTimeListener(value, old, scope) {
31623        lastValue = value;
31624        if (isFunction(listener)) {
31625          listener.call(this, value, old, scope);
31626        }
31627        if (isAllDefined(value)) {
31628          scope.$$postDigest(function() {
31629            if (isAllDefined(lastValue)) unwatch();
31630          });
31631        }
31632      }, objectEquality);
31633
31634      function isAllDefined(value) {
31635        var allDefined = true;
31636        forEach(value, function(val) {
31637          if (!isDefined(val)) allDefined = false;
31638        });
31639        return allDefined;
31640      }
31641    }
31642
31643    function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
31644      var unwatch;
31645      return unwatch = scope.$watch(function constantWatch(scope) {
31646        return parsedExpression(scope);
31647      }, function constantListener(value, old, scope) {
31648        if (isFunction(listener)) {
31649          listener.apply(this, arguments);
31650        }
31651        unwatch();
31652      }, objectEquality);
31653    }
31654
31655    function addInterceptor(parsedExpression, interceptorFn) {
31656      if (!interceptorFn) return parsedExpression;
31657      var watchDelegate = parsedExpression.$$watchDelegate;
31658
31659      var regularWatch =
31660          watchDelegate !== oneTimeLiteralWatchDelegate &&
31661          watchDelegate !== oneTimeWatchDelegate;
31662
31663      var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) {
31664        var value = parsedExpression(scope, locals, assign, inputs);
31665        return interceptorFn(value, scope, locals);
31666      } : function oneTimeInterceptedExpression(scope, locals, assign, inputs) {
31667        var value = parsedExpression(scope, locals, assign, inputs);
31668        var result = interceptorFn(value, scope, locals);
31669        // we only return the interceptor's result if the
31670        // initial value is defined (for bind-once)
31671        return isDefined(value) ? result : value;
31672      };
31673
31674      // Propagate $$watchDelegates other then inputsWatchDelegate
31675      if (parsedExpression.$$watchDelegate &&
31676          parsedExpression.$$watchDelegate !== inputsWatchDelegate) {
31677        fn.$$watchDelegate = parsedExpression.$$watchDelegate;
31678      } else if (!interceptorFn.$stateful) {
31679        // If there is an interceptor, but no watchDelegate then treat the interceptor like
31680        // we treat filters - it is assumed to be a pure function unless flagged with $stateful
31681        fn.$$watchDelegate = inputsWatchDelegate;
31682        fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression];
31683      }
31684
31685      return fn;
31686    }
31687  }];
31688}
31689
31690/**
31691 * @ngdoc service
31692 * @name $q
31693 * @requires $rootScope
31694 *
31695 * @description
31696 * A service that helps you run functions asynchronously, and use their return values (or exceptions)
31697 * when they are done processing.
31698 *
31699 * This is an implementation of promises/deferred objects inspired by
31700 * [Kris Kowal's Q](https://github.com/kriskowal/q).
31701 *
31702 * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
31703 * implementations, and the other which resembles ES6 promises to some degree.
31704 *
31705 * # $q constructor
31706 *
31707 * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
31708 * function as the first argument. This is similar to the native Promise implementation from ES6 Harmony,
31709 * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
31710 *
31711 * While the constructor-style use is supported, not all of the supporting methods from ES6 Harmony promises are
31712 * available yet.
31713 *
31714 * It can be used like so:
31715 *
31716 * ```js
31717 *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
31718 *   // are available in the current lexical scope (they could have been injected or passed in).
31719 *
31720 *   function asyncGreet(name) {
31721 *     // perform some asynchronous operation, resolve or reject the promise when appropriate.
31722 *     return $q(function(resolve, reject) {
31723 *       setTimeout(function() {
31724 *         if (okToGreet(name)) {
31725 *           resolve('Hello, ' + name + '!');
31726 *         } else {
31727 *           reject('Greeting ' + name + ' is not allowed.');
31728 *         }
31729 *       }, 1000);
31730 *     });
31731 *   }
31732 *
31733 *   var promise = asyncGreet('Robin Hood');
31734 *   promise.then(function(greeting) {
31735 *     alert('Success: ' + greeting);
31736 *   }, function(reason) {
31737 *     alert('Failed: ' + reason);
31738 *   });
31739 * ```
31740 *
31741 * Note: progress/notify callbacks are not currently supported via the ES6-style interface.
31742 *
31743 * However, the more traditional CommonJS-style usage is still available, and documented below.
31744 *
31745 * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
31746 * interface for interacting with an object that represents the result of an action that is
31747 * performed asynchronously, and may or may not be finished at any given point in time.
31748 *
31749 * From the perspective of dealing with error handling, deferred and promise APIs are to
31750 * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
31751 *
31752 * ```js
31753 *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
31754 *   // are available in the current lexical scope (they could have been injected or passed in).
31755 *
31756 *   function asyncGreet(name) {
31757 *     var deferred = $q.defer();
31758 *
31759 *     setTimeout(function() {
31760 *       deferred.notify('About to greet ' + name + '.');
31761 *
31762 *       if (okToGreet(name)) {
31763 *         deferred.resolve('Hello, ' + name + '!');
31764 *       } else {
31765 *         deferred.reject('Greeting ' + name + ' is not allowed.');
31766 *       }
31767 *     }, 1000);
31768 *
31769 *     return deferred.promise;
31770 *   }
31771 *
31772 *   var promise = asyncGreet('Robin Hood');
31773 *   promise.then(function(greeting) {
31774 *     alert('Success: ' + greeting);
31775 *   }, function(reason) {
31776 *     alert('Failed: ' + reason);
31777 *   }, function(update) {
31778 *     alert('Got notification: ' + update);
31779 *   });
31780 * ```
31781 *
31782 * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
31783 * comes in the way of guarantees that promise and deferred APIs make, see
31784 * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md.
31785 *
31786 * Additionally the promise api allows for composition that is very hard to do with the
31787 * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
31788 * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
31789 * section on serial or parallel joining of promises.
31790 *
31791 * # The Deferred API
31792 *
31793 * A new instance of deferred is constructed by calling `$q.defer()`.
31794 *
31795 * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
31796 * that can be used for signaling the successful or unsuccessful completion, as well as the status
31797 * of the task.
31798 *
31799 * **Methods**
31800 *
31801 * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection
31802 *   constructed via `$q.reject`, the promise will be rejected instead.
31803 * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
31804 *   resolving it with a rejection constructed via `$q.reject`.
31805 * - `notify(value)` - provides updates on the status of the promise's execution. This may be called
31806 *   multiple times before the promise is either resolved or rejected.
31807 *
31808 * **Properties**
31809 *
31810 * - promise – `{Promise}` – promise object associated with this deferred.
31811 *
31812 *
31813 * # The Promise API
31814 *
31815 * A new promise instance is created when a deferred instance is created and can be retrieved by
31816 * calling `deferred.promise`.
31817 *
31818 * The purpose of the promise object is to allow for interested parties to get access to the result
31819 * of the deferred task when it completes.
31820 *
31821 * **Methods**
31822 *
31823 * - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or
31824 *   will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
31825 *   as soon as the result is available. The callbacks are called with a single argument: the result
31826 *   or rejection reason. Additionally, the notify callback may be called zero or more times to
31827 *   provide a progress indication, before the promise is resolved or rejected.
31828 *
31829 *   This method *returns a new promise* which is resolved or rejected via the return value of the
31830 *   `successCallback`, `errorCallback` (unless that value is a promise, in which case it is resolved
31831 *   with the value which is resolved in that promise using
31832 *   [promise chaining](http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promises-queues)).
31833 *   It also notifies via the return value of the `notifyCallback` method. The promise cannot be
31834 *   resolved or rejected from the notifyCallback method.
31835 *
31836 * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
31837 *
31838 * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise,
31839 *   but to do so without modifying the final value. This is useful to release resources or do some
31840 *   clean-up that needs to be done whether the promise was rejected or resolved. See the [full
31841 *   specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
31842 *   more information.
31843 *
31844 * # Chaining promises
31845 *
31846 * Because calling the `then` method of a promise returns a new derived promise, it is easily
31847 * possible to create a chain of promises:
31848 *
31849 * ```js
31850 *   promiseB = promiseA.then(function(result) {
31851 *     return result + 1;
31852 *   });
31853 *
31854 *   // promiseB will be resolved immediately after promiseA is resolved and its value
31855 *   // will be the result of promiseA incremented by 1
31856 * ```
31857 *
31858 * It is possible to create chains of any length and since a promise can be resolved with another
31859 * promise (which will defer its resolution further), it is possible to pause/defer resolution of
31860 * the promises at any point in the chain. This makes it possible to implement powerful APIs like
31861 * $http's response interceptors.
31862 *
31863 *
31864 * # Differences between Kris Kowal's Q and $q
31865 *
31866 *  There are two main differences:
31867 *
31868 * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
31869 *   mechanism in angular, which means faster propagation of resolution or rejection into your
31870 *   models and avoiding unnecessary browser repaints, which would result in flickering UI.
31871 * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
31872 *   all the important functionality needed for common async tasks.
31873 *
31874 *  # Testing
31875 *
31876 *  ```js
31877 *    it('should simulate promise', inject(function($q, $rootScope) {
31878 *      var deferred = $q.defer();
31879 *      var promise = deferred.promise;
31880 *      var resolvedValue;
31881 *
31882 *      promise.then(function(value) { resolvedValue = value; });
31883 *      expect(resolvedValue).toBeUndefined();
31884 *
31885 *      // Simulate resolving of promise
31886 *      deferred.resolve(123);
31887 *      // Note that the 'then' function does not get called synchronously.
31888 *      // This is because we want the promise API to always be async, whether or not
31889 *      // it got called synchronously or asynchronously.
31890 *      expect(resolvedValue).toBeUndefined();
31891 *
31892 *      // Propagate promise resolution to 'then' functions using $apply().
31893 *      $rootScope.$apply();
31894 *      expect(resolvedValue).toEqual(123);
31895 *    }));
31896 *  ```
31897 *
31898 * @param {function(function, function)} resolver Function which is responsible for resolving or
31899 *   rejecting the newly created promise. The first parameter is a function which resolves the
31900 *   promise, the second parameter is a function which rejects the promise.
31901 *
31902 * @returns {Promise} The newly created promise.
31903 */
31904function $QProvider() {
31905
31906  this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
31907    return qFactory(function(callback) {
31908      $rootScope.$evalAsync(callback);
31909    }, $exceptionHandler);
31910  }];
31911}
31912
31913function $$QProvider() {
31914  this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) {
31915    return qFactory(function(callback) {
31916      $browser.defer(callback);
31917    }, $exceptionHandler);
31918  }];
31919}
31920
31921/**
31922 * Constructs a promise manager.
31923 *
31924 * @param {function(function)} nextTick Function for executing functions in the next turn.
31925 * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
31926 *     debugging purposes.
31927 * @returns {object} Promise manager.
31928 */
31929function qFactory(nextTick, exceptionHandler) {
31930  var $qMinErr = minErr('$q', TypeError);
31931  function callOnce(self, resolveFn, rejectFn) {
31932    var called = false;
31933    function wrap(fn) {
31934      return function(value) {
31935        if (called) return;
31936        called = true;
31937        fn.call(self, value);
31938      };
31939    }
31940
31941    return [wrap(resolveFn), wrap(rejectFn)];
31942  }
31943
31944  /**
31945   * @ngdoc method
31946   * @name ng.$q#defer
31947   * @kind function
31948   *
31949   * @description
31950   * Creates a `Deferred` object which represents a task which will finish in the future.
31951   *
31952   * @returns {Deferred} Returns a new instance of deferred.
31953   */
31954  var defer = function() {
31955    return new Deferred();
31956  };
31957
31958  function Promise() {
31959    this.$$state = { status: 0 };
31960  }
31961
31962  extend(Promise.prototype, {
31963    then: function(onFulfilled, onRejected, progressBack) {
31964      if (isUndefined(onFulfilled) && isUndefined(onRejected) && isUndefined(progressBack)) {
31965        return this;
31966      }
31967      var result = new Deferred();
31968
31969      this.$$state.pending = this.$$state.pending || [];
31970      this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
31971      if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);
31972
31973      return result.promise;
31974    },
31975
31976    "catch": function(callback) {
31977      return this.then(null, callback);
31978    },
31979
31980    "finally": function(callback, progressBack) {
31981      return this.then(function(value) {
31982        return handleCallback(value, true, callback);
31983      }, function(error) {
31984        return handleCallback(error, false, callback);
31985      }, progressBack);
31986    }
31987  });
31988
31989  //Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native
31990  function simpleBind(context, fn) {
31991    return function(value) {
31992      fn.call(context, value);
31993    };
31994  }
31995
31996  function processQueue(state) {
31997    var fn, deferred, pending;
31998
31999    pending = state.pending;
32000    state.processScheduled = false;
32001    state.pending = undefined;
32002    for (var i = 0, ii = pending.length; i < ii; ++i) {
32003      deferred = pending[i][0];
32004      fn = pending[i][state.status];
32005      try {
32006        if (isFunction(fn)) {
32007          deferred.resolve(fn(state.value));
32008        } else if (state.status === 1) {
32009          deferred.resolve(state.value);
32010        } else {
32011          deferred.reject(state.value);
32012        }
32013      } catch (e) {
32014        deferred.reject(e);
32015        exceptionHandler(e);
32016      }
32017    }
32018  }
32019
32020  function scheduleProcessQueue(state) {
32021    if (state.processScheduled || !state.pending) return;
32022    state.processScheduled = true;
32023    nextTick(function() { processQueue(state); });
32024  }
32025
32026  function Deferred() {
32027    this.promise = new Promise();
32028    //Necessary to support unbound execution :/
32029    this.resolve = simpleBind(this, this.resolve);
32030    this.reject = simpleBind(this, this.reject);
32031    this.notify = simpleBind(this, this.notify);
32032  }
32033
32034  extend(Deferred.prototype, {
32035    resolve: function(val) {
32036      if (this.promise.$$state.status) return;
32037      if (val === this.promise) {
32038        this.$$reject($qMinErr(
32039          'qcycle',
32040          "Expected promise to be resolved with value other than 
32040itself '{0}'",
32041          val));
32042      } else {
32043        this.$$resolve(val);
32044      }
32045
32046    },
32047
32048    $$resolve: function(val) {
32049      var then, fns;
32050
32051      fns = callOnce(this, this.$$resolve, this.$$reject);
32052      try {
32053        if ((isObject(val) || isFunction(val))) then = val && val.then;
32054        if (isFunction(then)) {
32055          this.promise.$$state.status = -1;
32056          then.call(val, fns[0], fns[1], this.notify);
32057        } else {
32058          this.promise.$$state.value = val;
32059          this.promise.$$state.status = 1;
32060          scheduleProcessQueue(this.promise.$$state);
32061        }
32062      } catch (e) {
32063        fns[1](e);
32064        exceptionHandler(e);
32065      }
32066    },
32067
32068    reject: function(reason) {
32069      if (this.promise.$$state.status) return;
32070      this.$$reject(reason);
32071    },
32072
32073    $$reject: function(reason) {
32074      this.promise.$$state.value = reason;
32075      this.promise.$$state.status = 2;
32076      scheduleProcessQueue(this.promise.$$state);
32077    },
32078
32079    notify: function(progress) {
32080      var callbacks = this.promise.$$state.pending;
32081
32082      if ((this.promise.$$state.status <= 0) && callbacks && callbacks.length) {
32083        nextTick(function() {
32084          var callback, result;
32085          for (var i = 0, ii = callbacks.length; i < ii; i++) {
32086            result = callbacks[i][0];
32087            callback = callbacks[i][3];
32088            try {
32089              result.notify(isFunction(callback) ? callback(progress) : progress);
32090            } catch (e) {
32091              exceptionHandler(e);
32092            }
32093          }
32094        });
32095      }
32096    }
32097  });
32098
32099  /**
32100   * @ngdoc method
32101   * @name $q#reject
32102   * @kind function
32103   *
32104   * @description
32105   * Creates a promise that is resolved as rejected with the specified `reason`. This api should be
32106   * used to forward rejection in a chain of promises. If you are dealing with the last promise in
32107   * a promise chain, you don't need to worry about it.
32108   *
32109   * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
32110   * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
32111   * a promise error callback and you want to forward the error to the promise derived from the
32112   * current promise, you have to "rethrow" the error by returning a rejection constructed via
32113   * `reject`.
32114   *
32115   * ```js
32116   *   promiseB = promiseA.then(function(result) {
32117   *     // success: do something and resolve promiseB
32118   *     //          with the old or a new result
32119   *     return result;
32120   *   }, function(reason) {
32121   *     // error: handle the error if possible and
32122   *     //        resolve promiseB with newPromiseOrValue,
32123   *     //        otherwise forward the rejection to promiseB
32124   *     if (canHandle(reason)) {
32125   *      // handle the error and recover
32126   *      return newPromiseOrValue;
32127   *     }
32128   *     return $q.reject(reason);
32129   *   });
32130   * ```
32131   *
32132   * @param {*} reason Constant, message, exception or an object representing the rejection reason.
32133   * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
32134   */
32135  var reject = function(reason) {
32136    var result = new Deferred();
32137    result.reject(reason);
32138    return result.promise;
32139  };
32140
32141  var makePromise = function makePromise(value, resolved) {
32142    var result = new Deferred();
32143    if (resolved) {
32144      result.resolve(value);
32145    } else {
32146      result.reject(value);
32147    }
32148    return result.promise;
32149  };
32150
32151  var handleCallback = function handleCallback(value, isResolved, callback) {
32152    var callbackOutput = null;
32153    try {
32154      if (isFunction(callback)) callbackOutput = callback();
32155    } catch (e) {
32156      return makePromise(e, false);
32157    }
32158    if (isPromiseLike(callbackOutput)) {
32159      return callbackOutput.then(function() {
32160        return makePromise(value, isResolved);
32161      }, function(error) {
32162        return makePromise(error, false);
32163      });
32164    } else {
32165      return makePromise(value, isResolved);
32166    }
32167  };
32168
32169  /**
32170   * @ngdoc method
32171   * @name $q#when
32172   * @kind function
32173   *
32174   * @description
32175   * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
32176   * This is useful when you are dealing with an object that might or might not be a promise, or if
32177   * the promise comes from a source that can't be trusted.
32178   *
32179   * @param {*} value Value or a promise
32180   * @param {Function=} successCallback
32181   * @param {Function=} errorCallback
32182   * @param {Function=} progressCallback
32183   * @returns {Promise} Returns a promise of the passed value or promise
32184   */
32185
32186
32187  var when = function(value, callback, errback, progressBack) {
32188    var result = new Deferred();
32189    result.resolve(value);
32190    return result.promise.then(callback, errback, progressBack);
32191  };
32192
32193  /**
32194   * @ngdoc method
32195   * @name $q#resolve
32196   * @kind function
32197   *
32198   * @description
32199   * Alias of {@link ng.$q#when when} to maintain naming consistency with ES6.
32200   *
32201   * @param {*} value Value or a promise
32202   * @param {Function=} successCallback
32203   * @param {Function=} errorCallback
32204   * @param {Function=} progressCallback
32205   * @returns {Promise} Returns a promise of the passed value or promise
32206   */
32207  var resolve = when;
32208
32209  /**
32210   * @ngdoc method
32211   * @name $q#all
32212   * @kind function
32213   *
32214   * @description
32215   * Combines multiple promises into a single promise that is resolved when all of the input
32216   * promises are resolved.
32217   *
32218   * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
32219   * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
32220   *   each value corresponding to the promise at the same index/key in the `promises` array/hash.
32221   *   If any of the promises is resolved with a rejection, this resulting promise will be rejected
32222   *   with the same rejection value.
32223   */
32224
32225  function all(promises) {
32226    var deferred = new Deferred(),
32227        counter = 0,
32228        results = isArray(promises) ? [] : {};
32229
32230    forEach(promises, function(promise, key) {
32231      counter++;
32232      when(promise).then(function(value) {
32233        if (results.hasOwnProperty(key)) return;
32234        results[key] = value;
32235        if (!(--counter)) deferred.resolve(results);
32236      }, function(reason) {
32237        if (results.hasOwnProperty(key)) return;
32238        deferred.reject(reason);
32239      });
32240    });
32241
32242    if (counter === 0) {
32243      deferred.resolve(results);
32244    }
32245
32246    return deferred.promise;
32247  }
32248
32249  var $Q = function Q(resolver) {
32250    if (!isFunction(resolver)) {
32251      throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver);
32252    }
32253
32254    if (!(this instanceof Q)) {
32255      // More useful when $Q is the Promise itself.
32256      return new Q(resolver);
32257    }
32258
32259    var deferred = new Deferred();
32260
32261    function resolveFn(value) {
32262      deferred.resolve(value);
32263    }
32264
32265    function rejectFn(reason) {
32266      deferred.reject(reason);
32267    }
32268
32269    resolver(resolveFn, rejectFn);
32270
32271    return deferred.promise;
32272  };
32273
32274  $Q.defer = defer;
32275  $Q.reject = reject;
32276  $Q.when = when;
32277  $Q.resolve = resolve;
32278  $Q.all = all;
32279
32280  return $Q;
32281}
32282
32283function $$RAFProvider() { //rAF
32284  this.$get = ['$window', '$timeout', function($window, $timeout) {
32285    var requestAnimationFrame = $window.requestAnimationFrame ||
32286                                $window.webkitRequestAnimationFrame;
32287
32288    var cancelAnimationFrame = $window.cancelAnimationFrame ||
32289                               $window.webkitCancelAnimationFrame ||
32290                               $window.webkitCancelRequestAnimationFrame;
32291
32292    var rafSupported = !!requestAnimationFrame;
32293    var raf = rafSupported
32294      ? function(fn) {
32295          var id = requestAnimationFrame(fn);
32296          return function() {
32297            cancelAnimationFrame(id);
32298          };
32299        }
32300      : function(fn) {
32301          var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
32302          return function() {
32303            $timeout.cancel(timer);
32304          };
32305        };
32306
32307    raf.supported = rafSupported;
32308
32309    return raf;
32310  }];
32311}
32312
32313/**
32314 * DESIGN NOTES
32315 *
32316 * The design decisions behind the scope are heavily favored for speed and memory consumption.
32317 *
32318 * The typical use of scope is to watch the expressions, which most of the time return the same
32319 * value as last time so we optimize the operation.
32320 *
32321 * Closures construction is expensive in terms of speed as well as memory:
32322 *   - No closures, instead use prototypical inheritance for API
32323 *   - Internal state needs to be stored on scope directly, which means that private state is
32324 *     exposed as $$____ properties
32325 *
32326 * Loop operations are optimized by using while(count--) { ... }
32327 *   - this means that in order to keep the same order of execution as addition we have to add
32328 *     items to the array at the beginning (unshift) instead of at the end (push)
32329 *
32330 * Child scopes are created and removed often
32331 *   - Using an array would be slow since inserts in middle are expensive so we use linked list
32332 *
32333 * There are few watches then a lot of observers. This is why you don't want the observer to be
32334 * implemented in the same way as watch. Watch requires return of initialization function which
32335 * are expensive to construct.
32336 */
32337
32338
32339/**
32340 * @ngdoc provider
32341 * @name $rootScopeProvider
32342 * @description
32343 *
32344 * Provider for the $rootScope service.
32345 */
32346
32347/**
32348 * @ngdoc method
32349 * @name $rootScopeProvider#digestTtl
32350 * @description
32351 *
32352 * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and
32353 * assuming that the model is unstable.
32354 *
32355 * The current default is 10 iterations.
32356 *
32357 * In complex applications it's possible that the dependencies between `$watch`s will result in
32358 * several digest iterations. However if an application needs more than the default 10 digest
32359 * iterations for its model to stabilize then you should investigate what is causing the model to
32360 * continuously change during the digest.
32361 *
32362 * Increasing the TTL could have performance implications, so you should not change it without
32363 * proper justification.
32364 *
32365 * @param {number} limit The number of digest iterations.
32366 */
32367
32368
32369/**
32370 * @ngdoc service
32371 * @name $rootScope
32372 * @description
32373 *
32374 * Every application has a single root {@link ng.$rootScope.Scope scope}.
32375 * All other scopes are descendant scopes of the root scope. Scopes provide separation
32376 * between the model and the view, via a mechanism for watching the model for changes.
32377 * They also provide an event emission/broadcast and subscription facility. See the
32378 * {@link guide/scope developer guide on scopes}.
32379 */
32380function $RootScopeProvider() {
32381  var TTL = 10;
32382  var $rootScopeMinErr = minErr('$rootScope');
32383  var lastDirtyWatch = null;
32384  var applyAsyncId = null;
32385
32386  this.digestTtl = function(value) {
32387    if (arguments.length) {
32388      TTL = value;
32389    }
32390    return TTL;
32391  };
32392
32393  function createChildScopeClass(parent) {
32394    function ChildScope() {
32395      this.$$watchers = this.$$nextSibling =
32396          this.$$childHead = this.$$childTail = null;
32397      this.$$listeners = {};
32398      this.$$listenerCount = {};
32399      this.$$watchersCount = 0;
32400      this.$id = nextUid();
32401      this.$$ChildScope = null;
32402    }
32403    ChildScope.prototype = parent;
32404    return ChildScope;
32405  }
32406
32407  this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser',
32408      function($injector, $exceptionHandler, $parse, $browser) {
32409
32410    function destroyChildScope($event) {
32411        $event.currentScope.$$destroyed = true;
32412    }
32413
32414    /**
32415     * @ngdoc type
32416     * @name $rootScope.Scope
32417     *
32418     * @description
32419     * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
32420     * {@link auto.$injector $injector}. Child scopes are created using the
32421     * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
32422     * compiled HTML template is executed.) See also the {@link guide/scope Scopes guide} for
32423     * an in-depth introduction and usage examples.
32424     *
32425     *
32426     * # Inheritance
32427     * A scope can inherit from a parent scope, as in this example:
32428     * ```js
32429         var parent = $rootScope;
32430         var child = parent.$new();
32431
32432         parent.salutation = "Hello";
32433         expect(child.salutation).toEqual('Hello');
32434
32435         child.salutation = "Welcome";
32436         expect(child.salutation).toEqual('Welcome');
32437         expect(parent.salutation).toEqual('Hello');
32438     * ```
32439     *
32440     * When interacting with `Scope` in tests, additional helper methods are available on the
32441     * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional
32442     * details.
32443     *
32444     *
32445     * @param {Object.<string, function()>=} providers Map of service factory which need to be
32446     *                                       provided for the current scope. Defaults to {@link ng}.
32447     * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
32448     *                              append/override services provided by `providers`. This is handy
32449     *                              when unit-testing and having the need to override a default
32450     *                              service.
32451     * @returns {Object} Newly created scope.
32452     *
32453     */
32454    function Scope() {
32455      this.$id = nextUid();
32456      this.$$phase = this.$parent = this.$$watchers =
32457                     this.$$nextSibling = this.$$prevSibling =
32458                     this.$$childHead = this.$$childTail = null;
32459      this.$root = this;
32460      this.$$destroyed = false;
32461      this.$$listeners = {};
32462      this.$$listenerCount = {};
32463      this.$$watchersCount = 0;
32464      this.$$isolateBindings = null;
32465    }
32466
32467    /**
32468     * @ngdoc property
32469     * @name $rootScope.Scope#$id
32470     *
32471     * @description
32472     * Unique scope ID (monotonically increasing) useful for debugging.
32473     */
32474
32475     /**
32476      * @ngdoc property
32477      * @name $rootScope.Scope#$parent
32478      *
32479      * @description
32480      * Reference to the parent scope.
32481      */
32482
32483      /**
32484       * @ngdoc property
32485       * @name $rootScope.Scope#$root
32486       *
32487       * @description
32488       * Reference to the root scope.
32489       */
32490
32491    Scope.prototype = {
32492      constructor: Scope,
32493      /**
32494       * @ngdoc method
32495       * @name $rootScope.Scope#$new
32496       * @kind function
32497       *
32498       * @description
32499       * Creates a new child {@link ng.$rootScope.Scope scope}.
32500       *
32501       * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event.
32502       * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
32503       *
32504       * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
32505       * desired for the scope and its child scopes to be permanently detached from the parent and
32506       * thus stop participating in model change detection and listener notification by invoking.
32507       *
32508       * @param {boolean} isolate If true, then the scope does not prototypically inherit from the
32509       *         parent scope. The scope is isolated, as it can not see parent scope properties.
32510       *         When creating widgets, it is useful for the widget to not accidentally read parent
32511       *         state.
32512       *
32513       * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`
32514       *                              of the newly created scope. Defaults to `this` scope if not provided.
32515       *                              This is used when creating a transclude scope to correctly place it
32516       *                              in the scope hierarchy while maintaining the correct prototypical
32517       *                              inheritance.
32518       *
32519       * @returns {Object} The newly created child scope.
32520       *
32521       */
32522      $new: function(isolate, parent) {
32523        var child;
32524
32525        parent = parent || this;
32526
32527        if (isolate) {
32528          child = new Scope();
32529          child.$root = this.$root;
32530        } else {
32531          // Only create a child scope class if somebody asks for one,
32532          // but cache it to allow the VM to optimize lookups.
32533          if (!this.$$ChildScope) {
32534            this.$$ChildScope = createChildScopeClass(this);
32535          }
32536          child = new this.$$ChildScope();
32537        }
32538        child.$parent = parent;
32539        child.$$prevSibling = parent.$$childTail;
32540        if (parent.$$childHead) {
32541          parent.$$childTail.$$nextSibling = child;
32542          parent.$$childTail = child;
32543        } else {
32544          parent.$$childHead = parent.$$childTail = child;
32545        }
32546
32547        // When the new scope is not isolated or we inherit from `this`, and
32548        // the parent scope is destroyed, the property `$$destroyed` is inherited
32549        // prototypically. In all other cases, this property needs to be set
32550        // when the parent scope is destroyed.
32551        // The listener needs to be added after the parent is set
32552        if (isolate || parent != this) child.$on('$destroy', destroyChildScope);
32553
32554        return child;
32555      },
32556
32557      /**
32558       * @ngdoc method
32559       * @name $rootScope.Scope#$watch
32560       * @kind function
32561       *
32562       * @description
32563       * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
32564       *
32565       * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
32566       *   $digest()} and should return the value that will be watched. (`watchExpression` should not change
32567       *   its value when executed multiple times with the same input because it may be executed multiple
32568       *   times by {@link ng.$rootScope.Scope#$digest $digest()}. That is, `watchExpression` should be
32569       *   [idempotent](http://en.wikipedia.org/wiki/Idempotence).
32570       * - The `listener` is called only when the value from the current `watchExpression` and the
32571       *   previous call to `watchExpression` are not equal (with the exception of the initial run,
32572       *   see below). Inequality is determined according to reference inequality,
32573       *   [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators)
32574       *    via the `!==` Javascript operator, unless `objectEquality == true`
32575       *   (see next point)
32576       * - When `objectEquality == true`, inequality of the `watchExpression` is determined
32577       *   according to the {@link angular.equals} function. To save the value of the object for
32578       *   later comparison, the {@link angular.copy} function is used. This therefore means that
32579       *   watching complex objects will have adverse memory and performance implications.
32580       * - The watch `listener` may change the model, which may trigger other `listener`s to fire.
32581       *   This is achieved by rerunning the watchers until no changes are detected. The rerun
32582       *   iteration limit is 10 to prevent an infinite loop deadlock.
32583       *
32584       *
32585       * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
32586       * you can register a `watchExpression` function with no `listener`. (Be prepared for
32587       * multiple calls to your `watchExpression` because it will execute multiple times in a
32588       * single {@link ng.$rootScope.Scope#$digest $digest} cycle if a change is detected.)
32589       *
32590       * After a watcher is registered with the scope, the `listener` fn is called asynchronously
32591       * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
32592       * watcher. In rare cases, this is undesirable because the listener is called when the result
32593       * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
32594       * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
32595       * listener was called due to initialization.
32596       *
32597       *
32598       *
32599       * # Example
32600       * ```js
32601           // let's assume that scope was dependency injected as the $rootScope
32602           var scope = $rootScope;
32603           scope.name = 'misko';
32604           scope.counter = 0;
32605
32606           expect(scope.counter).toEqual(0);
32607           scope.$watch('name', function(newValue, oldValue) {
32608             scope.counter = scope.counter + 1;
32609           });
32610           expect(scope.counter).toEqual(0);
32611
32612           scope.$digest();
32613           // the listener is always called during the first $digest loop after it was registered
32614           expect(scope.counter).toEqual(1);
32615
32616           scope.$digest();
32617           // but now it will not be called unless the value changes
32618           expect(scope.counter).toEqual(1);
32619
32620           scope.name = 'adam';
32621           scope.$digest();
32622           expect(scope.counter).toEqual(2);
32623
32624
32625
32626           // Using a function as a watchExpression
32627           var food;
32628           scope.foodCounter = 0;
32629           expect(scope.foodCounter).toEqual(0);
32630           scope.$watch(
32631             // This function returns the value being watched. It is called for each turn of the $digest loop
32632             function() { return food; },
32633             // This is the change listener, called when the value returned from the above function changes
32634             function(newValue, oldValue) {
32635               if ( newValue !== oldValue ) {
32636                 // Only increment the counter if the value changed
32637                 scope.foodCounter = scope.foodCounter + 1;
32638               }
32639             }
32640           );
32641           // No digest has been run so the counter will be zero
32642           expect(scope.foodCounter).toEqual(0);
32643
32644           // Run the digest but since food has not changed count will still be zero
32645           scope.$digest();
32646           expect(scope.foodCounter).toEqual(0);
32647
32648           // Update food and run digest.  Now the counter will increment
32649           food = 'cheeseburger';
32650           scope.$digest();
32651           expect(scope.foodCounter).toEqual(1);
32652
32653       * ```
32654       *
32655       *
32656       *
32657       * @param {(function()|string)} watchExpression Expression that is evaluated on each
32658       *    {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
32659       *    a call to the `listener`.
32660       *
32661       *    - `string`: Evaluated as {@link guide/expression expression}
32662       *    - `function(scope)`: called with current `scope` as a parameter.
32663       * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value
32664       *    of `watchExpression` changes.
32665       *
32666       *    - `newVal` contains the current value of the `watchExpression`
32667       *    - `oldVal` contains the previous value of the `watchExpression`
32668       *    - `scope` refers to the current scope
32669       * @param {boolean=} objectEquality Compare for object equality using {@link angular.equals} instead of
32670       *     comparing for reference equality.
32671       * @returns {function()} Returns a deregistration function for this listener.
32672       */
32673      $watch: function(watchExp, listener, objectEquality, prettyPrintExpression) {
32674        var get = $parse(watchExp);
32675
32676        if (get.$$watchDelegate) {
32677          return get.$$watchDelegate(this, listener, objectEquality, get, watchExp);
32678        }
32679        var scope = this,
32680            array = scope.$$watchers,
32681            watcher = {
32682              fn: listener,
32683              last: initWatchVal,
32684              get: get,
32685              exp: prettyPrintExpression || watchExp,
32686              eq: !!objectEquality
32687            };
32688
32689        lastDirtyWatch = null;
32690
32691        if (!isFunction(listener)) {
32692          watcher.fn = noop;
32693        }
32694
32695        if (!array) {
32696          array = scope.$$watchers = [];
32697        }
32698        // we use unshift since we use a while loop in $digest for speed.
32699        // the while loop reads in reverse order.
32700        array.unshift(watcher);
32701        incrementWatchersCount(this, 1);
32702
32703        return function deregisterWatch() {
32704          if (arrayRemove(array, watcher) >= 0) {
32705            incrementWatchersCount(scope, -1);
32706          }
32707          lastDirtyWatch = null;
32708        };
32709      },
32710
32711      /**
32712       * @ngdoc method
32713       * @name $rootScope.Scope#$watchGroup
32714       * @kind function
32715       *
32716       * @description
32717       * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`.
32718       * If any one expression in the collection changes the `listener` is executed.
32719       *
32720       * - The items in the `watchExpressions` array are observed via standard $watch operation and are examined on every
32721       *   call to $digest() to see if any items changes.
32722       * - The `listener` is called whenever any expression in the `watchExpressions` array changes.
32723       *
32724       * @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
32725       * watched using {@link ng.$rootScope.Scope#$watch $watch()}
32726       *
32727       * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any
32728       *    expression in `watchExpressions` changes
32729       *    The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
32730       *    those of `watchExpression`
32731       *    and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
32732       *    those of `watchExpression`
32733       *    The `scope` refers to the current scope.
32734       * @returns {function()} Returns a de-registration function for all listeners.
32735       */
32736      $watchGroup: function(watchExpressions, listener) {
32737        var oldValues = new Array(watchExpressions.length);
32738        var newValues = new Array(watchExpressions.length);
32739        var deregisterFns = [];
32740        var self = this;
32741        var changeReactionScheduled = false;
vendor: 10,192 bytes, lines 32742-33004
32742        var firstRun = true;
32743
32744        if (!watchExpressions.length) {
32745          // No expressions means we call the listener ASAP
32746          var shouldCall = true;
32747          self.$evalAsync(function() {
32748            if (shouldCall) listener(newValues, newValues, self);
32749          });
32750          return function deregisterWatchGroup() {
32751            shouldCall = false;
32752          };
32753        }
32754
32755        if (watchExpressions.length === 1) {
32756          // Special case size of one
32757          return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) {
32758            newValues[0] = value;
32759            oldValues[0] = oldValue;
32760            listener(newValues, (value === oldValue) ? newValues : oldValues, scope);
32761          });
32762        }
32763
32764        forEach(watchExpressions, function(expr, i) {
32765          var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
32766            newValues[i] = value;
32767            oldValues[i] = oldValue;
32768            if (!changeReactionScheduled) {
32769              changeReactionScheduled = true;
32770              self.$evalAsync(watchGroupAction);
32771            }
32772          });
32773          deregisterFns.push(unwatchFn);
32774        });
32775
32776        function watchGroupAction() {
32777          changeReactionScheduled = false;
32778
32779          if (firstRun) {
32780            firstRun = false;
32781            listener(newValues, newValues, self);
32782          } else {
32783            listener(newValues, oldValues, self);
32784          }
32785        }
32786
32787        return function deregisterWatchGroup() {
32788          while (deregisterFns.length) {
32789            deregisterFns.shift()();
32790          }
32791        };
32792      },
32793
32794
32795      /**
32796       * @ngdoc method
32797       * @name $rootScope.Scope#$watchCollection
32798       * @kind function
32799       *
32800       * @description
32801       * Shallow watches the properties of an object and fires whenever any of the properties change
32802       * (for arrays, this implies watching the array items; for object maps, this implies watching
32803       * the properties). If a change is detected, the `listener` callback is fired.
32804       *
32805       * - The `obj` collection is observed via standard $watch operation and is examined on every
32806       *   call to $digest() to see if any items have been added, removed, or moved.
32807       * - The `listener` is called whenever anything within the `obj` has changed. Examples include
32808       *   adding, removing, and moving items belonging to an object or array.
32809       *
32810       *
32811       * # Example
32812       * ```js
32813          $scope.names = ['igor', 'matias', 'misko', 'james'];
32814          $scope.dataCount = 4;
32815
32816          $scope.$watchCollection('names', function(newNames, oldNames) {
32817            $scope.dataCount = newNames.length;
32818          });
32819
32820          expect($scope.dataCount).toEqual(4);
32821          $scope.$digest();
32822
32823          //still at 4 ... no changes
32824          expect($scope.dataCount).toEqual(4);
32825
32826          $scope.names.pop();
32827          $scope.$digest();
32828
32829          //now there's been a change
32830          expect($scope.dataCount).toEqual(3);
32831       * ```
32832       *
32833       *
32834       * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The
32835       *    expression value should evaluate to an object or an array which is observed on each
32836       *    {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
32837       *    collection will trigger a call to the `listener`.
32838       *
32839       * @param {function(newCollection, oldCollection, scope)} listener a callback function called
32840       *    when a change is detected.
32841       *    - The `newCollection` object is the newly modified data obtained from the `obj` expression
32842       *    - The `oldCollection` object is a copy of the former collection data.
32843       *      Due to performance considerations, the`oldCollection` value is computed only if the
32844       *      `listener` function declares two or more arguments.
32845       *    - The `scope` argument refers to the current scope.
32846       *
32847       * @returns {function()} Returns a de-registration function for this listener. When the
32848       *    de-registration function is executed, the internal watch operation is terminated.
32849       */
32850      $watchCollection: function(obj, listener) {
32851        $watchCollectionInterceptor.$stateful = true;
32852
32853        var self = this;
32854        // the current value, updated on each dirty-check run
32855        var newValue;
32856        // a shallow copy of the newValue from the last dirty-check run,
32857        // updated to match newValue during dirty-check run
32858        var oldValue;
32859        // a shallow copy of the newValue from when the last change happened
32860        var veryOldValue;
32861        // only track veryOldValue if the listener is asking for it
32862        var trackVeryOldValue = (listener.length > 1);
32863        var changeDetected = 0;
32864        var changeDetector = $parse(obj, $watchCollectionInterceptor);
32865        var internalArray = [];
32866        var internalObject = {};
32867        var initRun = true;
32868        var oldLength = 0;
32869
32870        function $watchCollectionInterceptor(_value) {
32871          newValue = _value;
32872          var newLength, key, bothNaN, newItem, oldItem;
32873
32874          // If the new value is undefined, then return undefined as the watch may be a one-time watch
32875          if (isUndefined(newValue)) return;
32876
32877          if (!isObject(newValue)) { // if primitive
32878            if (oldValue !== newValue) {
32879              oldValue = newValue;
32880              changeDetected++;
32881            }
32882          } else if (isArrayLike(newValue)) {
32883            if (oldValue !== internalArray) {
32884              // we are transitioning from something which was not an array into array.
32885              oldValue = internalArray;
32886              oldLength = oldValue.length = 0;
32887              changeDetected++;
32888            }
32889
32890            newLength = newValue.length;
32891
32892            if (oldLength !== newLength) {
32893              // if lengths do not match we need to trigger change notification
32894              changeDetected++;
32895              oldValue.length = oldLength = newLength;
32896            }
32897            // copy the items to oldValue and look for changes.
32898            for (var i = 0; i < newLength; i++) {
32899              oldItem = oldValue[i];
32900              newItem = newValue[i];
32901
32902              bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
32903              if (!bothNaN && (oldItem !== newItem)) {
32904                changeDetected++;
32905                oldValue[i] = newItem;
32906              }
32907            }
32908          } else {
32909            if (oldValue !== internalObject) {
32910              // we are transitioning from something which was not an object into object.
32911              oldValue = internalObject = {};
32912              oldLength = 0;
32913              changeDetected++;
32914            }
32915            // copy the items to oldValue and look for changes.
32916            newLength = 0;
32917            for (key in newValue) {
32918              if (hasOwnProperty.call(newValue, key)) {
32919                newLength++;
32920                newItem = newValue[key];
32921                oldItem = oldValue[key];
32922
32923                if (key in oldValue) {
32924                  bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
32925                  if (!bothNaN && (oldItem !== newItem)) {
32926                    changeDetected++;
32927                    oldValue[key] = newItem;
32928                  }
32929                } else {
32930                  oldLength++;
32931                  oldValue[key] = newItem;
32932                  changeDetected++;
32933                }
32934              }
32935            }
32936            if (oldLength > newLength) {
32937              // we used to have more keys, need to find them and destroy them.
32938              changeDetected++;
32939              for (key in oldValue) {
32940                if (!hasOwnProperty.call(newValue, key)) {
32941                  oldLength--;
32942                  delete oldValue[key];
32943                }
32944              }
32945            }
32946          }
32947          return changeDetected;
32948        }
32949
32950        function $watchCollectionAction() {
32951          if (initRun) {
32952            initRun = false;
32953            listener(newValue, newValue, self);
32954          } else {
32955            listener(newValue, veryOldValue, self);
32956          }
32957
32958          // make a copy for the next time a collection is changed
32959          if (trackVeryOldValue) {
32960            if (!isObject(newValue)) {
32961              //primitive
32962              veryOldValue = newValue;
32963            } else if (isArrayLike(newValue)) {
32964              veryOldValue = new Array(newValue.length);
32965              for (var i = 0; i < newValue.length; i++) {
32966                veryOldValue[i] = newValue[i];
32967              }
32968            } else { // if object
32969              veryOldValue = {};
32970              for (var key in newValue) {
32971                if (hasOwnProperty.call(newValue, key)) {
32972                  veryOldValue[key] = newValue[key];
32973                }
32974              }
32975            }
32976          }
32977        }
32978
32979        return this.$watch(changeDetector, $watchCollectionAction);
32980      },
32981
32982      /**
32983       * @ngdoc method
32984       * @name $rootScope.Scope#$digest
32985       * @kind function
32986       *
32987       * @description
32988       * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
32989       * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
32990       * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
32991       * until no more listeners are firing. This means that it is possible to get into an infinite
32992       * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
32993       * iterations exceeds 10.
32994       *
32995       * Usually, you don't call `$digest()` directly in
32996       * {@link ng.directive:ngController controllers} or in
32997       * {@link ng.$compileProvider#directive directives}.
32998       * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
32999       * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`.
33000       *
33001       * If you want to be notified whenever `$digest()` is called,
33002       * you can register a `watchExpression` function with
33003       * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
33004       *
33005       * In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
33006       *
33007       * # Example
33008       * ```js
33009           var scope = ...;
33010           scope.name = 'misko';
33011           scope.counter = 0;
33012
33013           expect(scope.counter).toEqual(0);
33014           scope.$watch('name', function(newValue, oldValue) {
33015             scope.counter = scope.counter + 1;
33016           });
33017           expect(scope.counter).toEqual(0);
33018
33019           scope.$digest();
33020           // the listener is always called during the first $digest loop after it was registered
33021           expect(scope.counter).toEqual(1);
33022
33023           scope.$digest();
33024           // but now it will not be called unless the value changes
33025           expect(scope.counter).toEqual(1);
33026
33027           scope.name = 'adam';
33028           scope.$digest();
33029           expect(scope.counter).toEqual(2);
33030       * ```
33031       *
33032       */
33033      $digest: function() {
33034        var watch, value, last,
33035            watchers,
33036            length,
33037            dirty, ttl = TTL,
33038            next, current, target = this,
33039            watchLog = [],
33040            logIdx, logMsg, asyncTask;
33041
33042        beginPhase('$digest');
33043        // Check for changes to browser url that happened in sync before the call to $digest
33044        $browser.$$checkUrlChange();
33045
33046        if (this === $rootScope && applyAsyncId !== null) {
33047          // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then
33048          // cancel the scheduled $apply and flush the queue of expressions to be evaluated.
33049          $browser.defer.cancel(applyAsyncId);
33050          flushApplyAsync();
33051        }
33052
33053        lastDirtyWatch = null;
33054
33055        do { // "while dirty" loop
33056          dirty = false;
33057          current = target;
33058
33059          while (asyncQueue.length) {
33060            try {
33061              asyncTask = asyncQueue.shift();
33062              asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals);
33063            } catch (e) {
33064              $exceptionHandler(e);
33065            }
33066            lastDirtyWatch = null;
33067          }
33068
33069          traverseScopesLoop:
33070          do { // "traverse the scopes" loop
33071            if ((watchers = current.$$watchers)) {
33072              // process our watches
33073              length = watchers.length;
33074              while (length--) {
33075                try {
33076                  watch = watchers[length];
33077                  // Most common watches are on primitives, in which case we can short
33078                  // circuit it with === operator, only when === fails do we use .equals
33079                  if (watch) {
33080                    if ((value = watch.get(current)) !== (last = watch.last) &&
33081                        !(watch.eq
33082                            ? equals(value, last)
33083                            : (typeof value === 'number' && typeof last === 'number'
33084                               && isNaN(value) && isNaN(last)))) {
33085                      dirty = true;
33086                      lastDirtyWatch = watch;
33087                      watch.last = watch.eq ? copy(value, null) : value;
33088                      watch.fn(value, ((last === initWatchVal) ? value : last), current);
33089                      if (ttl < 5) {
33090                        logIdx = 4 - ttl;
33091                        if (!watchLog[logIdx]) watchLog[logIdx] = [];
33092                        watchLog[logIdx].push({
33093                          msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp,
33094                          newVal: value,
33095                          oldVal: last
33096                        });
33097                      }
33098                    } else if (watch === lastDirtyWatch) {
33099                      // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
33100                      // have already been tested.
33101                      dirty = false;
33102                      break traverseScopesLoop;
33103                    }
33104                  }
33105                } catch (e) {
33106                  $exceptionHandler(e);
33107                }
33108              }
33109            }
33110
33111            // Insanity Warning: scope depth-first traversal
33112            // yes, this code is a bit crazy, but it works and we have tests to prove it!
33113            // this piece should be kept in sync with the traversal in $broadcast
33114            if (!(next = ((current.$$watchersCount && current.$$childHead) ||
33115                (current !== target && current.$$nextSibling)))) {
33116              while (current !== target && !(next = current.$$nextSibling)) {
33117                current = current.$parent;
33118              }
33119            }
33120          } while ((current = next));
33121
33122          // `break traverseScopesLoop;` takes us to here
33123
33124          if ((dirty || asyncQueue.length) && !(ttl--)) {
33125            clearPhase();
33126            throw $rootScopeMinErr('infdig',
33127                '{0} $digest() iterations reached. Aborting!\n' +
33128                'Watchers fired in the last 5 iterations: {1}',
33129                TTL, watchLog);
33130          }
33131
33132        }
33132 while (dirty || asyncQueue.length);
33133
33134        clearPhase();
33135
33136        while (postDigestQueue.length) {
33137          try {
33138            postDigestQueue.shift()();
33139          } catch (e) {
33140            $exceptionHandler(e);
33141          }
33142        }
33143      },
33144
33145
33146      /**
33147       * @ngdoc event
33148       * @name $rootScope.Scope#$destroy
33149       * @eventType broadcast on scope being destroyed
33150       *
33151       * @description
33152       * Broadcasted when a scope and its children are being destroyed.
33153       *
33154       * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
33155       * clean up DOM bindings before an element is removed from the DOM.
33156       */
33157
33158      /**
33159       * @ngdoc method
33160       * @name $rootScope.Scope#$destroy
33161       * @kind function
33162       *
33163       * @description
33164       * Removes the current scope (and all of its children) from the parent scope. Removal implies
33165       * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
33166       * propagate to the current scope and its children. Removal also implies that the current
33167       * scope is eligible for garbage collection.
33168       *
33169       * The `$destroy()` is usually used by directives such as
33170       * {@link ng.directive:ngRepeat ngRepeat} for managing the
33171       * unrolling of the loop.
33172       *
33173       * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope.
33174       * Application code can register a `$destroy` event handler that will give it a chance to
33175       * perform any necessary cleanup.
33176       *
33177       * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
33178       * clean up DOM bindings before an element is removed from the DOM.
33179       */
33180      $destroy: function() {
33181        // We can't destroy a scope that has been already destroyed.
33182        if (this.$$destroyed) return;
33183        var parent = this.$parent;
33184
33185        this.$broadcast('$destroy');
33186        this.$$destroyed = true;
33187
33188        if (this === $rootScope) {
33189          //Remove handlers attached to window when $rootScope is removed
33190          $browser.$$applicationDestroyed();
33191        }
33192
33193        incrementWatchersCount(this, -this.$$watchersCount);
33194        for (var eventName in this.$$listenerCount) {
33195          decrementListenerCount(this, this.$$listenerCount[eventName], eventName);
33196        }
33197
33198        // sever all the references to parent scopes (after this cleanup, the current scope should
33199        // not be retained by any of our references and should be eligible for garbage collection)
33200        if (parent && parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
33201        if (parent && parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
33202        if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
33203        if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
33204
33205        // Disable listeners, watchers and apply/digest methods
33206        this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
33207        this.$on = this.$watch = this.$watchGroup = function() { return noop; };
33208        this.$$listeners = {};
33209
33210        // All of the code below is bogus code that works around V8's memory leak via optimized code
33211        // and inline caches.
33212        //
33213        // see:
33214        // - https://code.google.com/p/v8/issues/detail?id=2073#c26
33215        // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
33216        // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
33217
33218        this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead =
33219            this.$$childTail = this.$root = this.$$watchers = null;
33220      },
33221
33222      /**
33223       * @ngdoc method
33224       * @name $rootScope.Scope#$eval
33225       * @kind function
33226       *
33227       * @description
33228       * Executes the `expression` on the current scope and returns the result. Any exceptions in
33229       * the expression are propagated (uncaught). This is useful when evaluating Angular
33230       * expressions.
33231       *
33232       * # Example
33233       * ```js
33234           var scope = ng.$rootScope.Scope();
33235           scope.a = 1;
33236           scope.b = 2;
33237
33238           expect(scope.$eval('a+b')).toEqual(3);
33239           expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
33240       * ```
33241       *
33242       * @param {(string|function())=} expression An angular expression to be executed.
33243       *
33244       *    - `string`: execute using the rules as defined in  {@link guide/expression expression}.
33245       *    - `function(scope)`: execute the function with the current `scope` parameter.
33246       *
33247       * @param {(object)=} locals Local variables object, useful for overriding values in scope.
33248       * @returns {*} The result of evaluating the expression.
33249       */
33250      $eval: function(expr, locals) {
33251        return $parse(expr)(this, locals);
33252      },
33253
33254      /**
33255       * @ngdoc method
33256       * @name $rootScope.Scope#$evalAsync
33257       * @kind function
33258       *
33259       * @description
33260       * Executes the expression on the current scope at a later point in time.
33261       *
33262       * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only
33263       * that:
33264       *
33265       *   - it will execute after the function that scheduled the evaluation (preferably before DOM
33266       *     rendering).
33267       *   - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
33268       *     `expression` execution.
33269       *
33270       * Any exceptions from the execution of the expression are forwarded to the
33271       * {@link ng.$exceptionHandler $exceptionHandler} service.
33272       *
33273       * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle
33274       * will be scheduled. However, it is encouraged to always call code that changes the model
33275       * from within an `$apply` call. That includes code evaluated via `$evalAsync`.
33276       *
33277       * @param {(string|function())=} expression An angular expression to be executed.
33278       *
33279       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
33280       *    - `function(scope)`: execute the function with the current `scope` parameter.
33281       *
33282       * @param {(object)=} locals Local variables object, useful for overriding values in scope.
33283       */
33284      $evalAsync: function(expr, locals) {
33285        // if we are outside of an $digest loop and this is the first time we are scheduling async
33286        // task also schedule async auto-flush
33287        if (!$rootScope.$$phase && !asyncQueue.length) {
33288          $browser.defer(function() {
33289            if (asyncQueue.length) {
33290              $rootScope.$digest();
33291            }
33292          });
33293        }
33294
33295        asyncQueue.push({scope: this, expression: expr, locals: locals});
33296      },
33297
33298      $$postDigest: function(fn) {
33299        postDigestQueue.push(fn);
33300      },
33301
33302      /**
33303       * @ngdoc method
33304       * @name $rootScope.Scope#$apply
33305       * @kind function
33306       *
33307       * @description
33308       * `$apply()` is used to execute an expression in angular from outside of the angular
33309       * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
33310       * Because we are calling into the angular framework we need to perform proper scope life
33311       * cycle of {@link ng.$exceptionHandler exception handling},
33312       * {@link ng.$rootScope.Scope#$digest executing watches}.
33313       *
33314       * ## Life cycle
33315       *
33316       * # Pseudo-Code of `$apply()`
33317       * ```js
33318           function $apply(expr) {
33319             try {
33320               return $eval(expr);
33321             } catch (e) {
33322               $exceptionHandler(e);
33323             } finally {
33324               $root.$digest();
33325             }
33326           }
33327       * ```
33328       *
33329       *
33330       * Scope's `$apply()` method transitions through the following stages:
33331       *
33332       * 1. The {@link guide/expression expression} is executed using the
33333       *    {@link ng.$rootScope.Scope#$eval $eval()} method.
33334       * 2. Any exceptions from the execution of the expression are forwarded to the
33335       *    {@link ng.$exceptionHandler $exceptionHandler} service.
33336       * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
33337       *    expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
33338       *
33339       *
33340       * @param {(string|function())=} exp An angular expression to be executed.
33341       *
33342       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
33343       *    - `function(scope)`: execute the function with current `scope` parameter.
33344       *
33345       * @returns {*} The result of evaluating the expression.
33346       */
33347      $apply: function(expr) {
33348        try {
33349          beginPhase('$apply');
33350          try {
33351            return this.$eval(expr);
33352          } finally {
33353            clearPhase();
33354          }
33355        } catch (e) {
33356          $exceptionHandler(e);
33357        } finally {
33358          try {
33359            $rootScope.$digest();
33360          } catch (e) {
33361            $exceptionHandler(e);
33362            throw e;
33363          }
33364        }
33365      },
33366
33367      /**
33368       * @ngdoc method
33369       * @name $rootScope.Scope#$applyAsync
33370       * @kind function
33371       *
33372       * @description
33373       * Schedule the invocation of $apply to occur at a later time. The actual time difference
33374       * varies across browsers, but is typically around ~10 milliseconds.
33375       *
33376       * This can be used to queue up multiple expressions which need to be evaluated in the same
33377       * digest.
33378       *
33379       * @param {(string|function())=} exp An angular expression to be executed.
33380       *
33381       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
33382       *    - `function(scope)`: execute the function with current `scope` parameter.
33383       */
33384      $applyAsync: function(expr) {
33385        var scope = this;
33386        expr && applyAsyncQueue.push($applyAsyncExpression);
33387        scheduleApplyAsync();
33388
33389        function $applyAsyncExpression() {
33390          scope.$eval(expr);
33391        }
33392      },
33393
33394      /**
33395       * @ngdoc method
33396       * @name $rootScope.Scope#$on
33397       * @kind function
33398       *
33399       * @description
33400       * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
33401       * discussion of event life cycle.
33402       *
33403       * The event listener function format is: `function(event, args...)`. The `event` object
33404       * passed into the listener has the following attributes:
33405       *
33406       *   - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or
33407       *     `$broadcast`-ed.
33408       *   - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the
33409       *     event propagates through the scope hierarchy, this property is set to null.
33410       *   - `name` - `{string}`: name of the event.
33411       *   - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
33412       *     further event propagation (available only for events that were `$emit`-ed).
33413       *   - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
33414       *     to true.
33415       *   - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
33416       *
33417       * @param {string} name Event name to listen on.
33418       * @param {function(event, ...args)} listener Function to call when the event is emitted.
33419       * @returns {function()} Returns a deregistration function for this listener.
33420       */
33421      $on: function(name, listener) {
33422        var namedListeners = this.$$listeners[name];
33423        if (!namedListeners) {
33424          this.$$listeners[name] = namedListeners = [];
33425        }
33426        namedListeners.push(listener);
33427
33428        var current = this;
33429        do {
33430          if (!current.$$listenerCount[name]) {
33431            current.$$listenerCount[name] = 0;
33432          }
33433          current.$$listenerCount[name]++;
33434        } while ((current = current.$parent));
33435
33436        var self = this;
33437        return function() {
33438          var indexOfListener = namedListeners.indexOf(listener);
33439          if (indexOfListener !== -1) {
33440            namedListeners[indexOfListener] = null;
33441            decrementListenerCount(self, 1, name);
33442          }
33443        };
33444      },
33445
33446
33447      /**
33448       * @ngdoc method
33449       * @name $rootScope.Scope#$emit
33450       * @kind function
33451       *
33452       * @description
33453       * Dispatches an event `name` upwards through the scope hierarchy notifying the
33454       * registered {@link ng.$rootScope.Scope#$on} listeners.
33455       *
33456       * The event life cycle starts at the scope on which `$emit` was called. All
33457       * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
33458       * notified. Afterwards, the event traverses upwards toward the root scope and calls all
33459       * registered listeners along the way. The event will stop propagating if one of the listeners
33460       * cancels it.
33461       *
33462       * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
33463       * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
33464       *
33465       * @param {string} name Event name to emit.
33466       * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
33467       * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
33468       */
33469      $emit: function(name, args) {
33470        var empty = [],
33471            namedListeners,
33472            scope = this,
33473            stopPropagation = false,
33474            event = {
33475              name: name,
33476              targetScope: scope,
33477              stopPropagation: function() {stopPropagation = true;},
33478              preventDefault: function() {
33479                event.defaultPrevented = true;
33480              },
33481              defaultPrevented: false
33482            },
33483            listenerArgs = concat([event], arguments, 1),
33484            i, length;
33485
33486        do {
33487          namedListeners = scope.$$listeners[name] || empty;
33488          event.currentScope = scope;
33489          for (i = 0, length = namedListeners.length; i < length; i++) {
33490
33491            // if listeners were deregistered, defragment the array
33492            if (!namedListeners[i]) {
33493              namedListeners.splice(i, 1);
33494              i--;
33495              length--;
33496              continue;
33497            }
33498            try {
33499              //allow all listeners attached to the current scope to run
33500              namedListeners[i].apply(null, listenerArgs);
33501            } catch (e) {
33502              $exceptionHandler(e);
33503            }
33504          }
33505          //if any listener on the current scope stops propagation, prevent bubbling
33506          if (stopPropagation) {
33507            event.currentScope = null;
33508            return event;
33509          }
33510          //traverse upwards
33511          scope = scope.$parent;
33512        } while (scope);
33513
33514        event.currentScope = null;
33515
33516        return event;
33517      },
33518
33519
33520      /**
33521       * @ngdoc method
33522       * @name $rootScope.Scope#$broadcast
33523       * @kind function
33524       *
33525       * @description
33526       * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
33527       * registered {@link ng.$rootScope.Scope#$on} listeners.
33528       *
33529       * The event life cycle starts at the scope on which `$broadcast` was called. All
33530       * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
33531       * notified. Afterwards, the event propagates to all direct and indirect scopes of the current
33532       * scope and calls all registered listeners along the way. The event cannot be canceled.
33533       *
33534       * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
33535       * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
33536       *
33537       * @param {string} name Event name to broadcast.
33538       * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
33539       * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
33540       */
33541      $broadcast: function(name, args) {
33542        var target = this,
33543            current = target,
33544            next = target,
33545            event = {
33546              name: name,
33547              targetScope: target,
33548              preventDefault: function() {
33549                event.defaultPrevented = true;
33550              },
33551              defaultPrevented: false
33552            };
33553
33554        if (!target.$$listenerCount[name]) return event;
33555
33556        var listenerArgs = concat([event], arguments, 1),
33557            listeners, i, length;
33558
33559        //down while you can, then up and next sibling or up and next sibling until back at root
33560        while ((current = next)) {
33561          event.currentScope = current;
33562          listeners = current.$$listeners[name] || [];
33563          for (i = 0, length = listeners.length; i < length; i++) {
33564            // if listeners were deregistered, defragment the array
33565            if (!listeners[i]) {
33566              listeners.splice(i, 1);
33567              i--;
33568              length--;
33569              continue;
33570            }
33571
33572            try {
33573              listeners[i].apply(null, listenerArgs);
33574            } catch (e) {
33575              $exceptionHandler(e);
33576            }
33577          }
33578
33579          // Insanity Warning: scope depth-first traversal
33580          // yes, this code is a bit crazy, but it works and we have tests to prove it!
33581          // this piece should be kept in sync with the traversal in $digest
33582          // (though it differs due to having the extra check for $$listenerCount)
33583          if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
33584              (current !== target && current.$$nextSibling)))) {
33585            while (current !== target && !(next = current.$$nextSibling)) {
33586              current = current.$parent;
33587            }
33588          }
33589        }
33590
33591        event.currentScope = null;
33592        return event;
33593      }
33594    };
33595
33596    var $rootScope = new Scope();
33597
33598    //The internal queues. Expose them on the $rootScope for debugging/testing purposes.
33599    var asyncQueue = $rootScope.$$asyncQueue = [];
33600    var postDigestQueue = $rootScope.$$postDigestQueue = [];
33601    var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];
33602
33603    return $rootScope;
33604
33605
33606    function beginPhase(phase) {
33607      if ($rootScope.$$phase) {
33608        throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase);
33609      }
33610
33611      $rootScope.$$phase = phase;
33612    }
33613
33614    function clearPhase() {
33615      $rootScope.$$phase = null;
33616    }
33617
33618    function incrementWatchersCount(current, count) {
33619      do {
33620        current.$$watchersCount += count;
33621      } while ((current = current.$parent));
33622    }
33623
33624    function decrementListenerCount(current, count, name) {
33625      do {
33626        current.$$listenerCount[name] -= count;
33627
33628        if (current.$$listenerCount[name] === 0) {
33629          delete current.$$listenerCount[name];
33630        }
33631      } while ((current = current.$parent));
33632    }
33633
33634    /**
33635     * function used as an initial value for watchers.
33636     * because it's unique we can easily tell it apart from other values
33637     */
33638    function initWatchVal() {}
33639
33640    function flushApplyAsync() {
vendor: 4,838 bytes, lines 33641-33768
33641      while (applyAsyncQueue.length) {
33642        try {
33643          applyAsyncQueue.shift()();
33644        } catch (e) {
33645          $exceptionHandler(e);
33646        }
33647      }
33648      applyAsyncId = null;
33649    }
33650
33651    function scheduleApplyAsync() {
33652      if (applyAsyncId === null) {
33653        applyAsyncId = $browser.defer(function() {
33654          $rootScope.$apply(flushApplyAsync);
33655        });
33656      }
33657    }
33658  }];
33659}
33660
33661/**
33662 * @description
33663 * Private service to sanitize uris for links and images. Used by $compile and $sanitize.
33664 */
33665function $$SanitizeUriProvider() {
33666  var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
33667    imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/;
33668
33669  /**
33670   * @description
33671   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
33672   * urls during a[href] sanitization.
33673   *
33674   * The sanitization is a security measure aimed at prevent XSS attacks via html links.
33675   *
33676   * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
33677   * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
33678   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
33679   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
33680   *
33681   * @param {RegExp=} regexp New regexp to whitelist urls with.
33682   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
33683   *    chaining otherwise.
33684   */
33685  this.aHrefSanitizationWhitelist = function(regexp) {
33686    if (isDefined(regexp)) {
33687      aHrefSanitizationWhitelist = regexp;
33688      return this;
33689    }
33690    return aHrefSanitizationWhitelist;
33691  };
33692
33693
33694  /**
33695   * @description
33696   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
33697   * urls during img[src] sanitization.
33698   *
33699   * The sanitization is a security measure aimed at prevent XSS attacks via html links.
33700   *
33701   * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
33702   * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
33703   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
33704   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
33705   *
33706   * @param {RegExp=} regexp New regexp to whitelist urls with.
33707   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
33708   *    chaining otherwise.
33709   */
33710  this.imgSrcSanitizationWhitelist = function(regexp) {
33711    if (isDefined(regexp)) {
33712      imgSrcSanitizationWhitelist = regexp;
33713      return this;
33714    }
33715    return imgSrcSanitizationWhitelist;
33716  };
33717
33718  this.$get = function() {
33719    return function sanitizeUri(uri, isImage) {
33720      var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
33721      var normalizedVal;
33722      normalizedVal = urlResolve(uri).href;
33723      if (normalizedVal !== '' && !normalizedVal.match(regex)) {
33724        return 'unsafe:' + normalizedVal;
33725      }
33726      return uri;
33727    };
33728  };
33729}
33730
33731/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
33732 *     Any commits to this file should be reviewed with security in mind.  *
33733 *   Changes to this file can potentially create security vulnerabilities. *
33734 *          An approval from 2 Core members with history of modifying      *
33735 *                         this file is required.                          *
33736 *                                                                         *
33737 *  Does the change somehow allow for arbitrary javascript to be executed? *
33738 *    Or allows for someone to change the prototype of built-in objects?   *
33739 *     Or gives undesired access to variables likes document or window?    *
33740 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
33741
33742var $sceMinErr = minErr('$sce');
33743
33744var SCE_CONTEXTS = {
33745  HTML: 'html',
33746  CSS: 'css',
33747  URL: 'url',
33748  // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
33749  // url.  (e.g. ng-include, script src, templateUrl)
33750  RESOURCE_URL: 'resourceUrl',
33751  JS: 'js'
33752};
33753
33754// Helper functions follow.
33755
33756function adjustMatcher(matcher) {
33757  if (matcher === 'self') {
33758    return matcher;
33759  } else if (isString(matcher)) {
33760    // Strings match exactly except for 2 wildcards - '*' and '**'.
33761    // '*' matches any character except those from the set ':/.?&'.
33762    // '**' matches any character (like .* in a RegExp).
33763    // More than 2 *'s raises an error as it's ill defined.
33764    if (matcher.indexOf('***') > -1) {
33765      throw $sceMinErr('iwcard',
33766          'Illegal sequence *** in string matcher.  String: {0}', matcher);
33767    }
33768    matcher = escapeForRegexp(matcher).
33769                  replace('\\*\\*', '.*').
33770                  replace('\\*', '[^:/.?&;]*');
33771    return new RegExp('^' + matcher + '$');
33772  } else if (isRegExp(matcher)) {
33773    // The only other type of matcher allowed is a Regexp.
33774    // Match entire URL / disallow partial matches.
33775    // Flags are reset (i.e. no global, ignoreCase or multiline)
33776    return new RegExp('^' + matcher.source + '$');
33777  } else {
33778    throw $sceMinErr('imatcher',
33779        'Matchers may only be "self", string patterns or RegExp objects');
33780  }
33781}
33782
33783
33784function adjustMatchers(matchers) {
33785  var adjustedMatchers = [];
33786  if (isDefined(matchers)) {
33787    forEach(matchers, function(matcher) {
33788      adjustedMatchers.push(adjustMatcher(matcher));
33789    });
33790  }
33791  return adjustedMatchers;
33792}
33793
33794
33795/**
33796 * @ngdoc service
33797 * @name $sceDelegate
33798 * @kind function
33799 *
33800 * @description
33801 *
33802 * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
33803 * Contextual Escaping (SCE)} services to AngularJS.
33804 *
33805 * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
33806 * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS.  This is
33807 * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
33808 * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things
33809 * work because `$sce` delegates to `$sceDelegate` for these operations.
33810 *
33811 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service.
33812 *
33813 * The default instance of `$sceDelegate` should work out of the box with little pain.  While you
33814 * can override it completely to change the behavior of `$sce`, the common case would
33815 * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting
33816 * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as
33817 * templates.  Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist
33818 * $sceDelegateProvider.resourceUrlWhitelist} and {@link
33819 * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
33820 */
33821
33822/**
33823 * @ngdoc provider
33824 * @name $sceDelegateProvider
33825 * @description
33826 *
33827 * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
33828 * $sceDelegate} service.  This allows one to get/set the whitelists and blacklists used to ensure
33829 * that the URLs used for sourcing Angular templates are safe.  Refer {@link
33830 * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
33831 * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
33832 *
33833 * For the general details about this service in Angular, read the main page for {@link ng.$sce
33834 * Strict Contextual Escaping (SCE)}.
33835 *
33836 * **Example**:  Consider the following case. <a name="example"></a>
33837 *
33838 * - your app is hosted at url `http://myapp.example.com/`
33839 * - but some of your templates are hosted on other domains you control such as
33840 *   `http://srv01.assets.example.com/`,  `http://srv02.assets.example.com/`, etc.
33841 * - and you have an open redirect at `http://myapp.example.com/clickThru?...`.
33842 *
33843 * Here is what a secure configuration for this scenario might look like:
33844 *
33845 * ```
33846 *  angular.module('myApp', []).config(function($sceDelegateProvider) {
33847 *    $sceDelegateProvider.resourceUrlWhitelist([
33848 *      // Allow same origin resource loads.
33849 *      'self',
33850 *      // Allow loading from our assets domain.  Notice the difference between * and **.
33851 *      'http://srv*.assets.example.com/**'
33852 *    ]);
33853 *
33854 *    // The blacklist overrides the whitelist so the open redirect here is blocked.
33855 *    $sceDelegateProvider.resourceUrlBlacklist([
33856 *      'http://myapp.example.com/clickThru**'
33857 *    ]);
33858 *  });
33859 * ```
33860 */
33861
33862function $SceDelegateProvider() {
33863  this.SCE_CONTEXTS = SCE_CONTEXTS;
33864
33865  // Resource URLs can also be trusted by policy.
33866  var resourceUrlWhitelist = ['self'],
33867      resourceUrlBlacklist = [];
33868
33869  /**
33870   * @ngdoc method
33871   * @name $sceDelegateProvider#resourceUrlWhitelist
33872   * @kind function
33873   *
33874   * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
33875   *     provided.  This must be an array or null.  A snapshot of this array is used so further
33876   *     changes to the array are ignored.
33877   *
33878   *     Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
33879   *     allowed in this array.
33880   *
33881   *     Note: **an empty whitelist array will block all URLs**!
33882   *
33883   * @return {Array} the currently set whitelist array.
33884   *
33885   * The **default value** when no whitelist has been explicitly set is `['self']` allowing only
33886   * same origin resource requests.
33887   *
33888   * @description
33889   * Sets/Gets the whitelist of trusted resource URLs.
33890   */
33891  this.resourceUrlWhitelist = function(value) {
33892    if (arguments.length) {
33893      resourceUrlWhitelist = adjustMatchers(value);
33894    }
33895    return resourceUrlWhitelist;
33896  };
33897
33898  /**
33899   * @ngdoc method
33900   * @name $sceDelegateProvider#resourceUrlBlacklist
33901   * @kind function
33902   *
33903   * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
33904   *     provided.  This must be an array or null.  A snapshot of this array is used so further
33905   *     changes to the array are ignored.
33906   *
33907   *     Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
33908   *     allowed in this array.
33909   *
33910   *     The typical usage for the blacklist is to **block
33911   *     [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
33912   *     these would otherwise be trusted but actually return content from the redirected domain.
33913   *
33914   *     Finally, **the blacklist overrides the whitelist** and has the final say.
33915   *
33916   * @return {Array} the currently set blacklist array.
33917   *
33918   * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
33919   * is no blacklist.)
33920   *
33921   * @description
33922   * Sets/Gets the blacklist of trusted resource URLs.
33923   */
33924
33925  this.resourceUrlBlacklist = function(value) {
33926    if (arguments.length) {
33927      resourceUrlBlacklist = adjustMatchers(value);
33928    }
33929    return resourceUrlBlacklist;
33930  };
33931
33932  this.$get = ['$injector', function($injector) {
33933
33934    var htmlSanitizer = function htmlSanitizer(html) {
33935      throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
33936    };
33937
33938    if ($injector.has('$sanitize')) {
33939      htmlSanitizer = $injector.get('$sanitize');
33940    }
33941
33942
33943    function matchUrl(matcher, parsedUrl) {
33944      if (matcher === 'self') {
33945        return urlIsSameOrigin(parsedUrl);
33946      } else {
33947        // definitely a regex.  See adjustMatchers()
33948        return !!matcher.exec(parsedUrl.href);
33949      }
33950    }
33951
33952    function isResourceUrlAllowedByPolicy(url) {
33953      var parsedUrl = urlResolve(url.toString());
33954      var i, n, allowed = false;
33955      // Ensure that at least one item from the whitelist allows this url.
33956      for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {
33957        if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {
33958          allowed = true;
33959          break;
33960        }
33961      }
33962      if (allowed) {
33963        // Ensure that no item from the blacklist blocked this url.
33964        for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {
33965          if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {
33966            allowed = false;
33967            break;
33968          }
33969        }
33970      }
33971      return allowed;
33972    }
33973
33974    function generateHolderType(Base) {
33975      var holderType = function TrustedValueHolderType(trustedValue) {
33976        this.$$unwrapTrustedValue = function() {
33977          return trustedValue;
33978        };
33979      };
33980      if (Base) {
33981        holderType.prototype = new Base();
33982      }
33983      holderType.prototype.valueOf = function sceValueOf() {
33984        return this.$$unwrapTrustedValue();
33985      };
33986      holderType.prototype.toString = function sceToString() {
33987        return this.$$unwrapTrustedValue().toString();
33988      };
33989      return holderType;
33990    }
33991
33992    var trustedValueHolderBase = generateHolderType(),
33993        byType = {};
33994
33995    byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
33996    byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
33997    byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);
33998    byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
33999    byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);
34000
34001    /**
34002     * @ngdoc method
34003     * @name $sceDelegate#trustAs
34004     *
34005     * @description
34006     * Returns an object that is trusted by angular for use in specified strict
34007     * contextual escaping contexts (such as ng-bind-html, ng-include, any src
34008     * attribute interpolation, any dom event binding attribute interpolation
34009     * such as for onclick,  etc.) that uses the provided value.
34010     * See {@link ng.$sce $sce} for enabling strict contextual escaping.
34011     *
34012     * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
34013     *   resourceUrl, html, js and css.
34014     * @param {*} value The value that that should be considered trusted/safe.
34015     * @returns {*} A value that can be used to stand in for the provided `value` in places
34016     * where Angular expects a $sce.trustAs() return value.
34017     */
34018    function trustAs(type, trustedValue) {
34019      var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
34020      if (!Constructor) {
34021        throw $sceMinErr('icontext',
34022            'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
34023            type, trustedValue);
34024      }
34025      if (trustedValue === null || isUndefined(trustedValue) || trustedValue === '') {
34026        return trustedValue;
34027      }
34028      // All the current contexts in SCE_CONTEXTS happen to be strings.  In order to avoid trusting
34029      // mutable objects, we ensure here that the value passed in is actually a string.
34030      if (typeof trustedValue !== 'string') {
34031        throw $sceMinErr('itype',
34032            'Attempted to trust a non-string value in a content requiring a string: Context: {0}',
34033            type);
34034      }
34035      return new Constructor(trustedValue);
34036    }
34037
34038    /**
34039     * @ngdoc method
34040     * @name $sceDelegate#valueOf
34041     *
34042     * @description
34043     * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs
34044     * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link
34045     * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
34046     *
34047     * If the passed parameter is not a value that had been returned by {@link
34048     * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.
34049     *
34050     * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
34051     *      call or anything else.
34052     * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
34053     *     `$sceDelegate.trustAs`} if `value` is the result of such a call.  Otherwise, returns
34054     *     `value` unchanged.
34055     */
34056    function valueOf(maybeTrusted) {
34057      if (maybeTrusted instanceof trustedValueHolderBase) {
34058        return maybeTrusted.$$unwrapTrustedValue();
34059      } else {
34060        return maybeTrusted;
34061      }
34062    }
34063
34064    /**
34065     * @ngdoc method
34066     * @name $sceDelegate#getTrusted
34067     *
34068     * @description
34069     * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and
34070     * returns the originally supplied value if the queried context type is a supertype of the
34071     * created type.  If this condition isn't satisfied, throws an exception.
34072     *
34073     * @param {string} type The kind of context in which this value is to be used.
34074     * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
34075     *     `$sceDelegate.trustAs`} call.
34076     * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
34077     *     `$sceDelegate.trustAs`} if valid in this context.  Otherwise, throws an exception.
34078     */
34079    function getTrusted(type, maybeTrusted) {
34080      if (maybeTrusted === null || isUndefined(maybeTrusted) || maybeTrusted === '') {
34081        return maybeTrusted;
34082      }
34083      var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
34084      if (constructor && maybeTrusted instanceof constructor) {
34085        return maybeTrusted.$$unwrapTrustedValue();
34086      }
34087      // If we get here, then we may only take one of two actions.
34088      // 1. sanitize the value for the requested type, or
34089      // 2. throw an exception.
34090      if (type === SCE_CONTEXTS.RESOURCE_URL) {
34091        if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
34092          return maybeTrusted;
34093        } else {
34094          throw $sceMinErr('insecurl',
34095              'Blocked loading resource from url not allowed by $sceDelegate policy.  URL: {0}',
34096              maybeTrusted.toString());
34097        }
34098      } else if (type === SCE_CONTEXTS.HTML) {
34099        return htmlSanitizer(maybeTrusted);
34100      }
34101      throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
34102    }
34103
34104    return { trustAs: trustAs,
34105             getTrusted: getTrusted,
34106             valueOf: valueOf };
34107  }];
34108}
34109
34110
34111/**
34112 * @ngdoc provider
34113 * @name $sceProvider
34114 * @description
34115 *
34116 * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service.
34117 * -   enable/disable Strict Contextual Escaping (SCE) in a module
34118 * -   override the default implementation with a custom delegate
34119 *
34120 * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.
34121 */
34122
34123/* jshint maxlen: false*/
34124
34125/**
34126 * @ngdoc service
34127 * @name $sce
34128 * @kind function
34129 *
34130 * @description
34131 *
34132 * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.
34133 *
34134 * # Strict Contextual Escaping
34135 *
34136 * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
34137 * contexts to result in a value that is marked as safe to use for that context.  One example of
34138 * such a context is binding arbitrary html controlled by the user via `ng-bind-html`.  We refer
34139 * to these contexts as privileged or SCE contexts.
34140 *
34141 * As of version 1.2, Angular ships with SCE enabled by default.
34142 *
34143 * Note:  When enabled (the default), IE<11 in quirks mode is not supported.  In this mode, IE<11 allow
34144 * one to execute arbitrary javascript by the use of the expression() syntax.  Refer
34145 * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
34146 * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
34147 * to the top of your HTML document.
34148 *
34149 * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for
34150 * security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
34151 *
34152 * Here's an example of a binding in a privileged context:
34153 *
34154 * ```
34155 * <input ng-model="userHtml" aria-label="User input">
34156 * <div ng-bind-html="userHtml"></div>
34157 * ```
34158 *
34159 * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user.  With SCE
34160 * disabled, this application allows the user to render arbitrary HTML into the DIV.
34161 * In a more realistic example, one may be rendering user comments, blog articles, etc. via
34162 * bindings.  (HTML is just one example of a context where rendering user controlled input creates
34163 * security vulnerabilities.)
34164 *
34165 * For the case of HTML, you might use a library, either on the client side, or on the server side,
34166 * to sanitize unsafe HTML before binding to the value and rendering it in the document.
34167 *
34168 * How would you ensure that every place that used these types of bindings was bound to a value that
34169 * was sanitized by your library (or returned as safe for rendering by your server?)  How can you
34170 * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
34171 * properties/fields and forgot to update the binding to the sanitized value?
34172 *
34173 * To be secure by default, you want to ensure that any such bindings are disallowed unless you can
34174 * determine that something explicitly says it's safe to use a value for binding in that
34175 * context.  You can then audit your code (a simple grep would do) to ensure that this is only done
34176 * for those values that you can easily tell are safe - because they were received from your server,
34177 * sanitized by your library, etc.  You can organize your codebase to help with this - perhaps
34178 * allowing only the files in a specific directory to do this.  Ensuring that the internal API
34179 * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
34180 *
34181 * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}
34182 * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to
34183 * obtain values that will be accepted by SCE / privileged contexts.
34184 *
34185 *
34186 * ## How does it work?
34187 *
34188 * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
34189 * $sce.getTrusted(context, value)} rather than to the value directly.  Directives use {@link
34190 * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
34191 * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
34192 *
34193 * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
34194 * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}.  Here's the actual code (slightly
34195 * simplified):
34196 *
34197 * ```
34198 * var ngBindHtmlDirective = ['$sce', function($sce) {
34199 *   return function(scope, element, attr) {
34200 *     scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
34201 *       element.html(value || '');
34202 *     });
34203 *   };
34204 * }];
34205 * ```
34206 *
34207 * ## Impact on loading templates
34208 *
34209 * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as
34210 * `templateUrl`'s specified by {@link guide/directive directives}.
34211 *
34212 * By default, Angular only loads templates from the same domain and protocol as the application
34213 * document.  This is done by calling {@link ng.$sce#getTrustedResourceUrl
34214 * $sce.getTrustedResourceUrl} on the template URL.  To load templates from other domains and/or
34215 * protocols, you may either either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
34216 * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
34217 *
34218 * *Please note*:
34219 * The browser's
34220 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
34221 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
34222 * policy apply in addition to this and may further restrict whether the template is successfully
34223 * loaded.  This means that without the right CORS policy, loading templates from a different domain
34224 * won't work on all browsers.  Also, loading templates from `file://` URL does not work on some
34225 * browsers.
34226 *
34227 * ## This feels like too much overhead
34228 *
34229 * It's important to remember that SCE only applies to interpolation expressions.
34230 *
34231 * If your expressions are constant literals, they're automatically trusted and you don't need to
34232 * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
34233 * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
34234 *
34235 * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
34236 * through {@link ng.$sce#getTrusted $sce.getTrusted}.  SCE doesn't play a role here.
34237 *
34238 * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
34239 * templates in `ng-include` from your application's domain without having to even know about SCE.
34240 * It blocks loading templates from other domains or loading templates over http from an https
34241 * served document.  You can change these by setting your own custom {@link
34242 * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link
34243 * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs.
34244 *
34245 * This significantly reduces the overhead.  It is far easier to pay the small overhead and have an
34246 * application that's secure and can be audited to verify that with much more ease than bolting
34247 * security onto an application later.
34248 *
34249 * <a name="contexts"></a>
34250 * ## What trusted context types are supported?
34251 *
34252 * | Context             | Notes          |
34253 * |---------------------|----------------|
34254 * | `$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. |
34255 * | `$sce.CSS`          | For CSS that's safe to source into the application.  Currently unused.  Feel free to use it in your own directives. |
34256 * | `$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. |
34257 * | `$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` (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. |
34258 * | `$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. |
34259 *
34260 * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
34261 *
34262 *  Each element in these arrays must be one of the following:
34263 *
34264 *  - **'self'**
34265 *    - The special **string**, `'self'`, can be used to match against all URLs of the **same
34266 *      domain** as the application document using the **same protocol**.
34267 *  - **String** (except the special value `'self'`)
34268 *    - The string is matched against the full *normalized / absolute URL* of the resource
34269 *      being tested (substring matches are not good enough.)
34270 *    - There are exactly **two wildcard sequences** - `*` and `**`.  All other characters
34271 *      match themselves.
34272 *    - `*`: matches zero or more occurrences of any character other than one of the following 6
34273 *      characters: '`:`', '`/`', '`.`', '`?`', '`&`' and '`;`'.  It's a useful wildcard for use
34274 *      in a whitelist.
34275 *    - `**`: matches zero or more occurrences of *any* character.  As such, it's not
34276 *      appropriate for use in a scheme, domain, etc. as it would match too much.  (e.g.
34277 *      http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
34278 *      not have been the intention.)  Its usage at the very end of the path is ok.  (e.g.
34279 *      http://foo.example.com/templates/**).
34280 *  - **RegExp** (*see caveat below*)
34281 *    - *Caveat*:  While regular expressions are powerful and offer great flexibility,  their syntax
34282 *      (and all the inevitable escaping) makes them *harder to maintain*.  It's easy to
34283 *      accidentally introduce a bug when one updates a complex expression (imho, all regexes should
34284 *      have good test coverage).  For instance, the use of `.` in the regex is correct only in a
34285 *      small number of cases.  A `.` character in the regex used when matching the scheme or a
34286 *      subdomain could be matched against a `:` or literal `.` that was likely not intended.   It
34287 *      is highly recommended to use the string patterns and only fall back to regular expressions
34288 *      as a last resort.
34289 *    - The regular expression must be an instance of RegExp (i.e. not a string.)  It is
34290 *      matched against the **entire** *normalized / absolute URL* of the resource being tested
34291 *      (even when the RegExp did not have the `^` and `$` codes.)  In addition, any flags
34292 *      present on the RegExp (such as multiline, global, ignoreCase) are ignored.
34293 *    - If you are generating your JavaScript from some other templating engine (not
34294 *      recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
34295 *      remember to escape your regular expression (and be aware that you might need more than
34296 *      one level of escaping depending on your templating engine and the way you interpolated
34297 *      the value.)  Do make use of your platform's escaping mechanism as it might be good
34298 *      enough before coding your own.  E.g. Ruby has
34299 *      [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)
34300 *      and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).
34301 *      Javascript lacks a similar built in function for escaping.  Take a look at Google
34302 *      Closure library's [goog.string.regExpEscape(s)](
34303 *      http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962).
34304 *
34305 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example.
34306 *
34307 * ## Show me an example using SCE.
34308 *
34309 * <example module="mySceApp" deps="angular-sanitize.js">
34310 * <file name="index.html">
34311 *   <div ng-controller="AppController as myCtrl">
34312 *     <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
34313 *     <b>User comments</b><br>
34314 *     By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when
34315 *     $sanitize is available.  If $sanitize isn't available, this results in an error instead of an
34316 *     exploit.
34317 *     <div class="well">
34318 *       <div ng-repeat="userComment in myCtrl.userComments">
34319 *         <b>{{userComment.name}}</b>:
34320 *         <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
34321 *         <br>
34322 *       </div>
34323 *     </div>
34324 *   </div>
34325 * </file>
34326 *
34327 * <file name="script.js">
34328 *   angular.module('mySceApp', ['ngSanitize'])
34329 *     .controller('AppController', ['$http', '$templateCache', '$sce',
34330 *       function($http, $templateCache, $sce) {
34331 *         var self = this;
34332 *         $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) {
34333 *           self.userComments = userComments;
34334 *         });
34335 *         self.explicitlyTrustedHtml = $sce.trustAsHtml(
34336 *             '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
34337 *             'sanitization.&quot;">Hover over this text.</span>');
34338 *       }]);
34339 * </file>
34340 *
34341 * <file name="test_data.json">
34342 * [
34343 *   { "name": "Alice",
34344 *     "htmlComment":
34345 *         "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
34346 *   },
34347 *   { "name": "Bob",
34348 *     "htmlComment": "<i>Yes!</i>  Am I the only other one?"
34349 *   }
34350 * ]
34351 * </file>
34352 *
34353 * <file name="protractor.js" type="protractor">
34354 *   describe('SCE doc demo', function() {
34355 *     it('should sanitize untrusted values', function() {
34356 *       expect(element.all(by.css('.htmlComment')).first().getInnerHtml())
34357 *           .toBe('<span>Is <i>anyone</i> reading this?</span>');
34358 *     });
34359 *
34360 *     it('should NOT sanitize explicitly trusted values', function() {
34361 *       expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe(
34362 *           '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
34363 *           'sanitization.&quot;">Hover over this text.</span>');
34364 *     });
34365 *   });
34366 * </file>
34367 * </example>
34368 *
34369 *
34370 *
34371 * ## Can I disable SCE completely?
34372 *
34373 * Yes, you can.  However, this is strongly discouraged.  SCE gives you a lot of security benefits
34374 * for little coding overhead.  It will be much harder to take an SCE disabled application and
34375 * either secure it on your own or enable SCE at a later stage.  It might make sense to disable SCE
34376 * for cases where you have a lot of existing code that was written before SCE was introduced and
34377 * you're migrating them a module at a time.
34378 *
34379 * That said, here's how you can completely disable SCE:
34380 *
34381 * ```
34382 * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
34383 *   // Completely disable SCE.  For demonstration purposes only!
34384 *   // Do not use in new projects.
34385 *   $sceProvider.enabled(false);
34386 * });
34387 * ```
34388 *
34389 */
34390/* jshint maxlen: 100 */
34391
34392function $SceProvider() {
34393  var enabled = true;
34394
34395  /**
34396   * @ngdoc method
34397   * @name $sceProvider#enabled
34398   * @kind function
34399   *
34400   * @param {boolean=} value If provided, then enables/disables SCE.
34401   * @return {boolean} true if SCE is enabled, false otherwise.
34402   *
34403   * @description
34404   * Enables/disables SCE and returns the current value.
34405   */
34406  this.enabled = function(value) {
34407    if (arguments.length) {
34408      enabled = !!value;
34409    }
34410    return enabled;
34411  };
34412
34413
34414  /* Design notes on the default implementation for SCE.
34415   *
34416   * The API contract for the SCE delegate
34417   * -------------------------------------
34418   * The SCE delegate object must provide the following 3 methods:
34419   *
34420   * - trustAs(contextEnum, value)
34421   *     This method is used to tell the SCE service that the provided value is OK to use in the
34422   *     contexts specified by contextEnum.  It must return an object that will be accepted by
34423   *     getTrusted() for a compatible contextEnum and return this value.
34424   *
34425   * - valueOf(value)
34426   *     For values that were not produced by trustAs(), return them as is.  For values that were
34427   *     produced by trustAs(), return the corresponding input value to trustAs.  Basically, if
34428   *     trustAs is wrapping the given values into some type, this operation unwraps it when given
34429   *     such a value.
34430   *
34431   * - getTrusted(contextEnum, value)
34432   *     This function should return the a value that is safe to use in the context specified by
34433   *     contextEnum or throw and exception otherwise.
34434   *
34435   * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be
34436   * opaque or wrapped in some holder object.  That happens to be an implementation detail.  For
34437   * instance, an implementation could maintain a registry of all trusted objects by context.  In
34438   * such a case, trustAs() would return the same object that was passed in.  getTrusted() would
34439   * return the same object passed in if it was found in the registry under a compatible context or
34440   * throw an exception otherwise.  An implementation might only wrap values some of the time based
34441   * on some criteria.  getTrusted() might return a value and not throw an exception for special
34442   * constants or objects even if not wrapped.  All such implementations fulfill this contract.
34443   *
34444   *
34445   * A note on the inheritance model for SCE contexts
34446   * ------------------------------------------------
34447   * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types.  This
34448   * is purely an implementation details.
34449   *
34450   * The contract is simply this:
34451   *
34452   *     getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
34453   *     will also succeed.
34454   *
34455   * Inheritance happens to capture this in a natural way.  In some future, we
34456   * may not use inheritance anymore.  That is OK because no code outside of
34457   * sce.js and sceSpecs.js would need to be aware of this detail.
34458   */
34459
34460  this.$get = ['$parse', '$sceDelegate', function(
34461                $parse,   $sceDelegate) {
34462    // Prereq: Ensure that we're not running in IE<11 quirks mode.  In that mode, IE < 11 allow
34463    // the "expression(javascript expression)" syntax which is insecure.
34464    if (enabled && msie < 8) {
34465      throw $sceMinErr('iequirks',
34466        'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' +
34467        'mode.  You can fix this by adding the text <!doctype html> to the top of your HTML ' +
34468        'document.  See http://docs.angularjs.org/api/ng.$sce for more information.');
34469    }
34470
34471    var sce = shallowCopy(SCE_CONTEXTS);
34472
34473    /**
34474     * @ngdoc method
34475     * @name $sce#isEnabled
34476     * @kind function
34477     *
34478     * @return {Boolean} true if SCE is enabled, false otherwise.  If you want to set the value, you
34479     * have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
34480     *
34481     * @description
34482     * Returns a boolean indicating if SCE is enabled.
34483     */
34484    sce.isEnabled = function() {
34485      return enabled;
34486    };
34487    sce.trustAs = $sceDelegate.trustAs;
34488    sce.getTrusted = $sceDelegate.getTrusted;
34489    sce.valueOf = $sceDelegate.valueOf;
34490
34491    if (!enabled) {
34492      sce.trustAs = sce.getTrusted = function(type, value) { return value; };
34493      sce.valueOf = identity;
34494    }
34495
34496    /**
34497     * @ngdoc method
34498     * @name $sce#parseAs
34499     *
34500     * @description
34501     * Converts Angular {@link guide/expression expression} into a function.  This is like {@link
34502     * ng.$parse $parse} and is identical when the expression is a literal constant.  Otherwise, it
34503     * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
34504     * *result*)}
34505     *
34506     * @param {string} type The kind of SCE context in which this result will be used.
34507     * @param {string} expression String expression to compile.
34508     * @returns {function(context, locals)} a function which represents the compiled expression:
34509     *
34510     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
34511     *      are evaluated against (typically a scope object).
34512     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
34513     *      `context`.
34514     */
34515    sce.parseAs = function sceParseAs(type, expr) {
34516      var parsed = $parse(expr);
34517      if (parsed.literal && parsed.constant) {
34518        return parsed;
34519      } else {
34520        return $parse(expr, function(value) {
34521          return sce.getTrusted(type, value);
34522        });
34523      }
34524    };
34525
34526    /**
34527     * @ngdoc method
34528     * @name $sce#trustAs
34529     *
34530     * @description
34531     * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.  As such,
34532     * returns an object that is trusted by angular for use in specified strict contextual
34533     * escaping contexts (such as ng-bind-html, ng-include, any src attribute
34534     * interpolation, any dom event binding attribute interpolation such as for onclick,  etc.)
34535     * that uses the provided value.  See * {@link ng.$sce $sce} for enabling strict contextual
34536     * escaping.
34537     *
34538     * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
34539     *   resourceUrl, html, js and css.
34540     * @param {*} value The value that that should be considered trusted/safe.
34541     * @returns {*} A value that can be used to stand in for the provided `value` in places
34542     * where Angular expects a $sce.trustAs() return value.
34543     */
34544
34545    /**
34546     * @ngdoc method
34547     * @name $sce#trustAsHtml
34548     *
34549     * @description
34550     * Shorthand method.  `$sce.trustAsHtml(value)` →
34551     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
34552     *
34553     * @param {*} value The value to trustAs.
34554     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml
34555     *     $sce.getTrustedHtml(value)} to obtain the original value.  (privileged directives
34556     *     only accept expressions that are either literal constants or are the
34557     *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
34558     */
34559
34560    /**
34561     * @ngdoc method
34562     * @name $sce#trustAsUrl
34563     *
34564     * @description
34565     * Shorthand method.  `$sce.trustAsUrl(value)` →
34566     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
34567     *
34568     * @param {*} value The value to trustAs.
34569     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl
34570     *     $sce.getTrustedUrl(value)} to obtain the original value.  (privileged directives
34571     *     only accept expressions that are either literal constants or are the
34572     *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
34573     */
34574
34575    /**
34576     * @ngdoc method
34577     * @name $sce#trustAsResourceUrl
34578     *
34579     * @description
34580     * Shorthand method.  `$sce.trustAsResourceUrl(value)` →
34581     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
34582     *
34583     * @param {*} value The value to trustAs.
34584     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl
34585     *     $sce.getTrustedResourceUrl(value)} to obtain the original value.  (privileged directives
34586     *     only accept expressions that are either literal constants or are the return
34587     *     value of {@link ng.$sce#trustAs $sce.trustAs}.)
34588     */
34589
34590    /**
34591     * @ngdoc method
34592     * @name $sce#trustAsJs
34593     *
34594     * @description
34595     * Shorthand method.  `$sce.trustAsJs(value)` →
34596     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
34597     *
34598     * @param {*} value The value to trustAs.
34599     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs
34600     *     $sce.getTrustedJs(value)} to obtain the original value.  (privileged directives
34601     *     only accept expressions that are either literal constants or are the
34602     *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
34603     */
34604
34605    /**
34606     * @ngdoc method
34607     * @name $sce#getTrusted
34608     *
34609     * @description
34610     * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}.  As such,
34611     * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the
34612     * originally supplied value if the queried context type is a supertype of the created type.
34613     * If this condition isn't satisfied, throws an exception.
34614     *
34615     * @param {string} type The kind of context in which this value is to be used.
34616     * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`}
34617     *                         call.
34618     * @returns {*} The value the was originally provided to
34619     *              {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context.
34620     *              Otherwise, throws an exception.
34621     */
34622
34623    /**
34624     * @ngdoc method
34625     * @name $sce#getTrustedHtml
34626     *
34627     * @description
34628     * Shorthand method.  `$sce.getTrustedHtml(value)` →
34629     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
34630     *
34631     * @param {*} value The value to pass to `$sce.getTrusted`.
34632     * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
34633     */
34634
34635    /**
34636     * @ngdoc method
34637     * @name $sce#getTrustedCss
34638     *
34639     * @description
34640     * Shorthand method.  `$sce.getTrustedCss(value)` →
34641     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
34642     *
34643     * @param {*} value The value to pass to `$sce.getTrusted`.
34644     * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
34645     */
34646
34647    /**
34648     * @ngdoc method
34649     * @name $sce#getTrustedUrl
34650     *
34651     * @description
34652     * Shorthand method.  `$sce.getTrustedUrl(value)` →
34653     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
34654     *
34655     * @param {*} value The value to pass to `$sce.getTrusted`.
34656     * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
34657     */
34658
34659    /**
34660     * @ngdoc method
34661     * @name $sce#getTrustedResourceUrl
34662     *
34663     * @description
34664     * Shorthand method.  `$sce.getTrustedResourceUrl(value)` →
34665     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
34666     *
34667     * @param {*} value The value to pass to `$sceDelegate.getTrusted`.
34668     * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
34669     */
34670
34671    /**
34672     * @ngdoc method
34673     * @name $sce#getTrustedJs
34674     *
34675     * @description
34676     * Shorthand method.  `$sce.getTrustedJs(value)` →
34677     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
34678     *
34679     * @param {*} value The value to pass to `$sce.getTrusted`.
34680     * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
34681     */
34682
34683    /**
34684     * @ngdoc method
34685     * @name $sce#parseAsHtml
34686     *
34687     * @description
34688     * Shorthand method.  `$sce.parseAsHtml(expression string)` →
34689     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`}
34690     *
34691     * @param {string} expression String expression to compile.
34692     * @returns {function(context, locals)} a function which represents the compiled expression:
34693     *
34694     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
34695     *      are evaluated against (typically a scope object).
34696     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
34697     *      `context`.
34698     */
34699
34700    /**
34701     * @ngdoc method
34702     * @name $sce#parseAsCss
34703     *
34704     * @description
34705     * Shorthand method.  `$sce.parseAsCss(value)` →
34706     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`}
34707     *
34708     * @param {string} expression String expression to compile.
34709     * @returns {function(context, locals)} a function which represents the compiled expression:
34710     *
34711     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
34712     *      are evaluated against (typically a scope object).
34713     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
34714     *      `context`.
34715     */
34716
34717    /**
34718     * @ngdoc method
34719     * @name $sce#parseAsUrl
34720     *
34721     * @description
34722     * Shorthand method.  `$sce.parseAsUrl(value)` →
34723     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`}
34724     *
34725     * @param {string} expression String expression to compile.
34726     * @returns {function(context, locals)} a function which represents the compiled expression:
34727     *
34728     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
34729     *      are evaluated against (typically a scope object).
34730     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
34731     *      `context`.
34732     */
34733
34734    /**
34735     * @ngdoc method
34736     * @name $sce#parseAsResourceUrl
34737     *
34738     * @description
34739     * Shorthand method.  `$sce.parseAsResourceUrl(value)` →
34740     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`}
34741     *
34742     * @param {string} expression String expression to compile.
34743     * @returns {function(context, locals)} a function which represents the compiled expression:
34744     *
34745     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
34746     *      are evaluated against (typically a scope object).
34747     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
34748     *      `context`.
34749     */
34750
34751    /**
34752     * @ngdoc method
34753     * @name $sce#parseAsJs
34754     *
34755     * @description
34756     * Shorthand method.  `$sce.parseAsJs(value)` →
34757     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`}
34758     *
34759     * @param {string} expression String expression to compile.
34760     * @returns {function(context, locals)} a function which represents the compiled expression:
34761     *
34762     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
34763     *      are evaluated against (typically a scope object).
34764     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
34765     *      `context`.
34766     */
34767
34768    // Shorthand delegations.
34769    var parse = sce.parseAs,
34770        getTrusted = sce.getTrusted,
34771        trustAs = sce.trustAs;
34772
34773    forEach(SCE_CONTEXTS, function(enumValue, name) {
34774      var lName = lowercase(name);
34775      sce[camelCase("parse_as_" + lName)] = function(expr) {
34776        return parse(enumValue, expr);
34777      };
34778      sce[camelCase("get_trusted_" + lName)] = function(value) {
34779        return getTrusted(enumValue, value);
34780      };
34781      sce[camelCase("trust_as_" + lName)] = function(value) {
34782        return trustAs(enumValue, value);
34783      };
34784    });
34785
34786    return sce;
34787  }];
34788}
34789
34790/**
34791 * !!! This is an undocumented "private" service !!!
34792 *
34793 * @name $sniffer
34794 * @requires $window
34795 * @requires $document
34796 *
34797 * @property {boolean} history Does the browser support html5 history api ?
34798 * @property {boolean} transitions Does the browser support CSS transition events ?
34799 * @property {boolean} animations Does the browser support CSS animation events ?
34800 *
34801 * @description
34802 * This is very simple implementation of testing browser's features.
34803 */
34804function $SnifferProvider() {
34805  this.$get = ['$window', '$document', function($window, $document) {
34806    var eventSupport = {},
34807        android =
34808          toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
34809        boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
34810        document = $document[0] || {},
34811        vendorPrefix,
34812        vendorRegex = /^(Moz|webkit|ms)(?=[A-Z])/,
34813        bodyStyle = document.body && document.body.style,
34814        transitions = false,
34815        animations = false,
34816        match;
34817
34818    if (bodyStyle) {
34819      for (var prop in bodyStyle) {
34820        if (match = vendorRegex.exec(prop)) {
34821          vendorPrefix = match[0];
34822          vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1);
34823          break;
34824        }
34825      }
34826
34827      if (!vendorPrefix) {
34828        vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit';
34829      }
34830
34831      transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
34832      animations  = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
34833
34834      if (android && (!transitions ||  !animations)) {
34835        transitions = isString(bodyStyle.webkitTransition);
34836        animations = isString(bodyStyle.webkitAnimation);
34837      }
34838    }
34839
34840
34841    return {
34842      // Android has history.pushState, but it does not update location correctly
34843      // so let's not use the history API at all.
34844      // http://code.google.com/p/android/issues/detail?id=17471
34845      // https://github.com/angular/angular.js/issues/904
34846
34847      // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
34848      // so let's not use the history API also
34849      // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
34850      // jshint -W018
34851      history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
34852      // jshint +W018
34853      hasEvent: function(event) {
34854        // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
34855        // it. In particular the event is not fired when backspace or delete key are pressed or
34856        // when cut operation is performed.
34857        // IE10+ implements 'input' event but it erroneously fires under various situations,
34858        // e.g. when placeholder changes, or a form is focused.
34859        if (event === 'input' && msie <= 11) return false;
34860
34861        if (isUndefined(eventSupport[event])) {
34862          var divElm = document.createElement('div');
34863          eventSupport[event] = 'on' + event in divElm;
34864        }
34865
34866        return eventSupport[event];
34867      },
34868      csp: csp(),
34869      vendorPrefix: vendorPrefix,
34870      transitions: transitions,
34871      animations: animations,
34872      android: android
34873    };
34874  }];
34875}
34876
34877var $compileMinErr = minErr('$compile');
34878
34879/**
34880 * @ngdoc service
34881 * @name $templateRequest
34882 *
34883 * @description
34884 * The `$templateRequest` service runs security checks then downloads the provided template using
34885 * `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request
34886 * fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the
34887 * exception can be thwarted by setting the 2nd parameter of the function to true). Note that the
34888 * contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted
34889 * when `tpl` is of type string and `$templateCache` has the matching entry.
34890 *
34891 * @param {string|TrustedResourceUrl} tpl The HTTP request template URL
34892 * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
34893 *
34894 * @return {Promise} a promise for the HTTP response data of the given URL.
34895 *
34896 * @property {number} totalPendingRequests total amount of pending template requests being downloaded.
34897 */
34898function $TemplateRequestProvider() {
34899  this.$get = ['$templateCache', '$http', '$q', '$sce', function($templateCache, $http, $q, $sce) {
34900    function handleRequestFn(tpl, ignoreRequestError) {
34901      handleRequestFn.totalPendingRequests++;
34902
34903      // We consider the template cache holds only trusted templates, so
34904      // there's no need to go through whitelisting again for keys that already
34905      // are included in there. This also makes Angular accept any script
34906      // directive, no matter its name. However, we still need to unwrap trusted
34907      // types.
34908      if (!isString(tpl) || !$templateCache.get(tpl)) {
34909        tpl = $sce.getTrustedResourceUrl(tpl);
34910      }
34911
34912      var transformResponse = $http.defaults && $http.defaults.transformResponse;
34913
34914      if (isArray(transformResponse)) {
34915        transformResponse = transformResponse.filter(function(transformer) {
34916          return transformer !== defaultHttpResponseTransform;
34917        });
34918      } else if (transformResponse === defaultHttpResponseTransform) {
34919        transformResponse = null;
34920      }
34921
34922      var httpOptions = {
34923        cache: $templateCache,
34924        transformResponse: transformResponse
34925      };
34926
34927      return $http.get(tpl, httpOptions)
34928        ['finally'](function() {
34929          handleRequestFn.totalPendingRequests--;
34930        })
34931        .then(function(response) {
34932          $templateCache.put(tpl, response.data);
34933          return response.data;
34934        }, handleError);
34935
34936      function handleError(resp) {
34937        if (!ignoreRequestError) {
34938          throw $compileMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})',
34939            tpl, resp.status, resp.statusText);
34940        }
34941        return $q.reject(resp);
34942      }
34943    }
34944
34945    handleRequestFn.totalPendingRequests = 0;
34946
34947    return handleRequestFn;
34948  }];
34949}
34950
vendor: 5,440 bytes, lines 34951-35104
34951function $$TestabilityProvider() {
34952  this.$get = ['$rootScope', '$browser', '$location',
34953       function($rootScope,   $browser,   $location) {
34954
34955    /**
34956     * @name $testability
34957     *
34958     * @description
34959     * The private $$testability service provides a collection of methods for use when debugging
34960     * or by automated test and debugging tools.
34961     */
34962    var testability = {};
34963
34964    /**
34965     * @name $$testability#findBindings
34966     *
34967     * @description
34968     * Returns an array of elements that are bound (via ng-bind or {{}})
34969     * to expressions matching the input.
34970     *
34971     * @param {Element} element The element root to search from.
34972     * @param {string} expression The binding expression to match.
34973     * @param {boolean} opt_exactMatch If true, only returns exact matches
34974     *     for the expression. Filters and whitespace are ignored.
34975     */
34976    testability.findBindings = function(element, expression, opt_exactMatch) {
34977      var bindings = element.getElementsByClassName('ng-binding');
34978      var matches = [];
34979      forEach(bindings, function(binding) {
34980        var dataBinding = angular.element(binding).data('$binding');
34981        if (dataBinding) {
34982          forEach(dataBinding, function(bindingName) {
34983            if (opt_exactMatch) {
34984              var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)');
34985              if (matcher.test(bindingName)) {
34986                matches.push(binding);
34987              }
34988            } else {
34989              if (bindingName.indexOf(expression) != -1) {
34990                matches.push(binding);
34991              }
34992            }
34993          });
34994        }
34995      });
34996      return matches;
34997    };
34998
34999    /**
35000     * @name $$testability#findModels
35001     *
35002     * @description
35003     * Returns an array of elements that are two-way found via ng-model to
35004     * expressions matching the input.
35005     *
35006     * @param {Element} element The element root to search from.
35007     * @param {string} expression The model expression to match.
35008     * @param {boolean} opt_exactMatch If true, only returns exact matches
35009     *     for the expression.
35010     */
35011    testability.findModels = function(element, expression, opt_exactMatch) {
35012      var prefixes = ['ng-', 'data-ng-', 'ng\\:'];
35013      for (var p = 0; p < prefixes.length; ++p) {
35014        var attributeEquals = opt_exactMatch ? '=' : '*=';
35015        var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]';
35016        var elements = element.querySelectorAll(selector);
35017        if (elements.length) {
35018          return elements;
35019        }
35020      }
35021    };
35022
35023    /**
35024     * @name $$testability#getLocation
35025     *
35026     * @description
35027     * Shortcut for getting the location in a browser agnostic way. Returns
35028     *     the path, search, and hash. (e.g. /path?a=b#hash)
35029     */
35030    testability.getLocation = function() {
35031      return $location.url();
35032    };
35033
35034    /**
35035     * @name $$testability#setLocation
35036     *
35037     * @description
35038     * Shortcut for navigating to a location without doing a full page reload.
35039     *
35040     * @param {string} url The location url (path, search and hash,
35041     *     e.g. /path?a=b#hash) to go to.
35042     */
35043    testability.setLocation = function(url) {
35044      if (url !== $location.url()) {
35045        $location.url(url);
35046        $rootScope.$digest();
35047      }
35048    };
35049
35050    /**
35051     * @name $$testability#whenStable
35052     *
35053     * @description
35054     * Calls the callback when $timeout and $http requests are completed.
35055     *
35056     * @param {function} callback
35057     */
35058    testability.whenStable = function(callback) {
35059      $browser.notifyWhenNoOutstandingRequests(callback);
35060    };
35061
35062    return testability;
35063  }];
35064}
35065
35066function $TimeoutProvider() {
35067  this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler',
35068       function($rootScope,   $browser,   $q,   $$q,   $exceptionHandler) {
35069
35070    var deferreds = {};
35071
35072
35073     /**
35074      * @ngdoc service
35075      * @name $timeout
35076      *
35077      * @description
35078      * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
35079      * block and delegates any exceptions to
35080      * {@link ng.$exceptionHandler $exceptionHandler} service.
35081      *
35082      * The return value of calling `$timeout` is a promise, which will be resolved when
35083      * the delay has passed and the timeout function, if provided, is executed.
35084      *
35085      * To cancel a timeout request, call `$timeout.cancel(promise)`.
35086      *
35087      * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
35088      * synchronously flush the queue of deferred functions.
35089      *
35090      * If you only want a promise that will be resolved after some specified delay
35091      * then you can call `$timeout` without the `fn` function.
35092      *
35093      * @param {function()=} fn A function, whose execution should be delayed.
35094      * @param {number=} [delay=0] Delay in milliseconds.
35095      * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
35096      *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
35097      * @param {...*=} Pass additional parameters to the executed function.
35098      * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
35099      *   promise will be resolved with is the return value of the `fn` function.
35100      *
35101      */
35102    function timeout(fn, delay, invokeApply) {
35103      if (!isFunction(fn)) {
35104        invokeApply = delay;
35105        delay = fn;
35106        fn = noop;
35107      }
35108
35109      var args = sliceArgs(arguments, 3),
35110          skipApply = (isDefined(invokeApply) && !invokeApply),
35111          deferred = (skipApply ? $$q : $q).defer(),
35112          promise = deferred.promise,
35113          timeoutId;
35114
35115      timeoutId = $browser.defer(function() {
35116        try {
35117          deferred.resolve(fn.apply(null, args));
35118        } catch (e) {
35119          deferred.reject(e);
35120          $exceptionHandler(e);
35121        }
35122        finally {
35123          delete deferreds[promise.$$timeoutId];
35124        }
35125
35126        if (!skipApply) $rootScope.$apply();
35127      }, delay);
35128
35129      promise.$$timeoutId = timeoutId;
35130      deferreds[timeoutId] = deferred;
35131
35132      return promise;
35133    }
35134
35135
35136     /**
35137      * @ngdoc method
35138      * @name $timeout#cancel
35139      *
35140      * @description
35141      * Cancels a task associated with the `promise`. As a result of this, the promise will be
35142      * resolved with a rejection.
35143      *
35144      * @param {Promise=} promise Promise returned by the `$timeout` function.
35145      * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
35146      *   canceled.
35147      */
35148    timeout.cancel = function(promise) {
35149      if (promise && promise.$$timeoutId in deferreds) {
35150        deferreds[promise.$$timeoutId].reject('canceled');
35151        delete deferreds[promise.$$timeoutId];
35152        return $browser.defer.cancel(promise.$$timeoutId);
35153      }
35154      return false;
35155    };
35156
35157    return timeout;
35158  }];
35159}
35160
35161// NOTE:  The usage of window and document instead of $window and $document here is
35162// deliberate.  This service depends on the specific behavior of anchor nodes created by the
35163// browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and
35164// cause us to break tests.  In addition, when the browser resolves a URL for XHR, it
35165// doesn't know about mocked locations and resolves URLs to the real document - which is
35166// exactly the behavior needed here.  There is little value is mocking these out for this
35167// service.
35168var urlParsingNode = document.createElement("a");
35169var originUrl = urlResolve(window.location.href);
35170
35171
35172/**
35173 *
35174 * Implementation Notes for non-IE browsers
35175 * ----------------------------------------
35176 * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM,
35177 * results both in the normalizing and parsing of the URL.  Normalizing means that a relative
35178 * URL will be resolved into an absolute URL in the context of the application document.
35179 * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
35180 * properties are all populated to reflect the normalized URL.  This approach has wide
35181 * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc.  See
35182 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
35183 *
35184 * Implementation Notes for IE
35185 * ---------------------------
35186 * IE <= 10 normalizes the URL when assigned to the anchor node similar to the other
35187 * browsers.  However, the parsed components will not be set if the URL assigned did not specify
35188 * them.  (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.)  We
35189 * work around that by performing the parsing in a 2nd step by taking a previously normalized
35190 * URL (e.g. by assigning to a.href) and assigning it a.href again.  This correctly populates the
35191 * properties such as protocol, hostname, port, etc.
35192 *
35193 * References:
35194 *   http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
35195 *   http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
35196 *   http://url.spec.whatwg.org/#urlutils
35197 *   https://github.com/angular/angular.js/pull/2902
35198 *   http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
35199 *
35200 * @kind function
35201 * @param {string} url The URL to be parsed.
35202 * @description Normalizes and parses a URL.
35203 * @returns {object} Returns the normalized URL as a dictionary.
35204 *
35205 *   | member name   | Description    |
35206 *   |---------------|----------------|
35207 *   | href          | A normalized version of the provided URL if it was not an absolute URL |
35208 *   | protocol      | The protocol including the trailing colon                              |
35209 *   | host          | The host and port (if the port is non-default) of the normalizedUrl    |
35210 *   | search        | The search params, minus the question mark                             |
35211 *   | hash          | The hash string, minus the hash symbol
35212 *   | hostname      | The hostname
35213 *   | port          | The port, without ":"
35214 *   | pathname      | The pathname, beginning with "/"
35215 *
35216 */
35217function urlResolve(url) {
35218  var href = url;
35219
35220  if (msie) {
35221    // Normalize before parse.  Refer Implementation Notes on why this is
35222    // done in two steps on IE.
35223    urlParsingNode.setAttribute("href", href);
35224    href = urlParsingNode.href;
35225  }
35226
35227  urlParsingNode.setAttribute('href', href);
35228
35229  // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
35230  return {
35231    href: urlParsingNode.href,
35232    protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
35233    host: urlParsingNode.host,
35234    search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
35235    hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
35236    hostname: urlParsingNode.hostname,
35237    port: urlParsingNode.port,
35238    pathname: (urlParsingNode.pathname.charAt(0) === '/')
35239      ? urlParsingNode.pathname
35240      : '/' + urlParsingNode.pathname
35241  };
35242}
35243
35244/**
35245 * Parse a request URL and determine whether this is a same-origin request as the application document.
35246 *
35247 * @param {string|object} requestUrl The url of the request as a string that will be resolved
35248 * or a parsed URL object.
35249 * @returns {boolean} Whether the request is for the same origin as the application document.
35250 */
35251function urlIsSameOrigin(requestUrl) {
35252  var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl;
35253  return (parsed.protocol === originUrl.protocol &&
35254          parsed.host === originUrl.host);
35255}
35256
35257/**
35258 * @ngdoc service
35259 * @name $window
35260 *
35261 * @description
35262 * A reference to the browser's `window` object. While `window`
35263 * is globally available in JavaScript, it causes testability problems, because
35264 * it is a global variable. In angular we always refer to it through the
35265 * `$window` service, so it may be overridden, removed or mocked for testing.
35266 *
35267 * Expressions, like the one defined for the `ngClick` directive in the example
35268 * below, are evaluated with respect to the current scope.  Therefore, there is
35269 * no risk of inadvertently coding in a dependency on a global value in such an
35270 * expression.
35271 *
35272 * @example
35273   <example module="windowExample">
35274     <file name="index.html">
35275       <script>
35276         angular.module('windowExample', [])
35277           .controller('ExampleController', ['$scope', '$window', function($scope, $window) {
35278             $scope.greeting = 'Hello, World!';
35279             $scope.doGreeting = function(greeting) {
35280               $window.alert(greeting);
35281             };
35282           }]);
35283       </script>
35284       <div ng-controller="ExampleController">
35285         <input type="text" ng-model="greeting" aria-label="greeting" />
35286         <button ng-click="doGreeting(greeting)">ALERT</button>
35287       </div>
35288     </file>
35289     <file name="protractor.js" type="protractor">
35290      it('should display the greeting in the input box', function() {
35291       element(by.model('greeting')).sendKeys('Hello, E2E Tests');
35292       // If we click the button it will block the test runner
35293       // element(':button').click();
35294      });
35295     </file>
35296   </example>
35297 */
35298function $WindowProvider() {
35299  this.$get = valueFn(window);
35300}
35301
35302/**
35303 * @name $$cookieReader
35304 * @requires $document
35305 *
35306 * @description
35307 * This is a private service for reading cookies used by $http and ngCookies
35308 *
35309 * @return {Object} a key/value map of the current cookies
35310 */
35311function $$CookieReader($document) {
35312  var rawDocument = $document[0] || {};
35313  var lastCookies = {};
35314  var lastCookieString = '';
35315
35316  function safeDecodeURIComponent(str) {
35317    try {
35318      return decodeURIComponent(str);
35319    } catch (e) {
35320      return str;
35321    }
35322  }
35323
35324  return function() {
35325    var cookieArray, cookie, i, index, name;
35326    var currentCookieString = rawDocument.cookie || '';
35327
35328    if (currentCookieString !== lastCookieString) {
35329      lastCookieString = currentCookieString;
35330      cookieArray = lastCookieString.split('; ');
35331      lastCookies = {};
35332
35333      for (i = 0; i < cookieArray.length; i++) {
35334        cookie = cookieArray[i];
35335        index = cookie.indexOf('=');
35336        if (index > 0) { //ignore nameless cookies
35337          name = safeDecodeURIComponent(cookie.substring(0, index));
35338          // the first value that is seen for a cookie is the most
35339          // specific one.  values for the same cookie name that
35340          // follow are for less specific paths.
35341          if (isUndefined(lastCookies[name])) {
35342            lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1));
35343          }
35344        }
35345      }
35346    }
35347    return lastCookies;
35348  };
35349}
35350
35351$$CookieReader.$inject = ['$document'];
35352
35353function $$CookieReaderProvider() {
35354  this.$get = $$CookieReader;
35355}
35356
35357/* global currencyFilter: true,
35358 dateFilter: true,
35359 filterFilter: true,
35360 jsonFilter: true,
35361 limitToFilter: true,
35362 lowercaseFilter: true,
35363 numberFilter: true,
35364 orderByFilter: true,
35365 uppercaseFilter: true,
35366 */
35367
35368/**
35369 * @ngdoc provider
35370 * @name $filterProvider
35371 * @description
35372 *
35373 * Filters are just functions which transform input to an output. However filters need to be
35374 * Dependency Injected. To achieve this a filter definition consists of a factory function which is
35375 * annotated with dependencies and is responsible for creating a filter function.
35376 *
35377 * <div class="alert alert-warning">
35378 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
35379 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
35380 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
35381 * (`myapp_subsection_filterx`).
35382 * </div>
35383 *
35384 * ```js
35385 *   // Filter registration
35386 *   function MyModule($provide, $filterProvider) {
35387 *     // create a service to demonstrate injection (not always needed)
35388 *     $provide.value('greet', function(name){
35389 *       return 'Hello ' + name + '!';
35390 *     });
35391 *
35392 *     // register a filter factory which uses the
35393 *     // greet service to demonstrate DI.
35394 *     $filterProvider.register('greet', function(greet){
35395 *       // return the filter function which uses the greet service
35396 *       // to generate salutation
35397 *       return function(text) {
35398 *         // filters need to be forgiving so check input validity
35399 *         return text && greet(text) || text;
35400 *       };
35401 *     });
35402 *   }
35403 * ```
35404 *
35405 * The filter function is registered with the `$injector` under the filter name suffix with
35406 * `Filter`.
35407 *
35408 * ```js
35409 *   it('should be the same instance', inject(
35410 *     function($filterProvider) {
35411 *       $filterProvider.register('reverse', function(){
35412 *         return ...;
35413 *       });
35414 *     },
35415 *     function($filter, reverseFilter) {
35416 *       expect($filter('reverse')).toBe(reverseFilter);
35417 *     });
35418 * ```
35419 *
35420 *
35421 * For more information about how angular filters work, and how to create your own filters, see
35422 * {@link guide/filter Filters} in the Angular Developer Guide.
35423 */
35424
35425/**
35426 * @ngdoc service
35427 * @name $filter
35428 * @kind function
35429 * @description
35430 * Filters are used for formatting data displayed to the user.
35431 *
35432 * The general syntax in templates is as follows:
35433 *
35434 *         {{ expression [| filter_name[:parameter_value] ... ] }}
35435 *
35436 * @param {String} name Name of the filter function to retrieve
35437 * @return {Function} the filter function
35438 * @example
35439   <example name="$filter" module="filterExample">
35440     <file name="index.html">
35441       <div ng-controller="MainCtrl">
35442        <h3>{{ originalText }}</h3>
35443        <h3>{{ filteredText }}</h3>
35444       </div>
35445     </file>
35446
35447     <file name="script.js">
35448      angular.module('filterExample', [])
35449      .controller('MainCtrl', function($scope, $filter) {
35450        $scope.originalText = 'hello';
35451        $scope.filteredText = $filter('uppercase')($scope.originalText);
35452      });
35453     </file>
35454   </example>
35455  */
35456$FilterProvider.$inject = ['$provide'];
35457function $FilterProvider($provide) {
35458  var suffix = 'Filter';
35459
35460  /**
35461   * @ngdoc method
35462   * @name $filterProvider#register
35463   * @param {string|Object} name Name of the filter function, or an object map of filters where
35464   *    the keys are the filter names and the values are the filter factories.
35465   *
35466   *    <div class="alert alert-warning">
35467   *    **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
35468   *    Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
35469   *    your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
35470   *    (`myapp_subsection_filterx`).
35471   *    </div>
35472    * @param {Function} factory If the first argument was a string, a factory function for the filter to be registered.
35473   * @returns {Object} Registered filter instance, or if a map of filters was provided then a map
35474   *    of the registered filter instances.
35475   */
35476  function register(name, factory) {
35477    if (isObject(name)) {
35478      var filters = {};
35479      forEach(name, function(filter, key) {
35480        filters[key] = register(key, filter);
35481      });
35482      return filters;
35483    } else {
35484      return $provide.factory(name + suffix, factory);
35485    }
35486  }
35487  this.register = register;
35488
35489  this.$get = ['$injector', function($injector) {
35490    return function(name) {
35491      return $injector.get(name + suffix);
35492    };
35493  }];
35494
35495  ////////////////////////////////////////
35496
35497  /* global
35498    currencyFilter: false,
35499    dateFilter: false,
35500    filterFilter: false,
35501    jsonFilter: false,
35502    limitToFilter: false,
35503    lowercaseFilter: false,
35504    numberFilter: false,
35505    orderByFilter: false,
35506    uppercaseFilter: false,
35507  */
35508
35509  register('currency', currencyFilter);
35510  register('date', dateFilter);
35511  register('filter', filterFilter);
35512  register('json', jsonFilter);
35513  register('limitTo', limitToFilter);
35514  register('lowercase', lowercaseFilter);
35515  register('number', numberFilter);
35516  register('orderBy', orderByFilter);
35517  register('uppercase', uppercaseFilter);
35518}
35519
35520/**
35521 * @ngdoc filter
35522 * @name filter
35523 * @kind function
35524 *
35525 * @description
35526 * Selects a subset of items from `array` and returns it as a new array.
35527 *
35528 * @param {Array} array The source array.
35529 * @param {string|Object|function()} expression The predicate to be used for selecting items from
35530 *   `array`.
35531 *
35532 *   Can be one of:
35533 *
35534 *   - `string`: The string is used for matching against the contents of the `array`. All strings or
35535 *     objects with string properties in `array` that match this string will be returned. This also
35536 *     applies to nested object properties.
35537 *     The predicate can be negated by prefixing the string with `!`.
35538 *
35539 *   - `Object`: A pattern object can be used to filter specific properties on objects contained
35540 *     by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
35541 *     which have property `name` containing "M" and property `phone` containing "1". A special
35542 *     property name `$` can be used (as in `{$:"text"}`) to accept a match against any
35543 *     property of the object or its nested object properties. That's equivalent to the simple
35544 *     substring match with a `string` as described above. The predicate can be negated by prefixing
35545 *     the string with `!`.
35546 *     For example `{name: "!M"}` predicate will return an array of items which have property `name`
35547 *     not containing "M".
35548 *
35549 *     Note that a named property will match properties on the same level only, while the special
35550 *     `$` property will match properties on the same level or deeper. E.g. an array item like
35551 *     `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but
35552 *     **will** be matched by `{$: 'John'}`.
35553 *
35554 *   - `function(value, index, array)`: A predicate function can be used to write arbitrary filters.
35555 *     The function is called for each element of the array, with the element, its index, and
35556 *     the entire array itself as arguments.
35557 *
35558 *     The final result is an array of those elements that the predicate returned true for.
35559 *
35560 * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in
35561 *     determining if the expected value (from the filter expression) and actual value (from
35562 *     the object in the array) should be considered a match.
35563 *
35564 *   Can be one of:
35565 *
35566 *   - `function(actual, expected)`:
35567 *     The function will be given the object value and the predicate value to compare and
35568 *     should return true if both values should be considered equal.
35569 *
35570 *   - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`.
35571 *     This is essentially strict comparison of expected and actual.
35572 *
35573 *   - `false|undefined`: A short hand for a function which will look for a substring match in case
35574 *     insensitive way.
35575 *
35576 *     Primitive values are converted to strings. Objects are not compared against primitives,
35577 *     unless they have a custom `toString` method (e.g. `Date` objects).
35578 *
35579 * @example
35580   <example>
35581     <file name="index.html">
35582       <div ng-init="friends = [{name:'John', phone:'555-1276'},
35583                                {name:'Mary', phone:'800-BIG-MARY'},
35584                                {name:'Mike', phone:'555-4321'},
35585                                {name:'Adam', phone:'555-5678'},
35586                                {name:'Julie', phone:'555-8765'},
35587                                {name:'Juliette', phone:'555-5678'}]"></div>
35588
35589       <label>Search: <input ng-model="searchText"></label>
35590       <table id="searchTextResults">
35591         <tr><th>
35591Name</th><th>Phone</th></tr>
35592         <tr ng-repeat="friend in friends | filter:searchText">
35593           <td>{{friend.name}}</td>
35594           <td>{{friend.phone}}</td>
35595         </tr>
35596       </table>
35597       <hr>
35598       <label>Any: <input ng-model="search.$"></label> <br>
35599       <label>Name only <input ng-model="search.name"></label><br>
35600       <label>Phone only <input ng-model="search.phone"></label><br>
35601       <label>Equality <input type="checkbox" ng-model="strict"></label><br>
35602       <table id="searchObjResults">
35603         <tr><th>Name</th><th>Phone</th></tr>
35604         <tr ng-repeat="friendObj in friends | filter:search:strict">
35605           <td>{{friendObj.name}}</td>
35606           <td>{{friendObj.phone}}</td>
35607         </tr>
35608       </table>
35609     </file>
35610     <file name="protractor.js" type="protractor">
35611       var expectFriendNames = function(expectedNames, key) {
35612         element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) {
35613           arr.forEach(function(wd, i) {
35614             expect(wd.getText()).toMatch(expectedNames[i]);
35615           });
35616         });
35617       };
35618
35619       it('should search across all fields when filtering with a string', function() {
35620         var searchText = element(by.model('searchText'));
35621         searchText.clear();
35622         searchText.sendKeys('m');
35623         expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend');
35624
35625         searchText.clear();
35626         searchText.sendKeys('76');
35627         expectFriendNames(['John', 'Julie'], 'friend');
35628       });
35629
35630       it('should search in specific fields when filtering with a predicate object', function() {
35631         var searchAny = element(by.model('search.$'));
35632         searchAny.clear();
35633         searchAny.sendKeys('i');
35634         expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj');
35635       });
35636       it('should use a equal comparison when comparator is true', function() {
35637         var searchName = element(by.model('search.name'));
35638         var strict = element(by.model('strict'));
35639         searchName.clear();
35640         searchName.sendKeys('Julie');
35641         strict.click();
35642         expectFriendNames(['Julie'], 'friendObj');
35643       });
35644     </file>
35645   </example>
35646 */
35647function filterFilter() {
35648  return function(array, expression, comparator) {
35649    if (!isArrayLike(array)) {
35650      if (array == null) {
35651        return array;
35652      } else {
35653        throw minErr('filter')('notarray', 'Expected array but received: {0}', array);
35654      }
35655    }
35656
35657    var expressionType = getTypeForFilter(expression);
35658    var predicateFn;
35659    var matchAgainstAnyProp;
35660
35661    switch (expressionType) {
35662      case 'function':
35663        predicateFn = expression;
35664        break;
35665      case 'boolean':
35666      case 'null':
35667      case 'number':
35668      case 'string':
35669        matchAgainstAnyProp = true;
35670        //jshint -W086
35671      case 'object':
35672        //jshint +W086
35673        predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp);
35674        break;
35675      default:
35676        return array;
35677    }
35678
35679    return Array.prototype.filter.call(array, predicateFn);
35680  };
35681}
35682
35683// Helper functions for `filterFilter`
35684function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
35685  var shouldMatchPrimitives = isObject(expression) && ('$' in expression);
35686  var predicateFn;
35687
35688  if (comparator === true) {
35689    comparator = equals;
35690  } else if (!isFunction(comparator)) {
35691    comparator = function(actual, expected) {
35692      if (isUndefined(actual)) {
35693        // No substring matching against `undefined`
35694        return false;
35695      }
35696      if ((actual === null) || (expected === null)) {
35697        // No substring matching against `null`; only match against `null`
35698        return actual === expected;
35699      }
35700      if (isObject(expected) || (isObject(actual) && !hasCustomToString(actual))) {
35701        // Should not compare primitives against objects, unless they have custom `toString` method
35702        return false;
35703      }
35704
35705      actual = lowercase('' + actual);
35706      expected = lowercase('' + expected);
35707      return actual.indexOf(expected) !== -1;
35708    };
35709  }
35710
35711  predicateFn = function(item) {
35712    if (shouldMatchPrimitives && !isObject(item)) {
35713      return deepCompare(item, expression.$, comparator, false);
35714    }
35715    return deepCompare(item, expression, comparator, matchAgainstAnyProp);
35716  };
35717
35718  return predicateFn;
35719}
35720
35721function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatchWholeObject) {
35722  var actualType = getTypeForFilter(actual);
35723  var expectedType = getTypeForFilter(expected);
35724
35725  if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
35726    return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp);
35727  } else if (isArray(actual)) {
35728    // In case `actual` is an array, consider it a match
35729    // if ANY of it's items matches `expected`
35730    return actual.some(function(item) {
35731      return deepCompare(item, expected, comparator, matchAgainstAnyProp);
35732    });
35733  }
35734
35735  switch (actualType) {
35736    case 'object':
35737      var key;
35738      if (matchAgainstAnyProp) {
35739        for (key in actual) {
35740          if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, true)) {
35741            return true;
35742          }
35743        }
35744        return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, false);
35745      } else if (expectedType === 'object') {
35746        for (key in expected) {
35747          var expectedVal = expected[key];
35748          if (isFunction(expectedVal) || isUndefined(expectedVal)) {
35749            continue;
35750          }
35751
35752          var matchAnyProperty = key === '$';
35753          var actualVal = matchAnyProperty ? actual : actual[key];
35754          if (!deepCompare(actualVal, expectedVal, comparator, matchAnyProperty, matchAnyProperty)) {
35755            return false;
35756          }
35757        }
35758        return true;
35759      } else {
35760        return comparator(actual, expected);
35761      }
35762      break;
35763    case 'function':
35764      return false;
35765    default:
35766      return comparator(actual, expected);
35767  }
35768}
35769
35770// Used for easily differentiating between `null` and actual `object`
35771function getTypeForFilter(val) {
35772  return (val === null) ? 'null' : typeof val;
35773}
35774
35775/**
35776 * @ngdoc filter
35777 * @name currency
35778 * @kind function
35779 *
35780 * @description
35781 * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
35782 * symbol for current locale is used.
35783 *
35784 * @param {number} amount Input to filter.
35785 * @param {string=} symbol Currency symbol or identifier to be displayed.
35786 * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale
35787 * @returns {string} Formatted number.
35788 *
35789 *
35790 * @example
35791   <example module="currencyExample">
35792     <file name="index.html">
35793       <script>
35794         angular.module('currencyExample', [])
35795           .controller('ExampleController', ['$scope', function($scope) {
35796             $scope.amount = 1234.56;
35797           }]);
35798       </script>
35799       <div ng-controller="ExampleController">
35800         <input type="number" ng-model="amount" aria-label="amount"> <br>
35801         default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br>
35802         custom currency identifier (USD$): <span id="currency-custom">{{amount | currency:"USD$"}}</span>
35803         no fractions (0): <span id="currency-no-fractions">{{amount | currency:"USD$":0}}</span>
35804       </div>
35805     </file>
35806     <file name="protractor.js" type="protractor">
35807       it('should init with 1234.56', function() {
35808         expect(element(by.id('currency-default')).getText()).toBe('$1,234.56');
35809         expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56');
35810         expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235');
35811       });
35812       it('should update', function() {
35813         if (browser.params.browser == 'safari') {
35814           // Safari does not understand the minus key. See
35815           // https://github.com/angular/protractor/issues/481
35816           return;
35817         }
35818         element(by.model('amount')).clear();
35819         element(by.model('amount')).sendKeys('-1234');
35820         expect(element(by.id('currency-default')).getText()).toBe('-$1,234.00');
35821         expect(element(by.id('currency-custom')).getText()).toBe('-USD$1,234.00');
35822         expect(element(by.id('currency-no-fractions')).getText()).toBe('-USD$1,234');
35823       });
35824     </file>
35825   </example>
35826 */
35827currencyFilter.$inject = ['$locale'];
35828function currencyFilter($locale) {
35829  var formats = $locale.NUMBER_FORMATS;
35830  return function(amount, currencySymbol, fractionSize) {
35831    if (isUndefined(currencySymbol)) {
35832      currencySymbol = formats.CURRENCY_SYM;
35833    }
35834
35835    if (isUndefined(fractionSize)) {
35836      fractionSize = formats.PATTERNS[1].maxFrac;
35837    }
35838
35839    // if null or undefined pass it through
35840    return (amount == null)
35841        ? amount
35842        : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize).
35843            replace(/\u00A4/g, currencySymbol);
35844  };
35845}
35846
35847/**
35848 * @ngdoc filter
35849 * @name number
35850 * @kind function
35851 *
35852 * @description
35853 * Formats a number as text.
35854 *
35855 * If the input is null or undefined, it will just be returned.
35856 * If the input is infinite (Infinity/-Infinity) the Infinity symbol '∞' is returned.
35857 * If the input is not a number an empty string is returned.
35858 *
35859 *
35860 * @param {number|string} number Number to format.
35861 * @param {(number|string)=} fractionSize Number of decimal places to round the number to.
35862 * If this is not provided then the fraction size is computed from the current locale's number
35863 * formatting pattern. In the case of the default locale, it will be 3.
35864 * @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit.
35865 *
35866 * @example
35867   <example module="numberFilterExample">
35868     <file name="index.html">
35869       <script>
35870         angular.module('numberFilterExample', [])
35871           .controller('ExampleController', ['$scope', function($scope) {
35872             $scope.val = 1234.56789;
35873           }]);
35874       </script>
35875       <div ng-controller="ExampleController">
35876         <label>Enter number: <input ng-model='val'></label><br>
35877         Default formatting: <span id='number-default'>{{val | number}}</span><br>
35878         No fractions: <span>{{val | number:0}}</span><br>
35879         Negative number: <span>{{-val | number:4}}</span>
35880       </div>
35881     </file>
35882     <file name="protractor.js" type="protractor">
35883       it('should format numbers', function() {
35884         expect(element(by.id('number-default')).getText()).toBe('1,234.568');
35885         expect(element(by.binding('val | number:0')).getText()).toBe('1,235');
35886         expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679');
35887       });
35888
35889       it('should update', function() {
35890         element(by.model('val')).clear();
35891         element(by.model('val')).sendKeys('3374.333');
35892         expect(element(by.id('number-default')).getText()).toBe('3,374.333');
35893         expect(element(by.binding('val | number:0')).getText()).toBe('3,374');
35894         expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330');
35895      });
35896     </file>
35897   </example>
35898 */
35899
35900
35901numberFilter.$inject = ['$locale'];
35902function numberFilter($locale) {
35903  var formats = $locale.NUMBER_FORMATS;
35904  return function(number, fractionSize) {
35905
35906    // if null or undefined pass it through
35907    return (number == null)
35908        ? number
35909        : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP,
35910                       fractionSize);
35911  };
35912}
35913
35914var DECIMAL_SEP = '.';
35915function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
35916  if (isObject(number)) return '';
35917
35918  var isNegative = number < 0;
35919  number = Math.abs(number);
35920
35921  var isInfinity = number === Infinity;
35922  if (!isInfinity && !isFinite(number)) return '';
35923
35924  var numStr = number + '',
35925      formatedText = '',
35926      hasExponent = false,
35927      parts = [];
35928
35929  if (isInfinity) formatedText = '\u221e';
35930
35931  if (!isInfinity && numStr.indexOf('e') !== -1) {
35932    var match = numStr.match(/([\d\.]+)e(-?)(\d+)/);
35933    if (match && match[2] == '-' && match[3] > fractionSize + 1) {
35934      number = 0;
35935    } else {
35936      formatedText = numStr;
35937      hasExponent = true;
35938    }
35939  }
35940
35941  if (!isInfinity && !hasExponent) {
35942    var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length;
35943
35944    // determine fractionSize if it is not specified
35945    if (isUndefined(fractionSize)) {
35946      fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac);
35947    }
35948
35949    // safely round numbers in JS without hitting imprecisions of floating-point arithmetics
35950    // inspired by:
35951    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
35952    number = +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize);
35953
35954    var fraction = ('' + number).split(DECIMAL_SEP);
35955    var whole = fraction[0];
35956    fraction = fraction[1] || '';
35957
35958    var i, pos = 0,
35959        lgroup = pattern.lgSize,
35960        group = pattern.gSize;
35961
35962    if (whole.length >= (lgroup + group)) {
35963      pos = whole.length - lgroup;
35964      for (i = 0; i < pos; i++) {
35965        if ((pos - i) % group === 0 && i !== 0) {
35966          formatedText += groupSep;
35967        }
35968        formatedText += whole.charAt(i);
35969      }
35970    }
35971
35972    for (i = pos; i < whole.length; i++) {
35973      if ((whole.length - i) % lgroup === 0 && i !== 0) {
35974        formatedText += groupSep;
35975      }
35976      formatedText += whole.charAt(i);
35977    }
35978
35979    // format fraction part.
35980    while (fraction.length < fractionSize) {
35981      fraction += '0';
35982    }
35983
35984    if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize);
35985  } else {
35986    if (fractionSize > 0 && number < 1) {
35987      formatedText = number.toFixed(fractionSize);
35988      number = parseFloat(formatedText);
35989      formatedText = formatedText.replace(DECIMAL_SEP, decimalSep);
35990    }
35991  }
35992
35993  if (number === 0) {
35994    isNegative = false;
35995  }
35996
35997  parts.push(isNegative ? pattern.negPre : pattern.posPre,
35998             formatedText,
35999             isNegative ? pattern.negSuf : pattern.posSuf);
36000  return parts.join('');
36001}
36002
36003function padNumber(num, digits, trim) {
36004  var neg = '';
36005  if (num < 0) {
36006    neg =  '-';
36007    num = -num;
36008  }
36009  num = '' + num;
36010  while (num.length < digits) num = '0' + num;
36011  if (trim) {
36012    num = num.substr(num.length - digits);
36013  }
36014  return neg + num;
36015}
36016
36017
36018function dateGetter(name, size, offset, trim) {
36019  offset = offset || 0;
36020  return function(date) {
36021    var value = date['get' + name]();
36022    if (offset > 0 || value > -offset) {
36023      value += offset;
36024    }
36025    if (value === 0 && offset == -12) value = 12;
36026    return padNumber(value, size, trim);
36027  };
36028}
36029
36030function dateStrGetter(name, shortForm) {
36031  return function(date, formats) {
36032    var value = date['get' + name]();
36033    var get = uppercase(shortForm ? ('SHORT' + name) : name);
36034
36035    return formats[get][value];
36036  };
36037}
36038
36039function timeZoneGetter(date, formats, offset) {
36040  var zone = -1 * offset;
36041  var paddedZone = (zone >= 0) ? "+" : "";
36042
36043  paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
36044                padNumber(Math.abs(zone % 60), 2);
36045
36046  return paddedZone;
36047}
36048
36049function getFirstThursdayOfYear(year) {
36050    // 0 = index of January
36051    var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay();
36052    // 4 = index of Thursday (+1 to account for 1st = 5)
36053    // 11 = index of *next* Thursday (+1 account for 1st = 12)
36054    return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst);
36055}
36056
36057function getThursdayThisWeek(datetime) {
36058    return new Date(datetime.getFullYear(), datetime.getMonth(),
36059      // 4 = index of Thursday
36060      datetime.getDate() + (4 - datetime.getDay()));
36061}
36062
36063function weekGetter(size) {
36064   return function(date) {
36065      var firstThurs = getFirstThursdayOfYear(date.getFullYear()),
36066         thisThurs = getThursdayThisWeek(date);
36067
36068      var diff = +thisThurs - +firstThurs,
36069         result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week
36070
36071      return padNumber(result, size);
36072   };
36073}
36074
36075function ampmGetter(date, formats) {
36076  return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
36077}
36078
36079function eraGetter(date, formats) {
vendor: 5,157 bytes, lines 36080-36192
36080  return date.getFullYear() <= 0 ? formats.ERAS[0] : formats.ERAS[1];
36081}
36082
36083function longEraGetter(date, formats) {
36084  return date.getFullYear() <= 0 ? formats.ERANAMES[0] : formats.ERANAMES[1];
36085}
36086
36087var DATE_FORMATS = {
36088  yyyy: dateGetter('FullYear', 4),
36089    yy: dateGetter('FullYear', 2, 0, true),
36090     y: dateGetter('FullYear', 1),
36091  MMMM: dateStrGetter('Month'),
36092   MMM: dateStrGetter('Month', true),
36093    MM: dateGetter('Month', 2, 1),
36094     M: dateGetter('Month', 1, 1),
36095    dd: dateGetter('Date', 2),
36096     d: dateGetter('Date', 1),
36097    HH: dateGetter('Hours', 2),
36098     H: dateGetter('Hours', 1),
36099    hh: dateGetter('Hours', 2, -12),
36100     h: dateGetter('Hours', 1, -12),
36101    mm: dateGetter('Minutes', 2),
36102     m: dateGetter('Minutes', 1),
36103    ss: dateGetter('Seconds', 2),
36104     s: dateGetter('Seconds', 1),
36105     // while ISO 8601 requires fractions to be prefixed with `.` or `,`
36106     // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
36107   sss: dateGetter('Milliseconds', 3),
36108  EEEE: dateStrGetter('Day'),
36109   EEE: dateStrGetter('Day', true),
36110     a: ampmGetter,
36111     Z: timeZoneGetter,
36112    ww: weekGetter(2),
36113     w: weekGetter(1),
36114     G: eraGetter,
36115     GG: eraGetter,
36116     GGG: eraGetter,
36117     GGGG: longEraGetter
36118};
36119
36120var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
36121    NUMBER_STRING = /^\-?\d+$/;
36122
36123/**
36124 * @ngdoc filter
36125 * @name date
36126 * @kind function
36127 *
36128 * @description
36129 *   Formats `date` to a string based on the requested `format`.
36130 *
36131 *   `format` string can be composed of the following elements:
36132 *
36133 *   * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
36134 *   * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
36135 *   * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
36136 *   * `'MMMM'`: Month in year (January-December)
36137 *   * `'MMM'`: Month in year (Jan-Dec)
36138 *   * `'MM'`: Month in year, padded (01-12)
36139 *   * `'M'`: Month in year (1-12)
36140 *   * `'dd'`: Day in month, padded (01-31)
36141 *   * `'d'`: Day in month (1-31)
36142 *   * `'EEEE'`: Day in Week,(Sunday-Saturday)
36143 *   * `'EEE'`: Day in Week, (Sun-Sat)
36144 *   * `'HH'`: Hour in day, padded (00-23)
36145 *   * `'H'`: Hour in day (0-23)
36146 *   * `'hh'`: Hour in AM/PM, padded (01-12)
36147 *   * `'h'`: Hour in AM/PM, (1-12)
36148 *   * `'mm'`: Minute in hour, padded (00-59)
36149 *   * `'m'`: Minute in hour (0-59)
36150 *   * `'ss'`: Second in minute, padded (00-59)
36151 *   * `'s'`: Second in minute (0-59)
36152 *   * `'sss'`: Millisecond in second, padded (000-999)
36153 *   * `'a'`: AM/PM marker
36154 *   * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
36155 *   * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year
36156 *   * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year
36157 *   * `'G'`, `'GG'`, `'GGG'`: The abbreviated form of the era string (e.g. 'AD')
36158 *   * `'GGGG'`: The long form of the era string (e.g. 'Anno Domini')
36159 *
36160 *   `format` string can also be one of the following predefined
36161 *   {@link guide/i18n localizable formats}:
36162 *
36163 *   * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
36164 *     (e.g. Sep 3, 2010 12:05:08 PM)
36165 *   * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US  locale (e.g. 9/3/10 12:05 PM)
36166 *   * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US  locale
36167 *     (e.g. Friday, September 3, 2010)
36168 *   * `'longDate'`: equivalent to `'MMMM d, y'` for en_US  locale (e.g. September 3, 2010)
36169 *   * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US  locale (e.g. Sep 3, 2010)
36170 *   * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
36171 *   * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM)
36172 *   * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM)
36173 *
36174 *   `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g.
36175 *   `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
36176 *   (e.g. `"h 'o''clock'"`).
36177 *
36178 * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
36179 *    number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
36180 *    shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
36181 *    specified in the string input, the time is considered to be in the local timezone.
36182 * @param {string=} format Formatting rules (see Description). If not specified,
36183 *    `mediumDate` is used.
36184 * @param {string=} timezone Timezone to be used for formatting. It understands UTC/GMT and the
36185 *    continental US time zone abbreviations, but for general use, use a time zone offset, for
36186 *    example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
36187 *    If not specified, the timezone of the browser will be used.
36188 * @returns {string} Formatted string or the input if input is not recognized as date/millis.
36189 *
36190 * @example
36191   <example>
36192     <file name="index.html">
36193       <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
36194           <span>{{1288323623006 | date:'medium'}}</span><br>
36195       <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
36196          <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
36197       <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
36198          <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
36199       <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>:
36200          <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br>
36201     </file>
36202     <file name="protractor.js" type="protractor">
36203       it('should format date', function() {
36204         expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
36205            toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
36206         expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
36207            toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
36208         expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
36209            toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
36210         expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()).
36211            toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/);
36212       });
36213     </file>
36214   </example>
36215 */
36216dateFilter.$inject = ['$locale'];
36217function dateFilter($locale) {
36218
36219
36220  var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
36221                     // 1        2       3         4          5          6          7          8  9     10      11
36222  function jsonStringToDate(string) {
36223    var match;
36224    if (match = string.match(R_ISO8601_STR)) {
36225      var date = new Date(0),
36226          tzHour = 0,
36227          tzMin  = 0,
36228          dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
36229          timeSetter = match[8] ? date.setUTCHours : date.setHours;
36230
36231      if (match[9]) {
36232        tzHour = toInt(match[9] + match[10]);
36233        tzMin = toInt(match[9] + match[11]);
36234      }
36235      dateSetter.call(date, toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
36236      var h = toInt(match[4] || 0) - tzHour;
36237      var m = toInt(match[5] || 0) - tzMin;
36238      var s = toInt(match[6] || 0);
36239      var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
36240      timeSetter.call(date, h, m, s, ms);
36241      return date;
36242    }
36243    return string;
36244  }
36245
36246
36247  return function(date, format, timezone) {
36248    var text = '',
36249        parts = [],
36250        fn, match;
36251
36252    format = format || 'mediumDate';
36253    format = $locale.DATETIME_FORMATS[format] || format;
36254    if (isString(date)) {
36255      date = NUMBER_STRING.test(date) ? toInt(date) : jsonStringToDate(date);
36256    }
36257
36258    if (isNumber(date)) {
36259      date = new Date(date);
36260    }
36261
36262    if (!isDate(date) || !isFinite(date.getTime())) {
36263      return date;
36264    }
36265
36266    while (format) {
36267      match = DATE_FORMATS_SPLIT.exec(format);
36268      if (match) {
36269        parts = concat(parts, match, 1);
36270        format = parts.pop();
36271      } else {
36272        parts.push(format);
36273        format = null;
36274      }
36275    }
36276
36277    var dateTimezoneOffset = date.getTimezoneOffset();
36278    if (timezone) {
36279      dateTimezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
36280      date = convertTimezoneToLocal(date, timezone, true);
36281    }
36282    forEach(parts, function(value) {
36283      fn = DATE_FORMATS[value];
36284      text += fn ? fn(date, $locale.DATETIME_FORMATS, dateTimezoneOffset)
36285                 : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
36286    });
36287
36288    return text;
36289  };
36290}
36291
36292
36293/**
36294 * @ngdoc filter
36295 * @name json
36296 * @kind function
36297 *
36298 * @description
36299 *   Allows you to convert a JavaScript object into JSON string.
36300 *
36301 *   This filter is mostly useful for debugging. When using the double curly {{value}} notation
36302 *   the binding is automatically converted to JSON.
36303 *
36304 * @param {*} object Any JavaScript object (including arrays and primitive types) to filter.
36305 * @param {number=}
36305 spacing The number of spaces to use per indentation, defaults to 2.
36306 * @returns {string} JSON string.
36307 *
36308 *
36309 * @example
36310   <example>
36311     <file name="index.html">
36312       <pre id="default-spacing">{{ {'name':'value'} | json }}</pre>
36313       <pre id="custom-spacing">{{ {'name':'value'} | json:4 }}</pre>
36314     </file>
36315     <file name="protractor.js" type="protractor">
36316       it('should jsonify filtered objects', function() {
36317         expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n  "name": ?"value"\n}/);
36318         expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n    "name": ?"value"\n}/);
36319       });
36320     </file>
36321   </example>
36322 *
36323 */
36324function jsonFilter() {
36325  return function(object, spacing) {
36326    if (isUndefined(spacing)) {
36327        spacing = 2;
36328    }
36329    return toJson(object, spacing);
36330  };
36331}
36332
36333
36334/**
36335 * @ngdoc filter
36336 * @name lowercase
36337 * @kind function
36338 * @description
36339 * Converts string to lowercase.
36340 * @see angular.lowercase
36341 */
36342var lowercaseFilter = valueFn(lowercase);
36343
36344
36345/**
36346 * @ngdoc filter
36347 * @name uppercase
36348 * @kind function
36349 * @description
36350 * Converts string to uppercase.
36351 * @see angular.uppercase
36352 */
36353var uppercaseFilter = valueFn(uppercase);
36354
36355/**
36356 * @ngdoc filter
36357 * @name limitTo
36358 * @kind function
36359 *
36360 * @description
36361 * Creates a new array or string containing only a specified number of elements. The elements
36362 * are taken from either the beginning or the end of the source array, string or number, as specified by
36363 * the value and sign (positive or negative) of `limit`. If a number is used as input, it is
36364 * converted to a string.
36365 *
36366 * @param {Array|string|number} input Source array, string or number to be limited.
36367 * @param {string|number} limit The length of the returned array or string. If the `limit` number
36368 *     is positive, `limit` number of items from the beginning of the source array/string are copied.
36369 *     If the number is negative, `limit` number  of items from the end of the source array/string
36370 *     are copied. The `limit` will be trimmed if it exceeds `array.length`. If `limit` is undefined,
36371 *     the input will be returned unchanged.
36372 * @param {(string|number)=} begin Index at which to begin limitation. As a negative index, `begin`
36373 *     indicates an offset from the end of `input`. Defaults to `0`.
36374 * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array
36375 *     had less than `limit` elements.
36376 *
36377 * @example
36378   <example module="limitToExample">
36379     <file name="index.html">
36380       <script>
36381         angular.module('limitToExample', [])
36382           .controller('ExampleController', ['$scope', function($scope) {
36383             $scope.numbers = [1,2,3,4,5,6,7,8,9];
36384             $scope.letters = "abcdefghi";
36385             $scope.longNumber = 2345432342;
36386             $scope.numLimit = 3;
36387             $scope.letterLimit = 3;
36388             $scope.longNumberLimit = 3;
36389           }]);
36390       </script>
36391       <div ng-controller="ExampleController">
36392         <label>
36393            Limit {{numbers}} to:
36394            <input type="number" step="1" ng-model="numLimit">
36395         </label>
36396         <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
36397         <label>
36398            Limit {{letters}} to:
36399            <input type="number" step="1" ng-model="letterLimit">
36400         </label>
36401         <p>Output letters: {{ letters | limitTo:letterLimit }}</p>
36402         <label>
36403            Limit {{longNumber}} to:
36404            <input type="number" step="1" ng-model="longNumberLimit">
36405         </label>
36406         <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p>
36407       </div>
36408     </file>
36409     <file name="protractor.js" type="protractor">
36410       var numLimitInput = element(by.model('numLimit'));
36411       var letterLimitInput = element(by.model('letterLimit'));
36412       var longNumberLimitInput = element(by.model('longNumberLimit'));
36413       var limitedNumbers = element(by.binding('numbers | limitTo:numLimit'));
36414       var limitedLetters = element(by.binding('letters | limitTo:letterLimit'));
36415       var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit'));
36416
36417       it('should limit the number array to first three items', function() {
36418         expect(numLimitInput.getAttribute('value')).toBe('3');
36419         expect(letterLimitInput.getAttribute('value')).toBe('3');
36420         expect(longNumberLimitInput.getAttribute('value')).toBe('3');
36421         expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]');
36422         expect(limitedLetters.getText()).toEqual('Output letters: abc');
36423         expect(limitedLongNumber.getText()).toEqual('Output long number: 234');
36424       });
36425
36426       // There is a bug in safari and protractor that doesn't like the minus key
36427       // it('should update the output when -3 is entered', function() {
36428       //   numLimitInput.clear();
36429       //   numLimitInput.sendKeys('-3');
36430       //   letterLimitInput.clear();
36431       //   letterLimitInput.sendKeys('-3');
36432       //   longNumberLimitInput.clear();
36433       //   longNumberLimitInput.sendKeys('-3');
36434       //   expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
36435       //   expect(limitedLetters.getText()).toEqual('Output letters: ghi');
36436       //   expect(limitedLongNumber.getText()).toEqual('Output long number: 342');
36437       // });
36438
36439       it('should not exceed the maximum size of input array', function() {
36440         numLimitInput.clear();
36441         numLimitInput.sendKeys('100');
36442         letterLimitInput.clear();
36443         letterLimitInput.sendKeys('100');
36444         longNumberLimitInput.clear();
36445         longNumberLimitInput.sendKeys('100');
36446         expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]');
36447         expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi');
36448         expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342');
36449       });
36450     </file>
36451   </example>
36452*/
36453function limitToFilter() {
36454  return function(input, limit, begin) {
36455    if (Math.abs(Number(limit)) === Infinity) {
36456      limit = Number(limit);
36457    } else {
36458      limit = toInt(limit);
36459    }
36460    if (isNaN(limit)) return input;
36461
36462    if (isNumber(input)) input = input.toString();
36463    if (!isArray(input) && !isString(input)) return input;
36464
36465    begin = (!begin || isNaN(begin)) ? 0 : toInt(begin);
36466    begin = (begin < 0 && begin >= -input.length) ? input.length + begin : begin;
36467
36468    if (limit >= 0) {
36469      return input.slice(begin, begin + limit);
36470    } else {
36471      if (begin === 0) {
36472        return input.slice(limit, input.length);
36473      } else {
36474        return input.slice(Math.max(0, begin + limit), begin);
36475      }
36476    }
36477  };
36478}
36479
36480/**
36481 * @ngdoc filter
36482 * @name orderBy
36483 * @kind function
36484 *
36485 * @description
36486 * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
36487 * for strings and numerically for numbers. Note: if you notice numbers are not being sorted
36488 * as expected, make sure they are actually being saved as numbers and not strings.
36489 *
36490 * @param {Array} array The array to sort.
36491 * @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
36492 *    used by the comparator to determine the order of elements.
36493 *
36494 *    Can be one of:
36495 *
36496 *    - `function`: Getter function. The result of this function will be sorted using the
36497 *      `<`, `===`, `>` operator.
36498 *    - `string`: An Angular expression. The result of this expression is used to compare elements
36499 *      (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by
36500 *      3 first characters of a property called `name`). The result of a constant expression
36501 *      is interpreted as a property name to be used in comparisons (for example `"special name"`
36502 *      to sort object by the value of their `special name` property). An expression can be
36503 *      optionally prefixed with `+` or `-` to control ascending or descending sort order
36504 *      (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array
36505 *      element itself is used to compare where sorting.
36506 *    - `Array`: An array of function or string predicates. The first predicate in the array
36507 *      is used for sorting, but when two items are equivalent, the next predicate is used.
36508 *
36509 *    If the predicate is missing or empty then it defaults to `'+'`.
36510 *
36511 * @param {boolean=} reverse Reverse the order of the array.
36512 * @returns {Array} Sorted copy of the source array.
36513 *
36514 *
36515 * @example
36516 * The example below demonstrates a simple ngRepeat, where the data is sorted
36517 * by age in descending order (predicate is set to `'-age'`).
36518 * `reverse` is not set, which means it defaults to `false`.
36519   <example module="orderByExample">
36520     <file name="index.html">
36521       <script>
36522         angular.module('orderByExample', [])
36523           .controller('ExampleController', ['$scope', function($scope) {
36524             $scope.friends =
36525                 [{name:'John', phone:'555-1212', age:10},
36526                  {name:'Mary', phone:'555-9876', age:19},
36527                  {name:'Mike', phone:'555-4321', age:21},
36528                  {name:'Adam', phone:'555-5678', age:35},
36529                  {name:'Julie', phone:'555-8765', age:29}];
36530           }]);
36531       </script>
36532       <div ng-controller="ExampleController">
36533         <table class="friend">
36534           <tr>
36535             <th>Name</th>
36536             <th>Phone Number</th>
36537             <th>Age</th>
36538           </tr>
36539           <tr ng-repeat="friend in friends | orderBy:'-age'">
36540             <td>{{friend.name}}</td>
36541             <td>{{friend.phone}}</td>
36542             <td>{{friend.age}}</td>
36543           </tr>
36544         </table>
36545       </div>
36546     </file>
36547   </example>
36548 *
36549 * The predicate and reverse parameters can be controlled dynamically through scope properties,
36550 * as shown in the next example.
36551 * @example
36552   <example module="orderByExample">
36553     <file name="index.html">
36554       <script>
36555         angular.module('orderByExample', [])
36556           .controller('ExampleController', ['$scope', function($scope) {
36557             $scope.friends =
36558                 [{name:'John', phone:'555-1212', age:10},
36559                  {name:'Mary', phone:'555-9876', age:19},
36560                  {name:'Mike', phone:'555-4321', age:21},
36561                  {name:'Adam', phone:'555-5678', age:35},
36562                  {name:'Julie', phone:'555-8765', age:29}];
36563             $scope.predicate = 'age';
36564             $scope.reverse = true;
36565             $scope.order = function(predicate) {
36566               $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
36567               $scope.predicate = predicate;
36568             };
36569           }]);
36570       </script>
36571       <style type="text/css">
36572         .sortorder:after {
36573           content: '\25b2';
36574         }
36575         .sortorder.reverse:after {
36576           content: '\25bc';
36577         }
36578       </style>
36579       <div ng-controller="ExampleController">
36580         <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
36581         <hr/>
36582         [ <a href="" ng-click="predicate=''">unsorted</a> ]
36583         <table class="friend">
36584           <tr>
36585             <th>
36586               <a href="" ng-click="order('name')">Name</a>
36587               <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
36588             </th>
36589             <th>
36590               <a href="" ng-click="order('phone')">Phone Number</a>
36591               <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
36592             </th>
36593             <th>
36594               <a href="" ng-click="order('age')">Age</a>
36595               <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
36596             </th>
36597           </tr>
36598           <tr ng-repeat="friend in friends | orderBy:predicate:reverse">
36599             <td>{{friend.name}}</td>
36600             <td>{{friend.phone}}</td>
36601             <td>{{friend.age}}</td>
36602           </tr>
36603         </table>
36604       </div>
36605     </file>
36606   </example>
36607 *
36608 * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the
36609 * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the
36610 * desired parameters.
36611 *
36612 * Example:
36613 *
36614 * @example
36615  <example module="orderByExample">
36616    <file name="index.html">
36617      <div ng-controller="ExampleController">
36618        <table class="friend">
36619          <tr>
36620            <th><a href="" ng-click="reverse=false;order('name', false)">Name</a>
36621              (<a href="" ng-click="order('-name',false)">^</a>)</th>
36622            <th><a href="" ng-click="reverse=!reverse;order('phone', reverse)">Phone Number</a></th>
36623            <th><a href="" ng-click="reverse=!reverse;order('age',reverse)">Age</a></th>
36624          </tr>
36625          <tr ng-repeat="friend in friends">
36626            <td>{{friend.name}}</td>
36627            <td>{{friend.phone}}</td>
36628            <td>{{friend.age}}</td>
36629          </tr>
36630        </table>
36631      </div>
36632    </file>
36633
36634    <file name="script.js">
36635      angular.module('orderByExample', [])
36636        .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) {
36637          var orderBy = $filter('orderBy');
36638          $scope.friends = [
36639            { name: 'John',    phone: '555-1212',    age: 10 },
36640            { name: 'Mary',    phone: '555-9876',    age: 19 },
36641            { name: 'Mike',    phone: '555-4321',    age: 21 },
36642            { name: 'Adam',    phone: '555-5678',    age: 35 },
36643            { name: 'Julie',   phone: '555-8765',    age: 29 }
36644          ];
36645          $scope.order = function(predicate, reverse) {
36646            $scope.friends = orderBy($scope.friends, predicate, reverse);
36647          };
36648          $scope.order('-age',false);
36649        }]);
36650    </file>
36651</example>
36652 */
36653orderByFilter.$inject = ['$parse'];
36654function orderByFilter($parse) {
36655  return function(array, sortPredicate, reverseOrder) {
36656
36657    if (!(isArrayLike(array))) return array;
36658
36659    if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; }
36660    if (sortPredicate.length === 0) { sortPredicate = ['+']; }
36661
36662    var predicates = processPredicates(sortPredicate, reverseOrder);
36663    // Add a predicate at the end that evaluates to the element index. This makes the
36664    // sort stable as it works as a tie-breaker when all the input predicates cannot
36665    // distinguish between two elements.
36666    predicates.push({ get: function() { return {}; }, descending: reverseOrder ? -1 : 1});
36667
36668    // The next three lines are a version of a Swartzian Transform idiom from Perl
36669    // (sometimes called the Decorate-Sort-Undecorate idiom)
36670    // See https://en.wikipedia.org/wiki/Schwartzian_transform
36671    var compareValues = Array.prototype.map.call(array, getComparisonObject);
36672    compareValues.sort(doComparison);
36673    array = compareValues.map(function(item) { return item.value; });
36674
36675    return array;
36676
36677    function getComparisonObject(value, index) {
36678      return {
36679        value: value,
36680        predicateValues: predicates.map(function(predicate) {
36681          return getPredicateValue(predicate.get(value), index);
36682        })
36683      };
36684    }
36685
36686    function doComparison(v1, v2) {
36687      var result = 0;
36688      for (var index=0, length = predicates.length; index < length; ++index) {
36689        result = compare(v1.predicateValues[index], v2.predicateValues[index]) * predicates[index].descending;
36690        if (result) break;
36691      }
36692      return result;
36693    }
36694  };
36695
36696  function processPredicates(sortPredicate, reverseOrder) {
36697    reverseOrder = reverseOrder ? -1 : 1;
36698    return sortPredicate.map(function(predicate) {
36699      var descending = 1, get = identity;
36700
36701      if (isFunction(predicate)) {
36702        get = predicate;
36703      } else if (isString(predicate)) {
36704        if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
36705          descending = predicate.charAt(0) == '-' ? -1 : 1;
36706          predicate = predicate.substring(1);
36707        }
36708        if (predicate !== '') {
36709          get = $parse(predicate);
36710          if (get.constant) {
36711            var key = get();
36712            get = function(value) { return value[key]; };
36713          }
36714        }
36715      }
36716      return { get: get, descending: descending * reverseOrder };
36717    });
36718  }
36719
36720  function isPrimitive(value) {
36721    switch (typeof value) {
36722      case 'number': /* falls through */
36723      case 'boolean': /* falls through */
36724      case 'string':
36725        return true;
36726      default:
36727        return false;
36728    }
36729  }
36730
36731  function objectValue(value, index) {
36732    // If `valueOf` is a valid function use that
36733    if (typeof value.valueOf === 'function') {
36734      value = value.valueOf();
36735      if (isPrimitive(value)) return value;
36736    }
36737    // If `toString` is a valid function and not the one from `Object.prototype` use that
36738    if (hasCustomToString(value)) {
36739      value = value.toString();
36740      if (isPrimitive(value)) return value;
36741    }
36742    // We have a basic object so we use the position of the object in the collection
36743    return index;
36744  }
36745
36746  function getPredicateValue(value, index) {
36747    var type = typeof value;
36748    if (value === null) {
36749      type = 'string';
36750      value = 'null';
36751    } else if (type === 'string') {
36752      value = value.toLowerCase();
36753    } else if (type === 'object') {
36754      value = objectValue(value, index);
36755    }
36756    return { value: value, type: type };
36757  }
36758
36759  function compare(v1, v2) {
36760    var result = 0;
36761    if (v1.type === v2.type) {
36762      if (v1.value !== v2.value) {
36763        result = v1.value < v2.value ? -1 : 1;
36764      }
36765    } else {
36766      result = v1.type < v2.type ? -1 : 1;
36767    }
36768    return result;
36769  }
36770}
36771
36772function ngDirective(directive) {
36773  if (isFunction(directive)) {
36774    directive = {
36775      link: directive
36776    };
36777  }
36778  directive.restrict = directive.restrict || 'AC';
36779  return valueFn(directive);
36780}
36781
36782/**
36783 * @ngdoc directive
36784 * @name a
36785 * @restrict E
36786 *
36787 * @description
36788 * Modifies the default behavior of the html A tag so that the default action is prevented when
36789 * the href attribute is empty.
36790 *
36791 * This change permits the easy creation of action links with the `ngClick` directive
36792 * without changing the location or causing page reloads, e.g.:
36793 * `<a href="" ng-click="list.addItem()">Add Item</a>`
36794 */
36795var htmlAnchorDirective = valueFn({
36796  restrict: 'E',
36797  compile: function(element, attr) {
36798    if (!attr.href && !attr.xlinkHref) {
36799      return function(scope, element) {
36800        // If the linked element is not an anchor tag anymore, do nothing
36801        if (element[0].nodeName.toLowerCase() !== 'a') return;
36802
36803        // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
36804        var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
36805                   'xlink:href' : 'href';
36806        element.on('click', function(event) {
36807          // if we have no href url, then don't navigate anywhere.
36808          if (!element.attr(href)) {
36809            event.preventDefault();
36810          }
36811        });
36812      };
36813    }
36814  }
36815});
36816
36817/**
36818 * @ngdoc directive
36819 * @name ngHref
36820 * @restrict A
36821 * @priority 99
36822 *
36823 * @description
36824 * Using Angular markup like `{{hash}}` in an href attribute will
36825 * make the link go to the wrong URL if the user clicks it before
36826 * Angular has a chance to replace the `{{hash}}` markup with its
36827 * value. Until Angular replaces the markup the link will be broken
36828 * and will most likely return a 404 error. The `ngHref` directive
36829 * solves this problem.
36830 *
36831 * The wrong way to write it:
36832 * ```html
36833 * <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
36834 * ```
36835 *
36836 * The correct way to write it:
36837 * ```html
36838 * <a ng-href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
36839 * ```
36840 *
36841 * @element A
36842 * @param {template} ngHref any string which can contain `{{}}` markup.
36843 *
36844 * @example
36845 * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes
36846 * in links and their different behaviors:
36847    <example>
36848      <file name="index.html">
36849        <input ng-model="value" /><br />
36850        <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
36851        <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
36852        <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br />
36853        <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
36854        <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
36855        <a id="link-6" ng-href="{{value}}">link</a> (link, change location)
36856      </file>
36857      <file name="protractor.js" type="protractor">
36858        it('should execute ng-click but not reload when href without value', function() {
36859          element(by.id('link-1')).click();
36860          expect(element(by.model('value')).getAttribute('value')).toEqual('1');
36861          expect(element(by.id('link-1')).getAttribute('href')).toBe('');
36862        });
36863
36864        it('should execute ng-click but not reload when href empty string', function() {
36865          element(by.id('link-2')).click();
36866          expect(element(by.model('value')).getAttribute('value')).toEqual('2');
36867          expect(element(by.id('link-2')).getAttribute('href')).toBe('');
36868        });
36869
36870        it('should execute ng-click and change url when ng-href specified', function() {
36871          expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
36872
36873          element(by.id('link-3')).click();
36874
36875          // At this point, we navigate away from an Angular page, so we need
36876          // to use browser.driver to get the base webdriver.
36877
36878          browser.wait(function() {
36879            return browser.driver.getCurrentUrl().then(function(url) {
36880              return url.match(/\/123$/);
36881            });
36882          }, 5000, 'page should navigate to /123');
36883        });
36884
36885        it('should execute ng-click but not reload when href empty string and name specified', function() {
36886          element(by.id('link-4')).click();
36887          expect(element(by.model('value')).getAttribute('value')).toEqual('4');
36888          expect(element(by.id('link-4')).getAttribute('href')).toBe('');
36889        });
36890
36891        it('should execute ng-click but not reload when no href but name specified', function() {
36892          element(by.id('link-5')).click();
36893          expect(element(by.model('value')).getAttribute('value')).toEqual('5');
36894          expect(element(by.id('link-5')).getAttribute('href')).toBe(null);
36895        });
36896
36897        it('should only change url when only ng-href', function() {
36898          element(by.model('value')).clear();
36899          element(by.model('value')).sendKeys('6');
36900          expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
36901
36902          element(by.id('link-6')).click();
36903
36904          // At this point, we navigate away from an Angular page, so we need
36905          // to use browser.driver to get the base webdriver.
36906          browser.wait(function() {
36907            return browser.driver.getCurrentUrl().then(function(url) {
36908              return url.match(/\/6$/);
36909            });
36910          }, 5000, 'page should navigate to /6');
36911        });
36912      </file>
36913    </example>
36914 */
36915
36916/**
36917 * @ngdoc directive
36918 * @name ngSrc
36919 * @restrict A
36920 * @priority 99
36921 *
36922 * @description
36923 * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
36924 * work right: The browser will fetch from the URL with the literal
36925 * text `{{hash}}` until Angular replaces the expression inside
36926 * `{{hash}}`. The `ngSrc` directive solves this problem.
36927 *
36928 * The buggy way to write it:
36929 * ```html
36930 * <img src="http://www.gravatar.com/avatar/{{hash}}" alt="Description"/>
36931 * ```
36932 *
36933 * The correct way to write it:
36934 * ```html
36935 * <img ng-src="http://www.gravatar.com/avatar/{{hash}}" alt="Description" />
36936 * ```
36937 *
36938 * @element IMG
36939 * @param {template} ngSrc any string which can contain `{{}}` markup.
36940 */
36941
36942/**
36943 * @ngdoc directive
36944 * @name ngSrcset
36945 * @restrict A
36946 * @priority 99
36947 *
36948 * @description
36949 * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
36950 * work right: The browser will fetch from the URL with the literal
36951 * text `{{hash}}` until Angular replaces the expression inside
36952 * `{{hash}}`. The `ngSrcset` directive solves this problem.
36953 *
36954 * The buggy way to write it:
36955 * ```html
36956 * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description"/>
36957 * ```
36958 *
36959 * The correct way to write it:
36960 * ```html
36961 * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description" />
36962 * ```
36963 *
36964 * @element IMG
36965 * @param {template} ngSrcset any string which can contain `{{}}` markup.
36966 */
36967
36968/**
36969 * @ngdoc directive
36970 * @name ngDisabled
36971 * @restrict A
36972 * @priority 100
36973 *
36974 * @description
36975 *
36976 * This directive sets the `disabled` attribute on the element if the
36977 * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy.
36978 *
36979 * A special directive is necessary because we cannot use interpolation inside the `disabled`
36980 * attribute.  The following example would make the button enabled on Chrome/Firefox
36981 * but not on older IEs:
36982 *
36983 * ```html
36984 * <!-- See below for an example of ng-disabled being used correctly -->
36985 * <div ng-init="isDisabled = false">
36986 *  <button disabled="{{isDisabled}}">Disabled</button>
36987 * </div>
36988 * ```
36989 *
36990 * This is because the HTML specification does not require browsers to preserve the values of
36991 * boolean attributes such as `disabled` (Their presence means true and their absence means false.)
36992 * If we put an Angular interpolation expression into such an attribute then the
36993 * binding information would be lost when the browser removes the attribute.
36994 *
36995 * @example
36996    <example>
36997      <file name="index.html">
36998        <label>Click me to toggle: <input type="checkbox" ng-model="checked"></label><br/>
36999        <button ng-model="button" ng-disabled="checked">Button</button>
37000      </file>
37001      <file name="protractor.js" type="protractor">
37002        it('should toggle button', function() {
37003          expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy();
37004          element(by.model('checked')).click();
37005          expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy();
37006        });
37007      </file>
37008    </example>
37009 *
37010 * @element INPUT
37011 * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
37012 *     then the `disabled` attribute will be set on the element
37013 */
37014
37015
37016/**
37017 * @ngdoc directive
37018 * @name ngChecked
37019 * @restrict A
37020 * @priority 100
37021 *
37022 * @description
37023 * Sets the `checked` attribute on the element, if the expression inside `ngChecked` is truthy.
37024 *
37025 * Note that this directive should not be used together with {@link ngModel `ngModel`},
37026 * as this can lead to unexpected behavior.
37027 *
37028 * ### Why do we need `ngChecked`?
37029 *
37030 * The HTML specification does not require browsers to preserve the values of boolean attributes
37031 * such as checked. (Their presence means true and their absence means false.)
37032 * If we put an Angular interpolation expression into such an attribute then the
37033 * binding information would be lost when the browser removes the attribute.
37034 * The `ngChecked` directive solves this problem for the `checked` attribute.
37035 * This complementary directive is not removed by the browser and so provides
37036 * a permanent reliable place to store the binding information.
37037 * @example
37038    <example>
37039      <file name="index.html">
37040        <label>Check me to check both: <input type="checkbox" ng-model="master"></label><br/>
37041        <input id="checkSlave" type="checkbox" ng-checked="master" aria-label="Slave input">
37042      </file>
37043      <file name="protractor.js" type="protractor">
37044        it('should check both checkBoxes', function() {
37045          expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
37046          element(by.model('master')).click();
37047          expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
37048        });
37049      </file>
37050    </example>
37051 *
37052 * @element INPUT
37053 * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
37054 *     then the `checked` attribute will be set on the element
37055 */
37056
37057
37058/**
37059 * @ngdoc directive
37060 * @name ngReadonly
37061 * @restrict A
37062 * @priority 100
37063 *
37064 * @description
37065 * The HTML specification does not require browsers to preserve the values of boolean attributes
37066 * such as readonly. (Their presence means true and their absence means false.)
37067 * If we put an Angular interpolation expression into such an attribute then the
37068 * binding information would be lost when the browser removes the attribute.
37069 * The `ngReadonly` directive solves this problem for the `readonly` attribute.
37070 * This complementary directive is not removed by the browser and so provides
37071 * a permanent reliable place to store the binding information.
37072 * @example
37073    <example>
37074      <file name="index.html">
37075        <label>Check me to make text readonly: <input type="checkbox" ng-model="checked"></label><br/>
37076        <input type="text" ng-readonly="checked" value="I'm Angular" aria-label="Readonly field" />
37077      </file>
37078      <file name="protractor.js" type="protractor">
37079        it('should toggle readonly attr', function() {
37080          expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy();
37081          element(by.model('checked')).click();
37082          expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy();
37083        });
37084      </file>
37085    </example>
37086 *
37087 * @element INPUT
37088 * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
37089 *     then special attribute "readonly" will be set on the element
37090 */
37091
37092
37093/**
37094 * @ngdoc directive
37095 * @name ngSelected
37096 * @restrict A
37097 * @priority 100
37098 *
37099 * @description
37100 * The HTML specification does not require browsers to preserve the values of boolean attributes
37101 * such as selected. (Their presence means true and their absence means false.)
37102 * If we put an Angular interpolation expression into such an attribute then the
37103 * binding information would be lost when the browser removes the attribute.
37104 * The `ngSelected` directive solves this problem for the `selected` attribute.
37105 * This complementary directive is not removed by the browser and so provides
37106 * a permanent reliable place to store the binding information.
37107 *
37108 * @example
37109    <example>
37110      <file name="index.html">
37111        <label>Check me to select: <input type="checkbox" ng-model="selected"></label><br/>
37112        <select aria-label="ngSelected demo">
37113          <option>Hello!</option>
37114          <option id="greet" ng-selected="selected">Greetings!</option>
37115        </select>
37116      </file>
37117      <file name="protractor.js" type="protractor">
37118        it('should select Greetings!', function() {
37119          expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
37120          element(by.model('selected')).click();
37121          expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
37122        });
37123      </file>
37124    </example>
37125 *
37126 * @element OPTION
37127 * @param {expression} ngSelected If the {@link guide/expression expression} is truthy,
37128 *     then special attribute "selected" will be set on the element
37129 */
37130
37131/**
37132 * @ngdoc directive
37133 * @name ngOpen
37134 * @restrict A
37135 * @priority 100
37136 *
37137 * @description
37138 * The HTML specification does not require browsers to preserve the values of boolean attributes
37139 * such as open. (Their presence means true and their absence means false.)
37140 * If we put an Angular interpolation expression into such an attribute then the
37141 * binding information would be lost when the browser removes the attribute.
37142 * The `ngOpen` directive solves this problem for the `open` attribute.
37143 * This complementary directive is not removed by the browser and so provides
37144 * a permanent reliable place to store the binding information.
37145 * @example
37146     <example>
37147       <file name="index.html">
37148         <label>Check me check multiple: <input type="checkbox" ng-model="open"></label><br/>
37149         <details id="details" ng-open="open">
37150            <summary>Show/Hide me</summary>
37151         </details>
37152       </file>
37153       <file name="protractor.js" type="protractor">
37154         it('should toggle open', function() {
37155           expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
37156           element(by.model('open')).click();
37157           expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
37158         });
37159       </file>
37160     </example>
37161 *
37162 * @element DETAILS
37163 * @param {expression} ngOpen If the {@link guide/expression expression} is truthy,
37164 *     then special attribute "open" will be set on the element
37165 */
37166
37167var ngAttributeAliasDirectives = {};
37168
37169// boolean attrs are evaluated
37170forEach(BOOLEAN_ATTR, function(propName, attrName) {
37171  // binding to multiple is not supported
37172  if (propName == "multiple") return;
37173
37174  function defaultLinkFn(scope, element, attr) {
37175    scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
37176      attr.$set(attrName, !!value);
37177    });
37178  }
37179
37180  var normalized = directiveNormalize('ng-' + attrName);
37181  var linkFn = defaultLinkFn;
37182
37183  if (propName === 'checked') {
37184    linkFn = function(scope, element, attr) {
37185      // ensuring ngChecked doesn't interfere with ngModel when both are set on the same input
37186      if (attr.ngModel !== attr[normalized]) {
37187        defaultLinkFn(scope, element, attr);
37188      }
37189    };
37190  }
37191
37192  ngAttributeAliasDirectives[normalized] = function() {
37193    return {
37194      restrict: 'A',
37195      priority: 100,
37196      link: linkFn
37197    };
37198  };
37199});
37200
37201// aliased input attrs are evaluated
37202forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) {
37203  ngAttributeAliasDirectives[ngAttr] = function() {
37204    return {
37205      priority: 100,
37206      link: function(scope, element, attr) {
37207        //special case ngPattern when a literal regular expression value
37208        //is used as the expression (this way we don't have to watch anything).
37209        if (ngAttr === "ngPattern" && attr.ngPattern.charAt(0) == "/") {
37210          var match = attr.ngPattern.match(REGEX_STRING_REGEXP);
37211          if (match) {
37212            attr.$set("ngPattern", new RegExp(match[1], match[2]));
37213            return;
37214          }
37215        }
37216
37217        scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) {
37218          attr.$set(ngAttr, value);
37219        });
37220      }
37221    };
37222  };
37223});
37224
37225// ng-src, ng-srcset, ng-href are interpolated
37226forEach(['src', 'srcset', 'href'], function(attrName) {
37227  var normalized = directiveNormalize('ng-' + attrName);
37228  ngAttributeAliasDirectives[normalized] = function() {
37229    return {
37230      priority: 99, // it needs to run after the attributes are interpolated
37231      link: function(scope, element, attr) {
37232        var propName = attrName,
37233            name = attrName;
37234
37235        if (attrName === 'href' &&
37236            toString.call(element.prop('href')) === '[object SVGAnimatedString]') {
37237          name = 'xlinkHref';
37238          attr.$attr[name] = 'xlink:href';
37239          propName = null;
37240        }
37241
37242        attr.$observe(normalized, function(value) {
37243          if (!value) {
37244            if (attrName === 'href') {
37245              attr.$set(name, null);
37246            }
37247            return;
37248          }
37249
37250          attr.$set(name, value);
37251
37252          // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
37253          // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
37254          // to set the property as well to achieve the desired effect.
37255          // we use attr[attrName] value since $set can sanitize the url.
37256          if (msie && propName) element.prop(propName, attr[name]);
37257        });
37258      }
37259    };
37260  };
37261});
37262
37263/* global -nullFormCtrl, -SUBMITTED_CLASS, addSetValidityMethod: true
37264 */
37265var nullFormCtrl = {
37266  $addControl: noop,
37267  $$renameControl: nullFormRenameControl,
37268  $removeControl: noop,
37269  $setValidity: noop,
37270  $setDirty: noop,
37271  $setPristine: noop,
37272  $setSubmitted: noop
37273},
37274SUBMITTED_CLASS = 'ng-submitted';
37275
37276function nullFormRenameControl(control, name) {
37277  control.$name = name;
37278}
37279
37280/**
37281 * @ngdoc type
37282 * @name form.FormController
37283 *
37284 * @property {boolean} $pristine True if user has not interacted with the form yet.
37285 * @property {boolean} $dirty True if user has already interacted with the form.
37286 * @property {boolean} $valid True if all of the containing forms and controls are valid.
37287 * @property {boolean} $invalid True if at least one containing control or form is invalid.
37288 * @property {boolean} $pending True if at least one containing control or form is pending.
37289 * @property {boolean} $submitted True if user has submitted the form even if its invalid.
37290 *
37291 * @property {Object} $error Is an object hash, containing references to controls or
37292 *  forms with failing validators, where:
37293 *
37294 *  - keys are validation tokens (error names),
37295 *  - values are arrays of controls or forms that have a failing validator for given error name.
37296 *
37297 *  Built-in validation tokens:
37298 *
37299 *  - `email`
37300 *  - `max`
37301 *  - `maxlength`
37302 *  - `min`
37303 *  - `minlength`
37304 *  - `number`
37305 *  - `pattern`
37306 *  - `required`
37307 *  - `url`
37308 *  - `date`
37309 *  - `datetimelocal`
37310 *  - `time`
37311 *  - `week`
37312 *  - `month`
37313 *
37314 * @description
37315 * `FormController` keeps track of all its controls and nested forms as well as the state of them,
37316 * such as being valid/invalid or dirty/pristine.
37317 *
37318 * Each {@link ng.directive:form form} directive creates an instance
37319 * of `FormController`.
37320 *
37321 */
37322//asks for $scope to fool the BC controller module
37323FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate'];
37324function FormController(element, attrs, $scope, $animate, $interpolate) {
37325  var form = this,
37326      controls = [];
37327
37328  // init state
37329  form.$error = {};
37330  form.$$success = {};
37331  form.$pending = undefined;
37332  form.$name = $interpolate(attrs.name || attrs.ngForm || '')($scope);
37333  form.$dirty = false;
37334  form.$pristine = true;
37335  form.$valid = true;
37336  form.$invalid = false;
37337  form.$submitted = false;
37338  form.$$parentForm = nullFormCtrl;
37339
37340  /**
37341   * @ngdoc method
37342   * @name form.FormController#$rollbackViewValue
37343   *
37344   * @description
37345   * Rollback all form controls pending updates to the `$modelValue`.
37346   *
37347   * Updates may be pending by a debounced event or because the input is waiting for a some future
37348   * event defined in `ng-model-options`. This method is typically needed by the reset button of
37349   * a form that uses `ng-model-options` to pend updates.
37350   */
37351  form.$rollbackViewValue = function() {
37352    forEach(controls, function(control) {
37353      control.$rollbackViewValue();
37354    });
37355  };
37356
37357  /**
37358   * @ngdoc method
37359   * @name form.FormController#$commitViewValue
37360   *
37361   * @description
37362   * Commit all form controls pending updates to the `$modelValue`.
37363   *
37364   * Updates may be pending by a debounced event or because the input is waiting for a some future
37365   * event defined in `ng-model-options`. This method is rarely needed as `NgModelController`
37366   * usually handles calling this in response to input events.
37367   */
37368  form.$commitViewValue = function() {
37369    forEach(controls, function(control) {
37370      control.$commitViewValue();
37371    });
37372  };
37373
37374  /**
37375   * @ngdoc method
37376   * @name form.FormController#$addControl
37377   * @param {object} control control object, either a {@link form.FormController} or an
37378   * {@link ngModel.NgModelController}
37379   *
37380   * @description
37381   * Register a control with the form. Input elements using ngModelController do this automatically
37382   * when they are linked.
37383   *
37384   * Note that the current state of the control will not be reflected on the new parent form. This
37385   * is not an issue with normal use, as freshly compiled and linked controls are in a `$pristine`
37386   * state.
37387   *
37388   * However, if the method is used programmatically, for example by adding dynamically created controls,
37389   * or controls that have been previously removed without destroying their corresponding DOM element,
37390   * it's the developers responsiblity to make sure the current state propagates to the parent form.
37391   *
37392   * For example, if an input control is added that is already `$dirty` and has `$error` properties,
37393   * calling `$setDirty()` and `$validate()` afterwards will propagate the state to the parent form.
37394   */
37395  form.$addControl = function(control) {
37396    // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
37397    // and not added to the scope.  Now we throw an error.
37398    assertNotHasOwnProperty(control.$name, 'input');
37399    controls.push(control);
37400
37401    if (control.$name) {
37402      form[control.$name] = control;
37403    }
37404
37405    control.$$parentForm = form;
37406  };
37407
37408  // Private API: rename a form control
37409  form.$$renameControl = function(control, newName) {
37410    var oldName = control.$name;
37411
37412    if (form[oldName] === control) {
37413      delete form[oldName];
37414    }
37415    form[newName] = control;
37416    control.$name = newName;
37417  };
37418
37419  /**
37420   * @ngdoc method
37421   * @name form.FormController#$removeControl
37422   * @param {object} control control object, either a {@link form.FormController} or an
37423   * {@link ngModel.NgModelController}
37424   *
37425   * @description
37426   * Deregister a control from the form.
37427   *
37428   * Input elements using ngModelController do this automatically when they are destroyed.
37429   *
37430   * Note that only the removed control's validation state (`$errors`etc.) will be removed from the
37431   * form. `$dirty`, `$submitted` states will not be changed, because the expected behavior can be
37432   * different from case to case. For example, removing the only `$dirty` control from a form may or
37433   * may not mean that the form is still `$dirty`.
37434   */
37435  form.$removeControl = function(control) {
37436    if (control.$name && form[control.$name] === control) {
37437      delete form[control.$name];
37438    }
37439    forEach(form.$pending, function(value, name) {
37440      form.$setValidity(name, null, control);
37441    });
37442    forEach(form.$error, function(value, name) {
37443      form.$setValidity(name, null, control);
37444    });
37445    forEach(form.$$success, function(value, name) {
37446      form.$setValidity(name, null, control);
37447    });
37448
37449    arrayRemove(controls, control);
37450    control.$$parentForm = nullFormCtrl;
37451  };
37452
37453
37454  /**
37455   * @ngdoc method
37456   * @name form.FormController#$setValidity
37457   *
37458   * @description
37459   * Sets the validity of a form control.
37460   *
37461   * This method will also propagate to parent forms.
37462   */
37463  addSetValidityMethod({
37464    ctrl: this,
37465    $element: element,
37466    set: function(object, property, controller) {
37467      var list = object[property];
37468      if (!list) {
37469        object[property] = [controller];
37470      } else {
37471        var index = list.indexOf(controller);
37472        if (index === -1) {
37473          list.push(controller);
37474        }
37475      }
37476    },
37477    unset: function(object, property, controller) {
37478      var list = object[property];
37479      if (!list) {
37480        return;
37481      }
37482      arrayRemove(list, controller);
37483      if (list.length === 0) {
37484        delete object[property];
37485      }
37486    },
37487    $animate: $animate
37488  });
37489
37490  /**
37491   * @ngdoc method
37492   * @name form.FormController#$setDirty
37493   *
37494   * @description
37495   * Sets the form to a dirty state.
37496   *
37497   * This method can be called to add the 'ng-dirty' class and set the form to a dirty
37498   * state (ng-dirty class). This method will also propagate to parent forms.
37499   */
37500  form.$setDirty = function() {
37501    $animate.removeClass(element, PRISTINE_CLASS);
37502    $animate.addClass(element, DIRTY_CLASS);
37503    form.$dirty = true;
37504    form.$pristine = false;
37505    form.$$parentForm.$setDirty();
37506  };
37507
37508  /**
37509   * @ngdoc method
37510   * @name form.FormController#$setPristine
37511   *
37512   * @description
37513   * Sets the form to its pristine state.
37514   *
37515   * This method can be called to remove the 'ng-dirty' class and set the form to its pristine
37516   * state (ng-pristine class). This method will also propagate to all the controls contained
37517   * in this form.
37518   *
37519   * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
37520   * saving or resetting it.
37521   */
37522  form.$setPristine = function() {
37523    $animate.setClass(element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS);
37524    form.$dirty = false;
37525    form.$pristine = true;
37526    form.$submitted = false;
37527    forEach(controls, function(control) {
37528      control.$setPristine();
37529    });
37530  };
37531
37532  /**
37533   * @ngdoc method
37534   * @name form.FormController#$setUntouched
37535   *
37536   * @description
37537   * Sets the form to its untouched state.
37538   *
37539   * This method can be called to remove the 'ng-touched' class and set the form controls to their
37540   * untouched state (ng-untouched class).
37541   *
37542   * Setting a form controls back to their untouched state is often useful when setting the form
37543   * back to its pristine state.
37544   */
37545  form.$setUntouched = function() {
37546    forEach(controls, function(control) {
37547      control.$setUntouched();
37548    });
37549  };
37550
37551  /**
37552   * @ngdoc method
37553   * @name form.FormController#$setSubmitted
37554   *
37555   * @description
37556   * Sets the form to its submitted state.
37557   */
37558  form.$setSubmitted = function() {
37559    $animate.addClass(element, SUBMITTED_CLASS);
37560    form.$submitted = true;
37561    form.$$parentForm.$setSubmitted();
37562  };
37563}
37564
37565/**
37566 * @ngdoc directive
37567 * @name ngForm
37568 * @restrict EAC
37569 *
37570 * @description
37571 * Nestable alias of {@link ng.directive:form `form`} directive. HTML
37572 * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
37573 * sub-group of controls needs to be determined.
37574 *
37575 * Note: the purpose of `ngForm` is to group controls,
37576 * but not to be a replacement for the `<form>` tag with all of its capabilities
37577 * (e.g. posting to the server, ...).
37578 *
37579 * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
37580 *                       related scope, under this name.
37581 *
37582 */
37583
37584 /**
37585 * @ngdoc directive
37586 * @name form
37587 * @restrict E
37588 *
37589 * @description
37590 * Directive that instantiates
37591 * {@link form.FormController FormController}.
37592 *
37593 * If the `name` attribute is specified, the form controller is published onto the current scope under
37594 * this name.
37595 *
37596 * # Alias: {@link ng.directive:ngForm `ngForm`}
37597 *
37598 * In Angular, forms can be nested. This means that the outer form is valid when all of the child
37599 * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
37600 * Angular provides the {@link ng.directive:ngForm `ngForm`} directive which behaves identically to
37601 * `<form>` but can be nested.  This allows you to have nested forms, which is very useful when
37602 * using Angular validation directives in forms that are dynamically generated using the
37603 * {@link ng.directive:ngRepeat `ngRepeat`} directive. Since you cannot dynamically generate the `name`
37604 * attribute of input elements using interpolation, you have to wrap each set of repeated inputs in an
37605 * `ngForm` directive and nest these in an outer `form` element.
37606 *
37607 *
37608 * # CSS classes
37609 *  - `ng-valid` is set if the form is valid.
37610 *  - `ng-invalid` is set if the form is invalid.
37611 *  - `ng-pending` is set if the form is pending.
37612 *  - `ng-pristine` is set if the form is pristine.
37613 *  - `ng-dirty` is set if the form is dirty.
37614 *  - `ng-submitted` is set if the form was submitted.
37615 *
37616 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
37617 *
37618 *
37619 * # Submitting a form and preventing the default action
37620 *
37621 * Since the role of forms in client-side Angular applications is different than in classical
37622 * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
37623 * page reload that sends the data to the server. Instead some javascript logic should be triggered
37624 * to handle the form submission in an application-specific way.
37625 *
37626 * For this reason, Angular prevents the default action (form submission to the server) unless the
37627 * `<form>` element has an `action` attribute specified.
37628 *
37629 * You can use one of the following two ways to specify what javascript method should be called when
37630 * a form is submitted:
37631 *
37632 * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
37633 * - {@link ng.directive:ngClick ngClick} directive on the first
37634  *  button or input field of type submit (input[type=submit])
37635 *
37636 * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
37637 * or {@link ng.directive:ngClick ngClick} directives.
37638 * This is because of the following form submission rules in the HTML specification:
37639 *
37640 * - If a form has only one input field then hitting enter in this field triggers form submit
37641 * (`ngSubmit`)
37642 * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
37643 * doesn't trigger submit
37644 * - if a form has one or more input fields and one or more buttons or input[type=submit] then
37645 * hitting enter in any of the input fields will trigger the click handler on the *first* button or
37646 * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
37647 *
37648 * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is
37649 * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
37650 * to have access to the updated model.
37651 *
37652 * ## Animation Hooks
37653 *
37654 * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
37655 * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
37656 * other validations that are performed within the form. Animations in ngForm are similar to how
37657 * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
37658 * as JS animations.
37659 *
37660 * The following example shows a simple way to utilize CSS transitions to style a form element
37661 * that has been rendered as invalid after it has been validated:
37662 *
37663 * <pre>
37664 * //be sure to include ngAnimate as a module to hook into more
37665 * //advanced animations
37666 * .my-form {
37667 *   transition:0.5s linear all;
37668 *   background: white;
37669 * }
37670 * .my-form.ng-invalid {
37671 *   background: red;
37672 *   color:white;
37673 * }
37674 * </pre>
37675 *
37676 * @example
37677    <example deps="angular-animate.js" animations="true" fixBase="true" module="formExample">
37678      <file name="index.html">
37679       <script>
37680         angular.module('formExample', [])
37681           .controller('FormController', ['$scope', function($scope) {
37682             $scope.userType = 'guest';
37683           }]);
37684       </script>
37685       <style>
37686        .my-form {
37687          transition:all linear 0.5s;
37688          background: transparent;
37689        }
37690        .my-form.ng-invalid {
37691          background: red;
37692        }
37693       </style>
37694       <form name="myForm" ng-controller="FormController" class="my-form">
37695         userType: <input name="input" ng-model="userType" required>
37696         <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
37697         <code>userType = {{userType}}</code><br>
37698         <code>myForm.input.$valid = {{myForm.input.$valid}}</code><br>
37699         <code>myForm.input.$error = {{myForm.input.$error}}</code><br>
37700         <code>myForm.$valid = {{myForm.$valid}}</code><br>
37701         <code>myForm.$error.required = {{!!myForm.$error.required}}</code><br>
37702        </form>
37703      </file>
37704      <file name="protractor.js" type="protractor">
37705        it('should initialize to model', function() {
37706          var userType = element(by.binding('userType'));
37707          var valid = element(by.binding('myForm.input.$valid'));
37708
37709          expect(userType.getText()).toContain('guest');
37710          expect(valid.getText()).toContain('true');
37711        });
37712
37713        it('should be invalid if empty', function() {
37714          var userType = element(by.binding('userType'));
37715          var valid = element(by.binding('myForm.input.$valid'));
37716          var userInput = element(by.model('userType'));
37717
37718          userInput.clear();
37719          userInput.sendKeys('');
37720
37721          expect(userType.getText()).toEqual('userType =');
37722          expect(valid.getText()).toContain('false');
37723        });
37724      </file>
37725    </example>
37726 *
37727 * @param {string=} name Name of the form. If specified, the form controller will be published into
37728 *                       related scope, under this name.
37729 */
37730var formDirectiveFactory = function(isNgForm) {
37731  return ['$timeout', '$parse', function($timeout, $parse) {
37732    var formDirective = {
37733      name: 'form',
37734      restrict: isNgForm ? 'EAC' : 'E',
37735      require: ['form', '^^?form'], //first is the form's own ctrl, second is an optional parent form
37736      controller: FormController,
37737      compile: function ngFormCompile(formElement, attr) {
37738        // Setup initial state of the control
37739        formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS);
37740
37741        var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false);
37742
37743        return {
37744          pre: function ngFormPreLink(scope, formElement, attr, ctrls) {
37745            var controller = ctrls[0];
37746
37747            // if `action` attr is not present on the form, prevent the default action (submission)
37748            if (!('action' in attr)) {
37749              // we can't use jq events because if a form is destroyed during submission the default
37750              // action is not prevented. see #1238
37751              //
37752              // IE 9 is not affected because it doesn't fire a submit event and try to do a full
37753              // page reload if the form was destroyed by submission of the form via a click handler
37754              // on a button in the form. Looks like an IE9 specific bug.
37755              var handleFormSubmission = function(event) {
37756                scope.$apply(function() {
37757                  controller.$commitViewValue();
37758                  controller.$setSubmitted();
37759                });
37760
37761                event.preventDefault();
37762              };
37763
37764              addEventListenerFn(formElement[0], 'submit', handleFormSubmission);
37765
37766              // unregister the preventDefault listener so that we don't not leak memory but in a
37767              // way that will achieve the prevention of the default action.
37768              formElement.on('$destroy', function() {
37769                $timeout(function() {
37770                  removeEventListenerFn(formElement[0], 'submit', handleFormSubmission);
37771                }, 0, false);
37772              });
37773            }
37774
37775            var parentFormCtrl = ctrls[1] || controller.$$parentForm;
37776            parentFormCtrl.$addControl(controller);
37777
37778            var setter = nameAttr ? getSetter(controller.$name) : noop;
37779
37780            if (nameAttr) {
37781              setter(scope, controller);
37782              attr.$observe(nameAttr, function(newValue) {
37783                if (controller.$name === newValue) return;
37784                setter(scope, undefined);
37785                controller.$$parentForm.$$renameControl(controller, newValue);
37786                setter = getSetter(controller.$name);
37787                setter(scope, controller);
37788              });
37789            }
37790            formElement.on('$destroy', function() {
37791              controller.$$parentForm.$removeControl(controller);
37792              setter(scope, undefined);
37793              extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
37794            });
37795          }
37796        };
37797      }
37798    };
37799
37800    return formDirective;
37801
37802    function getSetter(expression) {
37803      if (expression === '') {
37804        //create an assignable expression, so forms with an empty name can be renamed later
37805        return $parse('this[""]').assign;
37806      }
37807      return $parse(expression).assign || noop;
37808    }
37809  }];
37810};
37811
37812var formDirective = formDirectiveFactory();
37813var ngFormDirective = formDirectiveFactory(true);
37814
37815/* global VALID_CLASS: false,
37816  INVALID_CLASS: false,
37817  PRISTINE_CLASS: false,
37818  DIRTY_CLASS: false,
37819  UNTOUCHED_CLASS: false,
37820  TOUCHED_CLASS: false,
37821  ngModelMinErr: false,
37822*/
37823
37824// Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
37825var 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)/;
37826var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/;
37827var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
37828var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
37829var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/;
37830var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
37831var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/;
37832var MONTH_REGEXP = /^(\d{4})-(\d\d)$/;
37833var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
37834
37835var inputType = {
37836
37837  /**
37838   * @ngdoc input
37839   * @name input[text]
37840   *
37841   * @description
37842   * Standard HTML text input with angular data binding, inherited by most of the `input` elements.
37843   *
37844   *
37845   * @param {string} ngModel Assignable angular expression to data-bind to.
37846   * @param {string=} name Property name of the form under which the control is published.
37847   * @param {string=} required Adds `required` validation error key if the value is not entered.
37848   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
37849   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
37850   *    `required` when you want to data-bind to the `required` attribute.
37851   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
37852   *    minlength.
37853   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
37854   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
37855   *    any length.
37856   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
37857   *    that contains the regular expression body that will be converted to a regular expression
37858   *    as in the ngPattern directive.
37859   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
37860   *    a RegExp found by evaluating the Angular expression given in the attribute value.
37861   *    If the expression evaluates to a RegExp object, then this is used directly.
37862   *    If the expression evaluates to a string, then it will be converted to a RegExp
37863   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
37864   *    `new RegExp('^abc$')`.<br />
37865   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
37866   *    start at the index of the last search's match, thus not taking the whole input value into
37867   *    account.
37868   * @param {string=} ngChange Angular expression to be executed when input changes due to user
37869   *    interaction with the input element.
37870   * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
37871   *    This parameter is ignored for input[type=password] controls, which will never trim the
37872   *    input.
37873   *
37874   * @example
37875      <example name="text-input-directive" module="textInputExample">
37876        <file name="index.html">
37877         <script>
37878           angular.module('textInputExample', [])
37879             .controller('ExampleController', ['$scope', function($scope) {
37880               $scope.example = {
37881                 text: 'guest',
37882                 word: /^\s*\w*\s*$/
37883               };
37884             }]);
37885         </script>
37886         <form name="myForm" ng-controller="ExampleController">
37887           <label>Single word:
37888             <input type="text" name="input" ng-model="example.text"
37889                    ng-pattern="example.word" required ng-trim="false">
37890           </label>
37891           <div role="alert">
37892             <span class="error" ng-show="myForm.input.$error.required">
37893               Required!</span>
37894             <span class="error" ng-show="myForm.input.$error.pattern">
37895               Single word only!</span>
37896           </div>
37897           <tt>text = {{example.text}}</tt><br/>
37898           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
37899           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
37900           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
37901           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
37902          </form>
37903        </file>
37904        <file name="protractor.js" type="protractor">
37905          var text = element(by.binding('example.text'));
37906          var valid = element(by.binding('myForm.input.$valid'));
37907          var input = element(by.model('example.text'));
37908
37909          it('should initialize to model', function() {
37910            expect(text.getText()).toContain('guest');
37911            expect(valid.getText()).toContain('true');
37912          });
37913
37914          it('should be invalid if empty', function() {
37915            input.clear();
37916            input.sendKeys('');
37917
37918            expect(text.getText()).toEqual('text =');
37919            expect(valid.getText()).toContain('false');
37920          });
37921
37922          it('should be invalid if multi word', function() {
37923            input.clear();
37924            input.sendKeys('hello world');
37925
37926            expect(valid.getText()).toContain('false');
37927          });
37928        </file>
37929      </example>
37930   */
37931  'text': textInputType,
37932
37933    /**
37934     * @ngdoc input
37935     * @name input[date]
37936     *
37937     * @description
37938     * Input with date validation and transformation. In browsers that do not yet support
37939     * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601
37940     * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many
37941     * modern browsers do not yet support this input type, it is important to provide cues to users on the
37942     * expected input format via a placeholder or label.
37943     *
37944     * The model must always be a Date object, otherwise Angular will throw an error.
37945     * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
37946     *
37947     * The timezone to be used to read/write the `Date` instance in the model can be defined using
37948     * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
37949     *
37950     * @param {string} ngModel Assignable angular expression to data-bind to.
37951     * @param {string=} name Property name of the form under which the control is published.
37952     * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
37953     *   valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
37954     *   (e.g. `min="{{minDate | date:'yyyy-MM-dd'}}"`). Note that `min` will also add native HTML5
37955     *   constraint validation.
37956     * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
37957     *   a valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
37958     *   (e.g. `max="{{maxDate | date:'yyyy-MM-dd'}}"`). Note that `max` will also add native HTML5
37959     *   constraint validation.
37960     * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO date string
37961     *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
37962     * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO date string
37963     *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
37964     * @param {string=} required Sets `required` validation error key if the value is not entered.
37965     * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
37966     *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
37967     *    `required` when you want to data-bind to the `required` attribute.
37968     * @param {string=} ngChange Angular expression to be executed when input changes due to user
37969     *    interaction with the input element.
37970     *
37971     * @example
37972     <example name="date-input-directive" module="dateInputExample">
37973     <file name="index.html">
37974       <script>
37975          angular.module('dateInputExample', [])
37976            .controller('DateController', ['$scope', function($scope) {
37977              $scope.example = {
37978                value: new Date(2013, 9, 22)
37979              };
37980            }]);
37981       </script>
37982       <form name="myForm" ng-controller="DateController as dateCtrl">
37983          <label for="exampleInput">Pick a date in 2013:</label>
37984          <input type="date" id="exampleInput" name="input" ng-model="example.value"
37985              placeholder="yyyy-MM-dd" min="2013-01-01" max="2013-12-31" required />
37986          <div role="alert">
37987            <span class="error" ng-show="myForm.input.$error.required">
37988                Required!</span>
37989            <span class="error" ng-show="myForm.input.$error.date">
37990                Not a valid date!</span>
37991           </div>
37992           <tt>value = {{example.value | date: "yyyy-MM-dd"}}</tt><br/>
37993           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
37994           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
37995           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
37996           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
37997       </form>
37998     </file>
37999     <file name="protractor.js" type="protractor">
38000        var value = element(by.binding('example.value | date: "yyyy-MM-dd"'));
38001        var valid = element(by.binding('myForm.input.$valid'));
38002        var input = element(by.model('example.value'));
38003
38004        // currently protractor/webdriver does not support
38005        // sending keys to all known HTML5 input controls
38006        // for various browsers (see https://github.com/angular/protractor/issues/562).
38007        function setInput(val) {
38008          // set the value of the element and force validation.
38009          var scr = "var ipt = document.getElementById('exampleInput'); " +
38010          "ipt.value = '" + val + "';" +
38011          "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
38012          browser.executeScript(scr);
38013        }
38014
38015        it('should initialize to model', function() {
38016          expect(value.getText()).toContain('2013-10-22');
38017          expect(valid.getText()).toContain('myForm.input.$valid = true');
38018        });
38019
38020        it('should be invalid if empty', function() {
38021          setInput('');
38022          expect(value.getText()).toEqual('value =');
38023          expect(valid.getText()).toContain('myForm.input.$valid = false');
38024        });
38025
38026        it('should be invalid if over max', function() {
38027          setInput('2015-01-01');
38028          expect(value.getText()).toContain('');
38029          expect(valid.getText()).toContain('myForm.input.$valid = false');
38030        });
38031     </file>
38032     </example>
38033     */
38034  'date': createDateInputType('date', DATE_REGEXP,
38035         createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']),
38036         'yyyy-MM-dd'),
38037
38038   /**
38039    * @ngdoc input
38040    * @name input[datetime-local]
38041    *
38042    * @description
38043    * Input with datetime validation and transformation. In browsers that do not yet support
38044    * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
38045    * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`.
38046    *
38047    * The model must always be a Date object, otherwise Angular will throw an error.
38048    * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
38049    *
38050    * The timezone to be used to read/write the `Date` instance in the model can be defined using
38051    * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
38052    *
38053    * @param {string} ngModel Assignable angular expression to data-bind to.
38054    * @param {string=} name Property name of the form under which the control is published.
38055    * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
38056    *   This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
38057    *   inside this attribute (e.g. `min="{{minDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
38058    *   Note that `min` will also add native HTML5 constraint validation.
38059    * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
38060    *   This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
38061    *   inside this attribute (e.g. `max="{{maxDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
38062    *   Note that `max` will also add native HTML5 constraint validation.
38063    * @param {(date|string)=} ngMin Sets the `min` validation error key to the Date / ISO datetime string
38064    *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
38065    * @param {(date|string)=} ngMax Sets the `max` validation error key to the Date / ISO datetime string
38066    *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
38067    * @param {string=} required Sets `required` validation error key if the value is not entered.
38068    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
38069    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
38070    *    `required` when you want to data-bind to the `required` attribute.
38071    * @param {string=} ngChange Angular expression to be executed when input changes due to user
38072    *    interaction with the input element.
38073    *
38074    * @example
38075    <example name="datetimelocal-input-directive" module="dateExample">
38076    <file name="index.html">
38077      <script>
38078        angular.module('dateExample', [])
38079          .controller('DateController', ['$scope', function($scope) {
38080            $scope.example = {
38081              value: new Date(2010, 11, 28, 14, 57)
38082            };
38083          }]);
38084      </script>
38085      <form name="myForm" ng-controller="DateController as dateCtrl">
38086        <label for="exampleInput">Pick a date between in 2013:</label>
38087        <input type="datetime-local" id="exampleInput" name="input" ng-model="example.value"
38088            placeholder="yyyy-MM-ddTHH:mm:ss" min="2001-01-01T00:00:00" max="2013-12-31T00:00:00" required />
38089        <div role="alert">
38090          <span class="error" ng-show="myForm.input.$error.required">
38091              Required!</span>
38092          <span class="error" ng-show="myForm.input.$error.datetimelocal">
38093              Not a valid date!</span>
38094        </div>
38095        <tt>value = {{example.value | date: "yyyy-MM-ddTHH:mm:ss"}}</tt><br/>
38096        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
38097        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
38098        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
38099        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
38100      </form>
38101    </file>
38102    <file name="protractor.js" type="protractor">
38103      var value = element(by.binding('example.value | date: "yyyy-MM-ddTHH:mm:ss"'));
38104      var valid = element(by.binding('myForm.input.$valid'));
38105      var input = element(by.model('example.value'));
38106
38107      // currently protractor/webdriver does not support
38108      // sending keys to all known HTML5 input controls
38109      // for various browsers (https://github.com/angular/protractor/issues/562).
38110      function setInput(val) {
38111        // set the value of the element and force validation.
38112        var scr = "var ipt = document.getElementById('exampleInput'); " +
38113        "ipt.value = '" + val + "';" +
38114        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
38115        browser.executeScript(scr);
38116      }
38117
38118      it('should initialize to model', function() {
38119        expect(value.getText()).toContain('2010-12-28T14:57:00');
38120        expect(valid.getText()).toContain('myForm.input.$valid = true');
38121      });
38122
38123      it('should be invalid if empty', function() {
38124        setInput('');
38125        expect(value.getText()).toEqual('value =');
38126        expect(valid.getText()).toContain('myForm.input.$valid = false');
38127      });
38128
38129      it('should be invalid if over max', function() {
38130        setInput('2015-01-01T23:59:00');
38131        expect(value.getText()).toContain('');
38132        expect(valid.getText()).toContain('myForm.input.$valid = false');
38133      });
38134    </file>
38135    </example>
38136    */
38137  'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP,
38138      createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']),
38139      'yyyy-MM-ddTHH:mm:ss.sss'),
38140
38141  /**
38142   * @ngdoc input
38143   * @name input[time]
38144   *
38145   * @description
38146   * Input with time validation and transformation. In browsers that do not yet support
38147   * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
38148   * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a
38149   * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`.
38150   *
38151   * The model must always be a Date object, otherwise Angular will throw an error.
38152   * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
38153   *
38154   * The timezone to be used to read/write the `Date` instance in the model can be defined using
38155   * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
38156   *
38157   * @param {string} ngModel Assignable angular expression to data-bind to.
38158   * @param {string=} name Property name of the form under which the control is published.
38159   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
38160   *   This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
38161   *   attribute (e.g. `min="{{minTime | date:'HH:mm:ss'}}"`). Note that `min` will also add
38162   *   native HTML5 constraint validation.
38163   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
38164   *   This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
38165   *   attribute (e.g. `max="{{maxTime | date:'HH:mm:ss'}}"`). Note that `max` will also add
38166   *   native HTML5 constraint validation.
38167   * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO time string the
38168   *   `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
38169   * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO time string the
38170   *   `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
38171   * @param {string=} required Sets `required` validation error key if the value is not entered.
38172   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
38173   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
38174   *    `required` when you want to data-bind to the `required` attribute.
38175   * @param {string=} ngChange Angular expression to be executed when input changes due to user
38176   *    interaction with the input element.
38177   *
38178   * @example
38179   <example name="time-input-directive" module="timeExample">
38180   <file name="index.html">
38181     <script>
38182      angular.module('timeExample', [])
38183        .controller('DateController', ['$scope', function($scope) {
38184          $scope.example = {
38185            value: new Date(1970, 0, 1, 14, 57, 0)
38186          };
38187        }]);
38188     </script>
38189     <form name="myForm" ng-controller="DateController as dateCtrl">
38190        <label for="exampleInput">Pick a between 8am and 5pm:</label>
38191        <input type="time" id="exampleInput" name="input" ng-model="example.value"
38192            placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
38193        <div role="alert">
38194          <span class="error" ng-show="myForm.input.$error.required">
38195              Required!</span>
38196          <span class="error" ng-show="myForm.input.$error.time">
38197              Not a valid date!</span>
38198        </div>
38199        <tt>value = {{example.value | date: "HH:mm:ss"}}</tt><br/>
38200        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
38201        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
38202        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
38203        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
38204     </form>
38205   </file>
38206   <file name="protractor.js" type="protractor">
38207      var value = element(by.binding('example.value | date: "HH:mm:ss"'));
38208      var valid = element(by.binding('myForm.input.$valid'));
38209      var input = element(by.model('example.value'));
38210
38211      // currently protractor/webdriver does not support
38212      // sending keys to all known HTML5 input controls
38213      // for various browsers (https://github.com/angular/protractor/issues/562).
38214      function setInput(val) {
38215        // set the value of the element and force validation.
38216        var scr = "var ipt = document.getElementById('exampleInput'); " +
38217        "ipt.value = '" + val + "';" +
38218        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
38219        browser.executeScript(scr);
38220      }
38221
38222      it('should initialize to model', function() {
38223        expect(value.getText()).toContain('14:57:00');
38224        expect(valid.getText()).toContain('myForm.input.$valid = true');
38225      });
38226
38227      it('should be invalid if empty', function() {
38228        setInput('');
38229        expect(value.getText()).toEqual('value =');
38230        expect(valid.getText()).toContain('myForm.input.$valid = false');
38231      });
38232
38233      it('should be invalid if over max', function() {
38234        setInput('23:59:00');
38235        expect(value.getText()).toContain('');
38236        expect(valid.getText()).toContain('myForm.input.$valid = false');
38237      });
38238   </file>
38239   </example>
38240   */
38241  'time': createDateInputType('time', TIME_REGEXP,
38242      createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']),
38243     'HH:mm:ss.sss'),
38244
38245   /**
38246    * @ngdoc input
38247    * @name input[week]
38248    *
38249    * @description
38250    * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support
38251    * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
38252    * week format (yyyy-W##), for example: `2013-W02`.
38253    *
38254    * The model must always be a Date object, otherwise Angular will throw an error.
38255    * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
38256    *
38257    * The timezone to be used to read/write the `Date` instance in the model can be defined using
38258    * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
38259    *
38260    * @param {string} ngModel Assignable angular expression to data-bind to.
38261    * @param {string=} name Property name of the form under which the control is published.
38262    * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
38263    *   This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
38264    *   attribute (e.g. `min="{{minWeek | date:'yyyy-Www'}}"`). Note that `min` will also add
38265    *   native HTML5 constraint validation.
38266    * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
38267    *   This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
38268    *   attribute (e.g. `max="{{maxWeek | date:'yyyy-Www'}}"`). Note that `max` will also add
38269    *   native HTML5 constraint validation.
38270    * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
38271    *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
38272    * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
38273    *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
38274    * @param {string=} required Sets `required` validation error key if the value is not entered.
38275    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
38276    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
38277    *    `required` when you want to data-bind to the `required` attribute.
38278    * @param {string=} ngChange Angular expression to be executed when input changes due to user
38279    *    interaction with the input element.
38280    *
38281    * @example
38282    <example name="week-input-directive" module="weekExample">
38283    <file name="index.html">
38284      <script>
38285      angular.module('weekExample', [])
38286        .controller('DateController', ['$scope', function($scope) {
38287          $scope.example = {
38288            value: new Date(2013, 0, 3)
38289          };
38290        }]);
38291      </script>
38292      <form name="myForm" ng-controller="DateController as dateCtrl">
38293        <label>Pick a date between in 2013:
38294          <input id="exampleInput" type="week" name="input" ng-model="example.value"
38295                 placeholder="YYYY-W##" min="2012-W32"
38296                 max="2013-W52" required />
38297        </label>
38298        <div role="alert">
38299          <span class="error" ng-show="myForm.input.$error.required">
38300              Required!</span>
38301          <span class="error" ng-show="myForm.input.$error.week">
38302              Not a valid date!</span>
38303        </div>
38304        <tt>value = {{example.value | date: "yyyy-Www"}}</tt><br/>
38305        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
38306        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
38307        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
38308        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
38309      </form>
38310    </file>
38311    <file name="protractor.js" type="protractor">
38312      var value = element(by.binding('example.value | date: "yyyy-Www"'));
38313      var valid = element(by.binding('myForm.input.$valid'));
38314      var input = element(by.model('example.value'));
38315
38316      // currently protractor/webdriver does not support
38317      // sending keys to all known HTML5 input controls
38318      // for various browsers (https://github.com/angular/protractor/issues/562).
38319      function setInput(val) {
38320        // set the value of the element and force validation.
38321        var scr = "var ipt = document.getElementById('exampleInput'); " +
38322        "ipt.value = '" + val + "';" +
38323        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
38324        browser.executeScript(scr);
38325      }
38326
38327      it('should initialize to model', function() {
38328        expect(value.getText()).toContain('2013-W01');
38329        expect(valid.getText()).toContain('myForm.input.$valid = true');
38330      });
38331
38332      it('should be invalid if empty', function() {
38333        setInput('');
38334        expect(value.getText()).toEqual('value =');
38335        expect(valid.getText()).toContain('myForm.input.$valid = false');
38336      });
38337
38338      it('should be invalid if over max', function() {
38339        setInput('2015-W01');
38340        expect(value.getText()).toContain('');
38341        expect(valid.getText()).toContain('myForm.input.$valid = false');
38342      });
38343    </file>
38344    </example>
38345    */
38346  'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'),
38347
38348  /**
38349   * @ngdoc input
38350   * @name input[month]
38351   *
38352   * @description
38353   * Input with month validation and transformation. In browsers that do not yet support
38354   * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
38355   * month format (yyyy-MM), for example: `2009-01`.
38356   *
38357   * The model must always be a Date object, otherwise Angular will throw an error.
38358   * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
38359   * If the model is not set to the first of the month, the next view to model update will set it
38360   * to the first of the month.
38361   *
38362   * The timezone to be used to read/write the `Date` instance in the model can be defined using
38363   * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
38364   *
38365   * @param {string} ngModel Assignable angular expression to data-bind to.
38366   * @param {string=} name Property name of the form under which the control is published.
38367   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
38368   *   This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
38369   *   attribute (e.g. `min="{{minMonth | date:'yyyy-MM'}}"`). Note that `min` will also add
38370   *   native HTML5 constraint validation.
38371   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
38372   *   This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
38373   *   attribute (e.g. `max="{{maxMonth | date:'yyyy-MM'}}"`). Note that `max` will also add
38374   *   native HTML5 constraint validation.
38375   * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
38376   *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
38377   * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
38378   *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
38379
38380   * @param {string=} required Sets `required` validation error key if the value is not entered.
38381   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
38382   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
38383   *    `required` when you want to data-bind to the `required` attribute.
38384   * @param {string=} ngChange Angular expression to be executed when input changes due to user
38385   *    interaction with the input element.
38386   *
38387   * @example
38388   <example name="month-input-directive" module="monthExample">
38389   <file name="index.html">
38390     <script>
38391      angular.module('monthExample', [])
38392        .controller('DateController', ['$scope', function($scope) {
38393          $scope.example = {
38394            value: new Date(2013, 9, 1)
38395          };
38396        }]);
38397     </script>
38398     <form name="myForm" ng-controller="DateController as dateCtrl">
38399       <label for="exampleInput">Pick a month in 2013:</label>
38400       <input id="exampleInput" type="month" name="input" ng-model="example.value"
38401          placeholder="yyyy-MM" min="2013-01" max="2013-12" required />
38402       <div role="alert">
38403         <span class="error" ng-show="myForm.input.$error.required">
38404            Required!</span>
38405         <span class="error" ng-show="myForm.input.$error.month">
38406            Not a valid month!</span>
38407       </div>
38408       <tt>value = {{example.value | date: "yyyy-MM"}}</tt><br/>
38409       <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
38410       <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
38411       <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
38412       <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
38413     </form>
38414   </file>
38415   <file name="protractor.js" type="protractor">
38416      var value = element(by.binding('example.value | date: "yyyy-MM"'));
38417      var valid = element(by.binding('myForm.input.$valid'));
38418      var input = element(by.model('example.value'));
38419
38420      // currently protractor/webdriver does not support
38421      // sending keys to all known HTML5 input controls
38422      // for various browsers (https://github.com/angular/protractor/issues/562).
38423      function setInput(val) {
38424        // set the value of the element and force validation.
38425        var scr = "var ipt = document.getElementById('exampleInput'); " +
38426        "ipt.value = '" + val + "';" +
38427        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
38428        browser.executeScript(scr);
38429      }
38430
38431      it('should initialize to model', function() {
38432        expect(value.getText()).toContain('2013-10');
38433        expect(valid.getText()).toContain('myForm.input.$valid = true');
38434      });
38435
38436      it('should be invalid if empty', function() {
38437        setInput('');
38438        expect(value.getText()).toEqual('value =');
38439        expect(valid.getText()).toContain('myForm.input.$valid = false');
38440      });
38441
38442      it('should be invalid if over max', function() {
38443        setInput('2015-01');
38444        expect(value.getText()).toContain('');
38445        expect(valid.getText()).toContain('myForm.input.$valid = false');
38446      });
38447   </file>
38448   </example>
38449   */
38450  'month': createDateInputType('month', MONTH_REGEXP,
38451     createDateParser(MONTH_REGEXP, ['yyyy', 'MM']),
38452     'yyyy-MM'),
38453
38454  /**
38455   * @ngdoc input
38456   * @name input[number]
38457   *
38458   * @description
38459   * Text input with number validation and transformation. Sets the `number` validation
38460   * error if not a valid number.
38461   *
38462   * <div class="alert alert-warning">
38463   * The model must always be of type `number` otherwise Angular will throw an error.
38464   * Be aware that a string containing a number is not enough. See the {@link ngModel:numfmt}
38465   * error docs for more information and an example of how to convert your model if necessary.
38466   * </div>
38467   *
38468   * ## Issues with HTML5 constraint validation
38469   *
38470   * In browsers that follow the
38471   * [HTML5 specification](https://html.spec.whatwg.org/multipage/forms.html#number-state-%28type=number%29),
38472   * `input[number]` does not work as expected with {@link ngModelOptions `ngModelOptions.allowInvalid`}.
38473   * If a non-number is entered in the input, the browser will report the value as an empty string,
38474   * which means the view / model values in `ngModel` and subsequently the scope value
38475   * will also be an empty string.
38476   *
38477   *
38478   * @param {string} ngModel Assignable angular expression to data-bind to.
38479   * @param {string=} name Property name of the form under which the control is published.
38480   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
38481   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
38482   * @param {string=} required Sets `required` validation error key if the value is not entered.
38483   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
38484   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
38485   *    `required` when you want to data-bind to the `required` attribute.
38486   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
38487   *    minlength.
38488   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
38489   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
38490   *    any length.
38491   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
38492   *    that contains the regular expression body that will be converted to a regular expression
38493   *    as in the ngPattern directive.
38494   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
38495   *    a RegExp found by evaluating the Angular expression given in the attribute value.
38496   *    If the expression evaluates to a RegExp object, then this is used directly.
38497   *    If the expression evaluates to a string, then it will be converted to a RegExp
38498   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
38499   *    `new RegExp('^abc$')`.<br />
38500   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
38501   *    start at the index of the last search's match, thus not taking the whole input value into
38502   *    account.
38503   * @param {string=} ngChange Angular expression to be executed when input changes due to user
38504   *    interaction with the input element.
38505   *
38506   * @example
38507      <example name="number-input-directive" module="numberExample">
38508        <file name="index.html">
38509         <script>
38510           angular.module('numberExample', [])
38511             .controller('ExampleController', ['$scope', function($scope) {
38512               $scope.example = {
38513                 value: 12
38514               };
38515             }]);
38516         </script>
38517         <form name="myForm" ng-controller="ExampleController">
38518           <label>Number:
38519             <input type="number" name="input" ng-model="example.value"
38520                    min="0" max="99" required>
38521          </label>
38522           <div role="alert">
38523             <span class="error" ng-show="myForm.input.$error.required">
38524               Required!</span>
38525             <span class="error" ng-show="myForm.input.$error.number">
38526               Not valid number!</span>
38527           </div>
38528           <tt>value = {{example.value}}</tt><br/>
38529           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
38530           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
38531           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
38532           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
38533          </form>
38534        </file>
38535        <file name="protractor.js" type="protractor">
38536          var value = element(by.binding('example.value'));
38537          var valid = element(by.binding('myForm.input.$valid'));
38538          var input = element(by.model('example.value'));
38539
38540          it('should initialize to model', function() {
38541            expect(value.getText()).toContain('12');
38542            expect(valid.getText()).toContain('true');
38543          });
38544
38545          it('should be invalid if empty', function() {
38546            input.clear();
38547            input.sendKeys('');
38548            expect(value.getText()).toEqual('value =');
38549            expect(valid.getText()).toContain('false');
38550          });
38551
38552          it('should be invalid if over max', function() {
38553            input.clear();
38554            input.sendKeys('123');
38555            expect(value.getText()).toEqual('value =');
38556            expect(valid.getText()).toContain('false');
38557          });
38558        </file>
38559      </example>
38560   */
38561  'number': numberInputType,
38562
38563
38564  /**
38565   * @ngdoc input
38566   * @name input[url]
38567   *
38568   * @description
38569   * Text input with URL validation. Sets the `url` validation error key if the content is not a
38570   * valid URL.
38571   *
38572   * <div class="alert alert-warning">
38573   * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex
38574   * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify
38575   * the built-in validators (see the {@link guide/forms Forms guide})
38576   * </div>
38577   *
38578   * @param {string} ngModel Assignable angular expression to data-bind to.
38579   * @param {string=} name Property name of the form under which the control is published.
38580   * @param {string=} required Sets `required` validation error key if the value is not entered.
38581   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
38582   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
38583   *    `required` when you want to data-bind to the `required` attribute.
38584   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
38585   *    minlength.
38586   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
38587   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
38588   *    any length.
38589   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
38590   *    that contains the regular expression body that will be converted to a regular expression
38591   *    as in the ngPattern directive.
38592   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
38593   *    a RegExp found by evaluating the Angular expression given in the attribute value.
38594   *    If the expression evaluates to a RegExp object, then this is used directly.
38595   *    If the expression evaluates to a string, then it will be converted to a RegExp
38596   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
38597   *    `new RegExp('^abc$')`.<br />
38598   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
38599   *    start at the index of the last search's match, thus not taking the whole input value into
38600   *    account.
38601   * @param {string=} ngChange Angular expression to be executed when input changes due to user
38602   *    interaction with the input element.
38603   *
38604   * @example
38605      <example name="url-input-directive" module="urlExample">
38606        <file name="index.html">
38607         <script>
38608           angular.module('urlExample', [])
38609             .controller('ExampleController', ['$scope', function($scope) {
38610               $scope.url = {
38611                 text: 'http://google.com'
38612               };
38613             }]);
38614         </script>
38615         <form name="myForm" ng-controller="ExampleController">
38616           <label>URL:
38617             <input type="url" name="input" ng-model="url.text" required>
38618           <label>
38619           <div role="alert">
38620             <span class="error" ng-show="myForm.input.$error.required">
38621               Required!</span>
38622             <span class="error" ng-show="myForm.input.$error.url">
38623               Not valid url!</span>
38624           </div>
38625           <tt>text = {{url.text}}</tt><br/>
38626           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
38627           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
38628           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
38629           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
38630           <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
38631          </form>
38632        </file>
38633        <file name="protractor.js" type="protractor">
38634          var text = element(by.binding('url.text'));
38635          var valid = element(by.binding('myForm.input.$valid'));
38636          var input = element(by.model('url.text'));
38637
38638          it('should initialize to model', function() {
38639            expect(text.getText()).toContain('http://google.com');
38640            expect(valid.getText()).toContain('true');
38641          });
38642
38643          it('should be invalid if empty', function() {
38644            input.clear();
38645            input.sendKeys('');
38646
38647            expect(text.getText()).toEqual('text =');
38648            expect(valid.getText()).toContain('false');
38649          });
38650
38651          it('should be invalid if not url', function() {
38652            input.clear();
38653            input.sendKeys('box');
38654
38655            expect(valid.getText()).toContain('false');
38656          });
38657        </file>
38658      </example>
38659   */
38660  'url': urlInputType,
38661
38662
38663  /**
38664   * @ngdoc input
38665   * @name input[email]
38666   *
38667   * @description
38668   * Text input with email validation. Sets the `email` validation error key if not a valid email
38669   * address.
38670   *
38671   * <div class="alert alert-warning">
38672   * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex
38673   * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can
38674   * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide})
38675   * </div>
38676   *
38677   * @param {string} ngModel Assignable angular expression to data-bind to.
38678   * @param {string=} name Property name of the form under which the control is published.
38679   * @param {string=} required Sets `required` validation error key if the value is not entered.
38680   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
38681   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
38682   *    `required` when you want to data-bind to the `required` attribute.
38683   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
38684   *    minlength.
38685   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
38686   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
38687   *    any length.
38688   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
38689   *    that contains the regular expression body that will be converted to a regular expression
38690   *    as in the ngPattern directive.
38691   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
38692   *    a RegExp found by evaluating the Angular expression given in the attribute value.
38693   *    If the expression evaluates to a RegExp object, then this is used directly.
38694   *    If the expression evaluates to a string, then it will be converted to a RegExp
38695   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
38696   *    `new RegExp('^abc$')`.<br />
38697   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
38698   *    start at the index of the last search's match, thus not taking the whole input value into
38699   *    account.
38700   * @param {string=} ngChange Angular expression to be executed when input changes due to user
38701   *    interaction with the input element.
38702   *
38703   * @example
38704      <example name="email-input-directive" module="emailExample">
38705        <file name="index.html">
38706         <script>
38707           angular.module('emailExample', [])
38708             .controller('ExampleController', ['$scope', function($scope) {
38709               $scope.email = {
38710                 text: '[email protected]'
38711               };
38712             }]);
38713         </script>
38714           <form name="myForm" ng-controller="ExampleController">
38715             <label>Email:
38716               <input type="email" name="input" ng-model="email.text" required>
38717             </label>
38718             <div role="alert">
38719               <span class="error" ng-show="myForm.input.$error.required">
38720                 Required!</span>
38721               <span class="error" ng-show="myForm.input.$error.email">
38722                 Not valid email!</span>
38723             </div>
38724             <tt>text = {{email.text}}</tt><br/>
38725             <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
38726             <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
38727             <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
38728             <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
38729             <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
38730           </form>
38731         </file>
38732        <file name="protractor.js" type="protractor">
38733          var text = element(by.binding('email.text'));
38734          var valid = element(by.binding('myForm.input.$valid'));
38735          var input = element(by.model('email.text'));
38736
38737          it('should initialize to model', function() {
38738            expect(text.getText()).toContain('[email protected]');
38739            expect(valid.getText()).toContain('true');
38740          });
38741
38742          it('should be invalid if empty', function() {
38743            input.clear();
38744            input.sendKeys('');
38745            expect(text.getText()).toEqual('text =');
38746            expect(valid.getText()).toContain('false');
38747          });
38748
38749          it('should be invalid if not email', function() {
38750            input.clear();
38751            input.sendKeys('xxx');
38752
38753            expect(valid.getText()).toContain('false');
38754          });
38755        </file>
38756      </example>
38757   */
38758  'email': emailInputType,
38759
38760
38761  /**
38762   * @ngdoc input
38763   * @name input[radio]
38764   *
38765   * @description
38766   * HTML radio button.
38767   *
38768   * @param {string} ngModel Assignable angular expression to data-bind to.
38769   * @param {string} value The value to which the `ngModel` expression should be set when selected.
38770   *    Note that `value` only supports `string` values, i.e. the scope model needs to be a string,
38771   *    too. Use `ngValue` if you need complex models (`number`, `object`, ...).
38772   * @param {string=} name Property name of the form under which the control is published.
38773   * @param {string=} ngChange Angular expression to be executed when input changes due to user
38774   *    interaction with the input element.
38775   * @param {string} ngValue Angular expression to which `ngModel` will be be set when the radio
38776   *    is selected. Should be used instead of the `value` attribute if you need
38777   *    a non-string `ngModel` (`boolean`, `array`, ...).
38778   *
38779   * @example
38780      <example name="radio-input-directive" module="radioExample">
38781        <file name="index.html">
38782         <script>
38783           angular.module('radioExample', [])
38784             .controller('ExampleController', ['$scope', function($scope) {
38785               $scope.color = {
38786                 name: 'blue'
38787               };
38788               $scope.specialValue = {
38789                 "id": "12345",
38790                 "value": "green"
38791               };
38792             }]);
38793         </script>
38794         <form name="myForm" ng-controller="ExampleController">
38795           <label>
38796             <input type="radio" ng-model="color.name" value="red">
38797             Red
38798           </label><br/>
38799           <label>
38800             <input type="radio" ng-model="color.name" ng-value="specialValue">
38801             Green
38802           </label><br/>
38803           <label>
38804             <input type="radio" ng-model="color.name" value="blue">
38805             Blue
38806           </label><br/>
38807           <tt>color = {{color.name | json}}</tt><br/>
38808          </form>
38809          Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
38810        </file>
38811        <file name="protractor.js" type="protractor">
38812          it('should change state', function() {
38813            var color = element(by.binding('color.name'));
38814
38815            expect(color.getText()).toContain('blue');
38816
38817            element.all(by.model('color.name')).get(0).click();
38818
38819            expect(color.getText()).toContain('red');
38820          });
38821        </file>
38822      </example>
38823   */
38824  'radio': radioInputType,
38825
38826
38827  /**
38828   * @ngdoc input
38829   * @name input[checkbox]
38830   *
38831   * @description
38832   * HTML checkbox.
38833   *
38834   * @param {string} ngModel Assignable angular expression to data-bind to.
38835   * @param {string=} name Property name of the form under which the control is published.
38836   * @param {expression=} ngTrueValue The value to which the expression should be set when selected.
38837   * @param {expression=} ngFalseValue The value to which the expression should be set when not selected.
38838   * @param {string=} ngChange Angular expression to be executed when input changes due to user
38839   *    interaction with the input element.
38840   *
38841   * @example
38842      <example name="checkbox-input-directive" module="checkboxExample">
38843        <file name="index.html">
38844         <script>
38845           angular.module('checkboxExample', [])
38846             .controller('ExampleController', ['$scope', function($scope) {
38847               $scope.checkboxModel = {
38848                value1 : true,
38849                value2 : 'YES'
38850              };
38851             }]);
38852         </script>
38853         <form name="myForm" ng-controller="ExampleController">
38854           <label>Value1:
38855             <input type="checkbox" ng-model="checkboxModel.value1">
38856           </label><br/>
38857           <label>Value2:
38858             <input type="checkbox" ng-model="checkboxModel.value2"
38859                    ng-true-value="'YES'" ng-false-value="'NO'">
38860            </label><br/>
38861           <tt>value1 = {{checkboxModel.value1}}</tt><br/>
38862           <tt>value2 = {{checkboxModel.value2}}</tt><br/>
38863          </form>
38864        </file>
38865        <file name="protractor.js" type="protractor">
38866          it('should change state', function() {
38867            var value1 = element(by.binding('checkboxModel.value1'));
38868            var value2 = element(by.binding('checkboxModel.value2'));
38869
38870            expect(value1.getText()).toContain('true');
38871            expect(value2.getText()).toContain('YES');
38872
38873            element(by.model('checkboxModel.value1')).click();
38874            element(by.model('checkboxModel.value2')).click();
38875
38876            expect(value1.getText()).toContain('false');
38877            expect(value2.getText()).toContain('NO');
38878          });
38879        </file>
38880      </example>
38881   */
38882  'checkbox': checkboxInputType,
38883
38884  'hidden': noop,
38885  'button': noop,
38886  'submit': noop,
38887  'reset': noop,
38888  'file': noop
38889};
38890
38891function stringBasedInputType(ctrl) {
38892  ctrl.$formatters.push(function(value) {
38893    return ctrl.$isEmpty(value) ? value : value.toString();
38894  });
38895}
38896
38897function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
38898  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
38899  stringBasedInputType(ctrl);
38900}
38901
38902function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
38903  var type = lowercase(element[0].type);
38904
38905  // In composition mode, users are still inputing intermediate text buffer,
38906  // hold the listener until composition is done.
38907  // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
38908  if (!$sniffer.android) {
38909    var composing = false;
38910
38911    element.on('compositionstart', function(data) {
38912      composing = true;
38913    });
38914
38915    element.on('compositionend', function() {
38916      composing = false;
38917      listener();
38918    });
38919  }
38920
38921  var listener = function(ev) {
38922    if (timeout) {
38923      $browser.defer.cancel(timeout);
38924      timeout = null;
38925    }
38926    if (composing) return;
38927    var value = element.val(),
38928        event = ev && ev.type;
38929
38930    // By default we will trim the value
38931    // If the attribute ng-trim exists we will avoid trimming
38932    // If input type is 'password', the value is never trimmed
38933    if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) {
38934      value = trim(value);
38935    }
38936
38937    // If a control is suffering from bad input (due to native validators), browsers discard its
38938    // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the
38939    // control's value is the same empty value twice in a row.
38940    if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) {
38941      ctrl.$setViewValue(value, event);
38942    }
38943  };
38944
38945  // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the
38946  // input event on backspace, delete or cut
38947  if ($sniffer.hasEvent('input')) {
38948    element.on('input', listener);
38949  } else {
38950    var timeout;
38951
38952    var deferListener = function(ev, input, origValue) {
38953      if (!timeout) {
38954        timeout = $browser.defer(function() {
38955          timeout = null;
38956          if (!input || input.value !== origValue) {
38957            listener(ev);
38958          }
38959        });
38960      }
38961    };
38962
38963    element.on('keydown', function(event) {
38964      var key = event.keyCode;
38965
38966      // ignore
38967      //    command            modifiers                   arrows
38968      if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
38969
38970      deferListener(event, this, this.value);
38971    });
38972
38973    // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
38974    if ($sniffer.hasEvent('paste')) {
38975      element.on('paste cut', deferListener);
38976    }
38977  }
38978
38979  // if user paste into input using mouse on older browser
38980  // or form autocomplete on newer browser, we need "change" event to catch it
38981  element.on('change', listener);
38982
38983  ctrl.$render = function() {
38984    // Workaround for Firefox validation #12102.
38985    var value = ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue;
38986    if (element.val() !== value) {
38987      element.val(value);
38988    }
38989  };
38990}
38991
38992function weekParser(isoWeek, existingDate) {
38993  if (isDate(isoWeek)) {
38994    return isoWeek;
38995  }
38996
38997  if (isString(isoWeek)) {
38998    WEEK_REGEXP.lastIndex = 0;
38999    var parts = WEEK_REGEXP.exec(isoWeek);
39000    if (parts) {
39001      var year = +parts[1],
39002          week = +parts[2],
39003          hours = 0,
39004          minutes = 0,
39005          seconds = 0,
39006          milliseconds = 0,
39007          firstThurs = getFirstThursdayOfYear(year),
39008          addDays = (week - 1) * 7;
39009
39010      if (existingDate) {
39011        hours = existingDate.getHours();
39012        minutes = existingDate.getMinutes();
39013        seconds = existingDate.getSeconds();
39014        milliseconds = existingDate.getMilliseconds();
39015      }
39016
39017      return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds);
39018    }
39019  }
39020
39021  return NaN;
39022}
39023
39024function createDateParser(regexp, mapping) {
39025  return function(iso, date) {
39026    var parts, map;
39027
39028    if (isDate(iso)) {
39029      return iso;
39030    }
39031
39032    if (isString(iso)) {
39033      // When a date is JSON'ified to wraps itself inside of an extra
39034      // set of double quotes. This makes the date parsing code unable
39035      // to match the date string and parse it as a date.
39036      if (iso.charAt(0) == '"' && iso.charAt(iso.length - 1) == '"') {
39037        iso = iso.substring(1, iso.length - 1);
39038      }
39039      if (ISO_DATE_REGEXP.test(iso)) {
39040        return new Date(iso);
39041      }
39042      regexp.lastIndex = 0;
39043      parts = regexp.exec(iso);
39044
39045      if (parts) {
39046        parts.shift();
39047        if (date) {
39048          map = {
39049            yyyy: date.getFullYear(),
39050            MM: date.getMonth() + 1,
39051            dd: date.getDate(),
39052            HH: date.getHours(),
39053            mm: date.getMinutes(),
39054            ss: date.getSeconds(),
39055            sss: date.getMilliseconds() / 1000
39056          };
39057        } else {
39058          map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 };
39059        }
39060
39061        forEach(parts, function(part, index) {
39062          if (index < mapping.length) {
39063            map[mapping[index]] = +part;
39064          }
39065        });
39066        return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0);
39067      }
39068    }
39069
39070    return NaN;
39071  };
39072}
39073
39074function createDateInputType(type, regexp, parseDate, format) {
39075  return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
39076    badInputChecker(scope, element, attr, ctrl);
39077    baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
39078    var timezone = ctrl && ctrl.$options && ctrl.$options.timezone;
39079    var previousDate;
39080
39081    ctrl.$$parserName = type;
39082    ctrl.$parsers.push(function(value) {
39083      if (ctrl.$isEmpty(value)) return null;
39084      if (regexp.test(value)) {
39085        // Note: We cannot read ctrl.$modelValue, as there might be a different
39086        // parser/formatter in the processing chain so that the model
39087        // contains some different data format!
39088        var parsedDate = parseDate(value, previousDate);
39089        if (timezone) {
39090          parsedDate = convertTimezoneToLocal(parsedDate, timezone);
39091        }
39092        return parsedDate;
39093      }
39094      return undefined;
39095    });
39096
39097    ctrl.$formatters.push(function(value) {
39098      if (value && !isDate(value)) {
39099        throw ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
39100      }
39101      if (isValidDate(value)) {
39102        previousDate = value;
39103        if (previousDate && timezone) {
39104          previousDate = convertTimezoneToLocal(previousDate, timezone, true);
39105        }
39106        return $filter('date')(value, format, timezone);
39107      } else {
39108        previousDate = null;
39109        return '';
39110      }
39111    });
39112
39113    if (isDefined(attr.min) || attr.ngMin) {
39114      var minVal;
39115      ctrl.$validators.min = function(value) {
39116        return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal;
39117      };
39118      attr.$observe('min', function(val) {
39119        minVal = parseObservedDateValue(val);
39120        ctrl.$validate();
39121      });
39122    }
39123
39124    if (isDefined(attr.max) || attr.ngMax) {
39125      var maxVal;
39126      ctrl.$validators.max = function(value) {
39127        return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;
39128      };
39129      attr.$observe('max', function(val) {
39130        maxVal = parseObservedDateValue(val);
39131        ctrl.$validate();
39132      });
39133    }
39134
39135    function isValidDate(value) {
39136      // Invalid Date: getTime() returns NaN
39137      return value && !(value.getTime && value.getTime() !== value.getTime());
39138    }
39139
39140    function parseObservedDateValue(val) {
39141      return isDefined(val) && !isDate(val) ? parseDate(val) || undefined : val;
39142    }
39143  };
39144}
39145
39146function badInputChecker(scope, element, attr, ctrl) {
39147  var node = element[0];
39148  var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity);
39149  if (nativeValidation) {
39150    ctrl.$parsers.push(function(value) {
39151      var validity = element.prop(VALIDITY_STATE_PROPERTY) || {};
39152      // Detect bug in FF35 for input[email] (https://bugzilla.mozilla.org/show_bug.cgi?id=1064430):
39153      // - also sets validity.badInput (should only be validity.typeMismatch).
39154      // - see http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#e-mail-state-(type=email)
39155      // - can ignore this case as we can still read out the erroneous email...
39156      return validity.badInput && !validity.typeMismatch ? undefined : value;
39157    });
39158  }
39159}
39160
39161function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
39162  badInputChecker(scope, element, attr, ctrl);
39163  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
39164
39165  ctrl.$$parserName = 'number';
39166  ctrl.$parsers.push(function(value) {
39167    if (ctrl.$isEmpty(value))      return null;
39168    if (NUMBER_REGEXP.test(value)) return parseFloat(value);
39169    return undefined;
39170  });
39171
39172  ctrl.$formatters.push(function(value) {
39173    if (!ctrl.$isEmpty(value)) {
39174      if (!isNumber(value)) {
39175        throw ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value);
39176      }
39177      value = value.toString();
39178    }
39179    return value;
39180  });
39181
39182  if (isDefined(attr.min) || attr.ngMin) {
39183    var minVal;
39184    ctrl.$validators.min = function(value) {
39185      return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal;
39186    };
39187
39188    attr.$observe('min', function(val) {
39189      if (isDefined(val) && !isNumber(val)) {
39190        val = parseFloat(val, 10);
39191      }
39192      minVal = isNumber(val) && !isNaN(val) ? val : undefined;
39193      // TODO(matsko): implement validateLater to reduce number of validations
39194      ctrl.$validate();
39195    });
39196  }
39197
39198  if (isDefined(attr.max) || attr.ngMax) {
39199    var maxVal;
39200    ctrl.$validators.max = function(value) {
39201      return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal;
39202    };
39203
39204    attr.$observe('max', function(val) {
39205      if (isDefined(val) && !isNumber(val)) {
39206        val = parseFloat(val, 10);
39207      }
39208      maxVal = isNumber(val) && !isNaN(val) ? val : undefined;
39209      // TODO(matsko): implement validateLater to reduce number of validations
39210      ctrl.$validate();
39211    });
39212  }
39213}
39214
39215function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
39216  // Note: no badInputChecker here by purpose as `url` is only a validation
39217  // in browsers, i.e. we can always read out input.value even if it is not val
39217id!
39218  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
39219  stringBasedInputType(ctrl);
39220
39221  ctrl.$$parserName = 'url';
39222  ctrl.$validators.url = function(modelValue, viewValue) {
39223    var value = modelValue || viewValue;
39224    return ctrl.$isEmpty(value) || URL_REGEXP.test(value);
39225  };
39226}
39227
39228function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
39229  // Note: no badInputChecker here by purpose as `url` is only a validation
39230  // in browsers, i.e. we can always read out input.value even if it is not valid!
39231  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
39232  stringBasedInputType(ctrl);
39233
39234  ctrl.$$parserName = 'email';
39235  ctrl.$validators.email = function(modelValue, viewValue) {
39236    var value = modelValue || viewValue;
39237    return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value);
39238  };
39239}
39240
39241function radioInputType(scope, element, attr, ctrl) {
39242  // make the name unique, if not defined
39243  if (isUndefined(attr.name)) {
39244    element.attr('name', nextUid());
39245  }
39246
39247  var listener = function(ev) {
39248    if (element[0].checked) {
39249      ctrl.$setViewValue(attr.value, ev && ev.type);
39250    }
39251  };
39252
39253  element.on('click', listener);
39254
39255  ctrl.$render = function() {
39256    var value = attr.value;
39257    element[0].checked = (value == ctrl.$viewValue);
39258  };
39259
39260  attr.$observe('value', ctrl.$render);
39261}
39262
39263function parseConstantExpr($parse, context, name, expression, fallback) {
39264  var parseFn;
39265  if (isDefined(expression)) {
39266    parseFn = $parse(expression);
39267    if (!parseFn.constant) {
39268      throw ngModelMinErr('constexpr', 'Expected constant expression for `{0}`, but saw ' +
39269                                   '`{1}`.', name, expression);
39270    }
39271    return parseFn(context);
39272  }
39273  return fallback;
39274}
39275
39276function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
39277  var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true);
39278  var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false);
39279
39280  var listener = function(ev) {
39281    ctrl.$setViewValue(element[0].checked, ev && ev.type);
39282  };
39283
39284  element.on('click', listener);
39285
39286  ctrl.$render = function() {
39287    element[0].checked = ctrl.$viewValue;
39288  };
39289
39290  // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false`
39291  // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert
39292  // it to a boolean.
39293  ctrl.$isEmpty = function(value) {
39294    return value === false;
39295  };
39296
39297  ctrl.$formatters.push(function(value) {
39298    return equals(value, trueValue);
39299  });
39300
39301  ctrl.$parsers.push(function(value) {
39302    return value ? trueValue : falseValue;
39303  });
39304}
39305
39306
39307/**
39308 * @ngdoc directive
39309 * @name textarea
39310 * @restrict E
39311 *
39312 * @description
39313 * HTML textarea element control with angular data-binding. The data-binding and validation
39314 * properties of this element are exactly the same as those of the
39315 * {@link ng.directive:input input element}.
39316 *
39317 * @param {string} ngModel Assignable angular expression to data-bind to.
39318 * @param {string=} name Property name of the form under which the control is published.
39319 * @param {string=} required Sets `required` validation error key if the value is not entered.
39320 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
39321 *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
39322 *    `required` when you want to data-bind to the `required` attribute.
39323 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
39324 *    minlength.
39325 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
39326 *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
39327 *    length.
39328 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
39329 *    a RegExp found by evaluating the Angular expression given in the attribute value.
39330 *    If the expression evaluates to a RegExp object, then this is used directly.
39331 *    If the expression evaluates to a string, then it will be converted to a RegExp
39332 *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
39333 *    `new RegExp('^abc$')`.<br />
39334 *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
39335 *    start at the index of the last search's match, thus not taking the whole input value into
39336 *    account.
39337 * @param {string=} ngChange Angular expression to be executed when input changes due to user
39338 *    interaction with the input element.
39339 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
39340 */
39341
39342
39343/**
39344 * @ngdoc directive
39345 * @name input
39346 * @restrict E
39347 *
39348 * @description
39349 * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding,
39350 * input state control, and validation.
39351 * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers.
39352 *
39353 * <div class="alert alert-warning">
39354 * **Note:** Not every feature offered is available for all input types.
39355 * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`.
39356 * </div>
39357 *
39358 * @param {string} ngModel Assignable angular expression to data-bind to.
39359 * @param {string=} name Property name of the form under which the control is published.
39360 * @param {string=} required Sets `required` validation error key if the value is not entered.
39361 * @param {boolean=} ngRequired Sets `required` attribute if set to true
39362 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
39363 *    minlength.
39364 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
39365 *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
39366 *    length.
39367 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
39368 *    a RegExp found by evaluating the Angular expression given in the attribute value.
39369 *    If the expression evaluates to a RegExp object, then this is used directly.
39370 *    If the expression evaluates to a string, then it will be converted to a RegExp
39371 *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
39372 *    `new RegExp('^abc$')`.<br />
39373 *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
39374 *    start at the index of the last search's match, thus not taking the whole input value into
39375 *    account.
39376 * @param {string=} ngChange Angular expression to be executed when input changes due to user
39377 *    interaction with the input element.
39378 * @param {boolean=} [ngTrim=true] If set to false Angular
vendor: 4,842 bytes, lines 39378-39496
39378 will not automatically trim the input.
39379 *    This parameter is ignored for input[type=password] controls, which will never trim the
39380 *    input.
39381 *
39382 * @example
39383    <example name="input-directive" module="inputExample">
39384      <file name="index.html">
39385       <script>
39386          angular.module('inputExample', [])
39387            .controller('ExampleController', ['$scope', function($scope) {
39388              $scope.user = {name: 'guest', last: 'visitor'};
39389            }]);
39390       </script>
39391       <div ng-controller="ExampleController">
39392         <form name="myForm">
39393           <label>
39394              User name:
39395              <input type="text" name="userName" ng-model="user.name" required>
39396           </label>
39397           <div role="alert">
39398             <span class="error" ng-show="myForm.userName.$error.required">
39399              Required!</span>
39400           </div>
39401           <label>
39402              Last name:
39403              <input type="text" name="lastName" ng-model="user.last"
39404              ng-minlength="3" ng-maxlength="10">
39405           </label>
39406           <div role="alert">
39407             <span class="error" ng-show="myForm.lastName.$error.minlength">
39408               Too short!</span>
39409             <span class="error" ng-show="myForm.lastName.$error.maxlength">
39410               Too long!</span>
39411           </div>
39412         </form>
39413         <hr>
39414         <tt>user = {{user}}</tt><br/>
39415         <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br/>
39416         <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br/>
39417         <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br/>
39418         <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br/>
39419         <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
39420         <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
39421         <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br/>
39422         <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br/>
39423       </div>
39424      </file>
39425      <file name="protractor.js" type="protractor">
39426        var user = element(by.exactBinding('user'));
39427        var userNameValid = element(by.binding('myForm.userName.$valid'));
39428        var lastNameValid = element(by.binding('myForm.lastName.$valid'));
39429        var lastNameError = element(by.binding('myForm.lastName.$error'));
39430        var formValid = element(by.binding('myForm.$valid'));
39431        var userNameInput = element(by.model('user.name'));
39432        var userLastInput = element(by.model('user.last'));
39433
39434        it('should initialize to model', function() {
39435          expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
39436          expect(userNameValid.getText()).toContain('true');
39437          expect(formValid.getText()).toContain('true');
39438        });
39439
39440        it('should be invalid if empty when required', function() {
39441          userNameInput.clear();
39442          userNameInput.sendKeys('');
39443
39444          expect(user.getText()).toContain('{"last":"visitor"}');
39445          expect(userNameValid.getText()).toContain('false');
39446          expect(formValid.getText()).toContain('false');
39447        });
39448
39449        it('should be valid if empty when min length is set', function() {
39450          userLastInput.clear();
39451          userLastInput.sendKeys('');
39452
39453          expect(user.getText()).toContain('{"name":"guest","last":""}');
39454          expect(lastNameValid.getText()).toContain('true');
39455          expect(formValid.getText()).toContain('true');
39456        });
39457
39458        it('should be invalid if less than required min length', function() {
39459          userLastInput.clear();
39460          userLastInput.sendKeys('xx');
39461
39462          expect(user.getText()).toContain('{"name":"guest"}');
39463          expect(lastNameValid.getText()).toContain('false');
39464          expect(lastNameError.getText()).toContain('minlength');
39465          expect(formValid.getText()).toContain('false');
39466        });
39467
39468        it('should be invalid if longer than max length', function() {
39469          userLastInput.clear();
39470          userLastInput.sendKeys('some ridiculously long name');
39471
39472          expect(user.getText()).toContain('{"name":"guest"}');
39473          expect(lastNameValid.getText()).toContain('false');
39474          expect(lastNameError.getText()).toContain('maxlength');
39475          expect(formValid.getText()).toContain('false');
39476        });
39477      </file>
39478    </example>
39479 */
39480var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
39481    function($browser, $sniffer, $filter, $parse) {
39482  return {
39483    restrict: 'E',
39484    require: ['?ngModel'],
39485    link: {
39486      pre: function(scope, element, attr, ctrls) {
39487        if (ctrls[0]) {
39488          (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
39489                                                              $browser, $filter, $parse);
39490        }
39491      }
39492    }
39493  };
39494}];
39495
39496
39497
39498var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
39499/**
39500 * @ngdoc directive
39501 * @name ngValue
39502 *
39503 * @description
39504 * Binds the given expression to the value of `<option>` or {@link input[radio] `input[radio]`},
39505 * so that when the element is selected, the {@link ngModel `ngModel`} of that element is set to
39506 * the bound value.
39507 *
39508 * `ngValue` is useful when dynamically generating lists of radio buttons using
39509 * {@link ngRepeat `ngRepeat`}, as shown below.
39510 *
39511 * Likewise, `ngValue` can be used to generate `<option>` elements for
39512 * the {@link select `select`} element. In that case however, only strings are supported
39513 * for the `value `attribute, so the resulting `ngModel` will always be a string.
39514 * Support for `select` models with non-string values is available via `ngOptions`.
39515 *
39516 * @element input
39517 * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute
39518 *   of the `input` element
39519 *
39520 * @example
39521    <example name="ngValue-directive" module="valueExample">
39522      <file name="index.html">
39523       <script>
39524          angular.module('valueExample', [])
39525            .controller('ExampleController', ['$scope', function($scope) {
39526              $scope.names = ['pizza', 'unicorns', 'robots'];
39527              $scope.my = { favorite: 'unicorns' };
39528            }]);
39529       </script>
39530        <form ng-controller="ExampleController">
39531          <h2>Which is your favorite?</h2>
39532            <label ng-repeat="name in names" for="{{name}}">
39533              {{name}}
39534              <input type="radio"
39535                     ng-model="my.favorite"
39536                     ng-value="name"
39537                     id="{{name}}"
39538                     name="favorite">
39539            </label>
39540          <div>You chose {{my.favorite}}</div>
39541        </form>
39542      </file>
39543      <file name="protractor.js" type="protractor">
39544        var favorite = element(by.binding('my.favorite'));
39545
39546        it('should initialize to model', function() {
39547          expect(favorite.getText()).toContain('unicorns');
39548        });
39549        it('should bind the values to the inputs', function() {
39550          element.all(by.model('my.favorite')).get(0).click();
39551          expect(favorite.getText()).toContain('pizza');
39552        });
39553      </file>
39554    </example>
39555 */
39556var ngValueDirective = function() {
39557  return {
39558    restrict: 'A',
39559    priority: 100,
39560    compile: function(tpl, tplAttr) {
39561      if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
39562        return function ngValueConstantLink(scope, elm, attr) {
39563          attr.$set('value', scope.$eval(attr.ngValue));
39564        };
39565      } else {
39566        return function ngValueLink(scope, elm, attr) {
39567          scope.$watch(attr.ngValue, function valueWatchAction(value) {
39568            attr.$set('value', value);
39569          });
39570        };
39571      }
39572    }
39573  };
39574};
39575
39576/**
39577 * @ngdoc directive
39578 * @name ngBind
39579 * @restrict AC
39580 *
39581 * @description
39582 * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element
39583 * with the value of a given expression, and to update the text content when the value of that
39584 * expression changes.
39585 *
39586 * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
39587 * `{{ expression }}` which is similar but less verbose.
39588 *
39589 * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily
39590 * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
39591 * element attribute, it makes the bindings invisible to the user while the page is loading.
39592 *
39593 * An alternative solution to this problem would be using the
39594 * {@link ng.directive:ngCloak ngCloak} directive.
39595 *
39596 *
39597 * @element ANY
39598 * @param {expression} ngBind {@link guide/expression Expression} to evaluate.
39599 *
39600 * @example
39601 * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
39602   <example module="bindExample">
39603     <file name="index.html">
39604       <script>
39605         angular.module('bindExample', [])
39606           .controller('ExampleController', ['$scope', function($scope) {
39607             $scope.name = 'Whirled';
39608           }]);
39609       </script>
39610       <div ng-controller="ExampleController">
39611         <label>Enter name: <input type="text" ng-model="name"></label><br>
39612         Hello <span ng-bind="name"></span>!
39613       </div>
39614     </file>
39615     <file name="protractor.js" type="protractor">
39616       it('should check ng-bind', function() {
39617         var nameInput = element(by.model('name'));
39618
39619         expect(element(by.binding('name')).getText()).toBe('Whirled');
39620         nameInput.clear();
39621         nameInput.sendKeys('world');
39622         expect(element(by.binding('name')).getText()).toBe('world');
39623       });
39624     </file>
39625   </example>
39626 */
39627var ngBindDirective = ['$compile', function($compile) {
39628  return {
39629    restrict: 'AC',
39630    compile: function ngBindCompile(templateElement) {
39631      $compile.$$addBindingClass(templateElement);
39632      return function ngBindLink(scope, element, attr) {
39633        $compile.$$addBindingInfo(element, attr.ngBind);
39634        element = element[0];
39635        scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
39636          element.textContent = isUndefined(value) ? '' : value;
39637        });
39638      };
39639    }
39640  };
39641}];
39642
39643
39644/**
39645 * @ngdoc directive
39646 * @name ngBindTemplate
39647 *
39648 * @description
39649 * The `ngBindTemplate` directive specifies that the element
39650 * text content should be replaced with the interpolation of the template
39651 * in the `ngBindTemplate` attribute.
39652 * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}`
39653 * expressions. This directive is needed since some HTML elements
39654 * (such as TITLE and OPTION) cannot contain SPAN elements.
39655 *
39656 * @element ANY
39657 * @param {string} ngBindTemplate template of form
39658 *   <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
39659 *
39660 * @example
39661 * Try it here: enter text in text box and watch the greeting change.
39662   <example module="bindExample">
39663     <file name="index.html">
39664       <script>
39665         angular.module('bindExample', [])
39666           .controller('ExampleController', ['$scope', function($scope) {
39667             $scope.salutation = 'Hello';
39668             $scope.name = 'World';
39669           }]);
39670       </script>
39671       <div ng-controller="ExampleController">
39672        <label>Salutation: <input type="text" ng-model="salutation"></label><br>
39673        <label>Name: <input type="text" ng-model="name"></label><br>
39674        <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
39675       </div>
39676     </file>
39677     <file name="protractor.js" type="protractor">
39678       it('should check ng-bind', function() {
39679         var salutationElem = element(by.binding('salutation'));
39680         var salutationInput = element(by.model('salutation'));
39681         var nameInput = element(by.model('name'));
39682
39683         expect(salutationElem.getText()).toBe('Hello World!');
39684
39685         salutationInput.clear();
39686         salutationInput.sendKeys('Greetings');
39687         nameInput.clear();
39688         nameInput.sendKeys('user');
39689
39690         expect(salutationElem.getText()).toBe('Greetings user!');
39691       });
39692     </file>
39693   </example>
39694 */
39695var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate, $compile) {
39696  return {
39697    compile: function ngBindTemplateCompile(templateElement) {
39698      $compile.$$addBindingClass(templateElement);
39699      return function ngBindTemplateLink(scope, element, attr) {
39700        var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
39701        $compile.$$addBindingInfo(element, interpolateFn.expressions);
39702        element = element[0];
39703        attr.$observe('ngBindTemplate', function(value) {
39704          element.textContent = isUndefined(value) ? '' : value;
39705        });
39706      };
39707    }
39708  };
39709}];
39710
39711
39712/**
39713 * @ngdoc directive
39714 * @name ngBindHtml
39715 *
39716 * @description
39717 * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default,
39718 * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service.
39719 * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link
39720 * ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize}
39721 * in your module's dependencies, you need to include "angular-sanitize.js" in your application.
39722 *
39723 * You may also bypass sanitization for values you know are safe. To do so, bind to
39724 * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}.  See the example
39725 * under {@link ng.$sce#show-me-an-example-using-sce- Strict Contextual Escaping (SCE)}.
39726 *
39727 * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you
39728 * will have an exception (instead of an exploit.)
39729 *
39730 * @element ANY
39731 * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
39732 *
39733 * @example
39734
39735   <example module="bindHtmlExample" deps="angular-sanitize.js">
39736     <file name="index.html">
39737       <div ng-controller="ExampleController">
39738        <p ng-bind-html="myHTML"></p>
39739       </div>
39740     </file>
39741
39742     <file name="script.js">
39743       angular.module('bindHtmlExample', ['ngSanitize'])
39744         .controller('ExampleController', ['$scope', function($scope) {
39745           $scope.myHTML =
39746              'I am an <code>HTML</code>string with ' +
39747              '<a href="#">links!</a> and other <em>stuff</em>';
39748         }]);
39749     </file>
39750
39751     <file name="protractor.js" type="protractor">
39752       it('should check ng-bind-html', function() {
39753         expect(element(by.binding('myHTML')).getText()).toBe(
39754             'I am an HTMLstring with links! and other stuff');
39755       });
39756     </file>
39757   </example>
39758 */
39759var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) {
39760  return {
39761    restrict: 'A',
39762    compile: function ngBindHtmlCompile(tElement, tAttrs) {
39763      var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
39764      var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function getStringValue(value) {
39765        return (value || '').toString();
39766      });
39767      $compile.$$addBindingClass(tElement);
39768
39769      return function ngBindHtmlLink(scope, element, attr) {
39770        $compile.$$addBindingInfo(element, attr.ngBindHtml);
39771
39772        scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
39773          // we re-evaluate the expr because we want a TrustedValueHolderType
39774          // for $sce, not a string
39775          element.html($sce.getTrustedHtml(ngBindHtmlGetter(scope)) || '');
39776        });
39777      };
39778    }
39779  };
39780}];
39781
39782/**
39783 * @ngdoc directive
39784 * @name ngChange
39785 *
39786 * @description
39787 * Evaluate the given expression when the user changes the input.
39788 * The expression is evaluated immediately, unlike the JavaScript onchange event
39789 * which only triggers at the end of a change (usually, when the user leaves the
39790 * form element or presses the return key).
39791 *
39792 * The `ngChange` expression is only evaluated when a change in the input value causes
39793 * a new value to be committed to the model.
39794 *
39795 * It will not be evaluated:
39796 * * if the value returned from the `$parsers` transformation pipeline has not changed
39797 * * if the input has continued to be invalid since the model will stay `null`
39798 * * if the model is changed programmatically and not by a change to the input value
39799 *
39800 *
39801 * Note, this directive requires `ngModel` to be present.
39802 *
39803 * @element input
39804 * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change
39805 * in input value.
39806 *
39807 * @example
39808 * <example name="ngChange-directive" module="changeExample">
39809 *   <file name="index.html">
39810 *     <script>
39811 *       angular.module('changeExample', [])
39812 *         .controller('ExampleController', ['$scope', function($scope) {
39813 *           $scope.counter = 0;
39814 *           $scope.change = function() {
39815 *             $scope.counter++;
39816 *           };
39817 *         }]);
39818 *     </script>
39819 *     <div ng-controller="ExampleController">
39820 *       <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
39821 *       <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
39822 *       <label for="ng-change-example2">Confirmed</label><br />
39823 *       <tt>debug = {{confirmed}}</tt><br/>
39824 *       <tt>counter = {{counter}}</tt><br/>
39825 *     </div>
39826 *   </file>
39827 *   <file name="protractor.js" type="protractor">
39828 *     var counter = element(by.binding('counter'));
39829 *     var debug = element(by.binding('confirmed'));
39830 *
39831 *     it('should evaluate the expression if changing from view', function() {
39832 *       expect(counter.getText()).toContain('0');
39833 *
39834 *       element(by.id('ng-change-example1')).click();
39835 *
39836 *       expect(counter.getText()).toContain('1');
39837 *       expect(debug.getText()).toContain('true');
39838 *     });
39839 *
39840 *     it('should not evaluate the expression if changing from model', function() {
39841 *       element(by.id('ng-change-example2')).click();
39842
39843 *       expect(counter.getText()).toContain('0');
39844 *       expect(debug.getText()).toContain('true');
39845 *     });
39846 *   </file>
39847 * </example>
39848 */
39849var ngChangeDirective = valueFn({
39850  restrict: 'A',
39851  require: 'ngModel',
39852  link: function(scope, element, attr, ctrl) {
39853    ctrl.$viewChangeListeners.push(function() {
39854      scope.$eval(attr.ngChange);
39855    });
39856  }
39857});
39858
39859function classDirective(name, selector) {
39860  name = 'ngClass' + name;
39861  return ['$animate', function($animate) {
39862    return {
39863      restrict: 'AC',
39864      link: function(scope, element, attr) {
39865        var oldVal;
39866
39867        scope.$watch(attr[name], ngClassWatchAction, true);
39868
39869        attr.$observe('class', function(value) {
39870          ngClassWatchAction(scope.$eval(attr[name]));
39871        });
39872
39873
39874        if (name !== 'ngClass') {
39875          scope.$watch('$index', function($index, old$index) {
39876            // jshint bitwise: false
39877            var mod = $index & 1;
39878            if (mod !== (old$index & 1)) {
39879              var classes = arrayClasses(scope.$eval(attr[name]));
39880              mod === selector ?
39881                addClasses(classes) :
39882                removeClasses(classes);
39883            }
39884          });
39885        }
39886
39887        function addClasses(classes) {
39888          var newClasses = digestClassCounts(classes, 1);
39889          attr.$addClass(newClasses);
39890        }
39891
39892        function removeClasses(classes) {
39893          var newClasses = digestClassCounts(classes, -1);
39894          attr.$removeClass(newClasses);
39895        }
39896
39897        function digestClassCounts(classes, count) {
39898          // Use createMap() to prevent class assumptions involving property
39899          // names in Object.prototype
39900          var classCounts = element.data('$classCounts') || createMap();
39901          var classesToUpdate = [];
39902          forEach(classes, function(className) {
39903            if (count > 0 || classCounts[className]) {
39904              classCounts[className] = (classCounts[className] || 0) + count;
39905              if (classCounts[className] === +(count > 0)) {
39906                classesToUpdate.push(className);
39907              }
39908            }
39909          });
39910          element.data('$classCounts', classCounts);
39911          return classesToUpdate.join(' ');
39912        }
39913
39914        function updateClasses(oldClasses, newClasses) {
39915          var toAdd = arrayDifference(newClasses, oldClasses);
39916          var toRemove = arrayDifference(oldClasses, newClasses);
39917          toAdd = digestClassCounts(toAdd, 1);
39918          toRemove = digestClassCounts(toRemove, -1);
39919          if (toAdd && toAdd.length) {
39920            $animate.addClass(element, toAdd);
39921          }
39922          if (toRemove && toRemove.length) {
39923            $animate.removeClass(element, toRemove);
39924          }
39925        }
39926
39927        function ngClassWatchAction(newVal) {
39928          if (selector === true || scope.$index % 2 === selector) {
39929            var newClasses = arrayClasses(newVal || []);
39930            if (!oldVal) {
39931              addClasses(newClasses);
39932            } else if (!equals(newVal,oldVal)) {
39933              var oldClasses = arrayClasses(oldVal);
39934              updateClasses(oldClasses, newClasses);
39935            }
39936          }
39937          oldVal = shallowCopy(newVal);
39938        }
39939      }
39940    };
39941
39942    function arrayDifference(tokens1, tokens2) {
39943      var values = [];
39944
39945      outer:
39946      for (var i = 0; i < tokens1.length; i++) {
39947        var token = tokens1[i];
39948        for (var j = 0; j < tokens2.length; j++) {
39949          if (token == tokens2[j]) continue outer;
39950        }
39951        values.push(token);
39952      }
39953      return values;
39954    }
39955
39956    function arrayClasses(classVal) {
39957      var classes = [];
39958      if (isArray(classVal)) {
39959        forEach(classVal, function(v) {
39960          classes = classes.concat(arrayClasses(v));
39961        });
39962        return classes;
39963      } else if (isString(classVal)) {
39964        return classVal.split(' ');
39965      } else if (isObject(classVal)) {
39966        forEach(classVal, function(v, k) {
39967          if (v) {
39968            classes = classes.concat(k.split(' '));
39969          }
39970        });
39971        return classes;
39972      }
39973      return classVal;
39974    }
39975  }];
39976}
39977
39978/**
39979 * @ngdoc directive
39980 * @name ngClass
39981 * @restrict AC
39982 *
39983 * @description
39984 * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding
39985 * an expression that represents all classes to be added.
39986 *
39987 * The directive operates in three different ways, depending on which of three types the expression
39988 * evaluates to:
39989 *
39990 * 1. If the expression evaluates to a string, the string should be one or more space-delimited class
39991 * names.
39992 *
39993 * 2. If the expression evaluates to an object, then for each key-value pair of the
39994 * object with a truthy value the corresponding key is used as a class name.
39995 *
39996 * 3. If the expression evaluates to an array, each element of the array should either be a string as in
39997 * type 1 or an object as in type 2. This means that you can mix strings and objects together in an array
39998 * to give you more control over what CSS classes appear. See the code below for an example of this.
39999 *
40000 *
40001 * The directive won't add duplicate classes if a particular class was already set.
40002 *
40003 * When the expression changes, the previously added classes are removed and only then are the
40004 * new classes added.
40005 *
40006 * @animations
40007 * **add** - happens just before the class is applied to the elements
40008 *
40009 * **remove** - happens just before the class is removed from the element
40010 *
40011 * @element ANY
40012 * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
40013 *   of the evaluation can be a string representing space delimited class
40014 *   names, an array, or a map of class names to boolean values. In the case of a map, the
40015 *   names of the properties whose values are truthy will be added as css classes to the
40016 *   element.
40017 *
40018 * @example Example that demonstrates basic bindings via ngClass directive.
40019   <example>
40020     <file name="index.html">
40021       <p ng-class="{strike: deleted, bold: important, 'has-error': error}">Map Syntax Example</p>
40022       <label>
40023          <input type="checkbox" ng-model="deleted">
40024          deleted (apply "strike" class)
40025       </label><br>
40026       <label>
40027          <input type="checkbox" ng-model="important">
40028          important (apply "bold" class)
40029       </label><br>
40030       <label>
40031          <input type="checkbox" ng-model="error">
40032          error (apply "has-error" class)
40033       </label>
40034       <hr>
40035       <p ng-class="style">Using String Syntax</p>
40036       <input type="text" ng-model="style"
40037              placeholder="Type: bold strike red" aria-label="Type: bold strike red">
40038       <hr>
40039       <p ng-class="[style1, style2, style3]">Using Array Syntax</p>
40040       <input ng-model="style1"
40041              placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red"><br>
40042       <input ng-model="style2"
40043              placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 2"><br>
40044       <input ng-model="style3"
40045              placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 3"><br>
40046       <hr>
40047       <p ng-class="[style4, {orange: warning}]">Using Array and Map Syntax</p>
40048       <input ng-model="style4" placeholder="Type: bold, strike" aria-label="Type: bold, strike"><br>
40049       <label><input type="checkbox" ng-model="warning"> warning (apply "orange" class)</label>
40050     </file>
40051     <file name="style.css">
40052       .strike {
40053           text-decoration: line-through;
40054       }
40055       .bold {
40056           font-weight: bold;
40057       }
40058       .red {
40059           color: red;
40060       }
40061       .has-error {
40062           color: red;
40063           background-color: yellow;
40064       }
40065       .orange {
40066           color: orange;
40067       }
40068     </file>
40069     <file name="protractor.js" type="protractor">
40070       var ps = element.all(by.css('p'));
40071
40072       it('should let you toggle the class', function() {
40073
40074         expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
40075         expect(ps.first().getAttribute('class')).not.toMatch(/has-error/);
40076
40077         element(by.model('important')).click();
40078         expect(ps.first().getAttribute('class')).toMatch(/bold/);
40079
40080         element(by.model('error')).click();
40081         expect(ps.first().getAttribute('class')).toMatch(/has-error/);
40082       });
40083
40084       it('should let you toggle string example', function() {
40085         expect(ps.get(1).getAttribute('class')).toBe('');
40086         element(by.model('style')).clear();
40087         element(by.model('style')).sendKeys('red');
40088         expect(ps.get(1).getAttribute('class')).toBe('red');
40089       });
40090
40091       it('array example should have 3 classes', function() {
40092         expect(ps.get(2).getAttribute('class')).toBe('');
40093         element(by.model('style1')).sendKeys('bold');
40094         element(by.model('style2')).sendKeys('strike');
40095         element(by.model('style3')).sendKeys('red');
40096         expect(ps.get(2).getAttribute('class')).toBe('bold strike red');
40097       });
40098
40099       it('array with map example should have 2 classes', function() {
40100         expect(ps.last().getAttribute('class')).toBe('');
40101         element(by.model('style4')).sendKeys('bold');
40102         element(by.model('warning')).click();
40103         expect(ps.last().getAttribute('class')).toBe('bold orange');
40104       });
40105     </file>
40106   </example>
40107
40108   ## Animations
40109
40110   The example below demonstrates how to perform animations using ngClass.
40111
40112   <example module="ngAnimate" deps="angular-animate.js" animations="true">
40113     <file name="index.html">
40114      <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
40115      <input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
40116      <br>
40117      <span class="base-class" ng-class="myVar">Sample Text</span>
40118     </file>
40119     <file name="style.css">
40120       .base-class {
40121         transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
40122       }
40123
40124       .base-class.my-class {
40125         color: red;
40126         font-size:3em;
40127       }
40128     </file>
40129     <file name="protractor.js" type="protractor">
40130       it('should check ng-class', function() {
40131         expect(element(by.css('.base-class')).getAttribute('class')).not.
40132           toMatch(/my-class/);
40133
40134         element(by.id('setbtn')).click();
40135
40136         expect(element(by.css('.base-class')).getAttribute('class')).
40137           toMatch(/my-class/);
40138
40139         element(by.id('clearbtn')).click();
40140
40141         expect(element(by.css('.base-class')).getAttribute('class')).not.
40142           toMatch(/my-class/);
40143       });
40144     </file>
40145   </example>
40146
40147
40148   ## ngClass and pre-existing CSS3 Transitions/Animations
40149   The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
40150   Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder
40151   any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure
40152   to view the step by step details of {@link $animate#addClass $animate.addClass} and
40153   {@link $animate#removeClass $animate.removeClass}.
40154 */
40155var ngClassDirective = classDirective('', true);
40156
40157/**
40158 * @ngdoc directive
40159 * @name ngClassOdd
40160 * @restrict AC
40161 *
40162 * @description
40163 * The `ngClassOdd` and `ngClassEven` directives work exactly as
40164 * {@link ng.directive:ngClass ngClass}, except they work in
40165 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
40166 *
40167 * This directive can be applied only within the scope of an
40168 * {@link ng.directive:ngRepeat ngRepeat}.
40169 *
40170 * @element ANY
40171 * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
40172 *   of the evaluation can be a string representing space delimited class names or an array.
40173 *
40174 * @example
40175   <example>
40176     <file name="index.html">
40177        <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
40178          <li ng-repeat="name in names">
40179           <span ng-class-odd="'odd'" ng-class-even="'even'">
40180             {{name}}
40181           </span>
40182          </li>
40183        </ol>
40184     </file>
40185     <file name="style.css">
40186       .odd {
40187         color: red;
40188       }
40189       .even {
40190         color: blue;
40191       }
40192     </file>
40193     <file name="protractor.js" type="protractor">
40194       it('should check ng-class-odd and ng-class-even', function() {
40195         expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
40196           toMatch(/odd/);
40197         expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
40198           toMatch(/even/);
40199       });
40200     </file>
40201   </example>
40202 */
40203var ngClassOddDirective = classDirective('Odd', 0);
40204
40205/**
40206 * @ngdoc directive
40207 * @name ngClassEven
40208 * @restrict AC
40209 *
40210 * @description
40211 * The `ngClassOdd` and `ngClassEven` directives work exactly as
40212 * {@link ng.directive:ngClass ngClass}, except they work in
40213 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
40214 *
40215 * This directive can be applied only within the scope of an
40216 * {@link ng.directive:ngRepeat ngRepeat}.
40217 *
40218 * @element ANY
40219 * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
40220 *   result of the evaluation can be a string representing space delimited class names or an array.
40221 *
40222 * @example
40223   <example>
40224     <file name="index.html">
40225        <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
40226          <li ng-repeat="name in names">
40227           <span ng-class-odd="'odd'" ng-class-even="'even'">
40228             {{name}} &nbsp; &nbsp; &nbsp;
40229           </span>
40230          </li>
40231        </ol>
40232     </file>
40233     <file name="style.css">
40234       .odd {
40235         color: red;
40236       }
40237       .even {
40238         color: blue;
40239       }
40240     </file>
40241     <file name="protractor.js" type="protractor">
40242       it('should check ng-class-odd and ng-class-even', function() {
40243         expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
40244           toMatch(/odd/);
40245         expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
40246           toMatch(/even/);
40247       });
40248     </file>
40249   </example>
40250 */
40251var ngClassEvenDirective = classDirective('Even', 1);
40252
40253/**
40254 * @ngdoc directive
40255 * @name ngCloak
40256 * @restrict AC
40257 *
40258 * @description
40259 * The `ngCloak` directive is used to prevent the Angular html template from being briefly
40260 * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
40261 * directive to avoid the undesirable flicker effect caused by the html template display.
40262 *
40263 * The directive can be applied to the `<body>` element, but the preferred usage is to apply
40264 * multiple `ngCloak` directives to small portions of the page to permit progressive rendering
40265 * of the browser view.
40266 *
40267 * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and
40268 * `angular.min.js`.
40269 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
40270 *
40271 * ```css
40272 * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
40273 *   display: none !important;
40274 * }
40275 * ```
40276 *
40277 * When this css rule is loaded by the browser, all html elements (including their children) that
40278 * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive
40279 * during the compilation of the template it deletes the `ngCloak` element attribute, making
40280 * the compiled element visible.
40281 *
40282 * For the best result, the `angular.js` script must be loaded in the head section of the html
40283 * document; alternatively, the css rule above must be included in the external stylesheet of the
40284 * application.
40285 *
40286 * @element ANY
40287 *
40288 * @example
40289   <example>
40290     <file name="index.html">
40291        <div id="template1" ng-cloak>{{ 'hello' }}</div>
40292        <div id="template2" class="ng-cloak">{{ 'world' }}</div>
40293     </file>
40294     <file name="protractor.js" type="protractor">
40295       it('should remove the template directive and css class', function() {
40296         expect($('#template1').getAttribute('ng-cloak')).
40297           toBeNull();
40298         expect($('#template2').getAttribute('ng-cloak')).
40299           toBeNull();
40300       });
40301     </file>
40302   </example>
40303 *
40304 */
40305var ngCloakDirective = ngDirective({
40306  compile: function(element, attr) {
40307    attr.$set('ngCloak', undefined);
40308    element.removeClass('ng-cloak');
40309  }
40310});
40311
40312/**
40313 * @ngdoc directive
40314 * @name ngController
40315 *
40316 * @description
40317 * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular
40318 * supports the principles behind the Model-View-Controller design pattern.
40319 *
40320 * MVC components in angular:
40321 *
40322 * * Model — Models are the properties of a scope; scopes are attached to the DOM where scope properties
40323 *   are accessed through bindings.
40324 * * View — The template (HTML with data bindings) that is rendered into the View.
40325 * * Controller — The `ngController` directive specifies a Controller class; the class contains business
40326 *   logic behind the application to decorate the scope with functions and values
40327 *
40328 * Note that you can also attach controllers to the DOM by declaring it in a route definition
40329 * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller
40330 * again using `ng-controller` in the template itself.  This will cause the controller to be attached
40331 * and executed twice.
40332 *
40333 * @element ANY
40334 * @scope
40335 * @priority 500
40336 * @param {expression} ngController Name of a constructor function registered with the current
40337 * {@link ng.$controllerProvider $controllerProvider} or an {@link guide/expression expression}
40338 * that on the current scope evaluates to a constructor function.
40339 *
40340 * The controller instance can be published into a scope property by specifying
40341 * `ng-controller="as propertyName"`.
40342 *
40343 * If the current `$controllerProvider` is configured to use globals (via
40344 * {@link ng.$controllerProvider#allowGlobals `$controllerProvider.allowGlobals()` }), this may
40345 * also be the name of a globally accessible constructor function (not recommended).
40346 *
40347 * @example
40348 * Here is a simple form for editing user contact information. Adding, remov
40348ing, clearing, and
40349 * greeting are methods declared on the controller (see source tab). These methods can
40350 * easily be called from the angular markup. Any changes to the data are automatically reflected
40351 * in the View without the need for a manual update.
40352 *
40353 * Two different declaration styles are included below:
40354 *
40355 * * one binds methods and properties directly onto the controller using `this`:
40356 * `ng-controller="SettingsController1 as settings"`
40357 * * one injects `$scope` into the controller:
40358 * `ng-controller="SettingsController2"`
40359 *
40360 * The second option is more common in the Angular community, and is generally used in boilerplates
40361 * and in this guide. However, there are advantages to binding properties directly to the controller
40362 * and avoiding scope.
40363 *
40364 * * Using `controller as` makes it obvious which controller you are accessing in the template when
40365 * multiple controllers apply to an element.
40366 * * If you are writing your controllers as classes you have easier access to the properties and
40367 * methods, which will appear on the scope, from inside the controller code.
40368 * * Since there is always a `.` in the bindings, you don't have to worry about prototypal
40369 * inheritance masking primitives.
40370 *
40371 * This example demonstrates the `controller as` syntax.
40372 *
40373 * <example name="ngControllerAs" module="controllerAsExample">
40374 *   <file name="index.html">
40375 *    <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings">
40376 *      <label>Name: <input type="text" ng-model="settings.name"/></label>
40377 *      <button ng-click="settings.greet()">greet</button><br/>
40378 *      Contact:
40379 *      <ul>
40380 *        <li ng-repeat="contact in settings.contacts">
40381 *          <select ng-model="contact.type" aria-label="Contact method" id="select_{{$index}}">
40382 *             <option>phone</option>
40383 *             <option>email</option>
40384 *          </select>
40385 *          <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
40386 *          <button ng-click="settings.clearContact(contact)">clear</button>
40387 *          <button ng-click="settings.removeContact(contact)" aria-label="Remove">X</button>
40388 *        </li>
40389 *        <li><button ng-click="settings.addContact()">add</button></li>
40390 *     </ul>
40391 *    </div>
40392 *   </file>
40393 *   <file name="app.js">
40394 *    angular.module('controllerAsExample', [])
40395 *      .controller('SettingsController1', SettingsController1);
40396 *
40397 *    function SettingsController1() {
40398 *      this.name = "John Smith";
40399 *      this.contacts = [
40400 *        {type: 'phone', value: '408 555 1212'},
40401 *        {type: 'email', value: '[email protected]'} ];
40402 *    }
40403 *
40404 *    SettingsController1.prototype.greet = function() {
40405 *      alert(this.name);
40406 *    };
40407 *
40408 *    SettingsController1.prototype.addContact = function() {
40409 *      this.contacts.push({type: 'email', value: '[email protected]'});
40410 *    };
40411 *
40412 *    SettingsController1.prototype.removeContact = function(contactToRemove) {
40413 *     var index = this.contacts.indexOf(contactToRemove);
40414 *      this.contacts.splice(index, 1);
40415 *    };
40416 *
40417 *    SettingsController1.prototype.clearContact = function(contact) {
40418 *      contact.type = 'phone';
40419 *      contact.value = '';
40420 *    };
40421 *   </file>
40422 *   <file name="protractor.js" type="protractor">
40423 *     it('should check controller as', function() {
40424 *       var container = element(by.id('ctrl-as-exmpl'));
40425 *         expect(container.element(by.model('settings.name'))
40426 *           .getAttribute('value')).toBe('John Smith');
40427 *
40428 *       var firstRepeat =
40429 *           container.element(by.repeater('contact in settings.contacts').row(0));
40430 *       var secondRepeat =
40431 *           container.element(by.repeater('contact in settings.contacts').row(1));
40432 *
40433 *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
40434 *           .toBe('408 555 1212');
40435 *
40436 *       expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
40437 *           .toBe('[email protected]');
40438 *
40439 *       firstRepeat.element(by.buttonText('clear')).click();
40440 *
40441 *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
40442 *           .toBe('');
40443 *
40444 *       container.element(by.buttonText('add')).click();
40445 *
40446 *       expect(container.element(by.repeater('contact in settings.contacts').row(2))
40447 *           .element(by.model('contact.value'))
40448 *           .getAttribute('value'))
40449 *           .toBe('[email protected]');
40450 *     });
40451 *   </file>
40452 * </example>
40453 *
40454 * This example demonstrates the "attach to `$scope`" style of controller.
40455 *
40456 * <example name="ngController" module="controllerExample">
40457 *  <file name="index.html">
40458 *   <div id="ctrl-exmpl" ng-controller="SettingsController2">
40459 *     <label>Name: <input type="text" ng-model="name"/></label>
40460 *     <button ng-click="greet()">greet</button><br/>
40461 *     Contact:
40462 *     <ul>
40463 *       <li ng-repeat="contact in contacts">
40464 *         <select ng-model="contact.type" id="select_{{$index}}">
40465 *            <option>phone</option>
40466 *            <option>email</option>
40467 *         </select>
40468 *         <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
40469 *         <button ng-click="clearContact(contact)">clear</button>
40470 *         <button ng-click="removeContact(contact)">X</button>
40471 *       </li>
40472 *       <li>[ <button ng-click="addContact()">add</button> ]</li>
40473 *    </ul>
40474 *   </div>
40475 *  </file>
40476 *  <file name="app.js">
40477 *   angular.module('controllerExample', [])
40478 *     .controller('SettingsController2', ['$scope', SettingsController2]);
40479 *
40480 *   function SettingsController2($scope) {
40481 *     $scope.name = "John Smith";
40482 *     $scope.contacts = [
40483 *       {type:'phone', value:'408 555 1212'},
40484 *       {type:'email', value:'[email protected]'} ];
40485 *
40486 *     $scope.greet = function() {
40487 *       alert($scope.name);
40488 *     };
40489 *
40490 *     $scope.addContact = function() {
40491 *       $scope.contacts.push({type:'email', value:'[email protected]'});
40492 *     };
40493 *
40494 *     $scope.removeContact = function(contactToRemove) {
40495 *       var index = $scope.contacts.indexOf(contactToRemove);
40496 *       $scope.contacts.splice(index, 1);
40497 *     };
40498 *
40499 *     $scope.clearContact = function(contact) {
40500 *       contact.type = 'phone';
40501 *       contact.value = '';
40502 *     };
40503 *   }
40504 *  </file>
40505 *  <file name="protractor.js" type="protractor">
40506 *    it('should check controller', function() {
40507 *      var container = element(by.id('ctrl-exmpl'));
40508 *
40509 *      expect(container.element(by.model('name'))
40510 *          .getAttribute('value')).toBe('John Smith');
40511 *
40512 *      var firstRepeat =
40513 *          container.element(by.repeater('contact in contacts').row(0));
40514 *      var secondRepeat =
40515 *          container.element(by.repeater('contact in contacts').row(1));
40516 *
40517 *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
40518 *          .toBe('408 555 1212');
40519 *      expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
40520 *          .toBe('[email protected]');
40521 *
40522 *      firstRepeat.element(by.buttonText('clear')).click();
40523 *
40524 *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
40525 *          .toBe('');
40526 *
40527 *      container.element(by.buttonText('add')).click();
40528 *
40529 *      expect(container.element(by.repeater('contact in contacts').row(2))
40530 *          .element(by.model('contact.value'))
40531 *          .getAttribute('value'))
40532 *          .toBe('[email protected]');
40533 *    });
40534 *  </file>
40535 *</example>
40536
40537 */
40538var ngControllerDirective = [function() {
40539  return {
40540    restrict: 'A',
40541    scope: true,
40542    controller: '@',
40543    priority: 500
40544  };
40545}];
40546
40547/**
40548 * @ngdoc directive
40549 * @name ngCsp
40550 *
40551 * @element html
40552 * @description
40553 *
40554 * Angular has some features that can break certain
40555 * [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) rules.
40556 *
40557 * If you intend to implement these rules then you must tell Angular not to use these features.
40558 *
40559 * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps.
40560 *
40561 *
40562 * The following rules affect Angular:
40563 *
40564 * * `unsafe-eval`: this rule forbids apps to use `eval` or `Function(string)` generated functions
40565 * (among other things). Angular makes use of this in the {@link $parse} service to provide a 30%
40566 * increase in the speed of evaluating Angular expressions.
40567 *
40568 * * `unsafe-inline`: this rule forbids apps from inject custom styles into the document. Angular
40569 * makes use of this to include some CSS rules (e.g. {@link ngCloak} and {@link ngHide}).
40570 * To make these directives work when a CSP rule is blocking inline styles, you must link to the
40571 * `angular-csp.css` in your HTML manually.
40572 *
40573 * If you do not provide `ngCsp` then Angular tries to autodetect if CSP is blocking unsafe-eval
40574 * and automatically deactivates this feature in the {@link $parse} service. This autodetection,
40575 * however, triggers a CSP error to be logged in the console:
40576 *
40577 * ```
40578 * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
40579 * script in the following Content Security Policy directive: "default-src 'self'". Note that
40580 * 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
40581 * ```
40582 *
40583 * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`
40584 * directive on an element of the HTML document that appears before the `<script>` tag that loads
40585 * the `angular.js` file.
40586 *
40587 * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
40588 *
40589 * You can specify which of the CSP related Angular features should be deactivated by providing
40590 * a value for the `ng-csp` attribute. The options are as follows:
40591 *
40592 * * no-inline-style: this stops Angular from injecting CSS styles into the DOM
40593 *
40594 * * no-unsafe-eval: this stops Angular from optimising $parse with unsafe eval of strings
40595 *
40596 * You can use these values in the following combinations:
40597 *
40598 *
40599 * * No declaration means that Angular will assume that you can do inline styles, but it will do
40600 * a runtime check for unsafe-eval. E.g. `<body>`. This is backwardly compatible with previous versions
40601 * of Angular.
40602 *
40603 * * A simple `ng-csp` (or `data-ng-csp`) attribute will tell Angular to deactivate both inline
40604 * styles and unsafe eval. E.g. `<body ng-csp>`. This is backwardly compatible with previous versions
40605 * of Angular.
40606 *
40607 * * Specifying only `no-unsafe-eval` tells Angular that we must not use eval, but that we can inject
40608 * inline styles. E.g. `<body ng-csp="no-unsafe-eval">`.
40609 *
40610 * * Specifying only `no-inline-style` tells Angular that we must not inject styles, but that we can
40611 * run eval - no automcatic check for unsafe eval will occur. E.g. `<body ng-csp="no-inline-style">`
40612 *
40613 * * Specifying both `no-unsafe-eval` and `no-inline-style` tells Angular that we must not inject
40614 * styles nor use eval, which is the same as an empty: ng-csp.
40615 * E.g.`<body ng-csp="no-inline-style;no-unsafe-eval">`
40616 *
40617 * @example
40618 * This example shows how to apply the `ngCsp` directive to the `html` tag.
40619   ```html
40620     <!doctype html>
40621     <html ng-app ng-csp>
40622     ...
40623     ...
40624     </html>
40625   ```
40626  * @example
40627      // Note: the suffix `.csp` in the example name triggers
40628      // csp mode in our http server!
40629      <example name="example.csp" module="cspExample" ng-csp="true">
40630        <file name="index.html">
40631          <div ng-controller="MainController as ctrl">
40632            <div>
40633              <button ng-click="ctrl.inc()" id="inc">Increment</button>
40634              <span id="counter">
40635                {{ctrl.counter}}
40636              </span>
40637            </div>
40638
40639            <div>
40640              <button ng-click="ctrl.evil()" id="evil">Evil</button>
40641              <span id="evilError">
40642                {{ctrl.evilError}}
40643              </span>
40644            </div>
40645          </div>
40646        </file>
40647        <file name="script.js">
40648           angular.module('cspExample', [])
40649             .controller('MainController', function() {
40650                this.counter = 0;
40651                this.inc = function() {
40652                  this.counter++;
40653                };
40654                this.evil = function() {
40655                  // jshint evil:true
40656                  try {
40657                    eval('1+2');
40658                  } catch (e) {
40659                    this.evilError = e.message;
40660                  }
40661                };
40662              });
40663        </file>
40664        <file name="protractor.js" type="protractor">
40665          var util, webdriver;
40666
40667          var incBtn = element(by.id('inc'));
40668          var counter = element(by.id('counter'));
40669          var evilBtn = element(by.id('evil'));
40670          var evilError = element(by.id('evilError'));
40671
40672          function getAndClearSevereErrors() {
40673            return browser.manage().logs().get('browser').then(function(browserLog) {
40674              return browserLog.filter(function(logEntry) {
40675                return logEntry.level.value > webdriver.logging.Level.WARNING.value;
40676              });
40677            });
40678          }
40679
40680          function clearErrors() {
40681            getAndClearSevereErrors();
40682          }
40683
40684          function expectNoErrors() {
40685            getAndClearSevereErrors().then(function(filteredLog) {
40686              expect(filteredLog.length).toEqual(0);
40687              if (filteredLog.length) {
40688                console.log('browser console errors: ' + util.inspect(filteredLog));
40689              }
40690            });
40691          }
40692
40693          function expectError(regex) {
40694            getAndClearSevereErrors().then(function(filteredLog) {
40695              var found = false;
40696              filteredLog.forEach(function(log) {
40697                if (log.message.match(regex)) {
40698                  found = true;
40699                }
40700              });
40701              if (!found) {
40702                throw new Error('expected an error that matches ' + regex);
40703              }
40704            });
40705          }
40706
40707          beforeEach(function() {
40708            util = require('util');
40709            webdriver = require('protractor/node_modules/selenium-webdriver');
40710          });
40711
40712          // For now, we only test on Chrome,
40713          // as Safari does not load the page with Protractor's injected scripts,
40714          // and Firefox webdriver always disables content security policy (#6358)
40715          if (browser.params.browser !== 'chrome') {
40716            return;
40717          }
40718
40719          it('should not report errors when the page is loaded', function() {
40720            // clear errors so we are not dependent on previous tests
40721            clearErrors();
40722            // Need to reload the page as the page is already loaded when
40723            // we come here
40724            browser.driver.getCurrentUrl().then(function(url) {
40725              browser.get(url);
40726            });
40727            expectNoErrors();
40728          });
40729
40730          it('should evaluate expressions', function() {
40731            expect(counter.getText()).toEqual('0');
40732            incBtn.click();
40733            expect(counter.getText()).toEqual('1');
40734            expectNoErrors();
40735          });
40736
40737          it('should throw and report an error when using "eval"', function() {
40738            evilBtn.click();
40739            expect(evilError.getText()).toMatch(/Content Security Policy/);
40740            expectError(/Content Security Policy/);
40741          });
40742        </file>
40743      </example>
40744  */
40745
40746// ngCsp is not implemented as a proper directive any more, because we need it be processed while we
40747// bootstrap the system (before $parse is instantiated), for this reason we just have
40748// the csp() fn that looks for the `ng-csp` attribute anywhere in the current doc
40749
40750/**
40751 * @ngdoc directive
40752 * @name ngClick
40753 *
40754 * @description
40755 * The ngClick directive allows you to specify custom behavior when
40756 * an element is clicked.
40757 *
40758 * @element ANY
40759 * @priority 0
40760 * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
40761 * click. ({@link guide/expression#-event- Event object is available as `$event`})
40762 *
40763 * @example
40764   <example>
40765     <file name="index.html">
40766      <button ng-click="count = count + 1" ng-init="count=0">
40767        Increment
40768      </button>
40769      <span>
40770        count: {{count}}
40771      </span>
40772     </file>
40773     <file name="protractor.js" type="protractor">
40774       it('should check ng-click', function() {
40775         expect(element(by.binding('count')).getText()).toMatch('0');
40776         element(by.css('button')).click();
40777         expect(element(by.binding('count')).getText()).toMatch('1');
40778       });
40779     </file>
40780   </example>
40781 */
40782/*
40783 * A collection of directives that allows creation of custom event handlers that are defined as
40784 * angular expressions and are compiled and executed within the current scope.
40785 */
40786var ngEventDirectives = {};
40787
40788// For events that might fire synchronously during DOM manipulation
40789// we need to execute their event handlers asynchronously using $evalAsync,
40790// so that they are not executed in an inconsistent state.
40791var forceAsyncEvents = {
40792  'blur': true,
40793  'focus': true
40794};
40795forEach(
40796  'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
40797  function(eventName) {
40798    var directiveName = directiveNormalize('ng-' + eventName);
40799    ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
40800      return {
40801        restrict: 'A',
40802        compile: function($element, attr) {
40803          // We expose the powerful $event object on the scope that provides access to the Window,
40804          // etc. that isn't protected by the fast paths in $parse.  We explicitly request better
40805          // checks at the cost of speed since event handler expressions are not executed as
40806          // frequently as regular change detection.
40807          var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true);
40808          return function ngEventHandler(scope, element) {
40809            element.on(eventName, function(event) {
40810              var callback = function() {
40811                fn(scope, {$event:event});
40812              };
40813              if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
40814                scope.$evalAsync(callback);
40815              } else {
40816                scope.$apply(callback);
40817              }
40818            });
40819          };
40820        }
40821      };
40822    }];
40823  }
40824);
40825
40826/**
40827 * @ngdoc directive
40828 * @name ngDblclick
40829 *
40830 * @description
40831 * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
40832 *
40833 * @element ANY
40834 * @priority 0
40835 * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
40836 * a dblclick. (The Event object is available as `$event`)
40837 *
40838 * @example
40839   <example>
40840     <file name="index.html">
40841      <button ng-dblclick="count = count + 1" ng-init="count=0">
40842        Increment (on double click)
40843      </button>
40844      count: {{count}}
40845     </file>
40846   </example>
40847 */
40848
40849
40850/**
40851 * @ngdoc directive
40852 * @name ngMousedown
40853 *
40854 * @description
40855 * The ngMousedown directive allows you to specify custom behavior on mousedown event.
40856 *
40857 * @element ANY
40858 * @priority 0
40859 * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
40860 * mousedown. ({@link guide/expression#-event- Event object is available as `$event`})
40861 *
40862 * @example
40863   <example>
40864     <file name="index.html">
40865      <button ng-mousedown="count = count + 1" ng-init="count=0">
40866        Increment (on mouse down)
40867      </button>
40868      count: {{count}}
40869     </file>
40870   </example>
40871 */
40872
40873
40874/**
40875 * @ngdoc directive
40876 * @name ngMouseup
40877 *
40878 * @description
40879 * Specify custom behavior on mouseup event.
40880 *
40881 * @element ANY
40882 * @priority 0
40883 * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
40884 * mouseup. ({@link guide/expression#-event- Event object is available as `$event`})
40885 *
40886 * @example
40887   <example>
40888     <file name="index.html">
40889      <button ng-mouseup="count = count + 1" ng-init="count=0">
40890        Increment (on mouse up)
40891      </button>
40892      count: {{count}}
40893     </file>
40894   </example>
40895 */
40896
40897/**
40898 * @ngdoc directive
40899 * @name ngMouseover
40900 *
40901 * @description
40902 * Specify custom behavior on mouseover event.
40903 *
40904 * @element ANY
40905 * @priority 0
40906 * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
40907 * mouseover. ({@link guide/expression#-event- Event object is available as `$event`})
40908 *
40909 * @example
40910   <example>
40911     <file name="index.html">
40912      <button ng-mouseover="count = count + 1" ng-init="count=0">
40913        Increment (when mouse is over)
40914      </button>
40915      count: {{count}}
40916     </file>
40917   </example>
40918 */
40919
40920
40921/**
40922 * @ngdoc directive
40923 * @name ngMouseenter
40924 *
40925 * @description
40926 * Specify custom behavior on mouseenter event.
40927 *
40928 * @element ANY
40929 * @priority 0
40930 * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
40931 * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})
40932 *
40933 * @example
40934   <example>
40935     <file name="index.html">
40936      <button ng-mouseenter="count = count + 1" ng-init="count=0">
40937        Increment (when mouse enters)
40938      </button>
40939      count: {{count}}
40940     </file>
40941   </example>
40942 */
40943
40944
40945/**
40946 * @ngdoc directive
40947 * @name ngMouseleave
40948 *
40949 * @description
40950 * Specify custom behavior on mouseleave event.
40951 *
40952 * @element ANY
40953 * @priority 0
40954 * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
40955 * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})
40956 *
40957 * @example
40958   <example>
40959     <file name="index.html">
40960      <button ng-mouseleave="count = count + 1" ng-init="count=0">
40961        Increment (when mouse leaves)
40962      </button>
40963      count: {{count}}
40964     </file>
40965   </example>
40966 */
40967
40968
40969/**
40970 * @ngdoc directive
40971 * @name ngMousemove
40972 *
40973 * @description
40974 * Specify custom behavior on mousemove event.
40975 *
40976 * @element ANY
40977 * @priority 0
40978 * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
40979 * mousemove. ({@link guide/expression#-event- Event object is available as `$event`})
40980 *
40981 * @example
40982   <example>
40983     <file name="index.html">
40984      <button ng-mousemove="count = count + 1" ng-init="count=0">
40985        Increment (when mouse moves)
40986      </button>
40987      count: {{count}}
40988     </file>
40989   </example>
40990 */
40991
40992
40993/**
40994 * @ngdoc directive
40995 * @name ngKeydown
40996 *
40997 * @description
40998 * Specify custom behavior on keydown event.
40999 *
41000 * @element ANY
41001 * @priority 0
41002 * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
41003 * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
41004 *
41005 * @example
41006   <example>
41007     <file name="index.html">
41008      <input ng-keydown="count = count + 1" ng-init="count=0">
41009      key down count: {{count}}
41010     </file>
41011   </example>
41012 */
41013
41014
41015/**
41016 * @ngdoc directive
41017 * @name ngKeyup
41018 *
41019 * @description
41020 * Specify custom behavior on keyup event.
41021 *
41022 * @element ANY
41023 * @priority 0
41024 * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
41025 * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
41026 *
41027 * @example
41028   <example>
41029     <file name="index.html">
41030       <p>Typing in the input box below updates the key count</p>
41031       <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}}
41032
41033       <p>Typing in the input box below updates the keycode</p>
41034       <input ng-keyup="event=$event">
41035       <p>event keyCode: {{ event.keyCode }}</p>
41036       <p>event altKey: {{ event.altKey }}</p>
41037     </file>
41038   </example>
41039 */
41040
41041
41042/**
41043 * @ngdoc directive
41044 * @name ngKeypress
41045 *
41046 * @description
41047 * Specify custom behavior on keypress event.
41048 *
41049 * @element ANY
41050 * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
41051 * keypress. ({@link guide/expression#-event- Event object is available as `$event`}
41052 * and can be interrogated for keyCode, altKey, etc.)
41053 *
41054 * @example
41055   <example>
41056     <file name="index.html">
41057      <input ng-keypress="count = count + 1" ng-init="count=0">
41058      key press count: {{count}}
41059     </file>
41060   </example>
41061 */
41062
41063
41064/**
41065 * @ngdoc directive
41066 * @name ngSubmit
41067 *
41068 * @description
41069 * Enables binding angular expressions to onsubmit events.
41070 *
41071 * Additionally it prevents the default action (which for form means 
41071sending the request to the
41072 * server and reloading the current page), but only if the form does not contain `action`,
41073 * `data-action`, or `x-action` attributes.
41074 *
41075 * <div class="alert alert-warning">
41076 * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and
41077 * `ngSubmit` handlers together. See the
41078 * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}
41079 * for a detailed discussion of when `ngSubmit` may be triggered.
41080 * </div>
41081 *
41082 * @element form
41083 * @priority 0
41084 * @param {expression} ngSubmit {@link guide/expression Expression} to eval.
41085 * ({@link guide/expression#-event- Event object is available as `$event`})
41086 *
41087 * @example
41088   <example module="submitExample">
41089     <file name="index.html">
41090      <script>
41091        angular.module('submitExample', [])
41092          .controller('ExampleController', ['$scope', function($scope) {
41093            $scope.list = [];
41094            $scope.text = 'hello';
41095            $scope.submit = function() {
41096              if ($scope.text) {
41097                $scope.list.push(this.text);
41098                $scope.text = '';
41099              }
41100            };
41101          }]);
41102      </script>
41103      <form ng-submit="submit()" ng-controller="ExampleController">
41104        Enter text and hit enter:
41105        <input type="text" ng-model="text" name="text" />
41106        <input type="submit" id="submit" value="Submit" />
41107        <pre>list={{list}}</pre>
41108      </form>
41109     </file>
41110     <file name="protractor.js" type="protractor">
41111       it('should check ng-submit', function() {
41112         expect(element(by.binding('list')).getText()).toBe('list=[]');
41113         element(by.css('#submit')).click();
41114         expect(element(by.binding('list')).getText()).toContain('hello');
41115         expect(element(by.model('text')).getAttribute('value')).toBe('');
41116       });
41117       it('should ignore empty strings', function() {
41118         expect(element(by.binding('list')).getText()).toBe('list=[]');
41119         element(by.css('#submit')).click();
41120         element(by.css('#submit')).click();
41121         expect(element(by.binding('list')).getText()).toContain('hello');
41122        });
41123     </file>
41124   </example>
41125 */
41126
41127/**
41128 * @ngdoc directive
41129 * @name ngFocus
41130 *
41131 * @description
41132 * Specify custom behavior on focus event.
41133 *
41134 * Note: As the `focus` event is executed synchronously when calling `input.focus()`
41135 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
41136 * during an `$apply` to ensure a consistent state.
41137 *
41138 * @element window, input, select, textarea, a
41139 * @priority 0
41140 * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
41141 * focus. ({@link guide/expression#-event- Event object is available as `$event`})
41142 *
41143 * @example
41144 * See {@link ng.directive:ngClick ngClick}
41145 */
41146
41147/**
41148 * @ngdoc directive
41149 * @name ngBlur
41150 *
41151 * @description
41152 * Specify custom behavior on blur event.
41153 *
41154 * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when
41155 * an element has lost focus.
41156 *
41157 * Note: As the `blur` event is executed synchronously also during DOM manipulations
41158 * (e.g. removing a focussed input),
41159 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
41160 * during an `$apply` to ensure a consistent state.
41161 *
41162 * @element window, input, select, textarea, a
41163 * @priority 0
41164 * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
41165 * blur. ({@link guide/expression#-event- Event object is available as `$event`})
41166 *
41167 * @example
41168 * See {@link ng.directive:ngClick ngClick}
41169 */
41170
41171/**
41172 * @ngdoc directive
41173 * @name ngCopy
41174 *
41175 * @description
41176 * Specify custom behavior on copy event.
41177 *
41178 * @element window, input, select, textarea, a
41179 * @priority 0
41180 * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
41181 * copy. ({@link guide/expression#-event- Event object is available as `$event`})
41182 *
41183 * @example
41184   <example>
41185     <file name="index.html">
41186      <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
41187      copied: {{copied}}
41188     </file>
41189   </example>
41190 */
41191
41192/**
41193 * @ngdoc directive
41194 * @name ngCut
41195 *
41196 * @description
41197 * Specify custom behavior on cut event.
41198 *
41199 * @element window, input, select, textarea, a
41200 * @priority 0
41201 * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
41202 * cut. ({@link guide/expression#-event- Event object is available as `$event`})
41203 *
41204 * @example
41205   <example>
41206     <file name="index.html">
41207      <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
41208      cut: {{cut}}
41209     </file>
41210   </example>
41211 */
41212
41213/**
41214 * @ngdoc directive
41215 * @name ngPaste
41216 *
41217 * @description
41218 * Specify custom behavior on paste event.
41219 *
41220 * @element window, input, select, textarea, a
41221 * @priority 0
41222 * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
41223 * paste. ({@link guide/expression#-event- Event object is available as `$event`})
41224 *
41225 * @example
41226   <example>
41227     <file name="index.html">
41228      <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
41229      pasted: {{paste}}
41230     </file>
41231   </example>
41232 */
41233
41234/**
41235 * @ngdoc directive
41236 * @name ngIf
41237 * @restrict A
41238 * @multiElement
41239 *
41240 * @description
41241 * The `ngIf` directive removes or recreates a portion of the DOM tree based on an
41242 * {expression}. If the expression assigned to `ngIf` evaluates to a false
41243 * value then the element is removed from the DOM, otherwise a clone of the
41244 * element is reinserted into the DOM.
41245 *
41246 * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the
41247 * element in the DOM rather than changing its visibility via the `display` css property.  A common
41248 * case when this difference is significant is when using css selectors that rely on an element's
41249 * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classe
41249s.
41250 *
41251 * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
41252 * is created when the element is restored.  The scope created within `ngIf` inherits from
41253 * its parent scope using
41254 * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance).
41255 * An important implication of this is if `ngModel` is used within `ngIf` to bind to
41256 * a javascript primitive defined in the parent scope. In this case any modifications made to the
41257 * variable within the child scope will override (hide) the value in the parent scope.
41258 *
41259 * Also, `ngIf` recreates elements using their compiled state. An example of this behavior
41260 * is if an element's class attribute is directly modified after it's compiled, using something like
41261 * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
41262 * the added class will be lost because the original compiled state is used to regenerate the element.
41263 *
41264 * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter`
41265 * and `leave` effects.
41266 *
41267 * @animations
41268 * enter - happens just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container
41269 * leave - happens just before the `ngIf` contents are removed from the DOM
41270 *
41271 * @element ANY
41272 * @scope
41273 * @priority 600
41274 * @param {expression} ngIf If the {@link guide/expression expression} is falsy then
41275 *     the element is removed from the DOM tree. If it is truthy a copy of the compiled
41276 *     element is added to the DOM tree.
41277 *
41278 * @example
41279  <example module="ngAnimate" deps="angular-animate.js" animations="true">
41280    <file name="index.html">
41281      <label>Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /></label><br/>
41282      Show when checked:
41283      <span ng-if="checked" class="animate-if">
41284        This is removed when the checkbox is unchecked.
41285      </span>
41286    </file>
41287    <file name="animations.css">
41288      .animate-if {
41289        background:white;
41290        border:1px solid black;
41291        padding:10px;
41292      }
41293
41294      .animate-if.ng-enter, .animate-if.ng-leave {
41295        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
41296      }
41297
41298      .animate-if.ng-enter,
41299      .animate-if.ng-leave.ng-leave-active {
41300        opacity:0;
41301      }
41302
41303      .animate-if.ng-leave,
41304      .animate-if.ng-enter.ng-enter-active {
41305        opacity:1;
41306      }
41307    </file>
41308  </example>
41309 */
41310var ngIfDirective = ['$animate', function($animate) {
41311  return {
41312    multiElement: true,
41313    transclude: 'element',
41314    priority: 600,
41315    terminal: true,
41316    restrict: 'A',
41317    $$tlb: true,
41318    link: function($scope, $element, $attr, ctrl, $transclude) {
41319        var block, childScope, previousElements;
41320        $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
41321
41322          if (value) {
41323            if (!childScope) {
41324              $transclude(function(clone, newScope) {
41325                childScope = newScope;
41326                clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ');
41327                // Note: We only need the first/last node of the cloned nodes.
41328                // However, we need to keep the reference to the jqlite wrapper as it might be changed later
41329                // by a directive with templateUrl when its template arrives.
41330                block = {
41331                  clone: clone
41332                };
41333                $animate.enter(clone, $element.parent(), $element);
41334              });
41335            }
41336          } else {
41337            if (previousElements) {
41338              previousElements.remove();
41339              previousElements = null;
41340            }
41341            if (childScope) {
41342              childScope.$destroy();
41343              childScope = null;
41344            }
41345            if (block) {
41346              previousElements = getBlockNodes(block.clone);
41347              $animate.leave(previousElements).then(function() {
41348                previousElements = null;
41349              });
41350              block = null;
41351            }
41352          }
41353        });
41354    }
41355  };
41356}];
41357
41358/**
41359 * @ngdoc directive
41360 * @name ngInclude
41361 * @restrict ECA
41362 *
41363 * @description
41364 * Fetches, compiles and includes an external HTML fragment.
41365 *
41366 * By default, the template URL is restricted to the same domain and protocol as the
41367 * application document. This is done by calling {@link $sce#getTrustedResourceUrl
41368 * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols
41369 * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or
41370 * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link
41371 * ng.$sce Strict Contextual Escaping}.
41372 *
41373 * In addition, the browser's
41374 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
41375 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
41376 * policy may further restrict whether the template is successfully loaded.
41377 * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://`
41378 * access on some browsers.
41379 *
41380 * @animations
41381 * enter - animation is used to bring new content into the browser.
41382 * leave - animation is used to animate existing content away.
41383 *
41384 * The enter and leave animation occur concurrently.
41385 *
41386 * @scope
41387 * @priority 400
41388 *
41389 * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
41390 *                 make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
41391 * @param {string=} onload Expression to evaluate when a new partial is loaded.
41392 *
41393 * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
41394 *                  $anchorScroll} to scroll the viewport after the content is loaded.
41395 *
41396 *                  - If the attribute is not set, disable scrolling.
41397 *                  - If the attribute is set without value, enable scrolling.
41398 *                  - Otherwise enable scrolling only if the expression evaluates to truthy value.
41399 *
41400 * @example
41401  <example module="includeExample" deps="angular-animate.js" animations="true">
41402    <file name="index.html">
41403     <div ng-controller="ExampleController">
41404       <select ng-model="template" ng-options="t.name for t in templates">
41405        <option value="">(blank)</option>
41406       </select>
41407       url of the template: <code>{{template.url}}</code>
41408       <hr/>
41409       <div class="slide-animate-container">
41410         <div class="slide-animate" ng-include="template.url"></div>
41411       </div>
41412     </div>
41413    </file>
41414    <file name="script.js">
41415      angular.module('includeExample', ['ngAnimate'])
41416        .controller('ExampleController', ['$scope', function($scope) {
41417          $scope.templates =
41418            [ { name: 'template1.html', url: 'template1.html'},
41419              { name: 'template2.html', url: 'template2.html'} ];
41420          $scope.template = $scope.templates[0];
41421        }]);
41422     </file>
41423    <file name="template1.html">
41424      Content of template1.html
41425    </file>
41426    <file name="template2.html">
41427      Content of template2.html
41428    </file>
41429    <file name="animations.css">
41430      .slide-animate-container {
41431        position:relative;
41432        background:white;
41433        border:1px solid black;
41434        height:40px;
41435        overflow:hidden;
41436      }
41437
41438      .slide-animate {
41439        padding:10px;
41440      }
41441
41442      .slide-animate.ng-enter, .slide-animate.ng-leave {
41443        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
41444
41445        position:absolute;
41446        top:0;
41447        left:0;
41448        right:0;
41449        bottom:0;
41450        display:block;
41451        padding:10px;
41452      }
41453
41454      .slide-animate.ng-enter {
41455        top:-50px;
41456      }
41457      .slide-animate.ng-enter.ng-enter-active {
41458        top:0;
41459      }
41460
41461      .slide-animate.ng-leave {
41462        top:0;
41463      }
41464      .slide-animate.ng-leave.ng-leave-active {
41465        top:50px;
41466      }
41467    </file>
41468    <file name="protractor.js" type="protractor">
41469      var templateSelect = element(by.model('template'));
41470      var includeElem = element(by.css('[ng-include]'));
41471
41472      it('should load template1.html', function() {
41473        expect(includeElem.getText()).toMatch(/Content of template1.html/);
41474      });
41475
41476      it('should load template2.html', function() {
41477        if (browser.params.browser == 'firefox') {
41478          // Firefox can't handle using selects
41479          // See https://github.com/angular/protractor/issues/480
41480          return;
41481        }
41482        templateSelect.click();
41483        templateSelect.all(by.css('option')).get(2).click();
41484        expect(includeElem.getText()).toMatch(/Content of template2.html/);
41485      });
41486
41487      it('should change to blank', function() {
41488        if (browser.params.browser == 'firefox') {
41489          // Firefox can't handle using selects
41490          return;
41491        }
41492        templateSelect.click();
41493        templateSelect.all(by.css('option')).get(0).click();
41494        expect(includeElem.isPresent()).toBe(false);
41495      });
41496    </file>
41497  </example>
41498 */
41499
41500
41501/**
41502 * @ngdoc event
41503 * @name ngInclude#$includeContentRequested
41504 * @eventType emit on the scope ngInclude was declared in
41505 * @description
41506 * Emitted every time the ngInclude content is requested.
41507 *
41508 * @param {Object} angularEvent Synthetic event object.
41509 * @param {String} src URL of content to load.
41510 */
41511
41512
41513/**
41514 * @ngdoc event
41515 * @name ngInclude#$includeContentLoaded
41516 * @eventType emit on the current ngInclude scope
41517 * @description
41518 * Emitted every time the ngInclude content is reloaded.
41519 *
41520 * @param {Object} angularEvent Synthetic event object.
41521 * @param {String} src URL of content to load.
41522 */
41523
41524
41525/**
41526 * @ngdoc event
41527 * @name ngInclude#$includeContentError
41528 * @eventType emit on the scope ngInclude was declared in
41529 * @description
41530 * Emitted when a template HTTP request yields an erroneous response (status < 200 || status > 299)
41531 *
41532 * @param {Object} angularEvent Synthetic event object.
41533 * @param {String} src URL of content to load.
41534 */
41535var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate',
41536                  function($templateRequest,   $anchorScroll,   $animate) {
41537  return {
41538    restrict: 'ECA',
41539    priority: 400,
41540    terminal: true,
41541    transclude: 'element',
41542    controller: angular.noop,
41543    compile: function(element, attr) {
41544      var srcExp = attr.ngInclude || attr.src,
41545          onloadExp = attr.onload || '',
41546          autoScrollExp = attr.autoscroll;
41547
41548      return function(scope, $element, $attr, ctrl, $transclude) {
41549        var changeCounter = 0,
41550            currentScope,
41551            previousElement,
41552            currentElement;
41553
41554        var cleanupLastIncludeContent = function() {
41555          if (previousElement) {
41556            previousElement.remove();
41557            previousElement = null;
41558          }
41559          if (currentScope) {
41560            currentScope.$destroy();
41561            currentScope = null;
41562          }
41563          if (currentElement) {
41564            $animate.leave(currentElement).then(function() {
41565              previousElement = null;
41566            });
41567            previousElement = currentElement;
41568            currentElement = null;
41569          }
41570        };
41571
41572        scope.$watch(srcExp, function ngIncludeWatchAction(src) {
41573          var afterAnimation = function() {
41574            if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
41575              $anchorScroll();
41576            }
41577          };
41578          var thisChangeId = ++changeCounter;
41579
41580          if (src) {
41581            //set the 2nd param to true to ignore the template request error so that the inner
41582            //contents and scope can be cleaned up.
41583            $templateRequest(src, true).then(function(response) {
41584              if (thisChangeId !== changeCounter) return;
41585              var newScope = scope.$new();
41586              ctrl.template = response;
41587
41588              // Note: This will also link all children of ng-include that were contained in the original
41589              // html. If that content contains controllers, ... they could pollute/change the scope.
41590              // However, using ng-include on an element with additional content does not make sense...
41591              // Note: We can't remove them in the cloneAttchFn of $transclude as that
41592              // function is called before linking the content, which would apply child
41593              // directives to non existing elements.
41594              var clone = $transclude(newScope, function(clone) {
41595                cleanupLastIncludeContent();
41596                $animate.enter(clone, null, $element).then(afterAnimation);
41597              });
41598
41599              currentScope = newScope;
41600              currentElement = clone;
41601
41602              currentScope.$emit('$includeContentLoaded', src);
41603              scope.$eval(onloadExp);
41604            }, function() {
41605              if (thisChangeId === changeCounter) {
41606                cleanupLastIncludeContent();
41607                scope.$emit('$includeContentError', src);
41608              }
41609            });
41610            scope.$emit('$includeContentRequested', src);
41611          } else {
41612            cleanupLastIncludeContent();
41613            ctrl.template = null;
41614          }
41615        });
41616      };
41617    }
41618  };
41619}];
41620
41621// This directive is called during the $transclude call of the first `ngInclude` directive.
41622// It will replace and compile the content of the element with the loaded template.
41623// We need this directive so that the element content is already filled when
41624// the link function of another directive on the same element as ngInclude
41625// is called.
41626var ngIncludeFillContentDirective = ['$compile',
41627  function($compile) {
41628    return {
41629      restrict: 'ECA',
41630      priority: -400,
41631      require: 'ngInclude',
41632      link: function(scope, $element, $attr, ctrl) {
41633        if (/SVG/.test($element[0].toString())) {
41634          // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not
41635          // support innerHTML, so detect this here and try to generate the contents
41636          // specially.
41637          $element.empty();
41638          $compile(jqLiteBuildFragment(ctrl.template, document).childNodes)(scope,
41639              function namespaceAdaptedClone(clone) {
41640            $element.append(clone);
41641          }, {futureParentElement: $element});
41642          return;
41643        }
41644
41645        $element.html(ctrl.template);
41646        $compile($element.contents())(scope);
41647      }
41648    };
41649  }];
41650
41651/**
41652 * @ngdoc directive
41653 * @name ngInit
41654 * @restrict AC
41655 *
41656 * @description
41657 * The `ngInit` directive allows you to evaluate an expression in the
41658 * current scope.
41659 *
41660 * <div class="alert alert-danger">
41661 * This directive can be abused to add unnecessary amounts of logic into your templates.
41662 * There are only a few appropriate uses of `ngInit`, such as for aliasing special properties of
41663 * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below; and for injecting data via
41664 * server side scripting. Besides these few cases, you should use {@link guide/controller controllers}
41665 * rather than `ngInit` to initialize values on a scope.
41666 * </div>
41667 *
41668 * <div class="alert alert-warning">
41669 * **Note**: If you have assignment in `ngInit` along with a {@link ng.$filter `filter`}, make
41670 * sure you have parentheses to ensure correct operator precedence:
41671 * <pre class="prettyprint">
41672 * `<div ng-init="test1 = ($index | toString)"></div>`
41673 * </pre>
41674 * </div>
41675 *
41676 * @priority 450
41677 *
41678 * @element ANY
41679 * @param {expression} ngInit {@link guide/expression Expression} to eval.
41680 *
41681 * @example
41682   <example module="initExample">
41683     <file name="index.html">
41684   <script>
41685     angular.module('initExample', [])
41686       .controller('ExampleController', ['$scope', function($scope) {
41687         $scope.list = [['a', 'b'], ['c', 'd']];
41688       }]);
41689   </script>
41690   <div ng-controller="ExampleController">
41691     <div ng-repeat="innerList in list" ng-init="outerIndex = $index">
41692       <div ng-repeat="value in innerList" ng-init="innerIndex = $index">
41693          <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>
41694       </div>
41695     </div>
41696   </div>
41697     </file>
41698     <file name="protractor.js" type="protractor">
41699       it('should alias index positions', function() {
41700         var elements = element.all(by.css('.example-init'));
41701         expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');
41702         expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');
41703         expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');
41704         expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');
41705       });
41706     </file>
41707   </example>
41708 */
41709var ngInitDirective = ngDirective({
41710  priority: 450,
41711  compile: function() {
41712    return {
41713      pre: function(scope, element, attrs) {
41714        scope.$eval(attrs.ngInit);
41715      }
41716    };
41717  }
41718});
41719
41720/**
41721 * @ngdoc directive
41722 * @name ngList
41723 *
41724 * @description
41725 * Text input that converts between a delimited string and an array of strings. The default
41726 * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom
41727 * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`.
41728 *
41729 * The behaviour of the directive is affected by the use of the `ngTrim` attribute.
41730 * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each
41731 *   list item is respected. This implies that the user of the directive is responsible for
41732 *   dealing with whitespace but also allows you to use whitespace as a delimiter, such as a
41733 *   tab or newline character.
41734 * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected
41735 *   when joining the list items back together) and whitespace around each list item is stripped
41736 *   before it is added to the model.
41737 *
41738 * ### Example with Validation
41739 *
41740 * <example name="ngList-directive" module="listExample">
41741 *   <file name="app.js">
41742 *      angular.module('listExample', [])
41743 *        .controller('ExampleController', ['$scope', function($scope) {
41744 *          $scope.names = ['morpheus', 'neo', 'trinity'];
41745 *        }]);
41746 *   </file>
41747 *   <file name="index.html">
41748 *    <form name="myForm" ng-controller="ExampleController">
41749 *      <label>List: <input name="namesInput" ng-model="names" ng-list required></label>
41750 *      <span role="alert">
41751 *        <span class="error" ng-show="myForm.namesInput.$error.required">
41752 *        Required!</span>
41753 *      </span>
41754 *      <br>
41755 *      <tt>names = {{names}}</tt><br/>
41756 *      <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
41757 *      <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
41758 *      <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
41759 *      <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
41760 *     </form>
41761 *   </file>
41762 *   <file name="protractor.js" type="protractor">
41763 *     var listInput = element(by.model('names'));
41764 *     var names = element(by.exactBinding('names'));
41765 *     var valid = element(by.binding('myForm.namesInput.$valid'));
41766 *     var error = element(by.css('span.error'));
41767 *
41768 *     it('should initialize to model', function() {
41769 *       expect(names.getText()).toContain('["morpheus","neo","trinity"]');
41770 *       expect(valid.getText()).toContain('true');
41771 *       expect(error.getCssValue('display')).toBe('none');
41772 *     });
41773 *
41774 *     it('should be invalid if empty', function() {
41775 *       listInput.clear();
41776 *       listInput.sendKeys('');
41777 *
41778 *       expect(names.getText()).toContain('');
41779 *       expect(valid.getText()).toContain('false');
41780 *       expect(error.getCssValue('display')).not.toBe('none');
41781 *     });
41782 *   </file>
41783 * </example>
41784 *
41785 * ### Example - splitting on newline
41786 * <example name="ngList-directive-newlines">
41787 *   <file name="index.html">
41788 *    <textarea ng-model="list" ng-list="&#10;" ng-trim="false"></textarea>
41789 *    <pre>{{ list | json }}</pre>
41790 *   </file>
41791 *   <file name="protractor.js" type="protractor">
41792 *     it("should split the text by newlines", function() {
41793 *       var listInput = element(by.model('list'));
41794 *       var output = element(by.binding('list | json'));
41795 *       listInput.sendKeys('abc\ndef\nghi');
41796 *       expect(output.getText()).toContain('[\n  "abc",\n  "def",\n  "ghi"\n]');
41797 *     });
41798 *   </file>
41799 * </example>
41800 *
41801 * @element input
41802 * @param {string=} ngList optional delimiter that should be used to split the value.
41803 */
41804var ngListDirective = function() {
41805  return {
41806    restrict: 'A',
41807    priority: 100,
41808    require: 'ngModel',
41809    link: function(scope, element, attr, ctrl) {
41810      // We want to control whitespace trimming so we use this convoluted approach
41811      // to access the ngList attribute, which doesn't pre-trim the attribute
41812      var ngList = element.attr(attr.$attr.ngList) || ', ';
41813      var trimValues = attr.ngTrim !== 'false';
41814      var separator = trimValues ? trim(ngList) : ngList;
41815
41816      var parse = function(viewValue) {
41817        // If the viewValue is invalid (say required but empty) it will be `undefined`
41818        if (isUndefined(viewValue)) return;
41819
41820        var list = [];
41821
41822        if (viewValue) {
41823          forEach(viewValue.split(separator), function(value) {
41824            if (value) list.push(trimValues ? trim(value) : value);
41825          });
41826        }
41827
41828        return list;
41829      };
41830
41831      ctrl.$parsers.push(parse);
41832      ctrl.$formatters.push(function(value) {
41833        if (isArray(value)) {
41834          return value.join(ngList);
41835        }
41836
41837        return undefined;
41838      });
41839
41840      // Override the standard $isEmpty because an empty array means the input is empty.
41841      ctrl.$isEmpty = function(value) {
41842        return !value || !value.length;
41843      };
41844    }
41845  };
41846};
41847
41848/* global VALID_CLASS: true,
41849  INVALID_CLASS: true,
41850  PRISTINE_CLASS: true,
41851  DIRTY_CLASS: true,
41852  UNTOUCHED_CLASS: true,
41853  TOUCHED_CLASS: true,
41854*/
41855
41856var VALID_CLASS = 'ng-valid',
41857    INVALID_CLASS = 'ng-invalid',
41858    PRISTINE_CLASS = 'ng-pristine',
41859    DIRTY_CLASS = 'ng-dirty',
41860    UNTOUCHED_CLASS = 'ng-untouched',
41861    TOUCHED_CLASS = 'ng-touched',
41862    PENDING_CLASS = 'ng-pending';
41863
41864var ngModelMinErr = minErr('ngModel');
41865
41866/**
41867 * @ngdoc type
41868 * @name ngModel.NgModelController
41869 *
41870 * @property {*} $viewValue The actual value from the control's view. For `input` elements, this is a
41871 * String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue
41872 * is set.
41873 * @property {*} $modelValue The value in the model that the control is bound to.
41874 * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
41875       the control reads value from the DOM. The functions are called in array order, each passing
41876       its return value through to the next. The last return value is forwarded to the
41877       {@link ngModel.NgModelController#$validators `$validators`} collection.
41878
41879Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
41880`$viewValue`}.
41881
41882Returning `undefined` from a parser means a parse error occurred. In that case,
41883no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
41884will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
41885is set to `true`. The parse error is stored in `ngModel.$error.parse`.
41886
41887 *
41888 * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
41889       the model value changes. The functions are called in reverse array order, each passing the value through to the
41890       next. The last return value is used as the actual DOM value.
41891       Used to format / convert values for display in the control.
41892 * ```js
41893 * function formatter(value) {
41894 *   if (value) {
41895 *     return value.toUpperCase();
41896 *   }
41897 * }
41898 * ngModel.$formatters.push(formatter);
41899 * ```
41900 *
41901 * @property {Object.<string, function>} $validators A collection of validators that are applied
41902 *      whenever the model value changes. The key value within the object refers to the name of the
41903 *      validator while the function refers to the validation operation. The validation operation is
41904 *      provided with the model value as an argument and must return a true or false value depending
41905 *      on the response of that validation.
41906 *
41907 * ```js
41908 * ngModel.$validators.validCharacters = function(modelValue, viewValue) {
41909 *   var value = modelValue || viewValue;
41910 *   return /[0-9]+/.test(value) &&
41911 *          /[a-z]+/.test(value) &&
41912 *          /[A-Z]+/.test(value) &&
41913 *          /\W+/.test(value);
41914 * };
41915 * ```
41916 *
41917 * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to
41918 *      perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided
41919 *      is expected to return a promise when it is run during the model validation process. Once the promise
41920 *      is delivered then the validation status will be set to true when fulfilled and false when rejected.
41921 *      When the asynchronous validators are triggered, each of the validators will run in parallel and the model
41922 *      value will only be updated once all validators have been fulfilled. As long as an asynchronous validator
41923 *      is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators
41924 *      will only run once all synchronous validators have passed.
41925 *
41926 * Please note that if $http is used then it is important that the server returns a success HTTP response code
41927 * in order to fulfill the validation and a status level of `4xx` in order to reject the validation.
41928 *
41929 * ```js
41930 * ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
41931 *   var value = modelValue || viewValue;
41932 *
41933 *   // Lookup user by username
41934 *   return $http.get('/api/users/' + value).
41935 *      then(function resolved() {
41936 *        //username exists, this means validation fails
41937 *        return $q.reject('exists');
41938 *      }, function rejected() {
41939 *        //username does not exist, therefore this validation passes
41940 *        return true;
41941 *      });
41942 * };
41943 * ```
41944 *
41945 * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
41946 *     view value has changed. It is called with no arguments, and its return value is ignored.
41947 *     This can be used in place of additional $watches against the model value.
41948 *
41949 * @property {Object} $error An object hash with all failing validator ids as keys.
41950 * @property {Object} $pending An object hash with all pending validator ids as keys.
41951 *
41952 * @property {boolean} $untouched True if control has not lost focus yet.
41953 * @property {boolean} $touched True if control has lost focus.
41954 * @property {boolean} $pristine True if user has not interacted with the control yet.
41955 * @property {boolean} $dirty True if user has already interacted with the control.
41956 * @property {boolean} $valid True if there is no error.
41957 * @property {boolean}
41957 $invalid True if at least one error on the control.
41958 * @property {string} $name The name attribute of the control.
41959 *
41960 * @description
41961 *
41962 * `NgModelController` provides API for the {@link ngModel `ngModel`} directive.
41963 * The controller contains services for data-binding, validation, CSS updates, and value formatting
41964 * and parsing. It purposefully does not contain any logic which deals with DOM rendering or
41965 * listening to DOM events.
41966 * Such DOM related logic should be provided by other directives which make use of
41967 * `NgModelController` for data-binding to control elements.
41968 * Angular provides this DOM logic for most {@link input `input`} elements.
41969 * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example
41970 * custom control example} that uses `ngModelController` to bind to `contenteditable` elements.
41971 *
41972 * @example
41973 * ### Custom Control Example
41974 * This example shows how to use `NgModelController` with a custom control to achieve
41975 * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
41976 * collaborate together to achieve the desired result.
41977 *
41978 * `contenteditable` is an HTML5 attribute, which tells the browser to let the element
41979 * contents be edited in place by the user.
41980 *
41981 * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}
41982 * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`).
41983 * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks
41984 * that content using the `$sce` service.
41985 *
41986 * <example name="NgModelController" module="customControl" deps="angular-sanitize.js">
41987    <file name="style.css">
41988      [contenteditable] {
41989        border: 1px solid black;
41990        background-color: white;
41991        min-height: 20px;
41992      }
41993
41994      .ng-invalid {
41995        border: 1px solid red;
41996      }
41997
41998    </file>
41999    <file name="script.js">
42000      angular.module('customControl', ['ngSanitize']).
42001        directive('contenteditable', ['$sce', function($sce) {
42002          return {
42003            restrict: 'A', // only activate on element attribute
42004            require: '?ngModel', // get a hold of NgModelController
42005            link: function(scope, element, attrs, ngModel) {
42006              if (!ngModel) return; // do nothing if no ng-model
42007
42008              // Specify how UI should be updated
42009              ngModel.$render = function() {
42010                element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
42011              };
42012
42013              // Listen for change events to enable binding
42014              element.on('blur keyup change', function() {
42015                scope.$evalAsync(read);
42016              });
42017              read(); // initialize
42018
42019              // Write data to the model
42020              function read() {
42021                var html = element.html();
42022                // When we clear the content editable the browser leaves a <br> behind
42023                // If strip-br attribute is provided then we strip this out
42024                if ( attrs.stripBr && html == '<br>' ) {
42025                  html = '';
42026                }
42027                ngModel.$setViewValue(html);
42028              }
42029            }
42030          };
42031        }]);
42032    </file>
42033    <file name="index.html">
42034      <form name="myForm">
42035       <div contenteditable
42036            name="myWidget" ng-model="userContent"
42037            strip-br="true"
42038            required>Change me!</div>
42039        <span ng-show="myForm.myWidget.$error.required">Required!</span>
42040       <hr>
42041       <textarea ng-model="userContent" aria-label="Dynamic textarea"></textarea>
42042      </form>
42043    </file>
42044    <file name="protractor.js" type="protractor">
42045    it('should data-bind and become invalid', function() {
42046      if (browser.params.browser == 'safari' || browser.params.browser == 'firefox') {
42047        // SafariDriver can't handle contenteditable
42048        // and Firefox driver can't clear contenteditables very well
42049        return;
42050      }
42051      var contentEditable = element(by.css('[contenteditable]'));
42052      var content = 'Change me!';
42053
42054      expect(contentEditable.getText()).toEqual(content);
42055
42056      contentEditable.clear();
42057      contentEditable.sendKeys(protractor.Key.BACK_SPACE);
42058      expect(contentEditable.getText()).toEqual('');
42059      expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
42060    });
42061    </file>
42062 * </example>
42063 *
42064 *
42065 */
42066var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q', '$interpolate',
42067    function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) {
42068  this.$viewValue = Number.NaN;
42069  this.$modelValue = Number.NaN;
42070  this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity.
42071  this.$validators = {};
42072  this.$asyncValidators = {};
42073  this.$parsers = [];
42074  this.$formatters = [];
42075  this.$viewChangeListeners = [];
42076  this.$untouched = true;
42077  this.$touched = false;
42078  this.$pristine = true;
42079  this.$dirty = false;
42080  this.$valid = true;
42081  this.$invalid = false;
42082  this.$error = {}; // keep invalid keys here
42083  this.$$success = {}; // keep valid keys here
42084  this.$pending = undefined; // keep pending keys here
42085  this.$name = $interpolate($attr.name || '', false)($scope);
42086  this.$$parentForm = nullFormCtrl;
42087
42088  var parsedNgModel = $parse($attr.ngModel),
42089      parsedNgModelAssign = parsedNgModel.assign,
42090      ngModelGet = parsedNgModel,
42091      ngModelSet = parsedNgModelAssign,
42092      pendingDebounce = null,
42093      parserValid,
42094      ctrl = this;
42095
42096  this.$$setOptions = function(options) {
42097    ctrl.$options = options;
42098    if (options && options.getterSetter) {
42099      var invokeModelGetter = $parse($attr.ngModel + '()'),
42100          invokeModelSetter = $parse($attr.ngModel + '($$$p)');
42101
42102      ngModelGet = function($scope) {
42103        var modelValue = parsedNgModel($scope);
42104        if (isFunction(modelValue)) {
42105          modelValue = invokeModelGetter($scope);
42106        }
42107        return modelValue;
42108      };
42109      ngModelSet = function($scope, newValue) {
42110        if (isFunction(parsedNgModel($scope))) {
42111          invokeModelSetter($scope, {$$$p: ctrl.$modelValue});
42112        } else {
42113          parsedNgModelAssign($scope, ctrl.$modelValue);
42114        }
42115      };
42116    } else if (!parsedNgModel.assign) {
42117      throw ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
42118          $attr.ngModel, startingTag($element));
42119    }
42120  };
42121
42122  /**
42123   * @ngdoc method
42124   * @name ngModel.NgModelController#$render
42125   *
42126   * @description
42127   * Called when the view needs to be updated. It is expected that the user of the ng-model
42128   * directive will implement this method.
42129   *
42130   * The `$render()` method is invoked in the following situations:
42131   *
42132   * * `$rollbackViewValue()` is called.  If we are rolling back the view value to the last
42133   *   committed value then `$render()` is called to update the input control.
42134   * * The value referenced by `ng-model` is changed programmatically and both the `$modelValue` and
42135   *   the `$viewValue` are different from last time.
42136   *
42137   * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of
42138   * `$modelValue` and `$viewValue` are actually different from their previous value. If `$modelValue`
42139   * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be
42140   * invoked if you only change a property on the objects.
42141   */
42142  this.$render = noop;
42143
42144  /**
42145   * @ngdoc method
42146   * @name ngModel.NgModelController#$isEmpty
42147   *
42148   * @description
42149   * This is called when we need to determine if the value of an input is empty.
42150   *
42151   * For instance, the required directive does this to work out if the input has data or not.
42152   *
42153   * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.
42154   *
42155   * You can override this for input directives whose concept of being empty is different from the
42156   * default. The `checkboxInputType` directive does this because in its case a value of `false`
42157   * implies empty.
42158   *
42159   * @param {*} value The value of the input to check for emptiness.
42160   * @returns {boolean} True if `value` is "empty".
42161   */
42162  this.$isEmpty = function(value) {
42163    return isUndefined(value) || value === '' || value === null || value !== value;
42164  };
42165
42166  var currentValidationRunId = 0;
42167
42168  /**
42169   * @ngdoc method
42170   * @name ngModel.NgModelController#$setValidity
42171   *
42172   * @description
42173   * Change the validity state, and notify the form.
42174   *
42175   * This method can be called within $parsers/$formatters or a custom validation implementation.
42176   * However, in most cases it should be sufficient to use the `ngModel.$validators` and
42177   * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically.
42178   *
42179   * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be assigned
42180   *        to either `$error[validationErrorKey]` or `$pending[validationErrorKey]`
42181   *        (for unfulfilled `$asyncValidators`), so that it is available for data-binding.
42182   *        The `validationErrorKey` should be in camelCase and will get converted into dash-case
42183   *        for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
42184   *        class and can be bound to as  `{{someForm.someControl.$error.myError}}` .
42185   * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined),
42186   *                          or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
42187   *                          Skipped is used by Angular when validators do not run because of parse errors and
42188   *                          when `$asyncValidators` do not run because any of the `$validators` failed.
42189   */
42190  addSetValidityMethod({
42191    ctrl: this,
42192    $element: $element,
42193    set: function(object, property) {
42194      object[property] = true;
42195    },
42196    unset: function(object, property) {
42197      delete object[property];
42198    },
42199    $animate: $animate
42200  });
42201
42202  /**
42203   * @ngdoc method
42204   * @name ngModel.NgModelController#$setPristine
42205   *
42206   * @description
42207   * Sets the control to its pristine state.
42208   *
42209   * This method can be called to remove the `ng-dirty` class and set the control to its pristine
42210   * state (`ng-pristine` class). A model is considered to be pristine when the control
42211   * has not been changed from when first compiled.
42212   */
42213  this.$setPristine = function() {
42214    ctrl.$dirty = false;
42215    ctrl.$pristine = true;
42216    $animate.removeClass($element, DIRTY_CLASS);
42217    $animate.addClass($element, PRISTINE_CLASS);
42218  };
42219
42220  /**
42221   * @ngdoc method
42222   * @name ngModel.NgModelController#$setDirty
42223   *
42224   * @description
42225   * Sets the control to its dirty state.
42226   *
42227   * This method can be called to remove the `ng-pristine` class and set the control to its dirty
42228   * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed
42229   * from when first compiled.
42230   */
42231  this.$setDirty = function() {
42232    ctrl.$dirty = true;
42233    ctrl.$pristine = false;
42234    $animate.removeClass($element, PRISTINE_CLASS);
42235    $animate.addClass($element, DIRTY_CLASS);
42236    ctrl.$$parentForm.$setDirty();
42237  };
42238
42239  /**
42240   * @ngdoc method
42241   * @name ngModel.NgModelController#$setUntouched
42242   *
42243   * @description
42244   * Sets the control to its untouched state.
42245   *
42246   * This method can be called to remove the `ng-touched` class and set the control to its
42247   * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched
42248   * by default, however this function can be used to restore that state if the model has
42249   * already been touched by the user.
42250   */
42251  this.$setUntouched = function() {
42252    ctrl.$touched = false;
42253    ctrl.$untouched = true;
42254    $animate.setClass($element, UNTOUCHED_CLASS, TOUCHED_CLASS);
42255  };
42256
42257  /**
42258   * @ngdoc method
42259   * @name ngModel.NgModelController#$setTouched
42260   *
42261   * @description
42262   * Sets the control to its touched state.
42263   *
42264   * This method can be called to remove the `ng-untouched` class and set the control to its
42265   * touched state (`ng-touched` class). A model is considered to be touched when the user has
42266   * first focused the control element and then shifted focus away from the control (blur event).
42267   */
42268  this.$setTouched = function() {
42269    ctrl.$touched = true;
42270    ctrl.$untouched = false;
42271    $animate.setClass($element, TOUCHED_CLASS, UNTOUCHED_CLASS);
42272  };
42273
42274  /**
42275   * @ngdoc method
42276   * @name ngModel.NgModelController#$rollbackViewValue
42277   *
42278   * @description
42279   * Cancel an update and reset the input element's value to prevent an update to the `$modelValue`,
42280   * which may be caused by a pending debounced event or because the input is waiting for a some
42281   * future event.
42282   *
42283   * If you have an input that uses `ng-model-options` to set up debounced events or events such
42284   * as blur you can have a situation where there is a period when the `$viewValue`
42285   * is out of synch with the ngModel's `$modelValue`.
42286   *
42287   * In this case, you can run into difficulties if you try to update the ngModel's `$modelValue`
42288   * programmatically before these debounced/future events have resolved/occurred, because Angular's
42289   * dirty checking mechanism is not able to tell whether the model has actually changed or not.
42290   *
42291   * The `$rollbackViewValue()` method should be called before programmatically changing the model of an
42292   * input which may have such events pending. This is important in order to make sure that the
42293   * input field will be updated with the new model value and any pending operations are cancelled.
42294   *
42295   * <example name="ng-model-cancel-update" module="cancel-update-example">
42296   *   <file name="app.js">
42297   *     angular.module('cancel-update-example', [])
42298   *
42299   *     .controller('CancelUpdateController', ['$scope', function($scope) {
42300   *       $scope.resetWithCancel = function(e) {
42301   *         if (e.keyCode == 27) {
42302   *           $scope.myForm.myInput1.$rollbackViewValue();
42303   *           $scope.myValue = '';
42304   *         }
42305   *       };
42306   *       $scope.resetWithoutCancel = function(e) {
42307   *         if (e.keyCode == 27) {
42308   *           $scope.myValue = '';
42309   *         }
42310   *       };
42311   *     }]);
42312   *   </file>
42313   *   <file name="index.html">
42314   *     <div ng-controller="CancelUpdateController">
42315   *       <p>Try typing something in each input.  See that the model only updates when you
42316   *          blur off the input.
42317   *        </p>
42318   *        <p>Now see what happens if you start typing then press the Escape key</p>
42319   *
42320   *       <form name="myForm" ng-model-options="{ updateOn: 'blur' }">
42321   *         <p id="inputDescription1">With $rollbackViewValue()</p>
42322   *         <input name="myInput1" aria-describedby="inputDescription1" ng-model="myValue"
42323   *                ng-keydown="resetWithCancel($event)"><br/>
42324   *         myValue: "{{ myValue }}"
42325   *
42326   *         <p id="inputDescription2">Without $rollbackViewValue()</p>
42327   *         <input name="myInput2" aria-describedby="inputDescription2" ng-model="myValue"
42328   *                ng-keydown="resetWithoutCancel($event)"><br/>
42329   *         myValue: "{{ myValue }}"
42330   *       </form>
42331   *     </div>
42332   *   </file>
42333   * </example>
42334   */
42335  this.$rollbackViewValue = function() {
42336    $timeout.cancel(pendingDebounce);
42337    ctrl.$viewValue = ctrl.$$lastCommittedViewValue;
42338    ctrl.$render();
42339  };
42340
42341  /**
42342   * @ngdoc method
42343   * @name ngModel.NgModelController#$validate
42344   *
42345   * @description
42346   * Runs each of the registered validators (first synchronous validators and then
42347   * asynchronous validators).
42348   * If the validity changes to invalid, the model will be set to `undefined`,
42349   * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`.
42350   * If the validity changes to valid, it will set the model to the last available valid
42351   * `$modelValue`, i.e. either the last parsed value or the last value set from the scope.
42352   */
42353  this.$validate = function() {
42354    // ignore $validate before model is initialized
42355    if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
42356      return;
42357    }
42358
42359    var viewValue = ctrl.$$lastCommittedViewValue;
42360    // Note: we use the $$rawModelValue as $modelValue might have been
42361    // set to undefined during a view -> model update that found validation
42362    // errors. We can't parse the view here, since that could change
42363    // the model although neither viewValue nor the model on the scope changed
42364    var modelValue = ctrl.$$rawModelValue;
42365
42366    var prevValid = ctrl.$valid;
42367    var prevModelValue = ctrl.$modelValue;
42368
42369    var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
42370
42371    ctrl.$$runValidators(modelValue, viewValue, function(allValid) {
42372      // If there was no change in validity, don't update the model
42373      // This prevents changing an invalid modelValue to undefined
42374      if (!allowInvalid && prevValid !== allValid) {
42375        // Note: Don't check ctrl.$valid here, as we could have
42376        // external validators (e.g. calculated on the server),
42377        // that just call $setValidity and need the model value
42378        // to calculate their validity.
42379        ctrl.$modelValue = allValid ? modelValue : undefined;
42380
42381        if (ctrl.$modelValue !== prevModelValue) {
42382          ctrl.$$writeModelToScope();
42383        }
42384      }
42385    });
42386
42387  };
42388
42389  this.$$runValidators = function(modelValue, viewValue, doneCallback) {
42390    currentValidationRunId++;
42391    var localValidationRunId = currentValidationRunId;
42392
42393    // check parser error
42394    if (!processParseErrors()) {
42395      validationDone(false);
42396      return;
42397    }
42398    if (!processSyncValidators()) {
42399      validationDone(false);
42400      return;
42401    }
42402    processAsyncValidators();
42403
42404    function processParseErrors() {
42405      var errorKey = ctrl.$$parserName || 'parse';
42406      if (isUndefined(parserValid)) {
42407        setValidity(errorKey, null);
42408      } else {
42409        if (!parserValid) {
42410          forEach(ctrl.$validators, function(v, name) {
42411            setValidity(name, null);
42412          });
42413          forEach(ctrl.$asyncValidators, function(v, name) {
42414            setValidity(name, null);
42415          });
42416        }
42417        // Set the parse error last, to prevent unsetting it, should a $validators key == parserName
42418        setValidity(errorKey, parserValid);
42419        return parserValid;
42420      }
42421      return true;
42422    }
42423
42424    function processSyncValidators() {
42425      var syncValidatorsValid = true;
42426      forEach(ctrl.$validators, function(validator, name) {
42427        var result = validator(modelValue, viewValue);
42428        syncValidatorsValid = syncValidatorsValid && result;
42429        setValidity(name, result);
42430      });
42431      if (!syncValidatorsValid) {
42432        forEach(ctrl.$asyncValidators, function(v, name) {
42433          setValidity(name, null);
42434        });
42435        return false;
42436      }
42437      return true;
42438    }
42439
42440    function processAsyncValidators() {
42441      var validatorPromises = [];
42442      var allValid = true;
42443      forEach(ctrl.$asyncValidators, function(validator, name) {
42444        var promise = validator(modelValue, viewValue);
42445        if (!isPromiseLike(promise)) {
42446          throw ngModelMinErr("$asyncValidators",
42447            "Expected asynchronous validator to return a promise but got '{0}' instead.", promise);
42448        }
42449        setValidity(name, undefined);
42450        validatorPromises.push(promise.then(function() {
42451          setValidity(name, true);
42452        }, function(error) {
42453          allValid = false;
42454          setValidity(name, false);
42455        }));
42456      });
42457      if (!validatorPromises.length) {
42458        validationDone(true);
42459      } else {
42460        $q.all(validatorPromises).then(function() {
42461          validationDone(allValid);
42462        }, noop);
42463      }
42464    }
42465
42466    function setValidity(name, isValid) {
42467      if (localValidationRunId === currentValidationRunId) {
42468        ctrl.$setValidity(name, isValid);
42469      }
42470    }
42471
42472    function validationDone(allValid) {
42473      if (localValidationRunId === currentValidationRunId) {
42474
42475        doneCallback(allValid);
42476      }
42477    }
42478  };
42479
42480  /**
42481   * @ngdoc method
42482   * @name ngModel.NgModelController#$commitViewValue
42483   *
42484   * @description
42485   * Commit a pending update to the `$modelValue`.
42486   *
42487   * Updates may be pending by a debounced event or because the input is waiting for a some future
42488   * event defined in `ng-model-options`. this method is rarely needed as `NgModelController`
42489   * usually handles calling this in response to input events.
42490   */
42491  this.$commitViewValue = function() {
42492    var viewValue = ctrl.$viewValue;
42493
42494    $timeout.cancel(pendingDebounce);
42495
42496    // If the view value has not changed then we should just exit, except in the case where there is
42497    // a native validator on the element. In this case the validation state may have changed even though
42498    // the viewValue has stayed empty.
42499    if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) {
42500      return;
42501    }
42502    ctrl.$$lastCommittedViewValue = viewValue;
42503
42504    // change to dirty
42505    if (ctrl.$pristine) {
42506      this.$setDirty();
42507    }
42508    this.$$parseAndValidate();
42509  };
42510
42511  this.$$parseAndValidate = function() {
42512    var viewValue = ctrl.$$lastCommittedViewValue;
42513    var modelValue = viewValue;
42514    parserValid = isUndefined(modelValue) ? undefined : true;
42515
42516    if (parserValid) {
42517      for (var i = 0; i < ctrl.$parsers.length; i++) {
42518        modelValue = ctrl.$parsers[i](modelValue);
42519        if (isUndefined(modelValue)) {
42520          parserValid = false;
42521          break;
42522        }
42523      }
42524    }
42525    if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
42526      // ctrl.$modelValue has not been touched yet...
42527      ctrl.$modelValue = ngModelGet($scope);
42528    }
42529    var prevModelValue = ctrl.$modelValue;
42530    var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
42531    ctrl.$$rawModelValue = modelValue;
42532
42533    if (allowInvalid) {
42534      ctrl.$modelValue = modelValue;
42535      writeToModelIfNeeded();
42536    }
42537
42538    // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date.
42539    // This can happen if e.g. $setViewValue is called from inside a parser
42540    ctrl.$$runValidators(modelValue, ctrl.$$lastCommittedViewValue, function(allValid) {
42541      if (!allowInvalid) {
42542        // Note: Don't check ctrl.$valid here, as we could have
42543        // external validators (e.g. calculated on the server),
42544        // that just call $setValidity and need the model value
42545        // to calculate their validity.
42546        ctrl.$modelValue = allValid ? modelValue : undefined;
42547        writeToModelIfNeeded();
42548      }
42549    });
42550
42551    function writeToModelIfNeeded() {
42552      if (ctrl.$modelValue !== prevModelValue) {
42553        ctrl.$$writeModelToScope();
42554      }
42555    }
42556  };
42557
42558  this.$$writeModelToScope = function() {
42559    ngModelSet($scope, ctrl.$modelValue);
42560    forEach(ctrl.$viewChangeListeners, function(listener) {
42561      try {
42562        listener();
42563      } catch (e) {
42564        $exceptionHandler(e);
42565      }
42566    });
42567  };
42568
42569  /**
42570   * @ngdoc method
42571   * @name ngModel.NgModelController#$setViewValue
42572   *
42573   * @description
42574   * Update the view value.
42575   *
42576   * This method should be called when a control wants to change the view value; typically,
42577   * this is done from within a DOM event handler. For example, the {@link ng.directive:input input}
42578   * directive calls it when the value of the input changes and {@link ng.directive:select select}
42579   * calls it when an option is selected.
42580   *
42581   * When `$setViewValue` is called, the new `value` will be staged for committing through the `$parsers`
42582   * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
42583   * value sent directly for processing, finally to be applied to `$modelValue` and then the
42584   * **expression** specified in the `ng-model` attribute. Lastly, all the registered change listeners,
42585   * in the `$viewChangeListeners` list, are called.
42586   *
42587   * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
42588   * and the `default` trigger is not listed, all those actions will remain pending until one of the
42589   * `updateOn` events is triggered on the DOM element.
42590   * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions}
42591   * directive is used with a custom debounce for this particular event.
42592   * Note that a `$digest` is only triggered once the `updateOn` events are fired, or if `debounce`
42593   * is specified, once the timer runs out.
42594   *
42595   * When used with standard inputs, the view value will always be a string (which is in some cases
42596   * parsed into another type, such as a `Date` object for `input[date]`.)
42597   * However, custom controls might also pass objects to this method. In this case, we should make
42598   * a copy of the object before passing it to `$setViewValue`. This is because `ngModel` does not
42599   * perform a deep watch of objects, it only looks for a change of identity. If you only change
42600   * the property of the object then ngModel will not realise that the object has changed and
42601   * will not invoke the `$parsers` and `$validators` pipelines. For this reason, you should
42602   * not change properties of the copy once it has been passed to `$setViewValue`.
42603   * Otherwise you may cause the model value on the scope to change incorrectly.
42604   *
42605   * <div class="alert alert-info">
42606   * In any case, the value passed to the method should always reflect the current value
42607   * of the control. For example, if you are calling `$setViewValue` for an input element,
42608   * you should pass the input DOM value. Otherwise, the control and the scope model become
42609   * out of sync. It's also important to note that `$setViewValue` does not call `$render` or change
42610   * the control's DOM value in any way. If we want to change the control's DOM value
42611   * programmatically, we should update the `ngModel` scope expression. Its new value will be
42612   * picked up by the model controller, which will run it through the `$formatters`, `$render` it
42613   * to update the DOM, and finally call `$validate` on it.
42614   * </div>
42615   *
42616   * @param {*} value value from the view.
42617   * @param {string} trigger Event that triggered the update.
42618   */
42619  this.$setViewValue = function(value, trigger) {
42620    ctrl.$viewValue = value;
42621    if (!ctrl.$options || ctrl.$options.updateOnDefault) {
42622      ctrl.$$debounceViewValueCommit(trigger);
42623    }
42624  };
42625
42626  this.$$debounceViewValueCommit = function(trigger) {
42627    var debounceDelay = 0,
42628        options = ctrl.$options,
42629        debounce;
42630
42631    if (options && isDefined(options.debounce)) {
42632      debounce = options.debounce;
42633      if (isNumber(debounce)) {
42634        debounceDelay = debounce;
42635      } else if (isNumber(debounce[trigger])) {
42636        debounceDelay = debounce[trigger];
42637      } else if (isNumber(debounce['default'])) {
42638        debounceDelay = debounce['default'];
42639      }
42640    }
42641
42642    $timeout.cancel(pendingDebounce);
42643    if (debounceDelay) {
42644      pendingDebounce = $timeout(function() {
42645        ctrl.$commitViewValue();
42646      }, debounceDelay);
42647    } else if ($rootScope.$$phase) {
42648      ctrl.$commitViewValue();
42649    } else {
42650      $scope.$apply(function() {
42651        ctrl.$commitViewValue();
42652      });
42653    }
42654  };
42655
42656  // model -> value
42657  // Note: we cannot use a normal scope.$watch as we want to detect the following:
42658  // 1. scope value is 'a'
42659  // 2. user enters 'b'
42660  // 3. ng-change kicks in and reverts scope value to 'a'
42661  //    ->
42661 scope value did not change since the last digest as
42662  //       ng-change executes in apply phase
42663  // 4. view should be changed back to 'a'
42664  $scope.$watch(function ngModelWatch() {
42665    var modelValue = ngModelGet($scope);
42666
42667    // if scope model value and ngModel value are out of sync
42668    // TODO(perf): why not move this to the action fn?
42669    if (modelValue !== ctrl.$modelValue &&
42670       // checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator
42671       (ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue)
42672    ) {
42673      ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
42674      parserValid = undefined;
42675
42676      var formatters = ctrl.$formatters,
42677          idx = formatters.length;
42678
42679      var viewValue = modelValue;
42680      while (idx--) {
42681        viewValue = formatters[idx](viewValue);
42682      }
42683      if (ctrl.$viewValue !== viewValue) {
42684        ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
42685        ctrl.$render();
42686
42687        ctrl.$$runValidators(modelValue, viewValue, noop);
42688      }
42689    }
42690
42691    return modelValue;
42692  });
42693}];
42694
42695
42696/**
42697 * @ngdoc directive
42698 * @name ngModel
42699 *
42700 * @element input
42701 * @priority 1
42702 *
42703 * @description
42704 * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a
42705 * property on the scope using {@link ngModel.NgModelController NgModelController},
42706 * which is created and exposed by this directive.
42707 *
42708 * `ngModel` is responsible for:
42709 *
42710 * - Binding the view into the model, which other directives such as `input`, `textarea` or `select`
42711 *   require.
42712 * - Providing validation behavior (i.e. required, number, email, url).
42713 * - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).
42714 * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`, `ng-untouched`) including animations.
42715 * - Registering the control with its parent {@link ng.directive:form form}.
42716 *
42717 * Note: `ngModel` will try to bind to the property given by evaluating the expression on the
42718 * current scope. If the property doesn't already exist on this scope, it will be created
42719 * implicitly and added to the scope.
42720 *
42721 * For best practices on using `ngModel`, see:
42722 *
42723 *  - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes)
42724 *
42725 * For basic examples, how to use `ngModel`, see:
42726 *
42727 *  - {@link ng.directive:input input}
42728 *    - {@link input[text] text}
42729 *    - {@link input[checkbox] checkbox}
42730 *    - {@link input[radio] radio}
42731 *    - {@link input[number] number}
42732 *    - {@link input[email] email}
42733 *    - {@link input[url] url}
42734 *    - {@link input[date] date}
42735 *    - {@link input[datetime-local] datetime-local}
42736 *    - {@link input[time] time}
42737 *    - {@link input[month] month}
42738 *    - {@link input[week] week}
42739 *  - {@link ng.directive:select select}
42740 *  - {@link ng.directive:textarea textarea}
42741 *
42742 * # CSS classes
42743 * The following CSS classes are added and removed on the associated input/select/textarea element
42744 * depending on the validity of the model.
42745 *
42746 *  - `ng-valid`: the model is valid
42747 *  - `ng-invalid`: the model is invalid
42748 *  - `ng-valid-[key]`: for each valid key added by `$setValidity`
42749 *  - `ng-invalid-[key]`: for each invalid key added by `$setValidity`
42750 *  - `ng-pristine`: the control hasn't been interacted with yet
42751 *  - `ng-dirty`: the control has been interacted with
42752 *  - `ng-touched`: the control has been blurred
42753 *  - `ng-untouched`: the control hasn't been blurred
42754 *  - `ng-pending`: any `$asyncValidators` are unfulfilled
42755 *
42756 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
42757 *
42758 * ## Animation Hooks
42759 *
42760 * Animations within models are triggered when any of the associated CSS classes are added and removed
42761 * on the input element which is attached to the model. These classes are: `.ng-pristine`, `.ng-dirty`,
42762 * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
42763 * The animations that are triggered within ngModel are similar to how they work in ngClass and
42764 * animations can be hooked into using CSS transitions, keyframes as well as JS animations.
42765 *
42766 * The following example shows a simple way to utilize CSS transitions to style an input element
42767 * that has been rendered as invalid after it has been validated:
42768 *
42769 * <pre>
42770 * //be sure to include ngAnimate as a module to hook into more
42771 * //advanced animations
42772 * .my-input {
42773 *   transition:0.5s linear all;
42774 *   background: white;
42775 * }
42776 * .my-input.ng-invalid {
42777 *   background: red;
42778 *   color:white;
42779 * }
42780 * </pre>
42781 *
42782 * @example
42783 * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample">
42784     <file name="index.html">
42785       <script>
42786        angular.module('inputExample', [])
42787          .controller('ExampleController', ['$scope', function($scope) {
42788            $scope.val = '1';
42789          }]);
42790       </script>
42791       <style>
42792         .my-input {
42793           transition:all linear 0.5s;
42794           background: transparent;
42795         }
42796         .my-input.ng-invalid {
42797           color:white;
42798           background: red;
42799         }
42800       </style>
42801       <p id="inputDescription">
42802        Update input to see transitions when valid/invalid.
42803        Integer is a valid value.
42804       </p>
42805       <form name="testForm" ng-controller="ExampleController">
42806         <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input"
42807                aria-describedby="inputDescription" />
42808       </form>
42809     </file>
42810 * </example>
42811 *
42812 * ## Binding to a getter/setter
42813 *
42814 * Sometimes it's helpful to bind `ngModel` to a getter/setter function.  A getter/setter is a
42815 * function that returns a representation of the model when called with zero arguments, and sets
42816 * the internal state of a model when called with an argument. It's sometimes useful to use this
42817 * for models that have an internal representation that's different from what the model exposes
42818 * to the view.
42819 *
42820 * <div class="alert alert-success">
42821 * **Best Practice:** It's best to keep getters fast because Angular is likely to call them more
42822 * frequently than other parts of your code.
42823 * </div>
42824 *
42825 * You use this behavior by adding `ng-model-options="{ getterSetter: true }"` to an element that
42826 * has `ng-model` attached to it. You can also add `ng-model-options="{ getterSetter: true }"` to
42827 * a `<form>`, which will enable this behavior for all `<input>`s within it. See
42828 * {@link ng.directive:ngModelOptions `ngModelOptions`} for more.
42829 *
42830 * The following example shows how to use `ngModel` with a getter/setter:
42831 *
42832 * @example
42833 * <example name="ngModel-getter-setter" module="getterSetterExample">
42834     <file name="index.html">
42835       <div ng-controller="ExampleController">
42836         <form name="userForm">
42837           <label>Name:
42838             <input type="text" name="userName"
42839                    ng-model="user.name"
42840                    ng-model-options="{ getterSetter: true }" />
42841           </label>
42842         </form>
42843         <pre>user.name = <span ng-bind="user.name()"></span></pre>
42844       </div>
42845     </file>
42846     <file name="app.js">
42847       angular.module('getterSetterExample', [])
42848         .controller('ExampleController', ['$scope', function($scope) {
42849           var _name = 'Brian';
42850           $scope.user = {
42851             name: function(newName) {
42852              // Note that newName can be undefined for two reasons:
42853              // 1. Because it is called as a getter and thus called with no arguments
42854              // 2. Because the property should actually be set to undefined. This happens e.g. if the
42855              //    input is invalid
42856              return arguments.length ? (_name = newName) : _name;
42857             }
42858           };
42859         }]);
42860     </file>
42861 * </example>
42862 */
42863var ngModelDirective = ['$rootScope', function($rootScope) {
42864  return {
42865    restrict: 'A',
42866    require: ['ngModel', '^?form', '^?ngModelOptions'],
42867    controller: NgModelController,
42868    // Prelink needs to run before any input directive
42869    // so that we can set the NgModelOptions in NgModelController
42870    // before anyone else uses it.
42871    priority: 1,
42872    compile: function ngModelCompile(element) {
42873      // Setup initial state of the control
42874      element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS);
42875
42876      return {
42877        pre: function ngModelPreLink(scope, element, attr, ctrls) {
42878          var modelCtrl = ctrls[0],
42879              formCtrl = ctrls[1] || modelCtrl.$$parentForm;
42880
42881          modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);
42882
42883          // notify others, especially parent forms
42884          formCtrl.$addControl(modelCtrl);
42885
42886          attr.$observe('name', function(newValue) {
42887            if (modelCtrl.$name !== newValue) {
42888              modelCtrl.$$parentForm.$$renameControl(modelCtrl, newValue);
42889            }
42890          });
42891
42892          scope.$on('$destroy', function() {
42893            modelCtrl.$$parentForm.$removeControl(modelCtrl);
42894          });
42895        },
42896        post: function ngModelPostLink(scope, element, attr, ctrls) {
42897          var modelCtrl = ctrls[0];
42898          if (modelCtrl.$options && modelCtrl.$options.updateOn) {
42899            element.on(modelCtrl.$options.updateOn, function(ev) {
42900              modelCtrl.$$debounceViewValueCommit(ev && ev.type);
42901            });
42902          }
42903
42904          element.on('blur', function(ev) {
42905            if (modelCtrl.$touched) return;
42906
42907            if ($rootScope.$$phase) {
42908              scope.$evalAsync(modelCtrl.$setTouched);
42909            } else {
42910              scope.$apply(modelCtrl.$setTouched);
42911            }
42912          });
42913        }
42914      };
42915    }
42916  };
42917}];
42918
42919var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
42920
42921/**
42922 * @ngdoc directive
42923 * @name ngModelOptions
42924 *
42925 * @description
42926 * Allows tuning how model updates are done. Using `ngModelOptions` you can specify a custom list of
42927 * events that will trigger a model update and/or a debouncing delay so that the actual update only
42928 * takes place when a timer expires; this timer will be reset after another change takes place.
42929 *
42930 * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might
42931 * be different from the value in the actual model. This means that if you update the model you
42932 * should also invoke {@link ngModel.NgModelController `$rollbackViewValue`} on the relevant input field in
42933 * order to make sure it is synchronized with the model and that any debounced action is canceled.
42934 *
42935 * The easiest way to reference the control's {@link ngModel.NgModelController `$rollbackViewValue`}
42936 * method is by making sure the input is placed inside a form that has a `name` attribute. This is
42937 * important because `form` controllers are published to the related scope under the name in their
42938 * `name` attribute.
42939 *
42940 * Any pending changes will take place immediately when an enclosing form is submitted via the
42941 * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
42942 * to have access to the updated model.
42943 *
42944 * `ngModelOptions` has an effect on the element it's declared on and its desce
42944ndants.
42945 *
42946 * @param {Object} ngModelOptions options to apply to the current model. Valid keys are:
42947 *   - `updateOn`: string specifying which event should the input be bound to. You can set several
42948 *     events using an space delimited list. There is a special event called `default` that
42949 *     matches the default events belonging of the control.
42950 *   - `debounce`: integer value which contains the debounce model update value in milliseconds. A
42951 *     value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
42952 *     custom value for each event. For example:
42953 *     `ng-model-options="{ updateOn: 'default blur', debounce: { 'default': 500, 'blur': 0 } }"`
42954 *   - `allowInvalid`: boolean value which indicates that the model can be set with values that did
42955 *     not validate correctly instead of the default behavior of setting the model to undefined.
42956 *   - `getterSetter`: boolean value which determines whether or not to treat functions bound to
42957       `ngModel` as getters/setters.
42958 *   - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for
42959 *     `<input type="date">`, `<input type="time">`, ... . It understands UTC/GMT and the
42960 *     continental US time zone abbreviations, but for general use, use a time zone offset, for
42961 *     example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
42962 *     If not specified, the timezone of the browser will be used.
42963 *
42964 * @example
42965
42966  The following example shows how to override immediate updates. Changes on the inputs within the
42967  form will update the model only when the control loses focus (blur event). If `escape` key is
42968  pressed while the input field is focused, the value is reset to the value in the current model.
42969
42970  <example name="ngModelOptions-directive-blur" module="optionsExample">
42971    <file name="index.html">
42972      <div ng-controller="ExampleController">
42973        <form name="userForm">
42974          <label>Name:
42975            <input type="text" name="userName"
42976                   ng-model="user.name"
42977                   ng-model-options="{ updateOn: 'blur' }"
42978                   ng-keyup="cancel($event)" />
42979          </label><br />
42980          <label>Other data:
42981            <input type="text" ng-model="user.data" />
42982          </label><br />
42983        </form>
42984        <pre>user.name = <span ng-bind="user.name"></span></pre>
42985      </div>
42986    </file>
42987    <file name="app.js">
42988      angular.module('optionsExample', [])
42989        .controller('ExampleController', ['$scope', function($scope) {
42990          $scope.user = { name: 'say', data: '' };
42991
42992          $scope.cancel = function(e) {
42993            if (e.keyCode == 27) {
42994              $scope.userForm.userName.$rollbackViewValue();
42995            }
42996          };
42997        }]);
42998    </file>
42999    <file name="protractor.js" type="protractor">
43000      var model = element(by.binding('user.name'));
43001      var input = element(by.model('user.name'));
43002      var other = element(by.model('user.data'));
43003
43004      it('should allow custom events', function() {
43005        input.sendKeys(' hello');
43006        input.click();
43007        expect(model.getText()).toEqual('say');
43008        other.click();
43009        expect(model.getText()).toEqual('say hello');
43010      });
43011
43012      it('should $rollbackViewValue when model changes', function() {
43013        input.sendKeys(' hello');
43014        expect(input.getAttribute('value')).toEqual('say hello');
43015        input.sendKeys(protractor.Key.ESCAPE);
43016        expect(input.getAttribute('value')).toEqual('say');
43017        other.click();
43018        expect(model.getText()).toEqual('say');
43019      });
43020    </file>
43021  </example>
43022
43023  This one shows how to debounce model changes. Model will be updated only 1 sec after last change.
43024  If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty.
43025
43026  <example name="ngModelOptions-directive-debounce" module="optionsExample">
43027    <file name="index.html">
43028      <div ng-controller="ExampleController">
43029        <form name="userForm">
43030          <label>Name:
43031            <input type="text" name="userName"
43032                   ng-model="user.name"
43033                   ng-model-options="{ debounce: 1000 }" />
43034          </label>
43035          <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button>
43036          <br />
43037        </form>
43038        <pre>user.name = <span ng-bind="user.name"></span></pre>
43039      </div>
43040    </file>
43041    <file name="app.js">
43042      angular.module('optionsExample', [])
43043        .controller('ExampleController', ['$scope', function($scope) {
43044          $scope.user = { name: 'say' };
43045        }]);
43046    </file>
43047  </example>
43048
43049  This one shows how to bind to getter/setters:
43050
43051  <example name="ngModelOptions-directive-getter-setter" module="getterSetterExample">
43052    <file name="index.html">
43053      <div ng-controller="ExampleController">
43054        <form name="userForm">
43055          <label>Name:
43056            <input type="text" name="userName"
43057                   ng-model="user.name"
43058                   ng-model-options="{ getterSetter: true }" />
43059          </label>
43060        </form>
43061        <pre>user.name = <span ng-bind="user.name()"></span></pre>
43062      </div>
43063    </file>
43064    <file name="app.js">
43065      angular.module('getterSetterExample', [])
43066        .controller('ExampleController', ['$scope', function($scope) {
43067          var _name = 'Brian';
43068          $scope.user = {
43069            name: function(newName) {
43070              // Note that newName can be undefined for two reasons:
43071              // 1. Because it is called as a getter and thus called with no arguments
43072              // 2. Because the property should actually be set to undefined. This happens e.g. if the
43073              //    input is invalid
43074              return arguments.length ? (_name = newName) : _name;
43075            }
43076          };
43077        }]);
43078    </file>
43079  </example>
43080 */
43081var ngModelOptionsDirective = function() {
43082  return {
43083    restrict: 'A',
43084    controller: ['$scope', '$attrs', function($scope, $attrs) {
43085      var that = this;
43086      this.$options = copy($scope.$eval($attrs.ngModelOptions));
43087      // Allow adding/overriding bound events
43088      if (isDefined(this.$options.updateOn)) {
43089        this.$options.updateOnDefault = false;
43090        // extract "default" pseudo-event from list of events that can trigger a model update
43091        this.$options.updateOn = trim(this.$options.updateOn.replace(DEFAULT_REGEXP, function() {
43092          that.$options.updateOnDefault = true;
43093          return ' ';
43094        }));
43095      } else {
43096        this.$options.updateOnDefault = true;
43097      }
43098    }]
43099  };
43100};
43101
43102
43103
43104// helper methods
43105function addSetValidityMethod(context) {
43106  var ctrl = context.ctrl,
43107      $element = context.$element,
43108      classCache = {},
43109      set = context.set,
43110      unset = context.unset,
43111      $animate = context.$animate;
43112
43113  classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS));
43114
43115  ctrl.$setValidity = setValidity;
43116
43117  function setValidity(validationErrorKey, state, controller) {
43118    if (isUndefined(state)) {
43119      createAndSet('$pending', validationErrorKey, controller);
43120    } else {
43121      unsetAndCleanup('$pending', validationErrorKey, controller);
43122    }
43123    if (!isBoolean(state)) {
43124      unset(ctrl.$error, validationErrorKey, controller);
43125      unset(ctrl.$$success, validationErrorKey, controller);
43126    } else {
43127      if (state) {
43128        unset(ctrl.$error, validationErrorKey, controller);
43129        set(ctrl.$$success, validationErrorKey, controller);
43130      } else {
43131        set(ctrl.$error, validationErrorKey, controller);
43132        unset(ctrl.$$success, validationErrorKey, controller);
43133      }
43134    }
43135    if (ctrl.$pending) {
43136      cachedToggleClass(PENDING_CLASS, true);
43137      ctrl.$valid = ctrl.$invalid = undefined;
43138      toggleValidationCss('', null);
43139    } else {
43140      cachedToggleClass(PENDING_CLASS, false);
43141      ctrl.$valid = isObjectEmpty(ctrl.$error);
43142      ctrl.$invalid = !ctrl.$valid;
43143      toggleValidationCss('', ctrl.$valid);
43144    }
43145
43146    // re-read the state as the set/unset methods could have
43147    // combined state in ctrl.$error[validationError] (used for forms),
43148    // where setting/unsetting only increments/decrements the value,
43149    // and does not replace it.
43150    var combinedState;
43151    if (ctrl.$pending && ctrl.$pending[validationErrorKey]) {
43152      combinedState = undefined;
43153    } else if (ctrl.$error[validationErrorKey]) {
43154      combinedState = false;
43155    } else if (ctrl.$$success[validationErrorKey]) {
43156      combinedState = true;
43157    } else {
43158      combinedState = null;
43159    }
43160
43161    toggleValidationCss(validationErrorKey, combinedState);
43162    ctrl.$$parentForm.$setValidity(validationErrorKey, combinedState, ctrl);
43163  }
43164
43165  function createAndSet(name, value, controller) {
43166    if (!ctrl[name]) {
43167      ctrl[name] = {};
43168    }
43169    set(ctrl[name], value, controller);
43170  }
43171
43172  function unsetAndCleanup(name, value, controller) {
43173    if (ctrl[name]) {
43174      unset(ctrl[name], value, controller);
43175    }
43176    if (isObjectEmpty(ctrl[name])) {
43177      ctrl[name] = undefined;
43178    }
43179  }
43180
43181  function cachedToggleClass(className, switchValue) {
43182    if (switchValue && !classCache[className]) {
43183      $animate.addClass($element, className);
43184      classCache[className] = true;
43185    } else if (!switchValue && classCache[className]) {
43186      $animate.removeClass($element, className);
43187      classCache[className] = false;
43188    }
43189  }
43190
43191  function toggleValidationCss(validationErrorKey, isValid) {
43192    validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
43193
43194    cachedToggleClass(VALID_CLASS + validationErrorKey, isValid === true);
43195    cachedToggleClass(INVALID_CLASS + validationErrorKey, isValid === false);
43196  }
43197}
43198
43199function isObjectEmpty(obj) {
43200  if (obj) {
43201    for (var prop in obj) {
43202      if (obj.hasOwnProperty(prop)) {
43203        return false;
43204      }
43205    }
43206  }
43207  return true;
43208}
43209
43210/**
43211 * @ngdoc directive
43212 * @name ngNonBindable
43213 * @restrict AC
43214 * @priority 1000
43215 *
43216 * @description
43217 * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current
43218 * DOM element. This is useful if the element contains what appears to be Angular directives and
43219 * bindings but which should be ignored by Angular. This could be the case if you have a site that
43220 * displays snippets of code, for instance.
43221 *
43222 * @element ANY
43223 *
43224 * @example
43225 * In this example there are two locations where a simple interpolation binding (`{{}}`) is present,
43226 * but the one wrapped in `ngNonBindable` is left alone.
43227 *
43228 * @example
43229    <example>
43230      <file name="index.html">
43231        <div>Normal: {{1 + 2}}</div>
43232        <div ng-non-bindable>Ignored: {{1 + 2}}</div>
43233      </file>
43234      <file name="protractor.js" type="protractor">
43235       it('should check ng-non-bindable', function() {
43236         expect(element(by.binding('1 + 2')).getText()).toContain('3');
43237         expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/);
43238       });
43239      </file>
43240    </example>
43241 */
43242var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
43243
43244/* global jqLiteRemove */
43245
43246var ngOptionsMinErr = minErr('ngOptions');
43247
43248/**
43249 * @ngdoc directive
43250 * @name ngOptions
43251 * @restrict A
43252 *
43253 * @description
43254 *
43255 * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
43256 * elements for the `<select>` element using the array or object obtained by evaluating the
43257 * `ngOptions` comprehension expression.
43258 *
43259 * In many cases, `ngRepeat` can be used on `<option>` elements instead of `ngOptions` to achieve a
43260 * similar result. However, `ngOptions` provides some benefits such as reducing memory and
43261 * increasing speed by not creating a new scope for each repeated instance, as well as providing
43262 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
43263 * comprehension expression. `ngOptions` should be used when the `<select>` model needs to be bound
43264 *  to a non-string value. This is because an option element can only be bound to string values at
43265 * present.
43266 *
43267 * When an item in the `<select>` menu is selected, the array element or object property
43268 * represented by the selected option will be bound to the model identified by the `ngModel`
43269 * directive.
43270 *
43271 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
43272 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
43273 * option. See example below for demonstration.
43274 *
43275 * ## Complex Models (objects or collections)
43276 *
43277 * **Note:** By default, `ngModel` watches the model by reference, not value. This is important when
43278 * binding any input directive to a model that is an object or a collection.
43279 *
43280 * Since this is a common situation for `ngOptions` the directive additionally watches the model using
43281 * `$watchCollection` when the select has the `multiple` attribute or when there is a `track by` clause in
43282 * the options expression. This allows ngOptions to trigger a re-rendering of the options even if the actual
43283 * object/collection has not changed identity but only a property on the object or an item in the collection
43284 * changes.
43285 *
43286 * Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection
43287 * if the model is an array). This means that changing a property deeper inside the object/collection that the
43288 * first level will not trigger a re-rendering.
43289 *
43290 *
43291 * ## `select` **`as`**
43292 *
43293 * Using `select` **`as`** will bind the result of the `select` expression to the model, but
43294 * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
43295 * or property name (for object data sources) of the value within the collection. If a **`track by`** expression
43296 * is used, the result of that expression will be set as the value of the `option` and `select` elements.
43297 *
43298 *
43299 * ### `select` **`as`** and **`track by`**
43300 *
43301 * <div class="alert alert-warning">
43302 * Do not use `select` **`as`** and **`track by`** in the same expression. They are not designed to work together.
43303 * </div>
43304 *
43305 * Consider the following example:
43306 *
43307 * ```html
43308 * <select ng-options="item.subItem as item.label for item in values track by item.id" ng-model="selected"></select>
43309 * ```
43310 *
43311 * ```js
43312 * $scope.values = [{
43313 *   id: 1,
43314 *   label: 'aLabel',
43315 *   subItem: { name: 'aSubItem' }
43316 * }, {
43317 *   id: 2,
43318 *   label: 'bLabel',
43319 *   subItem: { name: 'bSubItem' }
43320 * }];
43321 *
43322 * $scope.selected = { name: 'aSubItem' };
43323 * ```
43324 *
43325 * With the purpose of preserving the selection, the **`track by`** expression is always applied to the element
43326 * of the data source (to `item` in this example). To calculate whether an element is selected, we do the
43327 * following:
43328 *
43329 * 1. Apply **`track by`** to the elements in the array. In the example: `[1, 2]`
43330 * 2. Apply **`track by`** to the already selected value in `ngModel`.
43331 *    In the example: this is not possible as **`track by`** refers to `item.id`, but the selected
43332 *    value from `ngModel` is `{name: 'aSubItem'}`, so the **`track by`** expression is applied to
43333 *    a wrong object, the selected element can't be found, `<select>` is always reset to the "not
43334 *    selected" option.
43335 *
43336 *
43337 * @param {string} ngModel Assignable angular expression to data-bind to.
43338 * @param {string=} name Property name of the form under which the control is published.
43339 * @param {string=} required The control is considered valid only if value is entered.
43340 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
43341 *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
43342 *    `required` when you want to data-bind to the `required` attribute.
43343 * @param {comprehension_expression=} ngOptions in one of the following forms:
43344 *
43345 *   * for array data sources:
43346 *     * `label` **`for`** `value` **`in`** `array`
43347 *     * `select` **`as`** `label` **`for`** `value` **`in`** `array`
43348 *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
43349 *     * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array`
43350 *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
43351 *     * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
43352 *     * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr`
43353 *        (for including a filter with `track by`)
43354 *   * for object data sources:
43355 *     * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
43356 *     * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
43357 *     * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
43358 *     * `label` **`disable when`** `disable` **`for (`**`key`**`,`** `value`**`) in`** `object`
43359 *     * `select` **`as`** `label` **`group by`** `group`
43360 *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
43361 *     * `select` **`as`** `label` **`disable when`** `disable`
43362 *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
43363 *
43364 * Where:
43365 *
43366 *   * `array` / `object`: an expression which evaluates to an array / object to iterate over.
43367 *   * `value`: local variable which will refer to each item in the `array` or each property value
43368 *      of `object` during iteration.
43369 *   * `key`: local variable which will refer to a property name in `object` during iteration.
43370 *   * `label`: The result of this expression will be the label for `<option>` element. The
43371 *     `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
43372 *   * `select`: The result of this expression will be bound to the model of the parent `<select>`
43373 *      element. If not specified, `select` expression will default to `value`.
43374 *   * `group`: The result of this expression will be used to group options using the `<optgroup>`
43375 *      DOM element.
43376 *   * `disable`: The result of this expression will be used to disable the rendered `<option>`
43377 *      element. Return `true` to disable.
43378 *   * `trackexpr`: Used when working with an array of objects. The result of this expression will be
43379 *      used to identify the objects in the array. The `trackexpr` will most likely refer to the
43380 *     `value` variable (e.g. `value.propertyName`). With this the selection is preserved
43381 *      even when the options are recreated (e.g. reloaded from the server).
43382 *
43383 * @example
43384    <example module="selectExample">
43385      <file name="index.html">
43386        <script>
43387        angular.module('selectExample', [])
43388          .controller('ExampleController', ['$scope', function($scope) {
43389            $scope.colors = [
43390              {name:'black', shade:'dark'},
43391              {name:'white', shade:'light', notAnOption: true},
43392              {name:'red', shade:'dark'},
43393              {name:'blue', shade:'dark', notAnOption: true},
43394              {name:'yellow', shade:'light', notAnOption: false}
43395            ];
43396            $scope.myColor = $scope.colors[2]; // red
43397          }]);
43398        </script>
43399        <div ng-controller="ExampleController">
43400          <ul>
43401            <li ng-repeat="color in colors">
43402              <label>Name: <input ng-model="color.name"></label>
43403              <label><input type="checkbox" ng-model="color.notAnOption"> Disabled?</label>
43404              <button ng-click="colors.splice($index, 1)" aria-label="Remove">X</button>
43405            </li>
43406            <li>
43407              <button ng-click="colors.push({})">add</button>
43408            </li>
43409          </ul>
43410          <hr/>
43411          <label>Color (null not allowed):
43412            <select ng-model="myColor" ng-options="color.name for color in colors"></select>
43413          </label><br/>
43414          <label>Color (null allowed):
43415          <span  class="nullable">
43416            <select ng-model="myColor" ng-options="color.name for color in colors">
43417              <option value="">-- choose color --</option>
43418            </select>
43419          </span></label><br/>
43420
43421          <label>Color grouped by shade:
43422            <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors">
43423            </select>
43424          </label><br/>
43425
43426          <label>Color grouped by shade, with some disabled:
43427            <select ng-model="myColor"
43428                  ng-options="color.name group by color.shade disable when color.notAnOption for color in colors">
43429            </select>
43430          </label><br/>
43431
43432
43433
43434          Select <button ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</button>.
43435          <br/>
43436          <hr/>
43437          Currently selected: {{ {selected_color:myColor} }}
43438          <div style="border:solid 1px black; height:20px"
43439               ng-style="{'background-color':myColor.name}">
43440          </div>
43441        </div>
43442      </file>
43443      <file name="protractor.js" type="protractor">
43444         it('should check ng-options', function() {
43445           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red');
43446           element.all(by.model('myColor')).first().click();
43447           element.all(by.css('select[ng-model="myColor"] option')).first().click();
43448           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black');
43449           element(by.css('.nullable select[ng-model="myColor"]')).click();
43450           element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click();
43451           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null');
43452         });
43453      </file>
43454    </example>
43455 */
43456
43457// jshint maxlen: false
43458//                     //00001111111111000000000002222222222000000000000000000000333333333300000000000000000000000004444444444400000000000005555555555555550000000006666666666666660000000777777777777777000000000000000888888888800000000000000000009999999999
43459var 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]+?))?$/;
43460                        // 1: value expression (valueFn)
43461                        // 2: label expression (displayFn)
43462                        // 3: group by expression (groupByFn)
43463                        // 4: disable when expression (disableWhenFn)
43464                        // 5: array item variable name
43465                        // 6: object item key variable name
43466                        // 7: object item value variable name
43467                        // 8: collection expression
43468                        // 9: track by expression
43469// jshint maxlen: 100
43470
43471
43472var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
43473
43474  function parseOptionsExpression(optionsExp, selectElement, scope) {
43475
43476    var match = optionsExp.match(NG_OPTIONS_REGEXP);
43477    if (!(match)) {
43478      throw ngOptionsMinErr('iexp',
43479        "Expected expression in form of " +
43480        "'_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
43481        " but got '{0}'. Element: {1}",
43482        optionsExp, startingTag(selectElement));
43483    }
43484
43485    // Extract the parts from the ngOptions expression
43486
43487    // The variable name for the value of the item in the collection
43488    var valueName = match[5] || match[7];
43489    // The variable name for the key of the item in the collection
43490    var keyName = match[6];
43491
43492    // An expression that generates the viewValue for an option if there is a label expression
43493    var selectAs = / as /.test(match[0]) && match[1];
43494    // An expression that is used to track the id of each object in the options collection
43495    var trackBy = match[9];
43496    // An expression that generates the viewValue for an option if there is no label expression
43497    var valueFn = $parse(match[2] ? match[1] : valueName);
43498    var selectAsFn = selectAs && $parse(selectAs);
43499    var viewValueFn = selectAsFn || valueFn;
43500    var trackByFn = trackBy && $parse(trackBy);
43501
43502    // Get the value by which we are going to track the option
43503    // if we have a trackFn then use that (passing scope and locals)
43504    // otherwise just hash the given viewValue
43505    var getTrackByValueFn = trackBy ?
43506                              function(value, locals) { return trackByFn(scope, locals); } :
43507                              function getHashOfValue(value) { return hashKey(value); };
43508    var getTrackByValue = function(value, key) {
43509      return getTrackByValueFn(value, getLocals(value, key));
43510    };
43511
43512    var displayFn = $parse(match[2] || match[1]);
43513    var groupByFn = $parse(match[3] || '');
43514    var disableWhenFn = $parse(match[4] || '');
43515    var valuesFn = $parse(match[8]);
43516
43517    var locals = {};
43518    var getLocals = keyName ? function(value, key) {
43519      locals[keyName] = key;
43520      locals[valueName] = value;
43521      return locals;
43522    } : function(value) {
43523      locals[valueName] = value;
43524      return locals;
43525    };
43526
43527
43528    function Option(selectValue, viewValue, label, group, disabled) {
43529      this.selectValue = selectValue;
43530      this.viewValue = viewValue;
43531      this.label = label;
43532      this.group = group;
43533      this.disabled = disabled;
43534    }
43535
43536    function getOptionValuesKeys(optionValues) {
43537      var optionValuesKeys;
43538
43539      if (!keyName && isArrayLike(optionValues)) {
43540        optionValuesKeys = optionValues;
43541      } else {
43542        // if object, extract keys, in enumeration order, unsorted
43543        optionValuesKeys = [];
43544        for (var itemKey in optionValues) {
43545          if (optionValues.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') {
43546            optionValuesKeys.push(itemKey);
43547          }
43548        }
43549      }
43550      return optionValuesKeys;
43551    }
43552
43553    return {
43554      trackBy: trackBy,
43555      getTrackByValue: getTrackByValue,
43556      getWatchables: $parse(valuesFn, function(optionValues) {
43557        // Create a collection of things that we would like to watch (watchedArray)
43558        // so that they can all be watched using a single $watchCollection
43559        // that only runs the handler once if anything changes
43560        var watchedArray = [];
43561        optionValues = optionValues || [];
43562
43563        var optionValuesKeys = getOptionValuesKeys(optionValues);
43564        var optionValuesLength = optionValuesKeys.length;
43565        for (var index = 0; index < optionValuesLength; index++) {
43566          var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
43567          var value = optionValues[key];
43568
43569          var locals = getLocals(optionValues[key], key);
43570          var selectValue = getTrackByValueFn(optionValues[key], locals);
43571          watchedArray.push(selectValue);
43572
43573          // Only need to watch the displayFn if there is a specific label expression
43574          if (match[2] || match[1]) {
43575            var label = displayFn(scope, locals);
43576            watchedArray.push(label);
43577          }
43578
43579          // Only need to watch the disableWhenFn if there is a specific disable expression
43580          if (match[4]) {
43581            var disableWhen = disableWhenFn(scope, locals);
43582            watchedArray.push(disableWhen);
43583          }
43584        }
43585        return watchedArray;
43586      }),
43587
43588      getOptions: function() {
43589
43590        var optionItems = [];
43591        var selectValueMap = {};
43592
43593        // The option values were already computed in the `getWatchables` fn,
43594        // which must have been called to trigger `getOptions`
43595        var optionValues = valuesFn(scope) || [];
43596        var optionValuesKeys = getOptionValuesKeys(optionValues);
43597        var optionValuesLength = optionValuesKeys.length;
43598
43599        for (var index = 0; index < optionValuesLength; index++) {
43600          var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
43601          var value = optionValues[key];
43602          var locals = getLocals(value, key);
43603          var viewValue = viewValueFn(scope, locals);
43604          var selectValue = getTrackByValueFn(viewValue, locals);
43605          var label = displayFn(scope, locals);
43606          var group = groupByFn(scope, locals);
43607          var disabled = disableWhenFn(scope, locals);
43608          var optionItem = new Option(selectValue, viewValue, label, group, disabled);
43609
43610          optionItems.push(optionItem);
43611          selectValueMap[selectValue] = optionItem;
43612        }
43613
43614        return {
43615          items: optionItems,
43616          selectValueMap: selectValueMap,
43617          getOptionFromViewValue: function(value) {
43618            return selectValueMap[getTrackByValue(value)];
43619          },
43620          getViewValueFromOption: function(option) {
43621            // If the viewValue could be an object that may be mutated by the application,
43622            // we need to make a copy and not return the reference to the value on the option.
43623            return trackBy ? angular.copy(option.viewValue) : option.viewValue;
43624          }
43625        };
43626      }
43627    };
43628  }
43629
43630
43631  // we can't just jqLite('<option>') since jqLite is not smart enough
43632  // to create it in <select> and IE barfs otherwise.
43633  var optionTemplate = document.createElement('option'),
43634      optGroupTemplate = document.createElement('optgroup');
43635
43636  return {
43637    restrict: 'A',
43638    terminal: true,
43639    require: ['select', '?ngModel'],
43640    link: function(scope, selectElement, attr, ctrls) {
43641
43642      // if ngModel is not defined, we don't need to do anything
43643      var ngModelCtrl = ctrls[1];
43644      if (!ngModelCtrl) return;
43645
43646      var selectCtrl = ctrls[0];
43647      var multiple = attr.multiple;
43648
43649      // The emptyOption allows the application developer to provide their own custom "empty"
43650      // option when the viewValue does not match any of the option values.
43651      var emptyOption;
43652      for (var i = 0, children = selectElement.children(), ii = children.length; i < ii; i++) {
43653        if (children[i].value === '') {
43654          emptyOption = children.eq(i);
43655          break;
43656        }
43657      }
43658
43659      var providedEmptyOption = !!emptyOption;
43660
43661      var unknownOption = jqLite(optionTemplate.cloneNode(false));
43662      unknownOption.val('?');
43663
43664      var options;
43665      var ngOptions = parseOptionsExpression(attr.ngOptions, selectElement, scope);
43666
43667
43668      var renderEmptyOption = function() {
43669        if (!providedEmptyOption) {
43670          selectElement.prepend(emptyOption);
43671        }
43672        selectElement.val('');
43673        emptyOption.prop('selected', true); // needed for IE
43674        emptyOption.attr('selected', true);
43675      };
43676
43677      var removeEmptyOption = function() {
43678        if (!providedEmptyOption) {
43679          emptyOption.remove();
43680        }
43681      };
43682
43683
43684      var renderUnknownOption = function() {
43685        selectElement.prepend(unknownOption);
43686        selectElement.val('?');
43687        unknownOption.prop('selected', true); // needed for IE
43688        unknownOption.attr('selected', true);
43689      };
43690
43691      var removeUnknownOption = function() {
43692        unknownOption.remove();
43693      };
43694
43695
43696      // Update the controller methods for multiple selectable options
43697      if (!multiple) {
43698
43699        selectCtrl.writeValue = function writeNgOptionsValue(value) {
43700          var option = options.getOptionFromViewValue(value);
43701
43702          if (option && !option.disabled) {
43703            if (selectElement[0].value !== option.selectValue) {
43704              removeUnknownOption();
43705              removeEmptyOption();
43706
43707              selectElement[0].value = option.selectValue;
43708              option.element.selected = true;
43709              option.element.setAttribute('selected', 'selected');
43710            }
43711          } else {
43712            if (value === null || providedEmptyOption) {
43713              removeUnknownOption();
43714              renderEmptyOption();
43715            } else {
43716              removeEmptyOption();
43717              renderUnknownOption();
43718            }
43719          }
43720        };
43721
43722        selectCtrl.readValue = function readNgOptionsValue() {
43723
43724          var selectedOption = options.selectValueMap[selectElement.val()];
43725
43726          if (selectedOption && !selectedOption.disabled) {
43727            removeEmptyOption();
43728            removeUnknownOption();
43729            return options.getViewValueFromOption(selectedOption);
43730          }
43731          return null;
43732        };
43733
43734        // If we are using `track by` then we must watch the tracked value on the model
43735        // since ngModel only watches for object identity change
43736        if (ngOptions.trackBy) {
43737          scope.$watch(
43738            function() { return ngOptions.getTrackByValue(ngModelCtrl.$viewValue); },
43739            function() { ngModelCtrl.$render(); }
43740          );
43741        }
43742
43743      } else {
43744
43745        ngModelCtrl.$isEmpty = function(value) {
43746          return !value || value.length === 0;
43747        };
43748
43749
43750        selectCtrl.writeValue = function writeNgOptionsMultiple(value) {
43751          options.items.forEach(function(option) {
43752            option.element.selected = false;
43753          });
43754
43755          if (value) {
43756            value.forEach(function(item) {
43757              var option = options.getOptionFromViewValue(item);
43758              if (option && !option.disabled) option.element.selected = true;
43759            });
43760          }
43761        };
43762
43763
43764        selectCtrl.readValue = function readNgOptionsMultiple() {
43765          var selectedValues = selectElement.val() || [],
43766              selections = [];
43767
43768          forEach(selectedValues, function(value) {
43769            var option = options.selectValueMap[value];
43770            if (option && !option.disabled) selections.push(options.getViewValueFromOption(option));
43771          });
43772
43773          return selections;
43774        };
43775
43776        // If we are using `track by` then we must watch these tracked values on the model
43777        // since ngModel only watches for object identity change
43778        if (ngOptions.trackBy) {
43779
43780          scope.$watchCollection(function() {
43781            if (isArray(ngModelCtrl.$viewValue)) {
43782              return ngModelCtrl.$viewValue.map(function(value) {
43783                return ngOptions.getTrackByValue(value);
43784              });
43785            }
43786          }, function() {
43787            ngModelCtrl.$render();
43788          });
43789
43790        }
43791      }
43792
43793
43794      if (providedEmptyOption) {
43795
43796        // we need to remove it before calling selectElement.empty() because otherwise IE will
43797        // remove the label from the element. wtf?
43798        emptyOption.remove();
43799
43800        // compile the element since there might be bindings in it
43801        $compile(emptyOption)(scope);
43802
43803        // remove the class, which is added automatically because we recompile the element and it
43804        // becomes the compilation root
43805        emptyOption.removeClass('ng-scope');
43806      } else {
43807        emptyOption = jqLite(optionTemplate.cloneNode(false));
43808      }
43809
43810      // We need to do this here to ensure that the options object is defined
43811      // when we first hit it in writeNgOptionsValue
43812      updateOptions();
43813
43814      // We will re-render the option elements if the option values or labels change
43815      scope.$watchCollection(ngOptions.getWatchables, updateOptions);
43816
43817      // ------------------------------------------------------------------ //
43818
43819
43820      function updateOptionElement(option, element) {
43821        option.element = element;
43822        element.disabled = option.disabled;
43823        // NOTE: The label must be set before the value, otherwise IE10/11/EDGE create unresponsive
43824        // selects in certain circumstances when multiple selects are next to each other and display
43825        // the option list in listbox style, i.e. the select is [multiple], or specifies a [size].
43826        // See https://github.com/angular/angular.js/issues/11314 for more info.
43827        // This is unfortunately untestable with unit / e2e tests
43828        if (option.label !== element.label) {
43829          element.label = option.label;
43830          element.textContent = option.label;
43831        }
43832        if (option.value !== element.value) element.value = option.selectValue;
43833      }
43834
43835      function addOrReuseElement(parent, current, type, templateElement) {
43836        var element;
43837        // Check whether we can reuse the next element
43838        if (current && lowercase(current.nodeName) === type) {
43839          // The next element is the right type so reuse it
43840          element = current;
43841        } else {
43842          // The next element is not the right type so create a new one
43843          element = templateElement.cloneNode(false);
43844          if (!current) {
43845            // There are no more elements so just append it to the select
43846            parent.appendChild(element);
43847          } else {
43848            // The next element is not a group so insert the new one
43849            parent.insertBefore(element, current);
43850          }
43851        }
43852        return element;
43853      }
43854
43855
43856      function removeExcessElements(current) {
43857        var next;
43858        while (current) {
43859          next = current.nextSibling;
43860          jqLiteRemove(current);
43861          current = next;
43862        }
43863      }
43864
43865
43866      function skipEmptyAndUnknownOptions(current) {
43867        var emptyOption_ = emptyOption && emptyOption[0];
43868        var unknownOption_ = unknownOption && unknownOption[0];
43869
43870        if (emptyOption_ || unknownOption_) {
43871          while (current &&
43872                (current === emptyOption_ ||
43873                current === unknownOption_ ||
43874                emptyOption_ && emptyOption_.nodeType === NODE_TYPE_COMMENT)) {
43875            // Empty options might have directives that transclude
43876            // and insert comments (e.g. ngIf)
43877            current = current.nextSibling;
43878          }
43879        }
43880        return current;
43881      }
43882
43883
43884      function updateOptions() {
43885
43886        var previousValue = options && selectCtrl.readValue();
43887
43888        options = ngOptions.getOptions();
43889
43890        var groupMap = {};
43891        var currentElement = selectElement[0].firstChild;
43892
43893        // Ensure that the empty option is always there if it was explicitly provided
43894        if (providedEmptyOption) {
43895          selectElement.prepend(emptyOption);
43896        }
43897
43898        currentElement = skipEmptyAndUnknownOptions(currentElement);
43899
43900        options.items.forEach(function updateOption(option) {
43901          var group;
43902          var groupElement;
43903          var optionElement;
43904
43905          if (option.group) {
43906
43907            // This option is to live in a group
43908            // See if we have already created this group
43909            group = groupMap[option.group];
43910
43911            if (!group) {
43912
43913              // We have not already created this group
43914              groupElement = addOrReuseElement(selectElement[0],
43915                                               currentElement,
43916                                               'optgroup',
43917                                               optGroupTemplate);
43918              // Move to the next element
43919              currentElement = groupElement.nextSibling;
43920
43921              // Update the label on the group element
43922              groupElement.label = option.group;
43923
43924              // Store it for use later
43925              group = groupMap[option.group] = {
43926                groupElement: groupElement,
43927                currentOptionElement: groupElement.firstChild
43928              };
43929
43930            }
43931
43932            // So now we have a group for this option we add the option to the group
43933            optionElement = addOrReuseElement(group.groupElement,
43934                                              group.currentOptionElement,
43935                                              'option',
43936                                              optionTemplate);
43937            updateOptionElement(option, optionElement);
43938            // Move to the next element
43939            group.currentOptionElement = optionElement.nextSibling;
43940
43941          } else {
43942
43943            // This option is not in a group
43944            optionElement = addOrReuseElement(selectElement[0],
43945                                              currentElement,
43946                                              'option',
43947                                              optionTemplate);
43948            updateOptionElement(option, optionElement);
43949            // Move to the next element
43950            currentElement = optionElement.nextSibling;
43951          }
43952        });
43953
43954
43955        // Now remove all excess options and group
43956        Object.keys(groupMap).forEach(function(key) {
43957          removeExcessElements(groupMap[key].currentOptionElement);
43958        });
43959        removeExcessElements(currentElement);
43960
43961        ngModelCtrl.$render();
43962
43963        // Check to see if the value has changed due to the update to the options
43964        if (!ngModelCtrl.$isEmpty(previousValue)) {
43965          var nextValue = selectCtrl.readValue();
43966          if (ngOptions.trackBy ? !equals(previousValue, nextValue) : previousValue !== nextValue) {
43967            ngModelCtrl.$setViewValue(nextValue);
43968            ngModelCtrl.$render();
43969          }
43970        }
43971
43972      }
43973
43974    }
43975  };
43976}];
43977
43978/**
43979 * @ngdoc directive
43980 * @name ngPluralize
43981 * @restrict EA
43982 *
43983 * @description
43984 * `ngPluralize` is a directive that displays messages according to en-US localization rules.
43985 * These rules are bundled with angular.js, but can be overridden
43986 * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive
43987 * by specifying the mappings between
43988 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
43989 * and the strings to be displayed.
43990 *
43991 * # Plural categories and explicit number rules
43992 * There are two
43993 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
43994 * in Angular's default en-US locale: "one" and "other".
43995 *
43996 * While a plural category may match many numbers (for example, in en-US locale, "other" can match
43997 * any number that is not 1), an explicit number rule can only match one number. For example, the
43998 * explicit number rule for "3" matches the number 3. There are examples of plural categories
43999 * and explicit number rules throughout the rest of this documentation.
44000 *
44001 * # Configuring ngPluralize
44002 * You configure ngPluralize by providing 2 attributes: `count` and `when`.
44003 * You can also provide an optional attribute, `offset`.
44004 *
44005 * The value of the `count` attribute can be either a string or an {@link guide/expression
44006 * Angular expression}; these are evaluated on the current scope for its bound value.
44007 *
44008 * The `when` attribute specifies the mappings between plural categories and the actual
44009 * string to be displayed. The value of the attribute should be a JSON object.
44010 *
44011 * The following example shows how to configure ngPluralize:
44012 *
44013 * ```html
44014 * <ng-pluralize count="personCount"
44015                 when="{'0': 'Nobody is viewing.',
44016 *                      'one': '1 person is viewing.',
44017 *                      'other': '{} people are viewing.'}">
44018 * </ng-pluralize>
44019 *```
44020 *
44021 * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
44022 * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
44023 * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
44024 * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
44025 * show "a dozen people are viewing".
44026 *
44027 * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
44028 * into pluralized strings. In the previous example, Angular will replace `{}` with
44029 * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
44030 * for <span ng-non-bindable>{{numberExpression}}</span>.
44031 *
44032 * If no rule is defined for a category, then an empty string is displayed and a warning is generated.
44033 * Note that some locales define more categories than `one` and `other`. For example, fr-fr defines `few` and `many`.
44034 *
44035 * # Configuring ngPluralize with offset
44036 * The `offset` attribute allows further customization of pluralized text, which can result in
44037 * a better user experience. For example, instead of the message "4 people are viewing this document",
44038 * you might display "John, Kate and 2 others are viewing this document".
44039 * The offset attribute allows you to offset a number by any desired value.
44040 * Let's take a look at an example:
44041 *
44042 * ```html
44043 * <ng-pluralize count="personCount" offset=2
44044 *               when="{'0': 'Nobody is viewing.',
44045 *                      '1': '{{person1}} is viewing.',
44046 *                      '2': '{{person1}} and {{person2}} are viewing.',
44047 *                      'one': '{{person1}}, {{person2}} and one other person are viewing.',
44048 *                      'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
44049 * </ng-pluralize>
44050 * ```
44051 *
44052 * Notice that we are still using two plural categories(one, other), but we added
44053 * three explicit number rules 0, 1 and 2.
44054 * When one person, perhaps John, views the document, "John is viewing" will be shown.
44055 * When three people view the document, no explicit number rule is found, so
44056 * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
44057 * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing"
44058 * is shown.
44059 *
44060 * Note that when you specify offsets, you must provide explicit number rules for
44061 * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
44062 * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
44063 * plural categories "one" and "other".
44064 *
44065 * @param {string|expression} count The variable to be bound to.
44066 * @param {string} when The mapping between plural category to its corresponding strings.
44067 * @param {number=} offset Offset to deduct from the total number.
44068 *
44069 * @example
44070    <example module="pluralizeExampl
vendor: 4,913 bytes, lines 44070-44184
44070e">
44071      <file name="index.html">
44072        <script>
44073          angular.module('pluralizeExample', [])
44074            .controller('ExampleController', ['$scope', function($scope) {
44075              $scope.person1 = 'Igor';
44076              $scope.person2 = 'Misko';
44077              $scope.personCount = 1;
44078            }]);
44079        </script>
44080        <div ng-controller="ExampleController">
44081          <label>Person 1:<input type="text" ng-model="person1" value="Igor" /></label><br/>
44082          <label>Person 2:<input type="text" ng-model="person2" value="Misko" /></label><br/>
44083          <label>Number of People:<input type="text" ng-model="personCount" value="1" /></label><br/>
44084
44085          <!--- Example with simple pluralization rules for en locale --->
44086          Without Offset:
44087          <ng-pluralize count="personCount"
44088                        when="{'0': 'Nobody is viewing.',
44089                               'one': '1 person is viewing.',
44090                               'other': '{} people are viewing.'}">
44091          </ng-pluralize><br>
44092
44093          <!--- Example with offset --->
44094          With Offset(2):
44095          <ng-pluralize count="personCount" offset=2
44096                        when="{'0': 'Nobody is viewing.',
44097                               '1': '{{person1}} is viewing.',
44098                               '2': '{{person1}} and {{person2}} are viewing.',
44099                               'one': '{{person1}}, {{person2}} and one other person are viewing.',
44100                               'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
44101          </ng-pluralize>
44102        </div>
44103      </file>
44104      <file name="protractor.js" type="protractor">
44105        it('should show correct pluralized string', function() {
44106          var withoutOffset = element.all(by.css('ng-pluralize')).get(0);
44107          var withOffset = element.all(by.css('ng-pluralize')).get(1);
44108          var countInput = element(by.model('personCount'));
44109
44110          expect(withoutOffset.getText()).toEqual('1 person is viewing.');
44111          expect(withOffset.getText()).toEqual('Igor is viewing.');
44112
44113          countInput.clear();
44114          countInput.sendKeys('0');
44115
44116          expect(withoutOffset.getText()).toEqual('Nobody is viewing.');
44117          expect(withOffset.getText()).toEqual('Nobody is viewing.');
44118
44119          countInput.clear();
44120          countInput.sendKeys('2');
44121
44122          expect(withoutOffset.getText()).toEqual('2 people are viewing.');
44123          expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');
44124
44125          countInput.clear();
44126          countInput.sendKeys('3');
44127
44128          expect(withoutOffset.getText()).toEqual('3 people are viewing.');
44129          expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');
44130
44131          countInput.clear();
44132          countInput.sendKeys('4');
44133
44134          expect(withoutOffset.getText()).toEqual('4 people are viewing.');
44135          expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');
44136        });
44137        it('should show data-bound names', function() {
44138          var withOffset = element.all(by.css('ng-pluralize')).get(1);
44139          var personCount = element(by.model('personCount'));
44140          var person1 = element(by.model('person1'));
44141          var person2 = element(by.model('person2'));
44142          personCount.clear();
44143          personCount.sendKeys('4');
44144          person1.clear();
44145          person1.sendKeys('Di');
44146          person2.clear();
44147          person2.sendKeys('Vojta');
44148          expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');
44149        });
44150      </file>
44151    </example>
44152 */
44153var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale, $interpolate, $log) {
44154  var BRACE = /{}/g,
44155      IS_WHEN = /^when(Minus)?(.+)$/;
44156
44157  return {
44158    link: function(scope, element, attr) {
44159      var numberExp = attr.count,
44160          whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs
44161          offset = attr.offset || 0,
44162          whens = scope.$eval(whenExp) || {},
44163          whensExpFns = {},
44164          startSymbol = $interpolate.startSymbol(),
44165          endSymbol = $interpolate.endSymbol(),
44166          braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol,
44167          watchRemover = angular.noop,
44168          lastCount;
44169
44170      forEach(attr, function(expression, attributeName) {
44171        var tmpMatch = IS_WHEN.exec(attributeName);
44172        if (tmpMatch) {
44173          var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]);
44174          whens[whenKey] = element.attr(attr.$attr[attributeName]);
44175        }
44176      });
44177      forEach(whens, function(expression, key) {
44178        whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement));
44179
44180      });
44181
44182      scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) {
44183        var count = parseFloat(newVal);
44184        var countIsNaN = isN
vendor: 10,776 bytes, lines 44184-44423
44184aN(count);
44185
44186        if (!countIsNaN && !(count in whens)) {
44187          // If an explicit number rule such as 1, 2, 3... is defined, just use it.
44188          // Otherwise, check it against pluralization rules in $locale service.
44189          count = $locale.pluralCat(count - offset);
44190        }
44191
44192        // If both `count` and `lastCount` are NaN, we don't need to re-register a watch.
44193        // In JS `NaN !== NaN`, so we have to exlicitly check.
44194        if ((count !== lastCount) && !(countIsNaN && isNumber(lastCount) && isNaN(lastCount))) {
44195          watchRemover();
44196          var whenExpFn = whensExpFns[count];
44197          if (isUndefined(whenExpFn)) {
44198            if (newVal != null) {
44199              $log.debug("ngPluralize: no rule defined for '" + count + "' in " + whenExp);
44200            }
44201            watchRemover = noop;
44202            updateElementText();
44203          } else {
44204            watchRemover = scope.$watch(whenExpFn, updateElementText);
44205          }
44206          lastCount = count;
44207        }
44208      });
44209
44210      function updateElementText(newText) {
44211        element.text(newText || '');
44212      }
44213    }
44214  };
44215}];
44216
44217/**
44218 * @ngdoc directive
44219 * @name ngRepeat
44220 * @multiElement
44221 *
44222 * @description
44223 * The `ngRepeat` directive instantiates a template once per item from a collection. Each template
44224 * instance gets its own scope, where the given loop variable is set to the current collection item,
44225 * and `$index` is set to the item index or key.
44226 *
44227 * Special properties are exposed on the local scope of each template instance, including:
44228 *
44229 * | Variable  | Type            | Details                                                                     |
44230 * |-----------|-----------------|-----------------------------------------------------------------------------|
44231 * | `$index`  | {@type number}  | iterator offset of the repeated element (0..length-1)                       |
44232 * | `$first`  | {@type boolean} | true if the repeated element is first in the iterator.                      |
44233 * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. |
44234 * | `$last`   | {@type boolean} | true if the repeated element is last in the iterator.                       |
44235 * | `$even`   | {@type boolean} | true if the iterator position `$index` is even (otherwise false).           |
44236 * | `$odd`    | {@type boolean} | true if the iterator position `$index` is odd (otherwise false).            |
44237 *
44238 * <div class="alert alert-info">
44239 *   Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
44240 *   This may be useful when, for instance, nesting ngRepeats.
44241 * </div>
44242 *
44243 *
44244 * # Iterating over object properties
44245 *
44246 * It is possible to get `ngRepeat` to iterate over the properties of an object using the following
44247 * syntax:
44248 *
44249 * ```js
44250 * <div ng-repeat="(key, value) in myObj"> ... </div>
44251 * ```
44252 *
44253 * You need to be aware that the JavaScript specification does not define the order of keys
44254 * returned for an object. (To mitigate this in Angular 1.3 the `ngRepeat` directive
44255 * used to sort the keys alphabetically.)
44256 *
44257 * Version 1.4 removed the alphabetic sorting. We now rely on the order returned by the browser
44258 * when running `for key in myObj`. It seems that browsers generally follow the strategy of providing
44259 * keys in the order in which they were defined, although there are exceptions when keys are deleted
44260 * and reinstated. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_issues
44261 *
44262 * If this is not desired, the recommended workaround is to convert your object into an array
44263 * that is sorted into the order that you prefer before providing it to `ngRepeat`.  You could
44264 * do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter)
44265 * or implement a `$watch` on the object yourself.
44266 *
44267 *
44268 * # Tracking and Duplicates
44269 *
44270 * When the contents of the collection change, `ngRepeat` makes the corresponding changes to the DOM:
44271 *
44272 * * When an item is added, a new instance of the template is added to the DOM.
44273 * * When an item is removed, its template instance is removed from the DOM.
44274 * * When items are reordered, their respective templates are reordered in the DOM.
44275 *
44276 * By default, `ngRepeat` does not allow duplicate items in arrays. This is because when
44277 * there are duplicates, it is not possible to maintain a one-to-one mapping between collection
44278 * items and DOM elements.
44279 *
44280 * If you do need to repeat duplicate items, you can substitute the default tracking behavior
44281 * with your own using the `track by` expression.
44282 *
44283 * For example, you may track items by the index of each item in the collection, using the
44284 * special scope property `$index`:
44285 * ```html
44286 *    <div ng-repeat="n in [42, 42, 43, 43] track by $index">
44287 *      {{n}}
44288 *    </div>
44289 * ```
44290 *
44291 * You may use arbitrary expressions in `track by`, including references to custom functions
44292 * on the scope:
44293 * ```html
44294 *    <div ng-repeat="n in [42, 42, 43, 43] track by myTrackingFunction(n)">
44295 *      {{n}}
44296 *    </div>
44297 * ```
44298 *
44299 * If you are working with objects that have an identifier property, you can track
44300 * by the identifier instead of the whole object. Should you reload your data later, `ngRepeat`
44301 * will not have to rebuild the DOM elements for items it has already rendered, even if the
44302 * JavaScript objects in the collection have been substituted for new ones:
44303 * ```html
44304 *    <div ng-repeat="model in collection track by model.id">
44305 *      {{model.name}}
44306 *    </div>
44307 * ```
44308 *
44309 * When no `track by` expression is provided, it is equivalent to tracking by the built-in
44310 * `$id` function, which tracks items by their identity:
44311 * ```html
44312 *    <div ng-repeat="obj in collection track by $id(obj)">
44313 *      {{obj.prop}}
44314 *    </div>
44315 * ```
44316 *
44317 * <div class="alert alert-warning">
44318 * **Note:** `track by` must always be the last expression:
44319 * </div>
44320 * ```
44321 * <div ng-repeat="model in collection | orderBy: 'id' as filtered_result track by model.id">
44322 *     {{model.name}}
44323 * </div>
44324 * ```
44325 *
44326 * # Special repeat start and end points
44327 * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
44328 * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
44329 * 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)
44330 * up to and including the ending HTML tag where **ng-repeat-end** is placed.
44331 *
44332 * The example below makes use of this feature:
44333 * ```html
44334 *   <header ng-repeat-start="item in items">
44335 *     Header {{ item }}
44336 *   </header>
44337 *   <div class="body">
44338 *     Body {{ item }}
44339 *   </div>
44340 *   <footer ng-repeat-end>
44341 *     Footer {{ item }}
44342 *   </footer>
44343 * ```
44344 *
44345 * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to:
44346 * ```html
44347 *   <header>
44348 *     Header A
44349 *   </header>
44350 *   <div class="body">
44351 *     Body A
44352 *   </div>
44353 *   <footer>
44354 *     Footer A
44355 *   </footer>
44356 *   <header>
44357 *     Header B
44358 *   </header>
44359 *   <div class="body">
44360 *     Body B
44361 *   </div>
44362 *   <footer>
44363 *     Footer B
44364 *   </footer>
44365 * ```
44366 *
44367 * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such
44368 * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
44369 *
44370 * @animations
44371 * **.enter** - when a new item is added to the list or when an item is revealed after a filter
44372 *
44373 * **.leave** - when an item is removed from the list or when an item is filtered out
44374 *
44375 * **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
44376 *
44377 * @element ANY
44378 * @scope
44379 * @priority 1000
44380 * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These
44381 *   formats are currently supported:
44382 *
44383 *   * `variable in expression` – where variable is the user defined loop variable and `expression`
44384 *     is a scope expression giving the collection to enumerate.
44385 *
44386 *     For example: `album in artist.albums`.
44387 *
44388 *   * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
44389 *     and `expression` is the scope expression giving the collection to enumerate.
44390 *
44391 *     For example: `(name, age) in {'adam':10, 'amalie':12}`.
44392 *
44393 *   * `variable in expression track by tracking_expression` – You can also provide an optional tracking expression
44394 *     which can be used to associate the objects in the collection with the DOM elements. If no tracking expression
44395 *     is specified, ng-repeat associates elements by identity. It is an error to have
44396 *     more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are
44397 *     mapped to the same DOM element, which is not possible.)
44398 *
44399 *     Note that the tracking expression must come last, after any filters, and the alias expression.
44400 *
44401 *     For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
44402 *     will be associated by item identity in the array.
44403 *
44404 *     For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
44405 *     `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
44406 *     with the corresponding item in the array by identity. Moving the same object in array would move the DOM
44407 *     element in the same way in the DOM.
44408 *
44409 *     For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
44410 *     case the object identity does not matter. Two objects are considered equivalent as long as their `id`
44411 *     property is same.
44412 *
44413 *     For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter
44414 *     to items in conjunction with a tracking expression.
44415 *
44416 *   * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the
44417 *     intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message
44418 *     when a filter is active on the repeater, but the filtered result set is empty.
44419 *
44420 *     For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after
44421 *     the items have been processed through the filter.
44422 *
44423 *     Please note that `as [variable name] is not an operator but rather a part of ngRepeat micro-syntax s
44423o it can be used only at the end
44424 *     (and not as operator, inside an expression).
44425 *
44426 *     For example: `item in items | filter : x | orderBy : order | limitTo : limit as results` .
44427 *
44428 * @example
44429 * This example initializes the scope to a list of names and
44430 * then uses `ngRepeat` to display every person:
44431  <example module="ngAnimate" deps="angular-animate.js" animations="true">
44432    <file name="index.html">
44433      <div ng-init="friends = [
44434        {name:'John', age:25, gender:'boy'},
44435        {name:'Jessie', age:30, gender:'girl'},
44436        {name:'Johanna', age:28, gender:'girl'},
44437        {name:'Joy', age:15, gender:'girl'},
44438        {name:'Mary', age:28, gender:'girl'},
44439        {name:'Peter', age:95, gender:'boy'},
44440        {name:'Sebastian', age:50, gender:'boy'},
44441        {name:'Erika', age:27, gender:'girl'},
44442        {name:'Patrick', age:40, gender:'boy'},
44443        {name:'Samantha', age:60, gender:'girl'}
44444      ]">
44445        I have {{friends.length}} friends. They are:
44446        <input type="search" ng-model="q" placeholder="filter friends..." aria-label="filter friends" />
44447        <ul class="example-animate-container">
44448          <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results">
44449            [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
44450          </li>
44451          <li class="animate-repeat" ng-if="results.length == 0">
44452            <strong>No results found...</strong>
44453          </li>
44454        </ul>
44455      </div>
44456    </file>
44457    <file name="animations.css">
44458      .example-animate-container {
44459        background:white;
44460        border:1px solid black;
44461        list-style:none;
44462        margin:0;
44463        padding:0 10px;
44464      }
44465
44466      .animate-repeat {
44467        line-height:40px;
44468        list-style:none;
44469        box-sizing:border-box;
44470      }
44471
44472      .animate-repeat.ng-move,
44473      .animate-repeat.ng-enter,
44474      .animate-repeat.ng-leave {
44475        transition:all linear 0.5s;
44476      }
44477
44478      .animate-repeat.ng-leave.ng-leave-active,
44479      .animate-repeat.ng-move,
44480      .animate-repeat.ng-enter {
44481        opacity:0;
44482        max-height:0;
44483      }
44484
44485      .animate-repeat.ng-leave,
44486      .animate-repeat.ng-move.ng-move-active,
44487      .animate-repeat.ng-enter.ng-enter-active {
44488        opacity:1;
44489        max-height:40px;
44490      }
44491    </file>
44492    <file name="protractor.js" type="protractor">
44493      var friends = element.all(by.repeater('friend in friends'));
44494
44495      it('should render initial data set', function() {
44496        expect(friends.count()).toBe(10);
44497        expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');
44498        expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');
44499        expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');
44500        expect(element(by.binding('friends.length')).getText())
44501            .toMatch("I have 10 friends. They are:");
44502      });
44503
44504       it('should update repeater when filter predicate changes', function() {
44505         expect(friends.count()).toBe(10);
44506
44507         element(by.model('q')).sendKeys('ma');
44508
44509         expect(friends.count()).toBe(2);
44510         expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');
44511         expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');
44512       });
44513      </file>
44514    </example>
44515 */
44516var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
44517  var NG_REMOVED = '$$NG_REMOVED';
44518  var ngRepeatMinErr = minErr('ngRepeat');
44519
44520  var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength) {
44521    // TODO(perf): generate setters to shave off ~40ms or 1-1.5%
44522    scope[valueIdentifier] = value;
44523    if (keyIdentifier) scope[keyIdentifier] = key;
44524    scope.$index = index;
44525    scope.$first = (index === 0);
44526    scope.$last = (index === (arrayLength - 1));
44527    scope.$middle = !(scope.$first || scope.$last);
44528    // jshint bitwise: false
44529    scope.$odd = !(scope.$even = (index&1) === 0);
44530    // jshint bitwise: true
44531  };
44532
44533  var getBlockStart = function(block) {
44534    return block.clone[0];
44535  };
44536
44537  var getBlockEnd = function(block) {
44538    return block.clone[block.clone.length - 1];
44539  };
44540
44541
44542  return {
44543    restrict: 'A',
44544    multiElement: true,
44545    transclude: 'element',
44546    priority: 1000,
44547    terminal: true,
44548    $$tlb: true,
44549    compile: function ngRepeatCompile($element, $attr) {
44550      var expression = $attr.ngRepeat;
44551      var ngRepeatEndComment = document.createComment(' end ngRepeat: ' + expression + ' ');
44552
44553      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*$/);
44554
44555      if (!match) {
44556        throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.",
44557            expression);
44558      }
44559
44560      var lhs = match[1];
44561      var rhs = match[2];
44562      var aliasAs = match[3];
44563      var trackByExp = match[4];
44564
44565      match = lhs.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);
44566
44567      if (!match) {
44568        throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.",
44569            lhs);
44570      }
44571      var valueIdentifier = match[3] || match[1];
44572      var keyIdentifier = match[2];
44573
44574      if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) ||
44575          /^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(aliasAs))) {
44576        throw ngRepeatMinErr('badident', "alias '{0}
44576' is invalid --- must be a valid JS identifier which is not a reserved name.",
44577          aliasAs);
44578      }
44579
44580      var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn;
44581      var hashFnLocals = {$id: hashKey};
44582
44583      if (trackByExp) {
44584        trackByExpGetter = $parse(trackByExp);
44585      } else {
44586        trackByIdArrayFn = function(key, value) {
44587          return hashKey(value);
44588        };
44589        trackByIdObjFn = function(key) {
44590          return key;
44591        };
44592      }
44593
44594      return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) {
44595
44596        if (trackByExpGetter) {
44597          trackByIdExpFn = function(key, value, index) {
44598            // assign key, value, and $index to the locals so that they can be used in hash functions
44599            if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
44600            hashFnLocals[valueIdentifier] = value;
44601            hashFnLocals.$index = index;
44602            return trackByExpGetter($scope, hashFnLocals);
44603          };
44604        }
44605
44606        // Store a list of elements from previous run. This is a hash where key is the item from the
44607        // iterator, and the value is objects with following properties.
44608        //   - scope: bound scope
44609        //   - element: previous element.
44610        //   - index: position
44611        //
44612        // We are using no-proto object so that we don't need to guard against inherited props via
44613        // hasOwnProperty.
44614        var lastBlockMap = createMap();
44615
44616        //watch props
44617        $scope.$watchCollection(rhs, function ngRepeatAction(collection) {
44618          var index, length,
44619              previousNode = $element[0],     // node that cloned nodes should be inserted after
44620                                              // initialized to the comment node anchor
44621              nextNode,
44622              // Same as lastBlockMap but it has the current state. It will become the
44623              // lastBlockMap on the next iteration.
44624              nextBlockMap = createMap(),
44625              collectionLength,
44626              key, value, // key/value of iteration
44627              trackById,
44628              trackByIdFn,
44629              collectionKeys,
44630              block,       // last object information {scope, element, id}
44631              nextBlockOrder,
44632              elementsToRemove;
44633
44634          if (aliasAs) {
44635            $scope[aliasAs] = collection;
44636          }
44637
44638          if (isArrayLike(collection)) {
44639            collectionKeys = collection;
44640            trackByIdFn = trackByIdExpFn || trackByIdArrayFn;
44641          } else {
44642            trackByIdFn = trackByIdExpFn || trackByIdObjFn;
44643            // if object, extract keys, in enumeration order, unsorted
44644            collectionKeys = [];
44645            for (var itemKey in collection) {
44646              if (hasOwnProperty.call(collection, itemKey) && itemKey.charAt(0) !== '$') {
44647                collectionKeys.push(itemKey);
44648              }
44649            }
44650          }
44651
44652          collectionLength = collectionKeys.length;
44653          nextBlockOrder = new Array(collectionLength);
44654
44655          // locate existing items
44656          for (index = 0; index < collectionLength; index++) {
44657            key = (collection === collectionKeys) ? index : collectionKeys[index];
44658            value = collection[key];
44659            trackById = trackByIdFn(key, value, index);
44660            if (lastBlockMap[trackById]) {
44661              // found previously seen block
44662              block = lastBlockMap[trackById];
44663              delete lastBlockMap[trackById];
44664              nextBlockMap[trackById] = block;
44665              nextBlockOrder[index] = block;
44666            } else if (nextBlockMap[trackById]) {
44667              // if collision detected. restore lastBlockMap and throw an error
44668              forEach(nextBlockOrder, function(block) {
44669                if (block && block.scope) lastBlockMap[block.id] = block;
44670              });
44671              throw ngRepeatMinErr('dupes',
44672                  "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}",
44673                  expression, trackById, value);
44674            } else {
44675              // new never before seen block
44676              nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined};
44677              nextBlockMap[trackById] = true;
44678            }
44679          }
44680
44681          // remove leftover items
44682          for (var blockKey in lastBlockMap) {
44683            block = lastBlockMap[blockKey];
44684            elementsToRemove = getBlockNodes(block.clone);
44685            $animate.leave(elementsToRemove);
44686            if (elementsToRemove[0].parentNode) {
44687              // if the element was not removed yet because of pending animation, mark it as deleted
44688              // so that we can ignore it later
44689              for (index = 0, length = elementsToRemove.length; index < length; index++) {
44690                elementsToRemove[index][NG_REMOVED] = true;
44691              }
44692            }
44693            block.scope.$destroy();
44694          }
44695
vendor: 6,929 bytes, lines 44696-44848
44696          // we are not using forEach for perf reasons (trying to avoid #call)
44697          for (index = 0; index < collectionLength; index++) {
44698            key = (collection === collectionKeys) ? index : collectionKeys[index];
44699            value = collection[key];
44700            block = nextBlockOrder[index];
44701
44702            if (block.scope) {
44703              // if we have already seen this object, then we need to reuse the
44704              // associated scope/element
44705
44706              nextNode = previousNode;
44707
44708              // skip nodes that are already pending removal via leave animation
44709              do {
44710                nextNode = nextNode.nextSibling;
44711              } while (nextNode && nextNode[NG_REMOVED]);
44712
44713              if (getBlockStart(block) != nextNode) {
44714                // existing item which got moved
44715                $animate.move(getBlockNodes(block.clone), null, jqLite(previousNode));
44716              }
44717              previousNode = getBlockEnd(block);
44718              updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
44719            } else {
44720              // new item which we don't know about
44721              $transclude(function ngRepeatTransclude(clone, scope) {
44722                block.scope = scope;
44723                // http://jsperf.com/clone-vs-createcomment
44724                var endNode = ngRepeatEndComment.cloneNode(false);
44725                clone[clone.length++] = endNode;
44726
44727                // TODO(perf): support naked previousNode in `enter` to avoid creation of jqLite wrapper?
44728                $animate.enter(clone, null, jqLite(previousNode));
44729                previousNode = endNode;
44730                // Note: We only need the first/last node of the cloned nodes.
44731                // However, we need to keep the reference to the jqlite wrapper as it might be changed later
44732                // by a directive with templateUrl when its template arrives.
44733                block.clone = clone;
44734                nextBlockMap[block.id] = block;
44735                updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
44736              });
44737            }
44738          }
44739          lastBlockMap = nextBlockMap;
44740        });
44741      };
44742    }
44743  };
44744}];
44745
44746var NG_HIDE_CLASS = 'ng-hide';
44747var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
44748/**
44749 * @ngdoc directive
44750 * @name ngShow
44751 * @multiElement
44752 *
44753 * @description
44754 * The `ngShow` directive shows or hides the given HTML element based on the expression
44755 * provided to the `ngShow` attribute. The element is shown or hidden by removing or adding
44756 * the `.ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
44757 * in AngularJS and sets the display style to none (using an !important flag).
44758 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
44759 *
44760 * ```html
44761 * <!-- when $scope.myValue is truthy (element is visible) -->
44762 * <div ng-show="myValue"></div>
44763 *
44764 * <!-- when $scope.myValue is falsy (element is hidden) -->
44765 * <div ng-show="myValue" class="ng-hide"></div>
44766 * ```
44767 *
44768 * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added to the class
44769 * attribute on the element causing it to become hidden. When truthy, the `.ng-hide` CSS class is removed
44770 * from the element causing the element not to appear hidden.
44771 *
44772 * ## Why is !important used?
44773 *
44774 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
44775 * can be easily overridden by heavier selectors. For example, something as simple
44776 * as changing the display style on a HTML list item would make hidden elements appear visible.
44777 * This also becomes a bigger issue when dealing with CSS frameworks.
44778 *
44779 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
44780 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
44781 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
44782 *
44783 * ### Overriding `.ng-hide`
44784 *
44785 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
44786 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
44787 * class CSS. Note that the selector that needs to be used is actually `.ng-hide:not(.ng-hide-animate)` to cope
44788 * with extra animation classes that can be added.
44789 *
44790 * ```css
44791 * .ng-hide:not(.ng-hide-animate) {
44792 *   /&#42; this is just another form of hiding an element &#42;/
44793 *   display: block!important;
44794 *   position: absolute;
44795 *   top: -9999px;
44796 *   left: -9999px;
44797 * }
44798 * ```
44799 *
44800 * By default you don't need to override in CSS anything and the animations will work around the display style.
44801 *
44802 * ## A note about animations with `ngShow`
44803 *
44804 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
44805 * is true and false. This system works like the animation system present with ngClass except that
44806 * you must also include the !important flag to override the display property
44807 * so that you can perform an animation when the element is hidden during the time of the animation.
44808 *
44809 * ```css
44810 * //
44811 * //a working example can be found at the bottom of this page
44812 * //
44813 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
44814 *   /&#42; this is required as of 1.3x to properly
44815 *      apply all styling in a show/hide animation &#42;/
44816 *   transition: 0s linear all;
44817 * }
44818 *
44819 * .my-element.ng-hide-add-active,
44820 * .my-element.ng-hide-remove-active {
44821 *   /&#42; the transition is defined in the active class &#42;/
44822 *   transition: 1s linear all;
44823 * }
44824 *
44825 * .my-element.ng-hide-add { ... }
44826 * .my-element.ng-hide-add.ng-hide-add-active { ... }
44827 * .my-element.ng-hide-remove { ... }
44828 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
44829 * ```
44830 *
44831 * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
44832 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
44833 *
44834 * @animations
44835 * addClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a truthy value and the just before contents are set to visible
44836 * removeClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden
44837 *
44838 * @element ANY
44839 * @param {expression} ngShow If the {@link guide/expression expression} is truthy
44840 *     then the element is shown or hidden respectively.
44841 *
44842 * @example
44843  <example module="ngAnimate" deps="angular-animate.js" animations="true">
44844    <file name="index.html">
44845      Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br/>
44846      <div>
44847        Show:
44848        <div class="check-element animate-show" ng-show="checked">
44849          <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
44850        </div>
44851      </div>
44852      <div>
44853        Hide:
44854        <div class="check-element animate-show" ng-hide="checked">
44855          <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
44856        </div>
44857      </div>
44858    </file>
44859    <file name="glyphicons.css">
44860      @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
44861    </file>
44862    <file name="animations.css">
44863      .animate-show {
44864        line-height: 20px;
44865        opacity: 1;
44866        padding: 10px;
44867        border: 1px solid black;
44868        background: white;
44869      }
44870
44871      .animate-show.ng-hide-add, .animate-show.ng-hide-remove {
44872        transition: all linear 0.5s;
44873      }
44874
44875      .animate-show.ng-hide {
44876        line-height: 0;
44877        opacity: 0;
44878        padding: 0 10px;
44879      }
44880
44881      .check-element {
44882        padding: 10px;
44883        border: 1px solid black;
44884        background: white;
44885      }
44886    </file>
44887    <file name="protractor.js" type="protractor">
44888      var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
44889      var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
44890
44891      it('should check ng-show / ng-hide', function() {
44892        expect(thumbsUp.isDisplayed()).toBeFalsy();
44893        expect(thumbsDown.isDisplayed()).toBeTruthy();
44894
44895        element(by.model('checked')).click();
44896
44897        expect(thumbsUp.isDisplayed()).toBeTruthy();
44898        expect(thumbsDown.isDisplayed()).toBeFalsy();
44899      });
44900    </file>
44901  </example>
44902 */
44903var ngShowDirective = ['$animate', function($animate) {
44904  return {
44905    restrict: 'A',
44906    multiElement: true,
44907    link: function(scope, element, attr) {
44908      scope.$watch(attr.ngShow, function ngShowWatchAction(value) {
44909        // we're adding a temporary, animation-specific class for ng-hide since this way
44910        // we can control when the element is actually displayed on screen without having
44911        // to have a global/greedy CSS selector that breaks when other animations are run.
44912        // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845
44913        $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, {
44914          tempClasses: NG_HIDE_IN_PROGRESS_CLASS
44915        });
44916      });
44917    }
44918  };
44919}];
44920
44921
44922/**
44923 * @ngdoc directive
44924 * @name ngHide
44925 * @multiElement
44926 *
44927 * @description
44928 * The `ngHide` directive shows or hides the given HTML element based on the expression
44929 * provided to the `ngHide` attribute. The element is shown or hidden by removing or adding
44930 * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
44931 * in AngularJS and sets the display style to none (using an !important flag).
44932 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
44933 *
44934 * ```html
44935 * <!-- when $scope.myValue is truthy (element is hidden) -->
44936 * <div ng-hide="myValue" class="ng-hide"></div>
44937 *
44938 * <!-- when $scope.myValue is falsy (element is visible) -->
44939 * <div ng-hide="myValue"></div>
44940 * ```
44941 *
44942 * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added to the class
44943 * attribute on the element causing it to become hidden. When falsy, the `.ng-hide` CSS class is removed
44944 * from the element causing the element not to appear hidden.
44945 *
44946 * ## Why is !important used?
44947 *
44948 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
44949 * can be easily overridden by heavier selectors. For example, something as simple
44950 * as changing the display style on a HTML list item would make hidden elements appear visible.
44951 * This also becomes a bigger issue when dealing with CSS frameworks.
44952 *
44953 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
44954 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
44955 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
44956 *
44957 * ### Overriding `.ng-hide`
44958 *
44959 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
44960 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
44961 * class in CSS:
44962 *
44963 * ```css
44964 * .ng-hide {
44965 *   /&#42; this is just another form of hiding an element &#42;/
44966 *   display: block!important;
44967 *   position: absolute;
44968 *   top: -9999px;
44969 *   left: -9999px;
44970 * }
44971 * ```
44972 *
44973 * By default you don't need to override in CSS anything and the animations will work around the display style.
44974 *
44975 * ## A note about animations with `ngHide`
44976 *
44977 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
44978 * is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide`
44979 * CSS class is added and removed for you instead of your own CSS class.
44980 *
44981 * ```css
44982 * //
44983 * //a working example can be found at the bottom of this page
44984 * //
44985 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
44986 *   transition: 0.5s linear all;
44987 * }
44988 *
44989 * .my-element.ng-hide-add { ... }
44990 * .my-element.ng-hide-add.ng-hide-add-active { ... }
44991 * .my-element.ng-hide-remove { ... }
44992 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
44993 * ```
44994 *
44995 * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
44996 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
44997 *
44998 * @animations
44999 * removeClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden
45000 * addClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a non truthy value and just before the contents are set to visible
45001 *
45002 * @element ANY
45003 * @param {expression} ngHide If the {@link guide/expression expression} is truthy then
45004 *     the element is shown or hidden respectively.
45005 *
45006 * @example
45007  <example module="ngAnimate" deps="angular-animate.js" animations="true">
45008    <file name="index.html">
45009      Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br/>
45010      <div>
45011        Show:
45012        <div class="check-element animate-hide" ng-show="checked">
45013          <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
45014        </div>
45015      </div>
45016      <div>
45017        Hide:
45018        <div class="check-element animate-hide" ng-hide="checked">
45019          <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
45020        </div>
45021      </div>
45022    </file>
45023    <file name="glyphicons.css">
45024      @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
45025    </file>
45026    <file name="animations.css">
45027      .animate-hide {
45028        transition: all linear 0.5s;
45029        line-height: 20px;
45030        opacity: 1;
45031        padding: 10px;
45032        border: 1px solid black;
45033        background: white;
45034      }
45035
45036      .animate-hide.ng-hide {
45037        line-height: 0;
45038        opacity: 0;
45039        padding: 0 10px;
45040      }
45041
45042      .check-element {
45043        padding: 10px;
45044        border: 1px solid black;
45045        background: white;
45046      }
45047    </file>
45048    <file name="protractor.js" type="protractor">
45049      var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
45050      var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
45051
45052      it('should check ng-show / ng-hide', function() {
45053        expect(thumbsUp.isDisplayed()).toBeFalsy();
45054        expect(thumbsDown.isDisplayed()).toBeTruthy();
45055
45056        element(by.model('checked')).click();
45057
45058        expect(thumbsUp.isDisplayed()).toBeTruthy();
45059        expect(thumbsDown.isDisplayed()).toBeFalsy();
45060      });
45061    </file>
45062  </example>
45063 */
45064var ngHideDirective = ['$animate', function($animate) {
45065  return {
45066    restrict: 'A',
45067    multiElement: true,
45068    link: function(scope, element, attr) {
45069      scope.$watch(attr.ngHide, function ngHideWatchAction(value) {
45070        // The comment inside of the ngShowDirective explains why we add and
45071        // remove a temporary class for the show/hide animation
45072        $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, {
45073          tempClasses: NG_HIDE_IN_PROGRESS_CLASS
45074        });
45075      });
45076    }
45077  };
45078}];
45079
45080/**
45081 * @ngdoc directive
45082 * @name ngStyle
45083 * @restrict AC
45084 *
45085 * @description
45086 * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
45087 *
45088 * @element ANY
45089 * @param {expression} ngStyle
45090 *
45091 * {@link guide/expression Expression} which evals to an
45092 * object whose keys are CSS style names and values are corresponding values for those CSS
45093 * keys.
45094 *
45095 * Since some CSS style names are not valid keys for an object, they must be quoted.
45096 * See the 'background-color' style in the example below.
45097 *
45098 * @example
45099   <example>
45100     <file name="index.html">
45101        <input type="button" value="set color" ng-click="myStyle={color:'red'}">
45102        <input type="button" value="set background" ng-click="myStyle={'background-color':'blue'}">
45103        <input type="button" value="clear" ng-click="myStyle={}">
45104        <br/>
45105        <span ng-style="myStyle">Sample Text</span>
45106        <pre>myStyle={{myStyle}}</pre>
45107     </file>
45108     <file name="style.css">
45109       span {
45110         color: black;
45111       }
45112     </file>
45113     <file name="protractor.js" type="protractor">
45114       var colorSpan = element(by.css('span'));
45115
45116       it('should check ng-style', function() {
45117         expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
45118         element(by.css('input[value=\'set color\']')).click();
45119         expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');
45120         element(by.css('input[value=clear]')).click();
45121         expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
45122       });
45123     </file>
45124   </example>
45125 */
45126var ngStyleDirective = ngDirective(function(scope, element, attr) {
45127  scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
45128    if (oldStyles && (newStyles !== oldStyles)) {
45129      forEach(oldStyles, function(val, style) { element.css(style, '');});
45130    }
45131    if (newStyles) element.css(newStyles);
45132  }, true);
45133});
45134
45135/**
45136 * @ngdoc directive
45137 * @name ngSwitch
45138 * @restrict EA
45139 *
45140 * @description
45141 * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression.
45142 * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location
45143 * as specified in the template.
45144 *
45145 * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
45146 * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element
45147 * matches the value obtained from the evaluated expression. In other words, you define a container element
45148 * (where you place the directive), place an expression on the **`on="..."` attribute**
45149 * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
45150 * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
45151 * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
45152 * attribute is displayed.
45153 *
45154 * <div class="alert alert-info">
45155 * Be aware that the attribute values to match against cannot be expressions. They are interpreted
45156 * as literal string values to match against.
45157 * For example, **`ng-switch-when="someVal"`** will match against the string `"s
45157omeVal"` not against the
45158 * value of the expression `$scope.someVal`.
45159 * </div>
45160
45161 * @animations
45162 * enter - happens after the ngSwitch contents change and the matched child element is placed inside the container
45163 * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM
45164 *
45165 * @usage
45166 *
45167 * ```
45168 * <ANY ng-switch="expression">
45169 *   <ANY ng-switch-when="matchValue1">...</ANY>
45170 *   <ANY ng-switch-when="matchValue2">...</ANY>
45171 *   <ANY ng-switch-default>...</ANY>
45172 * </ANY>
45173 * ```
45174 *
45175 *
45176 * @scope
45177 * @priority 1200
45178 * @param {*} ngSwitch|on expression to match against <code>ng-switch-when</code>.
45179 * On child elements add:
45180 *
45181 * * `ngSwitchWhen`: the case statement to match against. If match then this
45182 *   case will be displayed. If the same match appears multiple times, all the
45183 *   elements will be displayed.
45184 * * `ngSwitchDefault`: the default case when no other case match. If there
45185 *   are multiple default cases, all of them will be displayed when no other
45186 *   case match.
45187 *
45188 *
45189 * @example
45190  <example module="switchExample" deps="angular-animate.js" animations="true">
45191    <file name="index.html">
45192      <div ng-controller="ExampleController">
45193        <select ng-model="selection" ng-options="item for item in items">
45194        </select>
45195        <code>selection={{selection}}</code>
45196        <hr/>
45197        <div class="animate-switch-container"
45198          ng-switch on="selection">
45199            <div class="animate-switch" ng-switch-when="settings">Settings Div</div>
45200            <div class="animate-switch" ng-switch-when="home">Home Span</div>
45201            <div class="animate-switch" ng-switch-default>default</div>
45202        </div>
45203      </div>
45204    </file>
45205    <file name="script.js">
45206      angular.module('switchExample', ['ngAnimate'])
45207        .controller('ExampleController', ['$scope', function($scope) {
45208          $scope.items = ['settings', 'home', 'other'];
45209          $scope.selection = $scope.items[0];
45210        }]);
45211    </file>
45212    <file name="animations.css">
45213      .animate-switch-container {
45214        position:relative;
45215        background:white;
45216        border:1px solid black;
45217        height:40px;
45218        overflow:hidden;
45219      }
45220
45221      .animate-switch {
45222        padding:10px;
45223      }
45224
45225      .animate-switch.ng-animate {
45226        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
45227
45228        position:absolute;
45229        top:0;
45230        left:0;
45231        right:0;
45232        bottom:0;
45233      }
45234
45235      .animate-switch.ng-leave.ng-leave-active,
45236      .animate-switch.ng-enter {
45237        top:-50px;
45238      }
45239      .animate-switch.ng-leave,
45240      .animate-switch.ng-enter.ng-enter-active {
45241        top:0;
45242      }
45243    </file>
45244    <file name="protractor.js" type="protractor">
45245      var switchElem = element(by.css('[ng-switch]'));
45246      var select = element(by.model('selection'));
45247
45248      it('should start in settings', function() {
45249        expect(switchElem.getText()).toMatch(/Settings Div/);
45250      });
45251      it('should change to home', function() {
45252        select.all(by.css('option')).get(1).click();
45253        expect(switchElem.getText()).toMatch(/Home Span/);
45254      });
45255      it('should select default', function() {
45256        select.all(by.css('option')).get(2).click();
45257        expect(switchElem.getText()).toMatch(/default/);
45258      });
45259    </file>
45260  </example>
45261 */
45262var ngSwitchDirective = ['$animate', function($animate) {
45263  return {
45264    require: 'ngSwitch',
45265
45266    // asks for $scope to fool the BC controller module
45267    controller: ['$scope', function ngSwitchController() {
45268     this.cases = {};
45269    }],
45270    link: function(scope, element, attr, ngSwitchController) {
45271      var watchExpr = attr.ngSwitch || attr.on,
45272          selectedTranscludes = [],
45273          selectedElements = [],
45274          previousLeaveAnimations = [],
45275          selectedScopes = [];
45276
45277      var spliceFactory = function(array, index) {
45278          return function() { array.splice(index, 1); };
45279      };
45280
45281      scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
45282        var i, ii;
45283        for (i = 0, ii = previousLeaveAnimations.length; i < ii; ++i) {
45284          $animate.cancel(previousLeaveAnimations[i]);
45285        }
45286        previousLeaveAnimations.length = 0;
45287
45288        for (i = 0, ii = selectedScopes.length; i < ii; ++i) {
45289          var selected = getBlockNodes(selectedElements[i].clone);
45290          selectedScopes[i].$destroy();
45291          var promise = previousLeaveAnimations[i] = $animate.leave(selected);
45292          promise.then(spliceFactory(previousLeaveAnimations, i));
45293        }
45294
45295        selectedElements.length = 0;
45296        selectedScopes.length = 0;
45297
45298        if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {
45299          forEach(selectedTranscludes, function(selectedTransclude) {
45300            selectedTransclude.transclude(function(caseElement, selectedScope) {
45301              selectedScopes.push(selectedScope);
45302              var anchor = selectedTransclude.element;
45303              caseElement[caseElement.length++] = document.createComment(' end ngSwitchWhen: ');
45304              var block = { clone: caseElement };
45305
45306              selectedElements.push(block);
45307              $animate.enter(caseElement, anchor.parent(), anchor);
45308            });
45309          });
45310        }
45311      });
45312    }
45313  };
45314}];
45315
45316var ngSwitchWhenDirective = ngDirective({
45317  transclude: 'element',
45318  priority: 1200,
45319  require: '^ngSwitch',
45320  multiElement: true,
45321  link: function(scope, element, attrs, ctrl, $transclude) {
45322    ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
45323    ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
45324  }
45325});
45326
45327var ngSwitchDefaultDirective = ngDirective({
45328  transclude: 'element',
45329  priority: 1200,
45330  require: '^ngSwitch',
45331  multiElement: true,
45332  link: function(scope, element, attr, ctrl, $transclude) {
45333    ctrl.cases['?'] = (ctrl.cases['?'] || []);
45334    ctrl.cases['?'].push({ transclude: $transclude, element: element });
45335   }
45336});
45337
45338/**
45339 * @ngdoc directive
45340 * @name ngTransclude
45341 * @restrict EAC
45342 *
45343 * @description
45344 * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
45345 *
45346 * Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted.
45347 *
45348 * @element ANY
45349 *
45350 * @example
45351   <example module="transcludeExample">
45352     <file name="index.html">
45353       <script>
45354         angular.module('transcludeExample', [])
45355          .directive('pane', function(){
45356             return {
45357               restrict: 'E',
45358               transclude: true,
45359               scope: { title:'@' },
45360               template: '<div style="border: 1px solid black;">
45360' +
45361                           '<div style="background-color: gray">{{title}}</div>' +
45362                           '<ng-transclude></ng-transclude>' +
45363                         '</div>'
45364             };
45365         })
45366         .controller('ExampleController', ['$scope', function($scope) {
45367           $scope.title = 'Lorem Ipsum';
45368           $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
45369         }]);
45370       </script>
45371       <div ng-controller="ExampleController">
45372         <input ng-model="title" aria-label="title"> <br/>
45373         <textarea ng-model="text" aria-label="text"></textarea> <br/>
45374         <pane title="{{title}}">{{text}}</pane>
45375       </div>
45376     </file>
45377     <file name="protractor.js" type="protractor">
45378        it('should have transcluded', function() {
45379          var titleElement = element(by.model('title'));
45380          titleElement.clear();
45381          titleElement.sendKeys('TITLE');
45382          var textElement = element(by.model('text'));
45383          textElement.clear();
45384          textElement.sendKeys('TEXT');
45385          expect(element(by.binding('title')).getText()).toEqual('TITLE');
45386          expect(element(by.binding('text')).getText()).toEqual('TEXT');
45387        });
45388     </file>
45389   </example>
45390 *
45391 */
45392var ngTranscludeDirective = ngDirective({
45393  restrict: 'EAC',
45394  link: function($scope, $element, $attrs, controller, $transclude) {
45395    if (!$transclude) {
45396      throw minErr('ngTransclude')('orphan',
45397       'Illegal use of ngTransclude directive in the template! ' +
45398       'No parent directive that requires a transclusion found. ' +
45399       'Element: {0}',
45400       startingTag($element));
45401    }
45402
45403    $transclude(function(clone) {
45404      $element.empty();
45405      $element.append(clone);
45406    });
45407  }
45408});
45409
45410/**
45411 * @ngdoc directive
45412 * @name script
45413 * @restrict E
45414 *
45415 * @description
45416 * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the
45417 * template can be used by {@link ng.directive:ngInclude `ngInclude`},
45418 * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the
45419 * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be
45420 * assigned through the element's `id`, which can then be used as a directive's `templateUrl`.
45421 *
45422 * @param {string} type Must be set to `'text/ng-template'`.
45423 * @param {string} id Cache name of the template.
45424 *
45425 * @example
45426  <example>
45427    <file name="index.html">
45428      <script type="text/ng-template" id="/tpl.html">
45429        Content of the template.
45430      </script>
45431
45432      <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
45433      <div id="tpl-content" ng-include src="currentTpl"></div>
45434    </file>
45435    <file name="protractor.js" type="protractor">
45436      it('should load template defined inside script tag', function() {
45437        element(by.css('#tpl-link')).click();
45438        expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/);
45439      });
45440    </file>
45441  </example>
45442 */
45443var scriptDirective = ['$templateCache', function($templateCache) {
45444  return {
45445    restrict: 'E',
45446    terminal: true,
45447    compile: function(element, attr) {
45448      if (attr.type == 'text/ng-template') {
45449        var templateUrl = attr.id,
45450            text = element[0].text;
45451
45452        $templateCache.put(templateUrl, text);
45453      }
45454    }
45455  };
45456}];
45457
45458var noopNgModelController = { $setViewValue: noop, $render: noop };
45459
45460/**
45461 * @ngdoc type
45462 * @name  select.SelectController
45463 * @description
45464 * The controller for the `<select>` directive. This provides support for reading
45465 * and writing the selected value(s) of the control and also coordinates dynamically
45466 * added `<option>` elements, perhaps by an `ngRepeat` directive.
45467 */
45468var SelectController =
45469        ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
45470
45471  var self = this,
45472      optionsMap = new HashMap();
45473
45474  // If the ngModel doesn't get provided then provide a dummy noop version to prevent errors
45475  self.ngModelCtrl = noopNgModelController;
45476
45477  // The "unknown" option is one that is prepended to the list if the viewValue
45478  // does not match any of the options. When it is rendered the value of the unknown
45479  // option is '? XXX ?' where XXX is the hashKey of the value that is not known.
45480  //
45481  // We can't just jqLite('<option>') since jqLite is not smart enough
45482  // to create it in <select> and IE barfs otherwise.
45483  self.unknownOption = jqLite(document.createElement('option'));
45484  self.renderUnknownOption = function(val) {
45485    var unknownVal = '? ' + hashKey(val) + ' ?';
45486    self.unknownOption.val(unknownVal);
45487    $element.prepend(self.unknownOption);
45488    $element.val(unknownVal);
45489  };
45490
45491  $scope.$on('$destroy', function() {
45492    // disable unknown option so that we don't do work when the whole select is being destroyed
45493    self.renderUnknownOption = noop;
45494  });
45495
45496  self.removeUnknownOption = function() {
45497    if (self.unknownOption.parent()) self.unknownOption.remove();
45498  };
45499
45500
45501  // Read the value of the select control, the implementation of this changes depending
45502  // upon whether the select can have multiple values and whether ngOptions is at work.
45503  self.readValue = function readSingleValue() {
45504    self.removeUnknownOption();
45505    return $element.val();
45506  };
45507
45508
45509  // Write the value to the select control, the implementation of this changes depending
45510  // upon whether the select can have multiple values and whether ngOptions is at work.
45511  self.writeValue = function writeSingleValue(value) {
45512    if (self.hasOption(value)) {
45513      self.removeUnknownOption();
45514      $element.val(value);
45515      if (value === '') self.emptyOption.prop('selected', true); // to make IE9 happy
45516    } else {
45517      if (value == null && self.emptyOption) {
45518        self.removeUnknownOption();
45519        $element.val('');
45520      } else {
45521        self.renderUnknownOption(value);
45522      }
45523    }
45524  };
45525
45526
45527  // Tell the select control that an option, with the given value, has been added
45528  self.addOption = function(value, element) {
45529    assertNotHasOwnProperty(value, '"option value"');
45530    if (value === '') {
45531      self.emptyOption = element;
45532    }
45533    var count = optionsMap.get(value) || 0;
45534    optionsMap.put(value, count + 1);
45535  };
45536
45537  // Tell the select control that an option, with the given value, has been removed
45538  self.removeOption = function(value) {
45539    var count = optionsMap.get(value);
45540    if (count) {
45541      if (count === 1) {
45542        optionsMap.remove(value);
45543        if (value === '') {
45544          self.emptyOption = undefined;
45545        }
45546      } else {
45547        optionsMap.put(value, count - 1);
45548      }
45549    }
45550  };
45551
45552  // Check whether the select control has an option matching the given value
45553  self.hasOption = function(value) {
45554    return !!optionsMap.get(value);
45555  };
45556}];
45557
45558/**
45559 * @ngdoc directive
45560 * @name select
45561 * @restrict E
45562 *
45563 * @description
45564 * HTML `SELECT` element with angular data-binding.
45565 *
45566 * The `select` directive is used together with {@link ngModel `ngModel`} to provide data-binding
45567 * between the scope and the `<select>` control (including setting default values).
45568 * Ìt also handles dynamic `<option>` elements, which can be added using the {@link ngRepeat `ngRepeat}` or
45569 * {@link ngOptions `ngOptions`} directives.
45570 *
45571 * When an item in the `<select>` menu is selected, the value of the selected option will be bound
45572 * to the model identified by the `ngModel` directive. With static or repeated options, this is
45573 * the content of the `value` attribute or the textContent of the `<option>`, if the value attribute is missing.
45574 * If you want dynamic value attributes, you can use interpolation inside the value attribute.
45575 *
45576 * <div class="alert alert-warning">
45577 * Note that the value of a `select` directive used without `ngOptions` is always a string.
45578 * When the model needs to be bound to a non-string value, you must either explictly convert it
45579 * using a directive (see example below) or use `ngOptions` to specify the set of options.
45580 * This is because an option element can only be bound to string values at present.
45581 * </div>
45582 *
45583 * If the viewValue of `ngModel` does not match any of the options, then the control
45584 * will automatically add an "unknown" option, which it then removes when the mismatch is resolved.
45585 *
45586 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
45587 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
45588 * option. See example below for demonstration.
45589 *
45590 * <div class="alert alert-info">
45591 * In many cases, `ngRepeat` can be used on `<option>` elements instead of {@link ng.directive:ngOptions
45592 * ngOptions} to achieve a similar result. However, `ngOptions` provides some benef
45592its, such as
45593 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
45594 * comprehension expression, and additionally in reducing memory and increasing speed by not creating
45595 * a new scope for each repeated instance.
45596 * </div>
45597 *
45598 *
45599 * @param {string} ngModel Assignable angular expression to data-bind to.
45600 * @param {string=} name Property name of the form under which the control is published.
45601 * @param {string=} required Sets `required` validation error key if the value is not entered.
45602 * @param {string=} ngRequired Adds required attribute and required validation constraint to
45603 * the element when the ngRequired expression evaluates to true. Use ngRequired instead of required
45604 * when you want to data-bind to the required attribute.
45605 * @param {string=} ngChange Angular expression to be executed when selected option(s) changes due to user
45606 *    interaction with the select element.
45607 * @param {string=} ngOptions sets the options that the select is populated with and defines what is
45608 * set on the model on selection. See {@link ngOptions `ngOptions`}.
45609 *
45610 * @example
45611 * ### Simple `select` elements with static options
45612 *
45613 * <example name="static-select" module="staticSelect">
45614 * <file name="index.html">
45615 * <div ng-controller="ExampleController">
45616 *   <form name="myForm">
45617 *     <label for="singleSelect"> Single select: </label><br>
45618 *     <select name="singleSelect" ng-model="data.singleSelect">
45619 *       <option value="option-1">Option 1</option>
45620 *       <option value="option-2">Option 2</option>
45621 *     </select><br>
45622 *
45623 *     <label for="singleSelect"> Single select with "not selected" option and dynamic option values: </label><br>
45624 *     <select name="singleSelect" id="singleSelect" ng-model="data.singleSelect">
45625 *       <option value="">---Please select---</option> <!-- not selected / blank option -->
45626 *       <option value="{{data.option1}}">Option 1</option> <!-- interpolation -->
45627 *       <option value="option-2">Option 2</option>
45628 *     </select><br>
45629 *     <button ng-click="forceUnknownOption()">Force unknown option</button><br>
45630 *     <tt>singleSelect = {{data.singleSelect}}</tt>
45631 *
45632 *     <hr>
45633 *     <label for="multipleSelect"> Multiple select: </label><br>
45634 *     <select name="multipleSelect" id="multipleSelect" ng-model="data.multipleSelect" multiple>
45635 *       <option value="option-1">Option 1</option>
45636 *       <option value="option-2">Option 2</option>
45637 *       <option value="option-3">Option 3</option>
45638 *     </select><br>
45639 *     <tt>multipleSelect = {{data.multipleSelect}}</tt><br/>
45640 *   </form>
45641 * </div>
45642 * </file>
45643 * <file name="app.js">
45644 *  angular.module('staticSelect', [])
45645 *    .controller('ExampleController', ['$scope', function($scope) {
45646 *      $scope.data = {
45647 *       singleSelect: null,
45648 *       multipleSelect: [],
45649 *       option1: 'option-1',
45650 *      };
45651 *
45652 *      $scope.forceUnknownOption = function() {
45653 *        $scope.data.singleSelect = 'nonsense';
45654 *      };
45655 *   }]);
45656 * </file>
45657 *</example>
45658 *
45659 * ### Using `ngRepeat` to generate `select` options
45660 * <example name="ngrepeat-select" module="ngrepeatSelect">
45661 * <file name="index.html">
45662 * <div ng-controller="ExampleController">
45663 *   <form name="myForm">
45664 *     <label for="repeatSelect"> Repeat select: </label>
45665 *     <select name="repeatSelect" id="repeatSelect" ng-model="data.repeatSelect">
45666 *       <option ng-repeat="option in data.availableOptions" value="{{option.id}}">{{option.name}}</option>
45667 *     </select>
45668 *   </form>
45669 *   <hr>
45670 *   <tt>repeatSelect = {{data.repeatSelect}}</tt><br/>
45671 * </div>
45672 * </file>
45673 * <file name="app.js">
45674 *  angular.module('ngrepeatSelect', [])
45675 *    .controller('ExampleController', ['$scope', function($scope) {
45676 *      $scope.data = {
45677 *       repeatSelect: null,
45678 *       availableOptions: [
45679 *         {id: '1', name: 'Option A'},
45680 *         {id: '2', name: 'Option B'},
45681 *         {id: '3', name: 'Option C'}
45682 *       ],
45683 *      };
45684 *   }]);
45685 * </file>
45686 *</example>
45687 *
45688 *
45689 * ### Using `select` with `ngOptions` and setting a default value
45690 * See the {@link ngOptions ngOptions documentation} for more `ngOptions` usage examples.
45691 *
45692 * <example name="select-with-default-values" module="defaultValueSelect">
45693 * <file name="index.html">
45694 * <div ng-controller="ExampleController">
45695 *   <form name="myForm">
45696 *     <label for="mySelect">Make a choice:</label>
45697 *     <select name="mySelect" id="mySelect"
45698 *       ng-options="option.name for option in data.availableOptions track by option.id"
45699 *       ng-model="data.selectedOption"></select>
45700 *   </form>
45701 *   <hr>
45702 *   <tt>option = {{data.selectedOption}}</tt><br/>
45703 * </div>
45704 * </file>
45705 * <file name="app.js">
45706 *  angular.module('defaultValueSelect', [])
45707 *    .controller('ExampleController', ['$scope', function($scope) {
45708 *      $scope.data = {
45709 *       availableOptions: [
45710 *         {id: '1', name: 'Option A'},
45711 *         {id: '2', name: 'Option B'},
45712 *         {id: '3', name: 'Option C'}
45713 *       ],
45714 *       selectedOption: {id: '3', name: 'Option C'} //This sets the default value of the select in the ui
45715 *       };
45716 *   }]);
45717 * </file>
45718 *</example>
45719 *
45720 *
45721 * ### Binding `select` to a non-string value via `ngModel` parsing / formatting
45722 *
45723 * <example name="select-with-non-string-options" module="nonStringSelect">
45724 *   <file name="index.html">
45725 *     <select ng-model="model.id" convert-to-number>
45726 *       <option value="0">Zero</option>
45727 *       <option value="1">One</option>
45728 *       <option value="2">Two</option>
45729 *     </select>
45730 *     {{ model }}
45731 *   </file>
45732 *   <file name="app.js">
45733 *     angular.module('nonStringSelect', [])
45734 *       .run(function($rootScope) {
45735 *         $rootScope.model = { id: 2 };
45736 *       })
45737 *       .directive('convertToNumber', function() {
45738 *         return {
45739 *           require: 'ngModel',
45740 *           link: function(scope, element, attrs, ngModel) {
45741 *             ngModel.$parsers.push(function(val) {
45742 *               return parseInt(val, 10);
45743 *             });
45744 *             ngModel.$formatters.push(function(val) {
45745 *               return '' + val;
45746 *             });
45747 *           }
45748 *         };
45749 *       });
45750 *   </file>
45751 *   <file name="protractor.js" type="protractor">
45752 *     it('should initialize to model', function() {
45753 *       var select = element(by.css('select'));
45754 *       expect(element(by.model('model.id')).$('option:checked').getText()).toEqual('Two');
45755 *     });
45756 *   </file>
45757 * </example>
45758 *
45759 */
45760var selectDirective = function() {
45761
45762  return {
45763    restrict: 'E',
45764    require: ['select', '?ngModel'],
45765    controller: SelectController,
45766    link: function(scope, element, attr, ctrls) {
45767
45768      // if ngModel is not defined, we don't need to do anything
45769      var ngModelCtrl = ctrls[1];
45770      if (!ngModelCtrl) return;
45771
45772      var selectCtrl = ctrls[0];
45773
45774      selectCtrl.ngModelCtrl = ngModelCtrl;
45775
45776      // We delegate rendering to the `writeValue` method, which can be changed
45777      // if the select can have multiple selected values or if the options are being
45778      // generated by `ngOptions`
45779      ngModelCtrl.$render = function() {
45780        selectCtrl.writeValue(ngModelCtrl.$viewValue);
45781      };
45782
45783      // When the selected item(s) changes we delegate getting the value of the select control
45784      // to the `readValue` method, which can be changed if the select can have multiple
45785      // selected values or if the options are being generated by `ngOptions`
45786      element.on('change', function() {
45787        scope.$apply(function() {
45788          ngModelCtrl.$setViewValue(selectCtrl.readValue());
45789        });
45790      });
45791
45792      // If the select allows multiple values then we need to modify how we read and write
45793      // values from and to the control; also what it means for the value to be empty and
45794      // we have to add an extra watch since ngModel doesn't work well with arrays - it
45795      // doesn't trigger rendering if only an item in the array changes.
45796      if (attr.multiple) {
45797
45798        // Read value now needs to check each option to see if it is selected
45799        selectCtrl.readValue = function readMultipleValue() {
45800          var array = [];
45801          forEach(element.find('option'), function(option) {
45802            if (option.selected) {
45803              array.push(option.value);
45804            }
45805          });
45806          return array;
45807        };
45808
45809        // Write value now needs to set the selected property of each matching option
45810        selectCtrl.writeValue = function writeMultipleValue(value) {
45811          var items = new HashMap(value);
45812          forEach(element.find('option'), function(option) {
45813            option.selected = isDefined(items.get(option.value));
45814          });
45815        };
45816
45817        // we have to do it on each watch since ngModel watches reference, but
45818        // we need to work of an array, so we need to see if anything was inserted/removed
45819        var lastView, lastViewRef = NaN;
45820        scope.$watch(function selectMultipleWatch() {
45821          if (lastViewRef === ngModelCtrl.$viewValue && !equals(lastView, ngModelCtrl.$viewValue)) {
45822            lastView = shallowCopy(ngModelCtrl.$viewValue);
45823            ngModelCtrl.$render();
45824          }
45825          lastViewRef = ngModelCtrl.$viewValue;
45826        });
45827
45828        // If we are a multiple select then value is now a collection
45829        // so the meaning of $isEmpty changes
45830        ngModelCtrl.$isEmpty = function(value) {
45831          return !value || value.length === 0;
45832        };
45833
45834      }
45835    }
45836  };
45837};
45838
45839
45840// The option directive is purely designed to communicate the existence (or lack of)
45841// of dynamically created (and destroyed) option elements to their containing select
45842// directive via its controller.
45843var optionDirective = ['$interpolate', function($interpolate) {
45844
45845  function chromeHack(optionElement) {
45846    // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459
45847    // Adding an <option selected="selected"> element to a <select required="required"> should
45848    // automatically select the new element
45849    if (optionElement[0].hasAttribute('selected')) {
45850      optionElement[0].selected = true;
45851    }
45852  }
45853
45854  return {
45855    restrict: 'E',
45856    priority: 100,
45857    compile: function(element, attr) {
45858
45859      if (isDefined(attr.value)) {
45860        // If the value attribute is defined, check if it contains an interpolation
45861        var valueInterpolated = $interpolate(attr.value, true);
45862      } else {
45863        // If the value attribute is not defined then we fall back to the
45864        // text content of the option element, which may be interpolated
45865        var interpolateFn = $interpolate(element.text(), true);
45866        if (!interpolateFn) {
45867          attr.$set('value', element.text());
45868        }
45869      }
45870
45871      return function(scope, element, attr) {
45872
45873        // This is an optimization over using ^^ since we don't want to have to search
45874        // all the way to the root of the DOM for every single option element
45875        var selectCtrlName = '$selectController',
45876            parent = element.parent(),
45877            selectCtrl = parent.data(selectCtrlName) ||
45878              parent.parent().data(selectCtrlName); // in case we are in optgroup
45879
45880        function addOption(optionValue) {
45881          selectCtrl.addOption(optionValue, element);
45882          selectCtrl.ngModelCtrl.$render();
45883          chromeHack(element);
45884        }
45885
45886        // Only update trigger option updates if this is an option within a `select`
45887        // that also has `ngModel` attached
45888        if (selectCtrl && selectCtrl.ngModelCtrl) {
45889
45890          if (valueInterpolated) {
45891            // The value attribute is interpolated
45892            var oldVal;
45893            attr.$observe('value', function valueAttributeObserveAction(newVal) {
45894              if (isDefined(oldVal)) {
45895                selectCtrl.removeOption(oldVal);
45896              }
45897              oldVal = newVal;
45898              addOption(newVal);
45899            });
45900          } else if (interpolateFn) {
45901            // The text content is interpolated
45902            scope.$watch(interpolateFn, function interpolateWatchAction(newVal, oldVal) {
45903              attr.$set('value', newVal);
45904              if (oldVal !== newVal) {
45905                selectCtrl.removeOption(oldVal);
45906              }
45907              addOption(newVal);
45908            });
45909          } else {
45910            // The value attribute is static
45911            addOption(attr.value);
45912          }
45913
45914          element.on('$destroy', function() {
45915            selectCtrl.removeOption(attr.value);
45916            selectCtrl.ngModelCtrl.$render();
45917          });
45918        }
45919      };
45920    }
45921  };
45922}];
45923
45924var styleDirective = valueFn({
45925  restrict: 'E',
45926  terminal: false
45927});
45928
45929var requiredDirective = function() {
45930  return {
45931    restrict: 'A',
45932    require: '?ngModel',
45933    link: function(scope, elm, attr, ctrl) {
45934      if (!ctrl) return;
45935      attr.required = true; // force truthy in case we are on non input element
45936
45937      ctrl.$validators.required = function(modelValue, viewValue) {
45938        return !attr.required || !ctrl.$isEmpty(viewValue);
45939      };
45940
45941      attr.$observe('required', function() {
45942        ctrl.$validate();
45943      });
45944    }
45945  };
45946};
45947
45948
45949var patternDirective = function() {
45950  return {
45951    restrict: 'A',
45952    require: '?ngModel',
45953    link: function(scope, elm, attr, ctrl) {
45954      if (!ctrl) return;
45955
45956      var regexp, patternExp = attr.ngPattern || attr.pattern;
45957      attr.$observe('pattern', function(regex) {
45958        if (isString(regex) && regex.length > 0) {
45959          regex = new RegExp('^' + regex + '$');
45960        }
45961
45962        if (regex && !regex.test) {
45963          throw minErr('ngPattern')('noregexp',
45964            'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp,
45965            regex, startingTag(elm));
45966        }
45967
45968        regexp = regex || undefined;
45969        ctrl.$validate();
45970      });
45971
45972      ctrl.$validators.pattern = function(modelValue, viewValue) {
45973        // HTML5 pattern constraint validates the input value, so we validate the viewValue
45974        return ctrl.$isEmpty(viewValue) || isUndefined(regexp) || regexp.test(viewValue);
45975      };
45976    }
45977  };
45978};
45979
45980
45981var maxlengthDirective = function() {
45982  return {
45983    restrict: 'A',
45984    require: '?ngModel',
45985    link: function(scope, elm, attr, ctrl) {
45986      if (!ctrl) return;
45987
45988      var maxlength = -1;
45989      attr.$observe('maxlength', function(value) {
45990        var intVal = toInt(value);
45991        maxlength = isNaN(intVal) ? -1 : intVal;
45992        ctrl.$validate();
45993      });
45994      ctrl.$validators.maxlength = function(modelValue, viewValue) {
45995        return (maxlength < 0) || ctrl.$isEmpty(viewValue) || (viewValue.length <= maxlength);
45996      };
45997    }
45998  };
45999};
46000
46001var minlengthDirective = function() {
46002  return {
46003    restrict: 'A',
46004    require: '?ngModel',
46005    link: function(scope, elm, attr, ctrl) {
46006      if (!ctrl) return;
46007
46008      var minlength = 0;
46009      attr.$observe('minlength', function(value) {
46010        minlength = toInt(value) || 0;
46011        ctrl.$validate();
46012      });
46013      ctrl.$validators.minlength = function(modelValue, viewValue) {
46014        return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength;
46015      };
46016    }
46017  };
46018};
46019
46020if (window.angular.bootstrap) {
46021  //AngularJS is already loaded, so we can return here...
46022  console.log('WARNING: Tried to load angular more than once.');
46023  return;
46024}
46025
46026//try to bind to jquery now so that one can write jqLite(document).ready()
46027//but we will rebind on bootstrap again.
46028bindJQuery();
46029
46030publishExternalAPI(angular);
46031
46032angular.module("ngLocale", [], ["$provide", function($provide) {
46033var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
46034function getDecimals(n) {
46035  n = n + '';
46036  var i = n.indexOf('.');
46037  return (i == -1) ? 0 : n.length - i - 1;
46038}
46039
46040function getVF(n, opt_precision) {
46041  var v = opt_precision;
46042
46043  if (undefined === v) {
46044    v = Math.min(getDecimals(n), 3);
46045  }
46046
46047  var base = Math.pow(10, v);
46048  var f = ((n * base) | 0) % base;
46049  return {v: v, f: f};
46050}
46051
46052$provide.value("$locale", {
46053  "DATETIME_FORMATS": {
46054    "AMPMS": [
46055      "AM",
46056      "PM"
46057    ],
46058    "DAY": [
46059      "Sunday",
46060      "Monday",
46061      "Tuesday",
46062      "Wednesday",
46063      "Thursday",
46064      "Friday",
46065      "Saturday"
46066    ],
46067    "ERANAMES": [
46068      "Before Christ",
46069      "Anno Domini"
46070    ],
46071    "ERAS": [
46072      "BC",
46073      "AD"
46074    ],
46075    "FIRSTDAYOFWEEK": 6,
46076    "MONTH": [
46077      "January",
46078      "February",
46079      "March",
46080      "April",
46081      "May",
46082      "June",
46083      "July",
46084      "August",
46085      "September",
46086      "October",
46087      "November",
46088      "December"
46089    ],
46090    "SHORTDAY": [
46091      "Sun",
46092      "Mon",
46093      "Tue",
46094      "Wed",
46095      "Thu",
46096      "Fri",
46097      "Sat"
46098    ],
46099    "SHORTMONTH": [
46100      "Jan",
46101      "Feb",
46102      "Mar",
46103      "Apr",
46104      "May",
46105      "Jun",
46106      "Jul",
46107      "Aug",
46108      "Sep",
46109      "Oct",
46110      "Nov",
46111      "Dec"
46112    ],
46113    "WEEKENDRANGE": [
46114      5,
46115      6
46116    ],
46117    "fullDate": "EEEE, MMMM d, y",
46118    "longDate": "MMMM d, y",
46119    "medium": "MMM d, y h:mm:ss a",
46120    "mediumDate": "MMM d, y",
46121    "mediumTime": "h:mm:ss a",
46122    "short": "M/d/yy h:mm a",
46123    "shortDate": "M/d/yy",
46124    "shortTime": "h:mm a"
46125  },
46126  "NUMBER_FORMATS": {
46127    "CURRENCY_SYM": "$",
46128    "DECIMAL_SEP": ".",
46129    "GROUP_SEP": ",",
46130    "PATTERNS": [
46131      {
46132        "gSize": 3,
46133        "lgSize": 3,
46134        "maxFrac": 3,
46135        "minFrac": 0,
46136        "minInt": 1,
46137        "negPre": "-",
46138        "negSuf": "",
46139        "posPre": "",
46140        "posSuf": ""
46141      },
46142      {
46143        "gSize": 3,
46144        "lgSize": 3,
46145        "maxFrac": 2,
46146        "minFrac": 2,
46147        "minInt": 1,
46148        "negPre": "-\u00a4",
46149        "negSuf": "",
46150        "posPre": "\u00a4",
46151        "posSuf": ""
46152      }
46153    ]
46154  },
46155  "id": "en-us",
46156  "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;}
46157});
46158}]);
46159
46160  jqLite(document).ready(function() {
46161    angularInit(document, bootstrap);
46162  });
46163
46164})(window, document);
46165
46166!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>');
46167/**
46168 * @license AngularJS v1.4.7
46169 * (c) 2010-2015 Google, Inc. http://angularjs.org
46170 * License: MIT
46171 */
46172(function(window, angular, undefined) {'use strict';
46173
46174/**
46175 * @ngdoc module
46176 * @name ngTouch
46177 * @description
46178 *
46179 * # ngTouch
46180 *
46181 * The `ngTouch` module provides touch events and other helpers for touch-enabled devices.
46182 * The implementation is based on jQuery Mobile touch event handling
46183 * ([jquerymobile.com](http://jquerymobile.com/)).
46184 *
46185 *
46186 * See {@link ngTouch.$swipe `$swipe`} for usage.
46187 *
46188 * <div doc-module-components="ngTouch"></div>
46189 *
46190 */
46191
46192// define ngTouch module
46193/* global -ngTouch */
46194var ngTouch = angular.module('ngTouch', []);
46195
46196function nodeName_(element) {
46197  return angular.lowercase(element.nodeName || (element[0] && element[0].nodeName));
46198}
46199
46200/* global ngTouch: false */
46201
46202    /**
46203     * @ngdoc service
46204     * @name $swipe
46205     *
46206     * @description
46207     * The `$swipe` service is a service that abstracts the messier details of hold-and-drag swipe
46208     * behavior, to make implementing swipe-related directives more convenient.
46209     *
46210     * Requires the {@link ngTouch `ngTouch`} module to be installed.
46211     *
46212     * `$swipe` is used by the `ngSwipeLeft` and `ngSwipeRight` directives in `ngTouch`, and by
46213     * `ngCarousel` in a separate component.
46214     *
46215     * # Usage
46216     * The `$swipe` service is an object with a single method: `bind`. `bind` takes an element
46217     * which is to be watched for swipes, and an object with four handler functions. See the
46218     * documentation for `bind` below.
46219     */
46220
46221ngTouch.factory('$swipe', [function() {
46222  // The total distance in any direction before we make the call on swipe vs. scroll.
46223  var MOVE_BUFFER_RADIUS = 10;
46224
46225  var POINTER_EVENTS = {
46226    'mouse': {
46227      start: 'mousedown',
46228      move: 'mousemove',
46229      end: 'mouseup'
46230    },
46231    'touch': {
46232      start: 'touchstart',
46233      move: 'touchmove',
46234      end: 'touchend',
46235      cancel: 'touchcancel'
46236    }
46237  };
46238
46239  function getCoordinates(event) {
46240    var originalEvent = event.originalEvent || event;
46241    var touches = originalEvent.touches && originalEvent.touches.length ? originalEvent.touches : [originalEvent];
46242    var e = (originalEvent.changedTouches && originalEvent.changedTouches[0]) || touches[0];
46243
46244    return {
46245      x: e.clientX,
46246      y: e.clientY
46247    };
46248  }
46249
46250  function getEvents(pointerTypes, eventType) {
46251    var res = [];
46252    angular.forEach(pointerTypes, function(pointerType) {
46253      var eventName = POINTER_EVENTS[pointerType][eventType];
46254      if (eventName) {
46255        res.push(eventName);
46256      }
46257    });
46258    return res.join(' ');
46259  }
46260
46261  return {
46262    /**
46263     * @ngdoc method
46264     * @name $swipe#bind
46265     *
46266     * @description
46267     * The main method of `$swipe`. It takes an element to be watched for swipe motions, and an
46268     * object containing event handlers.
46269     * The pointer types that should be used can be specified via the optional
46270     * third argument, which is an array of strings `'mouse'` and `'touch'`. By default,
46271     * `$swipe` will listen for `mouse` and `touch` events.
46272     *
46273     * The four events are `start`, `move`, `end`, and `cancel`. `start`, `move`, and `end`
46274     * receive as a parameter a coordinates object of the form `{ x: 150, y: 310 }` and the raw
46275     * `event`. `cancel` receives the raw `event` as its single parameter.
46276     *
46277     * `start` is called on either `mousedown` or `touchstart`. After this event, `$swipe` is
46278     * watching for `touchmove` or `mousemove` events. These events are ignored until the total
46279     * distance moved in either dimension exceeds a small threshold.
46280     *
46281     * Once this threshold is exceeded, either the horizontal or vertical delta is greater.
46282     * - If the horizontal distance is greater, this is a swipe and `move` and `end` events follow.
46283     * - If the vertical distance is greater, this is a scroll, and we let the browser take over.
46284     *   A `cancel` event is sent.
46285     *
46286     * `move` is called on `mousemove` and `touchmove` after the above logic has determined that
46287     * a swipe is in progress.
46288     *
46289     * `end` is called when a swipe is successfully completed with a `touchend` or `mouseup`.
46290     *
46291     * `cancel` is called either on a `touchcancel` from the browser, or when we begin scrolling
46292     * as described above.
46293     *
46294     */
46295    bind: function(element, eventHandlers, pointerTypes) {
46296      // Absolute total movement, used to control swipe vs. scroll.
46297      var totalX, totalY;
46298      // Coordinates of the start position.
46299      var startCoords;
46300      // Last event's position.
46301      var lastPos;
46302      // Whether a swipe is active.
46303      var active = false;
46304
46305      pointerTypes = pointerTypes || ['mouse', 'touch'];
46306      element.on(getEvents(pointerTypes, 'start'), function(event) {
46307        startCoords = getCoordinates(event);
46308        active = true;
46309        totalX = 0;
46310        totalY = 0;
46311        lastPos = startCoords;
46312        eventHandlers['start'] && eventHandlers['start'](startCoords, event);
46313      });
46314      var events = getEvents(pointerTypes, 'cancel');
46315      if (events) {
46316        element.on(events, function(event) {
46317          active = false;
46318          eventHandlers['cancel'] && eventHandlers['cancel'](event);
46319        });
46320      }
46321
46322      element.on(getEvents(pointerTypes, 'move'), function(event) {
46323        if (!active) return;
46324
46325        // Android will send a touchcancel if it thinks we're starting to scroll.
46326        // So when the total distance (+ or - or both) exceeds 10px in either direction,
46327        // we either:
46328        // - On totalX > totalY, we send preventDefault() and treat this as a swipe.
46329        // - On totalY > totalX, we let the browser handle it as a scroll.
46330
46331        if (!startCoords) return;
46332        var coords = getCoordinates(event);
46333
46334        totalX += Math.abs(coords.x - lastPos.x);
46335        totalY += Math.abs(coords.y - lastPos.y);
46336
46337        lastPos = coords;
46338
46339        if (totalX < MOVE_BUFFER_RADIUS && totalY < MOVE_BUFFER_RADIUS) {
46340          return;
46341        }
46342
46343        // One of totalX or totalY has exceeded the buffer, so decide on swipe vs. scroll.
46344        if (totalY > totalX) {
46345          // Allow native scrolling to take over.
46346          active = false;
46347          eventHandlers['cancel'] && eventHandlers['cancel'](event);
46348          return;
46349        } else {
46350          // Prevent the browser from scrolling.
46351          event.preventDefault();
46352          eventHandlers['move'] && eventHandlers['move'](coords, event);
46353        }
46354      });
46355
46356      element.on(getEvents(pointerTypes, 'end'), function(event) {
46357        if (!active) return;
46358        active = false;
46359        eventHandlers['end'] && eventHandlers['end'](getCoordinates(event), event);
46360      });
46361    }
46362  };
46363}]);
46364
46365/* global ngTouch: false,
46366  nodeName_: false
46367*/
46368
46369/**
46370 * @ngdoc directive
46371 * @name ngClick
46372 *
46373 * @description
46374 * A more powerful replacement for the default ngClick designed to be used on touchscreen
46375 * devices. Most mobile browsers wait about 300ms after a tap-and-release before sending
46376 * the click event. This version handles them immediately, and then prevents the
46377 * following click event from propagating.
46378 *
46379 * Requires the {@link ngTouch `ngTouch`} module to be installed.
46380 *
46381 * This directive can fall back to using an ordinary click event, and so works on desktop
46382 * browsers as well as mobile.
46383 *
46384 * This directive also sets the CSS class `ng-click-active` while the element is being held
46385 * down (by a mouse click or touch) so you can restyle the depressed element if you wish.
46386 *
46387 * @element ANY
46388 * @param {expression} ngClick {@link guide/expression Expression} to evaluate
46389 * upon tap. (Event object is available as `$event`)
46390 *
46391 * @example
46392    <example module="ngClickExample" deps="angular-touch.js">
46393      <file name="index.html">
46394        <button ng-click="count = count + 1" ng-init="count=0">
46395          Increment
46396        </button>
46397        count: {{ count }}
46398      </file>
46399      <file name="script.js">
46400        angular.module('ngClickExample', ['ngTouch']);
46401      </file>
46402    </example>
46403 */
46404
46405ngTouch.config(['$provide', function($provide) {
46406  $provide.decorator('ngClickDirective', ['$delegate', function($delegate) {
46407    // drop the default ngClick directive
46408    $delegate.shift();
46409    return $delegate;
46410  }]);
46411}]);
46412
46413ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement',
46414    function($parse, $timeout, $rootElement) {
46415  var TAP_DURATION = 750; // Shorter than 750ms is a tap, longer is a taphold or drag.
46416  var MOVE_TOLERANCE = 12; // 12px seems to work in most mobile browsers.
46417  var PREVENT_DURATION = 2500; // 2.5 seconds maximum from preventGhostClick call to click
46418  var CLICKBUSTER_THRESHOLD = 25; // 25 pixels in any dimension is the limit for busting clicks.
46419
46420  var ACTIVE_CLASS_NAME = 'ng-click-active';
46421  var lastPreventedTime;
46422  var touchCoordinates;
46423  var lastLabelClickCoordinates;
46424
46425
46426  // TAP EVENTS AND GHOST CLICKS
46427  //
46428  // Why tap events?
46429  // Mobile browsers detect a tap, then wait a moment (usually ~300ms) to see if you're
46430  // double-tapping, and then fire a click event.
46431  //
46432  // This delay sucks and makes mobile apps feel unresponsive.
46433  // So we detect touchstart, touchcancel and touchend ourselves and determine when
46434  // the user has tapped on something.
46435  //
46436  // What happens when the browser then generates a click event?
46437  // The browser, of course, also detects the tap and fires a click after a delay. This results in
46438  // tapping/clicking twice. We do "clickbusting" to prevent it.
46439  //
46440  // How does it work?
46441  // We attach global touchstart and click handlers, that run during the capture (early) phase.
46442  // So the sequence for a tap is:
46443  // - global touchstart: Sets an "allowable region" at the point touched.
46444  // - element's touchstart: Starts a touch
46445  // (- touchcancel ends the touch, no click follows)
46446  // - element's touchend: Determines if the tap is valid (didn't move too far away, didn't hold
46447  //   too long) and fires the user's tap handler. The touchend also calls preventGhostClick().
46448  // - preventGhostClick() removes the allowable region the global touchstart created.
46449  // - The browser generates a click event.
46450  // - The global click handler catches the click, and checks whether it was in an allowable region.
46451  //     - If preventGhostClick was called, the region will have been removed, the click is busted.
46452  //     - If the region is still there, the click proceeds normally. Therefore clicks on links and
46453  //       other elements without ngTap on them work normally.
46454  //
46455  // This is an ugly, terrible hack!
46456  // Yeah, tell me about it. The alternatives are using the slow click events, or making our users
46457  // deal with the ghost clicks, so I consider this the least of evils. Fortunately Angular
46458  // encapsulates this ugly logic away from the user.
46459  //
46460  // Why not just put click handlers on the element?
46461  // We do that too, just to be sure. If the tap event caused the DOM to change,
46462  // it is possible another element is now in that position. To take account for these possibly
46463  // distinct elements, the handlers are global and care only about coordinates.
46464
46465  // Checks if the coordinates are close enough to be within the region.
46466  function hit(x1, y1, x2, y2) {
46467    return Math.abs(x1 - x2) < CLICKBUSTER_THRESHOLD && Math.abs(y1 - y2) < CLICKBUSTER_THRESHOLD;
46468  }
46469
46470  // Checks a list of allowable regions against a click location.
46471  // Returns true if the click should be allowed.
46472  // Splices out the allowable region from the list after it has been used.
46473  function checkAllowableRegions(touchCoordinates, x, y) {
46474    for (var i = 0; i < touchCoordinates.length; i += 2) {
46475      if (hit(touchCoordinates[i], touchCoordinates[i + 1], x, y)) {
46476        touchCoordinates.splice(i, i + 2);
46477        return true; // allowable region
46478      }
46479    }
46480    return false; // No allowable region; bust it.
46481  }
46482
46483  // Global click handler that prevents the click if it's in a bustable zone and preventGhostClick
46484  // was called recently.
46485  function onClick(event) {
46486    if (Date.now() - lastPreventedTime > PREVENT_DURATION) {
46487      return; // Too old.
46488    }
46489
46490    var touches = event.touches && event.touches.length ? event.touches : [event];
46491    var x = touches[0].clientX;
46492    var y = touches[0].clientY;
46493    // Work around desktop Webkit quirk where clicking a label will fire two clicks (on the label
46494    // and on the input element). Depending on the exact browser, this second click we don't want
46495    // to bust has either (0,0), negative coordinates, or coordinates equal to triggering label
46496    // click event
46497    if (x < 1 && y < 1) {
46498      return; // offscreen
46499    }
46500    if (lastLabelClickCoordinates &&
46501        lastLabelClickCoordinates[0] === x && lastLabelClickCoordinates[1] === y) {
46502      return; // input click triggered by label click
46503    }
46504    // reset label click coordinates on first subsequent click
46505    if (lastLabelClickCoordinates) {
46506      lastLabelClickCoordinates = null;
46507    }
46508    // remember label click coordinates to prevent click busting of trigger click event on input
46509    if (nodeName_(event.target) === 'label') {
46510      lastLabelClickCoordinates = [x, y];
46511    }
46512
46513    // Look for an allowable region containing this click.
46514    // If we find one, that means it was created by touchstart and not removed by
46515    // preventGhostClick, so we don't bust it.
46516    if (checkAllowableRegions(touchCoordinates, x, y)) {
46517      return;
46518    }
46519
46520    // If we didn't find an allowable region, bust the click.
46521    event.stopPropagation();
46522    event.preventDefault();
46523
46524    // Blur focused form elements
46525    event.target && event.target.blur && event.target.blur();
46526  }
46527
46528
46529  // Global touchstart handler that creates an allowable region for a click event.
46530  // This allowable region can be removed by preventGhostClick if we want to bust it.
46531  function onTouchStart(event) {
46532    var touches = event.touches && event.touches.length ? event.touches : [event];
46533    var x = touches[0].clientX;
46534    var y = touches[0].clientY;
46535    touchCoordinates.push(x, y);
46536
46537    $timeout(function() {
46538      // Remove the allowable region.
46539      for (var i = 0; i < touchCoordinates.length; i += 2) {
46540        if (touchCoordinates[i] == x && touchCoordinates[i + 1] == y) {
46541          touchCoordinates.splice(i, i + 2);
46542          return;
46543        }
46544      }
46545    }, PREVENT_DURATION, false);
46546  }
46547
46548  // On the first call, attaches some event handlers. Then whenever it gets called, it creates a
46549  // zone around the touchstart where clicks will get busted.
46550  function preventGhostClick(x, y) {
46551    if (!touchCoordinates) {
46552      $rootElement[0].addEventListener('click', onClick, true);
46553      $rootElement[0].addEventListener('touchstart', onTouchStart, true);
46554      touchCoordinates = [];
46555    }
46556
46557    lastPreventedTime = Date.now();
46558
46559    checkAllowableRegions(touchCoordinates, x, y);
46560  }
46561
46562  // Actual linking function.
46563  return function(scope, element, attr) {
46564    var clickHandler = $parse(attr.ngClick),
46565        tapping = false,
46566        tapElement,  // Used to blur the element after a tap.
46567        startTime,   // Used to check if the tap was held too long.
46568        touchStartX,
46569        touchStartY;
46570
46571    function resetState() {
46572      tapping = false;
46573      element.removeClass(ACTIVE_CLASS_NAME);
46574    }
46575
46576    element.on('touchstart', function(event) {
46577      tapping = true;
46578      tapElement = event.target ? event.target : event.srcElement; // IE uses srcElement.
46579      // Hack for Safari, which can target text nodes instead of containers.
46580      if (tapElement.nodeType == 3) {
46581        tapElement = tapElement.parentNode;
46582      }
46583
46584      element.addClass(ACTIVE_CLASS_NAME);
46585
46586      startTime = Date.now();
46587
46588      // Use jQuery originalEvent
46589      var originalEvent = event.originalEvent || event;
46590      var touches = originalEvent.touches && originalEvent.touches.length ? originalEvent.touches : [originalEvent];
46591      var e = touches[0];
46592      touchStartX = e.clientX;
46593      touchStartY = e.clientY;
46594    });
46595
46596    element.on('touchcancel', function(event) {
46597      resetState();
46598    });
46599
46600    element.on('touchend', function(event) {
46601      var diff = Date.now() - startTime;
46602
46603      // Use jQuery originalEvent
46604      var originalEvent = event.originalEvent || event;
46605      var touches = (originalEvent.changedTouches && originalEvent.changedTouches.length) ?
46606          originalEvent.changedTouches :
46607          ((originalEvent.touches && originalEvent.touches.length) ? originalEvent.touches : [originalEvent]);
46608      var e = touches[0];
46609      var x = e.clientX;
46610      var y = e.clientY;
46611      var dist = Math.sqrt(Math.pow(x - touchStartX, 2) + Math.pow(y - touchStartY, 2));
46612
46613      if (tapping && diff < TAP_DURATION && dist < MOVE_TOLERANCE) {
46614        // Call preventGhostClick so the clickbuster will catch the corresponding click.
46615        preventGhostClick(x, y);
46616
46617        // Blur the focused element (the button, probably) before firing the callback.
46618        // This doesn't work perfectly on Android Chrome, but seems to work elsewhere.
46619        // I couldn't get anything to work reliably on Android Chrome.
46620        if (tapElement) {
46621          tapElement.blur();
46622        }
46623
46624        if (!angular.isDefined(attr.disabled) || attr.disabled === false) {
46625          element.triggerHandler('click', [event]);
46626        }
46627      }
46628
46629      resetState();
46630    });
46631
46632    // Hack for iOS Safari's benefit. It goes searching for onclick handlers and is liable to click
46633    // something else nearby.
46634    element.onclick = function(event) { };
46635
46636    // Actual click handler.
46637    // There are three different kinds of clicks, only two of which reach this point.
46638    // - On desktop browsers without touch events, their clicks will always come here.
46639    // - On mobile browsers, the simulated "fast" click will call this.
46640    // - But the browser's follow-up slow click will be "busted" before it reaches this handler.
46641    // Therefore it's safe to use this directive on both mobile and desktop.
46642    element.on('click', function(event, touchend) {
46643      scope.$apply(function() {
46644        clickHandler(scope, {$event: (touchend || event)});
46645      });
46646    });
46647
46648    element.on('mousedown', function(event) {
46649      element.addClass(ACTIVE_CLASS_NAME);
46650    });
46651
46652    element.on('mousemove mouseup', function(event) {
46653      element.removeClass(ACTIVE_CLASS_NAME);
46654    });
46655
46656  };
46657}]);
46658
46659/* global ngTouch: false */
46660
46661/**
46662 * @ngdoc directive
46663 * @name ngSwipeLeft
46664 *
46665 * @description
46666 * Specify custom behavior when an element is swiped to the left on a touchscreen device.
46667 * A leftward swipe is a quick, right-to-left slide of the finger.
46668 * Though ngSwipeLeft is designed for touch-based devices, it will work with a mouse click and drag
46669 * too.
46670 *
46671 * To disable the mouse click and drag functionality, add `ng-swipe-disable-mouse` to
46672 * the `ng-swipe-left` or `ng-swipe-right` DOM Element.
46673 *
46674 * Requires the {@link ngTouch `ngTouch`} module to be installed.
46675 *
46676 * @element ANY
46677 * @param {expression} ngSwipeLeft {@link guide/expression Expression} to evaluate
46678 * upon left swipe. (Event object is available as `$event`)
46679 *
46680 * @example
46681    <example module="ngSwipeLeftExample" deps="angular-touch.js">
46682      <file name="index.html">
46683        <div ng-show="!showActions" ng-swipe-left="showActions = true">
46684          Some list content, like an email in the inbox
46685        </div>
46686        <div ng-show="showActions" ng-swipe-right="showActions = false">
46687          <button ng-click="reply()">Reply</button>
46688          <button ng-click="delete()">Delete</button>
46689        </div>
46690      </file>
46691      <file name="script.js">
46692        angular.module('ngSwipeLeftExample', ['ngTouch']);
46693      </file>
46694    </example>
46695 */
46696
46697/**
46698 * @ngdoc directive
46699 * @name ngSwipeRight
46700 *
46701 * @description
46702 * Specify custom behavior when an element is swiped to the right on a touchscreen device.
46703 * A rightward swipe is a quick, left-to-right slide of the finger.
46704 * Though ngSwipeRight is designed for touch-based devices, it will work with a mouse click and drag
46705 * too.
46706 *
46707 * Requires the {@link ngTouch `ngTouch`} module to be installed.
46708 *
46709 * @element ANY
46710 * @param {expression} ngSwipeRight {@link guide/expression Expression} to evaluate
46711 * upon right swipe. (Event object is available as `$event`)
46712 *
46713 * @example
46714    <example module="ngSwipeRightExample" deps="angular-touch.js">
46715      <file name="index.html">
46716        <div ng-show="!showActions" ng-swipe-left="showActions = true">
46717          Some list content, like an email in the inbox
46718        </div>
46719        <div ng-show="showActions" ng-swipe-right="showActions = false">
46720          <button ng-click="reply()">Reply</button>
46721          <button ng-click="delete()">Delete</button>
46722        </div>
46723      </file>
46724      <file name="script.js">
46725        angular.module('ngSwipeRightExample', ['ngTouch']);
46726      </file>
46727    </example>
46728 */
46729
46730function makeSwipeDirective(directiveName, direction, eventName) {
46731  ngTouch.directive(directiveName, ['$parse', '$swipe', function($parse, $swipe) {
46732    // The maximum vertical delta for a swipe should be less than 75px.
46733    var MAX_VERTICAL_DISTANCE = 75;
46734    // Vertical distance should not be more than a fraction of the horizontal distance.
46735    var MAX_VERTICAL_RATIO = 0.3;
46736    // At least a 30px lateral motion is necessary for a swipe.
46737    var MIN_HORIZONTAL_DISTANCE = 30;
46738
46739    return function(scope, element, attr) {
46740      var swipeHandler = $parse(attr[directiveName]);
46741
46742      var startCoords, valid;
46743
46744      function validSwipe(coords) {
46745        // Check that it's within the coordinates.
46746        // Absolute vertical distance must be within tolerances.
46747        // Horizontal distance, we take the current X - the starting X.
46748        // This is negative for leftward swipes and positive for rightward swipes.
46749        // After multiplying by the direction (-1 for left, +1 for right), legal swipes
46750        // (ie. same direction as the directive wants) will have a positive delta and
46751        // illegal ones a negative delta.
46752        // Therefore this delta must be positive, and larger than the minimum.
46753        if (!startCoords) return false;
vendor: 4,168 bytes, lines 46754-46874
46754        var deltaY = Math.abs(coords.y - startCoords.y);
46755        var deltaX = (coords.x - startCoords.x) * direction;
46756        return valid && // Short circuit for already-invalidated swipes.
46757            deltaY < MAX_VERTICAL_DISTANCE &&
46758            deltaX > 0 &&
46759            deltaX > MIN_HORIZONTAL_DISTANCE &&
46760            deltaY / deltaX < MAX_VERTICAL_RATIO;
46761      }
46762
46763      var pointerTypes = ['touch'];
46764      if (!angular.isDefined(attr['ngSwipeDisableMouse'])) {
46765        pointerTypes.push('mouse');
46766      }
46767      $swipe.bind(element, {
46768        'start': function(coords, event) {
46769          startCoords = coords;
46770          valid = true;
46771        },
46772        'cancel': function(event) {
46773          valid = false;
46774        },
46775        'end': function(coords, event) {
46776          if (validSwipe(coords)) {
46777            scope.$apply(function() {
46778              element.triggerHandler(eventName);
46779              swipeHandler(scope, {$event: event});
46780            });
46781          }
46782        }
46783      }, pointerTypes);
46784    };
46785  }]);
46786}
46787
46788// Left is negative X-coordinate, right is positive.
46789makeSwipeDirective('ngSwipeLeft', -1, 'swipeleft');
46790makeSwipeDirective('ngSwipeRight', 1, 'swiperight');
46791
46792
46793
46794})(window, window.angular);
46795
46796/**
46797 * @license AngularJS v1.4.7
46798 * (c) 2010-2015 Google, Inc. http://angularjs.org
46799 * License: MIT
46800 */
46801(function(window, angular, undefined) {'use strict';
46802
46803/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
46804 *     Any commits to this file should be reviewed with security in mind.  *
46805 *   Changes to this file can potentially create security vulnerabilities. *
46806 *          An approval from 2 Core members with history of modifying      *
46807 *                         this file is required.                          *
46808 *                                                                         *
46809 *  Does the change somehow allow for arbitrary javascript to be executed? *
46810 *    Or allows for someone to change the prototype of built-in objects?   *
46811 *     Or gives undesired access to variables likes document or window?    *
46812 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
46813
46814var $sanitizeMinErr = angular.$$minErr('$sanitize');
46815
46816/**
46817 * @ngdoc module
46818 * @name ngSanitize
46819 * @description
46820 *
46821 * # ngSanitize
46822 *
46823 * The `ngSanitize` module provides functionality to sanitize HTML.
46824 *
46825 *
46826 * <div doc-module-components="ngSanitize"></div>
46827 *
46828 * See {@link ngSanitize.$sanitize `$sanitize`} for usage.
46829 */
46830
46831/*
46832 * HTML Parser By Misko Hevery ([email protected])
46833 * based on:  HTML Parser By John Resig (ejohn.org)
46834 * Original code by Erik Arvidsson, Mozilla Public License
46835 * http://erik.eae.net/simplehtmlparser/simplehtmlparser.js
46836 *
46837 * // Use like so:
46838 * htmlParser(htmlString, {
46839 *     start: function(tag, attrs, unary) {},
46840 *     end: function(tag) {},
46841 *     chars: function(text) {},
46842 *     comment: function(text) {}
46843 * });
46844 *
46845 */
46846
46847
46848/**
46849 * @ngdoc service
46850 * @name $sanitize
46851 * @kind function
46852 *
46853 * @description
46854 *   The input is sanitized by parsing the HTML into tokens. All safe tokens (from a whitelist) are
46855 *   then serialized back to properly escaped html string. This means that no unsafe input can make
46856 *   it into the returned string, however, since our parser is more strict than a typical browser
46857 *   parser, it's possible that some obscure input, which would be recognized as valid HTML by a
46858 *   browser, won't make it through the sanitizer. The input may also contain SVG markup.
46859 *   The whitelist is configured using the functions `aHrefSanitizationWhitelist` and
46860 *   `imgSrcSanitizationWhitelist` of {@link ng.$compileProvider `$compileProvider`}.
46861 *
46862 * @param {string} html HTML input.
46863 * @returns {string} Sanitized HTML.
46864 *
46865 * @example
46866   <example module="sanitizeExample" deps="angular-sanitize.js">
46867   <file name="index.html">
46868     <script>
46869         angular.module('sanitizeExample', ['ngSanitize'])
46870           .controller('ExampleController', ['$scope', '$sce', function($scope, $sce) {
46871             $scope.snippet =
46872               '<p style="color:blue">an html\n' +
46873               '<em onmouseover="this.textContent=\'PWN3D!\'">click here</em>\n' +
46874               'snippet</p>';
46875             $scope.deliberatelyTrustDangerousSnippet = function() {
46876               return $sce.trustAsHtml($scope.snippet);
46877             };
46878           }]);
46879     </script>
46880     <div ng-controller="ExampleController">
46881        Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
46882       <table>
46883         <tr>
46884           <td>Directive</td>
46885           <td>How</td>
46886           <td>Source</td>
46887           <td>Rendered</td>
46888         </tr>
46889         <tr id="bind-html-with-sanitize">
46890           <td>ng-bind-html</td>
46891           <td>Automatically uses $sanitize</td>
46892           <td><pre>&lt;div ng-bind-html="snippet"&gt;<br/>&lt;/div&gt;</pre></td>
46893           <td><div ng-bind-html="snippet"></div></td>
46894         </tr>
46895         <tr id="bind-html-with-trust">
46896           <td>ng-bind-html</td>
46897           <td>Bypass $sanitize by explicitly trusting the dangerous value</td>
46898           <td>
46899           <pre>&lt;div ng-bind-html="deliberatelyTrustDangerousSnippet()"&gt;
46900&lt;/div&gt;</pre>
46901           </td>
46902           <td><div ng-bind-html="deliberatelyTrustDangerousSnippet()"></div></td>
46903         </tr>
46904         <tr id="bind-default">
46905           <td>ng-bind</td>
46906           <td>Automatically escapes</td>
46907           <td><pre>&lt;div ng-bind="snippet"&gt;<br/>&lt;/div&gt;</pre></td>
46908           <td><div ng-bind="snippet"></div></td>
46909         </tr>
46910       </table>
46911       </div>
46912   </file>
46913   <file name="protractor.js" type="protractor">
46914     it('should sanitize the html snippet by default', function() {
46915       expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()).
46916         toBe('<p>an html\n<em>click here</em>\nsnippet</p>');
46917     });
46918
46919     it('should inline raw snippet if bound to a trusted value', function() {
46920       expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()).
46921         toBe("<p style=\"color:blue\">an html\n" +
46922              "<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
46923              "snippet</p>");
46924     });
46925
46926     it('should escape snippet without any filter', function() {
46927       expect(element(by.css('#bind-default div')).getInnerHtml()).
46928         toBe("&lt;p style=\"color:blue\"&gt;an html\n" +
46929              "&lt;em onmouseover=\"this.textContent='PWN3D!'\"&gt;click here&lt;/em&gt;\n" +
46930              "snippet&lt;/p&gt;");
46931     });
46932
46933     it('should update', function() {
46934       element(by.model('snippet')).clear();
46935       element(by.model('snippet')).sendKeys('new <b onclick="alert(1)">text</b>');
46936       expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()).
46937         toBe('new <b>text</b>');
46938       expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()).toBe(
46939         'new <b onclick="alert(1)">text</b>');
46940       expect(element(by.css('#bind-default div')).getInnerHtml()).toBe(
46941         "new &lt;b onclick=\"alert(1)\"&gt;text&lt;/b&gt;");
46942     });
46943   </file>
46944   </example>
46945 */
46946function $SanitizeProvider() {
46947  this.$get = ['$$sanitizeUri', function($$sanitizeUri) {
46948    return function(html) {
46949      var buf = [];
46950      htmlParser(html, htmlSanitizeWriter(buf, function(uri, isImage) {
46951        return !/^unsafe/.test($$sanitizeUri(uri, isImage));
46952      }));
46953      return buf.join('');
46954    };
46955  }];
46956}
46957
46958function sanitizeText(chars) {
46959  var buf = [];
46960  var writer = htmlSanitizeWriter(buf, angular.noop);
46961  writer.chars(chars);
46962  return buf.join('');
46963}
46964
46965
46966// Regular Expressions for parsing tags and attributes
46967var START_TAG_REGEXP =
46968       /^<((?:[a-zA-Z])[\w:-]*)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*(>?)/,
46969  END_TAG_REGEXP = /^<\/\s*([\w:-]+)[^>]*>/,
46970  ATTR_REGEXP = /([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,
46971  BEGIN_TAG_REGEXP = /^</,
46972  BEGING_END_TAGE_REGEXP = /^<\//,
46973  COMMENT_REGEXP = /<!--(.*?)-->/g,
46974  DOCTYPE_REGEXP = /<!DOCTYPE([^>]*?)>/i,
46975  CDATA_REGEXP = /<!\[CDATA\[(.*?)]]>/g,
46976  SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
46977  // Match everything outside of normal chars and " (quote character)
46978  NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g;
46979
46980
46981// Good source of info about elements and attributes
46982// http://dev.w3.org/html5/spec/Overview.html#semantics
46983// http://simon.html5.org/html-elements
46984
46985// Safe Void Elements - HTML5
46986// http://dev.w3.org/html5/spec/Overview.html#void-elements
46987var voidElements = makeMap("area,br,col,hr,img,wbr");
46988
46989// Elements that you can, intentionally, leave open (and which close themselves)
46990// http://dev.w3.org/html5/spec/Overview.html#optional-tags
46991var optionalEndTagBlockElements = makeMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),
46992    optionalEndTagInlineElements = makeMap("rp,rt"),
46993    optionalEndTagElements = angular.extend({},
46994                                            optionalEndTagInlineElements,
46995                                            optionalEndTagBlockElements);
46996
46997// Safe Block Elements - HTML5
46998var blockElements = angular.extend({}, optionalEndTagBlockElements, makeMap("address,article," +
46999        "aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5," +
47000        "h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul"));
47001
47002// Inline Elements - HTML5
47003var inlineElements = angular.extend({}, optionalEndTagInlineElements, makeMap("a,abbr,acronym,b," +
47004        "bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s," +
47005        "samp,small,span,strike,strong,sub,sup,time,tt,u,var"));
47006
47007// SVG Elements
47008// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Elements
47009// Note: the elements animate,animateColor,animateMotion,animateTransform,set are intentionally omitted.
47010// They can potentially allow for arbitrary javascript to be executed. See #11290
47011var svgElements = makeMap("circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph," +
47012        "hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline," +
47013        "radialGradient,rect,stop,svg,switch,text,title,tspan,use");
47014
47015// Special Elements (can contain anything)
47016var specialElements = makeMap("script,style");
47017
47018var validElements = angular.extend({},
47019                                   voidElements,
47020                                   blockElements,
47021                                   inlineElements,
47022                                   optionalEndTagElements,
47023                                   svgElements);
47024
47025//Attributes that have href and hence need to be sanitized
47026var uriAttrs = makeMap("background,cite,href,longdesc,src,usemap,xlink:href");
47027
47028var htmlAttrs = makeMap('abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,' +
47029    'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,' +
47030    'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,' +
47031    'scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,' +
47032    'valign,value,vspace,width');
47033
47034// SVG attributes (without "id" and "name" attributes)
47035// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Attributes
47036var svgAttrs = makeMap('accent-height,accumulate,additive,alphabetic,arabic-form,ascent,' +
47037    'baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,' +
47038    'cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,' +
47039    'font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,' +
47040    'height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,' +
47041    'marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,' +
47042    'max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,' +
47043    'path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,' +
47044    'requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,' +
47045    'stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,' +
47046    'stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,' +
47047    'stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,' +
47048    'underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,' +
47049    'width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,' +
47050    'xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan', true);
47051
47052var validAttrs = angular.extend({},
47053                                uriAttrs,
47054                                svgAttrs,
47055                                htmlAttrs);
47056
47057function makeMap(str, lowercaseKeys) {
47058  var obj = {}, items = str.split(','), i;
47059  for (i = 0; i < items.length; i++) {
47060    obj[lowercaseKeys ? angular.lowercase(items[i]) : items[i]] = true;
47061  }
47062  return obj;
47063}
47064
47065
47066/**
47067 * @example
47068 * htmlParser(htmlString, {
47069 *     start: function(tag, attrs, unary) {},
47070 *     end: function(tag) {},
47071 *     chars: function(text) {},
47072 *     comment: function(text) {}
47073 * });
47074 *
47075 * @param {string} html string
47076 * @param {object} handler
47077 */
47078function htmlParser(html, handler) {
47079  if (typeof html !== 'string') {
47080    if (html === null || typeof html === 'undefined') {
47081      html = '';
47082    } else {
47083      html = '' + html;
47084    }
47085  }
47086  var index, chars, match, stack = [], last = html, text;
47087  stack.last = function() { return stack[stack.length - 1]; };
47088
47089  while (html) {
47090    text = '';
47091    chars = true;
47092
47093    // Make sure we're not in a script or style element
47094    if (!stack.last() || !specialElements[stack.last()]) {
47095
47096      // Comment
47097      if (html.indexOf("<!--") === 0) {
47098        // comments containing -- are not allowed unless they terminate the comment
47099        index = html.indexOf("--", 4);
47100
47101        if (index >= 0 && html.lastIndexOf("-->", index) === index) {
47102          if (handler.comment) handler.comment(html.substring(4, index));
47103          html = html.substring(index + 3);
47104          chars = false;
47105        }
47106      // DOCTYPE
47107      } else if (DOCTYPE_REGEXP.test(html)) {
47108        match = html.match(DOCTYPE_REGEXP);
47109
47110        if (match) {
47111          html = html.replace(match[0], '');
47112          chars = false;
47113        }
47114      // end tag
47115      } else if (BEGING_END_TAGE_REGEXP.test(html)) {
47116        match = html.match(END_TAG_REGEXP);
47117
47118        if (match) {
47119          html = html.substring(match[0].length);
47120          match[0].replace(END_TAG_REGEXP, parseEndTag);
47121          chars = false;
47122        }
47123
47124      // start tag
47125      } else if (BEGIN_TAG_REGEXP.test(html)) {
47126        match = html.match(START_TAG_REGEXP);
47127
47128        if (match) {
47129          // We only have a valid start-tag if there is a '>'.
47130          if (match[4]) {
47131            html = html.substring(match[0].length);
47132            match[0].replace(START_TAG_REGEXP, parseStartTag);
47133          }
47134          chars = false;
47135        } else {
47136          // no ending tag found --- this piece should be encoded as an entity.
47137          text += '<';
47138          html = html.substring(1);
47139        }
47140      }
47141
47142      if (chars) {
47143        index = html.indexOf("<");
47144
47145        text += index < 0 ? html : html.substring(0, index);
47146        html = index < 0 ? "" : html.substring(index);
47147
47148        if (handler.chars) handler.chars(decodeEntities(text));
47149      }
47150
47151    } else {
47152      // IE versions 9 and 10 do not understand the regex '[^]', so using a workaround with [\W\w].
47153      html = html.replace(new RegExp("([\\W\\w]*)<\\s*\\/\\s*" + stack.last() + "[^>]*>", 'i'),
47154        function(all, text) {
47155          text = text.replace(COMMENT_REGEXP, "$1").replace(CDATA_REGEXP, "$1");
47156
47157          if (handler.chars) handler.chars(decodeEntities(text));
47158
47159          return "";
47160      });
47161
47162      parseEndTag("", stack.last());
47163    }
47164
47165    if (html == last) {
47166      throw $sanitizeMinErr('badparse', "The sanitizer was unable to parse the following block " +
47167                                        "of html: {0}", html);
47168    }
47169    last = html;
47170  }
47171
47172  // Clean up any remaining tags
47173  parseEndTag();
47174
47175  function parseStartTag(tag, tagName, rest, unary) {
47176    tagName = angular.lowercase(tagName);
47177    if (blockElements[tagName]) {
47178      while (stack.last() && inlineElements[stack.last()]) {
47179        parseEndTag("", stack.last());
47180      }
47181    }
47182
47183    if (optionalEndTagElements[tagName] && stack.last() == tagName) {
47184      parseEndTag("", tagName);
47185    }
47186
47187    unary = voidElements[tagName] || !!unary;
47188
47189    if (!unary) {
47190      stack.push(tagName);
47191    }
47192
47193    var attrs = {};
47194
47195    rest.replace(ATTR_REGEXP,
47196      function(match, name, doubleQuotedValue, singleQuotedValue, unquotedValue) {
47197        var value = doubleQuotedValue
47198          || singleQuotedValue
47199          || unquotedValue
47200          || '';
47201
47202        attrs[name] = decodeEntities(value);
47203    });
47204    if (handler.start) handler.start(tagName, attrs, unary);
47205  }
47206
47207  function parseEndTag(tag, tagName) {
47208    var pos = 0, i;
47209    tagName = angular.lowercase(tagName);
47210    if (tagName) {
47211      // Find the closest opened tag of the same type
47212      for (pos = stack.length - 1; pos >= 0; pos--) {
47213        if (stack[pos] == tagName) break;
47214      }
47215    }
47216
47217    if (pos >= 0) {
47218      // Close all the open elements, up the stack
47219      for (i = stack.length - 1; i >= pos; i--)
47220        if (handler.end) handler.end(stack[i]);
47221
47222      // Remove the open elements from the stack
47223      stack.length = pos;
47224    }
47225  }
47226}
47227
47228var hiddenPre=document.createElement("pre");
47229/**
47230 * decodes all entities into regular string
47231 * @param value
47232 * @returns {string} A string with decoded entities.
47233 */
47234function decodeEntities(value) {
47235  if (!value) { return ''; }
47236
47237  hiddenPre.innerHTML = value.replace(/</g,"&lt;");
47238  // innerText depends on styling as it doesn't display hidden elements.
47239  // Therefore, it's better to use textContent not to cause unnecessary reflows.
47240  return hiddenPre.textContent;
47241}
47242
vendor: 1,248 bytes, lines 47243-47283
47243/**
47244 * Escapes all potentially dangerous characters, so that the
47245 * resulting string can be safely inserted into attribute or
47246 * element text.
47247 * @param value
47248 * @returns {string} escaped text
47249 */
47250function encodeEntities(value) {
47251  return value.
47252    replace(/&/g, '&amp;').
47253    replace(SURROGATE_PAIR_REGEXP, function(value) {
47254      var hi = value.charCodeAt(0);
47255      var low = value.charCodeAt(1);
47256      return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';
47257    }).
47258    replace(NON_ALPHANUMERIC_REGEXP, function(value) {
47259      return '&#' + value.charCodeAt(0) + ';';
47260    }).
47261    replace(/</g, '&lt;').
47262    replace(/>/g, '&gt;');
47263}
47264
47265/**
47266 * create an HTML/XML writer which writes to buffer
47267 * @param {Array} buf use buf.jain('') to get out sanitized html string
47268 * @returns {object} in the form of {
47269 *     start: function(tag, attrs, unary) {},
47270 *     end: function(tag) {},
47271 *     chars: function(text) {},
47272 *     comment: function(text) {}
47273 * }
47274 */
47275function htmlSanitizeWriter(buf, uriValidator) {
47276  var ignore = false;
47277  var out = angular.bind(buf, buf.push);
47278  return {
47279    start: function(tag, attrs, unary) {
47280      tag = angular.lowercase(tag);
47281      if (!ignore && specialElements[tag]) {
47282        ignore = tag;
47283      }
vendor: 2,266 bytes, lines 47284-47359
47284      if (!ignore && validElements[tag] === true) {
47285        out('<');
47286        out(tag);
47287        angular.forEach(attrs, function(value, key) {
47288          var lkey=angular.lowercase(key);
47289          var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background');
47290          if (validAttrs[lkey] === true &&
47291            (uriAttrs[lkey] !== true || uriValidator(value, isImage))) {
47292            out(' ');
47293            out(key);
47294            out('="');
47295            out(encodeEntities(value));
47296            out('"');
47297          }
47298        });
47299        out(unary ? '/>' : '>');
47300      }
47301    },
47302    end: function(tag) {
47303        tag = angular.lowercase(tag);
47304        if (!ignore && validElements[tag] === true) {
47305          out('</');
47306          out(tag);
47307          out('>');
47308        }
47309        if (tag == ignore) {
47310          ignore = false;
47311        }
47312      },
47313    chars: function(chars) {
47314        if (!ignore) {
47315          out(encodeEntities(chars));
47316        }
47317      }
47318  };
47319}
47320
47321
47322// define ngSanitize module and register $sanitize service
47323angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider);
47324
47325/* global sanitizeText: false */
47326
47327/**
47328 * @ngdoc filter
47329 * @name linky
47330 * @kind function
47331 *
47332 * @description
47333 * Finds links in text input and turns them into html links. Supports http/https/ftp/mailto and
47334 * plain email address links.
47335 *
47336 * Requires the {@link ngSanitize `ngSanitize`} module to be installed.
47337 *
47338 * @param {string} text Input text.
47339 * @param {string} target Window (_blank|_self|_parent|_top) or named frame to open links in.
47340 * @returns {string} Html-linkified text.
47341 *
47342 * @usage
47343   <span ng-bind-html="linky_expression | linky"></span>
47344 *
47345 * @example
47346   <example module="linkyExample" deps="angular-sanitize.js">
47347     <file name="index.html">
47348       <script>
47349         angular.module('linkyExample', ['ngSanitize'])
47350           .controller('ExampleController', ['$scope', function($scope) {
47351             $scope.snippet =
47352               'Pretty text with some links:\n'+
47353               'http://angularjs.org/,\n'+
47354               'mailto:[email protected],\n'+
47355               '[email protected],\n'+
47356               'and one more: ftp://127.0.0.1/.';
47357             $scope.snippetWithTarget = 'http://angularjs.org/';
47358           }]);
47359       </script>
47360       <div ng-controller="ExampleController">
47361       Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
47362       <table>
47363         <tr>
47364           <td>Filter</td>
47365           <td>Source</td>
47366           <td>Rendered</td>
47367         </tr>
47368         <tr id="linky-filter">
47369           <td>linky filter</td>
47370           <td>
47371             <pre>&lt;div ng-bind-html="snippet | linky"&gt;<br>&lt;/div&gt;</pre>
47372           </td>
47373           <td>
47374             <div ng-bind-html="snippet | linky"></div>
47375           </td>
47376         </tr>
47377         <tr id="linky-target">
47378          <td>linky target</td>
47379          <td>
47380            <pre>&lt;div ng-bind-html="snippetWithTarget | linky:'_blank'"&gt;<br>&lt;/div&gt;</pre>
47381          </td>
47382          <td>
47383            <div ng-bind-html="snippetWithTarget | linky:'_blank'"></div>
47384          </td>
47385         </tr>
47386         <tr id="escaped-html">
47387           <td>no filter</td>
47388           <td><pre>&lt;div ng-bind="snippet"&gt;<br>&lt;/div&gt;</pre></td>
47389           <td><div ng-bind="snippet"></div></td>
47390         </tr>
47391       </table>
47392     </file>
47393     <file name="protractor.js" type="protractor">
47394       it('should linkify the snippet with urls', function() {
47395         expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
47396             toBe('Pretty text with some links: http://angularjs.org/, [email protected], ' +
47397                  '[email protected], and one more: ftp://127.0.0.1/.');
47398         expect(element.all(by.css('#linky-filter a')).count()).toEqual(4);
47399       });
47400
47401       it('should not linkify snippet without the linky filter', function() {
47402         expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()).
47403             toBe('Pretty text with some links: http://angularjs.org/, mailto:[email protected], ' +
47404                  '[email protected], and one more: ftp://127.0.0.1/.');
47405         expect(element.all(by.css('#escaped-html a')).count()).toEqual(0);
47406       });
47407
47408       it('should update', function() {
47409         element(by.model('snippet')).clear();
47410         element(by.model('snippet')).sendKeys('new http://link.');
47411         expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
47412             toBe('new http://link.');
47413         expect(element.all(by.css('#linky-filter a')).count()).toEqual(1);
47414         expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText())
47415             .toBe('new http://link.');
47416       });
47417
47418       it('should work with the target property', function() {
47419        expect(element(by.id('linky-target')).
47420            element(by.binding("snippetWithTarget | linky:'_blank'")).getText()).
47421            toBe('http://angularjs.org/');
47422        expect(element(by.css('#linky-target a')).getAttribute('target')).toEqual('_blank');
47423       });
47424     </file>
47425   </example>
47426 */
47427angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
47428  var LINKY_URL_REGEXP =
47429        /((ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"\u201d\u2019]/i,
47430      MAILTO_REGEXP = /^mailto:/i;
47431
47432  return function(text, target) {
47433    if (!text) return text;
47434    var match;
47435    var raw = text;
47436    var html = [];
47437    var url;
47438    var i;
47439    while ((match = raw.match(LINKY_URL_REGEXP))) {
47440      // We can not end in these as they are sometimes found at the end of the sentence
47441      url = match[0];
47442      // if we did not match ftp/http/www/mailto then assume mailto
47443      if (!match[2] && !match[4]) {
47444        url = (match[3] ? 'http://' : 'mailto:') + url;
47445      }
47446      i = match.index;
vendor: 1,451 bytes, lines 47447-47512
47447      addText(raw.substr(0, i));
47448      addLink(url, match[0].replace(MAILTO_REGEXP, ''));
47449      raw = raw.substring(i + match[0].length);
47450    }
47451    addText(raw);
47452    return $sanitize(html.join(''));
47453
47454    function addText(text) {
47455      if (!text) {
47456        return;
47457      }
47458      html.push(sanitizeText(text));
47459    }
47460
47461    function addLink(url, text) {
47462      html.push('<a ');
47463      if (angular.isDefined(target)) {
47464        html.push('target="',
47465                  target,
47466                  '" ');
47467      }
47468      html.push('href="',
47469                url.replace(/"/g, '&quot;'),
47470                '">');
47471      addText(text);
47472      html.push('</a>');
47473    }
47474  };
47475}]);
47476
47477
47478})(window, window.angular);
47479
47480/**
47481 * @license AngularJS v1.4.7
47482 * (c) 2010-2015 Google, Inc. http://angularjs.org
47483 * License: MIT
47484 */
47485(function(window, angular, undefined) {'use strict';
47486
47487/**
47488 * @ngdoc module
47489 * @name ngRoute
47490 * @description
47491 *
47492 * # ngRoute
47493 *
47494 * The `ngRoute` module provides routing and deeplinking services and directives for angular apps.
47495 *
47496 * ## Example
47497 * See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`.
47498 *
47499 *
47500 * <div doc-module-components="ngRoute"></div>
47501 */
47502 /* global -ngRouteModule */
47503var ngRouteModule = angular.module('ngRoute', ['ng']).
47504                        provider('$route', $RouteProvider),
47505    $routeMinErr = angular.$$minErr('ngRoute');
47506
47507/**
47508 * @ngdoc provider
47509 * @name $routeProvider
47510 *
47511 * @description
47512 *
47513 * Used for configuring routes.
47514 *
47515 * ## Example
47516 * See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`.
47517 *
47518 * ## Dependencies
47519 * Requires the {@link ngRoute `ngRoute`} module to be installed.
47520 */
47521function $RouteProvider() {
47522  function inherit(parent, extra) {
47523    return angular.extend(Object.create(parent), extra);
47524  }
47525
47526  var routes = {};
47527
47528  /**
47529   * @ngdoc method
47530   * @name $routeProvider#when
47531   *
47532   * @param {string} path Route path (matched against `$location.path`). If `$location.path`
47533   *    contains redundant trailing slash or is missing one, the route will still match and the
47534   *    `$location.path` will be updated to add or drop the trailing slash to exactly match the
47535   *    route definition.
47536   *
47537   *    * `path` can contain named groups starting with a colon: e.g. `:name`. All characters up
47538   *        to the next slash are matched and stored in `$routeParams` under the given `name`
47539   *        when the route matches.
47540   *    * `path` can contain named groups starting with a colon and ending with a star:
47541   *        e.g.`:name*`. All characters are eagerly stored in `$routeParams` under the given `name`
47542   *        when the route matches.
47543   *    * `path` can contain optional named groups with a question mark: e.g.`:name?`.
47544   *
47545   *    For example, routes like `/color/:color/largecode/:largecode*\/edit` will match
47546   *    `/color/brown/largecode/code/with/slashes/edit` and extract:
47547   *
47548   *    * `color: brown`
47549   *    * `largecode: code/with/slashes`.
47550   *
47551   *
47552   * @param {Object} route Mapping information to be assigned to `$route.current` on route
47553   *    match.
47554   *
47555   *    Object properties:
47556   *
47557   *    - `controller` – `{(string|function()=}` – Controller fn that should be associated with
47558   *      newly created scope or the name of a {@link angular.Module#controller registered
47559   *      controller} if passed as a string.
47560   *    - `controllerAs` – `{string=}` – An identifier name for a reference to the controller.
47561   *      If present, the controller will be published to scope under the `controllerAs` name.
47562   *    - `template` – `{string=|function()=}` – html template as a string or a function that
47563   *      returns an html template as a string which should be used by {@link
47564   *      ngRoute.directive:ngView ngView} or {@link ng.directive:ngInclude ngInclude} directives.
47565   *      This property takes precedence over `templateUrl`.
47566   *
47567   *      If `template` is a function, it will be called with the following parameters:
47568   *
47569   *      - `{Array.<Object>}` - route parameters extracted from the current
47570   *        `$location.path()` by applying the current route
47571   *
47572   *    - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html
47573   *      template that should be used by {@link ngRoute.directive:ngView ngView}.
47574   *
47575   *      If `templateUrl` is a function, it will be called with the following parameters:
47576   *
47577   *      - `{Array.<Object>}` - route parameters extracted from the current
47578   *        `$location.path()` by applying the current route
47579   *
47580   *    - `resolve` - `{Object.<string, function>=}` - An optional map of dependencies which should
47581   *      be injected into the controller. If any of these dependencies are promises, the router
47582   *      will wait for them all to be resolved or one to be rejected before the controller is
47583   *      instantiated.
47584   *      If all the promises are resolved successfully, the values of the resolved promises are
47585   *      injected and {@link ngRoute.$route#$routeChangeSuccess $routeChangeSuccess} event is
47586   *      fired. If any of the promises are rejected the
47587   *      {@link ngRoute.$route#$routeChangeError $routeChangeError} event is fired. The map object
47588   *      is:
47589   *
47590   *      - `key` – `{string}`: a name of a dependency to be injected into the controller.
47591   *      - `factory` - `{string|function}`: If `string` then it is an alias for a service.
47592   *        Otherwise if function, then it is {@link auto.$injector#invoke injected}
47593   *        and the return value is treated as the dependency. If the result is a promise, it is
47594   *        resolved before its value is injected into the controller. Be aware that
47595   *        `ngRoute.$routeParams` will still refer to the previous route within these resolve
47596   *        functions.  Use `$route.current.params` to access the new route parameters, instead.
47597   *
47598   *    - `redirectTo` – {(string|function())=} – value to update
47599   *      {@link ng.$location $location} path with and trigger route redirection.
47600   *
47601   *      If `redirectTo` is a function, it will be called with the following parameters:
47602   *
47603   *      - `{Object.<string>}` - route parameters extracted from the current
47604   *        `$location.path()` by applying the current route templateUrl.
47605   *      - `{string}` - current `$location.path()`
47606   *      - `{Object}` - current `$location.search()`
47607   *
47608   *      The custom `redirectTo` function is expected to return a string which will be used
47609   *      to update `$location.path()` and `$location.search()`.
47610   *
47611   *    - `[reloadOnSearch=true]` - {boolean=} - reload route when only `$location.search()`
47612   *      or `$location.hash()` changes.
47613   *
47614   *      If the option is set to `false` and url in the browser changes, then
47615   *      `$routeUpdate` event is broadcasted on the root scope.
47616   *
47617   *    - `[caseInsensitiveMatch=false]` - {boolean=} - match routes without being case sensitive
47618   *
47619   *      If the option is set to `true`, then the particular route can be matched without being
47620   *      case sensitive
47621   *
47622   * @returns {Object} self
47623   *
47624   * @description
47625   * Adds a new route definition to the `$route` service.
47626   */
47627  this.when = function(path, route) {
47628    //copy original route object to preserve params inherited from proto chain
47629    var routeCopy = angular.copy(route);
47630    if (angular.isUndefined(routeCopy.reloadOnSearch)) {
47631      routeCopy.reloadOnSearch = true;
47632    }
47633    if (angular.isUndefined(routeCopy.caseInsensitiveMatch)) {
47634      routeCopy.caseInsensitiveMatch = this.caseInsensitiveMatch;
47635    }
47636    routes[path] = angular.extend(
47637      routeCopy,
47638      path && pathRegExp(path, routeCopy)
47639    );
47640
47641    // create redirection for trailing slashes
47642    if (path) {
47643      var redirectPath = (path[path.length - 1] == '/')
47644            ? path.substr(0, path.length - 1)
47645            : path + '/';
47646
47647      routes[redirectPath] = angular.extend(
47648        {redirectTo: path},
47649        pathRegExp(redirectPath, routeCopy)
47650      );
47651    }
47652
47653    return this;
47654  };
47655
47656  /**
47657   * @ngdoc property
47658   * @name $routeProvider#caseInsensitiveMatch
47659   * @description
47660   *
47661   * A boolean property indicating if routes defined
47662   * using this provider should be matched using a case insensitive
47663   * algorithm. Defaults to `false`.
47664   */
47665  this.caseInsensitiveMatch = false;
47666
47667   /**
47668    * @param path {string} path
47669    * @param opts {Object} options
47670    * @return {?Object}
47671    *
47672    * @description
47673    * Normalizes the given path, returning a regular expression
47674    * and the original path.
47675    *
47676    * Inspired by pathRexp in visionmedia/express/lib/utils.js.
47677    */
47678  function pathRegExp(path, opts) {
47679    var insensitive = opts.caseInsensitiveMatch,
47680        ret = {
47681          originalPath: path,
47682          regexp: path
47683        },
47684        keys = ret.keys = [];
47685
47686    path = path
47687      .replace(/([().])/g, '\\$1')
47688      .replace(/(\/)?:(\w+)([\?\*])?/g, function(_, slash, key, option) {
47689        var optional = option === '?' ? option : null;
47690        var star = option === '*' ? option : null;
47691        keys.push({ name: key, optional: !!optional });
47692        slash = slash || '';
47693        return ''
47694          + (optional ? '' : slash)
47695          + '(?:'
47696          + (optional ? slash : '')
47697          + (star && '(.+?)' || '([^/]+)')
47698          + (optional || '')
47699          + ')'
47700          + (optional || '');
47701      })
47702      .replace(/([\/$\*])/g, '\\$1');
47703
47704    ret.regexp = new RegExp('^' + path + '$', insensitive ? 'i' : '');
47705    return ret;
47706  }
47707
47708  /**
47709   * @ngdoc method
47710   * @name $routeProvider#otherwise
47711   *
47712   * @description
47713   * Sets route definition that will be used on route change when no other route definition
47714   * is matched.
47715   *
47716   * @param {Object|string} params Mapping information to be assigned to `$route.current`.
47717   * If called with a string, the value maps to `redirectTo`.
47718   * @returns {Object} self
47719   */
47720  this.otherwise = function(params) {
47721    if (typeof params === 'string') {
47722      params = {redirectTo: params};
47723    }
47724    this.when(null, params);
47725    return this;
47726  };
47727
47728
47729  this.$get = ['$rootScope',
47730               '$location',
47731               '$routeParams',
47732               '$q',
47733               '$injector',
47734               '$templateRequest',
47735               '$sce',
47736      function($rootScope, $location, $routeParams, $q, $injector, $templateRequest, $sce) {
47737
47738    /**
47739     * @ngdoc service
47740     * @name $route
47741     * @requires $location
47742     * @requires $routeParams
47743     *
47744     * @property {Object} current Reference to the current route definition.
47745     * The route definition contains:
47746     *
47747     *   - `controller`: The controller constructor as define in route definition.
47748     *   - `locals`: A map of locals which is used by {@link ng.$controller $controller} service for
47749     *     controller instantiation. The `locals` contain
47750     *     the resolved values of the `resolve` map. Additionally the `locals` also contain:
47751     *
47752     *     - `$scope` - The current route scope.
47753     *     - `$template` - The current route template HTML.
47754     *
47755     * @property {Object} routes Object with all route configuration Objects as its properties.
47756     *
47757     * @description
47758     * `$route` is used for deep-linking URLs to controllers and views (HTML partials).
47759     * It watches `$location.url()` and tries to map the path to an existing route definition.
47760     *
47761     * Requires the {@link ngRoute `ngRoute`} module to be installed.
47762     *
47763     * You can define routes through {@link ngRoute.$routeProvider $routeProvider}'s API.
47764     *
47765     * The `$route` service is typically used in conjunction with the
47766     * {@link ngRoute.directive:ngView `ngView`} directive and the
47767     * {@link ngRoute.$routeParams `$routeParams`} service.
47768     *
47769     * @example
47770     * This example shows how changing the URL hash causes the `$route` to match a route against the
47771     * URL, and the `ngView` pulls in the partial.
47772     *
47773     * <example name="$route-service" module="ngRouteExample"
47774     *          deps="angular-route.js" fixBase="true">
47775     *   <file name="index.html">
47776     *     <div ng-controller="MainController">
47777     *       Choose:
47778     *       <a href="Book/Moby">Moby</a> |
47779     *       <a href="Book/Moby/ch/1">Moby: Ch1</a> |
47780     *       <a href="Book/Gatsby">Gatsby</a> |
47781     *       <a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
47782     *       <a href="Book/Scarlet">Scarlet Letter</a><br/>
47783     *
47784     *       <div ng-view></div>
47785     *
47786     *       <hr />
47787     *
47788     *       <pre>$location.path() = {{$location.path()}}</pre>
47789     *       <pre>$route.current.templateUrl = {{$route.current.templateUrl}}</pre>
47790     *       <pre>$route.current.params = {{$route.current.params}}</pre>
47791     *       <pre>$route.current.scope.name = {{$route.current.scope.name}}</pre>
47792     *       <pre>$routeParams = {{$routeParams}}</pre>
47793     *     </div>
47794     *   </file>
47795     *
47796     *   <file name="book.html">
47797     *     controller: {{name}}<br />
47798     *     Book Id: {{params.bookId}}<br />
47799     *   </file>
47800     *
47801     *   <file name="chapter.html">
47802     *     controller: {{name}}<br />
47803     *     Book Id: {{params.bookId}}<br />
47804     *     Chapter Id: {{params.chapterId}}
47805     *   </file>
47806     *
47807     *   <file name="script.js">
47808     *     angular.module('ngRouteExample', ['ngRoute'])
47809     *
47810     *      .controller('MainController', function($scope, $route, $routeParams, $location) {
47811     *          $scope.$route = $route;
47812     *          $scope.$location = $location;
47813     *          $scope.$routeParams = $routeParams;
47814     *      })
47815     *
47816     *      .controller('BookController', function($scope, $routeParams) {
47817     *          $scope.name = "BookController";
47818     *          $scope.params = $routeParams;
47819     *      })
47820     *
47821     *      .controller('ChapterController', function($scope, $routeParams) {
47822     *          $scope.name = "ChapterController";
47823     *          $scope.params = $routeParams;
47824     *      })
47825     *
47826     *     .config(function($routeProvider, $locationProvider) {
47827     *       $routeProvider
47828     *        .when('/Book/:bookId', {
47829     *         templateUrl: 'book.html',
47830     *         controller: 'BookController',
47831     *         resolve: {
47832     *           // I will cause a 1 second delay
47833     *           delay: function($q, $timeout) {
47834     *             var delay = $q.defer();
47835     *             $timeout(delay.resolve, 1000);
47836     *             return delay.promise;
47837     *           }
47838     *         }
47839     *       })
47840     *       .when('/Book/:bookId/ch/:chapterId', {
47841     *         templateUrl: 'chapter.html',
47842     *         controller: 'ChapterController'
47843     *       });
47844     *
47845     *       // configure html5 to get links working on jsfiddle
47846     *       $locationProvider.html5Mode(true);
47847     *     });
47848     *
47849     *   </file>
47850     *
47851     *   <file name="protractor.js" type="protractor">
47852     *     it('should load and compile correct template', function() {
47853     *       element(by.linkText('Moby: Ch1')).click();
47854     *       var content = element(by.css('[ng-view]')).getText();
47855     *       expect(content).toMatch(/controller\: ChapterController/);
47856     *       expect(content).toMatch(/Book Id\: Moby/);
47857     *       expect(content).toMatch(/Chapter Id\: 1/);
47858     *
47859     *       element(by.partialLinkText('Scarlet')).click();
47860     *
47861     *       content = element(by.css('[ng-view]')).getText();
47862     *       expect(content).toMatch(/controller\: BookController/);
47863     *       expect(content).toMatch(/Book Id\: Scarlet/);
47864     *     });
47865     *   </file>
47866     * </example>
47867     */
47868
47869    /**
47870     * @ngdoc event
47871     * @name $route#$routeChangeStart
47872     * @eventType broadcast on root scope
47873     * @description
47874     * Broadcasted before a route change. At this  point the route services starts
47875     * resolving all of the dependencies needed for the route change to occur.
47876     * Typically this involves fetching the view template as well as any dependencies
47877     * defined in `resolve` route property. Once  all of the dependencies are resolved
47878     * `$routeChangeSuccess` is fired.
47879     *
47880     * The route change (and the `$location` change that triggered it) can be prevented
47881     * by calling `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on}
47882     * for more details about event object.
47883     *
47884     * @param {Object} angularEvent Synthetic event object.
47885     * @param {Route} next Future route information.
47886     * @param {Route} current Current route information.
47887     */
47888
47889    /**
47890     * @ngdoc event
47891     * @name $route#$routeChangeSuccess
47892     * @eventType broadcast on root scope
47893     * @description
47894     * Broadcasted after a route change has happened successfully.
47895     * The `resolve` dependencies are now available in the `current.locals` property.
47896     *
47897     * {@link ngRoute.directive:ngView ngView} listens for the directive
47898     * to instantiate the controller and render the view.
47899     *
47900     * @param {Object} angularEvent Synthetic event object.
47901     * @param {Route} current Current route information.
47902     * @param {Route|Undefined} previous Previous route information, or undefined if current is
47903     * first route entered.
47904     */
47905
47906    /**
47907     * @ngdoc event
47908     * @name $route#$routeChangeError
47909     * @eventType broadcast on root scope
47910     * @description
47911     * Broadcasted if any of the resolve promises are rejected.
47912     *
47913     * @param {Object} angularEvent Synthetic event object
47914     * @param {Route} current Current route information.
47915     * @param {Route} previous Previous route information.
47916     * @param {Route} rejection Rejection of the promise. Usually the error of the failed promise.
47917     */
47918
47919    /**
47920     * @ngdoc event
47921     * @name $route#$routeUpdate
47922     * @eventType broadcast on root scope
47923     * @description
47924     * The `reloadOnSearch` property has been set to false, and we are reusing the same
47925     * instance of the Controller.
47926     *
47927     * @param {Object} angularEvent Synthetic event object
47928     * @param {Route} current Current/previous route information.
47929     */
47930
47931    var forceReload = false,
47932        preparedRoute,
47933        preparedRouteIsUpdateOnly,
47934        $route = {
47935          routes: routes,
47936
47937          /**
47938           * @ngdoc method
47939           * @name $route#reload
47940           *
47941           * @description
47942           * Causes `$route` service to reload the current route even if
47943           * {@link ng.$location $location} hasn't changed.
47944           *
47945           * As a result of that, {@link ngRoute.directive:ngView ngView}
47946           * creates new scope and reinstantiates the controller.
47947           */
47948          reload: function() {
47949            forceReload = true;
47950            $rootScope.$evalAsync(function() {
47951              // Don't support cancellation of a reload for now...
47952              prepareRoute();
47953              commitRoute();
47954            });
47955          },
47956
47957          /**
47958           * @ngdoc method
47959           * @name $route#updateParams
47960           *
47961           * @description
47962           * Causes `$route` service to update the current URL, replacing
47963           * current route parameters with those specified in `newParams`.
47964           * Provided property names that match the route's path segment
47965           * definitions will be interpolated into the location's path, while
47966           * remaining properties will be treated as query params.
47967           *
47968           * @param {!Object<string, string>} newParams mapping of URL parameter names to values
47969           */
47970          updateParams: function(newParams) {
47971            if (this.current && this.current.$$route) {
47972              newParams = angular.extend({}, this.current.params, newParams);
47973              $location.path(interpolate(this.current.$$route.originalPath, newParams));
47974              // interpolate modifies newParams, only query params are left
47975              $location.search(newParams);
47976            } else {
47977              throw $routeMinErr('norout', 'Tried updating route when with no current route');
47978            }
47979          }
47980        };
47981
47982    $rootScope.$on('$locationChangeStart', prepareRoute);
47983    $rootScope.$on('$locationChangeSuccess', commitRoute);
47984
47985    return $route;
47986
47987    /////////////////////////////////////////////////////
47988
47989    /**
47990     * @param on {string} current url
47991     * @param route {Object} route regexp to match the url against
47992     * @return {?Object}
47993     *
47994     * @description
47995     * Check if the route matches the current url.
47996     *
47997     * Inspired by match in
47998     * visionmedia/express/lib/router/router.js.
47999     */
48000    function switchRouteMatcher(on, route) {
48001      var keys = route.keys,
48002          params = {};
48003
48004      if (!route.regexp) return null;
48005
48006      var m = route.regexp.exec(on);
48007      if (!m) return null;
48008
48009      for (var i = 1, len = m.length; i < len; ++i) {
48010        var key = keys[i - 1];
48011
48012        var val = m[i];
48013
48014        if (key && val) {
48015          params[key.name] = val;
48016        }
48017      }
48018      return params;
48019    }
48020
48021    function prepareRoute($locationEvent) {
48022      var lastRoute = $route.current;
48023
48024      preparedRoute = parseRoute();
48025      preparedRouteIsUpdateOnly = preparedRoute && lastRoute && preparedRoute.$$route === lastRoute.$$route
48026          && angular.equals(preparedRoute.pathParams, lastRoute.pathParams)
48027          && !preparedRoute.reloadOnSearch && !forceReload;
48028
48029      if (!preparedRouteIsUpdateOnly && (lastRoute || preparedRoute)) {
48030        if ($rootScope.$broadcast('$routeChangeStart', preparedRoute, lastRoute).defaultPrevented) {
48031          if ($locationEvent) {
48032            $locationEvent.preventDefault();
48033          }
48034        }
48035      }
48036    }
48037
48038    function commitRoute() {
48039      var lastRoute = $route.current;
48040      var nextRoute = preparedRoute;
48041
48042      if (preparedRouteIsUpdateOnly) {
48043        lastRoute.params = nextRoute.params;
48044        angular.copy(lastRoute.params, $routeParams);
48045        $rootScope.$broadcast('$routeUpdate', lastRoute);
48046      } else if (nextRoute || lastRoute) {
48047        forceReload = false;
48048        $route.current = nextRoute;
48049        if (nextRoute) {
48050          if (nextRoute.redirectTo) {
48051            if (angular.isString(nextRoute.redirectTo)) {
48052              $location.path(interpolate(nextRoute.redirectTo, nextRoute.params)).search(nextRoute.params)
48053                       .replace();
48054            } else {
48055              $location.url(nextRoute.redirectTo(nextRoute.pathParams, $location.path(), $location.search()))
48056                       .replace();
48057            }
48058          }
48059        }
48060
48061        $q.when(nextRoute).
48062          then(function() {
48063            if (nextRoute) {
48064              var locals = angular.extend({}, nextRoute.resolve),
48065                  template, templateUrl;
48066
48067              angular.forEach(locals, function(value, key) {
48068                locals[key] = angular.isString(value) ?
48069                    $injector.get(value) : $injector.invoke(value, null, null, key);
48070              });
48071
48072              if (angular.isDefined(template = nextRoute.template)) {
48073                if (angular.isFunction(template)) {
48074                  template = template(nextRoute.params);
48075                }
48076              } else if (angular.isDefined(templateUrl = nextRoute.templateUrl)) {
48077                if (angular.isFunction(templateUrl)) {
48078                  templateUrl = templateUrl(nextRoute.params);
48079                }
48080                if (angular.isDefined(templateUrl)) {
48081                  nextRoute.loadedTemplateUrl = $sce.valueOf(templateUrl);
48082                  template = $templateRequest(templateUrl);
48083                }
48084              }
48085              if (angular.isDefined(template)) {
48086                locals['$template'] = template;
48087              }
48088              return $q.all(locals);
48089            }
48090          }).
48091          then(function(locals) {
48092            // after route change
48093            if (nextRoute == $route.current) {
48094              if (nextRoute) {
48095                nextRoute.locals = locals;
vendor: 4,780 bytes, lines 48096-48230
48096                angular.copy(nextRoute.params, $routeParams);
48097              }
48098              $rootScope.$broadcast('$routeChangeSuccess', nextRoute, lastRoute);
48099            }
48100          }, function(error) {
48101            if (nextRoute == $route.current) {
48102              $rootScope.$broadcast('$routeChangeError', nextRoute, lastRoute, error);
48103            }
48104          });
48105      }
48106    }
48107
48108
48109    /**
48110     * @returns {Object} the current active route, by matching it against the URL
48111     */
48112    function parseRoute() {
48113      // Match a route
48114      var params, match;
48115      angular.forEach(routes, function(route, path) {
48116        if (!match && (params = switchRouteMatcher($location.path(), route))) {
48117          match = inherit(route, {
48118            params: angular.extend({}, $location.search(), params),
48119            pathParams: params});
48120          match.$$route = route;
48121        }
48122      });
48123      // No route matched; fallback to "otherwise" route
48124      return match || routes[null] && inherit(routes[null], {params: {}, pathParams:{}});
48125    }
48126
48127    /**
48128     * @returns {string} interpolation of the redirect path with the parameters
48129     */
48130    function interpolate(string, params) {
48131      var result = [];
48132      angular.forEach((string || '').split(':'), function(segment, i) {
48133        if (i === 0) {
48134          result.push(segment);
48135        } else {
48136          var segmentMatch = segment.match(/(\w+)(?:[?*])?(.*)/);
48137          var key = segmentMatch[1];
48138          result.push(params[key]);
48139          result.push(segmentMatch[2] || '');
48140          delete params[key];
48141        }
48142      });
48143      return result.join('');
48144    }
48145  }];
48146}
48147
48148ngRouteModule.provider('$routeParams', $RouteParamsProvider);
48149
48150
48151/**
48152 * @ngdoc service
48153 * @name $routeParams
48154 * @requires $route
48155 *
48156 * @description
48157 * The `$routeParams` service allows you to retrieve the current set of route parameters.
48158 *
48159 * Requires the {@link ngRoute `ngRoute`} module to be installed.
48160 *
48161 * The route parameters are a combination of {@link ng.$location `$location`}'s
48162 * {@link ng.$location#search `search()`} and {@link ng.$location#path `path()`}.
48163 * The `path` parameters are extracted when the {@link ngRoute.$route `$route`} path is matched.
48164 *
48165 * In case of parameter name collision, `path` params take precedence over `search` params.
48166 *
48167 * The service guarantees that the identity of the `$routeParams` object will remain unchanged
48168 * (but its properties will likely change) even when a route change occurs.
48169 *
48170 * Note that the `$routeParams` are only updated *after* a route change completes successfully.
48171 * This means that you cannot rely on `$routeParams` being correct in route resolve functions.
48172 * Instead you can use `$route.current.params` to access the new route's parameters.
48173 *
48174 * @example
48175 * ```js
48176 *  // Given:
48177 *  // URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby
48178 *  // Route: /Chapter/:chapterId/Section/:sectionId
48179 *  //
48180 *  // Then
48181 *  $routeParams ==> {chapterId:'1', sectionId:'2', search:'moby'}
48182 * ```
48183 */
48184function $RouteParamsProvider() {
48185  this.$get = function() { return {}; };
48186}
48187
48188ngRouteModule.directive('ngView', ngViewFactory);
48189ngRouteModule.directive('ngView', ngViewFillContentFactory);
48190
48191
48192/**
48193 * @ngdoc directive
48194 * @name ngView
48195 * @restrict ECA
48196 *
48197 * @description
48198 * # Overview
48199 * `ngView` is a directive that complements the {@link ngRoute.$route $route} service by
48200 * including the rendered template of the current route into the main layout (`index.html`) file.
48201 * Every time the current route changes, the included view changes with it according to the
48202 * configuration of the `$route` service.
48203 *
48204 * Requires the {@link ngRoute `ngRoute`} module to be installed.
48205 *
48206 * @animations
48207 * enter - animation is used to bring new content into the browser.
48208 * leave - animation is used to animate existing content away.
48209 *
48210 * The enter and leave animation occur concurrently.
48211 *
48212 * @scope
48213 * @priority 400
48214 * @param {string=} onload Expression to evaluate whenever the view updates.
48215 *
48216 * @param {string=} autoscroll Whether `ngView` should call {@link ng.$anchorScroll
48217 *                  $anchorScroll} to scroll the viewport after the view is updated.
48218 *
48219 *                  - If the attribute is not set, disable scrolling.
48220 *                  - If the attribute is set without value, enable scrolling.
48221 *                  - Otherwise enable scrolling only if the `autoscroll` attribute value evaluated
48222 *                    as an expression yields a truthy value.
48223 * @example
48224    <example name="ngView-directive" module="ngViewExample"
48225             deps="angular-route.js;angular-animate.js"
48226             animations="true" fixBase="true">
48227      <file name="index.html">
48228        <div ng-controller="MainCtrl as main">
48229          Choose:
48230          <a href="Book/Moby">Moby</a> |
48231          <a href="Book/Moby/ch/1">Moby: Ch1</a> |
48232          <a href="Book/Gatsby">Gatsby</a> |
48233          <a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
48234          <a href="Book/Scarlet">Scarlet Letter</a><br/>
48235
48236          <div class="view-animate-container">
48237            <div ng-view class="view-animate"></div>
48238          </div>
48239          <hr />
48240
48241          <pre>$location.path() = {{main.$location.path()}}</pre>
48242          <pre>$route.current.templateUrl = {{main.$route.current.templateUrl}}</pre>
48243          <pre>$route.current.params = {{main.$route.current.params}}</pre>
48244          <pre>$routeParams = {{main.$routeParams}}</pre>
48245        </div>
48246      </file>
48247
48248      <file name="book.html">
48249        <div>
48250          controller: {{book.name}}<br />
48251          Book Id: {{book.params.bookId}}<br />
48252        </div>
48253      </file>
48254
48255      <file name="chapter.html">
48256        <div>
48257          controller: {{chapter.name}}<br />
48258          Book Id: {{chapter.params.bookId}}<br />
48259          Chapter Id: {{chapter.params.chapterId}}
48260        </div>
48261      </file>
48262
48263      <file name="animations.css">
48264        .view-animate-container {
48265          position:relative;
48266          height:100px!important;
48267          background:white;
48268          border:1px solid black;
48269          height:40px;
48270          overflow:hidden;
48271        }
48272
48273        .view-animate {
48274          padding:10px;
48275        }
48276
48277        .view-animate.ng-enter, .view-animate.ng-leave {
48278          transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
48279
48280          display:block;
48281          width:100%;
48282          border-left:1px solid black;
48283
48284          position:absolute;
48285          top:0;
48286          left:0;
48287          right:0;
48288          bottom:0;
48289          padding:10px;
48290        }
48291
48292        .view-animate.ng-enter {
48293          left:100%;
48294        }
48295        .view-animate.ng-enter.ng-enter-active {
48296          left:0;
48297        }
48298        .view-animate.ng-leave.ng-leave-active {
48299          left:-100%;
48300        }
48301      </file>
48302
48303      <file name="script.js">
48304        angular.module('ngViewExample', ['ngRoute', 'ngAnimate'])
48305          .config(['$routeProvider', '$locationProvider',
48306            function($routeProvider, $locationProvider) {
48307              $routeProvider
48308                .when('/Book/:bookId', {
48309                  templateUrl: 'book.html',
48310                  controller: 'BookCtrl',
48311                  controllerAs: 'book'
48312                })
48313                .when('/Book/:bookId/ch/:chapterId', {
48314                  templateUrl: 'chapter.html',
48315                  controller: 'ChapterCtrl',
48316                  controllerAs: 'chapter'
48317                });
48318
48319              $locationProvider.html5Mode(true);
48320          }])
48321          .controller('MainCtrl', ['$route', '$routeParams', '$location',
48322            function($route, $routeParams, $location) {
48323              this.$route = $route;
48324              this.$location = $location;
48325              this.$routeParams = $routeParams;
48326          }])
48327          .controller('BookCtrl', ['$routeParams', function($routeParams) {
48328            this.name = "BookCtrl";
48329            this.params = $routeParams;
48330          }])
48331          .controller('ChapterCtrl', ['$routeParams', function($routeParams) {
48332            this.name = "ChapterCtrl";
48333            this.params = $routeParams;
48334          }]);
48335
48336      </file>
48337
48338      <file name="protractor.js" type="protractor">
48339        it('should load and compile correct template', function() {
48340          element(by.linkText('Moby: Ch1')).click();
48341          var content = element(by.css('[ng-view]')).getText();
48342          expect(content).toMatch(/controller\: ChapterCtrl/);
48343          expect(content).toMatch(/Book Id\: Moby/);
48344          expect(content).toMatch(/Chapter Id\: 1/);
48345
48346          element(by.partialLinkText('Scarlet')).click();
48347
48348          content = element(by.css('[ng-view]')).getText();
48349          expect(content).toMatch(/controller\: BookCtrl/);
48350          expect(content).toMatch(/Book Id\: Scarlet/);
48351        });
48352      </file>
48353    </example>
48354 */
48355
48356
48357/**
48358 * @ngdoc event
48359 * @name ngView#$viewContentLoaded
48360 * @eventType emit on the current ngView scope
48361 * @description
48362 * Emitted every time the ngView content is reloaded.
48363 */
48364ngViewFactory.$inject = ['$route', '$anchorScroll', '$animate'];
48365function ngViewFactory($route, $anchorScroll, $animate) {
48366  return {
48367    restrict: 'ECA',
48368    terminal: true,
48369    priority: 400,
48370    transclude: 'element',
48371    link: function(scope, $element, attr, ctrl, $transclude) {
48372        var currentScope,
48373            currentElement,
48374            previousLeaveAnimation,
48375            autoScrollExp = attr.autoscroll,
48376            onloadExp = attr.onload || '';
48377
48378        scope.$on('$routeChangeSuccess', update);
48379        update();
48380
48381        function cleanupLastView() {
48382          if (previousLeaveAnimation) {
48383            $animate.cancel(previousLeaveAnimation);
48384            previousLeaveAnimation = null;
48385          }
48386
48387          if (currentScope) {
48388            currentScope.$destroy();
48389            currentScope = null;
48390          }
48391          if (currentElement) {
48392            previousLeaveAnimation = $animate.leave(currentElement);
48393            previousLeaveAnimation.then(function() {
48394              previousLeaveAnimation = null;
48395            });
48396            currentElement = null;
48397          }
48398        }
48399
48400        function update() {
48401          var locals = $route.current && $route.current.locals,
48402              template = locals && locals.$template;
48403
48404          if (angular.isDefined(template)) {
48405            var newScope = scope.$new();
48406            var current = $route.current;
48407
48408            // Note: This will also link all children of ng-view that were contained in the original
48409            // html. If that content contains controllers, ... they could pollute/change the scope.
48410            // However, using ng-view on an element with additional content does not make sense...
48411            // Note: We can't remove them in the cloneAttchFn of $transclude as that
48412            // function is called before linking the content, which would apply child
48413            // directives to non existing elements.
48414            var clone = $transclude(newScope, function(clone) {
48415              $animate.enter(clone, null, currentElement || $element).then(function onNgViewEnter() {
48416                if (angular.isDefined(autoScrollExp)
48417                  && (!autoScrollExp || scope.$eval(autoScrollExp))) {
48418                  $anchorScroll();
48419                }
48420              });
48421              cleanupLastView();
48422            });
48423
48424            currentElement = clone;
48425            currentScope = current.scope = newScope;
48426            currentScope.$emit('$viewContentLoaded');
48427            currentScope.$eval(onloadExp);
48428          } else {
48429            cleanupLastView();
48430          }
48431        }
48432    }
48433  };
48434}
48435
48436// This directive is called during the $transclude call of the first `ngView` directive.
48437// It will replace and compile the content of the element with the loaded template.
48438// We need this directive so that the element content is already filled when
48439// the link function of another directive on the same element as ngView
48440// is called.
48441ngViewFillContentFactory.$inject = ['$compile', '$controller', '$route'];
48442function ngViewFillContentFactory($compile, $controller, $route) {
48443  return {
48444    restrict: 'ECA',
48445    priority: -400,
48446    link: function(scope, $element) {
48447      var current = $route.current,
48448          locals = current.locals;
48449
48450      $element.html(locals.$template);
48451
48452      var link = $compile($element.contents());
48453
48454      if (current.controller) {
48455        locals.$scope = scope;
48456        var controller = $controller(current.controller, locals);
48457        if (current.controllerAs) {
48458          scope[current.controllerAs] = controller;
48459        }
48460        $element.data('$ngControllerController', controller);
48461        $element.children().data('$ngControllerController', controller);
48462      }
48463
48464      link(scope);
48465    }
48466  };
48467}
48468
48469
48470})(window, window.angular);
48471
48472/*!
48473 * iCheck v1.0.2, http://git.io/arlzeA
48474 * ===================================
48475 * Powerful jQuery and Zepto plugin for checkboxes and radio buttons customization
48476 *
48477 * (c) 2013 Damir Sultanov, http://fronteed.com
48478 * MIT Licensed
48479 */
48480
48481(function($) {
48482
48483  // Cached vars
48484  var _iCheck = 'iCheck',
48485    _iCheckHelper = _iCheck + '-helper',
48486    _checkbox = 'checkbox',
48487    _radio = 'radio',
48488    _checked = 'checked',
48489    _unchecked = 'un' + _checked,
48490    _disabled = 'disabled',
48491    _determinate = 'determinate',
48492    _indeterminate = 'in' + _determinate,
48493    _update = 'update',
48494    _type = 'type',
48495    _click = 'click',
48496    _touch = 'touchbegin.i touchend.i',
48497    _add = 'addClass',
48498    _remove = 'removeClass',
48499    _callback = 'trigger',
48500    _label = 'label',
48501    _cursor = 'cursor',
48502    _mobile = /ipad|iphone|ipod|android|blackberry|windows phone|opera mini|silk/i.test(navigator.userAgent);
48503
48504  // Plugin init
48505  $.fn[_iCheck] = function(options, fire) {
48506
48507    // Walker
48508    var handle = 'input[type="' + _checkbox + '"], input[type="' + _radio + '"]',
48509      stack = $(),
48510      walker = function(object) {
48511        object.each(function() {
48512          var self = $(this);
48513
48514          if (self.is(handle)) {
48515            stack = stack.add(self);
48516          } else {
48517            stack = stack.add(self.find(handle));
48518          }
48519        });
48520      };
48521
48522    // Check if we should operate with some method
48523    if (/^(check|uncheck|toggle|indeterminate|determinate|disable|enable|update|destroy)$/i.test(options)) {
48524
48525      // Normalize method's name
48526      options = options.toLowerCase();
48527
48528      // Find checkboxes and radio buttons
48529      walker(this);
48530
48531      return stack.each(function() {
48532        var self = $(this);
48533
48534        if (options == 'destroy') {
48535          tidy(self, 'ifDestroyed');
48536        } else {
48537          operate(self, true, options);
48538        }
48539
48540        // Fire method's callback
48541        if ($.isFunction(fire)) {
48542          fire();
48543        }
48544      });
48545
48546    // Customization
48547    } else if (typeof options == 'object' || !options) {
48548
48549      // Check if any options were passed
48550      var settings = $.extend({
48551          checkedClass: _checked,
48552          disabledClass: _disabled,
48553          indeterminateClass: _indeterminate,
48554          labelHover: true
48555        }, options),
48556
48557        selector = settings.handle,
48558        hoverClass = settings.hoverClass || 'hover',
vendor: 10,149 bytes, lines 48559-48916
48559        focusClass = settings.focusClass || 'focus',
48560        activeClass = settings.activeClass || 'active',
48561        labelHover = !!settings.labelHover,
48562        labelHoverClass = settings.labelHoverClass || 'hover',
48563
48564        // Setup clickable area
48565        area = ('' + settings.increaseArea).replace('%', '') | 0;
48566
48567      // Selector limit
48568      if (selector == _checkbox || selector == _radio) {
48569        handle = 'input[type="' + selector + '"]';
48570      }
48571
48572      // Clickable area limit
48573      if (area < -50) {
48574        area = -50;
48575      }
48576
48577      // Walk around the selector
48578      walker(this);
48579
48580      return stack.each(function() {
48581        var self = $(this);
48582
48583        // If already customized
48584        tidy(self);
48585
48586        var node = this,
48587          id = node.id,
48588
48589          // Layer styles
48590          offset = -area + '%',
48591          size = 100 + (area * 2) + '%',
48592          layer = {
48593            position: 'absolute',
48594            top: offset,
48595            left: offset,
48596            display: 'block',
48597            width: size,
48598            height: size,
48599            margin: 0,
48600            padding: 0,
48601            background: '#fff',
48602            border: 0,
48603            opacity: 0
48604          },
48605
48606          // Choose how to hide input
48607          hide = _mobile ? {
48608            position: 'absolute',
48609            visibility: 'hidden'
48610          } : area ? layer : {
48611            position: 'absolute',
48612            opacity: 0
48613          },
48614
48615          // Get proper class
48616          className = node[_type] == _checkbox ? settings.checkboxClass || 'i' + _checkbox : settings.radioClass || 'i' + _radio,
48617
48618          // Find assigned labels
48619          label = $(_label + '[for="' + id + '"]').add(self.closest(_label)),
48620
48621          // Check ARIA option
48622          aria = !!settings.aria,
48623
48624          // Set ARIA placeholder
48625          ariaID = _iCheck + '-' + Math.random().toString(36).substr(2,6),
48626
48627          // Parent & helper
48628          parent = '<div class="' + className + '" ' + (aria ? 'role="' + node[_type] + '" ' : ''),
48629          helper;
48630
48631        // Set ARIA "labelledby"
48632        if (aria) {
48633          label.each(function() {
48634            parent += 'aria-labelledby="';
48635
48636            if (this.id) {
48637              parent += this.id;
48638            } else {
48639              this.id = ariaID;
48640              parent += ariaID;
48641            }
48642
48643            parent += '"';
48644          });
48645        }
48646
48647        // Wrap input
48648        parent = self.wrap(parent + '/>')[_callback]('ifCreated').parent().append(settings.insert);
48649
48650        // Layer addition
48651        helper = $('<ins class="' + _iCheckHelper + '"/>').css(layer).appendTo(parent);
48652
48653        // Finalize customization
48654        self.data(_iCheck, {o: settings, s: self.attr('style')}).css(hide);
48655        !!settings.inheritClass && parent[_add](node.className || '');
48656        !!settings.inheritID && id && parent.attr('id', _iCheck + '-' + id);
48657        parent.css('position') == 'static' && parent.css('position', 'relative');
48658        operate(self, true, _update);
48659
48660        // Label events
48661        if (label.length) {
48662          label.on(_click + '.i mouseover.i mouseout.i ' + _touch, function(event) {
48663            var type = event[_type],
48664              item = $(this);
48665
48666            // Do nothing if input is disabled
48667            if (!node[_disabled]) {
48668
48669              // Click
48670              if (type == _click) {
48671                if ($(event.target).is('a')) {
48672                  return;
48673                }
48674                operate(self, false, true);
48675
48676              // Hover state
48677              } else if (labelHover) {
48678
48679                // mouseout|touchend
48680                if (/ut|nd/.test(type)) {
48681                  parent[_remove](hoverClass);
48682                  item[_remove](labelHoverClass);
48683                } else {
48684                  parent[_add](hoverClass);
48685                  item[_add](labelHoverClass);
48686                }
48687              }
48688
48689              if (_mobile) {
48690                event.stopPropagation();
48691              } else {
48692                return false;
48693              }
48694            }
48695          });
48696        }
48697
48698        // Input events
48699        self.on(_click + '.i focus.i blur.i keyup.i keydown.i keypress.i', function(event) {
48700          var type = event[_type],
48701            key = event.keyCode;
48702
48703          // Click
48704          if (type == _click) {
48705            return false;
48706
48707          // Keydown
48708          } else if (type == 'keydown' && key == 32) {
48709            if (!(node[_type] == _radio && node[_checked])) {
48710              if (node[_checked]) {
48711                off(self, _checked);
48712              } else {
48713                on(self, _checked);
48714              }
48715            }
48716
48717            return false;
48718
48719          // Keyup
48720          } else if (type == 'keyup' && node[_type] == _radio) {
48721            !node[_checked] && on(self, _checked);
48722
48723          // Focus/blur
48724          } else if (/us|ur/.test(type)) {
48725            parent[type == 'blur' ? _remove : _add](focusClass);
48726          }
48727        });
48728
48729        // Helper events
48730        helper.on(_click + ' mousedown mouseup mouseover mouseout ' + _touch, function(event) {
48731          var type = event[_type],
48732
48733            // mousedown|mouseup
48734            toggle = /wn|up/.test(type) ? activeClass : hoverClass;
48735
48736          // Do nothing if input is disabled
48737          if (!node[_disabled]) {
48738
48739            // Click
48740            if (type == _click) {
48741              operate(self, false, true);
48742
48743            // Active and hover states
48744            } else {
48745
48746              // State is on
48747              if (/wn|er|in/.test(type)) {
48748
48749                // mousedown|mouseover|touchbegin
48750                parent[_add](toggle);
48751
48752              // State is off
48753              } else {
48754                parent[_remove](toggle + ' ' + activeClass);
48755              }
48756
48757              // Label hover
48758              if (label.length && labelHover && toggle == hoverClass) {
48759
48760                // mouseout|touchend
48761                label[/ut|nd/.test(type) ? _remove : _add](labelHoverClass);
48762              }
48763            }
48764
48765            if (_mobile) {
48766              event.stopPropagation();
48767            } else {
48768              return false;
48769            }
48770          }
48771        });
48772      });
48773    } else {
48774      return this;
48775    }
48776  };
48777
48778  // Do something with inputs
48779  function operate(input, direct, method) {
48780    var node = input[0],
48781      state = /er/.test(method) ? _indeterminate : /bl/.test(method) ? _disabled : _checked,
48782      active = method == _update ? {
48783        checked: node[_checked],
48784        disabled: node[_disabled],
48785        indeterminate: input.attr(_indeterminate) == 'true' || input.attr(_determinate) == 'false'
48786      } : node[state];
48787
48788    // Check, disable or indeterminate
48789    if (/^(ch|di|in)/.test(method) && !active) {
48790      on(input, state);
48791
48792    // Uncheck, enable or determinate
48793    } else if (/^(un|en|de)/.test(method) && active) {
48794      off(input, state);
48795
48796    // Update
48797    } else if (method == _update) {
48798
48799      // Handle states
48800      for (var each in active) {
48801        if (active[each]) {
48802          on(input, each, true);
48803        } else {
48804          off(input, each, true);
48805        }
48806      }
48807
48808    } else if (!direct || method == 'toggle') {
48809
48810      // Helper or label was clicked
48811      if (!direct) {
48812        input[_callback]('ifClicked');
48813      }
48814
48815      // Toggle checked state
48816      if (active) {
48817        if (node[_type] !== _radio) {
48818          off(input, state);
48819        }
48820      } else {
48821        on(input, state);
48822      }
48823    }
48824  }
48825
48826  // Add checked, disabled or indeterminate state
48827  function on(input, state, keep) {
48828    var node = input[0],
48829      parent = input.parent(),
48830      checked = state == _checked,
48831      indeterminate = state == _indeterminate,
48832      disabled = state == _disabled,
48833      callback = indeterminate ? _determinate : checked ? _unchecked : 'enabled',
48834      regular = option(input, callback + capitalize(node[_type])),
48835      specific = option(input, state + capitalize(node[_type]));
48836
48837    // Prevent unnecessary actions
48838    if (node[state] !== true) {
48839
48840      // Toggle assigned radio buttons
48841      if (!keep && state == _checked && node[_type] == _radio && node.name) {
48842        var form = input.closest('form'),
48843          inputs = 'input[name="' + node.name + '"]';
48844
48845        inputs = form.length ? form.find(inputs) : $(inputs);
48846
48847        inputs.each(function() {
48848          if (this !== node && $(this).data(_iCheck)) {
48849            off($(this), state);
48850          }
48851        });
48852      }
48853
48854      // Indeterminate state
48855      if (indeterminate) {
48856
48857        // Add indeterminate state
48858        node[state] = true;
48859
48860        // Remove checked state
48861        if (node[_checked]) {
48862          off(input, _checked, 'force');
48863        }
48864
48865      // Checked or disabled state
48866      } else {
48867
48868        // Add checked or disabled state
48869        if (!keep) {
48870          node[state] = true;
48871        }
48872
48873        // Remove indeterminate state
48874        if (checked && node[_indeterminate]) {
48875          off(input, _indeterminate, false);
48876        }
48877      }
48878
48879      // Trigger callbacks
48880      callbacks(input, checked, state, keep);
48881    }
48882
48883    // Add proper cursor
48884    if (node[_disabled] && !!option(input, _cursor, true)) {
48885      parent.find('.' + _iCheckHelper).css(_cursor, 'default');
48886    }
48887
48888    // Add state class
48889    parent[_add](specific || option(input, state) || '');
48890
48891    // Set ARIA attribute
48892    if (!!parent.attr('role') && !indeterminate) {
48893      parent.attr('aria-' + (disabled ? _disabled : _checked), 'true');
48894    }
48895
48896    // Remove regular state class
48897    parent[_remove](regular || option(input, callback) || '');
48898  }
48899
48900  // Remove checked, disabled or indeterminate state
48901  function off(input, state, keep) {
48902    var node = input[0],
48903      parent = input.parent(),
48904      checked = state == _checked,
48905      indeterminate = state == _indeterminate,
48906      disabled = state == _disabled,
48907      callback = indeterminate ? _determinate : checked ? _unchecked : 'enabled',
48908      regular = option(input, callback + capitalize(node[_type])),
48909      specific = option(input, state + capitalize(node[_type]));
48910
48911    // Prevent unnecessary actions
48912    if (node[state] !== false) {
48913
48914      // Toggle state
48915      if (indeterminate || !keep || keep == 'force') {
48916        node[state] = false;
48917      }
48918
48919      // Trigger callbacks
48920      callbacks(input, checked, callback, keep);
48921    }
48922
48923    // Add proper cursor
48924    if (!node[_disabled] && !!option(input, _cursor, true)) {
48925      parent.find('.' + _iCheckHelper).css(_cursor, 'pointer');
48926    }
48927
48928    // Remove state class
48929    parent[_remove](specific || option(input, state) || '');
48930
48931    // Set ARIA attribute
48932    if (!!parent.attr('role') && !indeterminate) {
48933      parent.attr('aria-' + (disabled ? _disabled : _checked), 'false');
48934    }
48935
48936    // Add regular state class
48937    parent[_add](regular || option(input, callback) || '');
48938  }
48939
48940  // Remove all traces
48941  function tidy(input, callback) {
48942    if (input.data(_iCheck)) {
48943
48944      // Remove everything except input
48945      input.parent().html(input.attr('style', input.data(_iCheck).s || ''));
48946
48947      // Callback
48948      if (callback) {
48949        input[_callback](callback);
48950      }
48951
48952      // Unbind events
48953      input.off('.i').unwrap();
48954      $(_label + '[for="' + input[0].id + '"]').add(input.closest(_label)).off('.i');
48955    }
48956  }
48957
48958  // Get some option
48959  function option(input, state, regular) {
48960    if (input.data(_iCheck)) {
48961      return input.data(_iCheck).o[state + (regular ? '' : 'Class')];
48962    }
48963  }
48964
48965  // Capitalize some string
48966  function capitalize(string) {
48967    return string.charAt(0).toUpperCase() + string.slice(1);
48968  }
48969
48970  // Executable handlers
48971  function callbacks(input, checked, callback, keep) {
48972    if (!keep) {
48973      if (checked) {
48974        input[_callback]('ifToggled');
48975      }
48976
48977      input[_callback]('ifChanged')[_callback]('if' + capitalize(callback));
48978    }
48979  }
48980})(window.jQuery || window.Zepto);
48981
48982!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.Sweetalert2=t()}(this,function(){"use strict";function e(){if(void 0===arguments[0])return console.error("SweetAlert2 expects at least 1 attribute!"),!1;var e=c({},D);switch(typeof arguments[0]){case"string":e.title=arguments[0],e.text=arguments[1]||"",e.type=arguments[2]||"";break;case"object":c(e,arguments[0]),e.extraParams=arguments[0].extraParams,"email"===e.input&&null===e.inputValidator&&(e.inputValidator=function(e){return new Promise(function(t,n){var o=/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/;o.test(e)?t():n("Invalid email address")})});break;default:return console.error('SweetAlert2: Unexpected type of argument! Expected "string" or "object", got '+typeof arguments[0]),!1}I(e);var n=m();return new Promise(function(o,r){function a(e,t){for(var n=g(),o=0;o<n.length;o++){e+=t,e===n.length?e=0:-1===e&&(e=n.length-1);var i=n[e];if(i.offsetWidth||i.offsetHeight||i.getClientRects().length)return i.focus()}}function l(n){var o=n||window.event,i=o.keyCode||o.which;if(-1!==[9,13,32,27].indexOf(i)){for(var l=o.target||o.srcElement,c=g(),s=-1,u=0;u<c.length;u++)if(l===c[u]){s=u;break}9===i?(o.shiftKey?a(s,-1):a(s,1),V(o)):13===i||32===i?-1===s&&O(M,o):27===i&&e.allowEscapeKey===!0&&(t.closeModal(e.onClose),r("esc"))}}e.timer&&(n.timeout=setTimeout(function(){t.closeModal(e.onClose),r("timer")},e.timer));var c=function(){switch(e.input){case"select":return E(n,i.select);case"radio":return n.querySelector("."+i.radio+" input:checked")||n.querySelector("."+i.radio+" input:first-child");case"checkbox":return n.querySelector("#"+i.checkbox);case"textarea":return E(n,i.textarea);default:return E(n,i.input)}},u=function(){var t=c();switch(e.input){case"checkbox":return t.checked?1:0;case"radio":return t.checked?t.value:null;case"file":return t.files.length?t.files[0]:null;default:return e.inputAutoTrim?t.value.trim():t.value}};e.input&&setTimeout(function(){var e=c();e&&C(e)},0);var d,f=function(n){e.showLoaderOnConfirm&&t.showLoading(),e.preConfirm?e.preConfirm(n,e.extraParams).then(function(i){t.closeModal(e.onClose),o(i||n)},function(e){t.hideLoading(),e&&t.showValidationError(e)}):(t.closeModal(e.onClose),o(n))},m=function(o){var i=o||window.event,a=i.target||i.srcElement,l=y(),c=h(),d=l===a||l.contains(a),p=c===a||c.contains(a),m=w(n,"visible");switch(i.type){case"mouseover":case"mouseup":e.buttonsStyling&&(d?l.style.backgroundColor=s(e.confirmButtonColor,-.1):p&&(c.style.backgroundColor=s(e.cancelButtonColor,-.1)));break;case"mouseout":e.buttonsStyling&&(d?l.style.backgroundColor=e.confirmButtonColor:p&&(c.style.backgroundColor=e.cancelButtonColor));break;case"mousedown":e.buttonsStyling&&(d?l.style.backgroundColor=s(e.confirmButtonColor,-.2):p&&(c.style.backgroundColor=s(e.cancelButtonColor,-.2)));break;case"click":if(d&&m)if(e.input){var v=u();
48982e.inputValidator?(t.disableInput(),e.inputValidator(v,e.extraParams).then(function(){t.enableInput(),f(v)},function(e){t.enableInput(),e&&t.showValidationError(e)})):f(v)}else f(!0);else p&&m&&(t.closeModal(e.onClose),r("cancel"))}},q=n.querySelectorAll("button");for(d=0;d<q.length;d++)q[d].onclick=m,q[d].onmouseover=m,q[d].onmouseout=m,q[d].onmousedown=m;b().onclick=function(){t.closeModal(e.onClose),r("close")},v().onclick=function(){e.allowOutsideClick&&(t.closeModal(e.onClose),r("overlay"))};var M=y(),P=h();e.reverseButtons&&M.parentNode.insertBefore(P,M),p.previousWindowKeyDown=window.onkeydown,window.onkeydown=l,e.buttonsStyling&&(M.style.borderLeftColor=e.confirmButtonColor,M.style.borderRightColor=e.confirmButtonColor),t.showLoading=t.enableLoading=function(){k(M,"loading"),k(n,"loading"),M.disabled=!0,P.disabled=!0},t.hideLoading=t.disableLoading=function(){x(M,"loading"),x(n,"loading"),M.disabled=!1,P.disabled=!1},t.enableButtons=function(){M.disabled=!1,P.disabled=!1},t.disableButtons=function(){M.disabled=!0,P.disabled=!0},t.enableConfirmButton=function(){M.disabled=!1},t.disableConfirmButton=function(){M.disabled=!0},t.enableInput=function(){var e=c();if("radio"===e.type)for(var t=e.parentNode.parentNode,n=t.querySelectorAll("input"),o=0;o<n.length;o++)n[o].disabled=!1;else e.disabled=!1},t.disableInput=function(){var e=c();if("radio"===e.type)for(var t=e.parentNode.parentNode,n=t.querySelectorAll("input"),o=0;o<n.length;o++)n[o].disabled=!0;else e.disabled=!0},t.showValidationError=function(e){var t=n.querySelector("."+i.validationerror);t.innerHTML=e,B(t);var o=c();C(o),k(o,"error")},t.resetValidationError=function(){var e=n.querySelector("."+i.validationerror);L(e);var t=c();t&&x(t,"error")},t.enableButtons(),t.hideLoading(),t.resetValidationError();var T,H=["input","select","radio","checkbox","textarea"];for(d=0;d<H.length;d++){var N=i[H[d]];for(T=E(n,N);T.attributes.length>0;)T.removeAttribute(T.attributes[0].name);for(var D in e.inputAttributes)T.setAttribute(D,e.inputAttributes[D]);T.className=N,e.inputClass&&k(T,e.inputClass),A(T)}var I;switch(e.input){case"text":case"email":case"password":case"file":T=E(n,i.input),T.value=e.inputValue,T.placeholder=e.inputPlaceholder,T.type=e.input,S(T);break;case"select":var U=E(n,i.select);if(U.innerHTML="",e.inputPlaceholder){var W=document.createElement("option");W.innerHTML=e.inputPlaceholder,W.value="",W.disabled=!0,W.selected=!0,U.appendChild(W)}I=function(t){for(var n in t){var o=document.createElement("option");o.value=n,o.innerHTML=t[n],e.inputValue===n&&(o.selected=!0),U.appendChild(o)}S(U),U.focus()};break;case"radio":var z=E(n,i.radio);z.innerHTML="",I=function(t){for(var n in t){var o=1,r=document.createElement("input"),a=document.createElement("label"),l=document.createElement("span");r.type="radio",r.name=i.radio,r.value=n,r.id=i.radio+"-"+o++,e.inputValue===n&&(r.checked=!0),l.innerHTML=t[n],a.appendChild(r),a.appendChild(l),a["for"]=r.id,z.appendChild(a)}S(z);var c=z.querySelectorAll("input");c.length&&c[0].focus()};break;case"checkbox":var R=E(n,i.checkbox),Z=n.querySelector("#"+i.checkbox);Z.value=1,Z.checked=Boolean(e.inputValue);var $=R.getElementsByTagName("span");$.length&&R.removeChild($[0]),$=document.createElement("span"),$.innerHTML=e.inputPlaceholder,R.appendChild($),S(R);break;case"textarea":var _=E(n,i.textarea);_.value=e.inputValue,_.placeholder=e.inputPlaceholder,S(_);break;case null:break;default:console.error('SweetAlert2: Unexpected type of input! Expected "text" or "email" or "password", "select", "checkbox", "textarea" or "file", got "'+e.input+'"')}"select"!==e.input&&"radio"!==e.input||(e.inputOptions instanceof Promise?(t.showLoading(),e.inputOptions.then(function(e){t.hideLoading(),I(e)})):"object"==typeof e.inputOptions?I(e.inputOptions):console.error("SweetAlert2: Unexpected type of inputOptions! Expected object or Promise, got "+typeof e.inputOptions)),K(),j(e.animation,e.onOpen),a(-1,1)})}function t(){var n=arguments,o=m();return null===o&&(t.init(),o=m()),w(o,"visible")&&N(),e.apply(this,n)}var n="swal2-",o=function(e){var t={};for(var o in e)t[e[o]]=n+e[o];return t},i=o(["container","modal","overlay","close","content","spacer","confirm","cancel","icon","image","input","select","radio","checkbox","textarea","validationerror"]),r=o(["success","warning","info","question","error"]),a={title:"",text:"",html:"",type:null,animation:!0,allowOutsideClick:!0,allowEscapeKey:!0,showConfirmButton:!0,showCancelButton:!1,preConfirm:null,confirmButtonText:"OK",confirmButtonColor:"#3085d6",confirmButtonClass:null,cancelButtonText:"Cancel",cancelButtonColor:"#aaa",cancelButtonClass:null,buttonsStyling:!0,reverseButtons:!1,showCloseButton:!1,showLoaderOnConfirm:!1,imageUrl:null,imageWidth:null,imageHeight:null,imageClass:null,timer:null,width:500,padding:20,background:"#fff",input:null,inputPlaceholder:"",inputValue:"",inputOptions:{},inputAutoTrim:!0,inputClass:null,inputAttributes:{},inputValidator:null,onOpen:null,onClose:null},l='<div class="'+i.overlay+'" tabIndex="-1"></div><div class="'+i.modal+'" style="display: none" tabIndex="-1"><div class="'+i.icon+" "+r.error+'"><span class="x-mark"><span class="line left"></span><span class="line right"></span></span></div><div class="'+i.icon+" "+r.question+'">?</div><div class="'+i.icon+" "+r.warning+'">!</div><div class="'+i.icon+" "+r.info+'">i</div><div class="'+i.icon+" "+r.success+'"><span class="line tip"></span> <span class="line long"></span><div class="placeholder"></div> <div class="fix"></div></div><img class="'+i.image+'"><h2></h2><div class="'+i.content+'"></div><input class="'+i.input+'"><select class="'+i.select+'"></select><div class="'+i.radio+'"></div><label for="'+i.checkbox+'" class="'+i.checkbox+'"><input type="checkbox" id="'+i.checkbox+'"></label><textarea class="'+i.textarea+'"></textarea><div class="'+i.validationerror+'"></div><hr class="'+i.spacer+'"><button class="'+i.confirm+'">OK</button><button class="'+i.cancel+'">Cancel</button><span class="'+i.close+'">&times;</span></div>',c=function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);return e},s=function(e,t){e=String(e).replace(/[^0-9a-f]/gi,""),e.length<6&&(e=e[0]+e[0]+e[1]+e[1]+e[2]+e[2]),t=t||0;for(var n="#",o=0;3>o;o++){var i=parseInt(e.substr(2*o,2),16);i=Math.round(Math.min(Math.max(0,i+i*t),255)).toString(16),n+=("00"+i).substr(i.length)}return n},u=function(e){return"function"==typeof e},d=n+"mediaquery",p={previousWindowKeyDown:null,previousActiveElement:null},f=function(e){return document.querySelector("."+e)},m=function(){return f(i.modal)},v=function(){return f(i.overlay)},y=function(){return f(i.confirm)},h=function(){return f(i.cancel)},b=function(){return f(i.close)},g=function(){return[y(),h()].concat(Array.prototype.slice.call(m().querySelectorAll("button:not([class^="+n+"]), input:not([type=hidden]), textarea, select")))},w=function(e,t){return e.classList.contains(t)}
48982,C=function(e){e.focus();var t=e.value;e.value="",e.value=t},k=function(e,t){if(e&&t){var n=t.split(/\s+/);n.forEach(function(t){e.classList.add(t)})}},x=function(e,t){if(e&&t){var n=t.split(/\s+/);n.forEach(function(t){e.classList.remove(t)})}},E=function(e,t){for(var n=0;n<e.childNodes.length;n++)if(w(e.childNodes[n],t))return e.childNodes[n]},S=function(e){e.style.opacity="",e.style.display="block"},B=function(e){if(e&&!e.length)return S(e);for(var t=0;t<e.length;++t)S(e[t])},A=function(e){e.style.opacity="",e.style.display="none"},L=function(e){if(e&&!e.length)return A(e);for(var t=0;t<e.length;++t)A(e[t])},q=function(e,t){e.style.removeProperty?e.style.removeProperty(t):e.style.removeAttribute(t)},M=function(e){var t=e.style.display;e.style.left="-9999px",e.style.display="block";var n=e.clientHeight;return e.style.left="",e.style.display=t,"-"+parseInt(n/2,10)+"px"},P=function(e,t){if(+e.style.opacity<1){t=t||16,e.style.opacity=0,e.style.display="block";var n=+new Date,o=function(){var i=+e.style.opacity+(new Date-n)/100;e.style.opacity=i>1?1:i,n=+new Date,+e.style.opacity<1&&setTimeout(o,t)};o()}},T=function(e,t){if(+e.style.opacity>0){t=t||16;var n=e.style.opacity,o=+new Date,i=function(){var r=new Date-o,a=+e.style.opacity-r/(100*n);e.style.opacity=a,o=+new Date,+e.style.opacity>0?setTimeout(i,t):A(e)};i()}},O=function(e){if("function"==typeof MouseEvent){var t=new MouseEvent("click",{view:window,bubbles:!1,cancelable:!0});e.dispatchEvent(t)}else if(document.createEvent){var n=document.createEvent("MouseEvents");n.initEvent("click",!1,!1),e.dispatchEvent(n)}else document.createEventObject?e.fireEvent("onclick"):"function"==typeof e.onclick&&e.onclick()},V=function(e){"function"==typeof e.stopPropagation?(e.stopPropagation(),e.preventDefault()):window.event&&window.event.hasOwnProperty("cancelBubble")&&(window.event.cancelBubble=!0)},H=function(){var e=document.createElement("div"),t={WebkitAnimation:"webkitAnimationEnd",MozAnimation:"animationend",OAnimation:"oAnimationEnd oanimationend",msAnimation:"MSAnimationEnd",animation:"animationend"};for(var n in t)if(t.hasOwnProperty(n)&&void 0!==e.style[n])return t[n];return!1}(),N=function(){var e=m();window.onkeydown=p.previousWindowKeyDown,p.previousActiveElement&&p.previousActiveElement.focus&&p.previousActiveElement.focus(),clearTimeout(e.timeout);var t=document.getElementsByTagName("head")[0],n=document.getElementById(d);n&&t.removeChild(n)},D=c({},a),I=function(e){var t=m();for(var n in e)a.hasOwnProperty(n)||"extraParams"===n||console.warn('SweetAlert2: Unknown parameter "'+n+'"');t.style.width=e.width+"px",t.style.padding=e.padding+"px",t.style.marginLeft=-e.width/2+"px",t.style.background=e.background;var o=document.getElementsByTagName("head")[0],l=document.createElement("style");l.type="text/css",l.id=d;var c=5,s=e.width+parseInt(e.width*(c/100)*2,10);l.innerHTML="@media screen and (max-width: "+s+"px) {."+i.modal+" {width: auto !important;left: "+c+"% !important;right: "+c+"% !important;margin-left: 0 !important;}}",o.appendChild(l);var u=t.querySelector("h2"),p=t.querySelector("."+i.content),f=y(),v=h(),b=t.querySelector("."+i.spacer),g=t.querySelector("."+i.close);if(u.innerHTML=e.title.split("\n").join("<br>"),e.text||e.html){if("object"==typeof e.html)if(p.innerHTML="",0 in e.html)for(var w=0;w in e.html;w++)p.appendChild(e.html[w]);else p.appendChild(e.html);else p.innerHTML=e.html||e.text.split("\n").join("<br>");B(p)}else L(p);if(e.showCloseButton?B(g):L(g),t.className=i.modal,e.customClass&&k(t,e.customClass),L(t.querySelectorAll("."+i.icon)),e.type){var C=!1;for(var E in r)if(e.type===E){C=!0;break}if(!C)return console.error("SweetAlert2: Unknown alert type: "+e.type),!1;var S=t.querySelector("."+i.icon+"."+r[e.type]);switch(B(S),e.type){case"success":k(S,"animate"),k(S.querySelector(".tip"),"animate-success-tip"),k(S.querySelector(".long"),"animate-success-long");break;case"error":k(S,"animate-error-icon"),k(S.querySelector(".x-mark"),"animate-x-mark");break;case"warning":k(S,"pulse-warning")}}var A=t.querySelector("."+i.image);e.imageUrl?(A.setAttribute("src",e.imageUrl),B(A),e.imageWidth?A.setAttribute("width",e.imageWidth):A.removeAttribute("width"),e.imageHeight?A.setAttribute("height",e.imageHeight):A.removeAttribute("height"),e.imageClass&&k(A,e.imageClass)):L(A),e.showCancelButton?v.style.display="inline-block":L(v),e.showConfirmButton?q(f,"display"):L(f),e.showConfirmButton||e.showCancelButton?B(b):L(b),f.innerHTML=e.confirmButtonText,v.innerHTML=e.cancelButtonText,e.buttonsStyling&&(f.style.backgroundColor=e.confirmButtonColor,v.style.backgroundColor=e.cancelButtonColor),f.className=i.confirm,k(f,e.confirmButtonClass),v.className=i.cancel,k(v,e.cancelButtonClass),e.buttonsStyling?(k(f,"styled"),k(v,"styled")):(x(f,"styled"),x(v,"styled"),f.style.backgroundColor=f.style.borderLeftColor=f.style.borderRightColor="",v.style.backgroundColor=v.style.borderLeftColor=v.style.borderRightColor=""),e.animation===!0?x(t,"no-animation"):k(t,"no-animation")},j=function(e,t){var n=m();
48982e?(P(v(),10),k(n,"show-swal2"),x(n,"hide-swal2")):B(v()),B(n),p.previousActiveElement=document.activeElement,k(n,"visible"),null!==t&&"function"==typeof t&&t.call(this,n)},K=function(){var e=m();e.style.marginTop=M(e)};return t.queue=function(e){return new Promise(function(n,o){!function i(r,a){var l=null;u(e)?l=e(r):r<e.length&&(l=e[r]),l?t(l).then(function(){i(r+1,a)},function(e){o(e)}):n()}(0)})},t.close=t.closeModal=function(e){var t=m();x(t,"show-swal2"),k(t,"hide-swal2"),x(t,"visible");var n=t.querySelector("."+i.icon+"."+r.success);x(n,"animate"),x(n.querySelector(".tip"),"animate-success-tip"),x(n.querySelector(".long"),"animate-success-long");var o=t.querySelector("."+i.icon+"."+r.error);x(o,"animate-error-icon"),x(o.querySelector(".x-mark"),"animate-x-mark");var a=t.querySelector("."+i.icon+"."+r.warning);x(a,"pulse-warning"),N(),H&&!w(t,"no-animation")?t.addEventListener(H,function l(){t.removeEventListener(H,l),w(t,"hide-swal2")&&(A(t),T(v(),0))}):(A(t),A(v())),null!==e&&"function"==typeof e&&e.call(this,t)},t.clickConfirm=function(){y().click()},t.clickCancel=function(){h().click()},t.init=function(){if("undefined"==typeof document)return void console.log("SweetAlert2 requires document to initialize");if(!document.getElementsByClassName(i.container).length){var e=document.createElement("div");e.className=i.container,e.innerHTML=l,document.body.appendChild(e);var n=m(),o=E(n,i.input),r=E(n,i.select),a=n.querySelector("#"+i.checkbox),c=E(n,i.textarea);o.oninput=function(){t.resetValidationError()},o.onkeyup=function(e){e.stopPropagation(),13===e.keyCode&&t.clickConfirm()},r.onchange=function(){t.resetValidationError()},a.onchange=function(){t.resetValidationError()},c.oninput=function(){t.resetValidationError()},window.addEventListener("resize",K,!1)}},t.setDefaults=function(e){if(!e)throw new Error("userParams is required");if("object"!=typeof e)throw new Error("userParams has to be a object");c(D,e)},t.resetDefaults=function(){D=c({},a)},t.version="4.1.0",window.sweetAlert=window.swal=t,function(){"complete"===document.readyState||"interactive"===document.readyState&&document.body?t.init():document.addEventListener("DOMContentLoaded",function e(){document.removeEventListener("DOMContentLoaded",e,!1),t.init()},!1)}(),"function"==typeof Promise&&(Promise.prototype.done=function(){return this["catch"](function(){})}),t});
48983
48984(function(e){typeof define=="function"&&define.amd?define(["jquery"],e):typeof module=="object"&&module.exports?module.exports=function(t,n){return n===undefined&&(typeof window!="undefined"?n=require("jquery"):n=require("jquery")(t)),e(n),n}:e(jQuery)})(function(e){function A(t,n,i){typeof i=="string"&&(i={className:i}),this.options=E(w,e.isPlainObject(i)?i:{}),this.loadHTML(),this.wrapper=e(h.html),this.options.clickToHide&&this.wrapper.addClass(r+"-hidable"),this.wrapper.data(r,this),this.arrow=this.wrapper.find("."+r+"-arrow"),this.container=this.wrapper.find("."+r+"-container"),this.container.append(this.userContainer),t&&t.length&&(this.elementType=t.attr("type"),this.originalElement=t,this.elem=N(t),this.elem.data(r,this),this.elem.before(this.wrapper)),this.container.hide(),this.run(n)}var t=[].indexOf||function(e){for(var t=0,n=this.length;t<n;t++)if(t in this&&this[t]===e)return t;return-1},n="notify",r=n+"js",i=n+"!blank",s={t:"top",m:"middle",b:"bottom",l:"left",c:"center",r:"right"},o=["l","c","r"],u=["t","m","b"],a=["t","b","l","r"],f={t:"b",m:null,b:"t",l:"r",c:null,r:"l"},l=function(t){var n;return n=[],e.each(t.split(/\W+/),function(e,t){var r;r=t.toLowerCase().charAt(0);if(s[r])return n.push(r)}),n},c={},h={name:"core",html:'<div class="'+r+'-wrapper">\n	<div class="'+r+'-arrow"></div>\n	<div class="'+r+'-container"></div>\n</div>',css:"."+r+"-corner {\n	position: fixed;\n	margin: 5px;\n	z-index: 1050;\n}\n\n."+r+"-corner ."+r+"-wrapper,\n."+r+"-corner ."+r+"-container {\n	position: relative;\n	display: block;\n	height: inherit;\n	width: inherit;\n	margin: 3px;\n}\n\n."+r+"-wrapper {\n	z-index: 1;\n	position: absolute;\n	display: inline-block;\n	height: 0;\n	width: 0;\n}\n\n."+r+"-container {\n	display: none;\n	z-index: 1;\n	position: absolute;\n}\n\n."+r+"-hidable {\n	cursor: pointer;\n}\n\n[data-notify-text],[data-notify-html] {\n	position: relative;\n}\n\n."+r+"-arrow {\n	position: absolute;\n	z-index: 2;\n	width: 0;\n	height: 0;\n}"},p={"border-radius":["-webkit-","-moz-"]},d=function(e){return c[e]},v=function(e){if(!e)throw"Missing Style name";c[e]&&delete c[e]},m=function(t,i){if(!t)throw"Missing Style name";if(!i)throw"Missing Style definition";if(!i.html)throw"Missing Style HTML";var s=c[t];s&&s.cssElem&&(window.console&&console.warn(n+": overwriting style '"+t+"'"),c[t].cssElem.remove()),i.name=t,c[t]=i;var o="";i.classes&&e.each(i.classes,function(t,n){return o+="."+r+"-"+i.name+"-"+t+" {\n",e.each(n,function(t,n){return p[t]&&e.each(p[t],function(e,r){return o+="	"+r+t+": "+n+";\n"}),o+="	"+t+": "+n+";\n"}),o+="}\n"}),i.css&&(o+="/* styles for "+i.name+" */\n"+i.css),o&&(i.cssElem=g(o),i.cssElem.attr("id","notify-"+i.name));var u={},a=e(i.html);y("html",a,u),y("text",a,u),i.fields=u},g=function(t){var n,r,i;r=x("style"),r.attr("type","text/css"),e("head").append(r);try{r.html(t)}
48984catch(s){r[0].styleSheet.cssText=t}return r},y=function(t,n,r){var s;return t!=="html"&&(t="text"),s="data-notify-"+t,b(n,"["+s+"]").each(function(){var n;n=e(this).attr(s),n||(n=i),r[n]=t})},b=function(e,t){return e.is(t)?e:e.find(t)},w={clickToHide:!0,autoHide:!0,autoHideDelay:5e3,arrowShow:!0,arrowSize:5,breakNewLines:!0,elementPosition:"bottom",globalPosition:"top right",style:"bootstrap",className:"error",showAnimation:"slideDown",showDuration:400,hideAnimation:"slideUp",hideDuration:200,gap:5},E=function(t,n){var r;return r=function(){},r.prototype=t,e.extend(!0,new r,n)},S=function(t){return e.extend(w,t)},x=function(t){return e("<"+t+"></"+t+">")},T={},N=function(t){var n;return t.is("[type=radio]")&&(n=t.parents("form:first").find("[type=radio]").filter(function(n,r){return e(r).attr("name")===t.attr("name")}),t=n.first()),t},C=function(e,t,n){var r,i;if(typeof n=="string")n=parseInt(n,10);else if(typeof n!="number")return;if(isNaN(n))return;return r=s[f[t.charAt(0)]],i=t,e[r]!==undefined&&(t=s[r.charAt(0)],n=-n),e[t]===undefined?e[t]=n:e[t]+=n,null},k=function(e,t,n){if(e==="l"||e==="t")return 0;if(e==="c"||e==="m")return n/2-t/2;if(e==="r"||e==="b")return n-t;throw"Invalid alignment"},L=function(e){return L.e=L.e||x("div"),L.e.text(e).html()};A.prototype.loadHTML=function(){var t;t=this.getStyle(),this.userContainer=e(t.html),this.userFields=t.fields},A.prototype.show=function(e,t){var n,r,i,s,o;r=function(n){return function(){!e&&!n.elem&&n.destroy();if(t)return t()}}(this),o=this.container.parent().parents(":hidden").length>0,i=this.container.add(this.arrow),n=[];if(o&&e)s="show";else if(o&&!e)s="hide";else if(!o&&e)s=this.options.showAnimation,n.push(this.options.showDuration);else{if(!!o||!!e)return r();s=this.options.hideAnimation,n.push(this.options.hideDuration)}return n.push(r),i[s].apply(i,n)},A.prototype.setGlobalPosition=function(){var t=this.getPosition(),n=t[0],i=t[1],o=s[n],u=s[i],a=n+"|"+i,f=T[a];if(!f||!document.contains(f[0])){f=T[a]=x("div");var l={};l[o]=0,u==="middle"?l.top="45%":u==="center"?l.left="45%":l[u]=0,f.css(l).addClass(r+"-corner"),e("body").append(f)}return f.prepend(this.wrapper)},A.prototype.setElementPosition=function(){var n,r,i,l,c,h,p,d,v,m,g,y,b,w,E,S,x,T,N,L,A,O,M,_,D,P,H,B,j;H=this.getPosition(),_=H[0],O=H[1],M=H[2],g=this.elem.position(),d=this.elem.outerHeight(),y=this.elem.outerWidth(),v=this.elem.innerHe
48984ight(),m=this.elem.innerWidth(),j=this.wrapper.position(),c=this.container.height(),h=this.container.width(),T=s[_],L=f[_],A=s[L],p={},p[A]=_==="b"?d:_==="r"?y:0,C(p,"top",g.top-j.top),C(p,"left",g.left-j.left),B=["top","left"];for(w=0,S=B.length;w<S;w++)D=B[w],N=parseInt(this.elem.css("margin-"+D),10),N&&C(p,D,N);b=Math.max(0,this.options.gap-(this.options.arrowShow?i:0)),C(p,A,b);if(!this.options.arrowShow)this.arrow.hide();else{i=this.options.arrowSize,r=e.extend({},p),n=this.userContainer.css("border-color")||this.userContainer.css("border-top-color")||this.userContainer.css("background-color")||"white";for(E=0,x=a.length;E<x;E++){D=a[E],P=s[D];if(D===L)continue;l=P===T?n:"transparent",r["border-"+P]=i+"px solid "+l}C(p,s[L],i),t.call(a,O)>=0&&C(r,s[O],i*2)}t.call(u,_)>=0?(C(p,"left",k(O,h,y)),r&&C(r,"left",k(O,i,m))):t.call(o,_)>=0&&(C(p,"top",k(O,c,d)),r&&C(r,"top",k(O,i,v))),this.container.is(":visible")&&(p.display="block"),this.container.removeAttr("style").css(p);if(r)return this.arrow.removeAttr("style").css(r)},A.prototype.getPosition=function(){var e,n,r,i,s,f,c,h;h=this.options.position||(this.elem?this.options.elementPosition:this.options.globalPosition),e=l(h),e.length===0&&(e[0]="b");if(n=e[0],t.call(a,n)<0)throw"Must be one of ["+a+"]";if(e.length===1||(r=e[0],t.call(u,r)>=0)&&(i=e[1],t.call(o,i)<0)||(s=e[0],t.call(o,s)>=0)&&(f=e[1],t.call(u,f)<0))e[1]=(c=e[0],t.call(o,c)>=0)?"m":"l";return e.length===2&&(e[2]=e[1]),e},A.prototype.getStyle=function(e){var t;e||(e=this.options.style),e||(e="default"),t=c[e];if(!t)throw"Missing style: "+e;return t},A.prototype.updateClasses=function(){var t,n;return t=["base"],e.isArray(this.options.className)?t=t.concat(this.options.className):this.options.className&&t.push(this.options.className),n=this.getStyle(),t=e.map(t,function(e){return r+"-"+n.name+"-"+e}).join(" "),this.userContainer.attr("class",t)},A.prototype.run=function(t,n){var r,s,o,u,a;e.isPlainObject(n)?e.extend(this.options,n):e.type(n)==="string"&&(this.options.className=n);if(this.container&&!t){this.show(!1);return}if(!this.container&&!t)return;s={},e.isPlainObject(t)?s=t:s[i]=t;for(o in s){r=s[o],u=this.userFields[o];if(!u)continue;u==="text"&&(r=L(r),this.options.breakNewLines&&(r=r.replace(/\n/g,"<br/>"))),a=o===i?"":"="+o,b(this.userContainer,"[data-notify-"+u+a+"]").html(r)}this.updateClasses(),this.elem?this.setElementPosition():this.setGlobalPosition(),this.show(!0),this.options.autoHide&&(clearTimeout(this.autohideTimer),this.autohideTimer=setTimeout(this.show.bind(this,!1),this.options.autoHideDelay))},A.prototype.destroy=function(){this.wrapper.data(r,null),this.wrapper.remove()},e[n]=function(t,r,i){return t&&t.nodeName||t.jquery?e(t)[n](r,i):(i=r,r=t,new A(null,r,i)),t},e.fn[n]=function(t,n){return e(this).each(function(){var i=N(e(this)).data(r);i&&i.destroy();var s=new A(e(this),t,n)}),this},e.extend(e[n],{defaults:S,addStyle:m,removeStyle:v,pluginOptions:w,getStyle:d,insertCSS:g}),m("bootstrap",{html:"<div>\n<span data-notify-text></span>\n</div>",classes:{base:{"font-weight":"bold",padding:"8px 15px 8px 14px","text-shadow":"0 1px 0 rgba(255, 255, 255, 0.5)","background-color":"#fcf8e3",border:"1px solid #fbeed5","border-radius":"4px","white-space":"nowrap","padding-left":"25px","background-repeat":"no-repeat","background-position":"3px 7px"},error:{color:"#B94A48","background-color":"#F2DEDE","border-color":"#EED3D7","background-image":"url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAtRJREFUeNqkVc1u00AQHq+dOD+0poIQfkIjalW0SEGqRMuRnHos3DjwAH0ArlyQeANOOSMeAA5VjyBxKBQhgSpVUKKQNGloFdw4cWw2jtfMOna6JOUArDTazXi/b3dm55socPqQhFka++aHBsI8GsopRJERNFlY88FCEk9Yiwf8RhgRyaHFQpPHCDmZG5oX2ui2yilkcTT1AcDsbYC1NMAyOi7zTX2Agx7A9luAl88BauiiQ/cJaZQfIpAlngDcvZZMrl8vFPK5+XktrWlx3/ehZ5r9+t6e+WVnp1pxnNIjgBe4/6dAysQc8dsmHwPcW9C0h3fW1hans1ltwJhy0GxK7XZbUlMp5Ww2eyan6+ft/f2FAqXGK4CvQk5HueFz7D6GOZtIrK+srupdx1GRBBqNBtzc2AiMr7nPplRdKhb1q6q6zjFhrklEFOUutoQ50xcX86ZlqaZpQrfbBdu2R6/G19zX6XSgh6RX5ubyHCM8nqSID6ICrGiZjGYYxojEsiw4PDwMSL5VKsC8Yf4VRYFzMzMaxwjlJSlCyAQ9l0CW44PBADzXhe7xMdi9HtTrdYjFYkDQL0cn4Xdq2/EAE+InCnvADTf2eah4Sx9vExQjkqXT6aAERICMewd/UAp/IeYANM2joxt+q5VI+ieq2i0Wg3l6DNzHwTERPgo1ko7XBXj3vdlsT2F+UuhIhYkp7u7CarkcrFOCtR3H5JiwbAIeImjT/YQKKBtGjRFCU5IUgFRe7fF4cCNVIPMYo3VKqxwjyNAXNepuopyqnld602qVsfRpEkkz+GFL1wPj6ySXBpJtWVa5xlhpcyhBNwpZHmtX8AGgfIExo0ZpzkWVTBGiXCSEaHh62/PoR0p/vHaczxXGnj4bSo+G78lELU80h1uogBwWLf5YlsPmgDEd4M236xjm+8nm4IuE/9u+/PH2JXZfbwz4zw1WbO+SQPpXfwG/BBgAhCNZiSb/pOQAAAAASUVORK5CYII=)"},success:{color:"#468847","background-color":"#DFF0D8","border-color":"#D6E9C6","background-image":"url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAutJREFUeNq0lctPE0Ecx38zu/RFS1EryqtgJFA08YCiMZIAQQ4eRG8eDGdPJiYeTIwHTfwPiAcvXIwXLwoXPaDxkWgQ6islKlJLS
48984QWLUraPLTv7Gme32zoF9KSTfLO7v53vZ3d/M7/fIth+IO6INt2jjoA7bjHCJoAlzCRw59YwHYjBnfMPqAKWQYKjGkfCJqAF0xwZjipQtA3MxeSG87VhOOYegVrUCy7UZM9S6TLIdAamySTclZdYhFhRHloGYg7mgZv1Zzztvgud7V1tbQ2twYA34LJmF4p5dXF1KTufnE+SxeJtuCZNsLDCQU0+RyKTF27Unw101l8e6hns3u0PBalORVVVkcaEKBJDgV3+cGM4tKKmI+ohlIGnygKX00rSBfszz/n2uXv81wd6+rt1orsZCHRdr1Imk2F2Kob3hutSxW8thsd8AXNaln9D7CTfA6O+0UgkMuwVvEFFUbbAcrkcTA8+AtOk8E6KiQiDmMFSDqZItAzEVQviRkdDdaFgPp8HSZKAEAL5Qh7Sq2lIJBJwv2scUqkUnKoZgNhcDKhKg5aH+1IkcouCAdFGAQsuWZYhOjwFHQ96oagWgRoUov1T9kRBEODAwxM2QtEUl+Wp+Ln9VRo6BcMw4ErHRYjH4/B26AlQoQQTRdHWwcd9AH57+UAXddvDD37DmrBBV34WfqiXPl61g+vr6xA9zsGeM9gOdsNXkgpEtTwVvwOklXLKm6+/p5ezwk4B+j6droBs2CsGa/gNs6RIxazl4Tc25mpTgw/apPR1LYlNRFAzgsOxkyXYLIM1V8NMwyAkJSctD1eGVKiq5wWjSPdjmeTkiKvVW4f2YPHWl3GAVq6ymcyCTgovM3FzyRiDe2TaKcEKsLpJvNHjZgPNqEtyi6mZIm4SRFyLMUsONSSdkPeFtY1n0mczoY3BHTLhwPRy9/lzcziCw9ACI+yql0VLzcGAZbYSM5CCSZg1/9oc/nn7+i8N9p/8An4JMADxhH+xHfuiKwAAAABJRU5ErkJggg==)"},info:{color:"#3A87AD","background-color":"#D9EDF7","border-color":"#BCE8F1","background-image":"url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3QYFAhkSsdes/QAAA8dJREFUOMvVlGtMW2UYx//POaWHXg6lLaW0ypAtw1UCgbniNOLcVOLmAjHZolOYlxmTGXVZdAnRfXQm+7SoU4mXaOaiZsEpC9FkiQs6Z6bdCnNYruM6KNBw6YWewzl9z+sHImEWv+vz7XmT95f/+3/+7wP814v+efDOV3/SoX3lHAA+6ODeUFfMfjOWMADgdk+eEKz0pF7aQdMAcOKLLjrcVMVX3xdWN29/GhYP7SvnP0cWfS8caSkfHZsPE9Fgnt02JNutQ0QYHB2dDz9/pKX8QjjuO9xUxd/66HdxTeCHZ3rojQObGQBcuNjfplkD3b19Y/6MrimSaKgSMmpGU5WevmE/swa6Oy73tQHA0Rdr2Mmv/6A1n9w9suQ7097Z9lM4FlTgTDrzZTu4StXVfpiI48rVcUDM5cmEksrFnHxfpTtU/3BFQzCQF/2bYVoNbH7zmItbSoMj40JSzmMyX5qDvriA7QdrIIpA+3cdsMpu0nXI8cV0MtKXCPZev+gCEM1S2NHPvWfP/hL+7FSr3+0p5RBEyhEN5JCKYr8XnASMT0xBNyzQGQeI8fjsGD39RMPk7se2bd5ZtTyoFYXftF6y37gx7NeUtJJOTFlAHDZLDuILU3j3+H5oOrD3yWbIztugaAzg
48984nBKJuBLpGfQrS8wO4FZgV+c1IxaLgWVU0tMLEETCos4xMzEIv9cJXQcyagIwigDGwJgOAtHAwAhisQUjy0ORGERiELgG4iakkzo4MYAxcM5hAMi1WWG1yYCJIcMUaBkVRLdGeSU2995TLWzcUAzONJ7J6FBVBYIggMzmFbvdBV44Corg8vjhzC+EJEl8U1kJtgYrhCzgc/vvTwXKSib1paRFVRVORDAJAsw5FuTaJEhWM2SHB3mOAlhkNxwuLzeJsGwqWzf5TFNdKgtY5qHp6ZFf67Y/sAVadCaVY5YACDDb3Oi4NIjLnWMw2QthCBIsVhsUTU9tvXsjeq9+X1d75/KEs4LNOfcdf/+HthMnvwxOD0wmHaXr7ZItn2wuH2SnBzbZAbPJwpPx+VQuzcm7dgRCB57a1uBzUDRL4bfnI0RE0eaXd9W89mpjqHZnUI5Hh2l2dkZZUhOqpi2qSmpOmZ64Tuu9qlz/SEXo6MEHa3wOip46F1n7633eekV8ds8Wxjn37Wl63VVa+ej5oeEZ/82ZBETJjpJ1Rbij2D3Z/1trXUvLsblCK0XfOx0SX2kMsn9dX+d+7Kf6h8o4AIykuffjT8L20LU+w4AZd5VvEPY+XpWqLV327HR7DzXuDnD8r+ovkBehJ8i+y8YAAAAASUVORK5CYII=)"},warn:{color:"#C09853","background-color":"#FCF8E3","border-color":"#FBEED5","background-image":"url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAMAAAC6V+0/AAABJlBMVEXr6eb/2oD/wi7/xjr/0mP/ykf/tQD/vBj/3o7/uQ//vyL/twebhgD/4pzX1K3z8e349vK6tHCilCWbiQymn0jGworr6dXQza3HxcKkn1vWvV/5uRfk4dXZ1bD18+/52YebiAmyr5S9mhCzrWq5t6ufjRH54aLs0oS+qD751XqPhAybhwXsujG3sm+Zk0PTwG6Shg+PhhObhwOPgQL4zV2nlyrf27uLfgCPhRHu7OmLgAafkyiWkD3l49ibiAfTs0C+lgCniwD4sgDJxqOilzDWowWFfAH08uebig6qpFHBvH/aw26FfQTQzsvy8OyEfz20r3jAvaKbhgG9q0nc2LbZxXanoUu/u5WSggCtp1anpJKdmFz/zlX/1nGJiYmuq5Dx7+sAAADoPUZSAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfdBgUBGhh4aah5AAAAlklEQVQY02NgoBIIE8EUcwn1FkIXM1Tj5dDUQhPU502Mi7XXQxGz5uVIjGOJUUUW81HnYEyMi2HVcUOICQZzMMYmxrEyMylJwgUt5BljWRLjmJm4pI1hYp5SQLGYxDgmLnZOVxuooClIDKgXKMbN5ggV1ACLJcaBxNgcoiGCBiZwdWxOETBDrTyEFey0jYJ4eHjMGWgEAIpRFRCUt08qAAAAAElFTkSuQmCC)"}}}),e(function(){g(h.css).attr("id","core-notify"),e(document).on("click","."+r+"-hidable",function(t){e(this).trigger("notify-hide")}),e(document).on("notify-hide","."+r+"-wrapper",function(t){var n=e(this).data(r);n&&n.show(!1)})})})
48985
48986/*
48987 * angular-ui-bootstrap
48988 * http://angular-ui.github.io/bootstrap/
48989
48990 * Version: 1.3.3 - 2016-05-22
48991 * License: MIT
48992 */angular.module("ui.bootstrap",["ui.bootstrap.tpls","ui.bootstrap.collapse","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.dateparser","ui.bootstrap.isClass","ui.bootstrap.datepicker","ui.bootstrap.position","ui.bootstrap.datepickerPopup","ui.bootstrap.debounce","ui.bootstrap.dropdown","ui.bootstrap.stackedMap","ui.bootstrap.modal","ui.bootstrap.paging","ui.bootstrap.pager","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.typeahead"]),angular.module("ui.bootstrap.tpls",["uib/template/accordion/accordion-group.html","uib/template/accordion/accordion.html","uib/template/alert/alert.html","uib/template/carousel/carousel.html","uib/template/carousel/slide.html","uib/template/datepicker/datepicker.html","uib/template/datepicker/day.html","uib/template/datepicker/month.html","uib/template/datepicker/year.html","uib/template/datepickerPopup/popup.html","uib/template/modal/backdrop.html","uib/template/modal/window.html","uib/template/pager/pager.html","uib/template/pagination/pagination.html","uib/template/tooltip/tooltip-html-popup.html","uib/template/tooltip/tooltip-popup.html","uib/template/tooltip/tooltip-template-popup.html","uib/template/popover/popover-html.html","uib/template/popover/popover-template.html","uib/template/popover/popover.html","uib/template/progressbar/bar.html","uib/template/progressbar/progress.html","uib/template/progressbar/progressbar.html","uib/template/rating/rating.html","uib/template/tabs/tab.html","uib/template/tabs/tabset.html","uib/template/timepicker/timepicker.html","uib/template/typeahead/typeahead-match.html","uib/template/typeahead/typeahead-popup.html"]),angular.module("ui.bootstrap.collapse",[]).directive("uibCollapse",["$animate","$q","$parse","$injector",function(a,b,c,d){var e=d.has("$animateCss")?d.get("$animateCss"):null;return{link:function(d,f,g){function h(){f.hasClass("collapse")&&f.hasClass("in")||b.resolve(l(d)).then(function(){f.removeClass("collapse").addClass("collapsing").attr("aria-expanded",!0).attr("aria-hidden",!1),e?e(f,{addClass:"in",easing:"ease",to:{height:f[0].scrollHeight+"px"}}).start()["finally"](i):a.addClass(f,"in",{to:{height:f[0].scrollHeight+"px"}}).then(i)})}function i(){f.removeClass("collapsing").addClass("collapse").css({height:"auto"}),m(d)}function j(){return f.hasClass("collapse")||f.hasClass("in")?void b.resolve(n(d)).then(function(){f.css({height:f[0].scrollHeight+"px"}).removeClass("collapse").addClass("collapsing").attr("aria-expanded",!1).attr("aria-hidden",!0),e?e(f,{removeClass:"in",to:{height:"0"}}).start()["finally"](k):a.removeClass(f,"in",{to:{height:"0"}}).then(k)}):k()}function k(){f.css({height:"0"}),f.removeClass("collapsing").addClass("collapse"),o(d)}var l=c(g.expanding),m=c(g.expanded),n=c(g.collapsing),o=c(g.collapsed);d.$eval(g.uibCollapse)||f.addClass("in").addClass("collapse").attr("aria-expanded",!0).attr("aria-hidden",!1).css({height:"auto"}),d.$watch(g.uibCollapse,function(a){a?j():h()})}}}]),angular.module("ui.bootstrap.accordion",["ui.bootstrap.collapse"]).constant("uibAccordionConfig",{closeOthers:!0}).controller("UibAccordionController",["$scope","$attrs","uibAccordionConfig",function(a,b,c){this.groups=[],this.closeOthers=function(d){var e=angular.isDefined(b.closeOthers)?a.$eval(b.closeOthers):c.closeOthers;e&&angular.forEach(this.groups,function(a){a!==d&&(a.isOpen=!1)})},this.addGroup=function(a){var b=this;this.groups.push(a),a.$on("$destroy",function(c){b.removeGroup(a)})},this.removeGroup=function(a){var b=this.groups.indexOf(a);-1!==b&&this.groups.splice(b,1)}}]).directive("uibAccordion",function(){return{controller:"UibAccordionController",controllerAs:"accordion",transclude:!0,templateUrl:function(a,b){return b.templateUrl||"uib/template/accordion/accordion.html"}}}).directive("uibAccordionGroup",function(){return{require:"^uibAccordion",transclude:!0,replace:!0,templateUrl:function(a,b){return b.templateUrl||"uib/template/accordion/accordion-group.html"},scope:{heading:"@",panelClass:"@?",isOpen:"=?",isDisabled:"=?"},controller:function(){this.setHeading=function(a){this.heading=a}},link:function(a,b,c,d){d.addGroup(a),a.openClass=c.openClass||"panel-open",a.panelClass=c.panelClass||"panel-default",a.$watch("isOpen",function(c){b.toggleClass(a.openClass,!!c),c&&d.closeOthers(a)}),a.toggleOpen=function(b){a.isDisabled||b&&32!==b.which||(a.isOpen=!a.isOpen)};var e="accordiongroup-"+a.$id+"-"+Math.floor(1e4*Math.random());a.headingId=e+"-tab",a.panelId=e+"-panel"}}}).directive("uibAccordionHeading",function(){return{transclude:!0,template:"",replace:!0,require:"^uibAccordionGroup",link:function(a,b,c,d,e){d.setHeading(e(a,angular.noop))}}}).directive("uibAccordionTransclude",function(){function a(){return"uib-accordion-header,data-uib-accordion-header,x-uib-accordion-header,uib\\:accordion-header,[uib-accordion-header],[data-uib-accordion-header],[x-uib-accordion-header]"}return{require:"^uibAccordionGroup",link:function(b,c,d,e){b.$watch(function(){return e[d.uibAccordionTransclude]},function(b){if(b){var d=angular.element(c[0].querySelector(a()));d.html(""),d.append(b)}})}}}
vendor: 4,853 bytes, line 48992
48992),angular.module("ui.bootstrap.alert",[]).controller("UibAlertController",["$scope","$attrs","$interpolate","$timeout",function(a,b,c,d){a.closeable=!!b.close;var e=angular.isDefined(b.dismissOnTimeout)?c(b.dismissOnTimeout)(a.$parent):null;e&&d(function(){a.close()},parseInt(e,10))}]).directive("uibAlert",function(){return{controller:"UibAlertController",controllerAs:"alert",templateUrl:function(a,b){return b.templateUrl||"uib/template/alert/alert.html"},transclude:!0,replace:!0,scope:{type:"@",close:"&"}}}),angular.module("ui.bootstrap.buttons",[]).constant("uibButtonConfig",{activeClass:"active",toggleEvent:"click"}).controller("UibButtonsController",["uibButtonConfig",function(a){this.activeClass=a.activeClass||"active",this.toggleEvent=a.toggleEvent||"click"}]).directive("uibBtnRadio",["$parse",function(a){return{require:["uibBtnRadio","ngModel"],controller:"UibButtonsController",controllerAs:"buttons",link:function(b,c,d,e){var f=e[0],g=e[1],h=a(d.uibUncheckable);c.find("input").css({display:"none"}),g.$render=function(){c.toggleClass(f.activeClass,angular.equals(g.$modelValue,b.$eval(d.uibBtnRadio)))},c.on(f.toggleEvent,function(){if(!d.disabled){var a=c.hasClass(f.activeClass);a&&!angular.isDefined(d.uncheckable)||b.$apply(function(){g.$setViewValue(a?null:b.$eval(d.uibBtnRadio)),g.$render()})}}),d.uibUncheckable&&b.$watch(h,function(a){d.$set("uncheckable",a?"":void 0)})}}}]).directive("uibBtnCheckbox",function(){return{require:["uibBtnCheckbox","ngModel"],controller:"UibButtonsController",controllerAs:"button",link:function(a,b,c,d){function e(){return g(c.btnCheckboxTrue,!0)}function f(){return g(c.btnCheckboxFalse,!1)}function g(b,c){return angular.isDefined(b)?a.$eval(b):c}var h=d[0],i=d[1];b.find("input").css({display:"none"}),i.$render=function(){b.toggleClass(h.activeClass,angular.equals(i.$modelValue,e()))},b.on(h.toggleEvent,function(){c.disabled||a.$apply(function(){i.$setViewValue(b.hasClass(h.activeClass)?f():e()),i.$render()})})}}}),angular.module("ui.bootstrap.carousel",[]).controller("UibCarouselController",["$scope","$element","$interval","$timeout","$animate",function(a,b,c,d,e){function f(){for(;t.length;)t.shift()}function g(a){for(var b=0;b<q.length;b++)q[b].slide.active=b===a}function h(c,d,i){if(!u){if(angular.extend(c,{direction:i}),angular.extend(q[s].slide||{},{direction:i}),e.enabled(b)&&!a.$currentTransition&&q[d].element&&p.slides.length>1){q[d].element.data(r,c.direction);var j=p.getCurrentIndex();angular.isNumber(j)&&q[j].element&&q[j].element.data(r,c.direction),a.$currentTransition=!0,e.on("addClass",q[d].element,function(b,c){if("close"===c&&(a.$currentTransition=null,e.off("addClass",b),t.length)){var d=t.pop().slide,g=d.index,i=g>p.getCurrentIndex()?"next":"prev";f(),h(d,g,i)}})}a.active=c.index,s=c.index,g(d),l()}}function i(a){for(var b=0;b<q.length;b++)if(q[b].slide===a)return b}function j(){n&&(c.cancel(n),n=null)}function k(b){b.length||(a.$currentTransition=null,f())}function l(){j();var b=+a.interval;!isNaN(b)&&b>0&&(n=c(m,b))}function m(){var b=+a.interval;o&&!isNaN(b)&&b>0&&q.length?a.next():a.pause()}var n,o,p=this,q=p.slides=a.slides=[],r="uib-slideDirection",s=a.active,t=[],u=!1;p.addSlide=function(b,c){q.push({slide:b,element:c}),q.sort(function(a,b){return+a.slide.index-+b.slide.index}),(b.index===a.active||1===q.length&&!angular.isNumber(a.active))&&(a.$currentTransition&&(a.$currentTransition=null),s=b.index,a.active=b.index,g(s),p.select(q[i(b)]),1===q.length&&a.play())},p.getCurrentIndex=function(){for(var a=0;a<q.length;a++)if(q[a].slide.index===s)return a},p.next=a.next=function(){var b=(p.getCurrentIndex()+1)%q.length;return 0===b&&a.noWrap()?void a.pause():p.select(q[b],"next")},p.prev=a.prev=function(){var b=p.getCurrentIndex()-1<0?q.length-1:p.getCurrentIndex()-1;return a.noWrap()&&b===q.length-1?void a.pause():p.select(q[b],"prev")},p.removeSlide=function(b){var c=i(b),d=t.indexOf(q[c]);-1!==d&&t.splice(d,1),q.splice(c,1),q.length>0&&s===c?c>=q.length?(s=q.length-1,a.active=s,g(s),p.select(q[q.length-1])):(s=c,a.active=s,g(s),p.select(q[c])):s>c&&(s--,a.active=s),0===q.length&&(s=null,a.active=null,f())},p.select=a.select=function(b,c){var d=i(b.slide);void 0===c&&(c=d>p.getCurrentIndex()?"next":"prev"),b.slide.index===s||a.$currentTransition?b&&b.slide.index!==s&&a.$currentTransition&&t.push(q[d]):h(b.slide,d,c)},a.indexOfSlide=function(a){return+a.slide.index},a.isActive=function(b){return a.active===b.slide.index},a.isPrevDisabled=function(){return 0===a.active&&a.noWrap()},a.isNextDisabled=function(){return a.active===q.length-1&&a.noWrap()},a.pause=function(){a.noPause||(o=!1,j())},a.play=function(){o||(o=!0,l())},a.$on("$destroy",function(){u=!0,j()}),a.$watch("noTransition",function(a){e.enabled(b,!a)}),a.$watch("interval",l),a.$watchCollection("slides",k),a.$watch("active",function(a){if
48992(angular.isNumber(a)&&s!==a){for(var b=0;b<q.length;b++)if(q[b].slide.index===a){a=b;break}var c=q[a];c&&(g(a),p.select(q[a]),s=a)}})}]).directive("uibCarousel",function(){return{transclude:!0,replace:!0,controller:"UibCarouselController",controllerAs:"carousel",templateUrl:function(a,b){return b.templateUrl||"uib/template/carousel/carousel.html"},scope:{active:"=",interval:"=",noTransition:"=",noPause:"=",noWrap:"&"}}}).directive("uibSlide",function(){return{require:"^uibCarousel",transclude:!0,replace:!0,templateUrl:function(a,b){return b.templateUrl||"uib/template/carousel/slide.html"},scope:{actual:"=?",index:"=?"},link:function(a,b,c,d){d.addSlide(a,b),a.$on("$destroy",function(){d.removeSlide(a)})}}}).animation(".item",["$animateCss",function(a){function b(a,b,c){a.removeClass(b),c&&c()}var c="uib-slideDirection";return{beforeAddClass:function(d,e,f){if("active"===e){var g=!1,h=d.data(c),i="next"===h?"left":"right",j=b.bind(this,d,i+" "+h,f);return d.addClass(h),a(d,{addClass:i}).start().done(j),function(){g=!0}}f()},beforeRemoveClass:function(d,e,f){if("active"===e){var g=!1,h=d.data(c),i="next"===h?"left":"right",j=b.bind(this,d,i,f);return a(d,{addClass:i}).start().done(j),function(){g=!0}}f()}}}]),angular.module("ui.bootstrap.dateparser",[]).service("uibDateParser",["$log","$locale","dateFilter","orderByFilter",function(a,b,c,d){function e(a,b){var c=[],e=a.split(""),f=a.indexOf("'");if(f>-1){var g=!1;a=a.split("");for(var h=f;h<a.length;h++)g?("'"===a[h]&&(h+1<a.length&&"'"===a[h+1]?(a[h+1]="$",e[h+1]=""):(e[h]="",g=!1)),a[h]="$"):"'"===a[h]&&(a[h]="$",e[h]="",g=!0);a=a.join("")}return angular.forEach(n,function(d){var f=a.indexOf(d.key);if(f>-1){a=a.split(""),e[f]="("+d.regex+")",a[f]="$";for(var g=f+1,h=f+d.key.length;h>g;g++)e[g]="",a[g]="$";a=a.join(""),c.push({index:f,key:d.key,apply:d[b],matcher:d.regex})}}),{regex:new RegExp("^"+e.join("")+"$"),map:d(c,"index")}}function f(a,b,c){return 1>c?!1:1===b&&c>28?29===c&&(a%4===0&&a%100!==0||a%400===0):3===b||5===b||8===b||10===b?31>c:!0}function g(a){return parseInt(a,10)}function h(a,b){return a&&b?l(a,b):a}function i(a,b){return a&&b?l(a,b,!0):a}function j(a,b){a=a.replace(/:/g,"");var c=Date.parse("Jan 01, 1970 00:00:00 "+a)/6e4;return isNaN(c)?b:c}function k(a,b){return a=new Date(a.getTime()),a.setMinutes(a.getMinutes()+b),a}function l(a,b,c){c=c?-1:1;var d=a.getTimezoneOffset(),e=j(b,d);return k(a,c*(e-d))}var m,n,o=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;this.init=function(){m=b.id,this.parsers={},this.formatters={},n=[{key:"yyyy",regex:"\\d{4}",apply:function(a){this.year=+a},formatter:function(a){var b=new Date;return b.setFullYear(Math.abs(a.getFullYear())),c(b,"yyyy")}},{key:"yy",regex:"\\d{2}",apply:function(a){a=+a,this.year=69>a?a+2e3:a+1900},formatter:function(a){var b=new Date;return b.setFullYear(Math.abs(a.getFullYear())),c(b,"yy")}},{key:"y",regex:"\\d{1,4}",apply:function(a){this.year=+a},formatter:function(a){var b=new Date;return b.setFullYear(Math.abs(a.getFullYear())),c(b,"y")}},{key:"M!",regex:"0?[1-9]|1[0-2]",apply:function(a){this.month=a-1},formatter:function(a){var b=a.getMonth();return/^[0-9]$/.test(b)?c(a,"MM"):c(a,"M")}},{key:"MMMM",regex:b.DATETIME_FORMATS.MONTH.join("|"),apply:function(a){this.month=b.DATETIME_FORMATS.MONTH.indexOf(a)},formatter:function(a){return c(a,"MMMM")}},{key:"MMM",regex:b.DATETIME_FORMATS.SHORTMONTH.join("|"),apply:function(a){this.month=b.DATETIME_FORMATS.SHORTMONTH.indexOf(a)},formatter:function(a){return c(a,"MMM")}},{key:"MM",regex:"0[1-9]|1[0-2]",apply:function(a){this.month=a-1},formatter:function(a){return c(a,"MM")}},{key:"M",regex:"[1-9]|1[0-2]",apply:function(a){this.month=a-1},formatter:function(a){return c(a,"M")}},{key:"d!",regex:"[0-2]?[0-9]{1}|3[0-1]{1}",apply:function(a){this.date=+a},formatter:function(a){var b=a.getDate();return/^[1-9]$/.test(b)?c(a,"dd"):c(a,"d")}},{key:"dd",regex:"[0-2][0-9]{1}|3[0-1]{1}",apply:function(a){this.date=+a},formatter:function(a){return c(a,"dd")}},{key:"d",regex:"[1-2]?[0-9]{1}|3[0-1]{1}",apply:function(a){this.date=+a},formatter:function(a){return c(a,"d")}},{key:"EEEE",regex:b.DATETIME_FORMATS.DAY.join("|"),formatter:function(a){return c(a,"EEEE")}},{key:"EEE",regex:b.DATETIME_FORMATS.SHORTDAY.join("|"),formatter:function(a){return c(a,"EEE")}},{key:"HH",regex:"(?:0|1)[0-9]|2[0-3]",apply:function(a){this.hours=+a},formatter:function(a){return c(a,"HH")}},{key:"hh",regex:"0[0-9]|1[0-2]",apply:function(a){this.hours=+a},formatter:function(a){return c(a,"hh")}},{key:"H",regex:"1?[0-9]|2[0-3]",apply:function(a){this.hours=+a},formatter:function(a){return c(a,"H")}},{key:"h",regex:"[0-9]|1[0-2]",apply:function(a){this.hours=+a},formatter:function(a){return c(a,"h")}},{key:"mm",regex:"[0-5][0-9]",apply:function(a){this.minutes=+a},formatter:function(a){return c(a,"mm")}},{key:"m",regex:"[0-9]|[1-5][0-9]",apply:function(a){this.minutes=+a},formatter:function(a){return c(a,"m")}},{key:"sss",regex:"[0-9][0-9][0-9]",apply:function(a){this.milliseconds=+a},formatter:function(a){return c(a,"sss")}},{key:"ss",regex:"[0-5][0-9]",apply:function(a){this.seconds=+a},formatter:function(a){return c(a,"ss")}},{key:"s",regex:"[0-9]|[1-5][0-9]",apply:function(a){this.seconds=+a},formatter:function(a){return c(a,"s")}},{key:"a",regex:b.DATETIME_FORMATS.AMPMS.join("|"),apply:function(a){12===this.hours&&(this.hours=0),"PM"===a&&(this.hours+=12)},formatter:function(a){return c(a,"a")}},{key:"Z",regex:"[+-]\\d{4}",apply:function(a){var b=a.match(/([+-])(\d{2})(\d{2})/),c=b[1],d=b[2],e=b[3];this.hours+=g(c+d),this.minutes+=g(c+e)},formatter:function(a){return c(a,"Z")}},{key:"ww",regex:"[0-4][0-9]|5[0-3]",formatter:function(a){return c(a,"ww")}},{key:"w",regex:"[0-9]|[1-4][0-9]|5[0-3]",formatter:function(a){return c(a,"w")}},{key:"GGGG",regex:b.DATETIME_FORMATS.ERANAMES.join("|").replace(/\s/g,"\\s"),formatter:function(a){return c(a,"GGGG")}},{key:"GGG",regex:b.DATETIME_FORMATS.ERAS.join("|"),formatter:function(a){return c(a,"GGG")}},{key:"GG",regex:b.DATETIME_FORMATS.ERAS.join("|"),formatter:function(a){return c(a,"GG")}},{key:"G",regex:b.DATETIME_FORMATS.ERAS.join("|"),formatter:function(a){return c(a,"G")}}]},this.init(),this.filter=function(a,c){if(!angular.isDate(a)||isNaN(a)||!c)return"";c=b.DATETIME_FORMATS[c]||c,b.id!==m&&this.init(),this.formatters[c]||(this.formatters[c]=e(c,"formatter"));var d=this.formatters[c],f=d.map,g=c;return f.reduce(function(b,c,d){var e=g.match(new RegExp("(.*)"+c.key));e&&angular.isString(e[1])&&(b+=e[1],g=g.replace(e[1]+c.key,""));var h=d===f.length-1?g:"";return c.apply?b+c.apply.call(null,a)+h:b+h},"")},this.parse=function(c,d,g){if(!angular.isString(c)||!d)return c;d=b.DATETIME_FORMATS[d]||d,d=d.replace(o,"\\$&"),b.id!==m&&this.init(),this.parsers[d]||(this.parsers[d]=e(d,"apply"));var h=this.parsers[d],i=h.regex,j=h.map,k=c.match(i),l=!1;if(k&&k.length){var n,p;angular.isDate(g)&&!isNaN(g.getTime())?n={year:g.getFullYear(),month:g.getMonth(),date:g.getDate(),hours:g.getHours(),minutes:g.getMinutes(),seconds:g.getSeconds(),milliseconds:g.getMilliseconds()}:(g&&a.warn("dateparser:","baseDate is not a valid date"),n={year:1900,month:0,date:1,hours:0,minutes:0,seconds:0,milliseconds:0});for(var q=1,r=k.length;r>q;q++){var s=j[q-1];"Z"===s.matcher&&(l=!0),s.apply&&s.apply.call(n,k[q])}
vendor: 20,440 bytes, lines 48992-48993
48992var t=l?Date.prototype.setUTCFullYear:Date.prototype.setFullYear,u=l?Date.prototype.setUTCHours:Date.prototype.setHours;return f(n.year,n.month,n.date)&&(!angular.isDate(g)||isNaN(g.getTime())||l?(p=new Date(0),t.call(p,n.year,n.month,n.date),u.call(p,n.hours||0,n.minutes||0,n.seconds||0,n.milliseconds||0)):(p=new Date(g),t.call(p,n.year,n.month,n.date),u.call(p,n.hours,n.minutes,n.seconds,n.milliseconds))),p}},this.toTimezone=h,this.fromTimezone=i,this.timezoneToOffset=j,this.addDateMinutes=k,this.convertTimezoneToLocal=l}]),angular.module("ui.bootstrap.isClass",[]).directive("uibIsClass",["$animate",function(a){var b=/^\s*([\s\S]+?)\s+on\s+([\s\S]+?)\s*$/,c=/^\s*([\s\S]+?)\s+for\s+([\s\S]+?)\s*$/;return{restrict:"A",compile:function(d,e){function f(a,b,c){i.push(a),j.push({scope:a,element:b}),o.forEach(function(b,c){g(b,a)}),a.$on("$destroy",h)}function g(b,d){var e=b.match(c),f=d.$eval(e[1]),g=e[2],h=k[b];if(!h){var i=function(b){var c=null;j.some(function(a){var d=a.scope.$eval(m);return d===b?(c=a,!0):void 0}),h.lastActivated!==c&&(h.lastActivated&&a.removeClass(h.lastActivated.element,f),c&&a.addClass(c.element,f),h.lastActivated=c)};k[b]=h={lastActivated:null,scope:d,watchFn:i,compareWithExp:g,watcher:d.$watch(g,i)}}h.watchFn(d.$eval(g))}function h(a){var b=a.targetScope,c=i.indexOf(b);if(i.splice(c,1),j.splice(c,1),i.length){var d=i[0];angular.forEach(k,function(a){a.scope===b&&(a.watcher=d.$watch(a.compareWithExp,a.watchFn),a.scope=d)})}else k={}}var i=[],j=[],k={},l=e.uibIsClass.match(b),m=l[2],n=l[1],o=n.split(",");return f}}}]),angular.module("ui.bootstrap.datepicker",["ui.bootstrap.dateparser","ui.bootstrap.isClass"]).value("$datepickerSuppressError",!1).value("$datepickerLiteralWarning",!0).constant("uibDatepickerConfig",{datepickerMode:"day",formatDay:"dd",formatMonth:"MMMM",formatYear:"yyyy",formatDayHeader:"EEE",formatDayTitle:"MMMM yyyy",formatMonthTitle:"yyyy",maxDate:null,maxMode:"year",minDate:null,minMode:"day",ngModelOptions:{},shortcutPropagation:!1,showWeeks:!0,yearColumns:5,yearRows:4}).controller("UibDatepickerController",["$scope","$attrs","$parse","$interpolate","$locale","$log","dateFilter","uibDatepickerConfig","$datepickerLiteralWarning","$datepickerSuppressError","uibDateParser",function(a,b,c,d,e,f,g,h,i,j,k){function l(b){a.datepickerMode=b,a.datepickerOptions.datepickerMode=b}var m=this,n={$setViewValue:angular.noop},o={},p=[];!!b.datepickerOptions;a.datepickerOptions||(a.datepickerOptions={}),this.modes=["day","month","year"],["customClass","dateDisabled","datepickerMode","formatDay","formatDayHeader","formatDayTitle","formatMonth","formatMonthTitle","formatYear","maxDate","maxMode","minDate","minMode","showWeeks","shortcutPropagation","startingDay","yearColumns","yearRows"].forEach(function(b){switch(b){case"customClass":case"dateDisabled":a[b]=a.datepickerOptions[b]||angular.noop;break;case"datepickerMode":a.datepickerMode=angular.isDefined(a.datepickerOptions.datepickerMode)?a.datepickerOptions.datepickerMode:h.datepickerMode;break;case"formatDay":case"formatDayHeader":case"formatDayTitle":case"formatMonth":case"formatMonthTitle":case"formatYear":m[b]=angular.isDefined(a.datepickerOptions[b])?d(a.datepickerOptions[b])(a.$parent):h[b];break;case"showWeeks":case"shortcutPropagation":case"yearColumns":case"yearRows":m[b]=angular.isDefined(a.datepickerOptions[b])?a.datepickerOptions[b]:h[b];break;case"startingDay":angular.isDefined(a.datepickerOptions.startingDay)?m.startingDay=a.datepickerOptions.startingDay:angular.isNumber(h.startingDay)?m.startingDay=h.startingDay:m.startingDay=(e.DATETIME_FORMATS.FIRSTDAYOFWEEK+8)%7;break;case"maxDate":case"minDate":a.$watch("datepickerOptions."+b,function(a){a?angular.isDate(a)?m[b]=k.fromTimezone(new Date(a),o.timezone):(i&&f.warn("Literal date support has been deprecated, please switch to date object usage"),m[b]=new Date(g(a,"medium"))):m[b]=h[b]?k.fromTimezone(new Date(h[b]),o.timezone):null,m.refreshView()});break;case"maxMode":case"minMode":a.datepickerOptions[b]?a.$watch(function(){return a.datepickerOptions[b]},function(c){m[b]=a[b]=angular.isDefined(c)?c:datepickerOptions[b],("minMode"===b&&m.modes.indexOf(a.datepickerOptions.datepickerMode)<m.modes.indexOf(m[b])||"maxMode"===b&&m.modes.indexOf(a.datepickerOptions.datepickerMode)>m.modes.indexOf(m[b]))&&(a.datepickerMode=m[b],a.datepickerOptions.datepickerMode=m[b])}):m[b]=a[b]=h[b]||null}}),a.uniqueId="datepicker-"+a.$id+"-"+Math.floor(1e4*Math.random()),a.disabled=angular.isDefined(b.disabled)||!1,angular.isDefined(b.ngDisabled)&&p.push(a.$parent.$watch(b.ngDisabled,function(b){a.disabled=b,m.refreshView()})),a.isActive=function(b){return 0===m.compare(b.date,m.activeDate)?(a.activeDateId=b.uid,!0):!1},this.init=function(b){n=b,o=b.$options||h.ngModelOptions,a.datepickerOptions.initDate?(m.activeDate=k.fromTimezone(a.datepickerOptions.initDate,o.timezone)||new Date,a.$watch("datepickerOptions.initDate",function(a){a&&(n.$isEmpty(n.$modelValue)||n.$invalid)&&(m.activeDate=k.fromTimezone(a,o.timezone),m.refreshView())})):m.activeDate=new Date;var c=n.$modelValue?new Date(n.$modelValue):new Date;this.activeDate=isNaN(c)?k.fromTimezone(new Date,o.timezone):k.fromTimezone(c,o.timezone),n.$render=function(){m.render()}},this.render=function(){if(n.$viewValue){var a=new Date(n.$viewValue),b=!isNaN(a);b?this.activeDate=k.fromTimezone(a,o.timezone):j||f.error('Datepicker directive: "ng-model" value must be a Date object')}this.refreshView()},this.refreshView=function(){if(this.element){a.selectedDt=null,this._refreshView(),a.activeDt&&(a.activeDateId=a.activeDt.uid);var b=n.$viewValue?new Date(n.$viewValue):null;b=k.fromTimezone(b,o.timezone),n.$setValidity("dateDisabled",!b||this.element&&!this.isDisabled(b))}},this.createDateObject=function(b,c){var d=n.$viewValue?new Date(n.$viewValue):null;d=k.fromTimezone(d,o.timezone);var e=new Date;e=k.fromTimezone(e,o.timezone);var f=this.compare(b,e),g={date:b,label:k.filter(b,c),selected:d&&0===this.compare(b,d),disabled:this.isDisabled(b),past:0>f,current:0===f,future:f>0,customClass:this.customClass(b)||null};return d&&0===this.compare(b,d)&&(a.selectedDt=g),m.activeDate&&0===this.compare(g.date,m.activeDate)&&(a.activeDt=g),g},this.isDisabled=function(b){return a.disabled||this.minDate&&this.compare(b,this.minDate)<0||this.maxDate&&this.compare(b,this.maxDate)>0||a.dateDisabled&&a.dateDisabled({date:b,mode:a.datepickerMode})},this.customClass=function(b){return a.customClass({date:b,mode:a.datepickerMode})},this.split=function(a,b){for(var c=[];a.length>0;)c.push(a.splice(0,b));return c},a.select=function(b){if(a.datepickerMode===m.minMode){var c=n.$viewValue?k.fromTimezone(new Date(n.$viewValue),o.timezone):new Date(0,0,0,0,0,0,0);c.setFullYear(b.getFullYear(),b.getMonth(),b.getDate()),c=k.toTimezone(c,o.timezone),n.$setViewValue(c),n.$render()}else m.activeDate=b,l(m.modes[m.modes.indexOf(a.datepickerMode)-1]),a.$emit("uib:datepicker.mode");a.$broadcast("uib:datepicker.focus")},a.move=function(a){var b=m.activeDate.getFullYear()+a*(m.step.years||0),c=m.activeDate.getMonth()+a*(m.step.months||0);m.activeDate.setFullYear(b,c,1),m.refreshView()},a.toggleMode=function(b){b=b||1,a.datepickerMode===m.maxMode&&1===b||a.datepickerMode===m.minMode&&-1===b||(l(m.modes[m.modes.indexOf(a.datepickerMode)+b]),a.$emit("uib:datepicker.mode"))},a.keys={13:"enter",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down"};var q=function(){m.element[0].focus()};a.$on("uib:datepicker.focus",q),a.keydown=function(b){var c=a.keys[b.which];if(c&&!b.shiftKey&&!b.altKey&&!a.disabled)if(b.preventDefault(),m.shortcutPropagation||b.stopPropagation(),"enter"===c||"space"===c){if(m.isDisabled(m.activeDate))return;a.select(m.activeDate)}else!b.ctrlKey||"up"!==c&&"down"!==c?(m.handleKeyDown(c,b),m.refreshView()):a.toggleMode("up"===c?1:-1)},a.$on("$destroy",function(){for(;p.length;)p.shift()()})}]).controller("UibDaypickerController",["$scope","$element","dateFilter",function(a,b,c){function d(a,b){return 1!==b||a%4!==0||a%100===0&&a%400!==0?f[b]:29}function e(a){var b=new Date(a);b.setDate(b.getDate()+4-(b.getDay()||7));var c=b.getTime();return b.setMonth(0),b.setDate(1),Math.floor(Math.round((c-b)/864e5)/7)+1}var f=[31,28,31,30,31,30,31,31,30,31,30,31];this.step={months:1},this.element=b,this.init=function(b){angular.extend(b,this),a.showWeeks=b.showWeeks,b.refreshView()},this.getDates=function(a,b){for(var c,d=new Array(b),e=new Date(a),f=0;b>f;)c=new Date(e),d[f++]=c,e.setDate(e.getDate()+1);return d},this._refreshView=function(){var b=this.activeDate.getFullYear(),d=this.activeDate.getMonth(),f=new Date(this.activeDate);f.setFullYear(b,d,1);var g=this.startingDay-f.getDay(),h=g>0?7-g:-g,i=new Date(f);h>0&&i.setDate(-h+1);for(var j=this.getDates(i,42),k=0;42>k;k++)j[k]=angular.extend(this.createDateObject(j[k],this.formatDay),{secondary:j[k].getMonth()!==d,uid:a.uniqueId+"-"+k});a.labels=new Array(7);for(var l=0;7>l;l++)a.labels[l]={abbr:c(j[l].date,this.formatDayHeader),full:c(j[l].date,"EEEE")};if(a.title=c(this.activeDate,this.formatDayTitle),a.rows=this.split(j,7),a.showWeeks){a.weekNumbers=[];for(var m=(11-this.startingDay)%7,n=a.rows.length,o=0;n>o;o++)a.weekNumbers.push(e(a.rows[o][m].date))}},this.compare=function(a,b){var c=new Date(a.getFullYear(),a.getMonth(),a.getDate()),d=new Date(b.getFullYear(),b.getMonth(),b.getDate());return c.setFullYear(a.getFullYear()),d.setFullYear(b.getFullYear()),c-d},this.handleKeyDown=function(a,b){var c=this.activeDate.getDate();if("left"===a)c-=1;else if("up"===a)c-=7;else if("right"===a)c+=1;else if("down"===a)c+=7;else if("pageup"===a||"pagedown"===a){var e=this.activeDate.getMonth()+("pageup"===a?-1:1);this.activeDate.setMonth(e,1),c=Math.min(d(this.activeDate.getFullYear(),this.activeDate.getMonth()),c)}else"home"===a?c=1:"end"===a&&(c=d(this.activeDate.getFullYear(),this.activeDate.getMonth()));this.activeDate.setDate(c)}}]).controller("UibMonthpickerController",["$scope","$element","dateFilter",function(a,b,c){this.step={years:1},this.element=b,this.init=function(a){angular.extend(a,this),a.refreshView()},this._refreshView=function(){for(var b,d=new Array(12),e=this.activeDate.getFullYear(),f=0;12>f;f++)b=new Date(this.activeDate),b.setFullYear(e,f,1),d[f]=angular.extend(this.createDateObject(b,this.formatMonth),{uid:a.uniqueId+"-"+f});a.title=c(this.activeDate,this.formatMonthTitle),a.rows=this.split(d,3)},this.compare=function(a,b){var c=new Date(a.getFullYear(),a.getMonth()),d=new Date(b.getFullYear(),b.getMonth());return c.setFullYear(a.getFullYear()),d.setFullYear(b.getFullYear()),c-d},this.handleKeyDown=function(a,b){var c=this.activeDate.getMonth();if("left"===a)c-=1;else if("up"===a)c-=3;else if("right"===a)c+=1;else if("down"===a)c+=3;else if("pageup"===a||"pagedown"===a){var d=this.activeDate.getFullYear()+("pageup"===a?-1:1);this.activeDate.setFullYear(d)}else"home"===a?c=0:"end"===a&&(c=11);this.activeDate.setMonth(c)}}]).controller("UibYearpickerController",["$scope","$element","dateFilter",function(a,b,c){function d(a){return parseInt((a-1)/f,10)*f+1}var e,f;this.element=b,this.yearpickerInit=function(){e=this.yearColumns,f=this.yearRows*e,this.step={years:f}},this._refreshView=function(){for(var b,c=new Array(f),g=0,h=d(this.activeDate.getFullYear());f>g;g++)b=new Date(this.activeDate),b.setFullYear(h+g,0,1),c[g]=angular.extend(this.createDateObject(b,this.formatYear),{uid:a.uniqueId+"-"+g});a.title=[c[0].label,c[f-1].label].join(" - "),a.rows=this.split(c,e),a.columns=e},this.compare=function(a,b){return a.getFullYear()-b.getFullYear()},this.handleKeyDown=function(a,b){var c=this.activeDate.getFullYear();"left"===a?c-=1:"up"===a?c-=e:"right"===a?c+=1:"down"===a?c+=e:"pageup"===a||"pagedown"===a?c+=("pageup"===a?-1:1)*f:"home"===a?c=d(this.activeDate.getFullYear()):"end"===a&&(c=d(this.activeDate.getFullYear())+f-1),this.activeDate.setFullYear(c)}}]).directive("uibDatepicker",function(){return{replace:!0,templateUrl:function(a,b){return b.templateUrl||"uib/template/datepicker/datepicker.html"},scope:{datepickerOptions:"=?"},require:["uibDatepicker","^ngModel"],controller:"UibDatepickerController",controllerAs:"datepicker",link:function(a,b,c,d){var e=d[0],f=d[1];e.init(f)}}}).directive("uibDaypicker",function(){return{replace:!0,templateUrl:function(a,b){return b.templateUrl||"uib/template/datepicker/day.html"},require:["^uibDatepicker","uibDaypicker"],controller:"UibDaypickerController",link:function(a,b,c,d){var e=d[0],f=d[1];f.init(e)}}}).directive("uibMonthpicker",function(){return{replace:!0,templateUrl:function(a,b){return b.templateUrl||"uib/template/datepicker/month.html"},require:["^uibDatepicker","uibMonthpicker"],controller:"UibMonthpickerController",link:function(a,b,c,d){var e=d[0],f=d[1];f.init(e)}}}).directive("uibYearpicker",function(){return{replace:!0,templateUrl:function(a,b){return b.templateUrl||"uib/template/datepicker/year.html"},require:["^uibDatepicker","uibYearpicker"],controller:"UibYearpickerController",link:function(a,b,c,d){var e=d[0];angular.extend(e,d[1]),e.yearpickerInit(),e.refreshView()}}}),angular.module("ui.bootstrap.position",[]).factory("$uibPosition",["$document","$window",function(a,b){var c,d,e={normal:/(auto|scroll)/,hidden:/(auto|scroll|hidden)/},f={auto:/\s?auto?\s?/i,primary:/^(top|bottom|left|right)$/,secondary:/^(top|bottom|left|right|center)$/,vertical:/^(top|bottom)$/},g=/(HTML|BODY)/;return{getRawNode:function(a){return a.nodeName?a:a[0]||a},parseStyle:function(a){return a=parseFloat(a),isFinite(a)?a:0},offsetParent:function(c){function d(a){return"static"===(b.getComputedStyle(a).position||"static")}c=this.getRawNode(c);for(var e=c.offsetParent||a[0].documentElement;e&&e!==a[0].documentElement&&d(e);)e=e.offsetParent;return e||a[0].documentElement},scrollbarWidth:function(e){if(e){if(angular.isUndefined(d)){var f=a.find("body");f.addClass("uib-position-body-scrollbar-measure"),d=b.innerWidth-f[0].clientWidth,d=isFinite(d)?d:0,f.removeClass("uib-position-body-scrollbar-measure")}return d}if(angular.isUndefined(c)){var g=angular.element('<div class="uib-position-scrollbar-measure"></div>');a.find("body").append(g),c=g[0].offsetWidth-g[0].clientWidth,c=isFinite(c)?c:0,g.remove()}return c},scrollbarPadding:function(a){a=this.getRawNode(a);var c=b.getComputedStyle(a),d=this.parseStyle(c.paddingRight),e=this.parseStyle(c.paddingBottom),f=this.scrollParent(a,!1,!0),h=this.scrollbarWidth(f,g.test(f.tagName));return{scrollbarWidth:h,widthOverflow:f.scrollWidth>f.clientWidth,right:d+h,originalRight:d,heightOverflow:f.scrollHeight>f.clientHeight,
48993bottom:e+h,originalBottom:e}},isScrollable:function(a,c){a=this.getRawNode(a);var d=c?e.hidden:e.normal,f=b.getComputedStyle(a);return d.test(f.overflow+f.overflowY+f.overflowX)},scrollParent:function(c,d,f){c=this.getRawNode(c);var g=d?e.hidden:e.normal,h=a[0].documentElement,i=b.getComputedStyle(c);if(f&&g.test(i.overflow+i.overflowY+i.overflowX))return c;var j="absolute"===i.position,k=c.parentElement||h;if(k===h||"fixed"===i.position)return h;for(;k.parentElement&&k!==h;){var l=b.getComputedStyle(k);if(j&&"static"!==l.position&&(j=!1),!j&&g.test(l.overflow+l.overflowY+l.overflowX))break;k=k.parentElement}return k},position:function(c,d){c=this.getRawNode(c);var e=this.offset(c);if(d){var f=b.getComputedStyle(c);e.top-=this.parseStyle(f.marginTop),e.left-=this.parseStyle(f.marginLeft)}var g=this.offsetParent(c),h={top:0,left:0};return g!==a[0].documentElement&&(h=this.offset(g),h.top+=g.clientTop-g.scrollTop,h.left+=g.clientLeft-g.scrollLeft),{width:Math.round(angular.isNumber(e.width)?e.width:c.offsetWidth),height:Math.round(angular.isNumber(e.height)?e.height:c.offsetHeight),top:Math.round(e.top-h.top),left:Math.round(e.left-h.left)}},offset:function(c){c=this.getRawNode(c);var d=c.getBoundingClientRect();return{width:Math.round(angular.isNumber(d.width)?d.width:c.offsetWidth),height:Math.round(angular.isNumber(d.height)?d.height:c.offsetHeight),top:Math.round(d.top+(b.pageYOffset||a[0].documentElement.scrollTop)),left:Math.round(d.left+(b.pageXOffset||a[0].documentElement.scrollLeft))}},viewportOffset:function(c,d,e){c=this.getRawNode(c),e=e!==!1;var f=c.getBoundingClientRect(),g={top:0,left:0,bottom:0,right:0},h=d?a[0].documentElement:this.scrollParent(c),i=h.getBoundingClientRect();if(g.top=i.top+h.clientTop,g.left=i.left+h.clientLeft,h===a[0].documentElement&&(g.top+=b.pageYOffset,g.left+=b.pageXOffset),g.bottom=g.top+h.clientHeight,g.right=g.left+h.clientWidth,e){var j=b.getComputedStyle(h);g.top+=this.parseStyle(j.paddingTop),g.bottom-=this.parseStyle(j.paddingBottom),g.left+=this.parseStyle(j.paddingLeft),g.right-=this.parseStyle(j.paddingRight)}return{top:Math.round(f.top-g.top),bottom:Math.round(g.bottom-f.bottom),left:Math.round(f.left-g.left),right:Math.round(g.right-f.right)}},parsePlacement:function(a){var b=f.auto.test(a);return b&&(a=a.replace(f.auto,"")),a=a.split("-"),a[0]=a[0]||"top",f.primary.test(a[0])||(a[0]="top"),a[1]=a[1]||"center",f.secondary.test(a[1])||(a[1]="center"),b?a[2]=!0:a[2]=!1,a},positionElements:function(a,c,d,e){a=this.getRawNode(a),c=this.getRawNode(c);var g=angular.isDefined(c.offsetWidth)?c.offsetWidth:c.prop("offsetWidth"),h=angular.isDefined(c.offsetHeight)?c.offsetHeight:c.prop("offsetHeight");d=this.parsePlacement(d);var i=e?this.offset(a):this.position(a),j={top:0,left:0,placement:""};if(d[2]){var k=this.viewportOffset(a,e),l=b.getComputedStyle(c),m={width:g+Math.round(Math.abs(this.parseStyle(l.marginLeft)+this.parseStyle(l.marginRight))),height:h+Math.round(Math.abs(this.parseStyle(l.marginTop)+this.parseStyle(l.marginBottom)))};if(d[0]="top"===d[0]&&m.height>k.top&&m.height<=k.bottom?"bottom":"bottom"===d[0]&&m.height>k.bottom&&m.height<=k.top?"top":"left"===d[0]&&m.width>k.left&&m.width<=k.right?"right":"right"===d[0]&&m.width>k.right&&m.width<=k.left?"left":d[0],d[1]="top"===d[1]&&m.height-i.height>k.bottom&&m.height-i.height<=k.top?"bottom":"bottom"===d[1]&&m.height-i.height>k.top&&m.height-i.height<=k.bottom?"top":"left"===d[1]&&m.width-i.width>k.right&&m.width-i.width<=k.left?"right":"right"===d[1]&&m.width-i.width>k.left&&m.width-i.width<=k.right?"left":d[1],"center"===d[1])if(f.vertical.test(d[0])){var n=i.width/2-g/2;k.left+n<0&&m.width-i.width<=k.right?d[1]="left":k.right+n<0&&m.width-i.width<=k.left&&(d[1]="right")}else{var o=i.height/2-m.height/2;k.top+o<0&&m.height-i.height<=k.bottom?d[1]="top":k.bottom+o<0&&m.height-i.height<=k.top&&(d[1]="bottom")}}switch(d[0]){case"top":j.top=i.top-h;break;case"bottom":j.top=i.top+i.height;break;case"left":j.left=i.left-g;break;case"right":j.left=i.left+i.width}switch(d[1]){case"top":j.top=i.top;break;case"bottom":j.top=i.top+i.height-h;break;case"left":j.left=i.left;break;case"right":j.left=i.left+i.width-g;break;case"center":f.vertical.test(d[0])?j.left=i.left+i.width/2-g/2:j.top=i.top+i.height/2-h/2}return j.top=Math.round(j.top),j.left=Math.round(j.left),j.placement="center"===d[1]?d[0]:d[0]+"-"+d[1],j},positionArrow:function(a,c){a=this.getRawNode(a);var d=a.querySelector(".tooltip-inner, .popover-inner");if(d){var e=angular.element(d).hasClass("tooltip-inner"),g=e?a.querySelector(".tooltip-arrow"):a.querySelector(".arrow");if(g){var h={top:"",bottom:"",left:"",right:""};if(c=this.parsePlacement(c),"center"===c[1])return void angular.element(g).css(h);var i="border-"+c[0]+"-width",j=b.getComputedStyle(g)[i],k="border-";k+=f.vertical.test(c[0])?c[0]+"-"+c[1]:c[1]+"-"+c[0],k+="-radius";var l=b.getComputedStyle(e?d:a)[k];switch(c[0]){case"top":h.bottom=e?"0":"-"+j;break;case"bottom":h.top=e?"0":"-"+j;break;case"left":h.right=e?"0":"-"+j;break;case"right":h.left=e?"0":"-"+j}h[c[1]]=l,angular.element(g).css(h)}}}}}]),angular.module("ui.bootstrap.datepickerPopup",["ui.bootstrap.datepicker","ui.bootstrap.position"]).value("$datepickerPopupLiteralWarning",!0).constant("uibDatepickerPopupConfig",{altInputFormats:[],appendToBody:!1,clearText:"Clear",closeOnDateSelection:!0,closeText:"Done",currentText:"Today",datepickerPopup:"yyyy-MM-dd",datepickerPopupTemplateUrl:"uib/template/datepickerPopup/popup.html",datepickerTemplateUrl:"uib/template/datepicker/datepicker.html",html5Types:{date:"yyyy-MM-dd","datetime-local":"yyyy-MM-ddTHH:mm:ss.sss",month:"yyyy-MM"},onOpenFocus:!0,showButtonBar:!0,placement:"auto bottom-left"}).controller("UibDatepickerPopupController",["$scope","$element","$attrs","$compile","$log","$parse","$
48993window","$document","$rootScope","$uibPosition","dateFilter","uibDateParser","uibDatepickerPopupConfig","$timeout","uibDatepickerConfig","$datepickerPopupLiteralWarning",function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p){function q(b){var c=l.parse(b,w,a.date);if(isNaN(c))for(var d=0;d<I.length;d++)if(c=l.parse(b,I[d],a.date),!isNaN(c))return c;return c}function r(a){if(angular.isNumber(a)&&(a=new Date(a)),!a)return null;if(angular.isDate(a)&&!isNaN(a))return a;if(angular.isString(a)){var b=q(a);if(!isNaN(b))return l.toTimezone(b,J)}return F.$options&&F.$options.allowInvalid?a:void 0}function s(a,b){var d=a||b;return c.ngRequired||d?(angular.isNumber(d)&&(d=new Date(d)),d?angular.isDate(d)&&!isNaN(d)?!0:angular.isString(d)?!isNaN(q(b)):!1:!0):!0}function t(c){if(a.isOpen||!a.disabled){var d=H[0],e=b[0].contains(c.target),f=void 0!==d.contains&&d.contains(c.target);!a.isOpen||e||f||a.$apply(function(){a.isOpen=!1})}}function u(c){27===c.which&&a.isOpen?(c.preventDefault(),c.stopPropagation(),a.$apply(function(){a.isOpen=!1}),b[0].focus()):40!==c.which||a.isOpen||(c.preventDefault(),c.stopPropagation(),a.$apply(function(){a.isOpen=!0}))}function v(){if(a.isOpen){var d=angular.element(H[0].querySelector(".uib-datepicker-popup")),e=c.popupPlacement?c.popupPlacement:m.placement,f=j.positionElements(b,d,e,y);d.css({top:f.top+"px",left:f.left+"px"}),d.hasClass("uib-position-measure")&&d.removeClass("uib-position-measure")}}var w,x,y,z,A,B,C,D,E,F,G,H,I,J,K=!1,L=[];this.init=function(e){if(F=e,G=e.$options,x=angular.isDefined(c.closeOnDateSelection)?a.$parent.$eval(c.closeOnDateSelection):m.closeOnDateSelection,y=angular.isDefined(c.datepickerAppendToBody)?a.$parent.$eval(c.datepickerAppendToBody):m.appendToBody,z=angular.isDefined(c.onOpenFocus)?a.$parent.$eval(c.onOpenFocus):m.onOpenFocus,A=angular.isDefined(c.datepickerPopupTemplateUrl)?c.datepickerPopupTemplateUrl:m.datepickerPopupTemplateUrl,B=angular.isDefined(c.datepickerTemplateUrl)?c.datepickerTemplateUrl:m.datepickerTemplateUrl,I=angular.isDefined(c.altInputFormats)?a.$parent.$eval(c.altInputFormats):m.altInputFormats,a.showButtonBar=angular.isDefined(c.showButtonBar)?a.$parent.$eval(c.showButtonBar):m.showButtonBar,m.html5Types[c.type]?(w=m.html5Types[c.type],K=!0):(w=c.uibDatepickerPopup||m.datepickerPopup,c.$observe("uibDatepickerPopup",function(a,b){var c=a||m.datepickerPopup;if(c!==w&&(w=c,F.$modelValue=null,!w))throw new Error("uibDatepickerPopup must have a date format specified.")})),!w)throw new Error("uibDatepickerPopup must have a date format specified.");if(K&&c.uibDatepickerPopup)throw new Error("HTML5 date input types do not support custom formats.");C=angular.element("<div uib-datepicker-popup-wrap><div uib-datepicker></div></div>"),G?(J=G.timezone,a.ngModelOptions=angular.copy(G),a.ngModelOptions.timezone=null,a.ngModelOptions.updateOnDefault===!0&&(a.ngModelOptions.updateOn=a.ngModelOptions.updateOn?a.ngModelOptions.updateOn+" default":"default"),C.attr("ng-model-options","ngModelOptions")):J=null,C.attr({"ng-model":"date","ng-change":"dateSelection(date)","template-url":A}),D=angular.element(C.children()[0]),D.attr("template-url",B),a.datepickerOptions||(a.datepickerOptions={}),K&&"month"===c.type&&(a.datepickerOptions.datepickerMode="month",a.datepickerOptions.minMode="month"),D.attr("datepicker-options","datepickerOptions"),K?F.$formatters.push(function(b){return a.date=l.fromTimezone(b,J),b}):(F.$$parserName="date",F.$validators.date=s,F.$parsers.unshift(r),F.$formatters.push(function(b){return F.$isEmpty(b)?(a.date=b,b):(angular.isNumber(b)&&(b=new Date(b)),a.date=l.fromTimezone(b,J),l.filter(a.date,w))})),F.$viewChangeListeners.push(function(){a.date=q(F.$viewValue)}),b.on("keydown",u),H=d(C)(a),C.remove(),y?h.find("body").append(H):b.after(H),a.$on("$destroy",function(){for(a.isOpen===!0&&(i.$$phase||a.$apply(function(){a.isOpen=!1})),H.remove(),b.off("keydown",u),h.off("click",t),E&&E.off("scroll",v),angular.element(g).off("resize",v);L.length;)L.shift()()})},a.getText=function(b){return a[b+"Text"]||m[b+"Text"]},a.isDisabled=function(b){"today"===b&&(b=l.fromTimezone(new Date,J));var c={};return angular.forEach(["minDate","maxDate"],function(b){a.datepickerOptions[b]?angular.isDate(a.datepickerOptions[b])?c[b]=l.fromTimezone(new Date(a.datepickerOptions[b]),J):(p&&e.warn("Literal date support has been deprecated, please switch to date object usage"),c[b]=new Date(k(a.datepickerOptions[b],"medium"))):c[b]=null}),a.datepickerOptions&&c.minDate&&a.compare(b,c.minDate)<0||c.maxDate&&a.compare(b,c.maxDate)>0},a.compare=function(a,b){return new Date(a.getFullYear(),a.getMonth(),a.getDate())-new Date(b.getFullYear(),b.getMonth(),b.getDate())},a.dateSelection=function(c){angular.isDefined(c)&&(a.date=c);var d=a.date?l.filter(a.date,w):null;b.val(d),F.$setViewValue(d),x&&(a.isOpen=!1,b[0].focus())},a.keydown=function(c){27===c.which&&(c.stopPropagation(),a.isOpen=!1,b[0].focus())},a.select=function(b,c){if(c.stopPropagation(),"today"===b){var d=new Date;angular.isDate(a.date)?(b=new Date(a.date),b.setFullYear(d.getFullYear(),d.getMonth(),d.getDate())):b=new Date(d.setHours(0,0,0,0))}a.dateSelection(b)},a.close=function(c){c.stopPropagation(),a.isOpen=!1,b[0].focus()},a.disabled=angular.isDefined(c.disabled)||!1,c.ngDisabled&&L.push(a.$parent.$watch(f(c.ngDisabled),function(b){a.disabled=b})),a.$watch("isOpen",function(d){d?a.disabled?a.isOpen=!1:n(function(){v(),z&&a.$broadcast("uib:datepicker.focus"),h.on("click",t);var d=c.popupPlacement?c.popupPlacement:m.placement;y||j.parsePlacement(d)[2]?(E=E||angular.element(j.scrollParent(b)),E&&E.on("scroll",v)):E=null,angular.element(g).on("resize",v)},0,!1):(h.off("click",t),E&&E.off("scroll",v),angular.element(g).off("resize",v))}),a.$on("uib:datepicker.mode",function(){n(v,0,!1)})}]).directive("uibDatepickerPopup",function(){return{require:["ngModel","uibDatepickerPopup"],controller:"UibDatepickerPopupController",scope:{datepickerOptions:"=?",isOpen:"=?",currentText:"@",clearText:"@",closeText:"@"},link:function(a,b,c,d){var e=d[0],f=d[1];f.init(e)}}}).directive("uibDatepickerPopupWrap",function(){return{replace:!0,transclude:!0,templateUrl:function(a,b){return b.templateUrl||"uib/template/datepickerPopup/popup.html"}}}),angular.module("ui.bootstrap.debounce",[]).factory("$$debounce",["$timeout",function(a){return function(b,c){var d;return function(){var e=this,f=Array.prototype.slice.call(arguments);d&&a.cancel(d),d=a(function(){b.apply(e,f)},c)}}}]),angular.module("ui.bootstrap.dropdown",["ui.bootstrap.position"]).constant("uibDropdownConfig",{appendToOpenClass:"uib-dropdown-open",openClass:"open"}).service("uibDropdownService",["$document","$rootScope",function(a,b){var c=null;
48993this.open=function(b,f){c||(a.on("click",d),f.on("keydown",e)),c&&c!==b&&(c.isOpen=!1),c=b},this.close=function(b,f){c===b&&(c=null,a.off("click",d),f.off("keydown",e))};var d=function(a){if(c&&!(a&&"disabled"===c.getAutoClose()||a&&3===a.which)){var d=c.getToggleElement();if(!(a&&d&&d[0].contains(a.target))){var e=c.getDropdownElement();a&&"outsideClick"===c.getAutoClose()&&e&&e[0].contains(a.target)||(c.isOpen=!1,b.$$phase||c.$apply())}}},e=function(a){27===a.which?(a.stopPropagation(),c.focusToggleElement(),d()):c.isKeynavEnabled()&&-1!==[38,40].indexOf(a.which)&&c.isOpen&&(a.preventDefault(),a.stopPropagation(),c.focusDropdownEntry(a.which))}}]).controller("UibDropdownController",["$scope","$element","$attrs","$parse","uibDropdownConfig","uibDropdownService","$animate","$uibPosition","$document","$compile","$templateRequest",function(a,b,c,d,e,f,g,h,i,j,k){var l,m,n=this,o=a.$new(),p=e.appendToOpenClass,q=e.openClass,r=angular.noop,s=c.onToggle?d(c.onToggle):angular.noop,t=!1,u=null,v=!1,w=i.find("body");b.addClass("dropdown"),this.init=function(){if(c.isOpen&&(m=d(c.isOpen),r=m.assign,a.$watch(m,function(a){o.isOpen=!!a})),angular.isDefined(c.dropdownAppendTo)){var e=d(c.dropdownAppendTo)(o);e&&(u=angular.element(e))}t=angular.isDefined(c.dropdownAppendToBody),v=angular.isDefined(c.keyboardNav),t&&!u&&(u=w),u&&n.dropdownMenu&&(u.append(n.dropdownMenu),b.on("$destroy",function(){n.dropdownMenu.remove()}))},this.toggle=function(a){return o.isOpen=arguments.length?!!a:!o.isOpen,angular.isFunction(r)&&r(o,o.isOpen),o.isOpen},this.isOpen=function(){return o.isOpen},o.getToggleElement=function(){return n.toggleElement},o.getAutoClose=function(){return c.autoClose||"always"},o.getElement=function(){return b},o.isKeynavEnabled=function(){return v},o.focusDropdownEntry=function(a){var c=n.dropdownMenu?angular.element(n.dropdownMenu).find("a"):b.find("ul").eq(0).find("a");switch(a){case 40:angular.isNumber(n.selectedOption)?n.selectedOption=n.selectedOption===c.length-1?n.selectedOption:n.selectedOption+1:n.selectedOption=0;break;case 38:angular.isNumber(n.selectedOption)?n.selectedOption=0===n.selectedOption?0:n.selectedOption-1:n.selectedOption=c.length-1}c[n.selectedOption].focus()},o.getDropdownElement=function(){return n.dropdownMenu},o.focusToggleElement=function(){n.toggleElement&&n.toggleElement[0].focus()},o.$watch("isOpen",function(c,d){if(u&&n.dropdownMenu){var e,i,m,v=h.positionElements(b,n.dropdownMenu,"bottom-left",!0);if(e={top:v.top+"px",display:c?"block":"none"},i=n.dropdownMenu.hasClass("dropdown-menu-right"),i?(e.left="auto",m=h.scrollbarWidth(!0),e.right=window.innerWidth-m-(v.left+b.prop("offsetWidth"))+"px"):(e.left=v.left+"px",e.right="auto"),!t){var w=h.offset(u);e.top=v.top-w.top+"px",i?e.right=window.innerWidth-(v.left-w.left+b.prop("offsetWidth"))+"px":e.left=v.left-w.left+"px"}n.dropdownMenu.css(e)}var x=u?u:b,y=x.hasClass(u?p:q);if(y===!c&&g[c?"addClass":"removeClass"](x,u?p:q).then(function(){angular.isDefined(c)&&c!==d&&s(a,{open:!!c})}),c)n.dropdownMenuTemplateUrl&&k(n.dropdownMenuTemplateUrl).then(function(a){l=o.$new(),j(a.trim())(l,function(a){var b=a;n.dropdownMenu.replaceWith(b),n.dropdownMenu=b})}),o.focusToggleElement(),f.open(o,b);else{if(n.dropdownMenuTemplateUrl){l&&l.$destroy();var z=angular.element('<ul class="dropdown-menu"></ul>');n.dropdownMenu.replaceWith(z),n.dropdownMenu=z}f.close(o,b),n.selectedOption=null}angular.isFunction(r)&&r(a,c)})}]).directive("uibDropdown",function(){return{controller:"UibDropdownController",link:function(a,b,c,d){d.init()}}}).directive("uibDropdownMenu",function(){return{restrict:"A",require:"?^uibDropdown",link:function(a,b,c,d){if(d&&!angular.isDefined(c.dropdownNested)){b.addClass("dropdown-menu");var e=c.templateUrl;e&&(d.dropdownMenuTemplateUrl=e),d.dropdownMenu||(d.dropdownMenu=b)}}}}).directive("uibDropdownToggle",function(){return{require:"?^uibDropdown",link:function(a,b,c,d){if(d){b.addClass("dropdown-toggle"),d.toggleElement=b;var e=function(e){e.preventDefault(),b.hasClass("disabled")||c.disabled||a.$apply(function(){d.toggle()})};b.bind("click",e),b.attr({"aria-haspopup":!0,"aria-expanded":!1}
vendor: 4,156 bytes, line 48993
48993),a.$watch(d.isOpen,function(a){b.attr("aria-expanded",!!a)}),a.$on("$destroy",function(){b.unbind("click",e)})}}}}),angular.module("ui.bootstrap.stackedMap",[]).factory("$$stackedMap",function(){return{createNew:function(){var a=[];return{add:function(b,c){a.push({key:b,value:c})},get:function(b){for(var c=0;c<a.length;c++)if(b===a[c].key)return a[c]},keys:function(){for(var b=[],c=0;c<a.length;c++)b.push(a[c].key);return b},top:function(){return a[a.length-1]},remove:function(b){for(var c=-1,d=0;d<a.length;d++)if(b===a[d].key){c=d;break}return a.splice(c,1)[0]},removeTop:function(){return a.splice(a.length-1,1)[0]},length:function(){return a.length}}}}}),angular.module("ui.bootstrap.modal",["ui.bootstrap.stackedMap","ui.bootstrap.position"]).factory("$$multiMap",function(){return{createNew:function(){var a={};return{entries:function(){return Object.keys(a).map(function(b){return{key:b,value:a[b]}})},get:function(b){return a[b]},hasKey:function(b){return!!a[b]},keys:function(){return Object.keys(a)},put:function(b,c){a[b]||(a[b]=[]),a[b].push(c)},remove:function(b,c){var d=a[b];if(d){var e=d.indexOf(c);-1!==e&&d.splice(e,1),d.length||delete a[b]}}}}}}).provider("$uibResolve",function(){var a=this;this.resolver=null,this.setResolver=function(a){this.resolver=a},this.$get=["$injector","$q",function(b,c){var d=a.resolver?b.get(a.resolver):null;return{resolve:function(a,e,f,g){if(d)return d.resolve(a,e,f,g);var h=[];return angular.forEach(a,function(a){angular.isFunction(a)||angular.isArray(a)?h.push(c.resolve(b.invoke(a))):angular.isString(a)?h.push(c.resolve(b.get(a))):h.push(c.resolve(a))}),c.all(h).then(function(b){var c={},d=0;return angular.forEach(a,function(a,e){c[e]=b[d++]}),c})}}}]}).directive("uibModalBackdrop",["$animate","$injector","$uibModalStack",function(a,b,c){function d(b,d,e){e.modalInClass&&(a.addClass(d,e.modalInClass),b.$on(c.NOW_CLOSING_EVENT,function(c,f){var g=f();b.modalOptions.animation?a.removeClass(d,e.modalInClass).then(g):g()}))}return{replace:!0,templateUrl:"uib/template/modal/backdrop.html",compile:function(a,b){return a.addClass(b.backdropClass),d}}}]).directive("uibModalWindow",["$uibModalStack","$q","$animateCss","$document",function(a,b,c,d){return{scope:{index:"@"},replace:!0,transclude:!0,templateUrl:function(a,b){return b.templateUrl||"uib/template/modal/window.html"},link:function(e,f,g){f.addClass(g.windowClass||""),f.addClass(g.windowTopClass||""),e.size=g.size,e.close=function(b){var c=a.getTop();c&&c.value.backdrop&&"static"!==c.value.backdrop&&b.target===b.currentTarget&&(b.preventDefault(),b.stopPropagation(),a.dismiss(c.key,"backdrop click"))},f.on("click",e.close),e.$isRendered=!0;var h=b.defer();g.$observe("modalRender",function(a){"true"===a&&h.resolve()}),h.promise.then(function(){var h=null;g.modalInClass&&(h=c(f,{addClass:g.modalInClass}).start(),e.$on(a.NOW_CLOSING_EVENT,function(a,b){var d=b();c(f,{removeClass:g.modalInClass}).start().then(d)})),b.when(h).then(function(){var b=a.getTop();if(b&&a.modalRendered(b.key),!d[0].activeElement||!f[0].contains(d[0].activeElement)){var c=f[0].querySelector("[autofocus]");c?c.focus():f[0].focus()}})})}}}]).directive("uibModalAnimationClass",function(){return{compile:function(a,b){b.modalAnimation&&a.addClass(b.uibModalAnimationClass)}}}).directive("uibModalTransclude",function(){return{link:function(a,b,c,d,e){e(a.$parent,function(a){b.empty(),b.append(a)})}}}).factory("$uibModalStack",["$animate","$animateCss","$document","$compile","$rootScope","$q","$$multiMap","$$stackedMap","$uibPosition",function(a,b,c,d,e,f,g,h,i){function j(a){return!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)}function k(){for(var a=-1,b=v.keys(),c=0;c<b.length;c++)v.get(b[c]).value.backdrop&&(a=c);return a>-1&&y>a&&(a=y),a}function l(a,b){var c=v.get(a).value,d=c.appendTo;v.remove(a),z=v.top(),z&&(y=parseInt(z.value.modalDomEl.attr("index"),10)),o(c.modalDomEl,c.modalScope,function(){var b=c.openedClass||u;w.remove(b,a);var e=w.hasKey(b);d.toggleClass(b,e),!e&&t&&t.heightOverflow&&t.scrollbarWidth&&(t.originalRight?d.css({paddingRight:t.originalRight+"px"}):d.css({paddingRight:""}),t=null),m(!0)}
48993,c.closedDeferred),n(),b&&b.focus?b.focus():d.focus&&d.focus()}function m(a){var b;v.length()>0&&(b=v.top().value,b.modalDomEl.toggleClass(b.windowTopClass||"",a))}function n(){if(r&&-1===k()){var a=s;o(r,s,function(){a=null}),r=void 0,s=void 0}}function o(b,c,d,e){function g(){g.done||(g.done=!0,a.leave(b).then(function(){b.remove(),e&&e.resolve()}),c.$destroy(),d&&d())}var h,i=null,j=function(){return h||(h=f.defer(),i=h.promise),function(){h.resolve()}};return c.$broadcast(x.NOW_CLOSING_EVENT,j),f.when(i).then(g)}function p(a){if(a.isDefaultPrevented())return a;var b=v.top();if(b)switch(a.which){case 27:b.value.keyboard&&(a.preventDefault(),e.$apply(function(){x.dismiss(b.key,"escape key press")}));break;case 9:var c=x.loadFocusElementList(b),d=!1;a.shiftKey?(x.isFocusInFirstItem(a,c)||x.isModalFocused(a,b))&&(d=x.focusLastFocusableElement(c)):x.isFocusInLastItem(a,c)&&(d=x.focusFirstFocusableElement(c)),d&&(a.preventDefault(),a.stopPropagation())}}function q(a,b,c){return!a.value.modalScope.$broadcast("modal.closing",b,c).defaultPrevented}var r,s,t,u="modal-open",v=h.createNew(),w=g.createNew(),x={NOW_CLOSING_EVENT:"modal.stack.now-closing"},y=0,z=null,A="a[href], area[href], input:not([disabled]), button:not([disabled]),select:not([disabled]), textarea:not([disabled]), iframe, object, embed, *[tabindex], *[contenteditable=true]";return e.$watch(k,function(a){s&&(s.index=a)}),c.on("keydown",p),e.$on("$destroy",function(){c.off("keydown",p)}),x.open=function(b,f){var g=c[0].activeElement,h=f.openedClass||u;m(!1),z=v.top(),v.add(b,{deferred:f.deferred,renderDeferred:f.renderDeferred,closedDeferred:f.closedDeferred,modalScope:f.scope,backdrop:f.backdrop,keyboard:f.keyboard,openedClass:f.openedClass,windowTopClass:f.windowTopClass,animation:f.animation,appendTo:f.appendTo}),w.put(h,b);var j=f.appendTo,l=k();if(!j.length)throw new Error("appendTo element not found. Make sure that the element passed is in DOM.");l>=0&&!r&&(s=e.$new(!0),s.modalOptions=f,s.index=l,r=angular.element('<div uib-modal-backdrop="modal-backdrop"></div>'),r.attr("backdrop-class",f.backdropClass),f.animation&&r.attr("modal-animation","true"),d(r)(s),a.enter(r,j),t=i.scrollbarPadding(j),t.heightOverflow&&t.scrollbarWidth&&j.css({paddingRight:t.right+"px"})),y=z?parseInt(z.value.modalDomEl.attr("index"),10)+1:0;var n=angular.element('<div uib-modal-window="modal-window"></div>');n.attr({"template-url":f.windowTemplateUrl,"window-class":f.windowClass,"window-top-class":f.windowTopClass,size:f.size,index:y,animate:"animate"}).html(f.content),f.animation&&n.attr("modal-animation","true"),j.addClass(h),a.enter(d(n)(f.scope),j),v.top().value.modalDomEl=n,v.top().value.modalOpener=g},x.close=function(a,b){var c=v.get(a);return c&&q(c,b,!0)?(c.value.modalScope.$$uibDestructionScheduled=!0,c.value.deferred.resolve(b),l(a,c.value.modalOpener),!0):!c},x.dismiss=function(a,b){var c=v.get(a);return c&&q(c,b,!1)?(c.value.modalScope.$$uibDestructionScheduled=!0,c.value.deferred.reject(b),l(a,c.value.modalOpener),!0):!c},x.dismissAll=function(a){for(var b=this.getTop();b&&this.dismiss(b.key,a);)b=this.getTop()},x.getTop=function(){return v.top()},x.modalRendered=function(a){var b=v.get(a);b&&b.value.renderDeferred.resolve()},x.focusFirstFocusableElement=function(a){return a.length>0?(a[0].focus(),!0):!1},x.focusLastFocusableElement=function(a){return a.length>0?(a[a.length-1].focus(),!0):!1},x.isModalFocused=function(a,b){if(a&&b){var c=b.value.modalDomEl;if(c&&c.length)return(a.target||a.srcElement)===c[0]}return!1},x.isFocusInFirstItem=function(a,b){return b.length>0?(a.target||a.srcElement)===b[0]:!1},x.isFocusInLastItem=function(a,b){return b.length>0?(a.target||a.srcElement)===b[b.length-1]:!1},x.loadFocusElementList=function(a){if(a){var b=a.value.modalDomEl;if(b&&b.length){var c=b[0].querySelectorAll(A);return c?Array.prototype.filter.call(c,function(a){return j(a)}):c}}},x}]).provider("$uibModal",function(){var a={options:{animation:!0,backdrop:!0,keyboard:!0},$get:["$rootScope","$q","$document","$templateRequest","$controller","$uibResolve","$uibModalStack",function(b,c,d,e,f,g,h){function i(a){return a.template?c.when(a.template):e(angular.isFunction(a.templateUrl)?a.templateUrl():a.templateUrl)}var j={},k=null;return j.getPromiseChain=function(){return k},j.open=function(e){function j(){return r}var l=c.defer(),m=c.defer(),n=c.defer(),o=c.defer(),p={result:l.promise,opened:m.promise,closed:n.promise,rendered:o.promise,close:function(a){return h.close(p,a)}
vendor: 9,094 bytes, lines 48993-48994
48993,dismiss:function(a){return h.dismiss(p,a)}};if(e=angular.extend({},a.options,e),e.resolve=e.resolve||{},e.appendTo=e.appendTo||d.find("body").eq(0),!e.template&&!e.templateUrl)throw new Error("One of template or templateUrl options is required.");var q,r=c.all([i(e),g.resolve(e.resolve,{},null,null)]);return q=k=c.all([k]).then(j,j).then(function(a){var c=e.scope||b,d=c.$new();d.$close=p.close,d.$dismiss=p.dismiss,d.$on("$destroy",function(){d.$$uibDestructionScheduled||d.$dismiss("$uibUnscheduledDestruction")});var g,i,j={};e.controller&&(j.$scope=d,j.$scope.$resolve={},j.$uibModalInstance=p,angular.forEach(a[1],function(a,b){j[b]=a,j.$scope.$resolve[b]=a}),i=f(e.controller,j,!0,e.controllerAs),e.controllerAs&&e.bindToController&&(g=i.instance,g.$close=d.$close,g.$dismiss=d.$dismiss,angular.extend(g,{$resolve:j.$scope.$resolve},c)),g=i(),angular.isFunction(g.$onInit)&&g.$onInit()),h.open(p,{scope:d,deferred:l,renderDeferred:o,closedDeferred:n,content:a[0],animation:e.animation,backdrop:e.backdrop,keyboard:e.keyboard,backdropClass:e.backdropClass,windowTopClass:e.windowTopClass,windowClass:e.windowClass,windowTemplateUrl:e.windowTemplateUrl,size:e.size,openedClass:e.openedClass,appendTo:e.appendTo}),m.resolve(!0)},function(a){m.reject(a),l.reject(a)})["finally"](function(){k===q&&(k=null)}),p},j}]};return a}),angular.module("ui.bootstrap.paging",[]).factory("uibPaging",["$parse",function(a){return{create:function(b,c,d){b.setNumPages=d.numPages?a(d.numPages).assign:angular.noop,b.ngModelCtrl={$setViewValue:angular.noop},b._watchers=[],b.init=function(a,e){b.ngModelCtrl=a,b.config=e,a.$render=function(){b.render()},d.itemsPerPage?b._watchers.push(c.$parent.$watch(d.itemsPerPage,function(a){b.itemsPerPage=parseInt(a,10),c.totalPages=b.calculateTotalPages(),b.updatePage()})):b.itemsPerPage=e.itemsPerPage,c.$watch("totalItems",function(a,d){(angular.isDefined(a)||a!==d)&&(c.totalPages=b.calculateTotalPages(),b.updatePage())})},b.calculateTotalPages=function(){var a=b.itemsPerPage<1?1:Math.ceil(c.totalItems/b.itemsPerPage);return Math.max(a||0,1)},b.render=function(){c.page=parseInt(b.ngModelCtrl.$viewValue,10)||1},c.selectPage=function(a,d){d&&d.preventDefault();var e=!c.ngDisabled||!d;e&&c.page!==a&&a>0&&a<=c.totalPages&&(d&&d.target&&d.target.blur(),b.ngModelCtrl.$setViewValue(a),b.ngModelCtrl.$render())},c.getText=function(a){return c[a+"Text"]||b.config[a+"Text"]},c.noPrevious=function(){return 1===c.page},c.noNext=function(){return c.page===c.totalPages},b.updatePage=function(){b.setNumPages(c.$parent,c.totalPages),c.page>c.totalPages?c.selectPage(c.totalPages):b.ngModelCtrl.$render()},c.$on("$destroy",function(){for(;b._watchers.length;)b._watchers.shift()()})}}}]),angular.module("ui.bootstrap.pager",["ui.bootstrap.paging"]).controller("UibPagerController",["$scope","$attrs","uibPaging","uibPagerConfig",function(a,b,c,d){a.align=angular.isDefined(b.align)?a.$parent.$eval(b.align):d.align,c.create(this,a,b)}]).constant("uibPagerConfig",{itemsPerPage:10,previousText:"« Previous",nextText:"Next »",align:!0}).directive("uibPager",["uibPagerConfig",function(a){return{scope:{totalItems:"=",previousText:"@",nextText:"@",ngDisabled:"="},require:["uibPager","?ngModel"],controller:"UibPagerController",controllerAs:"pager",templateUrl:function(a,b){return b.templateUrl||"uib/template/pager/pager.html"},replace:!0,link:function(b,c,d,e){var f=e[0],g=e[1];g&&f.init(g,a)}}}]),angular.module("ui.bootstrap.pagination",["ui.bootstrap.paging"]).controller("UibPaginationController",["$scope","$attrs","$parse","uibPaging","uibPaginationConfig",function(a,b,c,d,e){function f(a,b,c){return{number:a,text:b,active:c}}function g(a,b){var c=[],d=1,e=b,g=angular.isDefined(i)&&b>i;g&&(j?(d=Math.max(a-Math.floor(i/2),1),e=d+i-1,e>b&&(e=b,d=e-i+1)):(d=(Math.ceil(a/i)-1)*i+1,e=Math.min(d+i-1,b)));for(var h=d;e>=h;h++){var n=f(h,m(h),h===a);c.push(n)}if(g&&i>0&&(!j||k||l)){if(d>1){if(!l||d>3){var o=f(d-1,"...",!1);c.unshift(o)}if(l){if(3===d){var p=f(2,"2",!1);c.unshift(p)}var q=f(1,"1",!1);c.unshift(q)}}if(b>e){if(!l||b-2>e){var r=f(e+1,"...",!1);c.push(r)}if(l){if(e===b-2){var s=f(b-1,b-1,!1);c.push(s)}var t=f(b,b,!1);c.push(t)}}}return c}var h=this,i=angular.isDefined(b.maxSize)?a.$parent.$eval(b.maxSize):e.maxSize,j=angular.isDefined(b.rotate)?a.$parent.$eval(b.rotate):e.rotate,k=angular.isDefined(b.forceEllipses)?a.$parent.$eval(b.forceEllipses):e.forceEllipses,l=angular.isDefined(b.boundaryLinkNumbers)?a.$parent.$eval(b.boundaryLinkNumbers):e.boundaryLinkNumbers,m=angular.isDefined(b.pageLabel)?function(c){return a.$parent.$eval(b.pageLabel,{$page:c})}:angular.identity;a.boundaryLinks=angular.isDefined(b.boundaryLinks)?a.$parent.$eval(b.boundaryLinks):e.boundaryLinks,a.directionLinks=angular.isDefined(b.directionLinks)?a.$parent.$eval(b.directionLinks):e.directionLinks,d.create(this,a,b),b.maxSize&&h._watchers.push(a.$parent.$watch(c(b.maxSize),function(a){i=parseInt(a,10),h.render()}));var n=this.render;this.render=function(){n(),a.page>0&&a.page<=a.totalPages&&(a.pages=g(a.page,a.totalPages))}}]).constant("uibPaginationConfig",{itemsPerPage:10,boundaryLinks:!1,boundaryLinkNumbers:!1,directionLinks:!0,firstText:"First",previousText:"Previous",nextText:"Next",lastText:"Last",rotate:!0,forceEllipses:!1}).directive("uibPagination",["$parse","uibPaginationConfig",function(a,b){return{scope:{totalItems:"=",firstText:"@",previousText:"@",nextText:"@",lastText:"@",ngDisabled:"="},require:["uibPagination","?ngModel"],controller:"UibPaginationController",controllerAs:"pagination",templateUrl:function(a,b){return b.templateUrl||"uib/template/pagination/pagination.html"},replace:!0,link:function(a,c,d,e){var f=e[0],g=e[1];g&&f.init(g,b)}}}]),angular.module("ui.bootstrap.tooltip",["ui.bootstrap.position","ui.bootstrap.stackedMap"]).provider("$uibTooltip",function(){function a(a){var b=/[A-Z]/g,c="-";return a.replace(b,function(a,b){return(b?c:"")+a.toLowerCase()})}var b={placement:"top",placementClassPrefix:"",animation:!0,popupDelay:0,popupCloseDelay:0,useContentExp:!1},c={mouseenter:"mouseleave",click:"click",outsideClick:"outsideClick",focus:"blur",none:""},d={};this.options=function(a){angular.extend(d,a)},this.setTriggers=function(a){angular.extend(c,a)},this.$get=["$window","$compile","$timeout","$document","$uibPosition","$interpolate","$rootScope","$parse","$$stackedMap",function(e,f,g,h,i,j,k,l,m){function n(a){if(27===a.which){var b=o.top();b&&(b.value.close(),o.removeTop(),b=null)}}var o=m.createNew();return h.on("keypress",n),k.$on("$destroy",function(){h.off("keypress",n)}),function(e,k,m,n){function p(a){var b=(a||n.trigger||m).split(" "),d=b.map(function(a){
48994return c[a]||a});return{show:b,hide:d}}n=angular.extend({},b,d,n);var q=a(e),r=j.startSymbol(),s=j.endSymbol(),t="<div "+q+'-popup uib-title="'+r+"title"+s+'" '+(n.useContentExp?'content-exp="contentExp()" ':'content="'+r+"content"+s+'" ')+'placement="'+r+"placement"+s+'" popup-class="'+r+"popupClass"+s+'" animation="animation" is-open="isOpen" origin-scope="origScope" class="uib-position-measure"></div>';return{compile:function(a,b){var c=f(t);return function(a,b,d,f){function j(){N.isOpen?q():m()}function m(){M&&!a.$eval(d[k+"Enable"])||(u(),x(),N.popupDelay?G||(G=g(r,N.popupDelay,!1)):r())}function q(){s(),N.popupCloseDelay?H||(H=g(t,N.popupCloseDelay,!1)):t()}function r(){return s(),u(),N.content?(v(),void N.$evalAsync(function(){N.isOpen=!0,y(!0),S()})):angular.noop}function s(){G&&(g.cancel(G),G=null),I&&(g.cancel(I),I=null)}function t(){N&&N.$evalAsync(function(){N&&(N.isOpen=!1,y(!1),N.animation?F||(F=g(w,150,!1)):w())})}function u(){H&&(g.cancel(H),H=null),F&&(g.cancel(F),F=null)}function v(){D||(E=N.$new(),D=c(E,function(a){K?h.find("body").append(a):b.after(a)}),z())}function w(){s(),u(),A(),D&&(D.remove(),D=null),E&&(E.$destroy(),E=null)}function x(){N.title=d[k+"Title"],Q?N.content=Q(a):N.content=d[e],N.popupClass=d[k+"Class"],N.placement=angular.isDefined(d[k+"Placement"])?d[k+"Placement"]:n.placement;var b=i.parsePlacement(N.placement);J=b[1]?b[0]+"-"+b[1]:b[0];var c=parseInt(d[k+"PopupDelay"],10),f=parseInt(d[k+"PopupCloseDelay"],10);N.popupDelay=isNaN(c)?n.popupDelay:c,N.popupCloseDelay=isNaN(f)?n.popupCloseDelay:f}function y(b){P&&angular.isFunction(P.assign)&&P.assign(a,b)}function z(){R.length=0,Q?(R.push(a.$watch(Q,function(a){N.content=a,!a&&N.isOpen&&t()})),R.push(E.$watch(function(){O||(O=!0,E.$$postDigest(function(){O=!1,N&&N.isOpen&&S()}))}))):R.push(d.$observe(e,function(a){N.content=a,!a&&N.isOpen?t():S()})),R.push(d.$observe(k+"Title",function(a){N.title=a,N.isOpen&&S()})),R.push(d.$observe(k+"Placement",function(a){N.placement=a?a:n.placement,N.isOpen&&S()}))}function A(){R.length&&(angular.forEach(R,function(a){a()}),R.length=0)}function B(a){N&&N.isOpen&&D&&(b[0].contains(a.target)||D[0].contains(a.target)||q())}function C(){var a=d[k+"Trigger"];T(),L=p(a),"none"!==L.show&&L.show.forEach(function(a,c){"outsideClick"===a?(b.on("click",j),h.on("click",B)):a===L.hide[c]?b.on(a,j):a&&(b.on(a,m),b.on(L.hide[c],q)),b.on("keypress",function(a){27===a.which&&q
vendor: 5,215 bytes, line 48994
48994()})})}var D,E,F,G,H,I,J,K=angular.isDefined(n.appendToBody)?n.appendToBody:!1,L=p(void 0),M=angular.isDefined(d[k+"Enable"]),N=a.$new(!0),O=!1,P=angular.isDefined(d[k+"IsOpen"])?l(d[k+"IsOpen"]):!1,Q=n.useContentExp?l(d[e]):!1,R=[],S=function(){D&&D.html()&&(I||(I=g(function(){var a=i.positionElements(b,D,N.placement,K);D.css({top:a.top+"px",left:a.left+"px"}),D.hasClass(a.placement.split("-")[0])||(D.removeClass(J.split("-")[0]),D.addClass(a.placement.split("-")[0])),D.hasClass(n.placementClassPrefix+a.placement)||(D.removeClass(n.placementClassPrefix+J),D.addClass(n.placementClassPrefix+a.placement)),D.hasClass("uib-position-measure")?(i.positionArrow(D,a.placement),D.removeClass("uib-position-measure")):J!==a.placement&&i.positionArrow(D,a.placement),J=a.placement,I=null},0,!1)))};N.origScope=a,N.isOpen=!1,o.add(N,{close:t}),N.contentExp=function(){return N.content},d.$observe("disabled",function(a){a&&s(),a&&N.isOpen&&t()}),P&&a.$watch(P,function(a){N&&!a===N.isOpen&&j()});var T=function(){L.show.forEach(function(a){"outsideClick"===a?b.off("click",j):(b.off(a,m),b.off(a,j))}),L.hide.forEach(function(a){"outsideClick"===a?h.off("click",B):b.off(a,q)})};C();var U=a.$eval(d[k+"Animation"]);N.animation=angular.isDefined(U)?!!U:n.animation;var V,W=k+"AppendToBody";V=W in d&&void 0===d[W]?!0:a.$eval(d[W]),K=angular.isDefined(V)?V:K,a.$on("$destroy",function(){T(),w(),o.remove(N),N=null})}}}}}]}).directive("uibTooltipTemplateTransclude",["$animate","$sce","$compile","$templateRequest",function(a,b,c,d){return{link:function(e,f,g){var h,i,j,k=e.$eval(g.tooltipTemplateTranscludeScope),l=0,m=function(){i&&(i.remove(),i=null),h&&(h.$destroy(),h=null),j&&(a.leave(j).then(function(){i=null}),i=j,j=null)};e.$watch(b.parseAsResourceUrl(g.uibTooltipTemplateTransclude),function(b){var g=++l;b?(d(b,!0).then(function(d){if(g===l){var e=k.$new(),i=d,n=c(i)(e,function(b){m(),a.enter(b,f)});h=e,j=n,h.$emit("$includeContentLoaded",b)}},function(){g===l&&(m(),e.$emit("$includeContentError",b))}),e.$emit("$includeContentRequested",b)):m()}),e.$on("$destroy",m)}}}]).directive("uibTooltipClasses",["$uibPosition",function(a){return{restrict:"A",link:function(b,c,d){if(b.placement){var e=a.parsePlacement(b.placement);c.addClass(e[0])}b.popupClass&&c.addClass(b.popupClass),b.animation()&&c.addClass(d.tooltipAnimationClass)}}}]).directive("uibTooltipPopup",function(){return{replace:!0,scope:{content:"@",placement:"@",popupClass:"@",animation:"&",isOpen:"&"},templateUrl:"uib/template/tooltip/tooltip-popup.html"}}).directive("uibTooltip",["$uibTooltip",function(a){return a("uibTooltip","tooltip","mouseenter")}]).directive("uibTooltipTemplatePopup",function(){return{replace:!0,scope:{contentExp:"&",placement:"@",popupClass:"@",animation:"&",isOpen:"&",originScope:"&"},templateUrl:"uib/template/tooltip/tooltip-template-popup.html"}}).directive("uibTooltipTemplate",["$uibTooltip",function(a){return a("uibTooltipTemplate","tooltip","mouseenter",{useContentExp:!0})}]).directive("uibTooltipHtmlPopup",function(){return{replace:!0,scope:{contentExp:"&",placement:"@",popupClass:"@",animation:"&",isOpen:"&"},templateUrl:"uib/template/tooltip/tooltip-html-popup.html"}}).directive("uibTooltipHtml",["$uibTooltip",function(a){return a("uibTooltipHtml","tooltip","mouseenter",{useContentExp:!0})}]),angular.module("ui.bootstrap.popover",["ui.bootstrap.tooltip"]).directive("uibPopoverTemplatePopup",function(){return{replace:!0,scope:{uibTitle:"@",contentExp:"&",placement:"@",popupClass:"@",animation:"&",isOpen:"&",originScope:"&"},templateUrl:"uib/template/popover/popover-template.html"}}).directive("uibPopoverTemplate",["$uibTooltip",function(a){return a("uibPopoverTemplate","popover","click",{useContentExp:!0})}]).directive("uibPopoverHtmlPopup",function(){return{replace:!0,scope:{contentExp:"&",uibTitle:"@",placement:"@",popupClass:"@",animation:"&",isOpen:"&"},templateUrl:"uib/template/popover/popover-html.html"}}).directive("uibPopoverHtml",["$uibTooltip",function(a){return a("uibPopoverHtml","popover","click",{useContentExp:!0})}]).directive("uibPopoverPopup",function(){return{replace:!0,scope:{uibTitle:"@",content:"@",placement:"@",popupClass:"@",animation:"&",isOpen:"&"},templateUrl:"uib/template/popover/popover.html"}}).directive("uibPopover",["$uibTooltip",function(a){return a("uibPopover","popover","click")}]),angular.module("ui.bootstrap.progressbar",[]).constant("uibProgressConfig",{animate:!0,max:100}).controller("UibProgressController",["$scope","$attrs","uibProgressConfig",function(a,b,c){function d(){return angular.isDefined(a.maxParam)?a.maxParam:c.max}var e=this,f=angular.isDefined(b.animate)?a.$parent.$eval(b.animate):c.animate;this.bars=[],a.max=d(),this.addBar=function(a,b,c){f||b.css({transition:"none"}),this.bars.push(a),a.max=d(),a.title=c&&angular.isDefined(c.title)?c.title:"progressbar",a.$watch("value",function(b){a.recalculatePercentage()}),a.recalculatePercentage=function(){var b=e.bars.reduce(function(a,b){return b.percent=+(100*b.value/b.max).toFixed(2),a+b.percent},0);b>100&&(a.percent-=b-100)},a.$on("$destroy",function(){b=null,e.removeBar(a)})},this.removeBar=function(a){this.bars.splice(this.bars.indexOf(a),1),this.bars.forEac
vendor: 18,321 bytes, line 48994
48994h(function(a){a.recalculatePercentage()})},a.$watch("maxParam",function(a){e.bars.forEach(function(a){a.max=d(),a.recalculatePercentage()})})}]).directive("uibProgress",function(){return{replace:!0,transclude:!0,controller:"UibProgressController",require:"uibProgress",scope:{maxParam:"=?max"},templateUrl:"uib/template/progressbar/progress.html"}}).directive("uibBar",function(){return{replace:!0,transclude:!0,require:"^uibProgress",scope:{value:"=",type:"@"},templateUrl:"uib/template/progressbar/bar.html",link:function(a,b,c,d){d.addBar(a,b,c)}}}).directive("uibProgressbar",function(){return{replace:!0,transclude:!0,controller:"UibProgressController",scope:{value:"=",maxParam:"=?max",type:"@"},templateUrl:"uib/template/progressbar/progressbar.html",link:function(a,b,c,d){d.addBar(a,angular.element(b.children()[0]),{title:c.title})}}}),angular.module("ui.bootstrap.rating",[]).constant("uibRatingConfig",{max:5,stateOn:null,stateOff:null,enableReset:!0,titles:["one","two","three","four","five"]}).controller("UibRatingController",["$scope","$attrs","uibRatingConfig",function(a,b,c){var d={$setViewValue:angular.noop},e=this;this.init=function(e){d=e,d.$render=this.render,d.$formatters.push(function(a){return angular.isNumber(a)&&a<<0!==a&&(a=Math.round(a)),a}),this.stateOn=angular.isDefined(b.stateOn)?a.$parent.$eval(b.stateOn):c.stateOn,this.stateOff=angular.isDefined(b.stateOff)?a.$parent.$eval(b.stateOff):c.stateOff,this.enableReset=angular.isDefined(b.enableReset)?a.$parent.$eval(b.enableReset):c.enableReset;var f=angular.isDefined(b.titles)?a.$parent.$eval(b.titles):c.titles;this.titles=angular.isArray(f)&&f.length>0?f:c.titles;var g=angular.isDefined(b.ratingStates)?a.$parent.$eval(b.ratingStates):new Array(angular.isDefined(b.max)?a.$parent.$eval(b.max):c.max);a.range=this.buildTemplateObjects(g)},this.buildTemplateObjects=function(a){for(var b=0,c=a.length;c>b;b++)a[b]=angular.extend({index:b},{stateOn:this.stateOn,stateOff:this.stateOff,title:this.getTitle(b)},a[b]);return a},this.getTitle=function(a){return a>=this.titles.length?a+1:this.titles[a]},a.rate=function(b){if(!a.readonly&&b>=0&&b<=a.range.length){var c=e.enableReset&&d.$viewValue===b?0:b;d.$setViewValue(c),d.$render()}},a.enter=function(b){a.readonly||(a.value=b),a.onHover({value:b})},a.reset=function(){a.value=d.$viewValue,a.onLeave()},a.onKeydown=function(b){/(37|38|39|40)/.test(b.which)&&(b.preventDefault(),b.stopPropagation(),a.rate(a.value+(38===b.which||39===b.which?1:-1)))},this.render=function(){a.value=d.$viewValue,a.title=e.getTitle(a.value-1)}}]).directive("uibRating",function(){return{require:["uibRating","ngModel"],scope:{readonly:"=?readOnly",onHover:"&",onLeave:"&"},controller:"UibRatingController",templateUrl:"uib/template/rating/rating.html",replace:!0,link:function(a,b,c,d){var e=d[0],f=d[1];e.init(f)}}}),angular.module("ui.bootstrap.tabs",[]).controller("UibTabsetController",["$scope",function(a){function b(a){for(var b=0;b<d.tabs.length;b++)if(d.tabs[b].index===a)return b}var c,d=this;d.tabs=[],d.select=function(a,f){if(!e){var g=b(c),h=d.tabs[g];if(h){if(h.tab.onDeselect({$event:f,$selectedIndex:a}),f&&f.isDefaultPrevented())return;h.tab.active=!1}var i=d.tabs[a];i?(i.tab.onSelect({$event:f}),i.tab.active=!0,d.active=i.index,c=i.index):!i&&angular.isDefined(c)&&(d.active=null,c=null)}},d.addTab=function(a){if(d.tabs.push({tab:a,index:a.index}),d.tabs.sort(function(a,b){return a.index>b.index?1:a.index<b.index?-1:0}),a.index===d.active||!angular.isDefined(d.active)&&1===d.tabs.length){var c=b(a.index);d.select(c)}},d.removeTab=function(a){for(var b,c=0;c<d.tabs.length;c++)if(d.tabs[c].tab===a){b=c;break}if(d.tabs[b].index===d.active){var e=b===d.tabs.length-1?b-1:b+1%d.tabs.length;d.select(e)}d.tabs.splice(b,1)},a.$watch("tabset.active",function(a){angular.isDefined(a)&&a!==c&&d.select(b(a))});var e;a.$on("$destroy",function(){e=!0})}]).directive("uibTabset",function(){return{transclude:!0,replace:!0,scope:{},bindToController:{active:"=?",type:"@"},controller:"UibTabsetController",controllerAs:"tabset",templateUrl:function(a,b){return b.templateUrl||"uib/template/tabs/tabset.html"},link:function(a,b,c){a.vertical=angular.isDefined(c.vertical)?a.$parent.$eval(c.vertical):!1,a.justified=angular.isDefined(c.justified)?a.$parent.$eval(c.justified):!1}}}).directive("uibTab",["$parse",function(a){return{require:"^uibTabset",replace:!0,templateUrl:function(a,b){return b.templateUrl||"uib/template/tabs/tab.html"},transclude:!0,scope:{heading:"@",index:"=?",classes:"@?",onSelect:"&select",onDeselect:"&deselect"},controller:function(){},controllerAs:"tab",link:function(b,c,d,e,f){b.disabled=!1,d.disable&&b.$parent.$watch(a(d.disable),function(a){b.disabled=!!a}),angular.isUndefined(d.index)&&(e.tabs&&e.tabs.length?b.index=Math.max.apply(null,e.tabs.map(function(a){return a.index}))+1:b.index=0),angular.isUndefined(d.classes)&&(b.classes=""),b.select=function(a){if(!b.disabled){for(var c,d=0;d<e.tabs.length;d++)if(e.tabs[d].tab===b){c=d;break}e.select(c,a)}},e.addTab(b),b.$on("$destroy",function(){e.removeTab(b)}),b.$transcludeFn=f}}}]).directive("uibTabHeadingTransclude",function(){return{restrict:"A",require:"^uibTab",link:function(a,b){a.$watch("headingElement",function(a){a&&(b.html(""),b.append(a))})}}}).directive("uibTabContentTransclude",function(){function a(a){return a.tagName&&(a.hasAttribute("uib-tab-heading")||a.hasAttribute("data-uib-tab-heading")||a.hasAttribute("x-uib-tab-heading")||"uib-tab-heading"===a.tagName.toLowerCase()||"data-uib-tab-heading"===a.tagName.toLowerCase()||"x-uib-tab-heading"===a.tagName.toLowerCase()||"uib:tab-heading"===a.tagName.toLowerCase())}return{restrict:"A",require:"^uibTabset",link:function(b,c,d){var e=b.$eval(d.uibTabContentTransclude).tab;e.$transcludeFn(e.$parent,function(b){angular.forEach(b,function(b){a(b)?e.headingElement=b:c.append(b)})})}}}),angular.module("ui.bootstrap.timepicker",[]).constant("uibTimepickerConfig",{hourStep:1,minuteStep:1,secondStep:1,showMeridian:!0,showSeconds:!1,meridians:null,readonlyInput:!1,mousewheel:!0,arrowkeys:!0,showSpinners:!0,templateUrl:"uib/template/timepicker/timepicker.html"}).controller("UibTimepickerController",["$scope","$element","$attrs","$parse","$log","$locale","uibTimepickerConfig",function(a,b,c,d,e,f,g){function h(){var b=+a.hours,c=a.showMeridian?b>0&&13>b:b>=0&&24>b;return c&&""!==a.hours?(a.showMeridian&&(12===b&&(b=0),a.meridian===v[1]&&(b+=12)),b):void 0}function i(){var b=+a.minutes,c=b>=0&&60>b;return c&&""!==a.minutes?b:void 0}function j(){var b=+a.seconds;return b>=0&&60>b?b:void 0}function k(a,b){return null===a?"":angular.isDefined(a)&&a.toString().length<2&&!b?"0"+a:a.toString()}function l(a){m(),u.$setViewValue(new Date(s)),n(a)}function m(){u.$setValidity("time",!0),a.invalidHours=!1,a.invalidMinutes=!1,a.invalidSeconds=!1}function n(b){if(u.$modelValue){var c=s.getHours(),d=s.getMinutes(),e=s.getSeconds();a.showMeridian&&(c=0===c||12===c?12:c%12),a.hours="h"===b?c:k(c,!w),"m"!==b&&(a.minutes=k(d)),a.meridian=s.getHours()<12?v[0]:v[1],"s"!==b&&(a.seconds=k(e)),a.meridian=s.getHours()<12?v[0]:v[1]}else a.hours=null,a.minutes=null,a.seconds=null,a.meridian=v[0]}function o(a){s=q(s,a),l()}function p(a,b){return q(a,60*b)}function q(a,b){var c=new Date(a.getTime()+1e3*b),d=new Date(a);return d.setHours(c.getHours(),c.getMinutes(),c.getSeconds()),d}function r(){return(null===a.hours||""===a.hours)&&(null===a.minutes||""===a.minutes)&&(!a.showSeconds||a.showSeconds&&(null===a.seconds||""===a.seconds))}var s=new Date,t=[],u={$setViewValue:angular.noop},v=angular.isDefined(c.meridians)?a.$parent.$eval(c.meridians):g.meridians||f.DATETIME_FORMATS.AMPMS,w=angular.isDefined(c.padHours)?a.$parent.$eval(c.padHours):!0;a.tabindex=angular.isDefined(c.tabindex)?c.tabindex:0,b.removeAttr("tabindex"),this.init=function(b,d){u=b,u.$render=this.render,u.$formatters.unshift(function(a){return a?new Date(a):null});var e=d.eq(0),f=d.eq(1),h=d.eq(2),i=angular.isDefined(c.mousewheel)?a.$parent.$eval(c.mousewheel):g.mousewheel;i&&this.setupMousewheelEvents(e,f,h);var j=angular.isDefined(c.arrowkeys)?a.$parent.$eval(c.arrowkeys):g.arrowkeys;j&&this.setupArrowkeyEvents(e,f,h),a.readonlyInput=angular.isDefined(c.readonlyInput)?a.$parent.$eval(c.readonlyInput):g.readonlyInput,this.setupInputEvents(e,f,h)};var x=g.hourStep;c.hourStep&&t.push(a.$parent.$watch(d(c.hourStep),function(a){x=+a}));var y=g.minuteStep;c.minuteStep&&t.push(a.$parent.$watch(d(c.minuteStep),function(a){y=+a}));var z;t.push(a.$parent.$watch(d(c.min),function(a){var b=new Date(a);z=isNaN(b)?void 0:b}));var A;t.push(a.$parent.$watch(d(c.max),function(a){var b=new Date(a);A=isNaN(b)?void 0:b}));var B=!1;c.ngDisabled&&t.push(a.$parent.$watch(d(c.ngDisabled),function(a){B=a})),a.noIncrementHours=function(){var a=p(s,60*x);return B||a>A||s>a&&z>a},a.noDecrementHours=function(){var a=p(s,60*-x);return B||z>a||a>s&&a>A},a.noIncrementMinutes=function(){var a=p(s,y);return B||a>A||s>a&&z>a},a.noDecrementMinutes=function(){var a=p(s,-y);return B||z>a||a>s&&a>A},a.noIncrementSeconds=function(){var a=q(s,C);return B||a>A||s>a&&z>a},a.noDecrementSeconds=function(){var a=q(s,-C);return B||z>a||a>s&&a>A},a.noToggleMeridian=function(){return s.getHours()<12?B||p(s,720)>A:B||p(s,-720)<z};var C=g.secondStep;c.secondStep&&t.push(a.$parent.$watch(d(c.secondStep),function(a){C=+a})),a.showSeconds=g.showSeconds,c.showSeconds&&t.push(a.$parent.$watch(d(c.showSeconds),function(b){a.showSeconds=!!b})),a.showMeridian=g.showMeridian,c.showMeridian&&t.push(a.$parent.$watch(d(c.showMeridian),function(b){if(a.showMeridian=!!b,u.$error.time){var c=h(),d=i();angular.isDefined(c)&&angular.isDefined(d)&&(s.setHours(c),l())}else n()})),this.setupMousewheelEvents=function(b,c,d){var e=function(a){a.originalEvent&&(a=a.originalEvent);var b=a.wheelDelta?a.wheelDelta:-a.deltaY;return a.detail||b>0};b.bind("mousewheel wheel",function(b){B||a.$apply(e(b)?a.incrementHours():a.decrementHours()),b.preventDefault()}),c.bind("mousewheel wheel",function(b){B||a.$apply(e(b)?a.incrementMinutes():a.decrementMinutes()),b.preventDefault()}),d.bind("mousewheel wheel",function(b){B||a.$apply(e(b)?a.incrementSeconds():a.decrementSeconds()),b.preventDefault()})},this.setupArrowkeyEvents=function(b,c,d){b.bind("keydown",function(b){B||(38===b.which?(b.preventDefault(),a.incrementHours(),a.$apply()):40===b.which&&(b.preventDefault(),a.decrementHours(),a.$apply()))}),c.bind("keydown",function(b){B||(38===b.which?(b.preventDefault(),a.incrementMinutes(),a.$apply()):40===b.which&&(b.preventDefault(),a.decrementMinutes(),a.$apply()))}),d.bind("keydown",function(b){B||(38===b.which?(b.preventDefault(),a.incrementSeconds(),a.$apply()):40===b.which&&(b.preventDefault(),a.decrementSeconds(),a.$apply()))})},this.setupInputEvents=function(b,c,d){if(a.readonlyInput)return a.updateHours=angular.noop,a.updateMinutes=angular.noop,void(a.updateSeconds=angular.noop);var e=function(b,c,d){u.$setViewValue(null),u.$setValidity("time",!1),angular.isDefined(b)&&(a.invalidHours=b),angular.isDefined(c)&&(a.invalidMinutes=c),angular.isDefined(d)&&(a.invalidSeconds=d)};a.updateHours=function(){var a=h(),b=i();u.$setDirty(),angular.isDefined(a)&&angular.isDefined(b)?(s.setHours(a),s.setMinutes(b),z>s||s>A?e(!0):l("h")):e(!0)},b.bind("blur",function(b){u.$setTouched(),r()?m():null===a.hours||""===a.hours?e(!0):!a.invalidHours&&a.hours<10&&a.$apply(function(){a.hours=k(a.hours,!w)})}),a.updateMinutes=function(){var a=i(),b=h();u.$setDirty(),angular.isDefined(a)&&angular.isDefined(b)?(s.setHours(b),s.setMinutes(a),z>s||s>A?e(void 0,!0):l("m")):e(void 0,!0)},c.bind("blur",function(b){u.$setTouched(),r()?m():null===a.minutes?e(void 0,!0):!a.invalidMinutes&&a.minutes<10&&a.$apply(function(){a.minutes=k(a.minutes)})}),a.updateSeconds=function(){var a=j();u.$setDirty(),angular.isDefined(a)?(s.setSeconds(a),l("s")):e(void 0,void 0,!0)},d.bind("blur",function(b){r()?m():!a.invalidSeconds&&a.seconds<10&&a.$apply(function(){a.seconds=k(a.seconds)})})},this.render=function(){var b=u.$viewValue;isNaN(b)?(u.$setValidity("time",!1),e.error('Timepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.')):(b&&(s=b),z>s||s>A?(u.$setValidity("time",!1),a.invalidHours=!0,a.invalidMinutes=!0):m(),n())},a.showSpinners=angular.isDefined(c.showSpinners)?a.$parent.$eval(c.showSpinners):g.showSpinners,a.incrementHours=function(){a.noIncrementHours()||o(60*x*60)},a.decrementHours=function(){a.noDecrementHours()||o(60*-x*60)},a.incrementMinutes=function(){a.noIncrementMinutes()||o(60*y)},a.decrementMinutes=function(){a.noDecrementMinutes()||o(60*-y)},a.incrementSeconds=function(){a.noIncrementSeconds()||o(C)},a.decrementSeconds=function(){a.noDecrementSeconds()||o(-C)},a.toggleMeridian=function(){var b=i(),c=h();a.noToggleMeridian()||(angular.isDefined(b)&&angular.isDefined(c)?o(720*(s.getHours()<12?60:-60)):a.meridian=a.meridian===v[0]?v[1]:v[0])},a.blur=function(){u.$setTouched()},a.$on("$destroy",function(){for(;t.length;)t.shift()()})}]).directive("uibTimepicker",["uibTimepickerConfig",function(a){return{require:["uibTimepicker","?^ngModel"],controller:"UibTimepickerController",controllerAs:"timepicker",replace:!0,scope:{},templateUrl:function(b,c){return c.templateUrl||a.templateUrl},link:function(a,b,c,d){var e=d[0],f=d[1];f&&e.init(f,b.find("input"))}}}]),angular.module("ui.bootstrap.typeahead",["ui.bootstrap.debounce","ui.bootstrap.position"]).factory("uibTypeaheadParser",["$parse",function(a){var b=/^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)$/;return{parse:function(c){var d=c.match(b);if(!d)throw new Error('Expected typeahead specification in form of "_modelValue_ (as _label_)? for _item_ in _collection_" but got "'+c+'".');return{itemName:d[3],source:a(d[4]),viewMapper:a(d[2]||d[1]),modelMapper:a(d[1])}}}}]).controller("UibTypeaheadController",["$scope","$element","$attrs","$compile","$parse","$q","$timeout","$document","$window","$rootScope","$$debounce","$uibPosition","uibTypeaheadParser",function(a,b,c,d,e,f,g,h,i,j,k,l,m){function n(){O.moveInProgress||(O.moveInProgress=!0,O.$digest()),Z()}function o(){O.position=E?l.offset(b):l.position(b),O.position.top+=b.prop("offsetHeight")}var p,q,r=[9,13,27,38,40],s=200,t=a.$eval(c.typeaheadMinLength);t||0===t||(t=1),a.$watch(c.typeaheadMinLength,function(a){t=a||0===a?a:1});var u=a.$eval(c.typeaheadWaitMs)||0,v=a.$eval(c.typeaheadEditable)!==!1;a.$watch(c.typeaheadEditable,function(a){v=a!==!1});var w,x,y=e(c.typeaheadLoading).assign||angular.noop,z=c.typeaheadShouldSelect?e(c.typeaheadShouldSelect):function(a,b){var c=b.$event;return 13===c.which||9===c.which},A=e(c.typeaheadOnSelect),B=angular.isDefined(c.typeaheadSelectOnBlur)?a.$eval(c.typeaheadSelectOnBlur):!1,C=e(c.typeaheadNoResults).assign||angular.noop,D=c.typeaheadInputFormatter?e(c.typeaheadInputFormatter):void 0,E=c.typeaheadAppendToBody?a.$eval(c.typeaheadAppendToBody):!1,F=c.typeaheadAppendTo?a.$eval(c.typeaheadAppendTo):null,G=a.$eval(c.typeaheadFocusFirst)!==!1,H=c.typeaheadSelectOnExact?a.$eval(c.typeaheadSelectOnExact):!1,I=e(c.typeaheadIsOpen).assign||angular.noop,J=a.$eval(c.typeaheadShowHint)||!1,K=e(c.ngModel),L=e(c.ngModel+"($$$p)"),M=function(b,c){return angular.isFunction(K(a))&&q&&q.$options&&q.$options.getterSetter?L(b,{$$$p:c}):K.assign(b,c)},N=m.parse(c.uibTypeahead),O=a.$new(),P=a.$on("$destroy",function(){O.$destroy()});O.$on("$destroy",P);var Q="typeahead-"+O.$id+"-"+Math.floor(1e4*Math.random());b.attr({"aria-autocomplete":"list","aria-expanded":!1,"aria-owns":Q});var R,S;J&&(R=angular.element("<div></div>"),R.css("position","relative"),b.after(R),S=b.clone(),S.attr("placeholder",""),S.attr("tabindex","-1"),S.val(""),S.css({position:"absolute",top:"0px",left:"0px","border-color":"transparent","box-shadow":"none",opacity:1,background:"none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255)",color:"#999"}),b.css({position:"relative","vertical-align":"top","background-color":"transparent"}),R.append(S),S.after(b));var T=angular.element("<div uib-typeahead-popup></div>");T.attr({id:Q,matches:"matches",active:"activeIdx",select:"select(activeIdx, evt)","move-in-progress":"moveInProgress",query:"query",position:"position","assign-is-open":"assignIsOpen(isOpen)",debounce:"debounceUpdate"}),angular.isDefined(c.typeaheadTemplateUrl)&&T.attr("template-url",c.typeaheadTemplateUrl),angular.isDefined(c.typeaheadPopupTemplateUrl)&&T.attr("popup-template-url",c.typeaheadPopupTemplateUrl);var U=function(){J&&S.val("")},V=function(){O.matches=[],O.activeIdx=-1,b.attr("aria-expanded",!1),U()},W=function(a){return Q+"-option-"+a};O.$watch("activeIdx",function(a){0>a?b.removeAttr("aria-activedescendant"):b.attr("aria-activedescendant",W(a))});var X=function(a,b){return O.matches.length>b&&a?a.toUpperCase()===O.matches[b].label.toUpperCase():!1},Y=function(c,d){var e={$viewValue:c};y(a,!0),C(a,!1),f.when(N.source(a,e)).then(function(f){var g=c===p.$viewValue;if(g&&w)if(f&&f.length>0){O.activeIdx=G?0:-1,C(a,!1),O.matches.length=0;for(var h=0;h<f.length;h++)e[N.itemName]=f[h],O.matches.push({id:W(h),label:N.viewMapper(O,e),model:f[h]});if(O.query=c,o(),b.attr("aria-expanded",!0),H&&1===O.matches.length&&X(c,0)&&(angular.isNumber(O.debounceUpdate)||angular.isObject(O.debounceUpdate)?k(function(){O.select(0,d)},angular.isNumber(O.debounceUpdate)?O.debounceUpdate:O.debounceUpdate["default"]):O.select(0,d)),J){var i=O.matches[0].label;angular.isString(c)&&c.length>0&&i.slice(0,c.length).toUpperCase()===c.toUpperCase()?S.val(c+i.slice(c.length)):S.val("")}}else V(),C(a,!0);g&&y(a,!1)},function(){V(),y(a,!1),C(a,!0)})};E&&(angular.element(i).on("resize",n),h.find("body").on("scroll",n));var Z=k(function(){O.matches.length&&o(),O.moveInProgress=!1},s);O.moveInProgress=!1,O.query=void 0;var $,_=function(a){$=g(function(){Y(a)},u)},aa=function(){$&&g.cancel($)};V(),O.assignIsOpen=function(b){I(a,b)},O.select=function(d,e){var f,h,i={};x=!0,i[N.itemName]=h=O.matches[d].model,f=N.modelMapper(a,i),M(a,f),p.$setValidity("editable",!0),p.$setValidity("parse",!0),A(a,{$item:h,$model:f,$label:N.viewMapper(a,i),$event:e}),V(),O.$eval(c.typeaheadFocusOnSelect)!==!1&&g(function(){b[0].focus()},0,!1)}
vendor: 5,086 bytes, line 48994
48994,b.on("keydown",function(b){if(0!==O.matches.length&&-1!==r.indexOf(b.which)){var c=z(a,{$event:b});if(-1===O.activeIdx&&c||9===b.which&&b.shiftKey)return V(),void O.$digest();b.preventDefault();var d;switch(b.which){case 27:b.stopPropagation(),V(),a.$digest();break;case 38:O.activeIdx=(O.activeIdx>0?O.activeIdx:O.matches.length)-1,O.$digest(),d=T.find("li")[O.activeIdx],d.parentNode.scrollTop=d.offsetTop;break;case 40:O.activeIdx=(O.activeIdx+1)%O.matches.length,O.$digest(),d=T.find("li")[O.activeIdx],d.parentNode.scrollTop=d.offsetTop;break;default:c&&O.$apply(function(){angular.isNumber(O.debounceUpdate)||angular.isObject(O.debounceUpdate)?k(function(){O.select(O.activeIdx,b)},angular.isNumber(O.debounceUpdate)?O.debounceUpdate:O.debounceUpdate["default"]):O.select(O.activeIdx,b)})}}}),b.bind("focus",function(a){w=!0,0!==t||p.$viewValue||g(function(){Y(p.$viewValue,a)},0)}),b.bind("blur",function(a){B&&O.matches.length&&-1!==O.activeIdx&&!x&&(x=!0,O.$apply(function(){angular.isObject(O.debounceUpdate)&&angular.isNumber(O.debounceUpdate.blur)?k(function(){O.select(O.activeIdx,a)},O.debounceUpdate.blur):O.select(O.activeIdx,a)})),!v&&p.$error.editable&&(p.$setViewValue(),p.$setValidity("editable",!0),p.$setValidity("parse",!0),b.val("")),w=!1,x=!1});var ba=function(c){b[0]!==c.target&&3!==c.which&&0!==O.matches.length&&(V(),j.$$phase||a.$digest())};h.on("click",ba),a.$on("$destroy",function(){h.off("click",ba),(E||F)&&ca.remove(),E&&(angular.element(i).off("resize",n),h.find("body").off("scroll",n)),T.remove(),J&&R.remove()});var ca=d(T)(O);E?h.find("body").append(ca):F?angular.element(F).eq(0).append(ca):b.after(ca),this.init=function(b,c){p=b,q=c,O.debounceUpdate=p.$options&&e(p.$options.debounce)(a),p.$parsers.unshift(function(b){return w=!0,0===t||b&&b.length>=t?u>0?(aa(),_(b)):Y(b):(y(a,!1),aa(),V()),v?b:b?void p.$setValidity("editable",!1):(p.$setValidity("editable",!0),null)}),p.$formatters.push(function(b){var c,d,e={};return v||p.$setValidity("editable",!0),D?(e.$model=b,D(a,e)):(e[N.itemName]=b,c=N.viewMapper(a,e),e[N.itemName]=void 0,d=N.viewMapper(a,e),c!==d?c:b)})}}]).directive("uibTypeahead",function(){return{controller:"UibTypeaheadController",require:["ngModel","^?ngModelOptions","uibTypeahead"],link:function(a,b,c,d){d[2].init(d[0],d[1])}}}).directive("uibTypeaheadPopup",["$$debounce",function(a){return{scope:{matches:"=",query:"=",active:"=",position:"&",moveInProgress:"=",select:"&",assignIsOpen:"&",debounce:"&"},replace:!0,templateUrl:function(a,b){return b.popupTemplateUrl||"uib/template/typeahead/typeahead-popup.html"},link:function(b,c,d){b.templateUrl=d.templateUrl,b.isOpen=function(){var a=b.matches.length>0;return b.assignIsOpen({isOpen:a}),a},b.isActive=function(a){return b.active===a},b.selectActive=function(a){b.active=a},b.selectMatch=function(c,d){var e=b.debounce();angular.isNumber(e)||angular.isObject(e)?a(function(){b.select({activeIdx:c,evt:d})},angular.isNumber(e)?e:e["default"]):b.select({activeIdx:c,evt:d})}}}}]).directive("uibTypeaheadMatch",["$templateRequest","$compile","$parse",function(a,b,c){return{scope:{index:"=",match:"=",query:"="},link:function(d,e,f){var g=c(f.templateUrl)(d.$parent)||"uib/template/typeahead/typeahead-match.html";a(g).then(function(a){var c=angular.element(a.trim());e.replaceWith(c),b(c)(d)})}}}]).filter("uibTypeaheadHighlight",["$sce","$injector","$log",function(a,b,c){function d(a){return a.replace(/([.?*+^$[\]\\(){}|-])/g,"\\$1")}function e(a){return/<.*>/g.test(a)}var f;return f=b.has("$sanitize"),function(b,g){return!f&&e(b)&&c.warn("Unsafe use of typeahead please use ngSanitize"),b=g?(""+b).replace(new RegExp(d(g),"gi"),"<strong>$&</strong>"):b,f||(b=a.trustAsHtml(b)),b}}]),angular.module("uib/template/accordion/accordion-group.html",[]).run(["$templateCache",function(a){a.put("uib/template/accordion/accordion-group.html",'<div class="panel" ng-class="panelClass || \'panel-default\'">\n  <div role="tab" id="{{::headingId}}" aria-selected="{{isOpen}}" class="panel-heading" ng-keypress="toggleOpen($event)">\n    <h4 class="panel-title">\n      <a role="button" data-toggle="collapse" href aria-expanded="{{isOpen}}" aria-controls="{{::panelId}}" tabindex="0" class="accordion-toggle" ng-click="toggleOpen()" uib-accordion-transclude="heading"><span uib-accordion-header ng-class="{\'text-muted\': isDisabled}">{{heading}}</span></a>\n    </h4>\n  </div>\n  <div id="{{::panelId}}" aria-labelledby="{{::headingId}}" aria-hidden="{{!isOpen}}" role="tabpanel" class="panel-collapse collapse" uib-collapse="!isOpen">\n    <div class="panel-body" ng-transclude></div>\n  </div>\n</div>\n')}]),angular.module("uib/template/accordion/accordion.html",[]).run(["$templateCache",function(a){a.put("uib/template/accordion/accordion.html",'<div role="tablist" class="panel-group" ng-transclude></div>')}]),angular.module("uib/template/alert/alert.html",[]).run(["$templateCache",function(a){a.put("uib/template/alert/alert.html",'<div class="alert" ng-class="[\'alert-\' + (type || \'warning\'), closeable ? \'alert-dismissible\' : null]" role="alert">
48994\n    <button ng-show="closeable" type="button" class="close" ng-click="close({$event: $event})">\n        <span aria-hidden="true">&times;</span>\n        <span class="sr-only">Close</span>\n    </button>\n    <div ng-transclude></div>\n</div>\n')}]),angular.module("uib/template/carousel/carousel.html",[]).run(["$templateCache",function(a){a.put("uib/template/carousel/carousel.html",'<div ng-mouseenter="pause()" ng-mouseleave="play()" class="carousel" ng-swipe-right="prev()" ng-swipe-left="next()">\n  <div class="carousel-inner" ng-transclude></div>\n  <a role="button" href class="left carousel-control" ng-click="prev()" ng-class="{ disabled: isPrevDisabled() }" ng-show="slides.length > 1">\n    <span aria-hidden="true" class="glyphicon glyphicon-chevron-left"></span>\n    <span class="sr-only">previous</span>\n  </a>\n  <a role="button" href class="right carousel-control" ng-click="next()" ng-class="{ disabled: isNextDisabled() }" ng-show="slides.length > 1">\n    <span aria-hidden="true" class="glyphicon glyphicon-chevron-right"></span>\n    <span class="sr-only">next</span>\n  </a>\n  <ol class="carousel-indicators" ng-show="slides.length > 1">\n    <li ng-repeat="slide in slides | orderBy:indexOfSlide track by $index" ng-class="{ active: isActive(slide) }" ng-click="select(slide)">
48994\n      <span class="sr-only">slide {{ $index + 1 }} of {{ slides.length }}<span ng-if="isActive(slide)">, currently active</span></span>\n    </li>\n  </ol>\n</div>\n');
48995}]),angular.module("uib/template/carousel/slide.html",[]).run(["$templateCache",function(a){a.put("uib/template/carousel/slide.html",'<div ng-class="{\n    \'active\': active\n  }" class="item text-center" ng-transclude></div>\n')}]),angular.module("uib/template/datepicker/datepicker.html",[]).run(["$templateCache",function(a){a.put("uib/template/datepicker/datepicker.html",'<div class="uib-datepicker" ng-switch="datepickerMode" role="application" ng-keydown="keydown($event)">\n  <uib-daypicker ng-switch-when="day" tabindex="0"></uib-daypicker>\n  <uib-monthpicker ng-switch-when="month" tabindex="0"></uib-monthpicker>\n  <uib-yearpicker ng-switch-when="year" tabindex="0"></uib-yearpicker>\n</div>\n')}]),angular.module("uib/template/datepicker/day.html",[]).run(["$templateCache",function(a){a.put("uib/template/datepicker/day.html",'<table class="uib-daypicker" role="grid" aria-labelledby="{{::uniqueId}}-title" aria-activedescendant="{{activeDateId}}">\n  <thead>\n    <tr>\n      <th><button type="button" class="btn btn-default btn-sm pull-left uib-left" ng-click="move(-1)" tabindex="-1"><i class="glyphicon glyphicon-chevron-left"></i></button></th>\n      <th colspan="{{::5 + showWeeks}}"><button id="{{::uniqueId}}-title" role="heading" aria-live="assertive" aria-atomic="true" type="button" class="btn btn-default btn-sm uib-title" ng-click="toggleMode()" ng-disabled="datepickerMode === maxMode" tabindex="-1"><strong>{{title}}</strong></button></th>\n      <th><button type="button" class="btn btn-default btn-sm pull-right uib-right" ng-click="move(1)" tabindex="-1"><i class="glyphicon glyphicon-chevron-right"></i></button></th>\n    </tr>\n    <tr>\n      <th ng-if="showWeeks" class="text-center"></th>\n      <th ng-repeat="label in ::labels track by $index" class="text-center"><small aria-label="{{::label.full}}">{{::label.abbr}}</small></th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr class="uib-weeks" ng-repeat="row in rows track by $index">\n      <td ng-if="showWeeks" class="text-center h6"><em>{{ weekNumbers[$index] }}</em></td>\n      <td ng-repeat="dt in row" class="uib-day text-center" role="gridcell"\n        id="{{::dt.uid}}"\n        ng-class="::dt.customClass">\n        <button type="button" class="btn btn-default btn-sm"\n          uib-is-class="\n            \'btn-info\' for selectedDt,\n            \'active\' for activeDt\n            on dt"\n          ng-click="select(dt.date)"\n          ng-disabled="::dt.disabled"\n          tabindex="-1"><span ng-class="::{\'text-muted\': dt.secondary, \'text-info\': dt.current}">{{::dt.label}}</span></button>\n      </td>\n    </tr>\n  </tbody>\n</table>\n')}]),angular.module("uib/template/datepicker/month.html",[]).run(["$templateCache",function(a){a.put("uib/template/datepicker/month.html",'<table class="uib-monthpicker" role="grid" aria-labelledby="{{::uniqueId}}-title" aria-activedescendant="{{activeDateId}}">\n  <thead>\n    <tr>\n      <th><button type="button" class="btn btn-default btn-sm pull-left uib-left" ng-click="move(-1)" tabindex="-1"><i class="glyphicon glyphicon-chevron-left"></i></button></th>\n      <th><button id="{{::uniqueId}}-title" role="heading" aria-live="assertive" aria-atomic="true" type="button" class="btn btn-default btn-sm uib-title" ng-click="toggleMode()" ng-disabled="datepickerMode === maxMode" tabindex="-1"><strong>{{title}}</strong></button></th>\n      <th><button type="button" class="btn btn-default btn-sm pull-right uib-right" ng-click="move(1)" tabindex="-1"><i class="glyphicon glyphicon-chevron-right"></i></button></th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr class="uib-months" ng-repeat="row in rows track by $index">\n      <td ng-repeat="dt in row" class="uib-month text-center" role="gridcell"\n        id="{{::dt.uid}}"\n        ng-class="::dt.customClass">\n        <button type="button" class="btn btn-default"\n          uib-is-class="\n            \'btn-info\' for selectedDt,\n            \'active\' for activeDt\n            on dt"\n          ng-click="select(dt.date)"\
48995n          ng-disabled="::dt.disabled"\n          tabindex="-1"><span ng-class="::{\'text-info\': dt.current}">{{::dt.label}}</span></button>\n      </td>\n    </tr>\n  </tbody>\n</table>\n')}]),angular.module("uib/template/datepicker/year.html",[]).run(["$templateCache",function(a){a.put("uib/template/datepicker/year.html",'<table class="uib-yearpicker" role="grid" aria-labelledby="{{::uniqueId}}-title" aria-activedescendant="{{activeDateId}}">\n  <thead>\n    <tr>\n      <th><button type="button" class="btn btn-default btn-sm pull-left uib-left" ng-click="move(-1)" tabindex="-1"><i class="glyphicon glyphicon-chevron-left"></i></button></th>\n      <th colspan="{{::columns - 2}}"><button id="{{::uniqueId}}-title" role="heading" aria-live="assertive" aria-atomic="true" type="button" class="btn btn-default btn-sm uib-title" ng-click="toggleMode()" ng-disabled="datepickerMode === maxMode" tabindex="-1"><strong>{{title}}</strong></button></th>\n      <th><button type="button" class="btn btn-default btn-sm pull-right uib-right" ng-click="move(1)" tabindex="-1"><i class="glyphicon glyphicon-chevron-right"></i></button></th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr class="uib-years" ng-repeat="row in rows track by $index">\n      <td ng-repeat="dt in row" class="uib-year text-center" role="gridcell"\n        id="{{::dt.uid}}"\n        ng-class="::dt.customClass">\n        <button type="button" class="btn btn-default"\n          uib-is-class="\n            \'btn-info\' for selectedDt,\n            \'active\' for activeDt\n            on dt"\n          ng-click="select(dt.date)"\n          ng-disabled="::dt.disabled"\n          tabindex="-1"><span ng-class="::{\'text-info\': dt.current}">{{::dt.label}}</span></button>\n      </td>\n    </tr>\n  </tbody>\n</table>\n')}]),angular.module("uib/template/datepickerPopup/popup.html",[]).run(["$templateCache",function(a){a.put("uib/template/datepickerPopup/popup.html",'<div>\n  <ul class="uib-datepicker-popup dropdown-menu uib-position-measure" dropdown-nested ng-if="isOpen" ng-keydown="keydown($event)" ng-click="$event.stopPropagation()">\n    <li ng-transclude></li>\n    <li ng-if="showButtonBar" class="uib-button-bar">
48995\n      <span class="btn-group pull-left">\n        <button type="button" class="btn btn-sm btn-info uib-datepicker-current" ng-click="select(\'today\', $event)" ng-disabled="isDisabled(\'today\')">{{ getText(\'current\') }}</button>\n        <button type="button" class="btn btn-sm btn-danger uib-clear" ng-click="select(null, $event)">{{ getText(\'clear\') }}</button>\n      </span>\n      <button type="button" class="btn btn-sm btn-success pull-right uib-close" ng-click="close($event)">{{ getText(\'close\') }}</button>\n    </li>\n  </ul>\n</div>\n')}]),angular.module("uib/template/modal/backdrop.html",[]).run(["$templateCache",function(a){a.put("uib/template/modal/backdrop.html",'<div class="modal-backdrop"\n     uib-modal-animation-class="fade"\n     modal-in-class="in"\n     ng-style="{\'z-index\': 1040 + (index && 1 || 0) + index*10}"\n></div>\n')}]),angular.module("uib/template/modal/window.html",[]).run(["$templateCache",function(a){a.put("uib/template/modal/window.html",'<div modal-render="{{$isRendered}}" tabindex="-1" role="dialog" class="modal"\n    uib-modal-animation-class="fade"\n    modal-in-class="in"\n    ng-style="{\'z-index\': 1050 + index*10, display: \'block\'}">\n    <div class="modal-dialog {{size ? \'modal-\' + size : \'\'}}"><div class="modal-content" uib-modal-transclude></div></div>\n</div>\n')}]),angular.module("uib/template/pager/pager.html",[]).run(["$templateCache",function(a){a.put("uib/template/pager/pager.html",'<ul class="pager">\n  <li ng-class="{disabled: noPrevious()||ngDisabled, previous: align}"><a href ng-click="selectPage(page - 1, $event)">{{::getText(\'previous\')}}</a></li>\n  <li ng-class="{disabled: noNext()||ngDisabled, next: align}"><a href ng-click="selectPage(page + 1, $event)">{{::getText(\'next\')}}</a></li>\n</ul>\n')}]),angular.module("uib/template/pagination/pagination.html",[]).run(["$templateCache",function(a){a.put("uib/template/pagination/pagination.html",'<ul class="pagination">\n  <li ng-if="::boundaryLinks" ng-class="{disabled: noPrevious()||ngDisabled}" class="pagination-first"><a href ng-click="selectPage(1, $event)">{{::getText(\'first\')}}</a></li>\n  <li ng-if="::directionLinks" ng-class="{disabled: noPrevious()||ngDisabled}" class="pagination-prev"><a href ng-click="selectPage(page - 1, $event)">{{::getText(\'previous\')}}</a></li>\n  <li ng-repeat="page in pages track by $index" ng-class="{active: page.active,disabled: ngDisabled&&!page.active}" class="pagination-page"><a href ng-click="selectPage(page.number, $event)">{{page.text}}</a></li>\n  <li ng-if="::directionLinks" ng-class="{disabled: noNext()||ngDisabled}" class="pagination-next"><a href ng-click="selectPage(page + 1, $event)">{{::getText(\'next\')}}</a></li>\n  <li ng-if="::boundaryLinks" ng-class="{disabled: noNext()||ngDisabled}" class="pagination-last"><a href ng-click="selectPage(totalPages, $event)">{{::getText(\'last\')}}</a></li>\n</ul>\n')}]),angular.module("uib/template/tooltip/tooltip-html-popup.html",[]).run(["$templateCache",function(a){a.put("uib/template/tooltip/tooltip-html-popup.html",'<div class="tooltip"\n  tooltip-animation-class="fade"\n  uib-tooltip-classes\n  ng-class="{ in: isOpen() }">\n  <div class="tooltip-arrow"></div>\n  <div class="tooltip-inner" ng-bind-html="contentExp()"></div>\n</div>\n')}]),angular.module("uib/template/tooltip/tooltip-popup.html",[]).run(["$templateCache",function(a){a.put("uib/template/tooltip/tooltip-popup.html",'<div class="tooltip"\n  tooltip-animation-class="fade"\n  uib-tooltip-classes\n  ng-class="{ in: isOpen() }">\n  <div class="tooltip-arrow"></div>\n  <div class="tooltip-inner" ng-bind="content"></div>\n</div>\n')}]),angular.module("uib/template/tooltip/tooltip-template-popup.html",[]).run(["$templateCache",function(a){a.put("uib/template/tooltip/tooltip-template-popup.html",'<div class="tooltip"\n  tooltip-animation-class="fade"\n  uib-tooltip-classes\n  ng-class="{ in: isOpen() }">\n  <div class="tooltip-arrow"></div>\n  <div class="tooltip-inner"\n    uib-tooltip-template-transclude="contentExp()"\n    tooltip-template-transclude-scope="originScope()"></div>\n</div>\n')}]),angular.module("uib/template/popover/popover-html.html",[]).run(["$templateCache",function(a){a.put("uib/template/popover/popover-html.html",'<div class="popover"\n  tooltip-animation-class="fade"\n  uib-tooltip-classe
48995s\n  ng-class="{ in: isOpen() }">\n  <div class="arrow"></div>\n\n  <div class="popover-inner">\n      <h3 class="popover-title" ng-bind="uibTitle" ng-if="uibTitle"></h3>\n      <div class="popover-content" ng-bind-html="contentExp()"></div>\n  </div>\n</div>\n')}]),angular.module("uib/template/popover/popover-template.html",[]).run(["$templateCache",function(a){a.put("uib/template/popover/popover-template.html",'<div class="popover"\n  tooltip-animation-class="fade"\n  uib-tooltip-classes\n  ng-class="{ in: isOpen() }">\n  <div class="arrow"></div>\n\n  <div class="popover-inner">\n      <h3 class="popover-title" ng-bind="uibTitle" ng-if="uibTitle"></h3>\n      <div class="popover-content"\n        uib-tooltip-template-transclude="contentExp()"\n        tooltip-template-transclude-scope="originScope()"></div>\n  </div>\n</div>\n')}]),angular.module("uib/template/popover/popover.html",[]).run(["$templateCache",function(a){a.put("uib/template/popover/popover.html",'<div class="popover"\n  tooltip-animation-class="fade"\n  uib-tooltip-classes\n  ng-class="{ in: isOpen() }">\n  <div class="arrow"></div>\n\n  <div class="popover-inner">\n      <h3 class="popover-title" ng-bind="uibTitle" ng-if="uibTitle"></h3>\n      <div class="popover-content" ng-bind="content"></div>\n  </div>\n</div>\n')}]),angular.module("uib/template/progressbar/bar.html",[]).run(["$templateCache",function(a){a.put("uib/template/progressbar/bar.html",'<div class="progress-bar" ng-class="type && \'progress-bar-\' + type" role="progressbar" aria-valuenow="{{value}}" aria-valuemin="0" aria-valuemax="{{max}}" ng-style="{width: (percent < 100 ? percent : 100) + \'%\'}" aria-valuetext="{{percent | number:0}}%" aria-labelledby="{{::title}}" ng-transclude></div>\n')}]),angular.module("uib/template/progressbar/progress.html",[]).run(["$templateCache",function(a){a.put("uib/template/progressbar/progress.html",'<div class="progress" ng-transclude aria-labelledby="{{::title}}"></div>')}]),angular.module("uib/template/progressbar/progressbar.html",[]).run(["$templateCache",function(a){a.put("uib/template/progressbar/progressbar.html",'<div class="progress">\n  <div class="progress-bar" ng-class="type && \'progress-bar-\' + type" role="progressbar" aria-valuenow="{{value}}" aria-valuemin="0" aria-valuemax="{{max}}" ng-style="{width: (percent < 100 ? percent : 100) + \'%\'}" aria-valuetext="{{percent | number:0}}%" aria-labelledby="{{::title}}" ng-transclude></div>\n</div>\n')}]),angular.module("uib/template/rating/rating.html",[]).run(["$templateCache",function(a){a.put("uib/template/rating/rating.html",'<span ng-mouseleave="reset()" ng-keydown="onKeydown($event)" tabindex="0" role="slider" aria-valuemin="0" aria-valuemax="{{range.length}}" aria-valuenow="{{value}}" aria-valuetext="{{title}}">\n    <span ng-repeat-start="r in range track by $index" class="sr-only">({{ $index < value ? \'*\' : \' \' }})</span>\n    <i ng-repeat-end ng-mouseenter="enter($index + 1)" ng-click="rate($index + 1)" class="glyphicon" ng-class="$index < value && (r.stateOn || \'glyphicon-star\') || (r.stateOff || \'glyphicon-star-empty\')" ng-attr-title="{{r.title}}"></i>\n</span>\n')}]),angular.module("uib/template/tabs/tab.html",[]).run(["$templateCache",function(a){a.put("uib/template/tabs/tab.html",'<li ng-class="[{active: active, disabled: disabled}, classes]" class="uib-tab nav-item">\n  <a href ng-click="select($event)" class="nav-link" uib-tab-heading-transclude>{{heading}}</a>\n</li>\n')}]),angular.module("uib/template/tabs/tabset.html",[]).run(["$templateCache",function(a){a.put("uib/template/tabs/tabset.html",'<div>\n  <ul class="nav nav-{{tabset.type || \'tabs\'}}" ng-class="{\'nav-stacked\': vertical, \'nav-justified\': justified}" ng-transclude></ul>\n  <div class="tab-content">\n    <div class="tab-pane"\n         ng-repeat="tab in tabset.tabs"\n         ng-class="{active: tabset.active === tab.index}"\n         uib-tab-content-transclude="tab">\n    </div>\n  </div>\n</div>\n')}]),angular.module("uib/template/timepicker/timepicker.html",[]).run(["$templateCache",function(a){a.put("uib/template/timepicker/timepicker.html",'<table class="uib-timepicker">\n  <tbody>\n    <tr class="text-center" ng-show="::showSpinners">\n      <td class="uib-increment hours"><a ng-click="incrementHours()" ng-class="{disabled: noIncrementHours()}" class="btn btn-link" ng-disabled="noIncrementHours()" tabindex="{{::tabindex}}"><span class="glyphicon glyphicon-chevron-up"></span></a></td>\n      <td>&nbsp;</td>\n      <td class="uib-increment minutes"><a ng-click="incrementMinutes()" ng-class="{disabled: noIncrementMinutes()}" class="btn btn-link" ng-disabled="noIncrementMinutes()" tabindex="{{::tabindex}}"><span class="glyphicon glyphicon-chevron-up"></span></a></td>\n      <td ng-show="showSeconds">&nbsp;</td>\n      <td ng-show="showSeconds" class="uib-increment seconds"><a ng-click="incrementSeconds()" ng-class="{disabled: noIncrementSeconds()}" class="btn btn-link" ng-disabled="noIncrementSeconds()" tabindex="{{::tabindex}}"><span class="glyphicon glyphicon-chevron-up"></span></a></td>\n      <td ng-show="showMeridian"></td>\n    </tr>\n    <tr>\n      <td class="form-group uib-time hours" ng-class="{\'has-error\': invalidHours}">\n        <input type="text" placeholder="HH" ng-model="hours" ng-change="updateHours()" class="form-control text-center" ng-readonly="::readonlyInput" maxlength="2" tabindex="{{::tabindex}}" ng-disabled="noIncrementHours()" ng-blur="blur()">\n      </td>\n      <td class="uib-separator">:</td>\n      <td class="form-group uib-time minutes" ng-class="{\'has-error\': invalidMinutes}">\n        <input type="text" placeholder="MM" ng-model="minutes" ng-change="updateMinutes()" class="form-control text-center" ng-readonly="::readonlyInput" maxlength="2" tabindex="{{::tabindex}}" ng-disabled="noIncrementMinutes()" ng-blur="blur()">\n      </td>\n      <td ng-show="showSeconds" class="uib-separator">:</td>\n      <td class="form-group uib-time seconds" ng-class="{\'has-error\': invalidSeconds}" ng-show="showSeconds">\n        <input type="text" placeholder="SS" ng-model="seconds" ng-change="updateSeconds()" class="form-control text-center" ng-readonly="readonlyInput" maxlength="2" tabindex="{{::tabindex}}" ng-disabled="noIncrementSeconds()" ng-blur="blur()">\n      </td>\n      <td ng-show="showMeridian" class="uib-time am-pm"><button type="button" ng-class="{disabled: noToggleMeridian()}" class="btn btn-default text-center" ng-click="toggleMeridian()" ng-disabled="noToggleMeridian()" tabindex="{{::tabindex}}">{{meridian}}</button></td>\n    </tr>\n    <tr class="text-center" ng-show="::showSpinners">\n      <td class="uib-decrement hours"><a ng-click="decrementHours()" ng-class="{disabled: noDecrementHours()}" class="btn btn-link" ng-disabled="noDecrementHours()" tabindex="{{::tabindex}}"><span class="glyphicon glyphicon-chevron-down"></span></a></td>\n      <td>&nbsp;</td>\n      <td class="uib-decrement minutes"><a ng-click="decrementMinutes()" ng-class="{disabled: noDecrementMinutes()}" class="btn btn-link" ng-disabled="noDecrementMinutes()" tabindex="{{::tabindex}}"><span class="glyphicon glyphicon-chevron-down"></span></a></td>\n      <td ng-show="showSeconds">&nbsp;</td>\n      <td ng-show="showSeconds" class="uib-decrement seconds"><a ng-click="decrementSeconds()" ng-class="{disabled: noDecrementSeconds()}" class="btn btn-link" ng-disabled="noDecrementSeconds()" tabindex="{{::tabindex}}"><span class="glyphicon glyphicon-chevron-down"></span></a></td>\n      <td ng-show="showMeridian"></td>\n    </tr>\n  </tbody>\n</table>\n')}]),angular.module("uib/template/typeahead/typeahead-match.html",[]).run(["$templateCache",function(a){a.put("uib/template/typeahead/typeahead-match.html",'<a href\n   tabindex="-1"\n   ng-bind-html="match.label | uibTypeaheadHighlight:query"\n   ng-attr-title="{{match.label}}"></a>\n')}]),angular.module("uib/template/typeahead/typeahead-popup.html",[]).run(["$templateCache",function(a){a.put("uib/template/typeahead/typeahead-popup.html",'<ul class="dropdown-menu" ng-show="isOpen() && !moveInProgress" ng-style="{top: position().top+\'px\', left: position().left+\'px\'}" role="listbox" aria-hidden="{{!isOpen()}}">\n    <li ng-repeat="match in matches track by $index" ng-class="{active: isActive($index) }" ng-mouseenter="selectActive($index)" ng-click="selectMatch($index, $event)" role="option" id="{{::match.id}}">\n        <div uib-typeahead-match index="$index" match="match" query="query" template-url="templateUrl"></div>\n    </li>\n</ul>\n')}]),angular.module("ui.bootstrap.carousel").run(function(){!angular.$$csp().noInlineStyle&&!
48995angular.$$uibCarouselCss&&angular.element(document).find("head").prepend('<style type="text/css">.ng-animate.item:not(.left):not(.right){-webkit-transition:0s ease-in-out left;transition:0s ease-in-out left}</style>'),angular.$$uibCarouselCss=!0}),angular.module("ui.bootstrap.datepicker").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibDatepickerCss&&angular.element(document).find("head").prepend('<style type="text/css">.uib-datepicker .uib-title{width:100%;}.uib-day button,.uib-month button,.uib-year button{min-width:100%;}.uib-left,.uib-right{width:100%}</style>'),angular.$$uibDatepickerCss=!0}),angular.module("ui.bootstrap.position").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibPositionCss&&angular.element(document).find("head").prepend('<style type="text/css">.uib-position-measure{display:block !important;visibility:hidden !important;position:absolute !important;top:-9999px !important;left:-9999px !important;}.uib-position-scrollbar-measure{position:absolute !important;top:-9999px !important;width:50px !important;height:50px !important;overflow:scroll !important;}.uib-position-body-scrollbar-measure{overflow:scroll !important;}</style>'),angular.$$uibPositionCss=!0}),angular.module("ui.bootstrap.datepickerPopup").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibDatepickerpopupCss&&angular.element(document).find("head").prepend('<style type="text/css">.uib-datepicker-popup.dropdown-menu{display:block;float:none;margin:0;}.uib-button-bar{padding:10px 9px 2px;}</style>'),angular.$$uibDatepickerpopupCss=!0}),angular.module("ui.bootstrap.tooltip").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibTooltipCss&&angular.element(document).find("head").prepend('<style type="text/css">[uib-tooltip-popup].tooltip.top-left > .tooltip-arrow,[uib-tooltip-popup].tooltip.top-right > .tooltip-arrow,[uib-tooltip-popup].tooltip.bottom-left > .tooltip-arrow,[uib-tooltip-popup].tooltip.bottom-right > .tooltip-arrow,[uib-tooltip-popup].tooltip.left-top > .tooltip-arrow,[uib-tooltip-popup].tooltip.left-bottom > .tooltip-arrow,[uib-tooltip-popup].tooltip.right-top > .tooltip-arrow,[uib-tooltip-popup].tooltip.right-bottom > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.top-left > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.top-right > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.bottom-left > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.bottom-right > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.left-top > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.left-bottom > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.right-top > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.right-bottom > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.top-left > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.top-right > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.bottom-left > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.bottom-right > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.left-top > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.left-bottom > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.right-top > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.right-bottom > .tooltip-arrow,[uib-popover-popup].popover.top-left > .arrow,[uib-popover-popup].popover.top-right > .arrow,[uib-popover-popup].popover.bottom-left > .arrow,[uib-popover-popup].popover.bottom-right > .arrow,[uib-popover-popup].popover.left-top > .arrow,[uib-popover-popup].popover.left-bottom > .arrow,[uib-popover-popup].popover.right-top > .arrow,[uib-popover-popup].popover.right-bottom > .arrow,[uib-popover-html-popup].popover.top-left > .arrow,[uib-popover-html-popup].popover.top-right > .arrow,[uib-popover-html-popup].popover.bottom-left > .arrow,[uib-popover-html-popup].popover.bottom-right > .arrow,[uib-popover-html-popup].popover.left-top > .arrow,[uib-popover-html-popup].popover.left-bottom > .arrow,[uib-popover-html-popup].popover.right-top > .arrow,[uib-popover-html-popup].popover.right-bottom > .arrow,[uib-popover-template-popup].popover.top-left > .arrow,[uib-popover-template-popup].popover.top-right > .arrow,[uib-popover-template-popup].popover.bottom-left > .arrow,[uib-popover-template-popup].popover.bottom-right > .arrow,[uib-popover-template-popup].popover.left-top > .arrow,[uib-popover-template-popup].popover.left-bottom > .arrow,[uib-popover-template-popup].popover.right-top > .arrow,[uib-popover-template-popup].popover.right-bottom > .arrow{top:auto;bottom:auto;left:auto;right:auto;margin:0;}[uib-popover-popup].popover,[uib-popover-html-popup].popover,[uib-popover-template-popup].popover{display:block !important;}</style>'),angular.$$uibTooltipCss=!0}),angular.module("ui.bootstrap.timepicker").run(function(){!angular.$$csp().noInlineStyle&&!
48995angular.$$uibTimepickerCss&&angular.element(document).find("head").prepend('<style type="text/css">.uib-time input{width:50px;}</style>'),angular.$$uibTimepickerCss=!0}),angular.module("ui.bootstrap.typeahead").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibTypeaheadCss&&angular.element(document).find("head").prepend('<style type="text/css">[uib-typeahead-popup].dropdown-menu{display:block;}</style>'),angular.$$uibTypeaheadCss=!0});
48996$(document).ready(setWrapBottomPadding);
48997$(window).resize(setWrapBottomPadding);
48998
48999function setWrapBottomPadding() {
49000	$('.wrap').css({
49001		paddingBottom: $('.footer').outerHeight()
49002	})
49003}
49004
49005function setCookie(cname, cvalue, cexpires) {
49006	var d = new Date();
49007	if(typeof cexpires != "undefined") {
49008	    d.setTime(d.getTime() + cexpires);
49009	} else {
49010    	d.setTime(d.getTime() + (24*60*60*1000));
49011    }
49012    var expires = "expires="+d.toUTCString();
49013    document.cookie = cname + "=" + cvalue + "; " + expires;
49014}
49015
49016function getCookie(cname) {
49017	var name = cname + "=";
49018    var ca = document.cookie.split(';');
49019    for(var i=0; i<ca.length; i++) {
49020        var c = ca[i];
49021        while (c.charAt(0)==' ') {
49022        	c = c.substring(1);
49023        }
49024        if (c.indexOf(name) == 0) {
49025        	return c.substring(name.length,c.length);
49026        }
49027    }
49028    return null;
49029}
49030
49031function resizeSweetAlert() {
49032	var sweetAlerts = $('.sweet-alert');
49033
49034	if(sweetAlerts.length > 0) {
49035		var windowHeight = $(window).height();
49036		sweetAlerts.each(function() {
49037			var maxHeight = windowHeight - $(this).offset().top;
49038			$(this).css('max-height', maxHeight + 'px');
49039		});
49040	}
49041}
49042
49043$(document).ready(function() {
49044    setLiefergebietSearchPosition();
49045});
49046$(window).resize(function() {
49047    setLiefergebietSearchPosition();
49048});
49049
49050function setLiefergebietSearchPosition() {
49051    var input = $('.liefergebiet-input');
49052    if(input.length > 0) {
49053        var positionTopResults = input.offset().top + input.outerHeight();
49054        var widthResults = input.outerWidth() - 6;
49055
49056        $('.liefergebiete-results').css({
49057            top: positionTopResults,
49058            width: widthResults
49059        });
49060    }
49061}
49062
49063// function alert(text) {
49064// 	if(typeof text != "string" && typeof text != "number") {
49065// 		text = JSON.stringify(text);
49066// 	}
49067// 	swal({
49068// 		text: text,
49069// 		type: "info"
49070// 	});
49071// }
49072
49073String.prototype.capitalizeFirstLetter = function() {
49074    return this.charAt(0).toUpperCase() + this.slice(1);
49075};
49076String.prototype.startsWith = function(param) {
49077	return this.indexOf(param) == 0;
49078};
49079
49080Number.prototype.formatMoney = function(c, d, t){
49081	var n = this,
49082	c = isNaN(c = Math.abs(c)) ? 2 : c,
49083	d = d == undefined ? "." : d,
49084	t = t == undefined ? "," : t,
49085	s = n < 0 ? "-" : "",
49086	i = parseInt(n = Math.abs(+n || 0).toFixed(c)) + "",
49087	j = (j = i.length) > 3 ? j % 3 : 0;
49088	return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : "");
49089 };
49090
49091var app = angular.module('shop', ['ngSanitize', 'ngRoute', 'ui.bootstrap'])
49092
49093.controller('Ctrl', function($rootScope, $http, $timeout, $compile, $filter, $location, $compile, $route, $routeParams, $templateCache) {
49094	$rootScope.version = 'v2';
49095	$rootScope.$watch('warenkorb', function(newValue) {
49096		if($rootScope.config['all-is-loaded']) {
49097            $rootScope.checkZutaten();
49098			var cookieWarenkorb = [];
49099			for(var g in $rootScope.warenkorb) {
49100				cookieWarenkorb.push({
49101					id: $rootScope.warenkorb[g].id,
49102					currentAnzahl: $rootScope.warenkorb[g].currentAnzahl,
49103					currentZutaten: $rootScope.warenkorb[g].currentZutaten,
49104					currentPreis: $rootScope.warenkorb[g].currentPreis,
49105					currentBemerkung: $rootScope.warenkorb[g].currentBemerkung,
49106					zutatenWahlReihenfolge: $rootScope.warenkorb[g].zutatenWahlReihenfolge
49107				});
49108			}
49109			if($rootScope.currentFilialId != 0) {
49110				$rootScope.setLocalObject("warenkorb" + $rootScope.currentFilialId, cookieWarenkorb);
49111			}
49112		}
49113
49114		$rootScope.checkForBoni();
49115        $rootScope.checkCouponData();
49116	}, true);
49117
49118    $rootScope.$watch('currentGericht', function() {
49119        var gericht = $rootScope.currentGericht;
49120        $rootScope.checkZutatenCurrentGericht();
49121    }, true);
49122
49123    $rootScope.$watch("currentCoupons", function(newValue) {
49124        if($rootScope.config['all-is-loaded']) {
49125            var cookieCoupons = [];
49126            for(var c in $rootScope.currentCoupons) {
49127                cookieCoupons.push({
49128                    code: $rootScope.currentCoupons[c].code,
49129					currentZutaten: $rootScope.currentCoupons[c].currentZutaten,
49130					currentPreis: $rootScope.currentCoupons[c].currentPreis,
49131					zutatenWahlReihenfolge: $rootScope.currentCoupons[c].zutatenWahlReihenfolge
49132                });
49133            }
49134
49135            if($rootScope.currentFilialId != 0) {
49136                $rootScope.setLocalObject("coupons" + $rootScope.currentFilialId, cookieCoupons);
49137            }
49138        }
49139    }, true);
49140
49141    $rootScope.$watch('formData.chooseLieferadresse', function() {
49142        if(
49143            typeof $rootScope.formData.chooseLieferadresse != "undefined"
49144            && $rootScope.formData.chooseLieferadresse != "null"
49145        ) {
49146            var adresse = JSON.parse($rootScope.formData.chooseLieferadresse);
49147
49148            $rootScope.setLieferadresseProfil(adresse);
49149            $rootScope.formData.chooseLieferadresse = "null";
49150        }
49151    });
49152
49153	$rootScope.$watch('currentKat', function() {
49154		$rootScope.renderAnimate();
49155	}, true);
49156
49157	$rootScope.$watch('gerichte', function() {
49158		$rootScope.renderAnimate();
49159	}, true);
49160
49161	Date.prototype.toString = function() {
49162		var tag = this.getDate();
49163		if(tag < 10) {
49164			tag = '0' + tag;
49165		}
49166		var monat = this.getMonth() + 1;
49167		if(monat < 10) {
49168			monat = '0' + monat;
49169		}
49170		var jahr = this.getFullYear();
49171
49172		return tag+'.'+monat+'.'+jahr;
49173	};
49174
49175	$rootScope.miniIcons = {
49176		0: {
49177			class: "",
49178			color: ""
49179		},
49180		1: {
49181			class: "vf-hot",
49182			color: "#D47871",
49183			title: "Scharf",
49184			cfClass: 'scharf'
49185		},
49186		2: {
49187			class: "vf-garlic",
49188			color: "#D4CE9D",
49189			title: "Knoblauch",
49190			cfClass: 'knoblauch'
49191		},
49192		3: {
49193			class: "vf-fish",
49194			color: "#AEC3D4",
49195			title: "Fisch",
49196			cfClass: 'fisch'
49197		},
49198		4: {
49199			class: "vf-veggi",
49200			color: "#89D487",
49201			title: "Vegetarisch",
49202			cfClass: 'veggi'
49203		},
49204		5: {
49205			class: "vf-newEck",
49206			color: "#FF674C"
49207		},
49208		6: {
49209			class: "nii-halal-sign",
49210			color: "#FF674C",
49211			title: "Vegetarisch",
49212			cfClass: 'veggi'
49213		},
49214		7: {
49215			class: "vf-newEck",
49216			color: "#FF674C",
49217			title: "Vegetarisch",
49218			cfClass: 'veggi'
49219		},
49220		8: {
49221			class: "vf-newEck",
49222			color: "#FF674C",
49223			title: "Vegetarisch",
49224			cfClass: 'veggi'
49225		},
49226		9: {
49227			class: "vf-newEck",
49228			color: "#FF674C",
49229			title: "Vegetarisch",
49230			cfClass: 'veggi'
49231		},
49232		10: {
49233			class: "vf-newEck",
49234			color: "#FF674C",
49235			title: "Vegetarisch",
49236			cfClass: 'veggi'
49237		},
49238		11: {
49239			class: "vf-newEck",
49240			color: "#FF674C",
49241			title: "Vegetarisch",
49242			cfClass: 'veggi'
49243		}
49244	};
49245
49246	//$rootScope.websiteLoaded = false;
49247	$rootScope.websiteChangeLoading = false;
49248	$rootScope.loading = false;
49249	$rootScope.favoritHinzufuegenLaden = {};
49250
49251	$rootScope.config = {};
49252	$rootScope.config['kategorie-bilder'] = kategorieBilder;
49253	$rootScope.config['zusatzstoffe'] = zusatzStoffe;
49254	$rootScope.config['zeitpunkt-zeiten'] = [];
49255	$rootScope.config['zeitpunkt-daten'] = [];
49256	$rootScope.config['all-is-loaded'] = false;
49257	$rootScope.config['check-liefergebiet'] = false;
49258	$rootScope.config['is-open'] = false;
49259	$rootScope.config['hat-shop'] = false;
49260	$rootScope.config['is-angebot'] = false;
49261	$rootScope.config['site-referer'] = 'http://www.clickfood.de';
49262	$rootScope.config['alle-liefergebiete'] = [];
49263	$rootScope.config['liefergebiet-search-index'] = 0;
49264	$rootScope.config['liefergebiete-plzs'] = [];
49265	$rootScope.config['website-loaded'] = false;
49266	$rootScope.config['lieferadresse-setted'] = false;
49267	$rootScope.config['last-visited'] = null;
49268	$rootScope.config['warenkorb-geladen'] = false;
49269	$rootScope.config['boni-geladen'] = false;
49270	$rootScope.config['boni-is-loading'] = false;
49271	$rootScope.config['current-date'] = (new Date()).toString();
49272    $rootScope.config['kat-anzahl-to-col-xs'] = ['12', '12', '12', '12', '12', '12', '12'];
49273    $rootScope.config['kat-anzahl-to-col-md'] = ['12', '12', '6', '4', '3', '2', '2'];
49274
49275	$rootScope.formErrors = [];
49276
49277	$rootScope.carouselConfig = {
49278        xsWidth: 0,
49279        smWidth: 720,
49280        mdWidth: 940,
49281        lgWidth: 1140
49282	};
49283
49284	$rootScope.animateLeftConfig = {
49285		origin: 'left'
49286	};
49287  	$rootScope.animateRightConfig = {
49288		origin: 'right'
49289	};
49290	$rootScope.animateShop2Config = {
49291		duration: 500,
49292		origin: 'top',
49293		distance:'50px',
49294		delay: 200,
49295		opacity:0,
49296		scale:0.5,
49297		easing:'linear',
49298		mobile:true,
49299		viewFactor: '0.2'
49300	};
49301
49302	$rootScope.filialId = null;
49303	$rootScope.faxId = 0;
49304	$rootScope.shopSettings = {};
49305	$rootScope.formData = {
49306        chooseLieferadresse: "null"
49307    };
49308
49309    var toCheckHours = (new Date()).getHours();
49310    if(toCheckHours < 6) {
49311        var d = new Date();
49312        d.setDate(d.getDate()-1);
49313        $rootScope.currentDatum = d.toString();
49314    } else {
49315	    $rootScope.currentDatum = (new Date()).toString();
49316    }
49317	$rootScope.currentUhrzeit = 'sofort';
49318	$rootScope.uhrzeitLocalstorageLoaded = false;
49319	$rootScope.currentOeffnungszeiten = {};
49320	$rootScope.currentZeitpunktSetted = false;
49321	$rootScope.currentFilialId = 0;
49322	$rootScope.currentType = 'liefer';
49323	$rootScope.currentReferer = 'portal';
49324	$rootScope.currentKat = 1;
49325	$rootScope.currentGericht = null;
49326	$rootScope.currentGerichtIndex = false;
49327	$rootScope.currentAnzahl = 1;
49328	$rootScope.currentLiefergebiet = {
49329		liefergebuehr: 0,
49330		mindestbestellwert: 0
49331	};
49332	$rootScope.currentLiefergebietSetted = false;
49333	$rootScope.currentStep = 'auswahl';
49334    $rootScope.changeCurrentStep = true;
49335	$rootScope.currentOeffnungszeiten = [];
49336	$rootScope.currentTreuepreis = null;
49337	$rootScope.currentTreuepunkte = 0;
49338	$rootScope.currentPaypalApprovalLink = '';
49339	$rootScope.currentLiefergebieteSwitch = [];
49340	$rootScope.currentBoniAuswahl = null;
49341	$rootScope.angebotsKategorien = [];
49342	$rootScope.userLieferadressen = [];
49343	$rootScope.queryLiefergebiet = null;
49344    $rootScope.filialAuswahlOffen = false;
49345    $rootScope.currentCouponCode = null;
49346    $rootScope.currentCoupons = [];
49347    $rootScope.usedCoupons = [];
49348    $rootScope.usedCodes = [];
49349    $rootScope.couponWithNotCombiningRabatt = false;
49350    $rootScope.couponRabattKomplett = 0;
49351    $rootScope.edittingCouponIndex = -1;
49352
49353	$rootScope.benutzerFavoriten = [];
49354
49355	$rootScope.$watch('currentTreuepreis', function(newValue) {
49356		// $timeout(function() {
49357			$rootScope.currentTreuepreis = newValue;
49358		// });
49359		if($rootScope.currentFilialId != 0) {
49360			$rootScope.setLocalObject('treuepreis' + $rootScope.currentFilialId, newValue);
49361		}
49362	});
49363
49364	$rootScope.lastCurrentDatum = null;
49365	$rootScope.lastCurrentUhrzeit = null;
49366	$rootScope.lastCurrentType = null;
49367
49368	$rootScope.warenkorb = [];
49369	$rootScope.kundenDaten = {
49370
49371	};
49372	$rootScope.bezahlDaten = {
49373		trinkgeld: null,
49374		art: '1'
49375	};
49376
49377	$rootScope.loadedKategorien = {};
49378	$rootScope.kategorien = {};
49379	$rootScope.gerichte = {};
49380	$rootScope.trenner = {};
49381	$rootScope.boni = {};
49382	$rootScope.boniWithOptions = {};
49383	$rootScope.selectedBoni = null;
49384	$rootScope.autoZusatzstoffe = false;
49385	$rootScope.zusatzStoffeGeneriert = {};
49386	$rootScope.treuepreise = [];
49387	$rootScope.angebotsKategorien = 0;
49388	$rootScope.uhrzeitTouched = false;
49389	if(typeof ScrollReveal != "undefined") {
49390		$rootScope.scrollReveal = new ScrollReveal();
49391	}
49392
49393	$rootScope.reset = function() {
49394		$rootScope.currentStep = 'auswahl';
49395
49396		$rootScope.warenkorb = [];
49397		$rootScope.bezahlDaten.trinkgeld = null;
49398		$rootScope.kundenDaten.agb = false;
49399		$rootScope.treuepreis = [];
49400		$rootScope.selectedBoni = null;
49401		$rootScope.faxId = null;
49402		// $rootScope.deleteCookie("filialAuswahl" + $rootScope.currentFilialId);
49403		$rootScope.deleteCookie("coupons" + $rootScope.currentFilialId);
49404        $rootScope.currentCoupons = [];
49405	};
49406
49407	$rootScope.$watch('currentDatum', function(newValue, oldValue) {
49408		if(newValue === oldValue) {
49409			return;
49410		}
49411
49412		// $timeout(function() {
49413			$rootScope.currentDatum = newValue;
49414		// });
49415		if($rootScope.currentFilialId != 0) {
49416			$rootScope.setLocalObject('datum' + $rootScope.currentFilialId, newValue);
49417		}
49418		$rootScope.loadIsOpen();
49419		$rootScope.getUhrzeiten(newValue);
49420
49421	}, true);
49422
49423	$rootScope.$watch('currentUhrzeit', function(newValue, oldValue) {
49424		if(newValue === oldValue) {
49425			return;
49426		}
49427		// $timeout(function() {
49428			$rootScope.currentUhrzeit = newValue;
49429		// });
49430		if($rootScope.currentFilialId != 0) {
49431			$rootScope.setLocalObject('uhrzeit' + $rootScope.currentFilialId, newValue);
49432		}
49433		$rootScope.loadIsOpen();
49434		$rootScope.loadKategorien();
49435	}, true);
49436
49437	$rootScope.$watch('currentLiefergebiet', function(newValue) {
49438		// $timeout(function() {
49439			$rootScope.currentLiefergebiet = newValue;
49440		// });
49441		if($rootScope.currentFilialId != 0) {
49442			$rootScope.setLocalObject('liefergebiet' + $rootScope.currentFilialId, newValue);
49443		}
49444	}, true);
49445
49446	$rootScope.$watch('currentType', function(newValue, oldValue) {
49447		if(newValue === oldValue) {
49448			return;
49449		}
49450
49451		console.log(newValue, oldValue);
49452		$rootScope.currentType = newValue;
49453
49454        $rootScope.refreshParams();
49455
49456		if($rootScope.currentFilialId != 0) {
49457			$rootScope.setLocalObject('type' + $rootScope.currentFilialId, newValue);
49458		}
49459	}, true);
49460
49461    $rootScope.refreshParams = function(doLoad) {
49462        $rootScope.loadKategorien(undefined, doLoad);
49463		$rootScope.loadBoni();
49464		$rootScope.getUhrzeiten($rootScope.currentDatum);
49465    };
49466
49467	$rootScope.$watch('selectedBoni', function(newValue) {
49468		if(typeof newValue != "undefined") {
49469			if($rootScope.selectedBoni === null) {
49470				var boniObj = null;
49471			} else {
49472				var boniObj = {
49473					id: $rootScope.selectedBoni.id,
49474					zutaten: $rootScope.selectedBoni.zutatenAuswahl,
49475					removed: $rootScope.selectedBoni.removed
49476				};
49477			}
49478
49479			if($rootScope.currentFilialId != 0) {
49480				$rootScope.setLocalObject('optionsBoni' + $rootScope.currentFilialId, boniObj);
49481			}
49482		}
49483	}, true);
49484
49485	// $rootScope.$watch('gerichte', function(val1, val2) {
49486	// 	$rootScope.setOwlCarousel();
49487	// }, true);
49488
49489	$rootScope.renderAnimate = function() {
49490		return;
49491		// window.sr = ScrollReveal();
49492		if($rootScope.scrollReveal !== undefined) {
49493			var sr = $rootScope.scrollReveal;
49494
49495			$timeout(function() {
49496				if($('.animateLeftWide').length > 0) {
49497					// console.log("blub");
49498				    sr.reveal('.animateLeftWide', {
49499				      duration: 500,origin: 'left',distance:'500px',  delay: 200,opacity:0,scale:0.1,easing:'linear',  mobile:true,  viewFactor: '0.2'
49500				    });
49501				}
49502
49503				if($('.animateRightWide').length > 0) {
49504					// console.log("blib");
49505				    sr.reveal('.animateRightWide', {
49506				      duration: 500,origin: 'right',distance:'500px',delay: 200,  opacity:0,  scale:0.1,easing:'linear',mobile:true,viewFactor: '0.2'
49507				    });
49508				}
49509			});
49510		}
49511	};
49512
49513	$rootScope.render = function() {
49514		$('[data-toggle="tooltip"]').tooltip()
49515
49516        $('input').each(function() {
49517            if(!$(this).is('[class^="magic-"]')) {
49518                $(this).iCheck({ //:not(.treuepunkte-auswahl)
49519        			checkboxClass: 'icheckbox_square-grey',
49520        			radioClass: 'iradio_square-grey',
49521        			increaseArea: '20%' // optional
49522        		});
49523            }
49524        });
49525
49526		$('input.zutat-xor').on('ifClicked', function() {
49527			if(!$(this).is(':checked')) {
49528				$(this).iCheck('check');
49529			}
49530			$rootScope.setZutat($(this).data('props-index'), $(this).data('zutat-index'));
49531		});
49532
49533		$('input.groesse-auswahl').on('ifClicked', function() {
49534			if(!$(this).is(':checked')) {
49535				$(this).iCheck('check');
49536			}
49537			$rootScope.setGroesse($(this).data('groesse-index'));
49538		});
49539
49540		$('input.bezahlart-auswahl,input.bezahlmethode-input').on('ifClicked', function() {
49541			var elem = $(this);
49542			if(!elem.is(':checked')) {
49543				elem.iCheck('check');
49544			}
49545
49546			$timeout(function() {
49547				$rootScope.bezahlDaten.art = elem.data('art');
49548			});
49549		});
49550
49551		$('input.anrede-auswahl').on('ifClicked', function() {
49552			var elem = $(this);
49553			if(!elem.is(':checked')) {
49554				elem.iCheck('check');
49555			}
49556
49557			$timeout(function() {
49558				$rootScope.kundenDaten.anrede = elem.data('anrede');
49559			});
49560		});
49561
49562		$('input.lieferadresse-anrede-auswahl').on('ifClicked', function() {
49563			var elem = $(this);
49564			if(!elem.is(':checked')) {
49565				elem.iCheck('check');
49566			}
49567
49568			$timeout(function() {
49569				$rootScope.lieferadresseAnrede = elem.val();
49570			});
49571		});
49572
49573		$('input.registrieren-anrede-auswahl').on('ifClicked', function() {
49574			var elem = $(this);
49575			if(!elem.is(':checked')) {
49576				elem.iCheck('check');
49577			}
49578
49579			$timeout(function() {
49580				$rootScope.registrierenAnrede = elem.val();
49581			});
49582		});
49583
49584		$('input.agb-auswahl').on('ifClicked', function() {
49585			var elem = $(this);
49586			$timeout(function() {
49587				if(elem.is(':checked')) {
49588					$rootScope.kundenDaten.agb = true;
49589				} else {
49590					$rootScope.kundenDaten.agb = false;
49591				}
49592			});
49593		});
49594
49595		$('input.liefergebiet-auswahl').on('ifClicked', function() {
49596			if(!$(this).is(':checked')) {
49597				$(this).iCheck('check');
49598			}
49599
49600			$rootScope.setLiefergebiet($(this).data('liefergebiet'));
49601		});
49602
49603		$('input.treuepunkte-auswahl').on('ifClicked', function() {
49604			var curThis = $(this);
49605			if(!$(this).is(':checked')) {
49606				$(this).iCheck('check');
49607			}
49608
49609			var tId = $(this).data('preis-id');
49610			var tPunkte = $(this).data('preis-punkte');
49611			var nPunkte = $(this).data('nutzer-punkte');
49612			$rootScope.setTreuepreis(tId, tPunkte, nPunkte);
49613		});
49614
49615		// $('input.treuepunkte-auswahl').on('ifChecked', function() {
49616		// 	var tId = $(this).data('preis-id');
49617		// 	var tPunkte = $(this).data('preis-punkte');
49618		// 	var nPunkte = $(this).data('nutzer-punkte');
49619		// 	$rootScope.setTreuepreis(tId, tPunkte, nPunkte);
49620		// });
49621	};
49622
49623	$rootScope.init = function(filialId, type, shopSettings, referer, oeffnungszeiten, benutzerTreuepunkte, hatShop, isSwitch, siteReferer) {
49624		// localStorage.removeItem('uhrzeit' + filialId);
49625		// localStorage.removeItem('datum' + filialId);
49626		// localStorage.removeItem('liefergebiet' + filialId);
49627
49628        if(shopSettings.abgeschaltet == 1) {
49629            swal({
49630                title: "Bestellen nicht möglich!",
49631                text: shopSettings.abschaltText,
49632                type: "error"
49633            });
49634        }
49635
49636		if(!shopSettings.hatLiefer && shopSettings.hatAbhol) {
49637			$rootScope.openNurAbholungHint();
49638		}
49639
49640		$rootScope.config['site-referer'] = siteReferer;
49641		$rootScope.loadTreuepreise();
49642		$rootScope.currentFilialId = filialId;
49643		$rootScope.currentType = type
49644		$rootScope.currentReferer = referer;
49645		$rootScope.shopSettings = shopSettings;
49646		$rootScope.currentTreuepunkte = benutzerTreuepunkte;
49647		$rootScope.config['hat-shop'] = hatShop;
49648		//$rootScope.currentOeffnungszeiten = oeffnungszeiten;
49649		$rootScope.autoZusatzstoffe = ($rootScope.shopSettings.autoZusatzstoffe == 'on');
49650		$rootScope.nextStep();
49651		$rootScope.loadBoni();
49652		$rootScope.loadFavoriten();
49653
49654		if(isSwitch) {
49655			$rootScope.setLocalObject("filialAuswahl" + $rootScope.currentFilialId, 'true');
49656		}
49657		$rootScope.loadAllLiefergebiete();
49658		$rootScope.loadLieferadressen();
49659
49660		var heute = new Date();
49661        var toCheckHours = (new Date()).getHours();
49662        if(toCheckHours < 6) {
49663            var d = new Date();
49664            d.setDate(d.getDate()-1);
49665            heute = d;
49666        }
49667		$rootScope.config['zeitpunkt-daten'].push(heute.toString());
49668		for(var i = 1; i <= 6; i++) {
49669			heute.setDate(heute.getDate() + 1);
49670			$rootScope.config['zeitpunkt-daten'].push(heute.toString());
49671		}
49672
49673		if($rootScope.getLocalObject("liefergebiet" + $rootScope.currentFilialId) != null) {
49674			var liefergebiet = $rootScope.getLocalObject("liefergebiet" + $rootScope.currentFilialId);
49675			if(typeof liefergebiet.ort != 'undefined') {
49676				$rootScope.currentLiefergebiet = $rootScope.getLocalObject("liefergebiet" + $rootScope.currentFilialId);
49677				$rootScope.currentLiefergebietSetted = true;
49678				$rootScope.formData.liefergebieteSearch = $rootScope.currentLiefergebiet.anzeige;
49679				$rootScope.formData.liefergebieteSearchSetted = true;
49680			}
49681		}
49682
49683		if($rootScope.getLocalObject("datum" + $rootScope.currentFilialId) != null) {
49684			$rootScope.currentDatum = $rootScope.getLocalObject("datum" + $rootScope.currentFilialId);
49685		}
49686
49687		if($rootScope.getLocalObject("uhrzeit" + $rootScope.currentFilialId) != null) {
49688			$rootScope.currentUhrzeit = $rootScope.getLocalObject("uhrzeit" + $rootScope.currentFilialId);
49689			$rootScope.uhrzeitLocalstorageLoaded = true;
49690		}
49691
49692		if($rootScope.getLocalObject("type" + $rootScope.currentFilialId) != null) {
49693			if(
49694				!$rootScope.shopSettings.hatAbhol && $rootScope.getLocalObject("type" + $rootScope.currentFilialId) == "abhol"
49695				|| !$rootScope.shopSettings.hatLiefer && $rootScope.getLocalObject("type" + $rootScope.currentFilialId) == "liefer"
49696			) {
49697
49698			} else {
49699				$rootScope.currentType = $rootScope.getLocalObject("type" + $rootScope.currentFilialId);
49700			}
49701		}
49702
49703		if($rootScope.getLocalObject("kundendaten" + $rootScope.currentFilialId) != null) {
49704			$rootScope.kundenDaten = $rootScope.getLocalObject("kundendaten" + $rootScope.currentFilialId);
49705			$rootScope.kundenDaten.agb = false;
49706			$rootScope.config['lieferadresse-setted'] = true;
49707		}
49708
49709		if($rootScope.getDateFromString($rootScope.currentDatum + " 00:00") < $rootScope.getDateFromString((new Date()).toString() + " 00:00")) {
49710			$rootScope.currentDatum = (new Date()).toString();
49711		}
49712
49713		if($rootScope.config['check-liefergebiet']) {
49714			$rootScope.checkLiefergebiet();
49715		}
49716
49717		// $timeout(function() {
49718			if($rootScope.config['check-liefergebiet']) {
49719				$rootScope.checkLiefergebiet();
49720			}
49721		// });
49722
49723		$rootScope.checkFiliale();
49724		$rootScope.getUhrzeiten($rootScope.currentDatum.toString());
49725		//$rootScope.loadSpeisekarte();
49726		$rootScope.loadKategorien();
49727		$rootScope.loadBoni();
49728
49729		if($('.zeitpunkt-modal').length > 0) {
49730			$('.zeitpunkt-modal').on('hide.bs.modal', function() {
49731				//$rootScope.loadSpeisekarte();
49732				$rootScope.loadKategorien();
49733				$rootScope.loadBoni();
49734			});
49735		}
49736
49737		$rootScope.loadCookieGericht();
49738        $rootScope.loadCookieCoupons();
49739		$rootScope.websiteLoaded = true;
49740		// if(
49741		// 	$rootScope.currentType == "liefer" && (
49742		// 		!$rootScope.currentLiefergebiet.postleitzahl
49743		// 		|| $rootScope.currentLiefergebiet.postleitzahl == ""
49744		// 		|| $rootScope.currentLiefergebiet.postleitzahl == 0
49745		// 	)
49746		// ) {
49747		// 	$rootScope.openLiefergebietModal();
49748		// }
49749	};
49750
49751	$rootScope.nextStep = function() {
49752		// $('.loadOverlay').hide();
49753		// if($rootScope.currentType != "liefer" && $rootScope.currentType != "abhol") {
49754		// 	$rootScope.openTypeModal();
49755		// } else if($rootScope.currentType == "liefer" && !$rootScope.currentLiefergebietSetted) {
49756		// 	$rootScope.openLiefergebietModal();
49757		// } else if(!$rootScope.currentZeitpunktSetted) {
49758		// 	$rootScope.openUhrzeitenModal();
49759		// } else {
49760		// 	$rootScope.loadSpeisekarte();
49761		// }
49762	};
49763
49764	$rootScope.setBestaetigen = function() {
49765		$(window).scrollTop();
49766		$rootScope.currentStep = "bestaetigen";
49767	};
49768
49769	$rootScope.setAuswahl = function() {
49770		$(window).scrollTop();
49771		$rootScope.currentStep = "auswahl";
49772	};
49773
49774	$rootScope.bestellen = function(redirectTo) {
49775		// $http.post('?cl=api&mt=apiLog&logArt=clickBestellen', {}).then(function(){});
49776
49777		$rootScope.currentStep = 'uebertragen';
49778		$rootScope.websiteChangeLoading = true;
49779		$rootScope.config['uebetragen-lange-timeout'] = window.setTimeout(function() {
49780			$rootScope.sendingVeryLong = true;
49781		}, 90000);
49782		$rootScope.bezahlDaten.abholOderLiefer = $rootScope.currentType;
49783		$rootScope.bezahlDaten.treuepreis = $rootScope.currentTreuepreis;
49784
49785		var kundenDaten = {...$rootScope.kundenDaten};
49786		if(kundenDaten.personen) {
49787			if(kundenDaten.bemerkung) {
49788				kundenDaten.bemerkung += ' | Personen: ' + kundenDaten.personen;
49789			} else {
49790				kundenDaten.bemerkung = 'Personen: ' + kundenDaten.personen;
49791			}
49792		}
49793
49794		$http.post('?cl=index&mt=apiBestellen', {
49795			filialId: $rootScope.currentFilialId,
49796			warenKorb: $rootScope.warenkorb,
49797			kundenDaten: kundenDaten,
49798			bezahlDaten: $rootScope.bezahlDaten,
49799			gesamtBetrag: $rootScope.getGesamtBetrag(),
49800			zeitpunktDatum: $rootScope.currentDatum,
49801			zeitpunktUhrzeit: $rootScope.currentUhrzeit,
49802			referer: $rootScope.currentReferer,
49803			optionsBonus: $rootScope.selectedBoni,
49804            coupons: $rootScope.currentCoupons,
49805		}).then(function(response) {
49806    		$rootScope.websiteChangeLoading = false;
49807
49808			if(response.data.success) {
49809				$http.post('?cl=api&mt=apiLog&logArt=responseBestellenSuccess', {response: response}).then(function(){});
49810                if(typeof redirectTo != "undefined") {
49811                    $rootScope.changeCurrentStep = false;
49812                    $location.path(redirectTo);
49813                }
49814				if($rootScope.bezahlDaten.art == 3) {
49815					$rootScope.currentPaypalApprovalLink = response.data.approval_link;
49816					location.href = response.data.approval_link;
49817				} else if(
49818					$rootScope.bezahlDaten.art == 2
49819					|| $rootScope.bezahlDaten.art == 4
49820					|| $rootScope.bezahlDaten.art == 6
49821					|| $rootScope.bezahlDaten.art == 7
49822				) {
49823					$rootScope.currentRedirectLink = response.data.redirect_link;
49824					location.href = response.data.redirect_link;
49825				} else if(
49826					$rootScope.bezahlDaten.art == 8
49827				) {
49828					$rootScope.currentRedirectLink = response.data.redirect_link;
49829					location.href = response.data.redirect_link;
49830				} else if(
49831					$rootScope.bezahlDaten.art == 15
49832				) {
49833					$rootScope.currentRedirectLink = response.data.redirect_link;
49834					location.href = response.data.redirect_link;
49835				} else {
49836					$rootScope.reset();
49837					$rootScope.currentStep = 'erfolgreich';
49838				}
49839			} else {
49840                if(response.data.code == 20001) {
49841                    swal({
49842                        title: 'Bestellen nicht möglich!',
49843                        text: response.data.message,
49844                        type: 'error'
49845                    });
49846                    return;
49847                }
49848
49849				var isError = (typeof response.data.errors == "undefined" || $rootScope.getObjectLength(response.data.errors) == 0);
49850
49851				if(isError) {
49852					$http.post('?cl=api&mt=apiLog&logArt=responseBestellenFail&isError=true', {response: response}).then(function(){});
49853				} else {
49854					$http.post('?cl=api&mt=apiLog&logArt=responseBestellenFail&isError=false', {response: response}).then(function(){});
49855				}
49856
49857				var liste = $('<div>');
49858				for(var e in response.data.errors) {
49859					liste.append($('<div>').html(response.data.errors[e].message));
49860				}
49861
49862				swal({
49863					title: "Es sind Fehler aufgetreten",
49864					type: "error",
49865					html: liste,
49866					confirmButtonColor: "red",
49867					confirmButtonText: "Fehler beheben"
49868				});
49869
49870				// alert(string);
49871
49872				$rootScope.currentStep = 'bestaetigen';
49873			}
49874		}, function(response) {
49875			$http.post('?cl=api&mt=apiLog&logArt=responseBestellenFail', {response: response});
49876			alert("Es ist ein Fehler aufgetreten.\nVersuchen Sie es erneut.");
49877			$rootScope.currentStep = 'bestaetigen';
49878		});
49879	};
49880
49881	$rootScope.reorderOrder = function(bestellung) {
49882		for(var p in bestellung.positions) {
49883			$rootScope.getGerichtById(bestellung.positions[p].gerichtObj.id, null, function(realObj, savedObj) {
49884				$rootScope.currentGericht = $rootScope.clone(realObj);
49885				$rootScope.currentGericht.currentZutaten = savedObj.zutatenObj;
49886				$rootScope.currentGericht.currentPreis = savedObj.groesse;
49887				$rootScope.currentGericht.currentAnzahl = savedObj.anzahl;
49888
49889				$rootScope.addGerichtV2();
49890
49891				$location.path('/cl/index/warenkorbv2');
49892			}, $rootScope.clone(bestellung.positions[p]));
49893		}
49894	};
49895
49896	$rootScope.addGerichtBySaved = function(realObj, savedObj) {
49897	};
49898
49899	$rootScope.paypalExecute = function(success, isFax, faxId, bestellArt, bestellNr) {
49900		$rootScope.currentStep = 'uebertragen';
49901		$rootScope.paypalExecuteSuccess = success;
49902
49903		$rootScope.reset();
49904		$rootScope.currentStep = 'erfolgreich';
49905
49906		// if(bestellArt == 'fax') {
49907		// 	$rootScope.faxId = faxId;
49908		// 	$rootScope.bestellNr = bestellNr;
49909		// 	$timeout($rootScope.isFaxSend, 10000);
49910		// 	$rootScope.currentStep = 'uebertragen';
49911		// } else if(bestellArt == 'bon') {
49912		// 	$rootScope.bestellNr = bestellNr;
49913		// 	$timeout($rootScope.isBonPrinted, 10000);
49914		// 	$rootScope.currentStep = 'uebertragen';
49915		// } else if(bestellArt == 'tel') {
49916		// 	$rootScope.bestellNr = bestellNr;
49917		// 	$timeout($rootScope.isCalled, 10000);
49918		// 	$rootScope.currentStep = 'uebertragen';
49919		// } else {
49920		// 	$rootScope.reset();
49921		// 	$rootScope.currentStep = 'erfolgreich';
49922		// }
49923	};
49924
49925	$rootScope.hpExecute = function(isFax, faxId, bestellArt, bestellNr) {
49926		$rootScope.currentStep = 'uebertragen';
49927		$rootScope.reset();
49928		$rootScope.currentStep = 'erfolgreich';
49929
49930		// if(bestellArt == 'fax') {
49931		// 	$rootScope.faxId = faxId;
49932		// 	$rootScope.bestellNr = bestellNr;
49933		// 	$timeout($rootScope.isFaxSend, 10000);
49934		// 	$rootScope.currentStep = 'uebertragen';
49935		// } else if(bestellArt == 'bon') {
49936		// 	$rootScope.bestellNr = bestellNr;
49937		// 	$timeout($rootScope.isBonPrinted, 10000);
49938		// 	$rootScope.currentStep = 'uebertragen';
49939		// } else if(bestellArt == 'tel') {
49940		// 	$rootScope.bestellNr = bestellNr;
49941		// 	$timeout($rootScope.isCalled, 10000);
49942		// 	$rootScope.currentStep = 'uebertragen';
49943		// } else {
49944		// 	$rootScope.reset();
49945		// 	$rootScope.currentStep = 'erfolgreich';
49946		// }
49947	};
49948
49949	$rootScope.checkLiefergebiet = function() {
49950		if($rootScope.getLocalObject("liefergebiet" + $rootScope.currentFilialId) != null) {
49951			var liefergebiet = $rootScope.getLocalObject("liefergebiet" + $rootScope.currentFilialId);
49952			if(typeof liefergebiet.ort != 'undefined') {
49953				$rootScope.currentLiefergebiet = $rootScope.getLocalObject("liefergebiet" + $rootScope.currentFilialId);
49954				$rootScope.currentLiefergebietSetted = true;
49955				$rootScope.formData.liefergebieteSearch = $rootScope.currentLiefergebiet.anzeige;
49956				$rootScope.liefergebieteSearchSetted = true;
49957			}
49958		}
49959
49960		$rootScope.config['hat-shop'] = true;
49961		if($rootScope.config['hat-shop']) {
49962			if(
49963				$rootScope.currentType == "liefer" && (
49964					!$rootScope.currentLiefergebiet.postleitzahl
49965					|| $rootScope.currentLiefergebiet.postleitzahl == ""
49966					|| $rootScope.currentLiefergebiet.postleitzahl == 0
49967					|| !$rootScope.currentLiefergebiet.ort
49968					|| $rootScope.currentLiefergebiet.ort.trim().length == 0
49969				)
49970			) {
49971                if(!$rootScope.filialAuswahlOffen) {
49972				    $rootScope.openLieferOptionenModal();
49973                }
49974			}
49975		}
49976	};
49977
49978    $rootScope.openLiefergebietIfLiefer = function() {
49979        if($rootScope.currentType == "liefer") {
49980            $rootScope.openLieferOptionenModal();
49981        }
49982    };
49983
49984	$rootScope.checkFiliale = function() {
49985		var filialeCookie = $rootScope.getLocalObject("filialAuswahl" + $rootScope.currentFilialId);
49986		if(filialeCookie === null) {
49987            $rootScope.closeLieferOptionenModal();
49988			$rootScope.currentLiefergebiet = {
49989				liefergebuehr: 0,
49990				mindestbestellwert: 0
49991			};
49992			$rootScope.currentLiefergebietSetted = false;
49993			$rootScope.formData.liefergebieteSearch = undefined;
49994			$rootScope.liefergebieteSearchSetted = false;
49995			$rootScope.loadFilialen($rootScope.openFilialauswahlModal);
49996		}
49997	};
49998
49999	$rootScope.isFaxSend = function() {
50000		$http.post('?cl=api&mt=faxSend', {
50001			faxId: $rootScope.faxId,
50002			bestellNr: $rootScope.bestellNr
50003		}).then(function(response) {
50004			// $http.post('?cl=api&mt=apiLog&logArt=userSendFaxSuccess', {response: response}).then(function(){});
50005			if(response.data.success) {
50006				$http.post('?cl=api&mt=apiLog&logArt=userSendFaxSuccess', {response: response}).then(function(){});
50007
50008				$rootScope.reset();
50009				$rootScope.currentStep = 'erfolgreich';
50010				$rootScope.sendingVeryLong = false;
50011				window.clearTimeout($rootScope.config['uebetragen-lange-timeout']);
50012			} else {
50013				var code = response.data.code;
50014				if(code == 90003) {
50015					$timeout($rootScope.isFaxSend, 10000);
50016				} else if(code == 90010) { //bon-sendung
50017					$rootScope.isBonPrinted();
50018				} else if(code == 90011) { //tel-sendung
50019					$rootScope.isCalled();
50020				} else if(code == 90034) {
50021					swal({
50022						title: "Bestellung abgebrochen",
50023						text: "Leider konnte deine Bestellung nicht an das Restaurant weitergeleitet werden.",
50024						type: "error"
50025					});
50026					$rootScope.currentStep = 'bestaetigen';
50027				} else {
50028					$http.post('?cl=api&mt=apiLog&logArt=userSendFaxFail', {response: response}).then(function(){});
50029					alert("Es ist ein Fehler aufgetreten. Versuche es erneut.");
50030					$rootScope.currentStep = 'bestaetigen';
50031				}
50032			}
50033		}, function(response) {
50034			$http.post('?cl=api&mt=apiLog&logArt=userSendFaxFail', {response: response}).then(function(){});
50035			alert("Es ist ein Fehler aufgetreten. Versuche es erneut.");
50036			$rootScope.currentStep = 'bestaetigen';
50037		});
50038	};
50039
50040	$rootScope.isBonPrinted = function() {
50041		$http.post('?cl=api&mt=bonPrinted', {
50042			bestellNr: $rootScope.bestellNr
50043		}).then(function(response) {
50044			if(response.data.success) {
50045				$http.post('?cl=api&mt=apiLog&logArt=userSendBonSuccess', {response: response}).then(function(){});
50046
50047				$rootScope.reset();
50048				$rootScope.currentStep = 'erfolgreich';
50049				$rootScope.sendingVeryLong = false;
50050				window.clearTimeout($rootScope.config['uebetragen-lange-timeout']);
50051			} else {
50052				var code = response.data.code;
50053				if(code == 90003) {
50054					$timeout($rootScope.isBonPrinted, 10000);
50055				} else if(code == 90012) { //fax-sendung
50056					$rootScope.isFaxSend();
50057				} else if(code == 90011) { //tel-sendung
50058					$rootScope.isCalled();
50059				} else if(code == 90034) {
50060					alert("Leider konnte deine Bestellung nicht an das Restaurant weitergeleitet werden.");
50061					$rootScope.currentStep = 'bestaetigen';
50062				} else {
50063					$http.post('?cl=api&mt=apiLog&logArt=userSendBonFail', {response: response}).then(function(){});
50064					alert("Es ist ein Fehler aufgetreten. Versuche es erneut.");
50065					$rootScope.currentStep = 'bestaetigen';
50066				}
50067			}
50068		}, function(response) {
50069			$http.post('?cl=api&mt=apiLog&logArt=userSendBonFail', {response: response}).then(function(){});
50070			alert("Es ist ein Fehler aufgetreten. Versuche es erneut.");
50071			$rootScope.currentStep = 'bestaetigen';
50072		});
50073	};
50074
50075	$rootScope.isCalled = function() {
50076		$http.post('?cl=api&mt=called', {
50077			bestellNr: $rootScope.bestellNr
50078		}).then(function(response) {
50079			if(response.data.success) {
50080				$http.post('?cl=api&mt=apiLog&logArt=userSendTelSuccess', {response: response}).then(function(){});
50081
50082				$rootScope.reset();
50083				$rootScope.currentStep = 'erfolgreich';
50084				$rootScope.sendingVeryLong = false;
50085				window.clearTimeout($rootScope.config['uebetragen-lange-timeout']);
50086			} else {
50087				var code = response.data.code;
50088				if(code == 90003) {
50089					$timeout($rootScope.isCalled, 10000);
50090				} else if(code == 90034) {
50091					alert("Leider konnte deine Bestellung nicht an das Restaurant weitergeleitet werden.");
50092					$rootScope.currentStep = 'bestaetigen';
50093				} else {
50094					$http.post('?cl=api&mt=apiLog&logArt=userSendTelFail', {response: response}).then(function(){});
50095					alert("Es ist ein Fehler aufgetreten. Versuche es erneut.");
50096					$rootScope.currentStep = 'bestaetigen';
50097				}
50098			}
50099		}, function(response) {
50100			$http.post('?cl=api&mt=apiLog&logArt=userSendTelFail', {response: response}).then(function(){});
50101			alert("Es ist ein Fehler aufgetreten. Versuche es erneut.");
50102			$rootScope.currentStep = 'bestaetigen';
50103		});
50104	}
50105
50106	$rootScope.loadTreuepreise = function() {
50107		$http.get(
50108			"?cl=index&mt=apiLoadTreuepreise"
50109		, {}).then(function(data) {
50110			$rootScope.treuepreise = data.data;
50111
50112			$rootScope.treuepreise.sort(function(v1, v2) {
50113				if(v1.punkte < v2.punkte) {
50114					return -1;
50115				}
50116
50117				if(v1.punkte > v2.punkte) {
50118					return 1;
50119				}
50120
50121				return 0;
50122			});
50123
50124			if($rootScope.getLocalObject("treuepreis" + $rootScope.currentFilialId) != null) {
50125				$rootScope.currentTreuepreis = $rootScope.getLocalObject("treuepreis" + $rootScope.currentFilialId);
50126			} else {
50127				if($rootScope.isProzentKomplett()) {
50128					$rootScope.currentTreuepreis = 'prozentKomplett';
50129				} else {
50130					$rootScope.currentTreuepreis = 0;
50131				}
50132			}
50133
50134			if(
50135				!$rootScope.getTreuepreisById($rootScope.currentTreuepreis)
50136				|| $rootScope.getTreuepreisById($rootScope.currentTreuepreis).punkte > $rootScope.currentTreuepunkte
50137			) {
50138				if(
50139					!$rootScope.currentTreuepreis == 'prozentKomplett'
50140					|| !$rootScope.isProzentKomplett()
50141				) {
50142					$rootScope.currentTreuepreis = 0;
50143				}
50144			}
50145		});
50146	};
50147
50148	$rootScope.loadFavoriten = function() {
50149		$http.get('?cl=api&mt=favoriten')
50150			.then(function(data) {
50151				$rootScope.benutzerFavoriten = data.data;
50152			});
50153	};
50154
50155	$.notify.addStyle('cf-notify', {
50156		html: '<div><i class="fa fa-check"></i> <span data-notify-text/></div>',
50157		classes: {
50158			'success': {
50159				'background-color': '#DFF0D8',
50160				'border-color': '#468847',
50161				'color': '#468847'
50162			}
50163		}
50164	});
50165
50166	$rootScope.addFavorite = function(gericht) {
50167		$rootScope.favoritHinzufuegenLaden[gericht.nummer] = true;
50168
50169		$http.post('?cl=api&mt=addFavorite', {nummer: gericht.nummer}).
50170			then(function(data) {
50171				if(data.data.success) {
50172					$rootScope.benutzerFavoriten.push(gericht.nummer);
50173					$.notify("Favorit wurde hinzugefügt", {
50174						style: 'cf-notify'
50175					});
50176				} else {
50177					if(data.data.error == 1001) {
50178						swal('Anmeldung erforderlich!', 'Du musst dich angemeldet haben, um Favoriten deinem Nutzerkonto hinzufügen zu können.', 'warning');
50179					} else {
50180						swal('Fehler', 'Es ist ein Fehler aufgetreten. Versuche es bitte noch einmal.', 'error');
50181					}
50182				}
50183				$rootScope.favoritHinzufuegenLaden[gericht.nummer] = false;
50184			}, function() {
50185				$rootScope.favoritHinzufuegenLaden[gericht.nummer] = false;
50186				swal('Fehler', 'Es ist ein Fehler aufgetreten. Versuche es bitte noch einmal.', 'error');
50187			});
50188	};
50189
50190	$rootScope.removeFavorite = function(gericht, doRefresh) {
50191		$rootScope.favoritHinzufuegenLaden[gericht.nummer] = true;
50192
50193		$http.post('?cl=api&mt=removeFavorite', {nummer: gericht.nummer}).
50194			then(function(data) {
50195				if(data.data.success) {
50196					if($rootScope.benutzerFavoriten.indexOf(gericht.nummer) > -1) {
50197						$rootScope.benutzerFavoriten.splice($rootScope.benutzerFavoriten.indexOf(gericht.nummer), 1);
50198						$.notify("Favorit wurde entfernt", {
50199							style: 'cf-notify'
50200						});
50201
50202						if(typeof doRefresh != 'undefined' && doRefresh === true) {
50203							$route.reload();
50204						}
50205					}
50206				} else {
50207					if(data.data.error == 1001) {
50208						swal('Anmeldung erforderlich!', 'Du musst dich angemeldet haben, um Favoriten deinem Nutzerkonto hinzufügen zu können.', 'warning');
50209					} else {
50210						swal('Fehler', 'Es ist ein Fehler aufgetreten. Versuche es bitte noch einmal.', 'error');
50211					}
50212				}
50213				$rootScope.favoritHinzufuegenLaden[gericht.nummer] = false;
50214			}, function() {
50215				$rootScope.favoritHinzufuegenLaden[gericht.nummer] = false;
50216				swal('Fehler', 'Es ist ein Fehler aufgetreten. Versuche es bitte noch einmal.', 'error');
50217			});
50218	};
50219
50220	$rootScope.setTreuepreis = function(value, punkteNoetig, punkteHat) {
50221		if(punkteNoetig > punkteHat) {
50222			return false;
50223		}
50224
50225		$timeout(function() {
50226			$rootScope.currentTreuepreis = value;
50227		});
50228	};
50229
50230	$rootScope.loadCookieGericht = function() {
50231		if(
50232			typeof $rootScope.getLocalObject("warenkorb" + $rootScope.currentFilialId) != "undefined"
50233			&& $rootScope.getLocalObject("warenkorb" + $rootScope.currentFilialId) != null
50234		) {
50235			var cookieWarenkorb = $rootScope.getLocalObject("warenkorb" + $rootScope.currentFilialId);
50236			for(var ckw in cookieWarenkorb) {
50237				var gericht = $rootScope.getGerichtById(cookieWarenkorb[ckw].id, cookieWarenkorb[ckw]);
50238			}
50239		}
50240	};
50241
50242	$rootScope.scrollToKategorie = function(katId) {
50243		const katZeile = document.querySelector('.shop-v5-kategorie-zeile[data-kat-id="' + katId + '"]');
50244		const katZeilen = document.querySelector('.shop-v5-kategorie-zeilen');
50245		if(katZeile && katZeilen) {
50246			const katZeileRect = katZeile.getBoundingClientRect();
50247			const containerRect = katZeilen.getBoundingClientRect();
50248			const offset = katZeileRect.left - containerRect.left + katZeilen.scrollLeft - (containerRect.width / 2) + (katZeileRect.width / 2);
50249			katZeilen.scrollTo({ left: offset, behavior: 'smooth' });
50250		}
50251	};
50252
50253	var lastKategorien = null;
50254	$rootScope.loadKategorien = function(angebotsKategorien, doLoad) {
50255		if(
50256			$rootScope.currentDatum == $rootScope.lastCurrentDatum
50257			&& $rootScope.currentUhrzeit == $rootScope.lastCurrentUhrzeit
50258			&& $rootScope.currentType == $rootScope.lastCurrentType
50259            && !doLoad
50260		) {
50261			return true;
50262		}
50263
50264		$rootScope.loading = true;
50265		$rootScope.angebotsKategorien = angebotsKategorien;
50266
50267		var newLastKategorien = $rootScope.currentType + "-" + $rootScope.currentDatum + "-" + $rootScope.currentUhrzeit + "-" + $rootScope.config['is-angebot'];
50268		if(newLastKategorien == lastKategorien) { return; }
50269		lastKategorien = newLastKategorien;
50270
50271		$http.get(
50272			"?cl=index&mt=apiKategorien"
50273			+ "&abholLiefer=" + $rootScope.currentType
50274			+ "&datum=" + $rootScope.currentDatum
50275			+ "&uhrzeit=" + $rootScope.currentUhrzeit
50276			+ "&angebot=" + $rootScope.config['is-angebot']
50277		, {}).then(function(data){
50278            if(data.data.error == true) {
50279				$rootScope.loading = false;
50280                return;
50281            }
50282			var selectedKatValid = false;
50283			$rootScope.loadedKategorien = data.data.data.kategorien;
50284			$rootScope.kategorien = $rootScope.clone($rootScope.loadedKategorien);
50285
50286			if($rootScope.getObjectLength($rootScope.kategorien) == 0) {
50287				$rootScope.loading = false;
50288				return;
50289			}
50290
50291			if(
50292				$rootScope.getLocalObject("kategorie" + $rootScope.currentFilialId) != null
50293				&& typeof $rootScope.kategorien[$rootScope.getLocalObject("kategorie" + $rootScope.currentFilialId)] != "undefined"
50294				&& $rootScope.kategorien[$rootScope.getLocalObject("kategorie" + $rootScope.currentFilialId)].art != "6"
50295			) {
50296				$rootScope.currentKat = $rootScope.getLocalObject("kategorie" + $rootScope.currentFilialId);
50297				$rootScope.scrollToKategorie($rootScope.currentKat);
50298			}
50299
50300			if(typeof $rootScope.kategorien[$rootScope.currentKat] != "undefined") {
50301				selectedKatValid = true;
50302			}
50303
50304			$rootScope.boni = data.data.data.boni;
50305
50306
50307			var sortedKategorien = [];
50308
50309			for(var k in $rootScope.kategorien) {
50310				if(typeof $rootScope.kategorien[k].icons === "string") {
50311					$rootScope.kategorien[k].icons = $rootScope.kategorien[k].icons.split(',');
50312				}
50313
50314				if($rootScope.kategorien[k].bild != '') {
50315					$rootScope.kategorien[k].imagev2 = $rootScope.kategorien[k].bild;
50316				} else {
50317					for(var ki in $rootScope.config['kategorie-bilder']) {
50318						var regex = new RegExp('(.*)(' + ki + ')(.*)', 'igm');
50319						if($rootScope.kategorien[k].name.match(regex)) {
50320							$rootScope.kategorien[k].image = $rootScope.config['kategorie-bilder'][ki][Math.floor(Math.random()*$rootScope.config['kategorie-bilder'][ki].length)];
50321							$rootScope.kategorien[k].imagev2 = "/img/Kategoriebilder/" + $rootScope.config['kategorie-bilder'][ki][Math.floor(Math.random()*$rootScope.config['kategorie-bilder'][ki].length)];
50322							break;
50323						}
50324					}
50325				}
50326
50327				sortedKategorien.push($rootScope.kategorien[k]);
50328			}
50329
50330			sortedKategorien.sort(function(x, y) {
50331				if($rootScope.isNumber(x.nummer) && $rootScope.isNumber(y.nummer)) {
50332					if(Number(x.nummer) < Number(y.nummer)) {
50333						return -1;
50334					}
50335					if(Number(x.nummer) > Number(y.nummer)) {
50336						return 1;
50337					}
50338
50339					return 0;
50340				} else {
50341					if(x.nummer < y.nummer) {
50342						return -1;
50343					}
50344					if(x.nummer > y.nummer) {
50345						return 1;
50346					}
50347
50348					return 0;
50349				}
50350			});
50351
50352			$rootScope.kategorien = sortedKategorien;
50353
50354			if($rootScope.kategorien.length > 0) {
50355				if(!selectedKatValid) {
50356					var selectKatIndex = 0;
50357					while($rootScope.kategorien[selectKatIndex].art == "6") {
50358						selectKatIndex++;
50359					}
50360					$rootScope.currentKat = $rootScope.kategorien[selectKatIndex].id;
50361				}
50362
50363				for(var g in $rootScope.gerichte) {
50364					$rootScope.loadGerichte(g);
50365				}
50366
50367				$rootScope.loadGerichte($rootScope.currentKat);
50368			}
50369
50370			$rootScope.lastCurrentDatum = $rootScope.currentDatum;
50371			$rootScope.lastCurrentUhrzeit = $rootScope.currentUhrzeit;
50372			$rootScope.lastCurrentType = $rootScope.currentType;
50373
50374			// $timeout(function() {
50375			// 	$rootScope.loading = false;
50376			// });
50377		});
50378	};
50379
50380    $rootScope.getKatNamesByNr = function(katNr) {
50381        var namen = [];
50382        for(var k in $rootScope.kategorien) {
50383            if(katNr.indexOf($rootScope.kategorien[k].nummer) > -1 && $rootScope.kategorien.art != 6) {
50384                namen.push($rootScope.kategorien[k].name);
50385            }
50386        }
50387
50388        return namen.join(', ');
50389    };
50390
50391	$rootScope.isKategoreInArray = function(katArray, katId) {
50392		return katArray.indexOf(katId) > -1;
50393	};
50394
50395	$rootScope.isKatNummerInArray = function(array, nummer) {
50396		if(!isNaN(nummer)) {
50397			nummer = Number(nummer);
50398		}
50399		return array.indexOf(nummer) > -1;
50400	};
50401
50402	$rootScope.loadGerichteByIds = function(gerichtIds, katId) {
50403			$rootScope.loading = true;
50404		$http.get(
50405			"?cl=index&mt=apiGerichteByIds"
50406			+ "&abholLiefer=" + $rootScope.currentType
50407			+ "&datum=" + $rootScope.currentDatum
50408			+ "&uhrzeit=" + $rootScope.currentUhrzeit
50409			+ "&gerichtIds=" + gerichtIds.join(',')
50410		, {}).then(function(data) {
50411			$rootScope.gerichte[katId]= data.data;
50412			$rootScope.config['all-is-loaded'] = true;
50413			$rootScope.render();
50414			$rootScope.loading = false;
50415			$rootScope.gerichteLoaded = true;
50416		});
50417	};
50418
50419	var lastGerichte = null;
50420	$rootScope.loadGerichte = function(katId, gerichtId, gerichtIndex) {
50421		$timeout(() => {
50422			$rootScope.scrollToKategorie(katId);
50423		});
50424
50425		var newLastGerichte = $rootScope.currentType + "-" + $rootScope.currentDatum + "-" + $rootScope.currentUhrzeit + "-" + katId + "-" + $rootScope.config['is-angebot'];
50426		if(lastGerichte == newLastGerichte) {
50427			$rootScope.config['all-is-loaded'] = true;
50428			$rootScope.loading = false;
50429			$rootScope.gerichteLoaded = true;
50430			return;
50431		}
50432		lastGerichte = newLastGerichte;
50433
50434		$rootScope.loading = true;
50435		$http.get(
50436			"?cl=index&mt=apiGerichte"
50437			+ "&abholLiefer=" + $rootScope.currentType
50438			+ "&datum=" + $rootScope.currentDatum
50439			+ "&uhrzeit=" + $rootScope.currentUhrzeit
50440			+ "&katId=" + katId
50441			+ "&angebot=" + $rootScope.config['is-angebot']
50442		, {}).then(function(data) {
50443            if(data.data.error == true) {
50444    			$rootScope.config['all-is-loaded'] = true;
50445				$rootScope.loading = false;
50446				$rootScope.gerichteLoaded = true;
50447                return;
50448            }
50449			// if(typeof $rootScope.firstGerichte == "undefined") {
50450			// 	$rootScope.firstGerichte = {};
50451			// }
50452			// $rootScope.firstGerichte[katId] = null;
50453			var dataGerichte = data.data.data.gerichte;
50454			// $rootScope.trenner = angular.extend($rootScope.trenner, data.data.data.trenner);
50455			$rootScope.trenner = data.data.data.trenner;
50456
50457            $rootScope.zusatzStoffeGeneriert = {};
50458
50459            if($rootScope.shopSettings.autoZusatzstoffe == 1) {
50460    			for(var k in dataGerichte) {
50461    				for(var g in dataGerichte[k]) {
50462    					dataGerichte[k][g].zusatzstoffe = [];
50463    					for(var zs in $rootScope.config['zusatzstoffe']) {
50464    						var regex = new RegExp($rootScope.config['zusatzstoffe'][zs].SearchString, 'igm');
50465    						if(dataGerichte[k][g].name.match(regex) || dataGerichte[k][g].beschreibung.match(regex)) {
50466    							dataGerichte[k][g].zusatzstoffe.push($rootScope.config['zusatzstoffe'][zs].kuerzel);
50467    							if(typeof $rootScope.zusatzStoffeGeneriert[$rootScope.config['zusatzstoffe'][zs].kuerzel] == 'undefined') {
50468    								$rootScope.zusatzStoffeGeneriert[$rootScope.config['zusatzstoffe'][zs].kuerzel] = $rootScope.config['zusatzstoffe'][zs].Text;
50469    							}
50470    						}
50471    					}
50472    				}
50473    			}
50474            } else {
50475                $http.get('?cl=api&mt=getZusatzstoffe')
50476                    .then(function(data) {
50477                        $rootScope.zusatzStoffeGeneriert = data.data;
50478                    })
50479            }
50480
50481			// $rootScope.gerichte = angular.extend($rootScope.gerichte, data.data.data.gerichte);
50482			$rootScope.gerichte[katId]= data.data.data.gerichte[katId];
50483
50484			// $rootScope.firstGerichte[katId] = $rootScope.gerichte.first();
50485
50486			$rootScope.config['all-is-loaded'] = true;
50487
50488			$rootScope.lastCurrentDatum = $rootScope.currentDatum;
50489			$rootScope.lastCurrentUhrzeit = $rootScope.currentUhrzeit;
50490			$rootScope.lastCurrentType = $rootScope.currentType;
50491
50492			if(typeof gerichtId != 'undefined' && typeof gerichtIndex == 'undefined') {
50493				$rootScope.setGerichtById(gerichtId);
50494			} else if(typeof gerichtIndex != 'undefined') {
50495				$rootScope.editGericht(gerichtIndex);
50496			}
50497
50498			// $timeout(function() {
50499				$rootScope.render();
50500
50501				//if($rootScope.config['is-angebot']) {
50502
50503				//}
50504
50505				$rootScope.loading = false;
50506				$rootScope.gerichteLoaded = true;
50507			// });
50508		});
50509	};
50510
50511	$rootScope.loadSpeisekarte = function() {
50512		if(
50513			$rootScope.currentDatum == $rootScope.lastCurrentDatum
50514			&& $rootScope.currentUhrzeit == $rootScope.lastCurrentUhrzeit
50515			&& $rootScope.currentType == $rootScope.lastCurrentType
50516		) {
50517			return true;
50518		}
50519
50520		$rootScope.loading = true;
50521
50522		$http.get(
50523			"?cl=index&mt=apiSpeisekarte"
50524			+ "&abholLiefer=" + $rootScope.currentType
50525			+ "&datum=" + $rootScope.currentDatum
50526			+ "&uhrzeit=" + $rootScope.currentUhrzeit
50527		, {}).then(function(data) {
50528			$rootScope.kategorien = data.data.data.kategorien;
50529			$rootScope.gerichte = data.data.data.gerichte;
50530			$rootScope.trenner = data.data.data.trenner;
50531			$rootScope.boni = data.data.data.boni;
50532
50533			var sortedKategorien = [];
50534
50535			for(var k in $rootScope.kategorien) {
50536				sortedKategorien.push($rootScope.kategorien[k]);
50537
50538				for(var ki in $rootScope.config['kategorie-bilder']) {
50539					var regex = new RegExp('(.*)(' + ki + ')(.*)', 'igm');
50540					if($rootScope.kategorien[k].name.match(regex)) {
50541						$rootScope.kategorien[k].image = $rootScope.config['kategorie-bilder'][ki][Math.floor(Math.random()*$rootScope.config['kategorie-bilder'][ki].length)];
50542						break;
50543					}
50544				}
50545
50546
50547				// if($rootScope.autoZusatzstoffe) {
50548					for(var g in $rootScope.gerichte[k]) {
50549						$rootScope.gerichte[k][g].zusatzstoffe = [];
50550						for(var zs in $rootScope.config['zusatzstoffe']) {
50551							var regex = new RegExp($rootScope.config['zusatzstoffe'][zs].SearchString, 'igm');
50552							if($rootScope.gerichte[k][g].name.match(regex) || $rootScope.gerichte[k][g].beschreibung.match(regex)) {
50553								$rootScope.gerichte[k][g].zusatzstoffe.push($rootScope.config['zusatzstoffe'][zs].kuerzel);
50554								if(typeof $rootScope.zusatzStoffeGeneriert[$rootScope.config['zusatzstoffe'][zs].kuerzel] == 'undefined') {
50555									$rootScope.zusatzStoffeGeneriert[$rootScope.config['zusatzstoffe'][zs].kuerzel] = $rootScope.config['zusatzstoffe'][zs].Text;
50556								}
50557							}
50558						}
50559					}
50560				// }
50561			}
50562
50563			sortedKategorien.sort(function(x, y) {
50564				if($rootScope.isNumber(x.nummer) && $rootScope.isNumber(y.nummer)) {
50565					if(Number(x.nummer) < Number(y.nummer)) {
50566						return -1;
50567					}
50568					if(Number(x.nummer) > Number(y.nummer)) {
50569						return 1;
50570					}
50571
50572					return 0;
50573				} else {
50574					if(x.nummer < y.nummer) {
50575						return -1;
50576					}
50577					if(x.nummer > y.nummer) {
50578						return 1;
50579					}
50580
50581					return 0;
50582				}
50583			});
50584
50585			$rootScope.kategorien = sortedKategorien;
50586
50587			$rootScope.currentKat = $rootScope.kategorien[0].id; //Object.keys($rootScope.kategorien)[0];
50588
50589			if(
50590				typeof $rootScope.getLocalObject("warenkorb" + $rootScope.currentFilialId) != "undefined"
50591				&& $rootScope.getLocalObject("warenkorb" + $rootScope.currentFilialId) != null
50592			) {
50593				var cookieWarenkorb = $rootScope.getLocalObject("warenkorb" + $rootScope.currentFilialId);
50594				for(var gId in cookieWarenkorb) {
50595					var gericht = $rootScope.getGerichtById(gId);
50596					if(gericht != null) {
50597						$rootScope.warenkorb.push(angular.extend($rootScope.clone(gericht), cookieWarenkorb[gId]));
50598					}
50599				}
50600			}
50601
50602			$rootScope.config['all-is-loaded'] = true;
50603
50604			$rootScope.lastCurrentDatum = $rootScope.currentDatum;
50605			$rootScope.lastCurrentUhrzeit = $rootScope.currentUhrzeit;
50606			$rootScope.lastCurrentType = $rootScope.currentType;
50607
50608			// $timeout(function() {
50609				$rootScope.loading = false;
50610			// });
50611		});
50612	};
50613
50614	var lastZeiten = null;
50615	$rootScope.getUhrzeiten = function(datum) {
50616		$('.load-overlay-zeitpunkt').show();
50617
50618		var newLastZeiten = datum + "-" + $rootScope.currentType;
50619		if(lastZeiten == newLastZeiten) { return; }
50620		lastZeiten = newLastZeiten;
50621
50622		$http.get("?cl=index&mt=apiZeiten"
50623			+ "&datum=" + datum
50624			+ "&type=" + $rootScope.currentType
50625		).then(function(data) {
50626			$rootScope.hatRuheTag = false;
50627			$rootScope.config['zeitpunkt-zeiten'] = data.data.zeiten;
50628
50629			if(data.data.zeiten == false && data.data.grund == 'ruheTag') {
50630				$rootScope.hatRuheTag = true;
50631				$('.load-overlay-zeitpunkt').hide();
50632				return false;
50633			}
50634
50635			if(!$rootScope.uhrzeitLocalstorageLoaded) {
50636				$rootScope.currentUhrzeit = $rootScope.config['zeitpunkt-zeiten'][0];
50637			}
50638			if($rootScope.config['zeitpunkt-zeiten'].indexOf($rootScope.currentUhrzeit) == -1) {
50639				$rootScope.currentUhrzeit = $rootScope.config['zeitpunkt-zeiten'][0];
50640			}
50641
50642			var oeffnungszeiten = [];
50643			for(var oz in data.data.oeffnungszeiten) {
50644				oeffnungszeiten.push(
50645					data.data.oeffnungszeiten[oz].von + " Uhr bis " + data.data.oeffnungszeiten[oz].bis + " Uhr"
50646				);
50647			}
50648
50649			$rootScope.currentOeffnungszeiten = oeffnungszeiten.join(" und ");
50650
50651			$('.load-overlay-zeitpunkt').hide();
50652			//$rootScope.loadSpeisekarte();
50653		});
50654	};
50655
50656	var lastLoaded = null;
50657	$rootScope.loadIsOpen = function() {
50658        if(typeof $rootScope.currentUhrzeit == 'undefined') {
50659            $rootScope.currentUhrzeit = '';
50660        }
50661		var newLastLoaded = $rootScope.currentDatum + "-" + $rootScope.currentUhrzeit + "-" + $rootScope.currentType;
50662		if(lastLoaded !== null && lastLoaded == newLastLoaded) {
50663			return;
50664		}
50665		lastLoaded = newLastLoaded;
50666		$http.get('?cl=index&mt=apiIsOpen'
50667			+ '&datum=' + $rootScope.currentDatum
50668			+ '&uhrzeit=' + $rootScope.currentUhrzeit
50669			+ '&type=' + $rootScope.currentType
50670		).then(function(data) {
50671			$rootScope.config['is-open'] = data.data.open;
50672		});
50673	};
50674
50675	$rootScope.aufrunden = function() {
50676		$rootScope.bezahlDaten.trinkgeld = $rootScope.getAufrundenBetrag();
50677	};
50678
50679	$rootScope.getAufrundenBetrag = function() {
50680		return Number((Math.ceil($rootScope.getGesamtBetrag()) - $rootScope.getGesamtBetrag()).toFixed(2));
50681	};
50682
50683	$rootScope.setZeitpunktGesetzt = function() {
50684		$rootScope.currentZeitpunktSetted = true;
50685
50686		$rootScope.nextStep();
50687		$rootScope.closeUhrzeitenModal();
50688	};
50689
50690	$rootScope.setLiefergebiet = function(obj) {
50691		// $timeout(function() {
50692			$rootScope.currentLiefergebiet = obj;
50693			$rootScope.currentLiefergebietSetted = true;
50694			$rootScope.kundenDaten.ort = obj.ort;
50695			$rootScope.kundenDaten.plz = obj.postleitzahl;
50696			if($rootScope.currentFilialId != 0) {
50697				$rootScope.setLocalObject('kundendaten' + $rootScope.currentFilialId, $rootScope.kundenDaten);
50698			}
50699		// });
50700
50701		//$rootScope.nextStep();
50702		// $rootScope.closeLiefergebietModal();
50703	};
50704
50705	$rootScope.setLieferadresseProfil = function(obj) {
50706		if($rootScope.currentType == "liefer" && $rootScope.config['liefergebiete-plzs'].indexOf(obj.plz) == -1) {
50707			swal(
50708				"Achtung",
50709				"Leider können Wir zu deiner ausgewählten Lieferadresse nicht liefern. Bitte wähle eine andere Lieferadresse.",
50710				"warning"
50711			);
50712			return;
50713		} else if(
50714			(
50715				$rootScope.getBetragGesamtGerichte().betragBest < $rootScope.getLiefergebietByPlzAndOrt(obj.plz, obj.ort).mindestbestellwert
50716				|| $rootScope.getBetragGesamtGerichte().betragBest == 0
50717			) && $rootScope.currentType == "liefer"
50718		) {
50719			swal(
50720				"Achtung",
50721				"Durch die Auswahl deiner Lieferadresse hat sich das Liefergebiet geändert und du erreichst nicht mehr den Mindestbestellwert!",
50722				"error"
50723			);
50724		}
50725
50726		// $timeout(function() {
50727			if($rootScope.currentLiefergebiet.plz != obj.plz && $rootScope.currentType == "liefer") {
50728				var plzIndex = $rootScope.getLiefergebietByPlzUndOrt(obj.plz, obj.ort);
50729				if(plzIndex > -1) {
50730					$rootScope.setLiefergebiet($rootScope.currentLiefergebieteSwitch[plzIndex]);
50731				} else {
50732					swal(
50733						"Achtung",
50734						"Leider können Wir zu deiner ausgewählten Lieferadresse nicht liefern. Bitte überprüfe deine Angaben.",
50735						"warning"
50736					);
50737					return;
50738				}
50739			}
50740			// $rootScope.kundenDaten.anrede = obj.anrede;
50741			$('.anrede-auswahl[data-anrede="' + obj.anrede + '"]').iCheck('check');
50742			$rootScope.kundenDaten.anrede = obj.anrede;
50743			$rootScope.kundenDaten.vorname = obj.vorname;
50744			$rootScope.kundenDaten.nachname = obj.nachname;
50745			$rootScope.kundenDaten.unternehmen = obj.unternehmen;
50746			$rootScope.kundenDaten.strasse = obj.strasse;
50747			$rootScope.kundenDaten.hausnummer = obj.hausnr;
50748			$rootScope.kundenDaten.ort = obj.ort;
50749			$rootScope.kundenDaten.plz = obj.plz;
50750			$rootScope.kundenDaten.telefon = obj.telefon;
50751			$rootScope.kundenDaten.email = obj.email;
50752			$rootScope.kundenDaten.bemerkung = obj.bemerkung;
50753		// });
50754	};
50755
50756	$rootScope.setType = function(type) {
50757		$rootScope.currentType = type;
50758
50759		$rootScope.closeTypeModal();
50760	};
50761
50762	$rootScope.setKat = function(katId, gerichtId, gerichtIndex) {
50763		if($rootScope.currentKat == katId) {
50764			return;
50765		}
50766
50767		if($rootScope.currentFilialId != 0) {
50768			$rootScope.setLocalObject("kategorie" + $rootScope.currentFilialId, katId);
50769		}
50770
50771		$rootScope.scrollToKategorie(katId);
50772
50773		window.scrollTo(0,0);
50774
50775		$rootScope.currentKat = katId;
50776		$rootScope.loadGerichte(katId, gerichtId, gerichtIndex);
50777	};
50778
50779	$rootScope.setGroesse = function(groesseIndex, gericht) {
50780		if(!gericht.showAt.show || !gericht.showAtSpecial.show) {
50781			return;
50782		}
50783
50784		if(typeof gericht == 'undefined') {
50785			gericht = $rootScope.currentGericht;
50786		}
50787		gericht.currentPreis = groesseIndex;
50788		// $timeout(function() {
50789			$rootScope.currentGericht = gericht;
50790		// });
50791	};
50792
50793	$rootScope.changeGroesse = function(ger) {
50794		$rootScope.currentGericht = ger;
50795	};
50796
50797	$rootScope.goToZutatenAuswahl = function(gericht, gerichtIndex) {
50798		if(!gericht.showAt.show || !$rootScope.config['hat-shop']) {
50799			return false;
50800		}
50801
50802		var gerichtIndexQuery = '';
50803		if(typeof gerichtIndex != 'undefined') {
50804			gerichtIndexQuery += '&gerichtIndex=' + gerichtIndex;
50805		}
50806
50807		location.href = '?cl=index&mt=zutatenAuswahl&gerichtId=' + gericht.id + '' + gerichtIndexQuery;
50808	};
50809
50810	$rootScope.setGerichtById = function(gerichtId) {
50811		for(var k in $rootScope.gerichte) {
50812			if(typeof $rootScope.gerichte[k][gerichtId] != 'undefined') {
50813				$rootScope.setGericht($rootScope.gerichte[k][gerichtId]);
50814			}
50815		}
50816	};
50817
50818	$rootScope.setGericht = function(gericht, $event, isV3) {
50819		if(!gericht.showAt.show || !gericht.showAtSpecial.show) {
50820            if(typeof isV3 != "undefined" && isV3 == "showMessage") {
50821                swal("Aktuell nicht verfügbar!", "Bitte beachte den Zeitraum, in dem dieses Gericht bestellt werden kann.", "error");
50822            }
50823			return false;
50824		}
50825
50826
50827		if(
50828			typeof $event != "undefined"
50829			&& (
50830				$($event.target).is('.btn-change-groesse')
50831				|| $($event.target).is('.span-change-groesse')
50832				|| $($event.target).is('.cfwp-favorite-2')
50833				|| $($event.target).is('.cfwp-favorite-1')
50834			) && !$($event.target).is('.btn-angebote')) {
50835			return false;
50836		}
50837
50838        if(!$rootScope.$$phase) {
50839    		$timeout(function() {
50840    			$rootScope.currentGericht = $rootScope.clone(gericht);
50841    			$rootScope.currentGericht.currentZutaten = {};
50842    			$rootScope.currentGericht.currentText = '';
50843    			$rootScope.currentGericht.currentAnzahl = 1;
50844
50845    			for(var zp in $rootScope.currentGericht.zutaten.zutaten) {
50846    				if($rootScope.isXor(zp, $rootScope.currentGericht)) {
50847    					for(var z in $rootScope.currentGericht.zutaten.zutaten[zp].zutaten) {
50848    						if($rootScope.currentGericht.zutaten.zutaten[zp].zutaten[z].preis != '#') {
50849    							$rootScope.setZutat(zp, z, $rootScope.currentGericht);
50850    							break;
50851    						}
50852    					}
50853    				}
50854    			}
50855
50856    			for(var zp in $rootScope.currentGericht.zutaten.zutaten) {
50857    				if(typeof $rootScope.currentGericht.zutaten.zutaten[zp].standardZutaten != "undefined") {
50858    					for(var sz in $rootScope.currentGericht.zutaten.zutaten[zp].standardZutaten) {
50859    						$rootScope.addZutat(zp, $rootScope.currentGericht.zutaten.zutaten[zp].standardZutaten[sz], $rootScope.currentGericht);
50860    					}
50861    				}
50862    			}
50863
50864    			if(
50865                    (
50866                        $rootScope.currentGericht.zutaten[0] != "zutaten"
50867                        && $rootScope.currentGericht.zutaten.length != 0
50868                    ) || $rootScope.currentGericht.anzahlPreise > 1
50869                    || $rootScope.currentGericht.art == 6
50870                    || $rootScope.currentGericht.textfeld == 2
50871                ) {
50872    				$rootScope.openZutatModal(gericht);
50873    			} else {
50874    				$rootScope.addGerichtV2();
50875    			}
50876    		});
50877        } else {
50878            $rootScope.currentGericht = $rootScope.clone(gericht);
50879            $rootScope.currentGericht.currentZutaten = {};
50880            $rootScope.currentGericht.currentText = '';
50881            $rootScope.currentGericht.currentAnzahl = 1;
50882
50883            for(var zp in $rootScope.currentGericht.zutaten.zutaten) {
50884                if($rootScope.isXor(zp, $rootScope.currentGericht)) {
50885                    for(var z in $rootScope.currentGericht.zutaten.zutaten[zp].zutaten) {
50886                        if($rootScope.currentGericht.zutaten.zutaten[zp].zutaten[z].preis != '#') {
50887                            $rootScope.setZutat(zp, z, $rootScope.currentGericht);
50888                            break;
50889                        }
50890                    }
50891                }
50892            }
50893
50894            for(var zp in $rootScope.currentGericht.zutaten.zutaten) {
50895                if(typeof $rootScope.currentGericht.zutaten.zutaten[zp].standardZutaten != "undefined") {
50896                    for(var sz in $rootScope.currentGericht.zutaten.zutaten[zp].standardZutaten) {
50897                        $rootScope.addZutat(zp, $rootScope.currentGericht.zutaten.zutaten[zp].standardZutaten[sz], $rootScope.currentGericht);
50898                    }
50899                }
50900            }
50901
50902            if(
50903                (
50904                    $rootScope.currentGericht.zutaten[0] != "zutaten"
50905                    && $rootScope.currentGericht.zutaten.length != 0
50906                ) || $rootScope.currentGericht.anzahlPreise > 1
50907                || $rootScope.currentGericht.art == 6
50908                || $rootScope.currentGericht.textfeld == 2
50909            ) {
50910                $rootScope.openZutatModal(gericht);
50911            } else {
50912                $rootScope.addGerichtV2();
50913            }
50914        }
50915
50916		$timeout(function() {
50917			if(isBigText) {
50918				increaseTextSize();
50919			}
50920		});
50921	};
50922
50923	$rootScope.checkGericht = function(gericht, gerichtIndex) {
50924		if(gericht.currentAnzahl == 0 && typeof gerichtIndex != "undefined") {
50925			$rootScope.removeGericht(gerichtIndex);
50926		}
50927
50928		gericht.currentAnzahl = Number(gericht.currentAnzahl);
50929	};
50930
50931	$rootScope.addZutat = function(zutatPropsIndex, zutatIndex, gericht) {
50932		if(typeof gericht == 'undefined') {
50933			gericht = $rootScope.currentGericht;
50934		}
50935		if(typeof gericht.currentZutaten == "undefined") {
50936			gericht.currentZutaten = {};
50937		}
50938		if(typeof gericht.currentZutaten[zutatPropsIndex] == "undefined") {
50939			gericht.currentZutaten[zutatPropsIndex] = {};
50940		}
50941		if(typeof gericht.currentZutaten[zutatPropsIndex][zutatIndex] == "undefined") {
50942			gericht.currentZutaten[zutatPropsIndex][zutatIndex] = 0;
50943		}
50944
50945		var zutatProps = gericht.zutaten.zutaten[zutatPropsIndex];
50946
50947		if(typeof gericht.zutatenWahlReihenfolge == "undefined") {
50948			gericht.zutatenWahlReihenfolge = {};
50949		}
50950
50951		if(typeof gericht.zutatenWahlReihenfolge[zutatPropsIndex] == "undefined") {
50952			gericht.zutatenWahlReihenfolge[zutatPropsIndex] = [];
50953		}
50954
50955		gericht.zutatenWahlReihenfolge[zutatPropsIndex].push(zutatIndex);
50956
50957		gericht.currentZutaten[zutatPropsIndex][zutatIndex]++;
50958
50959		$rootScope.currentGericht = gericht;
50960	};
50961
50962	$rootScope.setZutat = function(zutatPropsIndex, zutatIndex, gericht, event) {
50963		if(typeof gericht == 'undefined') {
50964			gericht = $rootScope.currentGericht;
50965		}
50966		if(typeof gericht.currentZutaten == "undefined") {
50967			gericht.currentZutaten = {};
50968		}
50969
50970		gericht.currentZutaten[zutatPropsIndex] = {};
50971		if(typeof gericht.currentZutaten[zutatPropsIndex][zutatIndex] == "undefined") {
50972			gericht.currentZutaten[zutatPropsIndex][zutatIndex] = 0;
50973		}
50974
50975		gericht.currentZutaten[zutatPropsIndex][zutatIndex] = 1;
50976
50977		// $timeout(function() {
50978			$rootScope.currentGericht = gericht;
50979		// });
50980	};
50981
50982	$rootScope.removeZutat = function(zutatPropsIndex, zutatIndex, gericht) {
50983		var indexWahl = -1;
50984		while((indexWahl = gericht.zutatenWahlReihenfolge[zutatPropsIndex].indexOf(Number(zutatIndex))) > -1) {
50985			gericht.zutatenWahlReihenfolge[zutatPropsIndex].splice(indexWahl, 1);
50986		}
50987
50988		delete gericht.currentZutaten[zutatPropsIndex][zutatIndex];
50989	};
50990
50991	$rootScope.getBetragGericht = function(gericht, inklRabatte) {
50992        if(inklRabatte === undefined) {
50993            inklRabatte = true;
50994        }
50995		if(gericht == null) {
50996			return 0;
50997		}
50998		var betrag = 0;
50999		var preisIndex = gericht.currentPreis;
51000		betrag += $rootScope.round($rootScope.getGerichtsPreis(gericht, preisIndex, undefined, undefined, inklRabatte), 2);
51001
51002		betrag += $rootScope.getZutatenpreis(gericht, preisIndex);
51003
51004		if(typeof gericht.currentAnzahl == "undefined") {
51005			gericht.currentAnzahl = 1;
51006		}
51007
51008		return betrag * gericht.currentAnzahl;
51009	};
51010
51011    $rootScope.getZutatProzentFromCoupon = function(gerichtNr) {
51012        if($rootScope.shopSettings["prozentAllZutaten"] == 0) {
51013            return 0;
51014        }
51015        var prozentRabatt = 0;
51016        for(var gc in $rootScope.currentCoupons) {
51017            if($rootScope.currentCoupons[gc].typ != 1) {
51018                continue;
51019            }
51020            if($rootScope.currentCoupons[gc].berechnungInbegriffeneGerichteObj.indexOf(gerichtNr) > -1 || $rootScope.currentCoupons[gc].berechnungInbegriffeneGerichteObj.length == 0) {
51021                if($rootScope.currentCoupons[gc].isKombinierbar == 1) {
51022                    prozentRabatt += Number($rootScope.currentCoupons[gc].wert);
51023                } else {
51024                    prozentRabatt = Number($rootScope.currentCoupons[gc].wert);
51025                }
51026            }
51027        }
51028
51029        if($rootScope.currentCoupons.length > 0 && $rootScope.couponWithNotCombiningRabatt) {
51030            prozentRabatt = $rootScope.couponRabattKomplett;
51031        }
51032
51033        return prozentRabatt;
51034    };
51035
51036	$rootScope.getZutatenpreis = function(gericht, preisIndex, zutatPropsIndex, zutatIndex, withProzent) {
51037		var betrag = 0;
51038        var einzelBetraege = {};
51039
51040		for(var zp in gericht.currentZutaten) {
51041            if(typeof einzelBetraege[zp] == "undefined") {
51042                einzelBetraege[zp] =  {};
51043            }
51044			var showZutat = $rootScope.showZutat(zp, gericht);
51045			if(!showZutat) {
51046				continue;
51047			}
51048
51049			var selectedZutaten = gericht.currentZutaten[zp];
51050			var zutatProps = gericht.zutaten.zutaten[zp];
51051			var isPlus = $rootScope.isPlus(zutatProps);
51052			var plusIndex = $rootScope.getPlusIndex(zutatProps);
51053            var prozentFaktor = 1;
51054            if(withProzent === undefined || withProzent === true) {
51055	             prozentFaktor = $rootScope.getProzentFaktor(zutatProps);
51056                 prozentFaktor -= $rootScope.getZutatProzentFromCoupon(gericht.devNummer);
51057             }
51058			for(var z in selectedZutaten) {
51059                if(typeof einzelBetraege[zp][z] == "undefined") {
51060                    einzelBetraege[zp][z] = 0;
51061                }
51062				var indexOfWahl = -1;
51063				for(var i = 0; i < selectedZutaten[z]; i++) {
51064					if(isPlus) {
51065						if(
51066							typeof gericht.zutatenWahlReihenfolge == "undefined"
51067							|| typeof gericht.zutatenWahlReihenfolge[zp] == "undefined"
51068						) {
51069                            einzelBetraege[zp][z] += 0;
51070							continue;
51071						}
51072						var maxFree = zutatProps.rules[plusIndex].anzahl;
51073						var indexOfWahl = gericht.zutatenWahlReihenfolge[zp].indexOf(Number(z), indexOfWahl + 1);
51074
51075						if(indexOfWahl > -1 && indexOfWahl < maxFree) {
51076                            einzelBetraege[zp][z] += 0;
51077							continue;
51078						}
51079
51080					}
51081
51082					if(typeof zutatProps.standardZutaten != "undefined" && i == 0 && zutatProps.standardZutaten.indexOf(Number(z)) > -1) {
51083                        einzelBetraege[zp][z] += 0;
51084						continue;
51085					}
51086
51087					if(
51088						typeof gericht.zutaten.zutaten[zp].zutaten[z].preise[$rootScope.currentType][preisIndex] != "undefined"
51089						&& !isNaN(gericht.zutaten.zutaten[zp].zutaten[z].preise[$rootScope.currentType][preisIndex])
51090					) {
51091						betrag +=
51092							gericht.zutaten.zutaten[zp].zutaten[z].preise[$rootScope.currentType][preisIndex]
51093							* prozentFaktor
51094						;
51095                        einzelBetraege[zp][z] += betrag;
51096					}
51097				}
51098
51099			}
51100		}
51101
51102        if(typeof zutatPropsIndex != "undefined" && typeof zutatIndex != "undefiend") {
51103            return einzelBetraege[zutatPropsIndex][zutatIndex];
51104        }
51105
51106		return betrag;
51107	};
51108
51109	$rootScope.getBetragGesamtGerichte = function() {
51110		var betragFuerMindWert = 0;
51111		var betragGesamt = 0;
51112
51113		for(var w in $rootScope.warenkorb) {
51114			var betrag =  $rootScope.getBetragGericht($rootScope.warenkorb[w]);
51115			if($rootScope.warenkorb[w].isBestellwert == 1) {
51116				betragFuerMindWert += betrag;
51117			}
51118
51119			betragGesamt += betrag;
51120		}
51121
51122        if($rootScope.bezahlDaten.art == 5) {
51123            betragGesamt += Number($rootScope.shopSettings["ecGebuehr"]);
51124        }
51125
51126        if($rootScope.currentCoupons.length > 0) {
51127            for(var c in $rootScope.currentCoupons) {
51128                betragGesamt += Number($rootScope.getCouponPreis(
51129                    $rootScope.currentCoupons[c],
51130                    $rootScope.currentCoupons[c].currentPreis
51131                ));
51132
51133                betragFuerMindWert += Number($rootScope.getCouponPreis(
51134                    $rootScope.currentCoupons[c],
51135                    $rootScope.currentCoupons[c].currentPreis
51136                ));
51137            }
51138        }
51139
51140		return {
51141			betragBest: betragFuerMindWert,
51142			betragGes: betragGesamt
51143		};
51144	};
51145
51146	$rootScope.getBonusBetragGesamtGerichte = function(bonusIndexOrExcludedKats) {
51147		var betragGesamt = 0;
51148		var treuepreis = $rootScope.getTreuepreisById($rootScope.currentTreuepreis);
51149
51150		if(typeof bonusIndexOrExcludedKats == "object") {
51151			var bonusExcludedKats = bonusIndexOrExcludedKats;
51152		} else {
51153			var bonusExcludedKats = $rootScope.boni[bonusIndexOrExcludedKats].excludedKats;
51154		}
51155
51156		for(var w in $rootScope.warenkorb) {
51157			var betrag =  $rootScope.getBetragGericht($rootScope.warenkorb[w]);
51158
51159			if(
51160				(
51161					typeof bonusIndexOrExcludedKats == "object"
51162					|| typeof bonusIndexOrExcludedKats == "array"
51163				) && treuepreis !== null
51164				&& treuepreis.art == 1
51165			) {
51166				betrag *= (1 - treuepreis.wert);
51167			}
51168
51169			if(bonusExcludedKats.indexOf($rootScope.warenkorb[w].katNr) == -1) {
51170				betragGesamt += betrag;
51171			}
51172		}
51173
51174		if(
51175			(
51176				typeof bonusIndexOrExcludedKats == "object"
51177				|| typeof bonusIndexOrExcludedKats == "array"
51178			) && treuepreis !== null
51179			&& treuepreis.art == 2
51180		) {
51181			betragGesamt -= treuepreis.wert;
51182		}
51183
51184		return betragGesamt;
51185	};
51186
51187	$rootScope.getBoni = function() {
51188		var show = false;
51189		var hoechsterBetrag = 0;
51190		var text = "";
51191		for(var b in $rootScope.boni) {
51192			if($rootScope.boni[b].betrag > hoechsterBetrag && $rootScope.getBonusBetragGesamtGerichte(b) > $rootScope.boni[b].betrag) {
51193				hoechsterBetrag = $rootScope.boni[b].betrag;
51194				text = $rootScope.boni[b].text;
51195				show = true;
51196			}
51197		}
51198
51199		return {
51200			show: show,
51201			text: text,
51202			betrag: hoechsterBetrag
51203		};
51204	};
51205
51206	$rootScope.getGesamtBetrag = function() {
51207		var gerichteBetrag = $rootScope.getBetragGesamtGerichte().betragGes;
51208		var treuepreis = $rootScope.getTreuepreisById($rootScope.currentTreuepreis);
51209
51210		if(treuepreis !== null) {
51211			if(treuepreis.art == 1) {
51212				gerichteBetrag *= (1 - treuepreis.wert);
51213			} else if(treuepreis.art == 2) {
51214				gerichteBetrag -= treuepreis.wert;
51215			}
51216		}
51217
51218		gerichteBetrag = Number(gerichteBetrag);
51219		if($rootScope.currentType == "liefer" && (
51220			gerichteBetrag < $rootScope.currentLiefergebiet.freiAb
51221			|| $rootScope.currentLiefergebiet.freiAb == 0
51222		)) {
51223			gerichteBetrag += Number($rootScope.currentLiefergebiet.liefergebuehr);
51224		}
51225
51226		return gerichteBetrag;
51227	};
51228
51229	$rootScope.getGesamtBetragOhneLiefergebuehr = function() {
51230		return $rootScope.getGesamtBetrag() - Number($rootScope.currentLiefergebiet.liefergebuehr);
51231	};
51232
51233	$rootScope.getArtikelAnzahl = function() {
51234		var anzahl = 0;
51235		for(var g in $rootScope.warenkorb) {
51236			anzahl += $rootScope.warenkorb[g].currentAnzahl;
51237		}
51238
51239		return anzahl;
51240	};
51241
51242
51243	$rootScope.increaseAnzahl = function() {
51244		$rootScope.currentGericht.currentAnzahl++;
51245	};
51246
51247	$rootScope.decreaseAnzahl = function() {
51248		$rootScope.currentGericht.currentAnzahl--;
51249
51250		if($rootScope.currentGericht.currentAnzahl < 1) {
51251			$rootScope.currentGericht.currentAnzahl = 1;
51252		}
51253	};
51254
51255	$rootScope.addGericht = function(gericht, returnId) {
51256		$rootScope.addGerichtLoading = true;
51257		var gerichtToAdd = $rootScope.currentGericht;
51258		if(typeof gericht != 'undefined' && gericht != null) {
51259			gerichtToAdd = gericht;
51260		}
51261
51262		$('.addGerichtOverlay').show();
51263
51264		if($rootScope.warenkorb == null) {
51265			$rootScope.warenkorb = [];
51266		}
51267
51268		gerichtToAdd.currentAnzahl = $rootScope.convertToNumber(gerichtToAdd.currentAnzahl);
51269
51270		if(gerichtToAdd.currentAnzahl == 0) {
51271			$rootScope.currentGerichtIndex = false;
51272			return;
51273		}
51274		$rootScope.warenkorb.push($rootScope.clone(gerichtToAdd));
51275
51276		$rootScope.currentGerichtIndex = false;
51277		// $rootScope.closeZutatModal();
51278
51279		// $timeout(function() {
51280			$('.addGerichtOverlay').hide();
51281			window.setTimeout(function () {
51282				$('#warenkorb-leiste-small').css({
51283					'color': '',
51284					'background-color': ''
51285				});
51286
51287				$('.shopping-cart').removeClass('shopping-cart-adding');
51288				$('.shopping-button').removeClass('shopping-cart-adding');
51289			}, 1000);
51290			$('#warenkorb-leiste-small').css({
51291				'color': 'red',
51292				'background-color': 'yellow'
51293			});
51294
51295			$('.shopping-cart').addClass('shopping-cart-adding');
51296			$('.shopping-button').addClass('shopping-cart-adding');
51297
51298			$rootScope.showGerichtAddedNotify();
51299
51300			$http.post('?cl=api&mt=apiLog&logArt=clickAddGericht', {gericht: gerichtToAdd})
51301				.then(function(){
51302					location.href = $rootScope.config['site-referer']; //'?cl=index&id=' + returnId;
51303					$rootScope.addGerichtLoading = false;
51304				});
51305		// });
51306	};
51307
51308	$rootScope.addGerichtV2 = function(gericht, returnId, modalEv, isFromAbfrage) {
51309		var gerichtToAdd = $rootScope.currentGericht;
51310		if(typeof gericht != 'undefined' && gericht != null) {
51311			gerichtToAdd = gericht;
51312		}
51313
51314		if(gerichtToAdd.mindestalter > 0 && !isFromAbfrage) {
51315			$rootScope.openModal($('.altersabfrage-modal'));
51316			return;
51317		}
51318
51319		var lastWkItem = $('.warenkorb .WKPosition:visible').last();
51320
51321		if(typeof modalEv != "undefined") {
51322			if(lastWkItem.length > 0) {
51323				var parentModal = $(modalEv.target).parents('.cfjs-modal');
51324				var parentWithAllElements = parentModal.find('*')
51325				parentWithAllElements.add(parentModal);
51326				var offsetTop = lastWkItem.offset().top - $(window).scrollTop();
51327				var offsetLeft = lastWkItem.offset().left;
51328				var width = lastWkItem.outerWidth();
51329				var positionTop = offsetTop + lastWkItem.outerHeight(true) - parentModal.height() / 2 * 0.1;
51330				var positionLeft = (offsetLeft + width / 2) - parentModal.width() / 2 * 0.1;
51331
51332				$('.modal-overlay').fadeOut();
51333				$('body').css('overflow', 'auto');
51334				// parentWithAllElements.effect('scale', {percent: 10}, 500, function() {});
51335				parentModal.effect('scale', {percent: 10}, 500, function() {});
51336				parentModal.animate({
51337					top: positionTop,
51338					left: positionLeft,
51339					marginLeft: 0
51340				}, {
51341					duration: 500,
51342					queue: false,
51343					complete: function() {
51344						$(this).hide();
51345						$(this).removeAttr('style');
51346						// parentModal.effect('scale', {percent: 100}, 0, function() {});
51347						parentModal.removeAttr('style');
51348						parentModal.find('img').removeAttr('style');
51349
51350						$timeout(function() {
51351							if($rootScope.warenkorb == null) {
51352								$rootScope.warenkorb = [];
51353							}
51354
51355							gerichtToAdd.currentAnzahl = $rootScope.convertToNumber(gerichtToAdd.currentAnzahl);
51356
51357							if(gerichtToAdd.currentAnzahl == 0) {
51358								$rootScope.currentGerichtIndex = false;
51359								return;
51360							}
51361							$rootScope.warenkorb.push($rootScope.clone(gerichtToAdd));
51362
51363							$rootScope.currentGerichtIndex = false;
51364							$rootScope.showGerichtAddedNotify();
51365						});
51366					}
51367				});
51368			} else {
51369				if($rootScope.warenkorb == null) {
51370					$rootScope.warenkorb = [];
51371				}
51372
51373				gerichtToAdd.currentAnzahl = $rootScope.convertToNumber(gerichtToAdd.currentAnzahl);
51374
51375				if(gerichtToAdd.currentAnzahl == 0) {
51376					$rootScope.currentGerichtIndex = false;
51377					return;
51378				}
51379				$rootScope.warenkorb.push($rootScope.clone(gerichtToAdd));
51380
51381				$rootScope.currentGerichtIndex = false;
51382				$rootScope.closeZutatModal();
51383
51384                $timeout(function() {
51385                    var wkFooter = $('.wkandInfo');
51386                    if(wkFooter.length > 0) {
51387                        wkFooter.addClass('active');
51388                        $timeout(function() {
51389                            wkFooter.removeClass('active');
51390                        }, 500);
51391                    }
51392
51393                    if($('.header-v21').length > 0) {
51394    					var btn = $('.btn-artikel-anzahl');
51395    					var btn2 = $('.btn-header-warenkorb');
51396    					btn.addClass('active');
51397    					btn2.addClass('active');
51398    					$timeout(function() {
51399    						btn.removeClass('active');
51400    						btn2.removeClass('active');
51401    					}, 500);
51402    				} else {
51403    					$('.navigations-shopping-cart').addClass('active');
51404    					$timeout(function () {
51405    						$('.navigations-shopping-cart').removeClass('active');
51406    					}, 1000);
51407    				}
51408    				$rootScope.showGerichtAddedNotify();
51409                }, 500);
51410			}
51411		} else {
51412			if(lastWkItem.length > 0) {
51413				var gerichtZeileOrig = $('[data-gericht-id="' + $rootScope.currentGericht.id + '"]');
51414				var gerichtZeile = gerichtZeileOrig.clone();
51415
51416				var offsetTop = lastWkItem.offset().top;
51417				var offsetLeft = lastWkItem.offset().left;
51418				var width = lastWkItem.outerWidth();
51419				var positionTop = offsetTop + lastWkItem.outerHeight(true) - gerichtZeile.height() / 2 * 0.1;
51420				var positionLeft = (offsetLeft + width / 2) - gerichtZeile.width() / 2 * 0.1;
51421
51422				gerichtZeile.css({
51423					position: "absolute",
51424					top: gerichtZeileOrig.offset().top,
51425					left: gerichtZeileOrig.offset().left,
51426					zIndex: 20,
51427					width: gerichtZeileOrig.width()
51428				});
51429				$('body').append(gerichtZeile);
51430
51431				gerichtZeile.effect('scale', {percent: 10}, 500, function() {});
51432				gerichtZeile.animate({
51433					top: positionTop,
51434					left: positionLeft,
51435					marginLeft: 0
51436				}, {
51437					duration: 500,
51438					queue: false,
51439					complete: function() {
51440						$(this).remove();
51441						$(this).removeAttr('style');
51442
51443						$timeout(function() {
51444							if($rootScope.warenkorb == null) {
51445								$rootScope.warenkorb = [];
51446							}
51447
51448							gerichtToAdd.currentAnzahl = $rootScope.convertToNumber(gerichtToAdd.currentAnzahl);
51449
51450							if(gerichtToAdd.currentAnzahl == 0) {
51451								$rootScope.currentGerichtIndex = false;
51452								return;
51453							}
51454							$rootScope.warenkorb.push($rootScope.clone(gerichtToAdd));
51455
51456							$rootScope.currentGerichtIndex = false;
51457							$rootScope.showGerichtAddedNotify();
51458						});
51459					}
51460				});
51461			} else {
51462				if($rootScope.warenkorb == null) {
51463					$rootScope.warenkorb = [];
51464				}
51465
51466				gerichtToAdd.currentAnzahl = $rootScope.convertToNumber(gerichtToAdd.currentAnzahl);
51467
51468				if(gerichtToAdd.currentAnzahl == 0) {
51469					$rootScope.currentGerichtIndex = false;
51470					return;
51471				}
51472
51473				$rootScope.warenkorb.push($rootScope.clone(gerichtToAdd));
51474
51475				if($rootScope.currentGerichtIndex !== false) {
51476					$rootScope.checkGericht($rootScope.currentGericht, $rootScope.currentGerichtIndex);
51477				}
51478
51479				$timeout(function() {
51480					$rootScope.currentGerichtIndex = false;
51481					$rootScope.currentGericht = null;
51482				});
51483
51484                var wkFooter = $('.wkandInfo');
51485                if(wkFooter.length > 0) {
51486                    wkFooter.addClass('active');
51487                    $timeout(function() {
51488                        wkFooter.removeClass('active');
51489                    }, 500);
51490                }
51491
51492				if($('.header-v21').length > 0) {
51493					var btn = $('.btn-artikel-anzahl');
51494					btn.addClass('active');
51495					$timeout(function() {
51496						btn.removeClass('active');
51497					}, 500);
51498				} else {
51499					$('.navigations-shopping-cart').addClass('active');
51500					$timeout(function () {
51501						$('.navigations-shopping-cart').removeClass('active');
51502					}, 1000);
51503				}
51504
51505				$rootScope.showGerichtAddedNotify();
51506			}
51507		}
51508	};
51509
51510	$rootScope.increaseAnzahlGericht = function(item) {
51511		item.currentAnzahl++;
51512	};
51513
51514	$rootScope.decreaseAnzahlGericht = function(itemIndex, item) {
51515		item.currentAnzahl--;
51516
51517		if(item.currentAnzahl <= 0) {
51518			$rootScope.removeGericht(itemIndex);
51519		}
51520	};
51521
51522	$rootScope.editGericht = function(index) {
51523		$rootScope.currentGericht = $rootScope.warenkorb[index];
51524		$rootScope.currentGerichtIndex = index;
51525		$rootScope.openZutatModal($rootScope.currentGericht);
51526	};
51527
51528	$rootScope.removeGericht = function(index) {
51529		$rootScope.warenkorb.splice(index, 1);
51530	};
51531
51532	$rootScope.clearWarenkorb = function() {
51533		$rootScope.warenkorb = [];
51534	};
51535
51536	$rootScope.getGerichtById = function(id, cookieGericht, callback, abgleichsGericht) {
51537		$http.get(
51538			"?cl=index&mt=apiGericht"
51539			+ "&abholLiefer=" + $rootScope.currentType
51540			+ "&datum=" + $rootScope.currentDatum
51541			+ "&uhrzeit=" + $rootScope.currentUhrzeit
51542			+ "&gerichtId=" + id
51543		, {}).then(function(data) {
51544			if(typeof data.data.error == 'undefined' || data.data.error == 'false') {
51545				var gericht = data.data;
51546				if(typeof callback != 'undefined') {
51547					callback(gericht, abgleichsGericht);
51548				} else {
51549					$rootScope.warenkorb.push(angular.extend(gericht, cookieGericht));
51550					if($rootScope.currentGerichtIndex !== false && typeof $rootScope.warenkorb[$rootScope.currentGerichtIndex] != "undefined") {
51551						$rootScope.currentGericht = $rootScope.warenkorb[$rootScope.currentGerichtIndex];
51552					}
51553				}
51554			}
51555		});
51556	};
51557
51558	$rootScope.isXor = function(zutatPropsIndex, gericht) {
51559        if(typeof gericht.zutaten == "string") {
51560            var zutaten = gericht.zutatenObj;
51561        } else {
51562            var zutaten = gericht.zutaten;
51563        }
51564
51565		for(var r in zutaten.zutaten[zutatPropsIndex].rules) {
51566			if(zutaten.zutaten[zutatPropsIndex].rules[r].type == "xor") {
51567				return true;
51568			}
51569		}
51570
51571		return false;
51572	};
51573
51574	$rootScope.isBoniXor = function(zutatProp) {
51575		for(var r in zutatProp.rules) {
51576			if(zutatProp.rules[r].type == "xor") {
51577				return true;
51578			}
51579		}
51580
51581		return false;
51582	};
51583
51584	$rootScope.isPlus = function(zutatProps) {
51585		for(var r in zutatProps.rules) {
51586			if(zutatProps.rules[r].type == "+") {
51587				return true;
51588			}
51589		}
51590
51591		return false;
51592	};
51593
51594	$rootScope.getShowIfIndex = function(zutatProps) {
51595		for(var r in zutatProps.rules) {
51596			if(zutatProps.rules[r].type == "showif") {
51597				return r;
51598			}
51599		}
51600
51601		return false;
51602	}
51603
51604	$rootScope.getPlusIndex = function(zutatProps) {
51605		for(var r in zutatProps.rules) {
51606			if(zutatProps.rules[r].type == "+") {
51607				return r;
51608			}
51609		}
51610
51611		return false;
51612	};
51613
51614	$rootScope.isMax = function(zutatProps) {
51615		for(var r in zutatProps.rules) {
51616			if(zutatProps.rules[r].type == "max") {
51617				return true;
51618			}
51619		}
51620
51621		return false;
51622	};
51623
51624	$rootScope.getMaxIndex = function(zutatProps) {
51625		for(var r in zutatProps.rules) {
51626			if(zutatProps.rules[r].type == "max") {
51627				return r;
51628			}
51629		}
51630
51631		return false;
51632	};
51633
51634	$rootScope.isMin = function(zutatProps) {
51635		for(var r in zutatProps.rules) {
51636			if(zutatProps.rules[r].type == "min") {
51637				return true;
51638			}
51639		}
51640
51641		return false;
51642	};
51643
51644	$rootScope.getMinIndex = function(zutatProps) {
51645		for(var r in zutatProps.rules) {
51646			if(zutatProps.rules[r].type == "min") {
51647				return r;
51648			}
51649		}
51650
51651		return false;
51652	};
51653
51654	$rootScope.isProzent = function(zutatProps) {
51655		for(var r in zutatProps.rules) {
51656			if(zutatProps.rules[r].type == "%") {
51657				return true;
51658			}
51659		}
51660
51661		return false;
51662	};
51663
51664	$rootScope.getProzentIndex = function(zutatProps) {
51665		for(var r in zutatProps.rules) {
51666			if(zutatProps.rules[r].type == "%") {
51667				return r;
51668			}
51669		}
51670
51671		return false;
51672	};
51673
51674	$rootScope.getProzentSatz = function(zutatProps) {
51675		var prozentIndex = $rootScope.getProzentIndex(zutatProps);
51676
51677		if(prozentIndex === false) {
51678			return 0;
51679		}
51680
51681		return zutatProps.rules[prozentIndex].satz;
51682	};
51683
51684	$rootScope.getProzentFaktor = function(zutatProps) {
51685		return 1 - $rootScope.getProzentSatz(zutatProps);
51686	};
51687
51688	$rootScope.getGerichtsPreis = function(gericht, index, katNr, withProzente, prozenteBerechnen) {
51689        if(prozenteBerechnen === undefined) {
51690            prozenteBerechnen = true;
51691        }
51692		if($rootScope.currentType != 'liefer' && $rootScope.currentType != 'abhol') {
51693			return 0;
51694		}
51695		if(typeof katNr == "undefined") {
51696			katNr = gericht.katNr; //$rootScope.getKatNrOfGericht(gericht.id);
51697		}
51698
51699		var preisNormal = gericht.preise[$rootScope.currentType][index].replace(',', '.');
51700		var preisRabatt = preisNormal;
51701
51702        if(prozenteBerechnen) {
51703    		var prozentRabatt = 0;
51704    		var betragRabatt = 0;
51705
51706    		var treuepreis = $rootScope.getTreuepreisById($rootScope.currentTreuepreis);
51707
51708    		if(treuepreis !== null && treuepreis.art != 3) {
51709
51710    		} else if(gericht.prozent.show) {
51711    			prozentRabatt =  gericht.prozent.prozente[$rootScope.currentType];
51712    		} else if($rootScope.getObjectLength(gericht.switch) > 0 && typeof gericht.switch.rabatt != "undefined" && typeof gericht.switch.rabatt.typ != "undefined") {
51713    			if(typeof gericht.switch.rabatt != "undefined") {
51714    				if(gericht.switch.rabatt.typ == "1") {
51715    					prozentRabatt = Number(gericht.switch.rabatt.satz);
51716    				} else if(gericht.switch.rabatt.typ == "2") {
51717    					betragRabatt = Number(gericht.switch.rabatt.satz);
51718    				} else if(gericht.switch.rabatt.typ == "3") {
51719    					gericht.preise.liefer = gericht.preise.abhol;
51720    				}
51721    			}
51722    		} else if($rootScope.getObjectLength($rootScope.shopSettings.prozentKomplett) > 0) {
51723    			if(
51724    				typeof $rootScope.shopSettings.prozentKomplett.prozente[$rootScope.currentReferer] != "undefined"
51725    				&& typeof $rootScope.shopSettings.prozentKomplett.prozente[$rootScope.currentReferer][$rootScope.currentType] != "undefined"
51726    				&& (
51727                        (
51728                            typeof ($rootScope.shopSettings.prozentKomplett.kategorien[$rootScope.currentReferer][$rootScope.currentType]) == "undefined"
51729                            && $rootScope.shopSettings.prozentKomplett.kategorien[$rootScope.currentReferer].indexOf(katNr) == -1
51730                        ) || (
51731                            typeof ($rootScope.shopSettings.prozentKomplett.kategorien[$rootScope.currentReferer][$rootScope.currentType]) != "undefined"
51732                            && $rootScope.shopSettings.prozentKomplett.kategorien[$rootScope.currentReferer][$rootScope.currentType].indexOf(katNr) == -1
51733                        )
51734                    )
51735    			) {
51736    				prozentRabatt = $rootScope.shopSettings.prozentKomplett.prozente[$rootScope.currentReferer][$rootScope.currentType];
51737    			} else if(
51738    				typeof $rootScope.shopSettings.prozentKomplett.prozente["normal"] != "undefined"
51739    				&& (
51740                        (
51741                            typeof ($rootScope.shopSettings.prozentKomplett.kategorien["normal"][$rootScope.currentType]) == "undefined"
51742                            && $rootScope.shopSettings.prozentKomplett.kategorien["normal"].indexOf(katNr) == -1
51743                        ) || (
51744                            typeof ($rootScope.shopSettings.prozentKomplett.kategorien["normal"][$rootScope.currentType]) != "undefined"
51745                            && $rootScope.shopSettings.prozentKomplett.kategorien["normal"][$rootScope.currentType].indexOf(katNr) == -1
51746                        )
51747                    )
51748    			) {
51749    				prozentRabatt = $rootScope.shopSettings.prozentKomplett.prozente["normal"][$rootScope.currentType];
51750    			} else if(
51751    				typeof $rootScope.shopSettings.prozentKomplett.prozente[$rootScope.currentType] != "undefined"
51752    				&& $rootScope.shopSettings.prozentKomplett.kategorien.indexOf(katNr) == -1
51753    			) {
51754    				prozentRabatt = $rootScope.shopSettings.prozentKomplett.prozente[$rootScope.currentType];
51755    			}
51756    		}
51757
51758            // var couponGueltig = false;
51759            // var prozenteZusammen = 0;
51760            prozentRabatt = Number(prozentRabatt);
51761
51762            for(var gc in $rootScope.currentCoupons) {
51763                if($rootScope.currentCoupons[gc].typ != 1) {
51764					if($rootScope.currentCoupons[gc].isKombinierbar == 0) {
51765						prozentRabatt = 0;
51766						betragRabatt = 0;
51767					}
51768                    continue;
51769                }
51770                if($rootScope.currentCoupons[gc].berechnungInbegriffeneGerichteObj.indexOf(gericht.devNummer) > -1 || $rootScope.currentCoupons[gc].berechnungInbegriffeneGerichteObj.length == 0) {
51771                    if($rootScope.currentCoupons[gc].isKombinierbar == 1) {
51772                        prozentRabatt += Number($rootScope.currentCoupons[gc].wert);
51773                    } else {
51774                        prozentRabatt = Number($rootScope.currentCoupons[gc].wert);
51775                        betragRabatt = 0;
51776                    }
51777                }
51778            }
51779
51780            if(
51781                withProzente == true
51782                || (
51783                    typeof withProzente == "undefined"
51784                    && (
51785                        typeof $rootScope.currentCoupons.length == 0
51786                        || !$rootScope.couponWithNotCombiningRabatt
51787                    )
51788                )
51789            ) {
51790            } else {
51791                prozentRabatt = $rootScope.couponRabattKomplett;
51792                betragRabatt = 0;
51793            }
51794
51795            return preisRabatt * (1 - prozentRabatt) - betragRabatt;
51796        } else {
51797            return $rootScope.convertToNumber(preisRabatt);
51798        }
51799	};
51800
51801	$rootScope.getTreuepreisById = function(id) {
51802		for(var tp in $rootScope.treuepreise) {
51803			if($rootScope.treuepreise[tp].id == id) {
51804				return $rootScope.treuepreise[tp];
51805			}
51806		}
51807
51808		return null;
51809	};
51810
51811	$rootScope.isProzentKomplett = function(katNr) {
51812		if($rootScope.getObjectLength($rootScope.shopSettings.prozentKomplett) == 0) {
51813			return false;
51814		}
51815
51816		if(
51817			typeof $rootScope.shopSettings.prozentKomplett.prozente[$rootScope.currentReferer] == "undefined"
51818			&& typeof $rootScope.shopSettings.prozentKomplett.prozente[$rootScope.currentType] == "undefined"
51819			&& typeof $rootScope.shopSettings.prozentKomplett.prozente["normal"] == "undefined"
51820		) {
51821			return false;
51822		}
51823
51824		if(
51825			typeof $rootScope.shopSettings.prozentKomplett.prozente[$rootScope.currentReferer] != "undefined"
51826			&& typeof $rootScope.shopSettings.prozentKomplett.prozente[$rootScope.currentReferer][$rootScope.currentType] != "undefined"
51827			&& (
51828                (
51829                    typeof $rootScope.shopSettings.prozentKomplett.kategorien[$rootScope.currentReferer][$rootScope.currentType] == "undefined"
51830				    && $rootScope.shopSettings.prozentKomplett.kategorien[$rootScope.currentReferer].indexOf(katNr) == -1
51831                ) || (
51832                    typeof $rootScope.shopSettings.prozentKomplett.kategorien[$rootScope.currentReferer][$rootScope.currentType] != "undefined"
51833    				&& $rootScope.shopSettings.prozentKomplett.kategorien[$rootScope.currentReferer][$rootScope.currentType].indexOf(katNr) == -1
51834                )
51835				|| typeof katNr == 'undefined'
51836			) && (
51837				$rootScope.shopSettings.prozentKomplett.prozente[$rootScope.currentReferer][$rootScope.currentType] > 0
51838			)
51839		) {
51840			return true;
51841		} else if(
51842			typeof $rootScope.shopSettings.prozentKomplett.prozente["normal"] != "undefined"
51843			&& typeof $rootScope.shopSettings.prozentKomplett.prozente["normal"][$rootScope.currentType] != "undefined"
51844			&& (
51845                (
51846                    typeof $rootScope.shopSettings.prozentKomplett.kategorien["normal"][$rootScope.currentType] == "undefined"
51847                    && $rootScope.shopSettings.prozentKomplett.kategorien["normal"].indexOf(katNr) == -1
51848                ) || (
51849                    typeof $rootScope.shopSettings.prozentKomplett.kategorien["normal"][$rootScope.currentType] != "undefined"
51850    				&& $rootScope.shopSettings.prozentKomplett.kategorien["normal"][$rootScope.currentType].indexOf(katNr) == -1
51851                )
51852				|| typeof katNr == 'undefined'
51853			) && (
51854				$rootScope.shopSettings.prozentKomplett.prozente["normal"][$rootScope.currentType] > 0
51855			)
51856		) {
51857			return true;
51858		} else if(
51859			typeof $rootScope.shopSettings.prozentKomplett.prozente[$rootScope.currentType] != "undefined"
51860			&& (
51861				$rootScope.shopSettings.prozentKomplett.kategorien.indexOf(katNr) == -1
51862				|| typeof katNr == 'undefined'
51863			) && (
51864				$rootScope.shopSettings.prozentKomplett.prozente[$rootScope.currentType] > 0
51865			)
51866		) {
51867			return true;
51868		}
51869
51870		return false;
51871	};
51872
51873	$rootScope.isProzentKomplettNormal = function(katNr) {
51874		if(
51875			typeof $rootScope.shopSettings.prozentKomplett.prozente[$rootScope.currentReferer] != "undefined"
51876			&& typeof $rootScope.shopSettings.prozentKomplett.prozente[$rootScope.currentReferer][$rootScope.currentType] != "undefined"
51877			&& (
51878                (
51879                    typeof $rootScope.shopSettings.prozentKomplett.kategorien[$rootScope.currentReferer][$rootScope.currentType] == "undefined"
51880				    && $rootScope.shopSettings.prozentKomplett.kategorien[$rootScope.currentReferer].indexOf(katNr) == -1
51881                ) || (
51882                    typeof $rootScope.shopSettings.prozentKomplett.kategorien[$rootScope.currentReferer][$rootScope.currentType] != "undefined"
51883    				&& $rootScope.shopSettings.prozentKomplett.kategorien[$rootScope.currentReferer][$rootScope.currentType].indexOf(katNr) == -1
51884                )
51885				|| typeof katNr == 'undefined'
51886			)
51887		) {
51888			return false;
51889		}
51890
51891		if(
51892			typeof $rootScope.shopSettings.prozentKomplett.prozente["normal"] != "undefined"
51893			&& typeof $rootScope.shopSettings.prozentKomplett.prozente["normal"][$rootScope.currentType] != "undefined"
51894			&& (
51895                (
51896                    typeof $rootScope.shopSettings.prozentKomplett.kategorien["normal"][$rootScope.currentType] == "undefined"
51897				    && $rootScope.shopSettings.prozentKomplett.kategorien["normal"].indexOf(katNr) == -1
51898                ) || (
51899                    typeof $rootScope.shopSettings.prozentKomplett.kategorien["normal"][$rootScope.currentType] != "undefined"
51900    				&& $rootScope.shopSettings.prozentKomplett.kategorien["normal"][$rootScope.currentType].indexOf(katNr) == -1
51901                )
51902				|| typeof katNr == 'undefined'
51903			) && (
51904				$rootScope.shopSettings.prozentKomplett.prozente["normal"][$rootScope.currentType] > 0
51905			)
51906		) {
51907			return true;
51908		}
51909
51910		return false;
51911	};
51912
51913	$rootScope.isAltProzent = function(katNr) {
51914		if(
51915			typeof $rootScope.shopSettings.prozentKomplett == 'undefined'
51916			|| $rootScope.shopSettings.prozentKomplett == null
51917			|| typeof $rootScope.shopSettings.prozentKomplett.prozente == "undefined"
51918		) {
51919			return false;
51920		}
51921		if(
51922			(
51923				typeof $rootScope.shopSettings.prozentKomplett.prozente.abhol != "undefined"
51924				|| typeof $rootScope.shopSettings.prozentKomplett.prozente.liefer != "undefined"
51925			) && (
51926				$rootScope.shopSettings.prozentKomplett.kategorien.indexOf(katNr) != -1
51927				|| typeof katNr == 'undefined'
51928			) && (
51929				$rootScope.shopSettings.prozentKomplett.prozente.abhol > 0
51930				|| $rootScope.shopSettings.prozentKomplett.prozente.liefer > 0
51931			)
51932		) {
51933			return true;
51934		}
51935
51936		return false;
51937	};
51938
51939	$rootScope.showZutat = function(zutatPropsIndex, gericht) {
51940        if(typeof gericht.zutaten == "string") {
51941            var zutatProps = gericht.zutatenObj.zutaten[zutatPropsIndex];
51942        } else {
51943		    var zutatProps = gericht.zutaten.zutaten[zutatPropsIndex];
51944        }
51945		var showIfIndex = $rootScope.getShowIfIndex(zutatProps);
51946		if(showIfIndex !== false) {
51947			var showIf = zutatProps.rules[showIfIndex];
51948			var zpiShowIf = Number(showIf.zutat) - 1;
51949			var ziShowIf = showIf.wahl.split(',');
51950			var opShowIf = showIf.operator;
51951
51952			if(typeof gericht.currentZutaten == "undefined") {
51953				return false;
51954			}
51955
51956			if(typeof gericht.currentZutaten[zpiShowIf] == "undefined") {
51957				return false;
51958			}
51959
51960			if(opShowIf == "or") {
51961				for(var zi in ziShowIf) {
51962					if(
51963						typeof gericht.currentZutaten[zpiShowIf][Number(ziShowIf[zi]) - 1] != "undefined"
51964						&& gericht.currentZutaten[zpiShowIf][Number(ziShowIf[zi]) - 1] >= 1
51965					) {
51966						return true;
51967					}
51968				}
51969				return false;
51970			} else if(opShowIf == "and") {
51971				for(var zi in ziShowIf) {
51972					if(
51973						typeof gericht.currentZutaten[zpiShowIf][Number(ziShowIf[zi]) - 1] == "undefined"
51974						|| gericht.currentZutaten[zpiShowIf][Number(ziShowIf[zi]) - 1] < 1
51975					) {
51976						return false;
51977					}
51978				}
51979				return true;
51980			} else {
51981				for(var zi in ziShowIf) {
51982					if(
51983						typeof gericht.currentZutaten[zpiShowIf][Number(ziShowIf[zi]) - 1] != "undefined"
51984						&& gericht.currentZutaten[zpiShowIf][Number(ziShowIf[zi]) - 1] >= 1
51985					) {
51986						return true;
51987					}
51988				}
51989				return false;
51990			}
51991		}
51992
51993		return true;
51994	};
51995
51996	$rootScope.isZutatMaxErreicht = function(zutatPropsIndex, zutatIndex, gericht) {
51997		if(typeof gericht.currentZutaten == "undefined") {
51998			return false;
51999		}
52000
52001		var zutatenGesamtAnzahl = 0;
52002		var xVorhanden = false;
52003		for(var z in gericht.currentZutaten[zutatPropsIndex]) {
52004			zutatenGesamtAnzahl += gericht.currentZutaten[zutatPropsIndex][z];
52005		}
52006
52007        if(typeof gericht.zutaten == "string") {
52008            var rules = gericht.zutatenObj.zutaten[zutatPropsIndex].rules;
52009        } else {
52010		    var rules = gericht.zutaten.zutaten[zutatPropsIndex].rules;
52011        }
52012
52013		for(var r in rules) {
52014			if(rules[r].type == "max" && zutatenGesamtAnzahl >= rules[r].anzahl) {
52015				return true;
52016			}
52017
52018			if(rules[r].type == "x") {
52019				if(
52020					typeof gericht.currentZutaten[zutatPropsIndex] != "undefined"
52021					&& gericht.currentZutaten[zutatPropsIndex][zutatIndex] >= rules[r].anzahl
52022				) {
52023					return true;
52024				}
52025				xVorhanden = true;
52026			}
52027		}
52028
52029		if(
52030			typeof gericht.currentZutaten[zutatPropsIndex] == "undefined"
52031			|| typeof gericht.currentZutaten[zutatPropsIndex][zutatIndex] == "undefined"
52032		) {
52033			return false;
52034		}
52035
52036		if(!xVorhanden && gericht.currentZutaten[zutatPropsIndex][zutatIndex] >= 1) {
52037			return true;
52038		}
52039
52040		return false;
52041	};
52042
52043	$rootScope.getZutatAnzahl = function(zutaten, gericht) {
52044        if(typeof gericht == "undefined") {
52045            gericht = $rootScope.currentGericht;
52046        }
52047		var anzahl = 0;
52048		for(var z in zutaten) {
52049			if(zutaten[z].preise[$rootScope.currentType][gericht.currentPreis] != "#") {
52050				anzahl++;
52051			}
52052		}
52053
52054		return anzahl;
52055	};
52056
52057	$rootScope.getZutatPreis = function(zutatProps, zutatIndex, zutatAnzahl, gericht) {
52058		var anzahlFuerRechnung = $rootScope.clone(zutatAnzahl);
52059		if(typeof zutatProps.standardZutaten != "undefined" && zutatProps.standardZutaten.indexOf(Number(zutatIndex)) > -1) {
52060			anzahlFuerRechnung--;
52061		}
52062
52063		if(
52064			typeof zutatProps.zutaten[zutatIndex].preise[$rootScope.currentType][gericht.currentPreis] == "undefined"
52065			|| isNaN(zutatProps.zutaten[zutatIndex].preise[$rootScope.currentType][gericht.currentPreis])
52066		) {
52067			return 0;
52068		}
52069
52070        var betrag = anzahlFuerRechnung * zutatProps.zutaten[zutatIndex].preise[$rootScope.currentType][gericht.currentPreis];
52071        var prozentFaktor = $rootScope.getProzentFaktor(zutatProps);
52072
52073        if($rootScope.shopSettings["prozentAllZutaten"] != 0) {
52074            prozentFaktor -= $rootScope.getZutatProzentFromCoupon(gericht.devNummer);
52075        }
52076
52077		return betrag * prozentFaktor;
52078	};
52079
52080	$rootScope.isStandardZutatRemoved = function(zutaten, standardZutatIndex) {
52081		if(typeof zutaten[standardZutatIndex] == "undefined") {
52082			return true;
52083		}
52084
52085		if(zutaten[standardZutatIndex] == 0) {
52086			return true;
52087		}
52088
52089		return false;
52090	};
52091
52092	$rootScope.moreThanStandard = function(zutatProps, zutatIndex, zutatAnzahl) {
52093		var anzahlFuerRechnung = $rootScope.clone(zutatAnzahl);
52094		if(typeof zutatProps.standardZutaten != "undefined" && zutatProps.standardZutaten.indexOf(Number(zutatIndex)) > -1) {
52095			anzahlFuerRechnung--;
52096		}
52097
52098		return anzahlFuerRechnung > 0;
52099	};
52100
52101    $rootScope.checkZutaten = function() {
52102        for(var g in $rootScope.warenkorb) {
52103            for(var zp in $rootScope.warenkorb[g].currentZutaten) {
52104                for(var z in $rootScope.warenkorb[g].currentZutaten[zp]) {
52105                    var preis = $rootScope.warenkorb[g].zutaten.zutaten[zp].zutaten[z].preise[$rootScope.currentType][$rootScope.warenkorb[g].currentPreis];
52106                    if(preis == "#") {
52107                        delete $rootScope.warenkorb[g].currentZutaten[zp][z];
52108                    }
52109                }
52110            }
52111        }
52112    };
52113
52114    $rootScope.checkZutatenCurrentGericht = function() {
52115        if(typeof $rootScope.currentGericht == "undefined" || $rootScope.currentGericht == null || typeof $rootScope.currentGericht.currentZutaten == "undefined") {
52116            return;
52117        }
52118        for(var zp in $rootScope.currentGericht.currentZutaten) {
52119            for(var z in $rootScope.currentGericht.currentZutaten[zp]) {
52120                var preis = $rootScope.currentGericht.zutaten.zutaten[zp].zutaten[z].preise[$rootScope.currentType][$rootScope.currentGericht.currentPreis];
52121                if(preis == "#") {
52122                    delete $rootScope.currentGericht.currentZutaten[zp][z];
52123                }
52124            }
52125        }
52126    };
52127
52128	$rootScope.getPreisAnzahl = function(preise) {
52129		var anzahl = 0;
52130		for(var p in preise) {
52131			if(preise[p] != '' && preise[p].length > 0 && preise[p] != '#') {
52132				anzahl++;
52133			}
52134		}
52135
52136		return anzahl;
52137	};
52138
52139	$rootScope.setCookieKundenDaten = function() {
52140		if($rootScope.currentFilialId != 0) {
52141			$rootScope.setLocalObject('kundendaten' + $rootScope.currentFilialId, $rootScope.kundenDaten, true);
52142		}
52143	};
52144
52145	$rootScope.areRulesSatisfied = function(gericht) {
52146		if(gericht == null) {
52147			return true;
52148		}
52149
52150        if(typeof gericht.zutaten == "string") {
52151            var zutaten = gericht.zutatenObj.zutaten;
52152        } else {
52153		    var zutaten = gericht.zutaten.zutaten;
52154        }
52155
52156		for(var z in zutaten) {
52157            if($rootScope.showZutat(z, gericht)) {
52158    			for(var r in zutaten[z].rules) {
52159    				if(zutaten[z].rules[r].type == "min") {
52160    					if(typeof gericht.currentZutaten == "undefined" || typeof gericht.currentZutaten[z] == "undefined") {
52161    						return false;
52162    					}
52163    					var anzahlSelectedZutaten = 0;
52164    					var selectedZutaten = gericht.currentZutaten[z];
52165    					for(var sz in selectedZutaten) {
52166    						anzahlSelectedZutaten += selectedZutaten[sz];
52167    					}
52168
52169    					if(anzahlSelectedZutaten < zutaten[z].rules[r].anzahl) {
52170    						return false;
52171    					}
52172    				}
52173    			}
52174            }
52175		}
52176
52177		return true;
52178	};
52179
52180	$rootScope.getKatNrOfGericht = function(gerichtId) {
52181		for(var k in $rootScope.gerichte) {
52182			for(var g in $rootScope.gerichte[k]) {
52183				if(g == gerichtId) {
52184					return $rootScope.kategorien[k].nummer;
52185				}
52186			}
52187		}
52188
52189		return 0;
52190	};
52191
52192	$rootScope.checkUhrzeitInArray = function(uhrzeit) {
52193		return $rootScope.config['zeitpunkt-zeiten'].indexOf(uhrzeit) > -1;
52194	};
52195
52196    $rootScope.getDecimals = function(zahl) {
52197        zahl = String($rootScope.convertToNumber(zahl));
52198        var indexOfComma = zahl.search(new RegExp(/\./));
52199        if(indexOfComma == -1) {
52200            indexOfComma = zahl.search(new RegExp(/\,/));
52201            if(indexOfComma == -1) {
52202                return "";
52203            }
52204        }
52205
52206        return zahl.substr(indexOfComma, zahl.length);
52207    };
52208
52209	$rootScope.convertToWaehrung = function(zahl, realValue) {
52210        if(typeof zahl == "undefined") {
52211			return "0,00";
52212		}
52213
52214		if(typeof zahl != "number") {
52215			zahl = zahl.replace(',', '.');
52216		}
52217
52218		if(zahl < 0 && realValue === undefined) {
52219			zahl = 0;
52220		}
52221        if(zahl < 0) {
52222            var vorzeichen = '-';
52223        } else {
52224            var vorzeichen = '+';
52225        }
52226
52227		var string = String(Math.round(zahl*100)/100);
52228        string = string.replace('-', '');
52229		var indexOfPoint = string.indexOf(".");
52230
52231		if(indexOfPoint == -1) {
52232            if(realValue) {
52233                string = vorzeichen + " " + string;
52234            }
52235			return string + ",00";
52236		}
52237
52238		var nachkommastellen = string.substring(indexOfPoint + 1, string.length);
52239
52240		if(nachkommastellen.length > 2) {
52241            if(realValue) {
52242                string = vorzeichen + " " + string;
52243            }
52244			return string.substring(0, string.length).replace('.', ',');
52245		}
52246
52247		if(nachkommastellen.length == 2) {
52248            if(realValue) {
52249                string = vorzeichen + " " + string;
52250            }
52251			return string.replace('.', ',');
52252		}
52253
52254		if(nachkommastellen.length == 1) {
52255            if(realValue) {
52256                string = vorzeichen + " " + string;
52257            }
52258			return (string + "0").replace('.', ',');
52259		}
52260
52261		if(nachkommastellen.length == 0) {
52262            if(realValue) {
52263                string = vorzeichen + " " + string;
52264            }
52265			return (string + "00").replace('.', ',');
52266		}
52267	};
52268
52269	$rootScope.convertToAusgabeNumber = function(number) {
52270		return String(number).replace('.', ',');
52271	};
52272
52273    $rootScope.round = function(number, decimals) {
52274        number = number * Math.pow(10, decimals);
52275        number = Math.round(number);
52276        return number / Math.pow(10, decimals);
52277    };
52278
52279	$rootScope.convertToNumber = function(number) {
52280        if(number === null || number === undefined) {
52281            return 0;
52282        }
52283        if(typeof number == "string") {
52284            number = number.replace(',', '.');
52285        }
52286		if($rootScope.isNumber(number)) {
52287			return Number(number);
52288		}
52289
52290		return 0;
52291	};
52292
52293	$rootScope.clone = function(obj) {
52294		if (null == obj || "object" != typeof obj) return obj;
52295	    var copy = obj.constructor();
52296	    for (var attr in obj) {
52297	        if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
52298	    }
52299	    return copy;
52300	};
52301
52302    $rootScope.jsonStringify = function(obj) {
52303        return JSON.stringify(obj);
52304    };
52305
52306	$rootScope.getObjectLength = function(object) {
52307		if(typeof object == "undefined")  {
52308			return 0;
52309		} else if(typeof object == "array") {
52310			return object.length;
52311		}
52312	    var size = 0, key;
52313	    for (key in object) {
52314	        if (object.hasOwnProperty(key)) size++;
52315	    }
52316	    return size;
52317	};
52318
52319	/** Nur im Format d.m.Y H:i (PHP) **/
52320	$rootScope.getDateFromString = function(string) {
52321		var datumUndUhrzeit = string.split(" ");
52322		if(datumUndUhrzeit.length < 2) {
52323			return false;
52324		}
52325
52326		var datumTeile = datumUndUhrzeit[0].split(".");
52327		if(datumTeile.length < 3) {
52328			return false;
52329		}
52330
52331		var uhrzeitTeile = datumUndUhrzeit[1].split(":");
52332		if(uhrzeitTeile.length < 2) {
52333			return false;
52334		}
52335
52336		return new Date(datumTeile[2], datumTeile[1], datumTeile[0], uhrzeitTeile[0], uhrzeitTeile[1], 0, 0);
52337	};
52338
52339	$rootScope.isNumber = function(value) {
52340		if(isNaN(value)) {
52341			return false;
52342		}
52343
52344		if(isNaN(Number(value))) {
52345			return false;
52346		}
52347
52348		if(Number(value) != value) {
52349			return false;
52350		}
52351
52352		return true;
52353	}
52354
52355	$rootScope.getWeekday = function() {
52356		var date = $rootScope.getDateFromString($rootScope.currentDatum + " " + $rootScope.currentUhrzeit);
52357
52358		if(date === false) {
52359			return 0;
52360		}
52361
52362		return date.getDay();
52363	};
52364
52365	$rootScope.toggleKategorie = function() {
52366		var windowWidth = $(window).width();
52367
52368		if(windowWidth <= 1199) {
52369			$('.HpKategorie').toggle();
52370		} else {
52371			$('.HpKategorie').show();
52372		}
52373	};
52374
52375	$rootScope.openZutatModal = function(ger, event) {
52376		if(!ger.showAt.show
52377			|| (
52378				typeof event != "undefined" && $(event.target).is('.btn') && !$(event.target).is('.btn-add-to-warenkorb')
52379			)
52380		) {
52381			return false;
52382		}
52383
52384		$rootScope.openModal($('.zutatenModal'));
52385	};
52386
52387	$rootScope.closeZutatModal = function(event) {
52388		if(typeof event == "undefined" || ($(event.target).is('.zutatenModal') && $(event.target).parents('.cfjs-modal').length == 0)) {
52389			if($rootScope.currentGerichtIndex !== false) {
52390				$rootScope.checkGericht($rootScope.currentGericht, $rootScope.currentGerichtIndex);
52391			}
52392
52393			$timeout(function() {
52394				$rootScope.currentGerichtIndex = false;
52395				$rootScope.currentGericht = null;
52396			}, 500);
52397			$rootScope.closeModal($('.zutatenModal'));
52398		}
52399	};
52400
52401	$rootScope.closeAltersAbfrage = function() {
52402		$rootScope.closeModal($('.altersabfrage-modal'));
52403	};
52404
52405	$rootScope.openUhrzeitenModal = function() {
52406		$rootScope.getUhrzeiten($rootScope.currentDatum);
52407		$('#UhrzeitenModal').show();
52408		resizeModal($('.UhrzeitenModal'));
52409		// $('body').css('overflow', 'hidden');
52410	};
52411
52412	$rootScope.closeUhrzeitenModal = function(event) {
52413		if(typeof event == "undefined" || $(event.target).is('#UhrzeitenModal')) {
52414			$('#UhrzeitenModal').modal("hide");
52415			// $('body').css('overflow', 'auto');
52416		}
52417	};
52418
52419	$rootScope.openLiefergebietModal = function() {
52420		$('#liefergebietModal').show();
52421		// $('body').css('overflow', 'hidden');
52422	};
52423
52424	$rootScope.openLieferOptionenModal = function() {
52425		$rootScope.openModal($('.lieferOptionenModal'));
52426	};
52427
52428	$rootScope.openFilialauswahlModal = function() {
52429        $rootScope.filialAuswahlOffen = true;
52430		$rootScope.openModal($('.filialauswahlModal'));
52431	};
52432
52433	$rootScope.openNurAbholungHint = function() {
52434		$rootScope.openModal($('.nurAbholungHint'));
52435	};
52436
52437	$rootScope.closeLieferOptionenModal = function(event) {
52438		if(typeof event == "undefined" || ($(event.target).is('.lieferOptionenModal') && $(event.target).parents('.cfjs-modal').length == 0)) {
52439			$rootScope.loadKategorien();
52440			$rootScope.loadBoni();
52441			$rootScope.closeModal($('.lieferOptionenModal'));
52442		}
52443	};
52444
52445	$rootScope.closeLiefergebietModal = function(event) {
52446		$rootScope.loadKategorien();
52447		$rootScope.loadBoni();
52448		$('#liefergebietModal').hide();
52449	};
52450
52451	$rootScope.closeFilialauswahlModal = function(event) {
52452		if(typeof event == "undefined" || $(event.target).is('.filialauswahlModal')) {
52453			$rootScope.closeModal($('.filialauswahlModal'));
52454		}
52455	};
52456
52457	$rootScope.closeNurAbholungHint = function(event) {
52458		$rootScope.closeModal($('.nurAbholungHint'));
52459	};
52460
52461	$rootScope.openTypeModal = function() {
52462		$('#typeModal').show();
52463		// $('body').css('overflow', 'hidden');
52464	};
52465
52466	$rootScope.closeTypeModal = function(event) {
52467		if(typeof event == "undefined" || $(event.target).is('#TypeModal')) {
52468			$('#typeModal').modal("hide");
52469			// $('body').css('overflow', 'auto');
52470		}
52471	};
52472
52473	$rootScope.openImpressumModal = function() {
52474		$('#ImpressumModal').show();
52475		resizeModal($('.ImpressumModal'));
52476		// $('body').css('overflow', 'hidden');
52477	};
52478
52479	$rootScope.closeImpressumModal = function(event) {
52480		if(typeof event == "undefined" || $(event.target).is('#ImpressumModal')) {
52481			$('#ImpressumModal').hide();
52482			// $('body').css('overflow', 'auto');
52483		}
52484	};
52485
52486	$rootScope.openWarenkorbModal = function() {
52487		$('#WarenkorbModal').show();
52488		resizeModal($('.WarenkorbModal'));
52489		// $('body').css('overflow', 'hidden');
52490	};
52491
52492	$rootScope.closeWarenkorbModal = function(event) {
52493		if(typeof event == "undefined" || $(event.target).is('#WarenkorbModal')) {
52494			$('#WarenkorbModal').hide();
52495			// $('body').css('overflow', 'auto');
52496		}
52497	};
52498
52499	$rootScope.searchLiefergebiet = function() {
52500		// $timeout(function() {
52501			$rootScope.liefergebietSearchLoading = true;
52502		// });
52503
52504		for(var fs in $rootScope.filialSwitch) {
52505			if(typeof $rootScope.formData.liefergebietQuery == "undefined" || $rootScope.formData.liefergebietQuery.length == 0) {
52506				// $timeout(function() {
52507					$rootScope.filialSwitch[fs].show = true;
52508				// });
52509				continue;
52510			}
52511
52512			if($rootScope.formData.liefergebietQuery.length > 0 && $rootScope.formData.liefergebietQuery.length < 5) {
52513				// $timeout(function() {
52514					$rootScope.filialSwitch[fs].show = false;
52515				// });
52516				continue;
52517			}
52518
52519			$rootScope.filialSwitch[fs].show = false;
52520
52521			for(var lg in $rootScope.filialSwitch[fs].liefergebiete) {
52522				if($rootScope.filialSwitch[fs].liefergebiete[lg].postleitzahl == $rootScope.formData.liefergebietQuery) {
52523					// $timeout(function() {
52524						$rootScope.filialSwitch[fs].show = true;
52525					// });
52526					continue;
52527				}
52528			}
52529		}
52530
52531		$timeout(function() {
52532			$rootScope.liefergebietSearchLoading = false;
52533		}, 500);
52534	};
52535
52536	// $rootScope.openFilialauswahlModal = function() {
52537	// 	$rootScope.liefergebietSearchOpen = true;
52538	// 	console.log($rootScope.liefergebietSearchOpen);
52539	// 	return;
52540	// 	$('.sweet-alert, .sweet-overlay').remove();
52541	// 	$('body').removeClass('stop-scrolling');
52542	//
52543	// 	var container = $('<div>').css('position', 'relative');
52544	//
52545	// 	var rowSearch = $('<div>')
52546	// 		.addClass('row')
52547	// 		.addClass('boxStandard')
52548	// 		.attr('ng-if', 'shopSettings["showLiefergebietSearch"] == 1');
52549	//
52550	// 	var inputSearch = $('<input>')
52551	// 		.attr('type', 'text')
52552	// 		.attr('maxlength', '5')
52553	// 		.attr('ng-model', 'liefergebietQuery')
52554	// 		.attr('placeholder', 'Postleitzahl eingeben')
52555	// 		.attr('ng-change', 'doLiefergebietsearch()')
52556	// 		.css('display', 'block');
52557	//
52558	// 	var inputSearchOverlay = $('<div>')
52559	// 		.attr('ng-show', 'liefergebietLoad')
52560	// 		.html('<i class="fa fa-spinner fa-spin fa-3x"></i>');
52561	//
52562	// 	var link = $('<a>')
52563	// 		.attr('href', 'javascript:;')
52564	// 		.attr('ng-click', 'switchToFiliale(filiale);')
52565	// 		.addClass('specialAFiliale')
52566	// 		.attr('ng-repeat', 'filiale in filialSwitch track by $index')
52567	// 		.attr('ng-show', 'liefergebietQuery.length == 0 || liefergebietQuery.length == 5');
52568	// 	var row = $('<div>').addClass('row').addClass('boxStandard');
52569	// 	var imageCol = $('<div>').addClass('col-xs-2').addClass('text-center').addClass('iFontSize').addClass('no-padding');
52570	// 	var imageCenter = $('<center>');
52571	// 	var image = $('<img>')
52572	// 		.attr('src', 'http://media.clickfood.de/{{::filiale.ordner}}/images/logoweb.png')
52573	// 		.attr('width', '100%');
52574	// 	var infosCol = $('<div>').addClass('col-xs-8').addClass('elipsisClass').html(""
52575	// 		+ "<b>{{::filiale.name}}</b> <small>{{::filiale.ort}}</small> <b ng-if='filiale.id == " + $rootScope.currentFilialId + "'>(ausgewählt)</b>"
52576	// 		+ "<br/>"
52577	// 		+ "<i class='fa fa-map-marker'></i> {{::filiale.strasse}}<br/>"
52578	// 		+ "<i class='fa fa-map-marker'></i> {{::filiale.plz}}<br/>"
52579	// 	+ "");
52580	// 	var caretCol = $('<div>').addClass('col-xs-2').addClass('text-center');
52581	// 	var caretColCaretIcon = $('<i>').addClass('fa').addClass('fa-caret-right').attr('ng-if', 'filiale.id !=' + $rootScope.currentFilialId);
52582	// 	var caretColCheckIcon = $('<i>').addClass('fa').addClass('fa-check').attr('ng-if', 'filiale.id ==' + $rootScope.currentFilialId);
52583	//
52584	// 	var rowInvalidPlz = $('<div>')
52585	// 		.addClass('row')
52586	// 		.addClass('boxStandard')
52587	// 		.attr('ng-if', 'liefergebietQuery.length > 0 && liefergebietQuery.length < 5')
52588	// 		.html('Bitte geben Sie eine gültige Postleitzahl ein');
52589	//
52590	// 	var rowNothingFound = $('<div>')
52591	// 		.addClass('row')
52592	// 		.addClass('boxStandard')
52593	// 		.attr('ng-if', 'filialSwitch.length == 0')
52594	// 		.html('Leider liefert keine Filiale zu Ihrem ausgewählten Liefergebiet');
52595	//
52596	// 	caretCol.append(caretColCaretIcon).append(caretColCheckIcon);
52597	// 	imageCenter.append(image);
52598	// 	imageCol.append(imageCenter);
52599	// 	row.append(imageCol).append(infosCol).append(caretCol);
52600	// 	link.append(row);
52601	// 	rowSearch.append(inputSearch);
52602	// 	rowSearch.append(inputSearchOverlay);
52603	// 	container.append(rowSearch);
52604	// 	container.append(link);
52605	// 	container.append(rowInvalidPlz);
52606	// 	container.append(rowNothingFound);
52607	//
52608	// 	swal({
52609	// 		title: "Bitte wähle eine Filiale aus",
52610	// 		text: container.html(),
52611	// 		html: true,
52612	// 		showConfirmButton: false
52613	// 	});
52614	//
52615	// 	var compileEl = $compile($('.sweet-alert'))($rootScope);
52616	// 	resizeSweetAlert();
52617	// };
52618
52619	$rootScope.doLiefergebietsearch = function() {
52620		if(typeof $rootScope.formData.liefergebietQuery == "undefined" || $rootScope.formData.liefergebietQuery.length >= 2 || $rootScope.formData.liefergebietQuery.length == 0) {
52621			$rootScope.loadFilialen();
52622		}
52623	};
52624
52625	$rootScope.isIE = function msieversion() {
52626	    var ua = window.navigator.userAgent;
52627	    var msie = ua.indexOf("MSIE ");
52628
52629	    if (msie > 0 || !!navigator.userAgent.match(/Trident.*rv\:11\./))  // If Internet Explorer, return version number
52630	    {
52631	        return true;
52632	    }
52633	    else  // If another browser, return 0
52634	    {
52635	        return false;
52636	    }
52637
52638	    return false;
52639	};
52640
52641
52642	$rootScope.loadFilialen = function(callback, doCallback) {
52643		// $timeout(function() {
52644			$rootScope.liefergebietLoad = true;
52645		// });
52646
52647		if(typeof $rootScope.formData.liefergebietQuery == "undefined") {
52648			$rootScope.formData.liefergebietQuery = '';
52649		}
52650
52651        var toLoadOrt = encodeURIComponent($rootScope.currentLiefergebiet.ort);
52652        var toLoadPlz = $rootScope.currentLiefergebiet.postleitzahl;
52653        // if($rootScope.shopSettings['showLiefergebietSearch'] == 0) {
52654        //     toLoadOrt = '';
52655        //     toLoadPlz = '';
52656        // }
52657		var ie = $rootScope.isIE() ? "true" : "false";
52658
52659		$http.get('?cl=index&mt=swalFilialswitch&plz=' + toLoadPlz + '&ort=' + toLoadOrt + '&isIe=' + ie, {})
52660			.then(function(data) {
52661				$rootScope.filialSwitch = data.data;
52662
52663				if($rootScope.filialSwitch.length == 0) {
52664					$rootScope.setLocalObject("filialAuswahl" + $rootScope.currentFilialId, 'true');
52665					$rootScope.liefergebietLoad = false;
52666					return;
52667				}
52668
52669				if(typeof callback != "undefined") {
52670                    if($rootScope.shopSettings["showLiefergebietSearch"] != 2 && $rootScope.shopSettings["showLiefergebietSearch"] != 3 || doCallback) {
52671                        callback();
52672                    }
52673				}
52674
52675				// $timeout(function() {
52676					$rootScope.liefergebietLoad = false;
52677				// });
52678			});
52679	};
52680
52681	$rootScope.loadAllLiefergebiete = function() {
52682		$http.get('?cl=api&mt=getLiefergebiete', {})
52683			.then(function(data) {
52684				$rootScope.currentLiefergebieteSwitch = data.data;
52685				for(var lgs in $rootScope.currentLiefergebieteSwitch) {
52686					$rootScope.config['liefergebiete-plzs'].push($rootScope.currentLiefergebieteSwitch[lgs].postleitzahl);
52687				}
52688
52689				if($rootScope.queryLiefergebiet !== null) {
52690					$rootScope.setLiefergebietBySearch(0, true);
52691				}
52692
52693				if($rootScope.getParameterByName('liefergebiet-search')) {
52694					$rootScope.formData.liefergebieteSearch = $rootScope.getParameterByName('liefergebiet-search');
52695					var filteredLiefergebiete = $rootScope.getLiefergebieteResults();
52696					if(filteredLiefergebiete.length === 1) {
52697						$rootScope.setLiefergebietBySearch(0, true);
52698					}
52699				}
52700			});
52701	};
52702
52703	$rootScope.loadBoni = function() {
52704		if($rootScope.config['boni-is-loading'] == false && $rootScope.uhrzeitLocalstorageLoaded) {
52705			$rootScope.config['boni-is-loading'] = true;
52706			$http.get('?cl=api&mt=loadBoni'
52707				+ "&datum=" + $rootScope.currentDatum
52708				+ "&uhrzeit=" + $rootScope.currentUhrzeit
52709				+ "&referer=" + $rootScope.currentReferer
52710				+ "&art=" + $rootScope.currentType, {})
52711				.then(function(data) {
52712					$rootScope.boniWithOptions = data.data;
52713					if($rootScope.boniWithOptions == null || $rootScope.boniWithOptions == 'null') {
52714						$rootScope.currentBoniAuswahl = null;
52715						$rootScope.selectedBoni = null;
52716						$rootScope.boniWithOptions = {};
52717					}
52718
52719					$rootScope.config['boni-is-loading'] = false;
52720				});
52721		}
52722	};
52723
52724	$rootScope.getBonusById = function(id) {
52725		for(var b in $rootScope.boniWithOptions.boni) {
52726			if($rootScope.boniWithOptions.boni[b].id == id) {
52727				return $rootScope.boniWithOptions.boni[b];
52728			}
52729		}
52730
52731		return null;
52732	};
52733
52734	$rootScope.checkForBoni = function() {
52735		$rootScope.currentBoniAuswahl = null;
52736
52737		if($rootScope.getObjectLength($rootScope.boniWithOptions) > 0) {
52738			for(var i in $rootScope.boniWithOptions.boni) {
52739				var currentBetrag = $rootScope.getBonusBetragGesamtGerichte($rootScope.boniWithOptions.boni[i].katsObj);
52740				if(
52741					currentBetrag >= Number($rootScope.boniWithOptions.boni[i].betrag)
52742					&& (
52743						$rootScope.currentBoniAuswahl == null
52744						|| Number($rootScope.currentBoniAuswahl.betrag) < Number($rootScope.boniWithOptions.boni[i].betrag)
52745					)
52746				) {
52747					$rootScope.currentBoniAuswahl = $rootScope.boniWithOptions.boni[i];
52748				}
52749			}
52750		}
52751
52752		if($rootScope.currentBoniAuswahl == null) {
52753			$rootScope.selectedBoni = null;
52754		}
52755
52756		if(($rootScope.selectedBoni == null || $rootScope.selectedBoni.id != $rootScope.currentBoniAuswahl.id) && $rootScope.currentBoniAuswahl !== null) {
52757			if($rootScope.getLocalObject("optionsBoni" + $rootScope.currentFilialId) != null && $rootScope.config['boni-geladen'] === false) {
52758				var savedBonus = $rootScope.getLocalObject("optionsBoni" + $rootScope.currentFilialId);
52759				$rootScope.selectedBoni = $rootScope.getBonusById(savedBonus.id);
52760				if($rootScope.selectedBoni !== null) {
52761					var minBetrag = $rootScope.getBonusBetragGesamtGerichte($rootScope.selectedBoni.katsObj);
52762					if(minBetrag < $rootScope.selectedBoni.betrag) {
52763						$rootScope.selectedBoni = null;
52764					} else {
52765						$rootScope.selectedBoni.zutatenAuswahl = savedBonus.zutaten;
52766						$rootScope.selectedBoni.removed = savedBonus.removed;
52767					}
52768				}
52769			} else {
52770				$rootScope.setSelectedBoni();
52771				$rootScope.openBoniModal();
52772			}
52773
52774			$rootScope.config['boni-geladen'] = true;
52775		}
52776	};
52777
52778	$rootScope.setSelectedBoni = function() {
52779		$rootScope.selectedBoni = $rootScope.clone($rootScope.currentBoniAuswahl);
52780		$rootScope.selectedBoni.removed = false;
52781		for(var bzaIndex in $rootScope.selectedBoni.zutatenObj.zutaten) {
52782			if($rootScope.isBoniXor($rootScope.selectedBoni.zutatenObj.zutaten[bzaIndex])) {
52783				$rootScope.setSelectedBoniZutatenXor(bzaIndex, 0);
52784			}
52785		}
52786	};
52787
52788	$rootScope.reRemoveBonus = function() {
52789		$rootScope.selectedBoni.removed = false;
52790	};
52791
52792	$rootScope.removeBonus = function() {
52793		$rootScope.selectedBoni.removed = true;
52794	};
52795
52796	$rootScope.setSelectedBoniZutatenXor = function(bzaIndex, bzIndex) {
52797		if(typeof $rootScope.selectedBoni.zutatenAuswahl == "undefined") {
52798			$rootScope.selectedBoni.zutatenAuswahl = {};
52799		}
52800
52801		$rootScope.selectedBoni.zutatenAuswahl[bzaIndex] = bzIndex;
52802	};
52803
52804	$rootScope.openBoniModal = function() {
52805		$rootScope.openModal($('.bonusModal'));
52806	};
52807
52808	$rootScope.closeBonusModal = function(event, removeBonus) {
52809		if(typeof event == "undefined" || ($(event.target).is('.bonusModal') && $(event.target).parents('.cfjs-modal').length == 0)) {
52810			if(typeof removeBonus != "undefined" && removeBonus == true) {
52811				$rootScope.removeBonus();
52812			}
52813			$rootScope.closeModal($('.bonusModal'));
52814		}
52815	};
52816
52817	$rootScope.switchToFiliale = function(filiale) {
52818        $rootScope.filialAuswahlOffen = false;
52819		if($rootScope.currentFilialId != filiale.id && $rootScope.shopSettings['showLiefergebietSearch'] == 0) {
52820			$rootScope.currentLiefergebiet = {
52821				liefergebuehr: 0,
52822				mindestbestellwert: 0
52823			};
52824			location.href = filiale.erreichbareDomain + "?cl=index&isSwitch=true";
52825		} else if($rootScope.currentLiefergebiet.filialId == $rootScope.currentFilialId && $rootScope.shopSettings["showLiefergebietSearch"] != 2 || $rootScope.shopSettings["showLiefergebietSearch"] == 0) {
52826			$rootScope.setLocalObject("filialAuswahl" + $rootScope.currentFilialId, 'true');
52827			$rootScope.closeFilialauswahlModal();
52828		} else if($rootScope.shopSettings["showLiefergebietSearch"] == 2) {
52829            $rootScope.setLocalObject("filialAuswahl" + $rootScope.currentFilialId, 'true');
52830            $http.get('?cl=api&mt=setFilialOrdnerCookie&cookieName=' + filiale.ordner).then(function(data) {
52831                // $('#liefer_only_click').click();
52832                $location.url('/cl/index/shop');
52833                $rootScope.refreshParams(true);
52834    			$rootScope.closeFilialauswahlModal();
52835                $rootScope.openLieferOptionenModal();
52836            });
52837        } else {
52838			var newLiefergebiet = $rootScope.clone($rootScope.currentLiefergebiet);
52839			$rootScope.currentLiefergebiet = {
52840				liefergebuehr: 0,
52841				mindestbestellwert: 0
52842			};
52843			if(
52844				typeof newLiefergebiet.postleitzahl != "undefined"
52845				&& newLiefergebiet.ort != "undefined"
52846			) {
52847				var url = filiale.erreichbareDomain + "?cl=index&isSwitch=true&plz=" + newLiefergebiet.postleitzahl + "&ort=" + newLiefergebiet.ort;
52848				// location.href = filiale.erreichbareDomain + "?cl=index&isSwitch=true&plz=" + newLiefergebiet.postleitzahl + "&ort=" + newLiefergebiet.ort;
52849				location.replace(filiale.erreichbareDomain + "?cl=index&isSwitch=true&plz=" + newLiefergebiet.postleitzahl + "&ort=" + newLiefergebiet.ort);
52850			} else {
52851				var url = filiale.erreichbareDomain + "?cl=index&isSwitch=true";
52852				location.href = filiale.erreichbareDomain + "?cl=index&isSwitch=true";
52853				location.replace(filiale.erreichbareDomain + "?cl=index&isSwitch=true");
52854			}
52855		}
52856	};
52857
52858	$rootScope.setLocalObject = function(key, value, long) {
52859		if(typeof value == "object") {
52860			value = JSON.stringify(value);
52861		}
52862
52863		// localStorage.setItem(key, value);
52864		$rootScope.setCookie(key, value, long ? 365 * 24 * 60 * 60 * 1000 : undefined);
52865	};
52866
52867	$rootScope.getLocalObject = function(key) {
52868		var value = null;
52869		try {
52870			// value = JSON.parse(localStorage.getItem(key));
52871			value = JSON.parse($rootScope.getCookie(key));
52872		} catch(e) {
52873			// value = localStorage.getItem(key);
52874			value = $rootScope.getCookie(key);
52875		}
52876		return value;
52877	};
52878
52879	// $rootScope.setCookie = function(cname, cvalue) {
52880	// 	var d = new Date();
52881	//     d.setTime(d.getTime() + (24*60*60*1000));
52882	//     var expires = "expires="+d.toUTCString();
52883	//     cvalue = encodeURIComponent(cvalue);
52884	//     document.cookie = cname + "=" + cvalue + "; " + expires;
52885	// };
52886
52887	$rootScope.getCookie = function(cname) {
52888		var name = cname + "=";
52889	    var ca = document.cookie.split(';');
52890	    for(var i=0; i<ca.length; i++) {
52891	        var c = ca[i];
52892	        while (c.charAt(0)==' ') {
52893	        	c = c.substring(1);
52894	        }
52895	        if (c.indexOf(name) == 0) {
52896	        	return decodeURIComponent(c.substring(name.length,c.length));
52897	        }
52898	    }
52899	    return null;
52900	};
52901
52902	$rootScope.deleteCookie = function(cname) {
52903		var cvalue = '';
52904		var d = new Date();
52905	    d.setTime(d.getTime() - 1);
52906	    var expires = "expires="+d.toUTCString();
52907	    cvalue = encodeURIComponent(cvalue);
52908	    document.cookie = cname + "=" + cvalue + "; " + expires;
52909	};
52910
52911	$rootScope.getParameterByName = function(name, url) {
52912		if (!url) url = window.location.href;
52913		name = name.replace(/[\[\]]/g, "\\$&");
52914		var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
52915		    results = regex.exec(url);
52916		if (!results) return null;
52917		if (!results[2]) return '';
52918		return decodeURIComponent(results[2].replace(/\+/g, " "));
52919	};
52920
52921	$rootScope.goToUrl = function(url) {
52922		location.href = url;
52923	};
52924
52925	$rootScope.setOwlCarousel = function() {
52926		// $timeout(function() {
52927		// 	for(var kId in $rootScope.gerichte) {
52928		// 		if($('#carousel' + kId).length > 0) {
52929		// 			var items = $($('#carousel' + kId)).data('items');
52930		// 			var carousel = $('#carousel' + kId);
52931		// 			var owlConfig = {
52932		// 	            items : 6,
52933		// 	            slideSpeed: 200,
52934		// 	            autoPlay: true
52935		// 	        };
52936		// 	        var owl = $('#carousel' + kId).data('owlCarousel');
52937
52938		// 			if($rootScope.getObjectLength($rootScope.gerichte[kId]) < items) {
52939		// 				items = $rootScope.getObjectLength($rootScope.gerichte[kId]);
52940		// 			}
52941
52942		// 			if(typeof owl != "undefined") {
52943		// 		        owl.destroy();
52944		// 			}
52945
52946		// 		    $('#carousel' + kId).owlCarousel(owlConfig);
52947
52948		// 	        var maxHeight = 0;
52949		// 	        var children = $(carousel).find('.owl-item');
52950		// 	        children.each(function() {
52951		// 	        	$(this).css('height', '');
52952		// 	        	if($(this).height() > maxHeight) {
52953		// 	        		maxHeight = $(this).height();
52954		// 	        	}
52955		// 	        });
52956		// 	        children.each(function() {
52957		// 	        	$(this).height(maxHeight + 45);
52958		// 	        });
52959
52960		// 	        $(window).resize(function() {
52961		// 	        	var maxHeight = 0;
52962		// 		        var children = $(carousel).find('.owl-item');
52963		// 		        children.each(function() {
52964		// 	        		$(this).css('height', '');
52965		// 		        	if($(this).height() > maxHeight) {
52966		// 		        		maxHeight = $(this).height();
52967		// 		        	}
52968		// 		        });
52969		// 		        children.each(function() {
52970		// 		        	$(this).height(maxHeight + 45);
52971		// 		        });
52972		// 	        });
52973		// 		}
52974		// 	}
52975		// });
52976	};
52977
52978	$rootScope.matchLiefergebieteSearch = function(plz, ort) {
52979		if(typeof $rootScope.formData.liefergebieteSearchModul == "undefined" || $rootScope.formData.liefergebieteSearchModul.length == 0) {
52980			return true;
52981		} else {
52982			var regex = new RegExp('(.*)(' + $rootScope.formData.liefergebieteSearchModul + ')(.*)', 'igm');
52983			if(String(plz).match(regex) || String(ort).match(regex)) {
52984				return true;
52985			}
52986		}
52987
52988		return false;
52989	};
52990
52991	$rootScope.addLiefergebietToConfig = function(plz, ort, liefergebuehr, mindestbestellwert) {
52992		$rootScope.config['alle-liefergebiete'].push({
52993			postleitzahl: plz,
52994			ort: ort,
52995			liefergebuehr: liefergebuehr,
52996			mindestbestellwert: mindestbestellwert
52997		});
52998	};
52999
53000
53001	$rootScope.$watch('liefergebieteSearch', function() {
53002		//$rootScope.getLiefergebieteSearch();
53003	});
53004
53005	$rootScope.liefergebieteResults = [];
53006	$rootScope.getLiefergebieteSearch = function() {
53007		$rootScope.liefergebieteResults = [];
53008		if(typeof $rootScope.formData.liefergebieteSearch != "undefined" && $rootScope.formData.liefergebieteSearch.length > 0) {
53009			var regex = new RegExp('(.*)(' + $rootScope.formData.liefergebieteSearch + ')(.*)', 'igm');
53010			for(var lg in $rootScope.config['alle-liefergebiete']) {
53011				if(String($rootScope.config['alle-liefergebiete'][lg].postleitzahl).match(regex) || String($rootScope.config['alle-liefergebiete'][lg].ort).match(regex)) {
53012					$rootScope.liefergebieteResults.push($rootScope.config['alle-liefergebiete'][lg]);
53013				}
53014			}
53015		}
53016
53017		if($rootScope.config['liefergebiet-search-index'] > ($rootScope.getObjectLength($rootScope.liefergebieteResults) - 1)) {
53018			$rootScope.config['liefergebiet-search-index'] = 0;
53019		}
53020
53021		//return liefergebiete;
53022	};
53023
53024	$rootScope.nextLiefergebietSearch = function(event, nextSite) {
53025		// $timeout(function() {
53026			if(event.keyCode == 40) {
53027				if($rootScope.config['liefergebiet-search-index'] === false) {
53028					$rootScope.config['liefergebiet-search-index'] = 0;
53029				} else {
53030					$rootScope.config['liefergebiet-search-index']++;
53031				}
53032
53033				if($rootScope.config['liefergebiet-search-index'] > ($rootScope.getObjectLength($rootScope.liefergebieteResults) - 1)) {
53034					$rootScope.config['liefergebiet-search-index'] = 0;
53035				}
53036			} else if(event.keyCode == 38) {
53037				if($rootScope.config['liefergebiet-search-index'] === false) {
53038					$rootScope.config['liefergebiet-search-index'] = $rootScope.getObjectLength($rootScope.liefergebieteResults) - 1;
53039				} else {
53040					$rootScope.config['liefergebiet-search-index']--;
53041				}
53042
53043				if($rootScope.config['liefergebiet-search-index'] < 0) {
53044					$rootScope.config['liefergebiet-search-index'] = $rootScope.getObjectLength($rootScope.liefergebieteResults) - 1;
53045				}
53046			} else if(event.keyCode == 13) {
53047				$rootScope.liefergebietSearchSet($rootScope.config['liefergebiet-search-index'], nextSite);
53048			}
53049
53050			if(event.keyCode == 38 || event.keyCode == 40) {
53051				var resultsHeight = $('.liefergebiete-search-results').height();
53052				var resultsScroll = $('.liefergebiete-search-results').scrollTop();
53053				var resultHeight = $('.liefergebiete-search-results .result').first().outerHeight();
53054				var resultOffset = $rootScope.config['liefergebiet-search-index'] * resultHeight;
53055				var resultsScrollHeight = resultsScroll + resultsHeight;
53056				var resultOffsetHeight = resultOffset + resultHeight;
53057
53058				if(
53059					resultOffset < resultsScroll || resultOffset > resultsScrollHeight
53060					|| resultOffsetHeight < resultsScroll || resultOffsetHeight > resultsScrollHeight
53061				) {
53062					if(event.keyCode == 40) {
53063						$('.liefergebiete-search-results').animate({
53064							scrollTop: (resultOffset + resultHeight - resultsHeight)
53065						}, {
53066							duration: 300,
53067							queue: false
53068						});
53069					} else if(event.keyCode == 38) {
53070						$('.liefergebiete-search-results').animate({
53071							scrollTop: resultOffset
53072						}, {
53073							duration: 300,
53074							queue: false
53075						});
53076					}
53077				}
53078			}
53079		// });
53080	};
53081
53082	$rootScope.liefergebietSearchSet = function(index, nextSite) {
53083		var lg = $rootScope.liefergebieteResults;
53084		// var obj = {
53085		// 	postleitzahl: lg[$rootScope.config['liefergebiet-search-index']].plz,
53086		// 	ort: lg[$rootScope.config['liefergebiet-search-index']].ort
53087		// };
53088
53089		$rootScope.setLiefergebiet(lg[index]);
53090		// $rootScope.liefergebieteSearch = '';
53091		// $rootScope.config['liefergebiet-search-index'] = 0;
53092
53093		//$rootScope.renderLiefergebietRadio();
53094
53095		$rootScope.goToUrl(nextSite);
53096	};
53097
53098	$rootScope.renderLiefergebietRadio = function() {
53099		var plz = $rootScope.currentLiefergebiet.postleitzahl;
53100		var ort = $rootScope.currentLiefergebiet.ort;
53101		$('input.liefergebiet-auswahl[data-plz="' + plz + '"][data-ort="' + ort + '"]').iCheck('check');
53102	};
53103
53104	$rootScope.setLiefergebietBySearch = function(index, isFilialSwitch) {
53105		$rootScope.formData.liefergebieteSearch = decodeURIComponent($rootScope.formData.liefergebieteSearch);
53106		var filteredLiefergebiete = $rootScope.getLiefergebieteResults();
53107
53108		$rootScope.currentLiefergebiet = filteredLiefergebiete[index];
53109		$rootScope.currentType = "liefer";
53110		// $timeout(function() {
53111			$rootScope.liefergebieteSearchSetted = true;
53112			$rootScope.formData.liefergebieteSearch = filteredLiefergebiete[index].anzeige;
53113			$rootScope.kundenDaten.plz = filteredLiefergebiete[index].postleitzahl;
53114			$rootScope.kundenDaten.ort = filteredLiefergebiete[index].ort;
53115			$rootScope.setCookieKundenDaten();
53116		// });
53117
53118		$rootScope.closeLieferOptionenModal();
53119
53120		$rootScope.loadFilialen(function() {
53121			if($rootScope.filialSwitch.length == 0) {
53122				swal('Fehler');
53123			} else {
53124				if($rootScope.filialId != $rootScope.filialSwitch[0].id) {
53125					$rootScope.switchToFiliale($rootScope.filialSwitch[0]);
53126				}
53127			}
53128		}, true);
53129	};
53130
53131	$rootScope.setPlzAndOrtAddLiefergebiet = function(gebiet) {
53132		var plzInput = $('#addLieferadresseForm input[name="plz"]');
53133		var ortInput = $('#addLieferadresseForm input[name="ort"]');
53134
53135		plzInput.val(gebiet.postleitzahl);
53136		ortInput.val(gebiet.ort);
53137
53138		$rootScope.addLieferadresseSetted = true;
53139	};
53140
53141	$rootScope.selectLiefergebiet = function(event, isFilialSwitch) {
53142		var keyCode = event.keyCode;
53143		var filteredLiefergebiete = $rootScope.getLiefergebieteResults();
53144
53145		if(keyCode == 40) {
53146			if(typeof $rootScope.selectedLiefergebiet == "undefined" || (filteredLiefergebiete.length - 2) < $rootScope.selectedLiefergebiet) {
53147				$rootScope.selectedLiefergebiet = 0;
53148			} else {
53149				$rootScope.selectedLiefergebiet++;
53150			}
53151		} else if(keyCode == 38) {
53152			if(typeof $rootScope.selectedLiefergebiet == "undefined" || $rootScope.selectedLiefergebiet < 0) {
53153				$rootScope.selectedLiefergebiet = filteredLiefergebiete.length - 1;
53154			} else {
53155				$rootScope.selectedLiefergebiet--;
53156			}
53157		}
53158
53159		if(keyCode == 40 || keyCode == 38) {
53160			var liefergebieteResults = $('.liefergebiete-results');
53161
53162			var scrollTop = $('.liefergebiete-result').first().outerHeight() * ($rootScope.selectedLiefergebiet + 1) - liefergebieteResults.height();
53163
53164			liefergebieteResults.animate({
53165				scrollTop: scrollTop
53166			}, {
53167				duration: 300,
53168				queue: false
53169			});
53170		} else if(keyCode == 13) {
53171			$rootScope.setLiefergebietBySearch($rootScope.selectedLiefergebiet, isFilialSwitch);
53172		} else {
53173			var liefergebieteResults = $('.liefergebiete-results');
53174			liefergebieteResults.animate({
53175				scrollTop: 0
53176			}, {
53177				duration: 0,
53178				queue: false
53179			});
53180
53181			// $timeout(function() {
53182				$rootScope.selectedLiefergebiet = 0;
53183			// });
53184		}
53185	};
53186
53187	$rootScope.getLiefergebieteResults = function() {
53188		return $filter('filter')($rootScope.currentLiefergebieteSwitch, {anzeige: $rootScope.formData.liefergebieteSearch});
53189	};
53190
53191	$rootScope.getLiefergebieteResultsAddLiefergebiet = function() {
53192		return $filter('filter')($rootScope.currentLiefergebieteSwitch, {anzeige: $rootScope.addLieferadressePlz});
53193	};
53194
53195	$rootScope.openModal = function(element) {
53196		// $('body').css('overflow', 'hidden');
53197		element.fadeIn();
53198		element.find('.cfjs-modal').show({
53199			duration: 500,
53200			easing: "easeOutBack",
53201			queue: false
53202		});
53203	};
53204
53205	$rootScope.closeModal = function(element) {
53206		element.fadeOut();
53207		element.find('.cfjs-modal').hide({
53208			duration: 500,
53209			easing: "easeOutBack",
53210			queue: false,
53211			complete: function() {
53212				$('body').css('overflow', 'auto');
53213			}
53214		});
53215	};
53216
53217	$rootScope.goToKasse = function() {
53218		// location.href = '?cl=index&mt=kasse';
53219		$location.url("/cl/index/kassev2");
53220	};
53221
53222	$rootScope.goToWarenkorb = function() {
53223		// location.href = '?cl=index&mt=warenkorb';
53224		$location.url("/cl/index/warenkorbv2");
53225	};
53226
53227	$rootScope.goToBackUrl = function() {
53228		window.history.back();
53229	};
53230
53231	$rootScope.goToShopReset = function() {
53232		$rootScope.currentStep = "bestaetigen";
53233		$location.url("/cl/index/shop");
53234	};
53235
53236	$rootScope.getImgUrl = function(kat, ordnername) {
53237		if(typeof kat == "undefined") {
53238			return '';
53239		}
53240		if(kat.bild != '') {
53241			// BUG fix, wenn der Bildname Leerzeichen enthält, ersetze diese durch _
53242			return "url('https://media.clickfood.de/" + ordnername + "/images/kategorie/" + (kat.bild).split(' ').join('_') + "')";
53243		} else {
53244			return "url('/img/Kategoriebilder/" + kat.image + "')";
53245		}
53246	};
53247
53248    $rootScope.getImg = function(kat, ordnername) {
53249        if(typeof kat == "undefined") {
53250			return '';
53251		}
53252		if(kat.bild != '') {
53253			// BUG fix, wenn der Bildname Leerzeichen enthält, ersetze diese durch _
53254			return "https://media.clickfood.de/" + ordnername + "/images/kategorie/" + (kat.bild).split(' ').join('_');
53255		} else {
53256			return "/img/Kategoriebilder/" + kat.image;
53257		}
53258    };
53259
53260    $rootScope.getKategorieBackgroundBild = function(kat, ordnername) {
53261        if(typeof kat == "undefined") {
53262            return "none";
53263        }
53264
53265        if(kat.backgroundBild.length > 0) {
53266			// BUG fix, wenn der Bildname Leerzeichen enthält, ersetze diese durch _
53267            return "url('https://media.clickfood.de/" + ordnername + "/images/kategorieBackground/" + (kat.backgroundBild).split(' ').join('_') + "')";
53268        } else {
53269            return "none";
53270        }
53271    };
53272
53273	$rootScope.getLiefergebietByPlz = function(plz) {
53274		for(var lg in $rootScope.currentLiefergebieteSwitch) {
53275			if($rootScope.currentLiefergebieteSwitch[lg].postleitzahl == plz) {
53276				return $rootScope.currentLiefergebieteSwitch[lg];
53277			}
53278		}
53279
53280		return -1;
53281	};
53282
53283	$rootScope.getLiefergebietByPlzAndOrt = function(plz, ort) {
53284		for(var lg in $rootScope.currentLiefergebieteSwitch) {
53285			if($rootScope.currentLiefergebieteSwitch[lg].postleitzahl == plz && $rootScope.currentLiefergebieteSwitch[lg].ort == ort) {
53286				return $rootScope.currentLiefergebieteSwitch[lg];
53287			}
53288		}
53289
53290		return -1;
53291	};
53292
53293	$rootScope.getLiefergebietByPlzUndOrt = function(plz, ort) {
53294		for(var lg in $rootScope.currentLiefergebieteSwitch) {
53295			if($rootScope.currentLiefergebieteSwitch[lg].postleitzahl == plz && $rootScope.currentLiefergebieteSwitch[lg].ort == ort) {
53296				return lg;
53297			}
53298		}
53299
53300		return -1;
53301	};
53302
53303	// $timeout(function() {
53304	// });
53305
53306	$rootScope.loadSettings = function() {
53307		if(!$rootScope.websiteLoaded) {
53308			$http.get('?cl=api&mt=settings', {})
53309				.then(function(data) {
53310					var response = data.data;
53311					var isSwitch = $rootScope.getParameterByName("isSwitch");
53312					var lgPlz = $rootScope.getParameterByName("plz");
53313					var lgOrt = $rootScope.getParameterByName("ort");
53314
53315					if(
53316						lgPlz !== null && lgPlz != '' && lgPlz != "undefined"
53317						&& lgOrt !== null && lgOrt != '' && lgOrt != "undefined"
53318					) {
53319						$rootScope.queryLiefergebiet = {
53320							plz: lgPlz,
53321							ort: lgOrt
53322						};
53323
53324						$rootScope.formData.liefergebieteSearch = lgPlz + ' - ' + lgOrt;
53325					}
53326
53327					$rootScope.init(
53328						response.filialId,
53329						response.type,
53330						response.shopSettings,
53331						response.referer,
53332						{},
53333						response.benutzerTreuepunkte,
53334						response.hatShop,
53335						isSwitch == 'true',
53336						response.siteReferer
53337					);
53338				});
53339		}
53340	};
53341
53342	$rootScope.setAbholung = function() {
53343		// $timeout(function() {
53344			$rootScope.currentType = 'abhol';
53345			$rootScope.closeLieferOptionenModal();
53346		// });
53347	};
53348
53349	$rootScope.sendNewsletterAnmeldung = function(event) {
53350		if(typeof event == "undefined" || event.keyCode == 13) {
53351			// $timeout(function() {
53352				$rootScope.newsletterLoading = true;
53353				$http.post('?cl=api&mt=registerNewsletter', {
53354					email: $rootScope.newsletterEmail
53355				}).then(function(data) {
53356					// $timeout(function() {
53357						$rootScope.newsletterLoading = false;
53358						if(data.data.success) {
53359							swal("Angemeldet", "Du hast dich erfolgreich für den Newsletter angemeldet", "success");
53360						} else {
53361							swal("Fehler", data.data.message, "error");
53362						}
53363					// });
53364				});
53365			// });
53366		}
53367	};
53368
53369	$rootScope.doWebBenutzerLogout = function() {
53370		$http.get('?cl=api&mt=doLogout', {})
53371			.then(function(data) {
53372				swal({
53373					title: "Erfolgreich abgemeldet",
53374					text: "Du hast dich erfolgreich abgemeldet!",
53375					type: "success",
53376					confirmButtonColor: "green",
53377					confirmButtonText: "Weiter",
53378				  	timer: 2000
53379				}).then(function(){
53380				  	location.reload();
53381				});
53382			});
53383	};
53384
53385	$rootScope.deleteAccount = async function() {
53386		const confirmDelete = await swal({
53387			title: 'Nutzer-Konto wirklich löschen?',
53388			text: 'Möchtest du wirklich dein Konto bei uns löschen?',
53389			type: 'warning',
53390			confirmButtonColor: 'red',
53391			confirmButtonText: 'Ja, wirklich löschen!',
53392			cancelButtonText: 'Nein!',
53393			showCancelButton: true
53394		});
53395
53396		if(confirmDelete) {
53397			$rootScope.loading = true;
53398			try {
53399				const data = await $http.post('?cl=api&mt=deleteAccountV2');
53400				if(data.data.success) {
53401					swal({
53402						title: 'Nutzer-Konto gelöscht',
53403						text: 'Dein Nutzer-Konto bei uns wurde gelöscht.',
53404						timer: 10000
53405					});
53406					location.reload();
53407				} else {
53408					swal({
53409						title: 'Nutzer-Konto konnte nicht gelöscht werden',
53410						text: 'Dein Nutzer-Konto konnte nicht gelöscht werden. Vielleicht bist du nicht mehr angemeldet. Versuche es erneut.',
53411						type: "error",
53412						confirmButtonColor: "red",
53413						confirmButtonText: "Erneut versuchen",
53414						timer: 10000
53415					});
53416				}
53417			} catch {
53418				swal({
53419					title: 'Nutzer-Konto konnte nicht gelöscht werden',
53420					text: 'Dein Nutzer-Konto konnte nicht gelöscht werden. Vielleicht bist du nicht mehr angemeldet. Versuche es erneut.',
53421					type: "error",
53422					confirmButtonColor: "red",
53423					confirmButtonText: "Erneut versuchen",
53424					timer: 10000
53425				});
53426			} finally {
53427				$rootScope.loading = true;
53428			}
53429		}
53430	};
53431
53432	$rootScope.doWebBenutzerDeleteAccount = function() {
53433		if($('#deleteAccountForm').length > 0) {
53434			$('#deleteAccountForm').submit(function(e) {
53435				e.preventDefault();
53436
53437				var inputValues = {};
53438
53439				var inputs = $(this).find('input,select,textarea');
53440				inputs.each(function() {
53441					if(typeof $(this).attr('name') != "undefined" && $(this).attr('name') != '') {
53442						if($(this).attr('type') == 'radio') {
53443							inputValues[$(this).attr('name')] = $("[name='" + $(this).attr('name') + "']:checked").val();
53444						} else {
53445							inputValues[$(this).attr('name')] = $(this).val();
53446						}
53447					}
53448				});
53449
53450				$http.post("?cl=api&mt=deleteAccount", inputValues)
53451					.then(function(data) {
53452						if(data.data.success) {
53453							swal({
53454							  title: "E-Mail versendet",
53455							  text: "Es wurde eine E-Mail an die von dir angegebene E-Mail Adresse gesendet. Bitte überprüfe dein Postfach.",
53456							  type: "success",
53457							  confirmButtonColor: "green",
53458							  confirmButtonText: "Weiter",
53459							  allowOutsideClick: false,
53460							  timer: false
53461							}).then((result) => {
53462								/* Read more about isConfirmed, isDenied below */
53463								console.log(result);
53464								if (result) {
53465									window.location.href = "#/";
53466								}
53467							});
53468						} else {
53469							swal({
53470							  title: "E-Mail Adresse fehlerhaft",
53471							  text: "Du hast eine falsche oder eine nicht vorhandene E-Mail Adresse angegeben.",
53472							  type: "error",
53473							  confirmButtonColor: "red",
53474							  confirmButtonText: "Erneut versuchen",
53475							  timer: 10000
53476							});
53477						}
53478					});
53479
53480				return false;
53481			});
53482		}
53483	};
53484
53485	$rootScope.setLieferadresseAnrede = function(anrede) {
53486		// $timeout(function() {
53487			$rootScope.lieferadresseAnrede = anrede;
53488		// });
53489	};
53490
53491	$rootScope.setLieferadresseStandard = function(laId) {
53492		$http.get('?cl=api&mt=setLieferadresseStandard&id=' + laId)
53493			.then(function(data) {
53494				if(data.data.success) {
53495					$route.reload();
53496					swal({
53497						title: "Erfolgreich als Standard",
53498						text: "Deine ausgewählte Lieferadresse wurde als Standard-Lieferadresse markiert.",
53499						type: "success",
53500						confirmButtonColor: "green",
53501						confirmButtonText: "Weiter",
53502					  	timer: 2000
53503					});
53504				} else {
53505					swal({
53506						title: "Fehler",
53507						text: "Es konnte keine Standard-Lieferadrese markiert werden! Bitte versuche es erneut.",
53508						type: "error",
53509						confirmButtonColor: "red",
53510						confirmButtonText: "Weiter",
53511					  	timer: 10000
53512					});
53513				}
53514			});
53515	};
53516
53517	$rootScope.removeLieferadresse = function(laId) {
53518		$http.get('?cl=api&mt=removeLieferadresse&id=' + laId)
53519			.then(function(data) {
53520				if(data.data.success) {
53521					$route.reload();
53522					swal({
53523						title: "Erfolgreich gelöscht",
53524						text: "Die Lieferadresse wurde erfolgreich von dir gelöscht!",
53525						type: "success",
53526						confirmButtonColor: "green",
53527						confirmButtonText: "Weiter",
53528					  	timer: 2000
53529					});
53530				} else {
53531					swal({
53532						title: "Fehler",
53533						text: "Die Lieferadresse konnte nicht gelöscht werden!",
53534						type: "error",
53535						confirmButtonColor: "red",
53536						confirmButtonText: "Weiter",
53537					  	timer: 10000
53538					});
53539				}
53540			});
53541	};
53542
53543	$rootScope.loadLieferadressen = function() {
53544		$http.get('?cl=api&mt=loadLieferadressen')
53545			.then(function(data) {
53546				$rootScope.userLieferadressen = data.data;
53547
53548				if(!$rootScope.config['lieferadresse-setted']) {
53549					for(var la in $rootScope.userLieferadressen) {
53550						if($rootScope.userLieferadressen[la].standard == 1) {
53551							// $timeout(function() {
53552								$rootScope.kundenDaten.anrede = $rootScope.userLieferadressen[la].anrede;
53553								$rootScope.kundenDaten.vorname = $rootScope.userLieferadressen[la].vorname;
53554								$rootScope.kundenDaten.unternehmen = $rootScope.userLieferadressen[la].unternehmen;
53555								$rootScope.kundenDaten.nachname = $rootScope.userLieferadressen[la].nachname;
53556								$rootScope.kundenDaten.strasse = $rootScope.userLieferadressen[la].strasse;
53557								$rootScope.kundenDaten.hausnummer = $rootScope.userLieferadressen[la].hausnr;
53558								if($rootScope.currentType == "abhol") {
53559									$rootScope.kundenDaten.ort = $rootScope.userLieferadressen[la].ort;
53560									$rootScope.kundenDaten.plz = $rootScope.userLieferadressen[la].plz;
53561								}
53562								$rootScope.kundenDaten.telefon = $rootScope.userLieferadressen[la].telefon;
53563								$rootScope.kundenDaten.email = $rootScope.userLieferadressen[la].email;
53564								$rootScope.kundenDaten.bemerkung = $rootScope.userLieferadressen[la].bemerkung;
53565							// });
53566							break;
53567						}
53568					}
53569				}
53570			});
53571	};
53572
53573	// $(document).ready(function() {
53574	// 	$(window).click(function(event) {
53575	// 		var target = $(event.target);
53576	// 		if(!target.is('.liefergebiete-search')) {
53577	// 			var parentFound = false;
53578	// 			var parents = target.parents();
53579
53580	// 			parents.each(function() {
53581	// 				if($(this).is('.liefergebiete-search')) {
53582	// 					parentFound = true;
53583	// 				}
53584	// 			});
53585
53586	// 			if(!parentFound) {
53587	// 				$timeout(function() {
53588	// 					$rootScope.liefergebieteSearch = '';
53589	// 				});
53590	// 			}
53591	// 		}
53592	// 	});
53593	// });
53594
53595	/**
53596	RootScope
53597	**/
53598	$rootScope.setCookie = function(cname, cvalue, cexpires) {
53599		var d = new Date();
53600		if(typeof cexpires != "undefined") {
53601		    d.setTime(d.getTime() + cexpires);
53602		} else {
53603	    	d.setTime(d.getTime() + (24*60*60*1000));
53604	    }
53605	    var expires = "expires="+d.toUTCString();
53606	    cvalue = encodeURIComponent(cvalue);
53607	    document.cookie = cname + "=" + cvalue + "; " + expires;
53608	};
53609
53610	$rootScope.getCookie = function(cname) {
53611		var name = cname + "=";
53612	    var ca = document.cookie.split(';');
53613	    for(var i=0; i<ca.length; i++) {
53614	        var c = ca[i];
53615	        while (c.charAt(0)==' ') {
53616	        	c = c.substring(1);
53617	        }
53618	        if (c.indexOf(name) == 0) {
53619	        	return decodeURIComponent(c.substring(name.length,c.length));
53620	        }
53621	    }
53622	    return null;
53623	};
53624
53625	$rootScope.checkCookieInfo = function() {
53626		var cookieCookieInfo = $rootScope.getCookie('cookie-info-checked');
53627		if(cookieCookieInfo !== null && cookieCookieInfo == "true") {
53628			$rootScope.removeCookieInfo();
53629		} else {
53630			$('#cookie').modal('show');
53631		}
53632	};
53633
53634	$rootScope.checkCookieApp = function() {
53635		var cookieCookieApp = $rootScope.getCookie('cookie-app-checked');
53636		if(cookieCookieApp !== null && cookieCookieApp == "true") {
53637			$rootScope.removeAppInfo(false);
53638		}
53639	};
53640
53641	$rootScope.removeCookieInfo = function() {
53642		$('#cookie').modal('hide');
53643		$('.cookie-info').remove();
53644		$('.CookieInfo').remove();
53645		$rootScope.setCookie("cookie-info-checked", "true", 30*24*60*60*1000);
53646	};
53647
53648	$rootScope.removeAppInfo = function(cookieErneuern) {
53649		$('.MobileDetect').remove();
53650		if(typeof cookieErneuern != "undefined" && cookieErneuern == true) {
53651			$rootScope.setCookie("cookie-app-checked", "true", 30*24*60*60*1000);
53652		}
53653	};
53654
53655	$rootScope.parseQuery = function(qstr) {
53656        var query = {};
53657        var a = qstr.substr(1).split('&');
53658        for (var i = 0; i < a.length; i++) {
53659            var b = a[i].split('=');
53660            query[decodeURIComponent(b[0])] = decodeURIComponent(b[1] || '');
53661        }
53662        return query;
53663    };
53664
53665	$rootScope.changeAnchorHref = function() {
53666		var anchorsWithoutBlank = $('a:not([target="_blank"]):not([target="blank"])');
53667		anchorsWithoutBlank.each(function() {
53668			var href = $(this).attr('href');
53669			if(
53670				typeof href != "undefined"
53671				&& href.search('#') != 0
53672				&& href.search('javascript:;') == -1
53673				&& href.search('tel:') != 0
53674				&& href.search('mailto:') != 0
53675				&& href.search('http:') != 0
53676				&& href.search('https:') != 0
53677			) {
53678				var url = $rootScope.changeUrlToRoute(href);
53679				$(this).attr('href', url);
53680			}
53681		});
53682	};
53683
53684	$rootScope.changeUrlToRoute = function(url) {
53685		var query = $rootScope.parseQuery(url);
53686		var cl = query.cl;
53687		var mt = query.mt;
53688		if(typeof cl == "undefined") {
53689			cl = "index";
53690		} else {
53691			delete query.cl;
53692		}
53693
53694		if(typeof mt == "undefined") {
53695			mt = "index";
53696		} else {
53697			delete query.mt;
53698		}
53699
53700		var queryParameter = [];
53701		for(var qp in query) {
53702			queryParameter.push(query[qp]);
53703		}
53704
53705
53706		return "#/cl/" + cl + "/" + mt + "/" + queryParameter.join("/");
53707	};
53708
53709	$rootScope.animateModalToWarenkorb = function() {
53710
53711	};
53712
53713	// $rootScope.changeFormSubmit = function() {
53714	// 	var forms = $('form');
53715	// 	forms.submit(function(event) {
53716	// 		event.preventDefault();
53717
53718	// 		var inputValues = {};
53719
53720	// 		var method = $(this).attr('method');
53721	// 		if(typeof method == "undefined") {
53722	// 			method = "get";
53723	// 		}
53724	// 		var inputs = $(this).find('input,select,textarea');
53725	// 		inputs.each(function() {
53726	// 			if(typeof $(this).attr('name') != "undefined" && $(this).attr('name') != '') {
53727	// 				inputValues[$(this).attr('name')] = $(this).val();
53728	// 			}
53729	// 		});
53730
53731	// 		inputValues.ajax = "true";
53732
53733	// 		// var url = $rootScope.changeUrlToRoute($(this).attr('action'));
53734
53735	// 		if(method.toLowerCase() == "post") {
53736	// 			$http.post($(this).attr('action'), inu)
53737	// 		}
53738
53739	// 		return false;
53740	// 	});
53741	// };
53742
53743	$rootScope.checkForLoginForm = function() {
53744		if($('#nutzerloginform').length > 0) {
53745			$('#nutzerloginform').submit(function(e) {
53746				e.preventDefault();
53747
53748				var inputValues = {};
53749
53750				var inputs = $(this).find('input,select,textarea');
53751				inputs.each(function() {
53752					if(typeof $(this).attr('name') != "undefined" && $(this).attr('name') != '') {
53753						if($(this).attr('type') == 'checkbox') {
53754							if($(this).is(':checked')) {
53755								inputValues[$(this).attr('name')] = 'on';
53756							}
53757						} else {
53758							inputValues[$(this).attr('name')] = $(this).val();
53759						}
53760					}
53761				});
53762
53763				$http.post("?cl=api&mt=doLogin", inputValues)
53764					.then(function(data) {
53765						if(data.data.success) {
53766							swal({
53767								title: "Erfolgreich eingeloggt",
53768								text: "Du hast dich erfolgreich eingeloggt und kannst nun auf dein Nutzerkonto zugreifen!",
53769								type: "success",
53770								confirmButtonColor: "green",
53771								confirmButtonText: "Weiter",
53772								timer: 2000
53773							}).then(function(){
53774								location.reload();
53775							});
53776						} else {
53777							swal({
53778								title: "Fehlgeschlagen",
53779								text: "Du hast eine falsche Email-Adresse oder ein falsches Passwort eingegeben. Versuch es erneut.",
53780								type: "warning",
53781								confirmButtonColor: "red",
53782								confirmButtonText: "Ok"
53783							});
53784						}
53785					});
53786
53787				return false;
53788			});
53789		}
53790	};
53791
53792	$rootScope.checkForRegistrierenForm = function() {
53793		if($('#registrierenForm').length > 0) {
53794			$('#registrierenForm').submit(function(e) {
53795				e.preventDefault();
53796
53797				var inputValues = {};
53798
53799				var inputs = $(this).find('input,select,textarea');
53800				inputs.each(function() {
53801					if(typeof $(this).attr('name') != "undefined" && $(this).attr('name') != '') {
53802						if($(this).attr('type') == 'radio') {
53803							inputValues[$(this).attr('name')] = $("[name='" + $(this).attr('name') + "']:checked").val();
53804						} else {
53805							inputValues[$(this).attr('name')] = $(this).val();
53806						}
53807					}
53808				});
53809
53810				$http.post("?cl=api&mt=registrieren", inputValues)
53811					.then(function(data) {
53812						if(data.data.success) {
53813							$rootScope.formErrors.registrieren = {};
53814							$location.url("/profil");
53815							swal({
53816								title: "Erfolgreich registriert",
53817								text: "Du hast dir erfolgreich ein Nutzerkonto erstellt und kannst nun auf dieses zugreifen!",
53818								type: "success",
53819								confirmButtonColor: "green",
53820								confirmButtonText: "Weiter",
53821								timer: 5000
53822							}).then(function() {
53823								location.reload();
53824							});
53825						} else {
53826							$rootScope.formErrors.registrieren = data.data.errors;;
53827						}
53828					});
53829
53830				return false;
53831			});
53832		}
53833	};
53834
53835	$rootScope.checkForAddLieferadresseForm = function() {
53836		if($('#addLieferadresseForm').length > 0) {
53837			$('#addLieferadresseForm').submit(function(e) {
53838				e.preventDefault();
53839
53840				var inputValues = {};
53841
53842				var inputs = $(this).find('input,select,textarea');
53843				inputs.each(function() {
53844					if(typeof $(this).attr('name') != "undefined" && $(this).attr('name') != '') {
53845						if($(this).attr('type') == 'radio') {
53846							inputValues[$(this).attr('name')] = $("[name='" + $(this).attr('name') + "']:checked").val();
53847						} else {
53848							inputValues[$(this).attr('name')] = $(this).val();
53849						}
53850					}
53851				});
53852
53853				if(inputValues.vorname) {
53854					inputValues.vorname = inputValues.vorname.capitalizeFirstLetter();
53855				}
53856				if(inputValues.nachname) {
53857					inputValues.nachname = inputValues.nachname.capitalizeFirstLetter();
53858				}
53859				if(inputValues.unternehmen) {
53860					inputValues.unternehmen = inputValues.unternehmen.capitalizeFirstLetter();
53861				}
53862				if(inputValues.strasse) {
53863					inputValues.strasse = inputValues.strasse.capitalizeFirstLetter();
53864				}
53865				if(inputValues.ort) {
53866					inputValues.ort = inputValues.ort.capitalizeFirstLetter();
53867				}
53868
53869				$http.post("?cl=api&mt=addLieferadresse", inputValues)
53870					.then(function(data) {
53871						if(data.data.success) {
53872							$rootScope.formErrors.addLieferadresse = {};
53873							$rootScope.loadLieferadressen();
53874							$location.url("/profil/lieferadressen");
53875							swal({
53876							  title: "Erfolgreich angelegt",
53877							  text: "Deine Lieferadresse wurde erfolgreich hinzugefügt.",
53878							  type: "success",
53879							  confirmButtonColor: "green",
53880							  confirmButtonText: "Weiter",
53881							  timer: 2000
53882                            }).then(function(){
53883							  $location.url("/profil/lieferadressen");
53884							});
53885						} else {
53886							// if(typeof $rootScope.formErrors.addLieferadresse == "undefined") {
53887							// 	$rootScope.formErrors.addLieferadresse = {};
53888							// }
53889
53890							$rootScope.formErrors.addLieferadresse = data.data.errors;
53891						}
53892					});
53893
53894				return false;
53895			});
53896		}
53897	};
53898
53899	$rootScope.checkForPasswortResetForm = function() {
53900		if($('#passwortResetForm').length > 0) {
53901			$('#passwortResetForm').submit(function(e) {
53902				e.preventDefault();
53903
53904				var inputValues = {};
53905
53906				var inputs = $(this).find('input,select,textarea');
53907				inputs.each(function() {
53908					if(typeof $(this).attr('name') != "undefined" && $(this).attr('name') != '') {
53909						if($(this).attr('type') == 'radio') {
53910							inputValues[$(this).attr('name')] = $("[name='" + $(this).attr('name') + "']:checked").val();
53911						} else {
53912							inputValues[$(this).attr('name')] = $(this).val();
53913						}
53914					}
53915				});
53916
53917				$http.post("?cl=api&mt=passwortReset", inputValues)
53918					.then(function(data) {
53919						if(data.data.success) {
53920							swal({
53921							  title: "E-Mail versendet",
53922							  text: "Es wurde eine E-Mail an die von dir angegebene E-Mail Adresse gesendet. Bitte überprüfe dein Postfach.",
53923							  type: "success",
53924							  confirmButtonColor: "green",
53925							  confirmButtonText: "Weiter",
53926							  timer: 2000
53927							});
53928						} else {
53929							swal({
53930							  title: "E-Mail Adresse fehlerhaft",
53931							  text: "Du hast eine falsche oder eine nicht vorhandene E-Mail Adresse angegeben.",
53932							  type: "error",
53933							  confirmButtonColor: "red",
53934							  confirmButtonText: "Erneut versuchen",
53935							  timer: 10000
53936							});
53937						}
53938					});
53939
53940				return false;
53941			});
53942		}
53943	};
53944
53945	$rootScope.checkForResetTokenForm = function() {
53946		if($('#resetTokenForm').length > 0) {
53947			$('#resetTokenForm').submit(function(e) {
53948				e.preventDefault();
53949
53950				var inputValues = {};
53951
53952				var inputs = $(this).find('input,select,textarea');
53953				inputs.each(function() {
53954					if(typeof $(this).attr('name') != "undefined" && $(this).attr('name') != '') {
53955						if($(this).attr('type') == 'radio') {
53956							inputValues[$(this).attr('name')] = $("[name='" + $(this).attr('name') + "']:checked").val();
53957						} else {
53958							inputValues[$(this).attr('name')] = $(this).val();
53959						}
53960					}
53961				});
53962
53963				$http.post("?cl=api&mt=resetToken", inputValues)
53964					.then(function(data) {
53965						if(data.data.success) {
53966							$location.url('/profil/lieferadressen');
53967							swal({
53968							  title: "Passwort geändert",
53969							  text: "Du hast dein Passwort erfolgreich geändert.",
53970							  type: "success",
53971							  confirmButtonColor: "green",
53972							  confirmButtonText: "Einloggen",
53973							  timer: 10000
53974							});
53975						} else {
53976							if(data.data.message == 'identical') {
53977								var title = 'Passwörter stimmen nicht überein';
53978								var text = 'Du hast zwei unterschiedliche Passwörter eingegeben.';
53979							} else if(data.data.message == 'empty') {
53980								var title = 'Leeres Passwort';
53981								var text = 'Du darfst kein leeres Passwort eingeben.';
53982							} else {
53983								var title = 'Unbekannter Fehler';
53984								var text = 'Es ist ein unbekannter Fehler aufgetreten. Bitte versuche es erneut.';
53985							}
53986							swal({
53987							  title: title,
53988							  text: text,
53989							  type: "error",
53990							  confirmButtonColor: "red",
53991							  confirmButtonText: "Erneut versuchen",
53992							  timer: 10000
53993							});
53994						}
53995					});
53996
53997				return false;
53998			});
53999		}
54000	};
54001
54002	$rootScope.checkForKontaktForm = function() {
54003		if($('#kontaktForm').length > 0) {
54004			$('#kontaktForm').submit(function(e) {
54005				e.preventDefault();
54006
54007				var inputValues = {};
54008
54009				var inputs = $(this).find('input,select,textarea');
54010				inputs.each(function() {
54011					if(typeof $(this).attr('name') != "undefined" && $(this).attr('name') != '') {
54012						if($(this).attr('type') == 'radio') {
54013							inputValues[$(this).attr('name')] = $("[name='" + $(this).attr('name') + "']:checked").val();
54014						} else {
54015							inputValues[$(this).attr('name')] = $(this).val();
54016						}
54017					}
54018				});
54019
54020				$http.post("?cl=api&mt=sendKontakt", inputValues)
54021					.then(function(data) {
54022						if(data.data.success) {
54023							swal({
54024							  title: "Nachricht gesendet",
54025							  text: "Deine Nachricht wird an uns weitergeleitet",
54026							  type: "success",
54027							  confirmButtonColor: "green",
54028							  confirmButtonText: "Ok",
54029							  timer: 10000
54030							});
54031						} else {
54032							swal({
54033							  title: "Nachricht konnte nicht gesendet werden",
54034							  text: "Bitte überprüfe deine Eingabe",
54035							  type: "error",
54036							  confirmButtonColor: "red",
54037							  confirmButtonText: "Ok",
54038							  timer: 10000
54039							});
54040						}
54041					});
54042
54043				return false;
54044			});
54045		}
54046	};
54047
54048	$rootScope.methodBootstrapSlider = function(selector, method, event) {
54049		if(typeof event != "undefined") {
54050			event.preventDefault();
54051		}
54052		$(selector).carousel(method);
54053	};
54054
54055	$rootScope.setTypeTab = function(element) {
54056		element.preventDefault();
54057		$(element.target).tab('show');
54058	};
54059
54060	$rootScope.toggleCollapse = function(selector) {
54061		if($(window).width() < 1200) {
54062			$(selector).collapse('toggle');
54063		}
54064	};
54065
54066    $rootScope.loadCookieCoupons = function() {
54067		if(!location.hash.startsWith("#/cl/index/executePaypal")) {
54068			$http.get('?cl=index&mt=api_checkCoupons')
54069				.then(function(data) {
54070					var coupons = $rootScope.getLocalObject("coupons" + $rootScope.currentFilialId);
54071			        if(coupons !== null) {
54072			            for(var c in coupons) {
54073			                $rootScope.checkCoupon(undefined, coupons[c].code, coupons[c]);
54074			            }
54075			        }
54076				});
54077		}
54078    };
54079
54080    $rootScope.checkCoupon = function(e, code, cookieInformations) {
54081        if(typeof code == "undefined") {
54082            code = $rootScope.formData.currentCouponCode;
54083        }
54084        if(typeof code == "undefined") {
54085            code = '';
54086        }
54087        if(typeof e != "undefined" && e.keyCode != 13 || code.length == 0) {
54088            return false;
54089        }
54090
54091        $rootScope.websiteChangeLoading = true;
54092        $http.post('?cl=api&mt=checkCoupon', {
54093            code: code,
54094            datum: $rootScope.currentDatum,
54095            uhrzeit: $rootScope.currentUhrzeit,
54096            typ: $rootScope.currentType,
54097            referer: $rootScope.currentReferer
54098        })
54099            .then(function(data) {
54100                $rootScope.websiteChangeLoading = false;
54101                if(data.data.success) {
54102                    data.data.coupon = $rootScope.setInitZutatenCoupon(data.data.coupon);
54103                    data.data.coupon.usedCode = code;
54104                    data.data.coupon.code = code;
54105                    if(data.data.coupon.mehrfachNutzbar == 0 && $rootScope.usedCoupons.indexOf(data.data.coupon.id) > -1) {
54106                        swal('Schon eingelöst!', "Du hast diesen Coupon schon eingelöst!", "warning");
54107                        return;
54108                    }
54109
54110                    if(data.data.coupon.mehrfachNutzbar == 0 && $rootScope.usedCodes.indexOf(code) > -1) {
54111                        swal('Schon eingelöst!', "Du hast diesen Code schon benutzt!", "warning");
54112                        return;
54113                    }
54114                    data.data.coupon.gueltig = false;
54115                    if(data.data.coupon.isCouponKombinierbar == 0 && $rootScope.currentCoupons.length > 0) {
54116                        swal({
54117                            title: "Nicht kombinierbar",
54118                            text: "Der von dir eingegebene Coupon ist nicht mit anderen Coupons komb
54118inierbar. Möchtest du diesen Coupon trotzdem einlösen? Vorsicht, die anderen Coupons werden dadurch wieder entfernt!",
54119                            type: "warning",
54120                            showCancelButton: true,
54121                            confirmButtonText: "Ja, trotzdem einlösen!",
54122                            cancelButtonText: "Nein, Coupons behalten!",
54123                            confirmButtonClass: 'btn btn-success',
54124                            cancelButtonClass: 'btn btn-danger',
54125                            buttonsStyling: false
54126                        }).then(function() {
54127                            $timeout(function() {
54128                                $rootScope.currentCoupons = [];
54129                                $rootScope.usedCoupons = [];
54130                                if(typeof cookieInformations != "undefined") {
54131                                    data.data.coupon.currentZutaten = cookieInformations.currentZutaten;
54132                                    data.data.coupon.currentPreis = cookieInformations.currentPreis;
54133                                    data.data.coupon.zutatenWahlReihenfolge = cookieInformations.zutatenWahlReihenfolge;
54134                                    $rootScope.addCoupon(data.data.coupon, true);
54135                                } else {
54136                                    $rootScope.addCoupon(data.data.coupon);
54137                                }
54138                            });
54139                        });
54140                    } else {
54141                        var isCombining = false;
54142                        for(var c in $rootScope.currentCoupons) {
54143                            if($rootScope.currentCoupons[c].isCouponKombinierbar == 0) {
54144                                swal({
54145                                    title: "Nicht kombinierbar",
54146                                    text: "Du hast schon einen Coupon eingelöst, welcher nicht mit anderen Coupons komb
54146inierbar ist. Möchtest du trotzdem den neuen Coupon einlösen? Vorsicht, der zuvor eingelöste Coupon wird dadurch wieder entfernt!",
54147                                    type: "warning",
54148                                    showCancelButton: true,
54149                                    confirmButtonText: "Ja, trotzdem einlösen!",
54150                                    cancelButtonText: "Nein, Coupon behalten!",
54151                                    confirmButtonClass: 'btn btn-success',
54152                                    cancelButtonClass: 'btn btn-danger',
54153                                    buttonsStyling: false
54154                                }).then(function() {
54155                                    $timeout(function() {
54156                                        $rootScope.usedCoupons.splice(
54157                                            $rootScope.usedCoupons.indexOf($rootScope.currentCoupons[c].id),
54158                                            1
54159                                        );
54160                                        $rootScope.currentCoupons.splice(c, 1);
54161                                        if(typeof cookieInformations != "undefined") {
54162                                            data.data.coupon.currentZutaten = cookieInformations.currentZutaten;
54163                                            data.data.coupon.currentPreis = cookieInformations.currentPreis;
54164                                            data.data.coupon.zutatenWahlReihenfolge = cookieInformations.zutatenWahlReihenfolge;
54165                                            $rootScope.addCoupon(data.data.coupon, true);
54166                                        } else {
54167                                            $rootScope.addCoupon(data.data.coupon);
54168                                        }
54169                                    });
54170                                });
54171                                isCombining = true;
54172                                break;
54173                            }
54174                        }
54175
54176                        if(!isCombining) {
54177                            if(typeof cookieInformations != "undefined") {
54178                                data.data.coupon.currentZutaten = cookieInformations.currentZutaten;
54179                                data.data.coupon.currentPreis = cookieInformations.currentPreis;
54180                                data.data.coupon.zutatenWahlReihenfolge = cookieInformations.zutatenWahlReihenfolge;
54181                                $rootScope.addCoupon(data.data.coupon, true);
54182                            } else {
54183                                $rootScope.addCoupon(data.data.coupon);
54184                            }
54185                        }
54186                    }
54187                } else {
54188                    // $rootScope.currentCoupons = [];
54189                    swal({
54190    					title: "Code ungültig",
54191    					text: data.data.grund,
54192    					type: "error",
54193    					confirmButtonColor: "red",
54194    					confirmButtonText: "Erneut versuchen"
54195    				});
54196                }
54197            }, function() {
54198                // $rootScope.currentCoupons = [];
54199                swal({
54200					title: "Code ungültig",
54201					text: "Der von dir eingegebene Code scheint ungültig zu sein. Bitte versuche es erneut.",
54202					type: "error",
54203					confirmButtonColor: "red",
54204					confirmButtonText: "Erneut versuchen"
54205				});
54206            })
54207    };
54208
54209    $rootScope.setInitZutatenCoupon = function(coupon) {
54210        for(var zp in coupon.zutatenObj.zutaten) {
54211            if($rootScope.isXor(zp, coupon)) {
54212                for(var z in coupon.zutatenObj.zutaten[zp].zutaten) {
54213                    if(coupon.zutatenObj.zutaten[zp].zutaten[z].preis != '#') {
54214                        $rootScope.setCouponZutat(zp, z, coupon);
54215                        break;
54216                    }
54217                }
54218            }
54219        }
54220
54221        return coupon;
54222    };
54223
54224    $rootScope.addCoupon = function(coupon, isCookie) {
54225        var newIndex = $rootScope.currentCoupons.length;
54226        $rootScope.currentCoupons.push(coupon);
54227        $rootScope.usedCoupons.push(coupon.id);
54228        $rootScope.usedCodes.push(coupon.code);
54229        $rootScope.formData.currentCouponCode = '';
54230
54231        //$rootScope.currentGericht.zutaten[0] != "zutaten" && $rootScope.currentGericht.zutaten.length != 0
54232
54233        if(
54234            $rootScope.currentCoupons[newIndex].zutatenObj[0] != "zutaten"
54235            && $rootScope.currentCoupons[newIndex].zutaten.length != 0
54236            && (
54237                typeof isCookie == "undefined"
54238                || isCookie == false
54239            )
54240        ) {
54241            $rootScope.setEdittingCoupon(newIndex);
54242            $rootScope.openCouponModal();
54243        }
54244
54245        $rootScope.checkCouponData();
54246    };
54247
54248    $rootScope.editCoupon = function(coupon, index) {
54249        $rootScope.setEdittingCoupon(index);
54250        $rootScope.openCouponModal();
54251    };
54252
54253    $rootScope.removeCoupon = function(index) {
54254        $rootScope.usedCoupons.splice(
54255            $rootScope.usedCoupons.indexOf($rootScope.currentCoupons[index].id),
54256            1
54257        );
54258        $rootScope.usedCodes.splice(
54259            $rootScope.usedCodes.indexOf($rootScope.currentCoupons[index].code),
54260            1
54261        );
54262        $rootScope.currentCoupons.splice(index, 1);
54263    };
54264
54265	$rootScope.setCouponGroesse = function(groesseIndex, couponIndex, coupon) {
54266        if(typeof couponIndex == "undefined") {
54267            couponIndex = $rootScope.edittingCouponIndex;
54268        }
54269		if(typeof coupon == 'undefined') {
54270			coupon = $rootScope.currentCoupons[couponIndex];
54271		}
54272		coupon.currentPreis = groesseIndex;
54273        $rootScope.currentCoupons[couponIndex] = coupon;
54274	};
54275
54276    $rootScope.getCouponPreis = function(coupon, index) {
54277        if(typeof index == "undefined") {
54278            index = coupon.currentPreis;
54279        }
54280        var normalPreis = coupon.preiseObj.normal[$rootScope.currentType][index];
54281
54282        if(coupon.isProzentKomplett == 0) {
54283            if(coupon.typ == 'gratis') {
54284                return 0;
54285            } else if(coupon.typ == '2for1') {
54286                return $rootScope.getCouponZutatenPreis(coupon, coupon.currentPreis);
54287            }  else if(coupon.typ == '1') {
54288                return $rootScope.convertToNumber(normalPreis) * (1 - $rootScope.convertToNumber(coupon.wert)) + $rootScope.getCouponZutatenPreis(coupon, coupon.currentPreis);
54289            } else if(coupon.typ == '2') {
54290				return $rootScope.convertToNumber(-coupon.wert);
54291				//return $rootScope.convertToNumber(normalPreis) - $rootScope.convertToNumber(coupon.wert) + $rootScope.getCouponZutatenPreis(coupon, coupon.currentPreis);
54292            }
54293
54294            var zutatenPreis = $rootScope.getCouponZutatenPreis(coupon, coupon.currentPreis);
54295
54296            return $rootScope.convertToNumber(normalPreis) + zutatenPreis;
54297        } else {
54298            return 0;
54299        }
54300    };
54301
54302    $rootScope.getCouponZutatenPreis = function(coupon, preisIndex, zutatPropsIndex, zutatIndex) {
54303        var betrag = 0;
54304        var kleinsterWert = false;
54305        var einzelBetraege = {};
54306
54307		for(var zp in coupon.currentZutaten) {
54308            if(typeof einzelBetraege[zp] == "undefined") {
54309                einzelBetraege[zp] =  {};
54310            }
54311        	var showZutat = $rootScope.showZutat(zp, coupon);
54312        	if(!showZutat) {
54313        		continue;
54314        	}
54315			var selectedZutaten = coupon.currentZutaten[zp];
54316			var zutatProps = coupon.zutatenObj.zutaten[zp];
54317			var isPlus = $rootScope.isPlus(zutatProps);
54318			var plusIndex = $rootScope.getPlusIndex(zutatProps);
54319			for(var z in selectedZutaten) {
54320                if(typeof einzelBetraege[zp][z] == "undefined") {
54321                    einzelBetraege[zp][z] = 0;
54322                }
54323				var indexOfWahl = -1;
54324				for(var i = 0; i < selectedZutaten[z]; i++) {
54325					if(isPlus && (coupon.typ != '2for1' || zp != 0)) {
54326						if(
54327							typeof coupon.zutatenWahlReihenfolge == "undefined"
54328							|| typeof coupon.zutatenWahlReihenfolge[zp] == "undefined"
54329						) {
54330                            einzelBetraege[zp][z] += 0;
54331							continue;
54332						}
54333						var maxFree = zutatProps.rules[plusIndex].anzahl;
54334						var indexOfWahl = coupon.zutatenWahlReihenfolge[zp].indexOf(Number(z), indexOfWahl + 1);
54335
54336						if(indexOfWahl > -1 && indexOfWahl < maxFree) {
54337                            einzelBetraege[zp][z] += 0;
54338							continue;
54339						}
54340
54341					}
54342
54343					if(
54344						typeof coupon.zutatenObj.zutaten[zp].zutaten[z].preise[$rootScope.currentType][preisIndex] != "undefined"
54345						&& !isNaN(coupon.zutatenObj.zutaten[zp].zutaten[z].preise[$rootScope.currentType][preisIndex])
54346					) {
54347                        var zutatPreis = Number(coupon.zutatenObj.zutaten[zp].zutaten[z].preise[$rootScope.currentType][preisIndex]);
54348                        if(zp == 0 && coupon.typ == '2for1' && Number(kleinsterWert) !== false && Number(kleinsterWert) > zutatPreis) {
54349                            betrag += Number(kleinsterWert);
54350                            kleinsterWert = zutatPreis;
54351                            for(var bZ in einzelBetraege[zp]) {
54352                                einzelBetraege[zp][bZ] = Number(coupon.zutatenObj.zutaten[zp].zutaten[bZ].preise[$rootScope.currentType][preisIndex]);
54353                            }
54354                            einzelBetraege[zp][z] += 0;
54355                        } else if(zp == 0 && coupon.typ == '2for1' && kleinsterWert === false) {
54356                            kleinsterWert = zutatPreis;
54357                            einzelBetraege[zp][z] += 0;
54358                        } else {
54359				            betrag += zutatPreis;
54360                            einzelBetraege[zp][z] += zutatPreis;
54361                        }
54362					}
54363				}
54364			}
54365		}
54366
54367        if(typeof zutatPropsIndex != "undefined" && typeof zutatIndex != "undefiend") {
54368            return einzelBetraege[zutatPropsIndex][zutatIndex];
54369        }
54370
54371		return betrag;
54372    };
54373
54374    $rootScope.addCouponZutat = function(zutatPropsIndex, zutatIndex) {
54375		if(typeof $rootScope.currentCoupons[$rootScope.edittingCouponIndex].currentZutaten == "undefined") {
54376			$rootScope.currentCoupons[$rootScope.edittingCouponIndex].currentZutaten = {};
54377		}
54378		if(typeof $rootScope.currentCoupons[$rootScope.edittingCouponIndex].currentZutaten[zutatPropsIndex] == "undefined") {
54379			$rootScope.currentCoupons[$rootScope.edittingCouponIndex].currentZutaten[zutatPropsIndex] = {};
54380		}
54381		if(typeof $rootScope.currentCoupons[$rootScope.edittingCouponIndex].currentZutaten[zutatPropsIndex][zutatIndex] == "undefined") {
54382			$rootScope.currentCoupons[$rootScope.edittingCouponIndex].currentZutaten[zutatPropsIndex][zutatIndex] = 0;
54383		}
54384
54385		var zutatProps = $rootScope.currentCoupons[$rootScope.edittingCouponIndex].zutatenObj.zutaten[zutatPropsIndex];
54386
54387		if(typeof $rootScope.currentCoupons[$rootScope.edittingCouponIndex].zutatenWahlReihenfolge == "undefined") {
54388			$rootScope.currentCoupons[$rootScope.edittingCouponIndex].zutatenWahlReihenfolge = {};
54389		}
54390
54391		if(typeof $rootScope.currentCoupons[$rootScope.edittingCouponIndex].zutatenWahlReihenfolge[zutatPropsIndex] == "undefined") {
54392			$rootScope.currentCoupons[$rootScope.edittingCouponIndex].zutatenWahlReihenfolge[zutatPropsIndex] = [];
54393		}
54394
54395		$rootScope.currentCoupons[$rootScope.edittingCouponIndex].zutatenWahlReihenfolge[zutatPropsIndex].push(zutatIndex);
54396
54397		$rootScope.currentCoupons[$rootScope.edittingCouponIndex].currentZutaten[zutatPropsIndex][zutatIndex]++;
54398	};
54399
54400    $rootScope.setCouponZutat = function(zutatPropsIndex, zutatIndex, coupon) {
54401        if(typeof coupon == "undefined") {
54402            coupon = $rootScope.currentCoupons[$rootScope.edittingCouponIndex];
54403        }
54404		if(typeof coupon.currentZutaten == "undefined") {
54405			coupon.currentZutaten = {};
54406		}
54407
54408		coupon.currentZutaten[zutatPropsIndex] = {};
54409		if(typeof coupon.currentZutaten[zutatPropsIndex][zutatIndex] == "undefined") {
54410			coupon.currentZutaten[zutatPropsIndex][zutatIndex] = 0;
54411		}
54412
54413		coupon.currentZutaten[zutatPropsIndex][zutatIndex] = 1;
54414	};
54415
54416    $rootScope.removeCouponZutat = function(zutatPropsIndex, zutatIndex) {
54417		var indexWahl = -1;
54418		while((indexWahl = $rootScope.currentCoupons[$rootScope.edittingCouponIndex].zutatenWahlReihenfolge[zutatPropsIndex].indexOf(Number(zutatIndex))) > -1) {
54419			$rootScope.currentCoupons[$rootScope.edittingCouponIndex].zutatenWahlReihenfolge[zutatPropsIndex].splice(indexWahl, 1);
54420		}
54421
54422		delete $rootScope.currentCoupons[$rootScope.edittingCouponIndex].currentZutaten[zutatPropsIndex][zutatIndex];
54423	};
54424
54425    $rootScope.setEdittingCoupon = function(index) {
54426        $rootScope.edittingCouponIndex = index;
54427    };
54428
54429    $rootScope.checkCouponData = function() {
54430        $rootScope.couponWithNotCombiningRabatt = false;
54431        if($rootScope.currentCoupons.length == 0) {
54432            return;
54433        }
54434
54435        for(var c in $rootScope.currentCoupons) {
54436            $rootScope.currentCoupons[c].gueltig = true;
54437            $rootScope.currentCoupons[c].fehler = {};
54438            if(typeof $rootScope.currentCoupons[c].currentPreis == "undefined") {
54439                $rootScope.currentCoupons[c].currentPreis = 0;
54440            }
54441            var bestellWert = 0;
54442            $rootScope.currentCoupons[c].fehler.inkats = false;
54443            var minGerichtErfuellt = true;
54444            // var couponWithProzent = typeof $rootScope.currentCoupons[c].withProzente == "undefined" || $rootScope.currentCoupons[c].withProzente == true;
54445
54446            if($rootScope.warenkorb.length == 0) {
54447                if($rootScope.currentCoupons[c].inbegriffeneKatsObj.length > 0) {
54448                    $rootScope.currentCoupons[c].fehler.inkats = true;
54449                    minGerichtErfuellt = false;
54450                }
54451                if(Number($rootScope.currentCoupons[c].minBetrag) > 0) {
54452                    $rootScope.currentCoupons[c].fehler.minbetrag = true;
54453                }
54454            }
54455
54456            for(var g in $rootScope.warenkorb) {
54457                if(
54458                    $rootScope.currentCoupons[c].ausgeschlosseneKatsObj.length > 0
54459                    && $rootScope.currentCoupons[c].ausgeschlosseneKatsObj.indexOf($rootScope.warenkorb[g].katNr) > -1
54460                    && $rootScope.currentCoupons[c].isProzentKomplett == 0
54461                ) {
54462                    $rootScope.currentCoupons[c].fehler.auskats = true;
54463                    $rootScope.currentCoupons[c].gueltig = false;
54464                    return;
54465                }
54466
54467                if(
54468                    minGerichtErfuellt == false
54469                    && (
54470                        $rootScope.currentCoupons[c].inbegriffeneKatsObj.length == 0
54471                        || $rootScope.currentCoupons[c].inbegriffeneKatsObj.indexOf($rootScope.warenkorb[g].katNr) > -1
54472                    )
54473                    && $rootScope.currentCoupons[c].isProzentKomplett == 0
54474                ) {
54475                    $rootScope.currentCoupons[c].fehler.inkats = false;
54476                    minGerichtErfuellt = true;
54477                }
54478
54479                if(
54480                    $rootScope.currentCoupons[c].wertAusgeschlosseneKatsObj.length == 0
54481                    || $rootScope.currentCoupons[c].wertAusgeschlosseneKatsObj.indexOf($rootScope.warenkorb[g].katNr) == -1
54482                ) {
54483                    bestellWert += $rootScope.getGerichtsPreis(
54484                        $rootScope.warenkorb[g],
54485                        $rootScope.warenkorb[g].currentPreis,
54486                        undefined,
54487                        $rootScope.currentCoupons[c].isKombinierbar == 1
54488                    ) * $rootScope.warenkorb[g].currentAnzahl;
54489                }
54490            }
54491
54492            if(bestellWert < Number($rootScope.currentCoupons[c].minBetrag)) {
54493                $rootScope.currentCoupons[c].gueltig = false;
54494                $rootScope.currentCoupons[c].fehler.minbetrag = true;
54495            }
54496
54497            if(!minGerichtErfuellt) {
54498                $rootScope.currentCoupons[c].gueltig = false;
54499            }
54500
54501            if($rootScope.currentCoupons[c].isKombinierbar == 0 && $rootScope.isProzentKomplett == 1) {
54502                $rootScope.couponWithNotCombiningRabatt = true;
54503            }
54504
54505            if($rootScope.currentCoupons[c].gueltig && $rootScope.currentCoupons[c].isProzentKomplett == 1) {
54506                $rootScope.couponRabattKomplett = $rootScope.convertToNumber($rootScope.currentCoupons[c].wert);
54507            }
54508        }
54509
54510        // console.log($rootScope.currentCoupons);
54511
54512        // console.log($rootScope.gerichte);
54513    };
54514
54515    $rootScope.couponHasFehler = function(coupon) {
54516        for(var f in coupon.fehler) {
54517            if(coupon.fehler[f] === true) {
54518                return true;
54519            }
54520        }
54521
54522        return false;
54523    };
54524
54525    $rootScope.openCouponModal = function() {
54526    	$rootScope.openModal($('.couponModal'));
54527    };
54528
54529    $rootScope.closeCouponModal = function(event) {
54530        if(typeof event == "undefined" || ($(event.target).is('.couponModal') && $(event.target).parents('.cfjs-modal').length == 0)) {
54531			// if(typeof removeBonus != "undefined" && removeBonus == true) {
54532				// $rootScope.removeBonus();
54533			// }
54534			$rootScope.closeModal($('.couponModal'));
54535		}
54536    };
54537
54538	$rootScope.checkForRabattGutscheinForm = function() {
54539		if($('#rabattGutscheinForm').length > 0) {
54540			$('#rabattGutscheinForm').submit(function(e) {
54541				e.preventDefault();
54542
54543				$http.post('?cl=api&mt=apiLog&logArt=gutscheinClick', {});
54544
54545				swal({
54546					title: "Code ungültig",
54547					text: "Der von dir eingegebene Code scheint ungültig zu sein. Bitte versuche es erneut.",
54548					type: "error",
54549					confirmButtonColor: "red",
54550					confirmButtonText: "Erneut versuchen"
54551				});
54552
54553				return false;
54554			});
54555		}
54556	};
54557
54558	$rootScope.renderForAjax = function() {
54559		$rootScope.checkForRegistrierenForm();
54560		$rootScope.checkForLoginForm();
54561		$rootScope.checkForAddLieferadresseForm();
54562		$rootScope.checkForPasswortResetForm();
54563		$rootScope.checkForResetTokenForm();
54564		$rootScope.checkForRabattGutscheinForm();
54565		$rootScope.checkForKontaktForm();
54566		$rootScope.changeAnchorHref();
54567		$rootScope.doWebBenutzerDeleteAccount();
54568
54569		$('.lightbox, .lightboxOverlay').each(function() {
54570			if($(this).is(':visible')) {
54571				$(this).remove();
54572			}
54573		});
54574
54575		$timeout(function() {
54576			$rootScope.render();
54577
54578            if(typeof FB != "undefined") {
54579                FB.XFBML.parse();
54580            }
54581		});
54582	};
54583
54584	$rootScope.$on("$locationChangeSuccess", function(event, next, current) {
54585		if($rootScope.currentStep == "erfolgreich" && $rootScope.changeCurrentStep) {
54586			$rootScope.currentStep = "bestaetigen";
54587		}
54588        if(!$rootScope.changeCurrentStep) {
54589            $rootScope.changeCurrentStep = true;
54590        }
54591		$rootScope.config['last-visited'] = current;
54592		$rootScope.config['current-site'] = $location.url();
54593		$rootScope.websiteChangeLoading = true;
54594	});
54595
54596	$rootScope.$on("$viewContentLoaded", function() {
54597		var route = $route.current.$$route.originalPath;
54598
54599		if($('.header-v21').length > 0 && route != "/") {
54600			var scrollTop = $('.header-v21 .navbar-header').offset().top;
54601			scrollTop -= 15;
54602            if($('.header-v21').css('position') == 'fixed') {
54603                scrollTop = 0;
54604            }
54605			window.scrollTo(0, scrollTop);
54606		} else {
54607			window.scrollTo(0, 0);
54608		}
54609		$rootScope.websiteChangeLoading = false;
54610		if(route == "/profil" || route == "/profil/:method" || route == "/profil/:method/:parameter*") {
54611			var params = $rootScope.clone($routeParams);
54612			params.ajax = "true";
54613			if(typeof params.method == "undefined") {
54614				params.mt = "dashboard";
54615			} else {
54616				params.mt = params.method;
54617			}
54618
54619			var urlParameter = [];
54620			for(var p in params) {
54621				urlParameter.push(p + "=" + params[p]);
54622			}
54623
54624			$rootScope.websiteChangeLoading = true;
54625			$http.get('?cl=index&' + urlParameter.join("&"))
54626				.then(function(data) {
54627					$('#profilContent').html(data.data);
54628					$compile($('#profilContent'))($rootScope);
54629
54630					// $timeout(function() {
54631						$rootScope.renderForAjax();
54632						$rootScope.websiteChangeLoading = false;
54633					// });
54634				});
54635		} else {
54636			// $timeout(function() {
54637				$rootScope.renderForAjax();
54638				// $rootScope.websiteChangeLoading = false;
54639			// });
54640		}
54641
54642		$timeout(() => {
54643			if(isBigText) {
54644				increaseTextSize();
54645			}
54646		});
54647	});
54648
54649    $rootScope.refreshContent = function() {
54650        var currentPageTemplate = $route.current.templateUrl;
54651        $templateCache.remove(currentPageTemplate);
54652        $route.reload();
54653        $rootScope.refreshParams(true);
54654    };
54655
54656    $rootScope.getCurrentUrl = function() {
54657        return $location.url();
54658    };
54659
54660	$rootScope.showGerichtAddedNotify = function() {
54661		$.notify('Gericht wurde hinzugefügt', {
54662			style: 'cf-notify'
54663		});
54664	};
54665
54666	$rootScope.checkCookieInfo();
54667	$rootScope.checkCookieApp();
54668	$rootScope.loadSettings();
54669});
54670
54671
54672app.filter('range', function() {
54673  return function(input, total) {
54674    total = parseInt(total);
54675
54676    for (var i=0; i<total; i++) {
54677      input.push(i);
54678    }
54679
54680    return input;
54681  };
54682});
54683
54684app.config(["$routeProvider", function($routeProvider) {
54685	$routeProvider
54686		.when("/", {
54687			templateUrl: "?cl=index&ajax=true"
54688		})
54689		.when("/page/:parameter*", {
54690			templateUrl: function(urlAttr) {
54691				var parameter = urlAttr.parameter.split('/');
54692				var query = [];
54693
54694				for(var p in parameter) {
54695					query.push("p" + p + "=" + parameter[p]);
54696				}
54697
54698				var url = "?cl=index&" + query.join('&') + "&ajax=true";
54699
54700				return url;
54701			}
54702		})
54703		.when("/profil/:method/:parameter*", {
54704			templateUrl: "?cl=index&mt=profil&ajax=true"
54705		})
54706		.when("/profil/:method", {
54707			templateUrl: "?cl=index&mt=profil&ajax=true"
54708		})
54709		.when("/profil", {
54710			templateUrl: "?cl=index&mt=profil&ajax=true"
54711		})
54712		.when("/cl/:controller/:method", {
54713			templateUrl: function(urlAttr) {
54714				var url = "?cl=" + urlAttr.controller + "&mt=" + urlAttr.method + "&ajax=true";
54715
54716				return url;
54717			}
54718		})
54719		.when("/cl/:controller/:method/:parameter*", {
54720			templateUrl: function(urlAttr) {
54721				var parameter = urlAttr.parameter.split('/');
54722				var query = [];
54723
54724				for(var p in parameter) {
54725					if(parameter[p].indexOf("=") > -1) {
54726						query.push(parameter[p]);
54727					} else {
54728						query.push("p" + p + "=" + parameter[p]);
54729					}
54730				}
54731
54732				var url = "?cl=" + urlAttr.controller + "&mt=" + urlAttr.method + "&" + query.join('&') + "&ajax=true";
54733
54734				return url;
54735			}
54736		})
54737		.otherwise('/');
54738}]);
54739
54740app.filter('trustHtml', function($sce) {
54741  return function(html) {
54742    return $sce.trustAsHtml(html);
54743  };
54744});
54745
54746app.directive("cfEqualizer", function($timeout) {
54747	return {
54748		restrict: "A",
54749		scope: {
54750			model: '=ngModel'
54751		},
54752		link: function(scope, elem, attr) {
54753			var imageLength = 0;
54754
54755			if(typeof scope.model !== undefined) {
54756				scope.$watch('model', lookForImages, true);
54757			} else {
54758				lookForImages();
54759			}
54760
54761			$(window).resize(lookForImages);
54762
54763			function lookForImages() {
54764				$timeout(function() {
54765					var images = elem.find('img');
54766					var imageLength = images.length;
54767
54768					if(imageLength > 0) {
54769						images.each(function() {
54770							if(this.complete) {
54771								imageLoaded();
54772							} else {
54773								$(this).on('load', imageLoaded);
54774							}
54775						});
54776					} else {
54777						setHeight();
54778					}
54779				});
54780			}
54781
54782			function imageLoaded() {
54783				imageLength--;
54784
54785				if(imageLength <= 0) {
54786					setHeight();
54787				}
54788			}
54789
54790			function setHeight() {
54791				var maxHeight = 0;
54792
54793				elem.children().each(function() {
54794					if($(this).height() > maxHeight) {
54795						maxHeight = $(this).height();
54796					}
54797				});
54798
54799				elem.children().each(function() {
54800					$(this).height(maxHeight);
54801				});
54802			}
54803		}
54804	}
54805});
54806
54807app.directive('cfInteger', function() {
54808	return {
54809		require: 'ngModel',
54810		link: function(scope, elm, attrs, ngModel) {
54811			ngModel.$formatters.push(function(value) {
54812				if(isNaN(Number(value)) || value != Math.round(Number(value))) {
54813					return 0;
54814				} else {
54815					return Number(value);
54816				}
54817			});
54818
54819			//format text from the user (view to model)
54820			ngModel.$parsers.push(function(value) {
54821				if(isNaN(Number(value)) || value != Math.round(Number(value))) {
54822					return 0;
54823				} else {
54824					return Number(value);
54825				}
54826			});
54827		}
54828	};
54829});
54830app.directive('iCheck', function($timeout, $parse, $rootScope) {
54831  return {
54832      require: 'ngModel',
54833      link: function($scope, element, $attrs, ngModel) {
54834          return $timeout(function() {
54835              var value;
54836              value = $attrs['value'];
54837
54838              $rootScope.$watch($attrs['ngModel'], function(newValue){
54839                  $(element).iCheck('update');
54840              })
54841
54842              return $(element).iCheck({
54843                  checkboxClass: 'icheckbox_square-grey',
54844                  radioClass: 'iradio_square-grey',
54845
54846              }).on('ifChanged', function(event) {
54847                  if ($(element).attr('type') === 'checkbox' && $attrs['ngModel']) {
54848                      $timeout(function() {
54849                        ngModel.$setViewValue(event.target.checked);
54850                      });
54851                      // $rootScope.$apply(function() {
54852                      //     return ngModel.$setViewValue(event.target.checked);
54853                      // });
54854                  }
54855                  if ($(element).attr('type') === 'radio' && $attrs['ngModel']) {
54856                      $timeout(function() {
54857                        console.log("change", value);
54858                        ngModel.$setViewValue(value);
54859                      });
54860                      // return $rootScope.$apply(function() {
54861                      //     console.log("change", value);
54862                      //     return ngModel.$setViewValue(value);
54863                      // });
54864                  }
54865              });
54866          });
54867      }
54868  };
54869})
54870app.directive("cfScrollReveal", function($timeout, $rootScope) {
54871	return {
54872		restrict: "A",
54873		scope: {
54874			model: '=ngModel',
54875            cfConfig: '=cfScrollRevealConfig',
54876            cfContainerConfig: '=cfScrollRevealContainerConfig'
54877		},
54878		link: function(scope, elem, attr) {
54879            var config = {
54880                duration: 500,
54881                origin: 'left',
54882                distance:'500px',
54883                delay: 200,
54884                opacity:0,
54885                scale:0.1,
54886                easing:'linear',
54887                mobile:true,
54888                viewFactor: '0.2'
54889                // reset: true
54890            };
54891            var containerConfig = {};
54892
54893            angular.extend(config, scope.cfConfig);
54894            angular.extend(containerConfig, scope.cfContainerConfig);
54895
54896            var cfNgModels = attr.cfNgModels;
54897
54898            if(typeof $rootScope.scrollReveal == "undefined") {
54899                $rootScope.scrollReveal = new ScrollReveal(containerConfig);
54900            }
54901
54902            if(typeof cfNgModels != "undefined") {
54903                var ngModels = cfNgModels.split(',');
54904                for(var m in ngModels) {
54905                    $rootScope.$watch(ngModels[m], function(oldValue, newValue) {
54906                        $rootScope.scrollReveal.reveal(elem[0], {reset: true});
54907                    });
54908                }
54909            }
54910
54911            if(scope.model !== undefined) {
54912    			scope.$watch('model', function() {
54913                    $rootScope.scrollReveal.reveal(elem[0], config);
54914                }, true);
54915            } else {
54916                $rootScope.scrollReveal.reveal(elem[0], config);
54917            }
54918		}
54919	};
54920});
54921app.directive('cfCarousel', function($timeout, $compile) {
54922    return {
54923        restrict: 'A',
54924        require: 'ngModel',
54925        scope: {
54926            model: '=ngModel',
54927            cfConfig: '=cfCarouselConfig'
54928        },
54929        link: function(scope, elem, attr, ngModel) {
54930            var config = {
54931                xsWidth: 0,
54932                smWidth: 768,
54933                mdWidth: 992,
54934                lgWidth: 1200,
54935                xsElements: 1,
54936                smElements: 2,
54937                mdElements: 4,
54938                lgElements: 6,
54939                autoplay: true,
54940                autoplayDuration: 5000,
54941                animateDuration: 1500,
54942                animateEasing: "swing",
54943                showIndicators: true,
54944                swipeAnimateDuration: 1000,
54945                pixelForNextItem: $(window).width() / 5,
54946                timeForNextItem: 300
54947            };
54948
54949			angular.extend(config, scope.cfConfig);
54950
54951            var itemWidth = 0;
54952            var isDragging = false;
54953            var startDragging = 0;
54954            var startDraggingTime = (new Date()).getTime();
54955            var lastDragPosition = 0;
54956            var elemLength = 0;
54957            var autoplayId = false;
54958            var containerIsSetted = false;
54959            scope.currentIndex = 0;
54960            scope.pages = [];
54961
54962            var cfModel = attr.ngModel;
54963            if(elem.children('.cfc-carousel-container').length > 0) {
54964            	containerIsSetted = true;
54965            	var carouselContainer = elem.children('.cfc-carousel-container');
54966            } else {
54967            	var carouselContainer = $('<div>').addClass('cfc-carousel-container');
54968            }
54969            elem.append(carouselContainer);
54970            var indicatorContainer = $('<div>').addClass('cfc-indicators');
54971            var indicator = $('<i class="fa cfc-fa" ng-class="{\'fa-circle\': ($index == currentIndex), \'fa-circle-thin\': ($index != currentIndex)}" ng-repeat="page in pages track by $index" ng-click="clickToIndex($index);"></i>');
54972            indicatorContainer.append(indicator);
54973            elem.append(indicatorContainer);
54974            $compile(indicatorContainer)(scope);
54975
54976            if(config.showIndicators) {
54977                indicatorContainer.show();
54978            }
54979
54980            if(config.autoplay) {
54981                autoplayId = window.setInterval(doSwipe, config.autoplayDuration);
54982            }
54983
54984            scope.$watch("model", function() {
54985                moveElements();
54986                initializeElements();
54987            }, true);
54988
54989            $(window).resize(initializeElements);
54990
54991            elem.on('dragstart', function(event) {
54992                if($(event.target).is('img')) {
54993                    event.preventDefault();
54994                }
54995            });
54996
54997            elem.on('mousedown', function(event) {
54998                swipeStart(event.pageX, event.pageY);
54999            });
55000            elem.on('mousemove', function(event) {
55001                swipeMove(event.pageX, event.pageY);
55002            });
55003            $(window).on('mouseup', function(event) {
55004                if(isDragging) {
55005                    swipeEnd(event.pageX, event.pageY);
55006                }
55007            });
55008
55009            window.addEventListener("touchstart", function(ev) {
55010                if(ev.type !== 'touchstart') {
55011                    return;
55012                }
55013                if($(elem).has($(ev.target)).length > 0) {
55014                    swipeStart(ev.changedTouches[0].clientX, ev.changedTouches[0].clientY)
55015                }
55016            });
55017            window.addEventListener("touchmove", function(ev) {
55018                if(ev.type !== 'touchmove') {
55019                    return;
55020                }
55021                if($(elem).has($(ev.target)).length > 0) {
55022                    swipeMove(ev.changedTouches[0].clientX, ev.changedTouches[0].clientY)
55023                }
55024            });
55025            window.addEventListener("touchend", function(ev) {
55026                if(isDragging) {
55027                    if(ev.type !== 'touchend') {
55028                        return;
55029                    }
55030                    if($(elem).has($(ev.target)).length > 0) {
55031                        console.log("touch");
55032                        swipeEnd(ev.changedTouches[0].clientX, ev.changedTouches[0].clientY)
55033                    }
55034                }
55035            });
55036
55037            $('body').scroll(function(ev) {
55038            	if(elem.has($(ev.target)).length > 0) {
55039            		ev.preventDefault();
55040            	}
55041            });
55042
55043            function initializeElements() {
55044                var elemWidth = carouselContainer.width();
55045                if(elemWidth < config.smWidth) {
55046                    var elements = config.xsElements;
55047                } else if(elemWidth < config.mdWidth) {
55048                    var elements = config.smElements;
55049                } else if(elemWidth < config.lgWidth) {
55050                    var elements = config.mdElements;
55051                } else {
55052                    var elements = config.lgElements;
55053                }
55054
55055                elemLength = carouselContainer.children().length;
55056                itemWidth = (elemLength > elements ? (elemWidth / elements) : (elemWidth / elemLength));
55057
55058                scope.pages = [];
55059                if(elemLength > elements) {
55060                    var pages = Math.ceil(elemLength / elements);
55061                    for(var i = 0; i < pages; i++) {
55062                        scope.pages.push(i);
55063                    }
55064                }
55065
55066                carouselContainer.children().each(function() {
55067                    $(this).width(itemWidth);
55068                });
55069            }
55070
55071            function moveElements() {
55072            	if(!containerIsSetted) {
55073	                var elements = $(elem).children('.cfc-carousel-item');
55074					carouselContainer.append(elements);
55075				}
55076            }
55077
55078            function swipeStart(pageX, pageY) {
55079                if(config.autoplay) {
55080                    clearInterval(autoplayId);
55081                }
55082                carouselContainer.stop();
55083                isDragging = true;
55084                startDraggingX = pageX;
55085                startDraggingTime = (new Date()).getTime();
55086                lastDragPosition = pageX;
55087            }
55088
55089            function swipeMove(pageX, pageY) {
55090                if(isDragging) {
55091                    var delta = lastDragPosition - pageX;
55092                    lastDragPosition = pageX;
55093                    carouselContainer.scrollLeft(carouselContainer.scrollLeft() + delta);
55094                }
55095            }
55096
55097            function swipeEnd(pageX, pageY) {
55098                var elemWidth = carouselContainer.width();
55099                if(elemWidth < config.smWidth) {
55100                    var elements = config.xsElements;
55101                } else if(elemWidth < config.mdWidth) {
55102                    var elements = config.smElements;
55103                } else if(elemWidth < config.lgWidth) {
55104                    var elements = config.mdElements;
55105                } else {
55106                    var elements = config.lgElements;
55107                }
55108                isDragging = false;
55109
55110                var curScrollLeft = pageX; //carouselContainer.scrollLeft();
55111                var curTime = (new Date()).getTime();
55112
55113                if(
55114                    curScrollLeft < startDraggingX
55115                    && Math.abs(curScrollLeft - startDraggingX) > config.pixelForNextItem
55116                    && (curTime - startDraggingTime) < config.timeForNextItem
55117                ) {
55118                    // next item
55119                    scope.currentIndex++;
55120                    if(scope.currentIndex > (elemLength - 1)) {
55121                        scope.currentIndex = (elemLength - 1);
55122                    }
55123                } else if (
55124                    curScrollLeft > startDraggingX
55125                    && Math.abs(curScrollLeft - startDraggingX) > config.pixelForNextItem
55126                    && (curTime - startDraggingTime) < config.timeForNextItem
55127                ) {
55128                    // prev item
55129                    scope.currentIndex--;
55130                    if(scope.currentIndex < 0) {
55131                        scope.currentIndex = 0;
55132                    }
55133                }
55134
55135                console.log(scope.currentIndex, curScrollLeft, startDraggingX);
55136
55137
55138                scope.swipeToIndex(scope.currentIndex);
55139                return;
55140
55141                carouselContainer.animate({
55142                    scrollLeft: (Math.round(carouselContainer.scrollLeft() / itemWidth) * itemWidth) + 'px'
55143                }, {
55144                    duration: config.swipeAnimateDuration,
55145                    easing: config.animateEasing,
55146                    queue: false,
55147                    complete: function() {
55148                        $timeout(function() {
55149                            scope.currentIndex = Math.ceil(carouselContainer.scrollLeft() / (itemWidth * elements));
55150                        });
55151
55152                        // isDragging = false;
55153                        if(config.autoplay) {
55154                            autoplayId = window.setInterval(doSwipe, config.autoplayDuration);
55155                        }
55156                    }
55157                });
55158            }
55159
55160            function doSwipe() {
55161                if(isDragging) {
55162                    return;
55163                }
55164
55165                var elemWidth = carouselContainer.width();
55166                if(elemWidth < config.smWidth) {
55167                    var elements = config.xsElements;
55168                } else if(elemWidth < config.mdWidth) {
55169                    var elements = config.smElements;
55170                } else if(elemWidth < config.lgWidth) {
55171                    var elements = config.mdElements;
55172                } else {
55173                    var elements = config.lgElements;
55174                }
55175
55176                var maxScrollLeft = elemLength * itemWidth - carouselContainer.width();
55177                var oneScroll = elements * itemWidth;
55178                var index = scope.currentIndex;
55179
55180                if($(carouselContainer).scrollLeft() >= maxScrollLeft) {
55181                    index = 0;
55182                } else if(($(carouselContainer).scrollLeft() + oneScroll) > maxScrollLeft) {
55183                    index = scope.pages.length - 1;
55184                } else {
55185                    index++;
55186                }
55187
55188                $timeout(function() {
55189                    scope.currentIndex = index;
55190                    scope.swipeToIndex(scope.currentIndex);
55191                })
55192            }
55193
55194            scope.clickToIndex = function(index) {
55195                clearInterval(autoplayId);
55196                carouselContainer.stop();
55197
55198                if(config.autoplay) {
55199                    autoplayId = window.setInterval(doSwipe, config.autoplayDuration);
55200                }
55201
55202                isDragging = false;
55203                scope.swipeToIndex(index);
55204            }
55205
55206            scope.swipeToIndex = function(index) {
55207                var elemWidth = carouselContainer.width();
55208                if(elemWidth < config.smWidth) {
55209                    var elements = config.xsElements;
55210                } else if(elemWidth < config.mdWidth) {
55211                    var elements = config.smElements;
55212                } else if(elemWidth < config.lgWidth) {
55213                    var elements = config.mdElements;
55214                } else {
55215                    var elements = config.lgElements;
55216                }
55217
55218                var newScrollLeft = index * itemWidth * elements;
55219
55220                if(newScrollLeft > elemLength * itemWidth - carouselContainer.width()) {
55221                    newScrollLeft = elemLength * itemWidth - carouselContainer.width();
55222                }
55223
55224                $timeout(function() {
55225                    scope.currentIndex = index;
55226
55227                    carouselContainer.animate({
55228                        scrollLeft: newScrollLeft + 'px'
55229                    }, {
55230                        duration: config.animateDuration,
55231                        easing: config.animateEasing,
55232                        queue: false
55233                    });
55234                });
55235            };
55236        }
55237    }
55238});
55239
55240
55241zusatzStoffe = [
55242	{
55243		'Text': 'Enthält glutenhaltiges Getreide  (Weizen, Roggen, Gerste, Hafer, Dinkel, Kamut oder deren Hybridstämme)',
55244		'kuerzel': 'gh',
55245		'SearchString': 'Weizen|Roggen|Gerste|Hafer|Dinkel|Kamut|Gries|Mehl|paniert|Müsli|Pizza|Pizzen|Baguette|Gnocchi|Brot|Brötchen|Hefe|Bier|Blätterteig|Kuchen|Salzstangen|Calzone|Nudeln|Nudel|Graupen|Couscous|Brot|Suppen|Suppe|Soße|Sauce|Panade|Bier'
55246	}, {
55247		'Text': 'Krebstiere und Krebserzeugnisse',
55248		'kuerzel': 'kt',
55249		'SearchString': 'Krebs|Hummer|Meeresfrüchte|Scampi|Krabben|Garnelen|Langusten|Shrimps|Paela|Sashimi|Surimi'
55250	}, {
55251		'Text': 'Eier und -erzeugnisse',
55252		'kuerzel': 'ei',
55253		'SearchString': 'Eier|Spiegelei|Mayonnaise|Mayo|Pfannekuchen|Omelette|Omlette|Kuchen|Gebäck|Nudeln|Friko|Frikadelle|Hackbraten|Burger|panier|Pastete|Quinches|Kartoffelsalat|Dressing|Sauce|Mousse|Eis|Pudding|Baiser'
55254	}, {
55255		'Text': 'Fisch und -erzeugnisse',
55256		'kuerzel': 'fi',
55257		'SearchString': 'Fisch|Krebs|Hummer|Meeresfrüchte|Scampi|Krabben|Thunfisch|Kavier|Fischsauce|Sardelle|Surimi|Sashimi|Sushi|Matjes|Maki|Inside'
55258	}, {
55259		'Text': 'Erdnüsse und Erdnusserzeunisse',
55260		'kuerzel': 'en',
55261		'SearchString': 'Erdnuss|Erdnüsse|Erdnusssauce|Müsli'
55262	}, {
55263		'Text': 'Sojabohnen und daraus gewonnene Erzeugnisse',
55264		'kuerzel': 'so',
55265		'SearchString': 'Soja|Miso|Tofu|Tamari|Margerine|Schokolade|Schoko'
55266	}, {
55267		'Text': 'Milch und -erzeugnisse (einschließlich Laktose)',
55268		'kuerzel': 'mi',
55269		'SearchString': 'Milch|Butter|Creme fraiche|Joghurt|Käse|Laktose|Quark|Sahne|Schokolade|Aufläufe|Gratin|Auflauf|Kroketten|Chips|Kartoffelpüree|Marinade'
55270	}, {
55271		'Text': 'Schalenfrüchte, d.h. Mandeln, Pistazien, Hasel-, Wal-, Kaschu-, Pekan-, Para-, Makadamia- und Queenslandnüsse und daraus gewonnene Erzeugnisse',
55272		'kuerzel': 'sf',
55273		'SearchString': 'Mandel|Mandeln|Pistatien|Haselnuss|Haselnüsse|Walnuss|Walnüsse|Kaschunüsse|Pekannuss|Pekannüsse|Paranuss|Paranüsse|Makadamia|Makadamianuss|Makadamianüsse|Queenslandnüsse|Quenslandnuss|Marzipan|Pesto|Likör|Cashewnüsse'
55274	}, {
55275		'Text': 'Sellerie und -erzeugnisse',
55276		'kuerzel': 'se',
55277		'SearchString': 'Selerie|Wurst|Kräuterkäse|Curry|Essig|Suppe|Eintopf|Eintöpfe|Gewürze|Bouillon|Chips|mariniert|Marinade'
55278	}, {
55279		'Text': 'Senf und -erzeugnisse',
55280		'kuerzel': 'sn',
55281		'SearchString': 'Senf|Sauce|Suppen|Dressing|Ketchup|Mayonnaise|Mayo|Geeürz|Gewürze'
55282	}, {
55283		'Text': 'Sesamsamen und -erzeugnisse',
55284		'kuerzel': 'ss',
55285		'SearchString': 'Sesam|Müsli|Falafel|Salat|Humus|Marinade|Dessert'
55286	}, {
55287		'Text': 'Schwefeldioxid und Sulfite (Konzentration mehr als 10mg/kg oder 10mg/l), ausgedrückt als SO2',
55288		'kuerzel': 'sd',
55289		'SearchString': 'Schwefel|Schwefeldioxid|Pilze|Pilz|Wein|Müsli|Sauerkraut|Suppen|Sauce|Soße|Chips|Brot'
55290	}, {
55291		'Text': 'Lupinen und daraus gewonnene Erzeugnisse',
55292		'kuerzel': 'lp',
55293		'SearchString': 'Lupinen|Brot|Gebäck|Dessert|Kaffee|Snack|Pizza|Nudel|Brot|Pizzen|Gebäck'
55294	}, {
55295		'Text': 'Weichtiere und daraus gewonnene Erzeugnisse',
55296		'kuerzel': 'wt',
55297		'SearchString': 'Schneke|Muschel|Tintenfisch|Calamar|Abelone|Oktopus|Austern|Auster|Paella|Sauce|Soße|Marinade'
55298	},
55299	{
55300		'Text': 'mit Farbstoffen',
55301		'kuerzel': '1',
55302		'SearchString': 'Curry|Margerine|Reisgericht|Mayonnaise|Nudeln|Suppe|Senf|Sirup|Aprikose|Schokolade|Schoko|Paniermehl|paniert|Marzipan|Likör|Pudding|Essig|Kaffee|Fischrogen|Butter|Käse|Joghurt|Cola|Fanta|Campari|Speiseeis|Soße|Sauce|Kirschen|Meerrettich|Sprite|Peperoni|Zwiebeln|Sardellen|Broccoli|Brokkoli|Paprika|Salami|Champignons|Pilze|Schinken|Mais|Spinat|Mozzarella|Schafskäse|Gorgonzola|Artischocken|Thunfisch|Parmesan|Fassbrause|Ginger Ale|Whiskey|Eis|Hollandaise|Bernaise|Spargel'
55303	}, {
55304		'Text': 'mit Konservierungsstoffen, nitrathaltig',
55305		'kuerzel': '2',
55306		'SearchString': 'Lachs|Fisch|Kartoffelsalat|Mayo|Majo|Essiggurke|Gurke|Rote Bete|Käse|Anchosen|Tunfisch|Wein|Marinade|Salat|Fleisch|Ananas|Essiggurke|Gewürzgurke|Anchovi|Speck|Bacon'
55307	}, {
55308		'Text': 'mit Antioxidationsmittel',
55309		'kuerzel': '3',
55310		'SearchString': 'Schinken|Suppe|Brühe|Rotkohl|Fanta|Sprite|Mezzo|Cola|Pepsi|Mirinda|Seven Up|Wasser|Red Bull|Öl|Schokolade|Schoko|Kakao|Wein|Käse|Wurst|Bier|Ananas|Sardellen|Salami|Thunfisch'
55311	}, {
55312		'Text': 'mit Geschmacksverstärkern',
55313		'kuerzel': '4',
55314		'SearchString': 'Salami|Schinken|Gewürz|Soße|Sauce|Käse|Mozzarella|Gorgonzola|Parmesan|Tomaten|Schinken|Ketchup|Remoulade|Majo|Mayo|Fleisch'
55315	}, {
55316		'Text': 'mit Schwefeloxid/Sulfide',
55317		'kuerzel': '5',
55318		'SearchString': 'Essig|Meerrettich|Kartoffelsalat'
55319	}, {
55320		'Text': 'mit Süßstoffen / Süßungsmittel',
55321		'kuerzel': '6',
55322		'SearchString': 'Cola|Fanta|Sprite|Ananas|Seven Up|Mirinda|Pepsi|Afri|Light|Ketchup|Majo|Mayo|Remoulade|Senf|Ingwer|Creme'
55323	}, {
55324		'Text': 'mit Phosphat',
55325		'kuerzel': '7',
55326		'SearchString': ''
55327	}, {
55328		'Text': 'mit Milcheiweiß',
55329		'kuerzel': '8',
55330		'SearchString': 'Milch'
55331	}, {
55332		'Text': 'geschwärzt',
55333		'kuerzel': '9',
55334		'SearchString': 'Oliven'
55335	}, {
55336		'Text': 'chininhaltig',
55337		'kuerzel': '10',
55338		'SearchString': 'Tonic|Ginger Ale|Bitter Lemon'
55339	}, {
55340		'Text': 'gewachst',
55341		'kuerzel': '11',
55342		'SearchString': 'Apfel|Melone|Birne|Zitrone|Mandarine'
55343	}, {
55344		'Text': 'coffeinhaltig',
55345		'kuerzel': '12',
55346		'SearchString': 'Cola|Red Bull|Pepsi|Cafe|Kaffee|café|Eistee'
55347	}, {
55348		'Text': 'taurinhaltig',
55349		'kuerzel': '13',
55350		'SearchString': 'Energy|Red Bull|Monster'
55351	}, {
55352		'Text': 'Gentechnisch verändert',
55353		'kuerzel': '14',
55354		'SearchString': 'Öl|Soja|Mais'
55355	},
55356];
55357var kategorieBilder = {
55358	"spezialitäten" : ["spezialitaeten.jpg"],
55359	"pizza|pizzen|piz": ["pizza.jpg","pizza2.jpg","pizza3.jpg","pizza4.jpg","pizza5.jpg"],
55360	"nudel|nudeln|pasta": ["nudeln.jpg","nudeln2.jpg","nudeln3.jpg"],
55361	"schwäb|schwäbisch|schwäbische": ["schwaebisch.jpg","schwaebisch2.jpg","schwaebisch3.jpg"],
55362	"mexikanisch|Mexican|Mexiko|nachos": ["mexikanisch.jpg"],
55363	"überbacken|ueberbacken|Überback": ["ueberbacken.jpg"],
55364	"Fleisch|Flei|Fleischgerichte|Carni|Bistecca": ["fleisch.jpg","fleisch2.jpg","fleisch3.jpg"],
55365	"Beilagen|Extras|Pommes|verschiedenes": ["beilagen.jpg"],
55366	"Salat|Salate|Salatgerichte|Salad|Salads": ["salat.jpg"],
55367	"Toast|Sandwich|Sandwiches": ["toast.jpg"],
55368	"Griech|Grieschische|Gyros": ["gyros.jpg"],
55369	"Rösti|Rösties|Rösty": ["roesti.jpg"],
55370	"baguette|croque|baguettes": ["baguette.jpg"],
55371	"fisch|Fischgerichte|Fische|meeresfrüchte": ["fisch.jpg","fisch1.jpg","fisch2.jpg"],
55372	"fingerfood|fastfood|finger|fast": ["fingerfood.jpg"],
55373	"burger|Hamburger|Burger": ["burger.jpg","burger1.jpg"],
55374	"kartoffeln|kartoffel|Kartoffelgerichte": ["kartoffeln.jpg"],
55375	"indisch|indische|indien|india": ["indisch.jpg","indisch2.jpg","indisch3.jpg"],
55376	"thai|thailändische|thailändisch": ["thailaendisch.jpg"],
55377	"Omelette|Eier|Omlettes|Omelettes": ["omlette.jpg"],
55378	"dessert|Nachtisch": ["dessert.jpg","dessert2.jpg","dessert3.jpg"],
55379	"auflauf|überbacken|Aufläufe": ["auflauf.jpg","auflauf2.jpg"],
55380	"getraenke|Getränke|Getränk|durstlöscher|Durstlöscher": ["getraenke.jpg","getraenke2.jpg","getraenke3.jpg"],
55381	"chinesich|china|chinesische": ["chinesisch.jpg","chinesisch2.jpg"],
55382	"Geflügel|Hähnchen|hähnchen": ["haenchen.jpg"],
55383	"Sushi|Maki|Sashimi|Tempura|Nigiri|Temaki": ["sushi.jpg"],
55384	"Angebote|Mittagsangebote|Angebot": ["angebote.jpg"],
55385	"Vorspeisen|Vorspeise|Antipasti": ["vorspeise.jpg","vorspeise2.jpg"],
55386	"Menü|Menüs": ["menu.jpg"],
55387	"Reis|Reisgerichte|Rissotto": ["reis.jpg"],
55388	"Wurst|Würstchen|Wurscht": ["wurst.jpg"],
55389	"Veggi|vegetarisch|vegetarische|veget": ["veggi.jpg","veggi2.jpg","veggi3.jpg"],
55390	"Chicken": ["chicken.jpg"],
55391	"Döner": ["doener.jpg","doener2.jpg"],
55392	"Lahmacun|Lamacun|Lamacum": ["lahmacun.jpg"],
55393	"Halloumi": ["halloumi.jpg"],
55394	"Pide|Seele": ["pide.jpg"],
55395	"Grill|Grillgerichte|Platten": ["grill.jpg","grill2.jpg"],
55396	"Aktion|Aktionen|specials|spezial": ["special.jpg"],
55397	"AlForno|Al Forno|Überback": ["alforno.jpg"],
55398	"Ente|Duck": ["ente.jpg"],
55399	"International": ["international.jpg"],
55400	"Afganisch|Afghanisch": ["afghanisch.jpg"],
55401	"Steak": ["steak.jpg","steak2.jpg"],
55402	"Neu": ["neu.jpg"],
55403	"Panini": ["panini.jpg"],
55404	"Suppen|Suppe": ["suppe.jpg"],
55405	"Schnitzel": ["schnitzel.jpg"],
55406	"Cordon|Cordon Bleu": ["cordonbleu.jpg"],
55407	"Süßes|Süßigkeiten": ["suesses.jpg","suesses2.jpg"],
55408	"Saucen|Sauce|Soße|Soßen": ["sauce.jpg"],
55409	"Dip|Dipps": ["dip.jpg"],
55410	"Maki": ["maki.jpg"],
55411	"TeMaki|Te Maki": ["temaki.jpg"],
55412	"Nigiri|Nilgiri": ["nigiri.jpg"],
55413	"Sashimi": ["sashimi.jpg"],
55414	"Nori|Nori Maki": ["norimaki.jpg"],
55415	"Inside Out": ["insideout.jpg"],
55416	"Calzone": ["calzone.jpg","calzone2.jpg"],
55417	"brötchen|klein": ["pizzabroetchen.jpg"],
55418	"frühstück": ["fruehstueck.jpg","fruehstueck2.jpg","fruehstueck3.jpg"],
55419	"türkisch": ["tuerkisch.jpg"],
55420	"asia|asiatisch": ["asiatisch.jpg"],
55421	"Snack|kleiner Hunger": ["snack.jpg"],
55422	"Sekt" : ["sekt.jpg"],
55423	"probier": ["probier.jpg"],
55424	"Bier" : ["bier.jpg"],
55425	"wein|Wein" : ["wein.jpg"],
55426	"lasagne|Lasagne" : ["lasagne.jpg"],
55427	"Pfannen|Pfannengerichte" : ["pfanne.jpg"],
55428	"kids|Kinder" : ["kids.jpg"],
55429	"ciabatta" : ["ciabatta.jpg"],
55430	"hits|des Hauses|Haus" : ["hits.jpg"],
55431	"gemüse|gemuese" : ["gemuese.jpg"],
55432	"top|meistbestellt": ["top10.jpg"],
55433	"eis": ["eis.jpg","eis1.jpg","eis2.jpg"],
55434	"ben|jerry": ["benundjerry.jpg"],
55435	"Haagen dazs|dazs|häagen" : ["haeggen.jpg"],
55436	"balkan": ["balkanfood.jpg"],
55437	"gringo": ["gringofood.jpg"],
55438	"Spare|Ribs": ["spareribs.jpg"],
55439	"wraps|wrap": ["wraps.jpg","wrap.jpg"],
55440	"backofen" : ["backofen.jpg"],
55441	"spezialitäten" : ["spezialitaeten.jpg"],
55442	"kracher" : ["kracher.jpg"],
55443	"muscheln|muschel" : ["muscheln.jpg"],
55444	"stammessen|stamm" : ["stammessen.jpg"],
55445	"tacos|taco|tako" : ["tacos.jpg","tacos1.jpg"],
55446	"party" : ["party1.jpg", "party2.jpg"],
55447	"family|familie" : ["family.jpg"],
55448	"roller" : ["roller.jpg"],
55449	"fladenbrot" : ["fladenbrot.jpg"],
55450	"puten|geschnetzeltes|putengeschnetzeltes" : ["geschnetzeltes.jpg"],
55451	"bagels|bagel" : ["bagel.jpg"],
55452	"deal|deals|empfehlung" : ["deals.jpg","deals2.jpg"],
55453	"gratin" : ["gratin.jpg"],
55454	"spargel" : ["spargel.jpg"],
55455	"gnocchi|gnoc" : ["gnocchi.jpg"],
55456	"doppelt": ["doppelt.jpg"],
55457	"Spaghetti": ["spaghetti.jpg"],
55458	"Tortellini": ["tortellini.jpg"],
55459	"Tagliatelle": ["tagliatelle.jpg"],
55460	"überbacken|ueberbacken|Überback": ["ueberbacken.jpg"],
55461	"Rigatoni": "[rigatoni.jpg]",
55462	"Bruschetta|bruscetta": ["bruschetta.jpg"],
55463	"burritos": ["burritos.jpg"],
55464	"tacchino": ["tacchino.jpg"],
55465	"besteck": ["besteck.jpg"],
55466	"bigroll|roll": ["bigroll.jpg"],
55467	"handroll|hand": ["handroll.jpg"],
55468	"Maki|Makis": ["maki.jpg"],
55469	"Nigiri|Nilgiri": ["nigiri.jpg"],
55470	"Sashimi": ["sashimi.jpg"],
55471	"Nori|Nori": ["norimaki.jpg"],
55472	"Inside|Out|Inside out": ["insideout.jpg"],
55473	"bento|Bentoboxen|bentos": ["bentobox.jpg"],
55474	"boxen": ["boxen.jpg"],
55475	"manti|manty": ["manti.jpg"],
55476	"Sushi|Maki|Sashimi|Tempura|Nigiri|Temaki": ["sushi.jpg"]
55477};
55478
55479function jumpTo(lon, lat, zoom) {
55480    var x = Lon2Merc(lon);
55481    var y = Lat2Merc(lat);
55482    map.setCenter(new OpenLayers.LonLat(x, y), zoom);
55483    return false;
55484}
55485
55486function Lon2Merc(lon) {
55487    return 20037508.34 * lon / 180;
55488}
55489
55490function Lat2Merc(lat) {
55491    var PI = 3.14159265358979323846;
55492    lat = Math.log(Math.tan( (90 + lat) * PI / 360)) / (PI / 180);
55493    return 20037508.34 * lat / 180;
55494}
55495
55496function addMarker(layer, lon, lat, popupContentHTML) {
55497
55498    var ll = new OpenLayers.LonLat(Lon2Merc(lon), Lat2Merc(lat));
55499    var feature = new OpenLayers.Feature(layer, ll); 
55500    feature.closeBox = true;
55501    feature.popupClass = OpenLayers.Class(OpenLayers.Popup.FramedCloud, {minSize: new OpenLayers.Size(300, 180) } );
55502    feature.data.popupContentHTML = popupContentHTML;
55503    feature.data.overflow = "hidden";
55504            
55505    var marker = new OpenLayers.Marker(ll);
55506    marker.feature = feature;
55507
55508    var markerClick = function(evt) {
55509        if (this.popup == null) {
55510            this.popup = this.createPopup(this.closeBox);
55511            map.addPopup(this.popup);
55512            this.popup.show();
55513        } else {
55514            this.popup.toggle();
55515        }
55516        OpenLayers.Event.stop(evt);
55517    };
55518    marker.events.register("mousedown", feature, markerClick);
55519
55520    layer.addMarker(marker);
55521    map.addPopup(feature.createPopup(feature.closeBox));
55522}
55523
55524function getCycleTileURL(bounds) {
55525   var res = this.map.getResolution();
55526   var x = Math.round((bounds.left - this.maxExtent.left) / (res * this.tileSize.w));
55527   var y = Math.round((this.maxExtent.top - bounds.top) / (res * this.tileSize.h));
55528   var z = this.map.getZoom();
55529   var limit = Math.pow(2, z);
55530
55531   if (y < 0 || y >= limit)
55532   {
55533     return null;
55534   }
55535   else
55536   {
55537     x = ((x % limit) + limit) % limit;
55538
55539     return this.url + z + "/" + x + "/" + y + "." + this.type;
55540   }
55541}
55542
55543document.addEventListener("DOMContentLoaded", function() {
55544    // Überprüfen, ob das Endgerät Android ist
55545    var userAgent = navigator.userAgent.toLowerCase();
55546    var isAndroid = userAgent.indexOf("android") > -1;
55547    var isIOS = /iphone|ipad|ipod/i.test(userAgent);
55548
55549    if (isAndroid || isIOS) {
55550        // Element sichtbar machen
55551        document.getElementById("app-store-header").style.display = "flex";
55552
55553        if (isAndroid) {
55554            document.getElementById("app-store-header-link-android").style.display = "initial";
55555            document.getElementById("app-store-header-link-apple").style.display = "none";
55556        } else if (isIOS) {
55557            document.getElementById("app-store-header-link-apple").style.display = "initial";
55558            document.getElementById("app-store-header-link-android").style.display = "none";
55559        }
55560    } else {
55561        // Element verstecken
55562        document.getElementById("app-store-header").style.display = "none";
55563    }
55564});

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.