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> </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, "'") + "'" : "") + // cell title 10449 (unselectable ? "" : " data-handler='selectDay' data-event='click' data-month='" + printDate.getMonth() + "' data-year='" + printDate.getFullYear() + "'") + ">" + // actions 10450 (otherMonth && !showOtherMonths ? " " : // 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) ? " " : ""); 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) ? " " : "") + 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( " " ); 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( " " ); 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>▲</span>" + 15629 "</a>" + 15630 "<a>" + 15631 "<span>▼</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(/</,'<').replace(/>/,'>')); 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)) 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 (<, >) with &lt; and &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="Explicitly trusted HTML bypasses ' + 34337 * 'sanitization."">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="Explicitly trusted HTML bypasses ' + 34363 * 'sanitization."">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}} 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=" " 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 * /* this is just another form of hiding an element */ 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 * /* this is required as of 1.3x to properly 44815 * apply all styling in a show/hide animation */ 44816 * transition: 0s linear all; 44817 * } 44818 * 44819 * .my-element.ng-hide-add-active, 44820 * .my-element.ng-hide-remove-active { 44821 * /* the transition is defined in the active class */ 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 * /* this is just another form of hiding an element */ 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><div ng-bind-html="snippet"><br/></div></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><div ng-bind-html="deliberatelyTrustDangerousSnippet()"> 46900</div></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><div ng-bind="snippet"><br/></div></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("<p style=\"color:blue\">an html\n" + 46929 "<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" + 46930 "snippet</p>"); 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 <b onclick=\"alert(1)\">text</b>"); 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,"<"); 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, '&'). 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, '<'). 47262 replace(/>/g, '>'); 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><div ng-bind-html="snippet | linky"><br></div></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><div ng-bind-html="snippetWithTarget | linky:'_blank'"><br></div></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><div ng-bind="snippet"><br></div></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, '"'), 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+'">×</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">×</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> </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"> </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> </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"> </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.