[FileItem] Fix mimetype content lookup
[xbmc.git] / addons / webinterface.default / js / kodi-webinterface.js
blob2a584e7ac6bef2101d9070787006c9b0561b4f9b
1 /*! Chorus 2 - A web interface for Kodi. Created by Jeremy Graham - built on 23-03-2024 */
3 !function(t,e){"object"==typeof module&&"object"==typeof module.exports?module.exports=t.document?e(t,!0):function(t){if(!t.document)throw new Error("jQuery requires a window with a document");return e(t)}:e(t)}("undefined"!=typeof window?window:this,function(p,t){function e(t,e){return e.toUpperCase()}var h=[],c=h.slice,g=h.concat,a=h.push,r=h.indexOf,n={},i=n.toString,v=n.hasOwnProperty,m={},o="1.11.1",S=function(t,e){return new S.fn.init(t,e)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,l=/^-ms-/,u=/-([\da-z])/gi;function d(t){var e=t.length,n=S.type(t);return"function"!==n&&!S.isWindow(t)&&(!(1!==t.nodeType||!e)||("array"===n||0===e||"number"==typeof e&&0<e&&e-1 in t))}S.fn=S.prototype={jquery:o,constructor:S,selector:"",length:0,toArray:function(){return c.call(this)},get:function(t){return null!=t?t<0?this[t+this.length]:this[t]:c.call(this)},pushStack:function(t){var e=S.merge(this.constructor(),t);return e.prevObject=this,e.context=this.context,e},each:function(t,e){return S.each(this,t,e)},map:function(n){return this.pushStack(S.map(this,function(t,e){return n.call(t,e,t)}))},slice:function(){return this.pushStack(c.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(t){var e=this.length,n=+t+(t<0?e:0);return this.pushStack(0<=n&&n<e?[this[n]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:a,sort:h.sort,splice:h.splice},S.extend=S.fn.extend=function(){var t,e,n,i,r,o,s=arguments[0]||{},a=1,l=arguments.length,u=!1;for("boolean"==typeof s&&(u=s,s=arguments[a]||{},a++),"object"==typeof s||S.isFunction(s)||(s={}),a===l&&(s=this,a--);a<l;a++)if(null!=(r=arguments[a]))for(i in r)t=s[i],s!==(n=r[i])&&(u&&n&&(S.isPlainObject(n)||(e=S.isArray(n)))?(o=e?(e=!1,t&&S.isArray(t)?t:[]):t&&S.isPlainObject(t)?t:{},s[i]=S.extend(u,o,n)):void 0!==n&&(s[i]=n));return s},S.extend({expando:"jQuery"+(o+Math.random()).replace(/\D/g,""),isReady:!0,error:function(t){throw new Error(t)},noop:function(){},isFunction:function(t){return"function"===S.type(t)},isArray:Array.isArray||function(t){return"array"===S.type(t)},isWindow:function(t){return null!=t&&t==t.window},isNumeric:function(t){return!S.isArray(t)&&0<=t-parseFloat(t)},isEmptyObject:function(t){var e;for(e in t)return!1;return!0},isPlainObject:function(t){var e;if(!t||"object"!==S.type(t)||t.nodeType||S.isWindow(t))return!1;try{if(t.constructor&&!v.call(t,"constructor")&&!v.call(t.constructor.prototype,"isPrototypeOf"))return!1}catch(t){return!1}if(m.ownLast)for(e in t)return v.call(t,e);for(e in t);return void 0===e||v.call(t,e)},type:function(t){return null==t?t+"":"object"==typeof t||"function"==typeof t?n[i.call(t)]||"object":typeof t},globalEval:function(t){t&&S.trim(t)&&(p.execScript||function(t){p.eval.call(p,t)})(t)},camelCase:function(t){return t.replace(l,"ms-").replace(u,e)},nodeName:function(t,e){return t.nodeName&&t.nodeName.toLowerCase()===e.toLowerCase()},each:function(t,e,n){var i=0,r=t.length,o=d(t);if(n){if(o)for(;i<r&&!1!==e.apply(t[i],n);i++);else for(i in t)if(!1===e.apply(t[i],n))break}else if(o)for(;i<r&&!1!==e.call(t[i],i,t[i]);i++);else for(i in t)if(!1===e.call(t[i],i,t[i]))break;return t},trim:function(t){return null==t?"":(t+"").replace(s,"")},makeArray:function(t,e){var n=e||[];return null!=t&&(d(Object(t))?S.merge(n,"string"==typeof t?[t]:t):a.call(n,t)),n},inArray:function(t,e,n){var i;if(e){if(r)return r.call(e,t,n);for(i=e.length,n=n?n<0?Math.max(0,i+n):n:0;n<i;n++)if(n in e&&e[n]===t)return n}return-1},merge:function(t,e){for(var n=+e.length,i=0,r=t.length;i<n;)t[r++]=e[i++];if(n!=n)for(;void 0!==e[i];)t[r++]=e[i++];return t.length=r,t},grep:function(t,e,n){for(var i=[],r=0,o=t.length,s=!n;r<o;r++)!e(t[r],r)!=s&&i.push(t[r]);return i},map:function(t,e,n){var i,r=0,o=t.length,s=[];if(d(t))for(;r<o;r++)null!=(i=e(t[r],r,n))&&s.push(i);else for(r in t)null!=(i=e(t[r],r,n))&&s.push(i);return g.apply([],s)},guid:1,proxy:function(t,e){var n,i,r;if("string"==typeof e&&(r=t[e],e=t,t=r),S.isFunction(t))return n=c.call(arguments,2),(i=function(){return t.apply(e||this,n.concat(c.call(arguments)))}).guid=t.guid=t.guid||S.guid++,i},now:function(){return+new Date},support:m}),S.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(t,e){n["[object "+e+"]"]=e.toLowerCase()});var f=function(n){function h(t,e,n){var i="0x"+e-65536;return i!=i||n?e:i<0?String.fromCharCode(65536+i):String.fromCharCode(i>>10|55296,1023&i|56320)}var t,p,w,o,i,g,d,v,_,u,c,m,x,r,y,b,s,a,S,T="sizzle"+-new Date,C=n.document,E=0,f=0,l=rt(),k=rt(),M=rt(),D=function(t,e){return t===e&&(c=!0),0},O="undefined",A={}.hasOwnProperty,e=[],N=e.pop,j=e.push,R=e.push,L=e.slice,I=e.indexOf||function(t){for(var e=0,n=this.length;e<n;e++)if(this[e]===t)return e;return-1},P="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",F="[\\x20\\t\\r\\n\\f]",H="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",q=H.replace("w","w#"),$="\\["+F+"*("+H+")(?:"+F+"*([*^$|!~]?=)"+F+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+q+"))|)"+F+"*\\]",U=":("+H+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+$+")*)|.*)\\)|)",B=new RegExp("^"+F+"+|((?:^|[^\\\\])(?:\\\\.)*)"+F+"+$","g"),V=new RegExp("^"+F+"*,"+F+"*"),z=new RegExp("^"+F+"*([>+~]|"+F+")"+F+"*"),W=new RegExp("="+F+"*([^\\]'\"]*?)"+F+"*\\]","g"),J=new RegExp(U),G=new RegExp("^"+q+"$"),X={ID:new RegExp("^#("+H+")"),CLASS:new RegExp("^\\.("+H+")"),TAG:new RegExp("^("+H.replace("w","w*")+")"),ATTR:new RegExp("^"+$),PSEUDO:new RegExp("^"+U),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+F+"*(even|odd|(([+-]|)(\\d*)n|)"+F+"*(?:([+-]|)"+F+"*(\\d+)|))"+F+"*\\)|)","i"),bool:new RegExp("^(?:"+P+")$","i"),needsContext:new RegExp("^"+F+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+F+"*((?:-\\d)?\\d*)"+F+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,K=/^h\d$/i,Q=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,tt=/[+~]/,et=/'|\\/g,nt=new RegExp("\\\\([\\da-f]{1,6}"+F+"?|("+F+")|.)","ig");try{R.apply(e=L.call(C.childNodes),C.childNodes),e[C.childNodes.length].nodeType}catch(t){R={apply:e.length?function(t,e){j.apply(t,L.call(e))}:function(t,e){for(var n=t.length,i=0;t[n++]=e[i++];);t.length=n-1}}}function it(t,e,n,i){var r,o,s,a,l,u,c,h,d,f;if((e?e.ownerDocument||e:C)!==x&&m(e),n=n||[],!t||"string"!=typeof t)return n;if(1!==(a=(e=e||x).nodeType)&&9!==a)return[];if(y&&!i){if(r=Z.exec(t))if(s=r[1]){if(9===a){if(!(o=e.getElementById(s))||!o.parentNode)return n;if(o.id===s)return n.push(o),n}else if(e.ownerDocument&&(o=e.ownerDocument.getElementById(s))&&S(e,o)&&o.id===s)return n.push(o),n}else{if(r[2])return R.apply(n,e.getElementsByTagName(t)),n;if((s=r[3])&&p.getElementsByClassName&&e.getElementsByClassName)return R.apply(n,e.getElementsByClassName(s)),n}if(p.qsa&&(!b||!b.test(t))){if(h=c=T,d=e,f=9===a&&t,1===a&&"object"!==e.nodeName.toLowerCase()){for(u=g(t),(c=e.getAttribute("id"))?h=c.replace(et,"\\$&"):e.setAttribute("id",h),h="[id='"+h+"'] ",l=u.length;l--;)u[l]=h+pt(u[l]);d=tt.test(t)&&dt(e.parentNode)||e,f=u.join(",")}if(f)try{return R.apply(n,d.querySelectorAll(f)),n}catch(t){}finally{c||e.removeAttribute("id")}}}return v(t.replace(B,"$1"),e,n,i)}function rt(){var i=[];return function t(e,n){return i.push(e+" ")>w.cacheLength&&delete t[i.shift()],t[e+" "]=n}}function ot(t){return t[T]=!0,t}function st(t){var e=x.createElement("div");try{return!!t(e)}catch(t){return!1}finally{e.parentNode&&e.parentNode.removeChild(e),e=null}}function at(t,e){for(var n=t.split("|"),i=t.length;i--;)w.attrHandle[n[i]]=e}function lt(t,e){var n=e&&t,i=n&&1===t.nodeType&&1===e.nodeType&&(~e.sourceIndex||1<<31)-(~t.sourceIndex||1<<31);if(i)return i;if(n)for(;n=n.nextSibling;)if(n===e)return-1;return t?1:-1}function ut(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}function ct(n){return function(t){var e=t.nodeName.toLowerCase();return("input"===e||"button"===e)&&t.type===n}}function ht(s){return ot(function(o){return o=+o,ot(function(t,e){for(var n,i=s([],t.length,o),r=i.length;r--;)t[n=i[r]]&&(t[n]=!(e[n]=t[n]))})})}function dt(t){return t&&typeof t.getElementsByTagName!==O&&t}for(t in p=it.support={},i=it.isXML=function(t){var e=t&&(t.ownerDocument||t).documentElement;return!!e&&"HTML"!==e.nodeName},m=it.setDocument=function(t){var e,l=t?t.ownerDocument||t:C,n=l.defaultView;return l!==x&&9===l.nodeType&&l.documentElement?(r=(x=l).documentElement,y=!i(l),n&&n!==n.top&&(n.addEventListener?n.addEventListener("unload",function(){m()},!1):n.attachEvent&&n.attachEvent("onunload",function(){m()})),p.attributes=st(function(t){return t.className="i",!t.getAttribute("className")}),p.getElementsByTagName=st(function(t){return t.appendChild(l.createComment("")),!t.getElementsByTagName("*").length}),p.getElementsByClassName=Q.test(l.getElementsByClassName)&&st(function(t){return t.innerHTML="<div class='a'></div><div class='a i'></div>",t.firstChild.className="i",2===t.getElementsByClassName("i").length}),p.getById=st(function(t){return r.appendChild(t).id=T,!l.getElementsByName||!l.getElementsByName(T).length}),p.getById?(w.find.ID=function(t,e){if(typeof e.getElementById!==O&&y){var n=e.getElementById(t);return n&&n.parentNode?[n]:[]}},w.filter.ID=function(t){var e=t.replace(nt,h);return function(t){return t.getAttribute("id")===e}}):(delete w.find.ID,w.filter.ID=function(t){var n=t.replace(nt,h);return function(t){var e=typeof t.getAttributeNode!==O&&t.getAttributeNode("id");return e&&e.value===n}}),w.find.TAG=p.getElementsByTagName?function(t,e){if(typeof e.getElementsByTagName!==O)return e.getElementsByTagName(t)}:function(t,e){var n,i=[],r=0,o=e.getElementsByTagName(t);if("*"!==t)return o;for(;n=o[r++];)1===n.nodeType&&i.push(n);return i},w.find.CLASS=p.getElementsByClassName&&function(t,e){if(typeof e.getElementsByClassName!==O&&y)return e.getElementsByClassName(t)},s=[],b=[],(p.qsa=Q.test(l.querySelectorAll))&&(st(function(t){t.innerHTML="<select msallowclip=''><option selected=''></option></select>",t.querySelectorAll("[msallowclip^='']").length&&b.push("[*^$]="+F+"*(?:''|\"\")"),t.querySelectorAll("[selected]").length||b.push("\\["+F+"*(?:value|"+P+")"),t.querySelectorAll(":checked").length||b.push(":checked")}),st(function(t){var e=l.createElement("input");e.setAttribute("type","hidden"),t.appendChild(e).setAttribute("name","D"),t.querySelectorAll("[name=d]").length&&b.push("name"+F+"*[*^$|!~]?="),t.querySelectorAll(":enabled").length||b.push(":enabled",":disabled"),t.querySelectorAll("*,:x"),b.push(",.*:")})),(p.matchesSelector=Q.test(a=r.matches||r.webkitMatchesSelector||r.mozMatchesSelector||r.oMatchesSelector||r.msMatchesSelector))&&st(function(t){p.disconnectedMatch=a.call(t,"div"),a.call(t,"[s!='']:x"),s.push("!=",U)}),b=b.length&&new RegExp(b.join("|")),s=s.length&&new RegExp(s.join("|")),e=Q.test(r.compareDocumentPosition),S=e||Q.test(r.contains)?function(t,e){var n=9===t.nodeType?t.documentElement:t,i=e&&e.parentNode;return t===i||!(!i||1!==i.nodeType||!(n.contains?n.contains(i):t.compareDocumentPosition&&16&t.compareDocumentPosition(i)))}:function(t,e){if(e)for(;e=e.parentNode;)if(e===t)return!0;return!1},D=e?function(t,e){if(t===e)return c=!0,0;var n=!t.compareDocumentPosition-!e.compareDocumentPosition;return n||(1&(n=(t.ownerDocument||t)===(e.ownerDocument||e)?t.compareDocumentPosition(e):1)||!p.sortDetached&&e.compareDocumentPosition(t)===n?t===l||t.ownerDocument===C&&S(C,t)?-1:e===l||e.ownerDocument===C&&S(C,e)?1:u?I.call(u,t)-I.call(u,e):0:4&n?-1:1)}:function(t,e){if(t===e)return c=!0,0;var n,i=0,r=t.parentNode,o=e.parentNode,s=[t],a=[e];if(!r||!o)return t===l?-1:e===l?1:r?-1:o?1:u?I.call(u,t)-I.call(u,e):0;if(r===o)return lt(t,e);for(n=t;n=n.parentNode;)s.unshift(n);for(n=e;n=n.parentNode;)a.unshift(n);for(;s[i]===a[i];)i++;return i?lt(s[i],a[i]):s[i]===C?-1:a[i]===C?1:0},l):x},it.matches=function(t,e){return it(t,null,null,e)},it.matchesSelector=function(t,e){if((t.ownerDocument||t)!==x&&m(t),e=e.replace(W,"='$1']"),p.matchesSelector&&y&&(!s||!s.test(e))&&(!b||!b.test(e)))try{var n=a.call(t,e);if(n||p.disconnectedMatch||t.document&&11!==t.document.nodeType)return n}catch(t){}return 0<it(e,x,null,[t]).length},it.contains=function(t,e){return(t.ownerDocument||t)!==x&&m(t),S(t,e)},it.attr=function(t,e){(t.ownerDocument||t)!==x&&m(t);var n=w.attrHandle[e.toLowerCase()],i=n&&A.call(w.attrHandle,e.toLowerCase())?n(t,e,!y):void 0;return void 0!==i?i:p.attributes||!y?t.getAttribute(e):(i=t.getAttributeNode(e))&&i.specified?i.value:null},it.error=function(t){throw new Error("Syntax error, unrecognized expression: "+t)},it.uniqueSort=function(t){var e,n=[],i=0,r=0;if(c=!p.detectDuplicates,u=!p.sortStable&&t.slice(0),t.sort(D),c){for(;e=t[r++];)e===t[r]&&(i=n.push(r));for(;i--;)t.splice(n[i],1)}return u=null,t},o=it.getText=function(t){var e,n="",i=0,r=t.nodeType;if(r){if(1===r||9===r||11===r){if("string"==typeof t.textContent)return t.textContent;for(t=t.firstChild;t;t=t.nextSibling)n+=o(t)}else if(3===r||4===r)return t.nodeValue}else for(;e=t[i++];)n+=o(e);return n},(w=it.selectors={cacheLength:50,createPseudo:ot,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(t){return t[1]=t[1].replace(nt,h),t[3]=(t[3]||t[4]||t[5]||"").replace(nt,h),"~="===t[2]&&(t[3]=" "+t[3]+" "),t.slice(0,4)},CHILD:function(t){return t[1]=t[1].toLowerCase(),"nth"===t[1].slice(0,3)?(t[3]||it.error(t[0]),t[4]=+(t[4]?t[5]+(t[6]||1):2*("even"===t[3]||"odd"===t[3])),t[5]=+(t[7]+t[8]||"odd"===t[3])):t[3]&&it.error(t[0]),t},PSEUDO:function(t){var e,n=!t[6]&&t[2];return X.CHILD.test(t[0])?null:(t[3]?t[2]=t[4]||t[5]||"":n&&J.test(n)&&(e=g(n,!0))&&(e=n.indexOf(")",n.length-e)-n.length)&&(t[0]=t[0].slice(0,e),t[2]=n.slice(0,e)),t.slice(0,3))}},filter:{TAG:function(t){var e=t.replace(nt,h).toLowerCase();return"*"===t?function(){return!0}:function(t){return t.nodeName&&t.nodeName.toLowerCase()===e}},CLASS:function(t){var e=l[t+" "];return e||(e=new RegExp("(^|"+F+")"+t+"("+F+"|$)"))&&l(t,function(t){return e.test("string"==typeof t.className&&t.className||typeof t.getAttribute!==O&&t.getAttribute("class")||"")})},ATTR:function(n,i,r){return function(t){var e=it.attr(t,n);return null==e?"!="===i:!i||(e+="","="===i?e===r:"!="===i?e!==r:"^="===i?r&&0===e.indexOf(r):"*="===i?r&&-1<e.indexOf(r):"$="===i?r&&e.slice(-r.length)===r:"~="===i?-1<(" "+e+" ").indexOf(r):"|="===i&&(e===r||e.slice(0,r.length+1)===r+"-"))}},CHILD:function(f,t,e,p,g){var v="nth"!==f.slice(0,3),m="last"!==f.slice(-4),y="of-type"===t;return 1===p&&0===g?function(t){return!!t.parentNode}:function(t,e,n){var i,r,o,s,a,l,u=v!=m?"nextSibling":"previousSibling",c=t.parentNode,h=y&&t.nodeName.toLowerCase(),d=!n&&!y;if(c){if(v){for(;u;){for(o=t;o=o[u];)if(y?o.nodeName.toLowerCase()===h:1===o.nodeType)return!1;l=u="only"===f&&!l&&"nextSibling"}return!0}if(l=[m?c.firstChild:c.lastChild],m&&d){for(a=(i=(r=c[T]||(c[T]={}))[f]||[])[0]===E&&i[1],s=i[0]===E&&i[2],o=a&&c.childNodes[a];o=++a&&o&&o[u]||(s=a=0)||l.pop();)if(1===o.nodeType&&++s&&o===t){r[f]=[E,a,s];break}}else if(d&&(i=(t[T]||(t[T]={}))[f])&&i[0]===E)s=i[1];else for(;(o=++a&&o&&o[u]||(s=a=0)||l.pop())&&((y?o.nodeName.toLowerCase()!==h:1!==o.nodeType)||!++s||(d&&((o[T]||(o[T]={}))[f]=[E,s]),o!==t)););return(s-=g)===p||s%p==0&&0<=s/p}}},PSEUDO:function(t,o){var e,s=w.pseudos[t]||w.setFilters[t.toLowerCase()]||it.error("unsupported pseudo: "+t);return s[T]?s(o):1<s.length?(e=[t,t,"",o],w.setFilters.hasOwnProperty(t.toLowerCase())?ot(function(t,e){for(var n,i=s(t,o),r=i.length;r--;)t[n=I.call(t,i[r])]=!(e[n]=i[r])}):function(t){return s(t,0,e)}):s}},pseudos:{not:ot(function(t){var i=[],r=[],a=d(t.replace(B,"$1"));return a[T]?ot(function(t,e,n,i){for(var r,o=a(t,null,i,[]),s=t.length;s--;)(r=o[s])&&(t[s]=!(e[s]=r))}):function(t,e,n){return i[0]=t,a(i,null,n,r),!r.pop()}}),has:ot(function(e){return function(t){return 0<it(e,t).length}}),contains:ot(function(e){return function(t){return-1<(t.textContent||t.innerText||o(t)).indexOf(e)}}),lang:ot(function(n){return G.test(n||"")||it.error("unsupported lang: "+n),n=n.replace(nt,h).toLowerCase(),function(t){var e;do{if(e=y?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(e=e.toLowerCase())===n||0===e.indexOf(n+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var e=n.location&&n.location.hash;return e&&e.slice(1)===t.id},root:function(t){return t===r},focus:function(t){return t===x.activeElement&&(!x.hasFocus||x.hasFocus())&&!!(t.type||t.href||~t.tabIndex)},enabled:function(t){return!1===t.disabled},disabled:function(t){return!0===t.disabled},checked:function(t){var e=t.nodeName.toLowerCase();return"input"===e&&!!t.checked||"option"===e&&!!t.selected},selected:function(t){return t.parentNode&&t.parentNode.selectedIndex,!0===t.selected},empty:function(t){for(t=t.firstChild;t;t=t.nextSibling)if(t.nodeType<6)return!1;return!0},parent:function(t){return!w.pseudos.empty(t)},header:function(t){return K.test(t.nodeName)},input:function(t){return Y.test(t.nodeName)},button:function(t){var e=t.nodeName.toLowerCase();return"input"===e&&"button"===t.type||"button"===e},text:function(t){var e;return"input"===t.nodeName.toLowerCase()&&"text"===t.type&&(null==(e=t.getAttribute("type"))||"text"===e.toLowerCase())},first:ht(function(){return[0]}),last:ht(function(t,e){return[e-1]}),eq:ht(function(t,e,n){return[n<0?n+e:n]}),even:ht(function(t,e){for(var n=0;n<e;n+=2)t.push(n);return t}),odd:ht(function(t,e){for(var n=1;n<e;n+=2)t.push(n);return t}),lt:ht(function(t,e,n){for(var i=n<0?n+e:n;0<=--i;)t.push(i);return t}),gt:ht(function(t,e,n){for(var i=n<0?n+e:n;++i<e;)t.push(i);return t})}}).pseudos.nth=w.pseudos.eq,{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})w.pseudos[t]=ut(t);for(t in{submit:!0,reset:!0})w.pseudos[t]=ct(t);function ft(){}function pt(t){for(var e=0,n=t.length,i="";e<n;e++)i+=t[e].value;return i}function gt(s,t,e){var a=t.dir,l=e&&"parentNode"===a,u=f++;return t.first?function(t,e,n){for(;t=t[a];)if(1===t.nodeType||l)return s(t,e,n)}:function(t,e,n){var i,r,o=[E,u];if(n){for(;t=t[a];)if((1===t.nodeType||l)&&s(t,e,n))return!0}else for(;t=t[a];)if(1===t.nodeType||l){if((i=(r=t[T]||(t[T]={}))[a])&&i[0]===E&&i[1]===u)return o[2]=i[2];if((r[a]=o)[2]=s(t,e,n))return!0}}}function vt(r){return 1<r.length?function(t,e,n){for(var i=r.length;i--;)if(!r[i](t,e,n))return!1;return!0}:r[0]}function mt(t,e,n,i,r){for(var o,s=[],a=0,l=t.length,u=null!=e;a<l;a++)(o=t[a])&&(n&&!n(o,i,r)||(s.push(o),u&&e.push(a)));return s}function yt(f,p,g,v,m,t){return v&&!v[T]&&(v=yt(v)),m&&!m[T]&&(m=yt(m,t)),ot(function(t,e,n,i){var r,o,s,a=[],l=[],u=e.length,c=t||function(t,e,n){for(var i=0,r=e.length;i<r;i++)it(t,e[i],n);return n}(p||"*",n.nodeType?[n]:n,[]),h=!f||!t&&p?c:mt(c,a,f,n,i),d=g?m||(t?f:u||v)?[]:e:h;if(g&&g(h,d,n,i),v)for(r=mt(d,l),v(r,[],n,i),o=r.length;o--;)(s=r[o])&&(d[l[o]]=!(h[l[o]]=s));if(t){if(m||f){if(m){for(r=[],o=d.length;o--;)(s=d[o])&&r.push(h[o]=s);m(null,d=[],r,i)}for(o=d.length;o--;)(s=d[o])&&-1<(r=m?I.call(t,s):a[o])&&(t[r]=!(e[r]=s))}}else d=mt(d===e?d.splice(u,d.length):d),m?m(null,e,d,i):R.apply(e,d)})}function bt(t){for(var i,e,n,r=t.length,o=w.relative[t[0].type],s=o||w.relative[" "],a=o?1:0,l=gt(function(t){return t===i},s,!0),u=gt(function(t){return-1<I.call(i,t)},s,!0),c=[function(t,e,n){return!o&&(n||e!==_)||((i=e).nodeType?l(t,e,n):u(t,e,n))}];a<r;a++)if(e=w.relative[t[a].type])c=[gt(vt(c),e)];else{if((e=w.filter[t[a].type].apply(null,t[a].matches))[T]){for(n=++a;n<r&&!w.relative[t[n].type];n++);return yt(1<a&&vt(c),1<a&&pt(t.slice(0,a-1).concat({value:" "===t[a-2].type?"*":""})).replace(B,"$1"),e,a<n&&bt(t.slice(a,n)),n<r&&bt(t=t.slice(n)),n<r&&pt(t))}c.push(e)}return vt(c)}function wt(v,m){function t(t,e,n,i,r){var o,s,a,l=0,u="0",c=t&&[],h=[],d=_,f=t||b&&w.find.TAG("*",r),p=E+=null==d?1:Math.random()||.1,g=f.length;for(r&&(_=e!==x&&e);u!==g&&null!=(o=f[u]);u++){if(b&&o){for(s=0;a=v[s++];)if(a(o,e,n)){i.push(o);break}r&&(E=p)}y&&((o=!a&&o)&&l--,t&&c.push(o))}if(l+=u,y&&u!==l){for(s=0;a=m[s++];)a(c,h,e,n);if(t){if(0<l)for(;u--;)c[u]||h[u]||(h[u]=N.call(i));h=mt(h)}R.apply(i,h),r&&!t&&0<h.length&&1<l+m.length&&it.uniqueSort(i)}return r&&(E=p,_=d),c}var y=0<m.length,b=0<v.length;return y?ot(t):t}return ft.prototype=w.filters=w.pseudos,w.setFilters=new ft,g=it.tokenize=function(t,e){var n,i,r,o,s,a,l,u=k[t+" "];if(u)return e?0:u.slice(0);for(s=t,a=[],l=w.preFilter;s;){for(o in n&&!(i=V.exec(s))||(i&&(s=s.slice(i[0].length)||s),a.push(r=[])),n=!1,(i=z.exec(s))&&(n=i.shift(),r.push({value:n,type:i[0].replace(B," ")}),s=s.slice(n.length)),w.filter)!(i=X[o].exec(s))||l[o]&&!(i=l[o](i))||(n=i.shift(),r.push({value:n,type:o,matches:i}),s=s.slice(n.length));if(!n)break}return e?s.length:s?it.error(t):k(t,a).slice(0)},d=it.compile=function(t,e){var n,i=[],r=[],o=M[t+" "];if(!o){for(n=(e=e||g(t)).length;n--;)(o=bt(e[n]))[T]?i.push(o):r.push(o);(o=M(t,wt(r,i))).selector=t}return o},v=it.select=function(t,e,n,i){var r,o,s,a,l,u="function"==typeof t&&t,c=!i&&g(t=u.selector||t);if(n=n||[],1===c.length){if(2<(o=c[0]=c[0].slice(0)).length&&"ID"===(s=o[0]).type&&p.getById&&9===e.nodeType&&y&&w.relative[o[1].type]){if(!(e=(w.find.ID(s.matches[0].replace(nt,h),e)||[])[0]))return n;u&&(e=e.parentNode),t=t.slice(o.shift().value.length)}for(r=X.needsContext.test(t)?0:o.length;r--&&(s=o[r],!w.relative[a=s.type]);)if((l=w.find[a])&&(i=l(s.matches[0].replace(nt,h),tt.test(o[0].type)&&dt(e.parentNode)||e))){if(o.splice(r,1),!(t=i.length&&pt(o)))return R.apply(n,i),n;break}}return(u||d(t,c))(i,e,!y,n,tt.test(t)&&dt(e.parentNode)||e),n},p.sortStable=T.split("").sort(D).join("")===T,p.detectDuplicates=!!c,m(),p.sortDetached=st(function(t){return 1&t.compareDocumentPosition(x.createElement("div"))}),st(function(t){return t.innerHTML="<a href='#'></a>","#"===t.firstChild.getAttribute("href")})||at("type|href|height|width",function(t,e,n){if(!n)return t.getAttribute(e,"type"===e.toLowerCase()?1:2)}),p.attributes&&st(function(t){return t.innerHTML="<input/>",t.firstChild.setAttribute("value",""),""===t.firstChild.getAttribute("value")})||at("value",function(t,e,n){if(!n&&"input"===t.nodeName.toLowerCase())return t.defaultValue}),st(function(t){return null==t.getAttribute("disabled")})||at(P,function(t,e,n){var i;if(!n)return!0===t[e]?e.toLowerCase():(i=t.getAttributeNode(e))&&i.specified?i.value:null}),it}(p);S.find=f,S.expr=f.selectors,S.expr[":"]=S.expr.pseudos,S.unique=f.uniqueSort,S.text=f.getText,S.isXMLDoc=f.isXML,S.contains=f.contains;var y=S.expr.match.needsContext,b=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^.[^:#\[\.,]*$/;function _(t,n,i){if(S.isFunction(n))return S.grep(t,function(t,e){return!!n.call(t,e,t)!==i});if(n.nodeType)return S.grep(t,function(t){return t===n!==i});if("string"==typeof n){if(w.test(n))return S.filter(n,t,i);n=S.filter(n,t)}return S.grep(t,function(t){return 0<=S.inArray(t,n)!==i})}S.filter=function(t,e,n){var i=e[0];return n&&(t=":not("+t+")"),1===e.length&&1===i.nodeType?S.find.matchesSelector(i,t)?[i]:[]:S.find.matches(t,S.grep(e,function(t){return 1===t.nodeType}))},S.fn.extend({find:function(t){var e,n=[],i=this,r=i.length;if("string"!=typeof t)return this.pushStack(S(t).filter(function(){for(e=0;e<r;e++)if(S.contains(i[e],this))return!0}));for(e=0;e<r;e++)S.find(t,i[e],n);return(n=this.pushStack(1<r?S.unique(n):n)).selector=this.selector?this.selector+" "+t:t,n},filter:function(t){return this.pushStack(_(this,t||[],!1))},not:function(t){return this.pushStack(_(this,t||[],!0))},is:function(t){return!!_(this,"string"==typeof t&&y.test(t)?S(t):t||[],!1).length}});var x,T=p.document,C=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/;(S.fn.init=function(t,e){var n,i;if(!t)return this;if("string"!=typeof t)return t.nodeType?(this.context=this[0]=t,this.length=1,this):S.isFunction(t)?void 0!==x.ready?x.ready(t):t(S):(void 0!==t.selector&&(this.selector=t.selector,this.context=t.context),S.makeArray(t,this));if(!(n="<"===t.charAt(0)&&">"===t.charAt(t.length-1)&&3<=t.length?[null,t,null]:C.exec(t))||!n[1]&&e)return!e||e.jquery?(e||x).find(t):this.constructor(e).find(t);if(n[1]){if(e=e instanceof S?e[0]:e,S.merge(this,S.parseHTML(n[1],e&&e.nodeType?e.ownerDocument||e:T,!0)),b.test(n[1])&&S.isPlainObject(e))for(n in e)S.isFunction(this[n])?this[n](e[n]):this.attr(n,e[n]);return this}if((i=T.getElementById(n[2]))&&i.parentNode){if(i.id!==n[2])return x.find(t);this.length=1,this[0]=i}return this.context=T,this.selector=t,this}).prototype=S.fn,x=S(T);var E=/^(?:parents|prev(?:Until|All))/,k={children:!0,contents:!0,next:!0,prev:!0};function M(t,e){for(;(t=t[e])&&1!==t.nodeType;);return t}S.extend({dir:function(t,e,n){for(var i=[],r=t[e];r&&9!==r.nodeType&&(void 0===n||1!==r.nodeType||!S(r).is(n));)1===r.nodeType&&i.push(r),r=r[e];return i},sibling:function(t,e){for(var n=[];t;t=t.nextSibling)1===t.nodeType&&t!==e&&n.push(t);return n}}),S.fn.extend({has:function(t){var e,n=S(t,this),i=n.length;return this.filter(function(){for(e=0;e<i;e++)if(S.contains(this,n[e]))return!0})},closest:function(t,e){for(var n,i=0,r=this.length,o=[],s=y.test(t)||"string"!=typeof t?S(t,e||this.context):0;i<r;i++)for(n=this[i];n&&n!==e;n=n.parentNode)if(n.nodeType<11&&(s?-1<s.index(n):1===n.nodeType&&S.find.matchesSelector(n,t))){o.push(n);break}return this.pushStack(1<o.length?S.unique(o):o)},index:function(t){return t?"string"==typeof t?S.inArray(this[0],S(t)):S.inArray(t.jquery?t[0]:t,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(t,e){return this.pushStack(S.unique(S.merge(this.get(),S(t,e))))},addBack:function(t){return this.add(null==t?this.prevObject:this.prevObject.filter(t))}}),S.each({parent:function(t){var e=t.parentNode;return e&&11!==e.nodeType?e:null},parents:function(t){return S.dir(t,"parentNode")},parentsUntil:function(t,e,n){return S.dir(t,"parentNode",n)},next:function(t){return M(t,"nextSibling")},prev:function(t){return M(t,"previousSibling")},nextAll:function(t){return S.dir(t,"nextSibling")},prevAll:function(t){return S.dir(t,"previousSibling")},nextUntil:function(t,e,n){return S.dir(t,"nextSibling",n)},prevUntil:function(t,e,n){return S.dir(t,"previousSibling",n)},siblings:function(t){return S.sibling((t.parentNode||{}).firstChild,t)},children:function(t){return S.sibling(t.firstChild)},contents:function(t){return S.nodeName(t,"iframe")?t.contentDocument||t.contentWindow.document:S.merge([],t.childNodes)}},function(i,r){S.fn[i]=function(t,e){var n=S.map(this,r,t);return"Until"!==i.slice(-5)&&(e=t),e&&"string"==typeof e&&(n=S.filter(e,n)),1<this.length&&(k[i]||(n=S.unique(n)),E.test(i)&&(n=n.reverse())),this.pushStack(n)}});var D,O=/\S+/g,A={};function N(){T.addEventListener?(T.removeEventListener("DOMContentLoaded",j,!1),p.removeEventListener("load",j,!1)):(T.detachEvent("onreadystatechange",j),p.detachEvent("onload",j))}function j(){!T.addEventListener&&"load"!==event.type&&"complete"!==T.readyState||(N(),S.ready())}S.Callbacks=function(r){r="string"==typeof r?A[r]||function(t){var n=A[t]={};return S.each(t.match(O)||[],function(t,e){n[e]=!0}),n}(r):S.extend({},r);var i,e,n,o,s,a,l=[],u=!r.once&&[],c=function(t){for(e=r.memory&&t,n=!0,s=a||0,a=0,o=l.length,i=!0;l&&s<o;s++)if(!1===l[s].apply(t[0],t[1])&&r.stopOnFalse){e=!1;break}i=!1,l&&(u?u.length&&c(u.shift()):e?l=[]:h.disable())},h={add:function(){if(l){var t=l.length;!function i(t){S.each(t,function(t,e){var n=S.type(e);"function"===n?r.unique&&h.has(e)||l.push(e):e&&e.length&&"string"!==n&&i(e)})}(arguments),i?o=l.length:e&&(a=t,c(e))}return this},remove:function(){return l&&S.each(arguments,function(t,e){for(var n;-1<(n=S.inArray(e,l,n));)l.splice(n,1),i&&(n<=o&&o--,n<=s&&s--)}),this},has:function(t){return t?-1<S.inArray(t,l):!(!l||!l.length)},empty:function(){return l=[],o=0,this},disable:function(){return l=u=e=void 0,this},disabled:function(){return!l},lock:function(){return u=void 0,e||h.disable(),this},locked:function(){return!u},fireWith:function(t,e){return!l||n&&!u||(e=[t,(e=e||[]).slice?e.slice():e],i?u.push(e):c(e)),this},fire:function(){return h.fireWith(this,arguments),this},fired:function(){return!!n}};return h},S.extend({Deferred:function(t){var o=[["resolve","done",S.Callbacks("once memory"),"resolved"],["reject","fail",S.Callbacks("once memory"),"rejected"],["notify","progress",S.Callbacks("memory")]],r="pending",s={state:function(){return r},always:function(){return a.done(arguments).fail(arguments),this},then:function(){var r=arguments;return S.Deferred(function(i){S.each(o,function(t,e){var n=S.isFunction(r[t])&&r[t];a[e[1]](function(){var t=n&&n.apply(this,arguments);t&&S.isFunction(t.promise)?t.promise().done(i.resolve).fail(i.reject).progress(i.notify):i[e[0]+"With"](this===s?i.promise():this,n?[t]:arguments)})}),r=null}).promise()},promise:function(t){return null!=t?S.extend(t,s):s}},a={};return s.pipe=s.then,S.each(o,function(t,e){var n=e[2],i=e[3];s[e[1]]=n.add,i&&n.add(function(){r=i},o[1^t][2].disable,o[2][2].lock),a[e[0]]=function(){return a[e[0]+"With"](this===a?s:this,arguments),this},a[e[0]+"With"]=n.fireWith}),s.promise(a),t&&t.call(a,a),a},when:function(t){function e(e,n,i){return function(t){n[e]=this,i[e]=1<arguments.length?c.call(arguments):t,i===r?u.notifyWith(n,i):--l||u.resolveWith(n,i)}}var r,n,i,o=0,s=c.call(arguments),a=s.length,l=1!==a||t&&S.isFunction(t.promise)?a:0,u=1===l?t:S.Deferred();if(1<a)for(r=new Array(a),n=new Array(a),i=new Array(a);o<a;o++)s[o]&&S.isFunction(s[o].promise)?s[o].promise().done(e(o,i,s)).fail(u.reject).progress(e(o,n,r)):--l;return l||u.resolveWith(i,s),u.promise()}}),S.fn.ready=function(t){return S.ready.promise().done(t),this},S.extend({isReady:!1,readyWait:1,holdReady:function(t){t?S.readyWait++:S.ready(!0)},ready:function(t){if(!0===t?!--S.readyWait:!S.isReady){if(!T.body)return setTimeout(S.ready);(S.isReady=!0)!==t&&0<--S.readyWait||(D.resolveWith(T,[S]),S.fn.triggerHandler&&(S(T).triggerHandler("ready"),S(T).off("ready")))}}}),S.ready.promise=function(t){if(!D)if(D=S.Deferred(),"complete"===T.readyState)setTimeout(S.ready);else if(T.addEventListener)T.addEventListener("DOMContentLoaded",j,!1),p.addEventListener("load",j,!1);else{T.attachEvent("onreadystatechange",j),p.attachEvent("onload",j);var n=!1;try{n=null==p.frameElement&&T.documentElement}catch(t){}n&&n.doScroll&&!function e(){if(!S.isReady){try{n.doScroll("left")}catch(t){return setTimeout(e,50)}N(),S.ready()}}()}return D.promise(t)};var R,L="undefined";for(R in S(m))break;m.ownLast="0"!==R,m.inlineBlockNeedsLayout=!1,S(function(){var t,e,n,i;(n=T.getElementsByTagName("body")[0])&&n.style&&(e=T.createElement("div"),(i=T.createElement("div")).style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",n.appendChild(i).appendChild(e),typeof e.style.zoom!==L&&(e.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",m.inlineBlockNeedsLayout=t=3===e.offsetWidth,t&&(n.style.zoom=1)),n.removeChild(i))}),function(){var t=T.createElement("div");if(null==m.deleteExpando){m.deleteExpando=!0;try{delete t.test}catch(t){m.deleteExpando=!1}}t=null}(),S.acceptData=function(t){var e=S.noData[(t.nodeName+" ").toLowerCase()],n=+t.nodeType||1;return(1===n||9===n)&&(!e||!0!==e&&t.getAttribute("classid")===e)};var I=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,P=/([A-Z])/g;function F(t,e,n){if(void 0===n&&1===t.nodeType){var i="data-"+e.replace(P,"-$1").toLowerCase();if("string"==typeof(n=t.getAttribute(i))){try{n="true"===n||"false"!==n&&("null"===n?null:+n+""===n?+n:I.test(n)?S.parseJSON(n):n)}catch(t){}S.data(t,e,n)}else n=void 0}return n}function H(t){var e;for(e in t)if(("data"!==e||!S.isEmptyObject(t[e]))&&"toJSON"!==e)return!1;return!0}function q(t,e,n,i){if(S.acceptData(t)){var r,o,s=S.expando,a=t.nodeType,l=a?S.cache:t,u=a?t[s]:t[s]&&s;if(u&&l[u]&&(i||l[u].data)||void 0!==n||"string"!=typeof e)return l[u=u||(a?t[s]=h.pop()||S.guid++:s)]||(l[u]=a?{}:{toJSON:S.noop}),"object"!=typeof e&&"function"!=typeof e||(i?l[u]=S.extend(l[u],e):l[u].data=S.extend(l[u].data,e)),o=l[u],i||(o.data||(o.data={}),o=o.data),void 0!==n&&(o[S.camelCase(e)]=n),"string"==typeof e?null==(r=o[e])&&(r=o[S.camelCase(e)]):r=o,r}}function $(t,e,n){if(S.acceptData(t)){var i,r,o=t.nodeType,s=o?S.cache:t,a=o?t[S.expando]:S.expando;if(s[a]){if(e&&(i=n?s[a]:s[a].data)){r=(e=S.isArray(e)?e.concat(S.map(e,S.camelCase)):e in i?[e]:(e=S.camelCase(e))in i?[e]:e.split(" ")).length;for(;r--;)delete i[e[r]];if(n?!H(i):!S.isEmptyObject(i))return}(n||(delete s[a].data,H(s[a])))&&(o?S.cleanData([t],!0):m.deleteExpando||s!=s.window?delete s[a]:s[a]=null)}}}S.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(t){return!!(t=t.nodeType?S.cache[t[S.expando]]:t[S.expando])&&!H(t)},data:function(t,e,n){return q(t,e,n)},removeData:function(t,e){return $(t,e)},_data:function(t,e,n){return q(t,e,n,!0)},_removeData:function(t,e){return $(t,e,!0)}}),S.fn.extend({data:function(t,e){var n,i,r,o=this[0],s=o&&o.attributes;if(void 0!==t)return"object"==typeof t?this.each(function(){S.data(this,t)}):1<arguments.length?this.each(function(){S.data(this,t,e)}):o?F(o,t,S.data(o,t)):void 0;if(this.length&&(r=S.data(o),1===o.nodeType&&!S._data(o,"parsedAttrs"))){for(n=s.length;n--;)s[n]&&0===(i=s[n].name).indexOf("data-")&&F(o,i=S.camelCase(i.slice(5)),r[i]);S._data(o,"parsedAttrs",!0)}return r},removeData:function(t){return this.each(function(){S.removeData(this,t)})}}),S.extend({queue:function(t,e,n){var i;if(t)return e=(e||"fx")+"queue",i=S._data(t,e),n&&(!i||S.isArray(n)?i=S._data(t,e,S.makeArray(n)):i.push(n)),i||[]},dequeue:function(t,e){e=e||"fx";var n=S.queue(t,e),i=n.length,r=n.shift(),o=S._queueHooks(t,e);"inprogress"===r&&(r=n.shift(),i--),r&&("fx"===e&&n.unshift("inprogress"),delete o.stop,r.call(t,function(){S.dequeue(t,e)},o)),!i&&o&&o.empty.fire()},_queueHooks:function(t,e){var n=e+"queueHooks";return S._data(t,n)||S._data(t,n,{empty:S.Callbacks("once memory").add(function(){S._removeData(t,e+"queue"),S._removeData(t,n)})})}}),S.fn.extend({queue:function(e,n){var t=2;return"string"!=typeof e&&(n=e,e="fx",t--),arguments.length<t?S.queue(this[0],e):void 0===n?this:this.each(function(){var t=S.queue(this,e,n);S._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&S.dequeue(this,e)})},dequeue:function(t){return this.each(function(){S.dequeue(this,t)})},clearQueue:function(t){return this.queue(t||"fx",[])},promise:function(t,e){function n(){--r||o.resolveWith(s,[s])}var i,r=1,o=S.Deferred(),s=this,a=this.length;for("string"!=typeof t&&(e=t,t=void 0),t=t||"fx";a--;)(i=S._data(s[a],t+"queueHooks"))&&i.empty&&(r++,i.empty.add(n));return n(),o.promise(e)}});function U(t,e){return t=e||t,"none"===S.css(t,"display")||!S.contains(t.ownerDocument,t)}var B=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,V=["Top","Right","Bottom","Left"],z=S.access=function(t,e,n,i,r,o,s){var a=0,l=t.length,u=null==n;if("object"===S.type(n))for(a in r=!0,n)S.access(t,e,a,n[a],!0,o,s);else if(void 0!==i&&(r=!0,S.isFunction(i)||(s=!0),u&&(e=s?(e.call(t,i),null):(u=e,function(t,e,n){return u.call(S(t),n)})),e))for(;a<l;a++)e(t[a],n,s?i:i.call(t[a],a,e(t[a],n)));return r?t:u?e.call(t):l?e(t[0],n):o},W=/^(?:checkbox|radio)$/i;!function(){var t=T.createElement("input"),e=T.createElement("div"),n=T.createDocumentFragment();if(e.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",m.leadingWhitespace=3===e.firstChild.nodeType,m.tbody=!e.getElementsByTagName("tbody").length,m.htmlSerialize=!!e.getElementsByTagName("link").length,m.html5Clone="<:nav></:nav>"!==T.createElement("nav").cloneNode(!0).outerHTML,t.type="checkbox",t.checked=!0,n.appendChild(t),m.appendChecked=t.checked,e.innerHTML="<textarea>x</textarea>",m.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue,n.appendChild(e),e.innerHTML="<input type='radio' checked='checked' name='t'/>",m.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,m.noCloneEvent=!0,e.attachEvent&&(e.attachEvent("onclick",function(){m.noCloneEvent=!1}),e.cloneNode(!0).click()),null==m.deleteExpando){m.deleteExpando=!0;try{delete e.test}catch(t){m.deleteExpando=!1}}}(),function(){var t,e,n=T.createElement("div");for(t in{submit:!0,change:!0,focusin:!0})e="on"+t,(m[t+"Bubbles"]=e in p)||(n.setAttribute(e,"t"),m[t+"Bubbles"]=!1===n.attributes[e].expando);n=null}();var J=/^(?:input|select|textarea)$/i,G=/^key/,X=/^(?:mouse|pointer|contextmenu)|click/,Y=/^(?:focusinfocus|focusoutblur)$/,K=/^([^.]*)(?:\.(.+)|)$/;function Q(){return!0}function Z(){return!1}function tt(){try{return T.activeElement}catch(t){}}function et(t){var e=nt.split("|"),n=t.createDocumentFragment();if(n.createElement)for(;e.length;)n.createElement(e.pop());return n}S.event={global:{},add:function(t,e,n,i,r){var o,s,a,l,u,c,h,d,f,p,g,v=S._data(t);if(v){for(n.handler&&(n=(l=n).handler,r=l.selector),n.guid||(n.guid=S.guid++),(s=v.events)||(s=v.events={}),(c=v.handle)||((c=v.handle=function(t){return typeof S===L||t&&S.event.triggered===t.type?void 0:S.event.dispatch.apply(c.elem,arguments)}).elem=t),a=(e=(e||"").match(O)||[""]).length;a--;)f=g=(o=K.exec(e[a])||[])[1],p=(o[2]||"").split(".").sort(),f&&(u=S.event.special[f]||{},f=(r?u.delegateType:u.bindType)||f,u=S.event.special[f]||{},h=S.extend({type:f,origType:g,data:i,handler:n,guid:n.guid,selector:r,needsContext:r&&S.expr.match.needsContext.test(r),namespace:p.join(".")},l),(d=s[f])||((d=s[f]=[]).delegateCount=0,u.setup&&!1!==u.setup.call(t,i,p,c)||(t.addEventListener?t.addEventListener(f,c,!1):t.attachEvent&&t.attachEvent("on"+f,c))),u.add&&(u.add.call(t,h),h.handler.guid||(h.handler.guid=n.guid)),r?d.splice(d.delegateCount++,0,h):d.push(h),S.event.global[f]=!0);t=null}},remove:function(t,e,n,i,r){var o,s,a,l,u,c,h,d,f,p,g,v=S.hasData(t)&&S._data(t);if(v&&(c=v.events)){for(u=(e=(e||"").match(O)||[""]).length;u--;)if(f=g=(a=K.exec(e[u])||[])[1],p=(a[2]||"").split(".").sort(),f){for(h=S.event.special[f]||{},d=c[f=(i?h.delegateType:h.bindType)||f]||[],a=a[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),l=o=d.length;o--;)s=d[o],!r&&g!==s.origType||n&&n.guid!==s.guid||a&&!a.test(s.namespace)||i&&i!==s.selector&&("**"!==i||!s.selector)||(d.splice(o,1),s.selector&&d.delegateCount--,h.remove&&h.remove.call(t,s));l&&!d.length&&(h.teardown&&!1!==h.teardown.call(t,p,v.handle)||S.removeEvent(t,f,v.handle),delete c[f])}else for(f in c)S.event.remove(t,f+e[u],n,i,!0);S.isEmptyObject(c)&&(delete v.handle,S._removeData(t,"events"))}},trigger:function(t,e,n,i){var r,o,s,a,l,u,c,h=[n||T],d=v.call(t,"type")?t.type:t,f=v.call(t,"namespace")?t.namespace.split("."):[];if(s=u=n=n||T,3!==n.nodeType&&8!==n.nodeType&&!Y.test(d+S.event.triggered)&&(0<=d.indexOf(".")&&(d=(f=d.split(".")).shift(),f.sort()),o=d.indexOf(":")<0&&"on"+d,(t=t[S.expando]?t:new S.Event(d,"object"==typeof t&&t)).isTrigger=i?2:3,t.namespace=f.join("."),t.namespace_re=t.namespace?new RegExp("(^|\\.)"+f.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=n),e=null==e?[t]:S.makeArray(e,[t]),l=S.event.special[d]||{},i||!l.trigger||!1!==l.trigger.apply(n,e))){if(!i&&!l.noBubble&&!S.isWindow(n)){for(a=l.delegateType||d,Y.test(a+d)||(s=s.parentNode);s;s=s.parentNode)h.push(s),u=s;u===(n.ownerDocument||T)&&h.push(u.defaultView||u.parentWindow||p)}for(c=0;(s=h[c++])&&!t.isPropagationStopped();)t.type=1<c?a:l.bindType||d,(r=(S._data(s,"events")||{})[t.type]&&S._data(s,"handle"))&&r.apply(s,e),(r=o&&s[o])&&r.apply&&S.acceptData(s)&&(t.result=r.apply(s,e),!1===t.result&&t.preventDefault());if(t.type=d,!i&&!t.isDefaultPrevented()&&(!l._default||!1===l._default.apply(h.pop(),e))&&S.acceptData(n)&&o&&n[d]&&!S.isWindow(n)){(u=n[o])&&(n[o]=null),S.event.triggered=d;try{n[d]()}catch(t){}S.event.triggered=void 0,u&&(n[o]=u)}return t.result}},dispatch:function(t){t=S.event.fix(t);var e,n,i,r,o,s,a=c.call(arguments),l=(S._data(this,"events")||{})[t.type]||[],u=S.event.special[t.type]||{};if((a[0]=t).delegateTarget=this,!u.preDispatch||!1!==u.preDispatch.call(this,t)){for(s=S.event.handlers.call(this,t,l),e=0;(r=s[e++])&&!t.isPropagationStopped();)for(t.currentTarget=r.elem,o=0;(i=r.handlers[o++])&&!t.isImmediatePropagationStopped();)t.namespace_re&&!t.namespace_re.test(i.namespace)||(t.handleObj=i,t.data=i.data,void 0!==(n=((S.event.special[i.origType]||{}).handle||i.handler).apply(r.elem,a))&&!1===(t.result=n)&&(t.preventDefault(),t.stopPropagation()));return u.postDispatch&&u.postDispatch.call(this,t),t.result}},handlers:function(t,e){var n,i,r,o,s=[],a=e.delegateCount,l=t.target;if(a&&l.nodeType&&(!t.button||"click"!==t.type))for(;l!=this;l=l.parentNode||this)if(1===l.nodeType&&(!0!==l.disabled||"click"!==t.type)){for(r=[],o=0;o<a;o++)void 0===r[n=(i=e[o]).selector+" "]&&(r[n]=i.needsContext?0<=S(n,this).index(l):S.find(n,this,null,[l]).length),r[n]&&r.push(i);r.length&&s.push({elem:l,handlers:r})}return a<e.length&&s.push({elem:this,handlers:e.slice(a)}),s},fix:function(t){if(t[S.expando])return t;var e,n,i,r=t.type,o=t,s=this.fixHooks[r];for(s||(this.fixHooks[r]=s=X.test(r)?this.mouseHooks:G.test(r)?this.keyHooks:{}),i=s.props?this.props.concat(s.props):this.props,t=new S.Event(o),e=i.length;e--;)t[n=i[e]]=o[n];return t.target||(t.target=o.srcElement||T),3===t.target.nodeType&&(t.target=t.target.parentNode),t.metaKey=!!t.metaKey,s.filter?s.filter(t,o):t},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(t,e){return null==t.which&&(t.which=null!=e.charCode?e.charCode:e.keyCode),t}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(t,e){var n,i,r,o=e.button,s=e.fromElement;return null==t.pageX&&null!=e.clientX&&(r=(i=t.target.ownerDocument||T).documentElement,n=i.body,t.pageX=e.clientX+(r&&r.scrollLeft||n&&n.scrollLeft||0)-(r&&r.clientLeft||n&&n.clientLeft||0),t.pageY=e.clientY+(r&&r.scrollTop||n&&n.scrollTop||0)-(r&&r.clientTop||n&&n.clientTop||0)),!t.relatedTarget&&s&&(t.relatedTarget=s===t.target?e.toElement:s),t.which||void 0===o||(t.which=1&o?1:2&o?3:4&o?2:0),t}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==tt()&&this.focus)try{return this.focus(),!1}catch(t){}},delegateType:"focusin"},blur:{trigger:function(){if(this===tt()&&this.blur)return this.blur(),!1},delegateType:"focusout"},click:{trigger:function(){if(S.nodeName(this,"input")&&"checkbox"===this.type&&this.click)return this.click(),!1},_default:function(t){return S.nodeName(t.target,"a")}},beforeunload:{postDispatch:function(t){void 0!==t.result&&t.originalEvent&&(t.originalEvent.returnValue=t.result)}}},simulate:function(t,e,n,i){var r=S.extend(new S.Event,n,{type:t,isSimulated:!0,originalEvent:{}});i?S.event.trigger(r,null,e):S.event.dispatch.call(e,r),r.isDefaultPrevented()&&n.preventDefault()}},S.removeEvent=T.removeEventListener?function(t,e,n){t.removeEventListener&&t.removeEventListener(e,n,!1)}:function(t,e,n){var i="on"+e;t.detachEvent&&(typeof t[i]===L&&(t[i]=null),t.detachEvent(i,n))},S.Event=function(t,e){if(!(this instanceof S.Event))return new S.Event(t,e);t&&t.type?(this.originalEvent=t,this.type=t.type,this.isDefaultPrevented=t.defaultPrevented||void 0===t.defaultPrevented&&!1===t.returnValue?Q:Z):this.type=t,e&&S.extend(this,e),this.timeStamp=t&&t.timeStamp||S.now(),this[S.expando]=!0},S.Event.prototype={isDefaultPrevented:Z,isPropagationStopped:Z,isImmediatePropagationStopped:Z,preventDefault:function(){var t=this.originalEvent;this.isDefaultPrevented=Q,t&&(t.preventDefault?t.preventDefault():t.returnValue=!1)},stopPropagation:function(){var t=this.originalEvent;this.isPropagationStopped=Q,t&&(t.stopPropagation&&t.stopPropagation(),t.cancelBubble=!0)},stopImmediatePropagation:function(){var t=this.originalEvent;this.isImmediatePropagationStopped=Q,t&&t.stopImmediatePropagation&&t.stopImmediatePropagation(),this.stopPropagation()}},S.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(t,r){S.event.special[t]={delegateType:r,bindType:r,handle:function(t){var e,n=t.relatedTarget,i=t.handleObj;return n&&(n===this||S.contains(this,n))||(t.type=i.origType,e=i.handler.apply(this,arguments),t.type=r),e}}}),m.submitBubbles||(S.event.special.submit={setup:function(){if(S.nodeName(this,"form"))return!1;S.event.add(this,"click._submit keypress._submit",function(t){var e=t.target,n=S.nodeName(e,"input")||S.nodeName(e,"button")?e.form:void 0;n&&!S._data(n,"submitBubbles")&&(S.event.add(n,"submit._submit",function(t){t._submit_bubble=!0}),S._data(n,"submitBubbles",!0))})},postDispatch:function(t){t._submit_bubble&&(delete t._submit_bubble,this.parentNode&&!t.isTrigger&&S.event.simulate("submit",this.parentNode,t,!0))},teardown:function(){if(S.nodeName(this,"form"))return!1;S.event.remove(this,"._submit")}}),m.changeBubbles||(S.event.special.change={setup:function(){if(J.test(this.nodeName))return"checkbox"!==this.type&&"radio"!==this.type||(S.event.add(this,"propertychange._change",function(t){"checked"===t.originalEvent.propertyName&&(this._just_changed=!0)}),S.event.add(this,"click._change",function(t){this._just_changed&&!t.isTrigger&&(this._just_changed=!1),S.event.simulate("change",this,t,!0)})),!1;S.event.add(this,"beforeactivate._change",function(t){var e=t.target;J.test(e.nodeName)&&!S._data(e,"changeBubbles")&&(S.event.add(e,"change._change",function(t){!this.parentNode||t.isSimulated||t.isTrigger||S.event.simulate("change",this.parentNode,t,!0)}),S._data(e,"changeBubbles",!0))})},handle:function(t){var e=t.target;if(this!==e||t.isSimulated||t.isTrigger||"radio"!==e.type&&"checkbox"!==e.type)return t.handleObj.handler.apply(this,arguments)},teardown:function(){return S.event.remove(this,"._change"),!J.test(this.nodeName)}}),m.focusinBubbles||S.each({focus:"focusin",blur:"focusout"},function(n,i){function r(t){S.event.simulate(i,t.target,S.event.fix(t),!0)}S.event.special[i]={setup:function(){var t=this.ownerDocument||this,e=S._data(t,i);e||t.addEventListener(n,r,!0),S._data(t,i,(e||0)+1)},teardown:function(){var t=this.ownerDocument||this,e=S._data(t,i)-1;e?S._data(t,i,e):(t.removeEventListener(n,r,!0),S._removeData(t,i))}}}),S.fn.extend({on:function(t,e,n,i,r){var o,s;if("object"==typeof t){for(o in"string"!=typeof e&&(n=n||e,e=void 0),t)this.on(o,e,n,t[o],r);return this}if(null==n&&null==i?(i=e,n=e=void 0):null==i&&("string"==typeof e?(i=n,n=void 0):(i=n,n=e,e=void 0)),!1===i)i=Z;else if(!i)return this;return 1===r&&(s=i,(i=function(t){return S().off(t),s.apply(this,arguments)}).guid=s.guid||(s.guid=S.guid++)),this.each(function(){S.event.add(this,t,i,n,e)})},one:function(t,e,n,i){return this.on(t,e,n,i,1)},off:function(t,e,n){var i,r;if(t&&t.preventDefault&&t.handleObj)return i=t.handleObj,S(t.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"!=typeof t)return!1!==e&&"function"!=typeof e||(n=e,e=void 0),!1===n&&(n=Z),this.each(function(){S.event.remove(this,t,n,e)});for(r in t)this.off(r,e,t[r]);return this},trigger:function(t,e){return this.each(function(){S.event.trigger(t,e,this)})},triggerHandler:function(t,e){var n=this[0];if(n)return S.event.trigger(t,e,n,!0)}});var nt="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",it=/ jQuery\d+="(?:null|\d+)"/g,rt=new RegExp("<(?:"+nt+")[\\s/>]","i"),ot=/^\s+/,st=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,at=/<([\w:]+)/,lt=/<tbody/i,ut=/<|&#?\w+;/,ct=/<(?:script|style|link)/i,ht=/checked\s*(?:[^=]|=\s*.checked.)/i,dt=/^$|\/(?:java|ecma)script/i,ft=/^true\/(.*)/,pt=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,gt={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:m.htmlSerialize?[0,"",""]:[1,"X<div>","</div>"]},vt=et(T).appendChild(T.createElement("div"));function mt(t,e){var n,i,r=0,o=typeof t.getElementsByTagName!==L?t.getElementsByTagName(e||"*"):typeof t.querySelectorAll!==L?t.querySelectorAll(e||"*"):void 0;if(!o)for(o=[],n=t.childNodes||t;null!=(i=n[r]);r++)!e||S.nodeName(i,e)?o.push(i):S.merge(o,mt(i,e));return void 0===e||e&&S.nodeName(t,e)?S.merge([t],o):o}function yt(t){W.test(t.type)&&(t.defaultChecked=t.checked)}function bt(t,e){return S.nodeName(t,"table")&&S.nodeName(11!==e.nodeType?e:e.firstChild,"tr")?t.getElementsByTagName("tbody")[0]||t.appendChild(t.ownerDocument.createElement("tbody")):t}function wt(t){return t.type=(null!==S.find.attr(t,"type"))+"/"+t.type,t}function _t(t){var e=ft.exec(t.type);return e?t.type=e[1]:t.removeAttribute("type"),t}function xt(t,e){for(var n,i=0;null!=(n=t[i]);i++)S._data(n,"globalEval",!e||S._data(e[i],"globalEval"))}function St(t,e){if(1===e.nodeType&&S.hasData(t)){var n,i,r,o=S._data(t),s=S._data(e,o),a=o.events;if(a)for(n in delete s.handle,s.events={},a)for(i=0,r=a[n].length;i<r;i++)S.event.add(e,n,a[n][i]);s.data&&(s.data=S.extend({},s.data))}}function Tt(t,e){var n,i,r;if(1===e.nodeType){if(n=e.nodeName.toLowerCase(),!m.noCloneEvent&&e[S.expando]){for(i in(r=S._data(e)).events)S.removeEvent(e,i,r.handle);e.removeAttribute(S.expando)}"script"===n&&e.text!==t.text?(wt(e).text=t.text,_t(e)):"object"===n?(e.parentNode&&(e.outerHTML=t.outerHTML),m.html5Clone&&t.innerHTML&&!S.trim(e.innerHTML)&&(e.innerHTML=t.innerHTML)):"input"===n&&W.test(t.type)?(e.defaultChecked=e.checked=t.checked,e.value!==t.value&&(e.value=t.value)):"option"===n?e.defaultSelected=e.selected=t.defaultSelected:"input"!==n&&"textarea"!==n||(e.defaultValue=t.defaultValue)}}gt.optgroup=gt.option,gt.tbody=gt.tfoot=gt.colgroup=gt.caption=gt.thead,gt.th=gt.td,S.extend({clone:function(t,e,n){var i,r,o,s,a,l=S.contains(t.ownerDocument,t);if(m.html5Clone||S.isXMLDoc(t)||!rt.test("<"+t.nodeName+">")?o=t.cloneNode(!0):(vt.innerHTML=t.outerHTML,vt.removeChild(o=vt.firstChild)),!(m.noCloneEvent&&m.noCloneChecked||1!==t.nodeType&&11!==t.nodeType||S.isXMLDoc(t)))for(i=mt(o),a=mt(t),s=0;null!=(r=a[s]);++s)i[s]&&Tt(r,i[s]);if(e)if(n)for(a=a||mt(t),i=i||mt(o),s=0;null!=(r=a[s]);s++)St(r,i[s]);else St(t,o);return 0<(i=mt(o,"script")).length&&xt(i,!l&&mt(t,"script")),i=a=r=null,o},buildFragment:function(t,e,n,i){for(var r,o,s,a,l,u,c,h=t.length,d=et(e),f=[],p=0;p<h;p++)if((o=t[p])||0===o)if("object"===S.type(o))S.merge(f,o.nodeType?[o]:o);else if(ut.test(o)){for(a=a||d.appendChild(e.createElement("div")),l=(at.exec(o)||["",""])[1].toLowerCase(),c=gt[l]||gt._default,a.innerHTML=c[1]+o.replace(st,"<$1></$2>")+c[2],r=c[0];r--;)a=a.lastChild;if(!m.leadingWhitespace&&ot.test(o)&&f.push(e.createTextNode(ot.exec(o)[0])),!m.tbody)for(r=(o="table"!==l||lt.test(o)?"<table>"!==c[1]||lt.test(o)?0:a:a.firstChild)&&o.childNodes.length;r--;)S.nodeName(u=o.childNodes[r],"tbody")&&!u.childNodes.length&&o.removeChild(u);for(S.merge(f,a.childNodes),a.textContent="";a.firstChild;)a.removeChild(a.firstChild);a=d.lastChild}else f.push(e.createTextNode(o));for(a&&d.removeChild(a),m.appendChecked||S.grep(mt(f,"input"),yt),p=0;o=f[p++];)if((!i||-1===S.inArray(o,i))&&(s=S.contains(o.ownerDocument,o),a=mt(d.appendChild(o),"script"),s&&xt(a),n))for(r=0;o=a[r++];)dt.test(o.type||"")&&n.push(o);return a=null,d},cleanData:function(t,e){for(var n,i,r,o,s=0,a=S.expando,l=S.cache,u=m.deleteExpando,c=S.event.special;null!=(n=t[s]);s++)if((e||S.acceptData(n))&&(o=(r=n[a])&&l[r])){if(o.events)for(i in o.events)c[i]?S.event.remove(n,i):S.removeEvent(n,i,o.handle);l[r]&&(delete l[r],u?delete n[a]:typeof n.removeAttribute!==L?n.removeAttribute(a):n[a]=null,h.push(r))}}}),S.fn.extend({text:function(t){return z(this,function(t){return void 0===t?S.text(this):this.empty().append((this[0]&&this[0].ownerDocument||T).createTextNode(t))},null,t,arguments.length)},append:function(){return this.domManip(arguments,function(t){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||bt(this,t).appendChild(t)})},prepend:function(){return this.domManip(arguments,function(t){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var e=bt(this,t);e.insertBefore(t,e.firstChild)}})},before:function(){return this.domManip(arguments,function(t){this.parentNode&&this.parentNode.insertBefore(t,this)})},after:function(){return this.domManip(arguments,function(t){this.parentNode&&this.parentNode.insertBefore(t,this.nextSibling)})},remove:function(t,e){for(var n,i=t?S.filter(t,this):this,r=0;null!=(n=i[r]);r++)e||1!==n.nodeType||S.cleanData(mt(n)),n.parentNode&&(e&&S.contains(n.ownerDocument,n)&&xt(mt(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){for(var t,e=0;null!=(t=this[e]);e++){for(1===t.nodeType&&S.cleanData(mt(t,!1));t.firstChild;)t.removeChild(t.firstChild);t.options&&S.nodeName(t,"select")&&(t.options.length=0)}return this},clone:function(t,e){return t=null!=t&&t,e=null==e?t:e,this.map(function(){return S.clone(this,t,e)})},html:function(t){return z(this,function(t){var e=this[0]||{},n=0,i=this.length;if(void 0===t)return 1===e.nodeType?e.innerHTML.replace(it,""):void 0;if("string"==typeof t&&!ct.test(t)&&(m.htmlSerialize||!rt.test(t))&&(m.leadingWhitespace||!ot.test(t))&&!gt[(at.exec(t)||["",""])[1].toLowerCase()]){t=t.replace(st,"<$1></$2>");try{for(;n<i;n++)1===(e=this[n]||{}).nodeType&&(S.cleanData(mt(e,!1)),e.innerHTML=t);e=0}catch(t){}}e&&this.empty().append(t)},null,t,arguments.length)},replaceWith:function(){var e=arguments[0];return this.domManip(arguments,function(t){e=this.parentNode,S.cleanData(mt(this)),e&&e.replaceChild(t,this)}),e&&(e.length||e.nodeType)?this:this.remove()},detach:function(t){return this.remove(t,!0)},domManip:function(n,i){n=g.apply([],n);var t,e,r,o,s,a,l=0,u=this.length,c=this,h=u-1,d=n[0],f=S.isFunction(d);if(f||1<u&&"string"==typeof d&&!m.checkClone&&ht.test(d))return this.each(function(t){var e=c.eq(t);f&&(n[0]=d.call(this,t,e.html())),e.domManip(n,i)});if(u&&(t=(a=S.buildFragment(n,this[0].ownerDocument,!1,this)).firstChild,1===a.childNodes.length&&(a=t),t)){for(r=(o=S.map(mt(a,"script"),wt)).length;l<u;l++)e=a,l!==h&&(e=S.clone(e,!0,!0),r&&S.merge(o,mt(e,"script"))),i.call(this[l],e,l);if(r)for(s=o[o.length-1].ownerDocument,S.map(o,_t),l=0;l<r;l++)e=o[l],dt.test(e.type||"")&&!S._data(e,"globalEval")&&S.contains(s,e)&&(e.src?S._evalUrl&&S._evalUrl(e.src):S.globalEval((e.text||e.textContent||e.innerHTML||"").replace(pt,"")));a=t=null}return this}}),S.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(t,s){S.fn[t]=function(t){for(var e,n=0,i=[],r=S(t),o=r.length-1;n<=o;n++)e=n===o?this:this.clone(!0),S(r[n])[s](e),a.apply(i,e.get());return this.pushStack(i)}});var Ct,Et,kt={};function Mt(t,e){var n,i=S(e.createElement(t)).appendTo(e.body),r=p.getDefaultComputedStyle&&(n=p.getDefaultComputedStyle(i[0]))?n.display:S.css(i[0],"display");return i.detach(),r}function Dt(t){var e=T,n=kt[t];return n||("none"!==(n=Mt(t,e))&&n||((e=((Ct=(Ct||S("<iframe frameborder='0' width='0' height='0'/>")).appendTo(e.documentElement))[0].contentWindow||Ct[0].contentDocument).document).write(),e.close(),n=Mt(t,e),Ct.detach()),kt[t]=n),n}m.shrinkWrapBlocks=function(){return null!=Et?Et:(Et=!1,(e=T.getElementsByTagName("body")[0])&&e.style?(t=T.createElement("div"),(n=T.createElement("div")).style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",e.appendChild(n).appendChild(t),typeof t.style.zoom!==L&&(t.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",t.appendChild(T.createElement("div")).style.width="5px",Et=3!==t.offsetWidth),e.removeChild(n),Et):void 0);var t,e,n};var Ot,At,Nt,jt,Rt,Lt,It,Pt,Ft,Ht=/^margin/,qt=new RegExp("^("+B+")(?!px)[a-z%]+$","i"),$t=/^(top|right|bottom|left)$/;function Ut(e,n){return{get:function(){var t=e();if(null!=t){if(!t)return(this.get=n).apply(this,arguments);delete this.get}}}}function Bt(){var t,e,n,i;(e=T.getElementsByTagName("body")[0])&&e.style&&(t=T.createElement("div"),(n=T.createElement("div")).style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",e.appendChild(n).appendChild(t),t.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",Lt=It=!1,Ft=!0,p.getComputedStyle&&(Lt="1%"!==(p.getComputedStyle(t,null)||{}).top,It="4px"===(p.getComputedStyle(t,null)||{width:"4px"}).width,(i=t.appendChild(T.createElement("div"))).style.cssText=t.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",t.style.width="1px",Ft=!parseFloat((p.getComputedStyle(i,null)||{}).marginRight)),t.innerHTML="<table><tr><td></td><td>t</td></tr></table>",(i=t.getElementsByTagName("td"))[0].style.cssText="margin:0;border:0;padding:0;display:none",(Pt=0===i[0].offsetHeight)&&(i[0].style.display="",i[1].style.display="none",Pt=0===i[0].offsetHeight),e.removeChild(n))}p.getComputedStyle?(Ot=function(t){return t.ownerDocument.defaultView.getComputedStyle(t,null)},At=function(t,e,n){var i,r,o,s,a=t.style;return s=(n=n||Ot(t))?n.getPropertyValue(e)||n[e]:void 0,n&&(""!==s||S.contains(t.ownerDocument,t)||(s=S.style(t,e)),qt.test(s)&&Ht.test(e)&&(i=a.width,r=a.minWidth,o=a.maxWidth,a.minWidth=a.maxWidth=a.width=s,s=n.width,a.width=i,a.minWidth=r,a.maxWidth=o)),void 0===s?s:s+""}):T.documentElement.currentStyle&&(Ot=function(t){return t.currentStyle},At=function(t,e,n){var i,r,o,s,a=t.style;return null==(s=(n=n||Ot(t))?n[e]:void 0)&&a&&a[e]&&(s=a[e]),qt.test(s)&&!$t.test(e)&&(i=a.left,(o=(r=t.runtimeStyle)&&r.left)&&(r.left=t.currentStyle.left),a.left="fontSize"===e?"1em":s,s=a.pixelLeft+"px",a.left=i,o&&(r.left=o)),void 0===s?s:s+""||"auto"}),(Nt=T.createElement("div")).innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",(jt=(Rt=Nt.getElementsByTagName("a")[0])&&Rt.style)&&(jt.cssText="float:left;opacity:.5",m.opacity="0.5"===jt.opacity,m.cssFloat=!!jt.cssFloat,Nt.style.backgroundClip="content-box",Nt.cloneNode(!0).style.backgroundClip="",m.clearCloneStyle="content-box"===Nt.style.backgroundClip,m.boxSizing=""===jt.boxSizing||""===jt.MozBoxSizing||""===jt.WebkitBoxSizing,S.extend(m,{reliableHiddenOffsets:function(){return null==Pt&&Bt(),Pt},boxSizingReliable:function(){return null==It&&Bt(),It},pixelPosition:function(){return null==Lt&&Bt(),Lt},reliableMarginRight:function(){return null==Ft&&Bt(),Ft}})),S.swap=function(t,e,n,i){var r,o,s={};for(o in e)s[o]=t.style[o],t.style[o]=e[o];for(o in r=n.apply(t,i||[]),e)t.style[o]=s[o];return r};var Vt=/alpha\([^)]*\)/i,zt=/opacity\s*=\s*([^)]*)/,Wt=/^(none|table(?!-c[ea]).+)/,Jt=new RegExp("^("+B+")(.*)$","i"),Gt=new RegExp("^([+-])=("+B+")","i"),Xt={position:"absolute",visibility:"hidden",display:"block"},Yt={letterSpacing:"0",fontWeight:"400"},Kt=["Webkit","O","Moz","ms"];function Qt(t,e){if(e in t)return e;for(var n=e.charAt(0).toUpperCase()+e.slice(1),i=e,r=Kt.length;r--;)if((e=Kt[r]+n)in t)return e;return i}function Zt(t,e){for(var n,i,r,o=[],s=0,a=t.length;s<a;s++)(i=t[s]).style&&(o[s]=S._data(i,"olddisplay"),n=i.style.display,e?(o[s]||"none"!==n||(i.style.display=""),""===i.style.display&&U(i)&&(o[s]=S._data(i,"olddisplay",Dt(i.nodeName)))):(r=U(i),(n&&"none"!==n||!r)&&S._data(i,"olddisplay",r?n:S.css(i,"display"))));for(s=0;s<a;s++)(i=t[s]).style&&(e&&"none"!==i.style.display&&""!==i.style.display||(i.style.display=e?o[s]||"":"none"));return t}function te(t,e,n){var i=Jt.exec(e);return i?Math.max(0,i[1]-(n||0))+(i[2]||"px"):e}function ee(t,e,n,i,r){for(var o=n===(i?"border":"content")?4:"width"===e?1:0,s=0;o<4;o+=2)"margin"===n&&(s+=S.css(t,n+V[o],!0,r)),i?("content"===n&&(s-=S.css(t,"padding"+V[o],!0,r)),"margin"!==n&&(s-=S.css(t,"border"+V[o]+"Width",!0,r))):(s+=S.css(t,"padding"+V[o],!0,r),"padding"!==n&&(s+=S.css(t,"border"+V[o]+"Width",!0,r)));return s}function ne(t,e,n){var i=!0,r="width"===e?t.offsetWidth:t.offsetHeight,o=Ot(t),s=m.boxSizing&&"border-box"===S.css(t,"boxSizing",!1,o);if(r<=0||null==r){if(((r=At(t,e,o))<0||null==r)&&(r=t.style[e]),qt.test(r))return r;i=s&&(m.boxSizingReliable()||r===t.style[e]),r=parseFloat(r)||0}return r+ee(t,e,n||(s?"border":"content"),i,o)+"px"}function ie(t,e,n,i,r){return new ie.prototype.init(t,e,n,i,r)}S.extend({cssHooks:{opacity:{get:function(t,e){if(e){var n=At(t,"opacity");return""===n?"1":n}}}},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:m.cssFloat?"cssFloat":"styleFloat"},style:function(t,e,n,i){if(t&&3!==t.nodeType&&8!==t.nodeType&&t.style){var r,o,s,a=S.camelCase(e),l=t.style;if(e=S.cssProps[a]||(S.cssProps[a]=Qt(l,a)),s=S.cssHooks[e]||S.cssHooks[a],void 0===n)return s&&"get"in s&&void 0!==(r=s.get(t,!1,i))?r:l[e];if("string"===(o=typeof n)&&(r=Gt.exec(n))&&(n=(r[1]+1)*r[2]+parseFloat(S.css(t,e)),o="number"),null!=n&&n==n&&("number"!==o||S.cssNumber[a]||(n+="px"),m.clearCloneStyle||""!==n||0!==e.indexOf("background")||(l[e]="inherit"),!(s&&"set"in s&&void 0===(n=s.set(t,n,i)))))try{l[e]=n}catch(t){}}},css:function(t,e,n,i){var r,o,s,a=S.camelCase(e);return e=S.cssProps[a]||(S.cssProps[a]=Qt(t.style,a)),(s=S.cssHooks[e]||S.cssHooks[a])&&"get"in s&&(o=s.get(t,!0,n)),void 0===o&&(o=At(t,e,i)),"normal"===o&&e in Yt&&(o=Yt[e]),""===n||n?(r=parseFloat(o),!0===n||S.isNumeric(r)?r||0:o):o}}),S.each(["height","width"],function(t,r){S.cssHooks[r]={get:function(t,e,n){if(e)return Wt.test(S.css(t,"display"))&&0===t.offsetWidth?S.swap(t,Xt,function(){return ne(t,r,n)}):ne(t,r,n)},set:function(t,e,n){var i=n&&Ot(t);return te(0,e,n?ee(t,r,n,m.boxSizing&&"border-box"===S.css(t,"boxSizing",!1,i),i):0)}}}),m.opacity||(S.cssHooks.opacity={get:function(t,e){return zt.test((e&&t.currentStyle?t.currentStyle.filter:t.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":e?"1":""},set:function(t,e){var n=t.style,i=t.currentStyle,r=S.isNumeric(e)?"alpha(opacity="+100*e+")":"",o=i&&i.filter||n.filter||"";((n.zoom=1)<=e||""===e)&&""===S.trim(o.replace(Vt,""))&&n.removeAttribute&&(n.removeAttribute("filter"),""===e||i&&!i.filter)||(n.filter=Vt.test(o)?o.replace(Vt,r):o+" "+r)}}),S.cssHooks.marginRight=Ut(m.reliableMarginRight,function(t,e){if(e)return S.swap(t,{display:"inline-block"},At,[t,"marginRight"])}),S.each({margin:"",padding:"",border:"Width"},function(r,o){S.cssHooks[r+o]={expand:function(t){for(var e=0,n={},i="string"==typeof t?t.split(" "):[t];e<4;e++)n[r+V[e]+o]=i[e]||i[e-2]||i[0];return n}},Ht.test(r)||(S.cssHooks[r+o].set=te)}),S.fn.extend({css:function(t,e){return z(this,function(t,e,n){var i,r,o={},s=0;if(S.isArray(e)){for(i=Ot(t),r=e.length;s<r;s++)o[e[s]]=S.css(t,e[s],!1,i);return o}return void 0!==n?S.style(t,e,n):S.css(t,e)},t,e,1<arguments.length)},show:function(){return Zt(this,!0)},hide:function(){return Zt(this)},toggle:function(t){return"boolean"==typeof t?t?this.show():this.hide():this.each(function(){U(this)?S(this).show():S(this).hide()})}}),((S.Tween=ie).prototype={constructor:ie,init:function(t,e,n,i,r,o){this.elem=t,this.prop=n,this.easing=r||"swing",this.options=e,this.start=this.now=this.cur(),this.end=i,this.unit=o||(S.cssNumber[n]?"":"px")},cur:function(){var t=ie.propHooks[this.prop];return t&&t.get?t.get(this):ie.propHooks._default.get(this)},run:function(t){var e,n=ie.propHooks[this.prop];return this.options.duration?this.pos=e=S.easing[this.easing](t,this.options.duration*t,0,1,this.options.duration):this.pos=e=t,this.now=(this.end-this.start)*e+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):ie.propHooks._default.set(this),this}}).init.prototype=ie.prototype,(ie.propHooks={_default:{get:function(t){var e;return null==t.elem[t.prop]||t.elem.style&&null!=t.elem.style[t.prop]?(e=S.css(t.elem,t.prop,""))&&"auto"!==e?e:0:t.elem[t.prop]},set:function(t){S.fx.step[t.prop]?S.fx.step[t.prop](t):t.elem.style&&(null!=t.elem.style[S.cssProps[t.prop]]||S.cssHooks[t.prop])?S.style(t.elem,t.prop,t.now+t.unit):t.elem[t.prop]=t.now}}}).scrollTop=ie.propHooks.scrollLeft={set:function(t){t.elem.nodeType&&t.elem.parentNode&&(t.elem[t.prop]=t.now)}},S.easing={linear:function(t){return t},swing:function(t){return.5-Math.cos(t*Math.PI)/2}},S.fx=ie.prototype.init,S.fx.step={};var re,oe,se,ae,le,ue,ce,he=/^(?:toggle|show|hide)$/,de=new RegExp("^(?:([+-])=|)("+B+")([a-z%]*)$","i"),fe=/queueHooks$/,pe=[function(e,t,n){var i,r,o,s,a,l,u,c=this,h={},d=e.style,f=e.nodeType&&U(e),p=S._data(e,"fxshow");n.queue||(null==(a=S._queueHooks(e,"fx")).unqueued&&(a.unqueued=0,l=a.empty.fire,a.empty.fire=function(){a.unqueued||l()}),a.unqueued++,c.always(function(){c.always(function(){a.unqueued--,S.queue(e,"fx").length||a.empty.fire()})}));1===e.nodeType&&("height"in t||"width"in t)&&(n.overflow=[d.overflow,d.overflowX,d.overflowY],u=S.css(e,"display"),"inline"===("none"===u?S._data(e,"olddisplay")||Dt(e.nodeName):u)&&"none"===S.css(e,"float")&&(m.inlineBlockNeedsLayout&&"inline"!==Dt(e.nodeName)?d.zoom=1:d.display="inline-block"));n.overflow&&(d.overflow="hidden",m.shrinkWrapBlocks()||c.always(function(){d.overflow=n.overflow[0],d.overflowX=n.overflow[1],d.overflowY=n.overflow[2]}));for(i in t)if(r=t[i],he.exec(r)){if(delete t[i],o=o||"toggle"===r,r===(f?"hide":"show")){if("show"!==r||!p||void 0===p[i])continue;f=!0}h[i]=p&&p[i]||S.style(e,i)}else u=void 0;if(S.isEmptyObject(h))"inline"===("none"===u?Dt(e.nodeName):u)&&(d.display=u);else for(i in p?"hidden"in p&&(f=p.hidden):p=S._data(e,"fxshow",{}),o&&(p.hidden=!f),f?S(e).show():c.done(function(){S(e).hide()}),c.done(function(){var t;for(t in S._removeData(e,"fxshow"),h)S.style(e,t,h[t])}),h)s=ye(f?p[i]:0,i,c),i in p||(p[i]=s.start,f&&(s.end=s.start,s.start="width"===i||"height"===i?1:0))}],ge={"*":[function(t,e){var n=this.createTween(t,e),i=n.cur(),r=de.exec(e),o=r&&r[3]||(S.cssNumber[t]?"":"px"),s=(S.cssNumber[t]||"px"!==o&&+i)&&de.exec(S.css(n.elem,t)),a=1,l=20;if(s&&s[3]!==o)for(o=o||s[3],r=r||[],s=+i||1;s/=a=a||".5",S.style(n.elem,t,s+o),a!==(a=n.cur()/i)&&1!==a&&--l;);return r&&(s=n.start=+s||+i||0,n.unit=o,n.end=r[1]?s+(r[1]+1)*r[2]:+r[2]),n}]};function ve(){return setTimeout(function(){re=void 0}),re=S.now()}function me(t,e){var n,i={height:t},r=0;for(e=e?1:0;r<4;r+=2-e)i["margin"+(n=V[r])]=i["padding"+n]=t;return e&&(i.opacity=i.width=t),i}function ye(t,e,n){for(var i,r=(ge[e]||[]).concat(ge["*"]),o=0,s=r.length;o<s;o++)if(i=r[o].call(n,e,t))return i}function be(o,t,e){var n,s,i=0,r=pe.length,a=S.Deferred().always(function(){delete l.elem}),l=function(){if(s)return!1;for(var t=re||ve(),e=Math.max(0,u.startTime+u.duration-t),n=1-(e/u.duration||0),i=0,r=u.tweens.length;i<r;i++)u.tweens[i].run(n);return a.notifyWith(o,[u,n,e]),n<1&&r?e:(a.resolveWith(o,[u]),!1)},u=a.promise({elem:o,props:S.extend({},t),opts:S.extend(!0,{specialEasing:{}},e),originalProperties:t,originalOptions:e,startTime:re||ve(),duration:e.duration,tweens:[],createTween:function(t,e){var n=S.Tween(o,u.opts,t,e,u.opts.specialEasing[t]||u.opts.easing);return u.tweens.push(n),n},stop:function(t){var e=0,n=t?u.tweens.length:0;if(s)return this;for(s=!0;e<n;e++)u.tweens[e].run(1);return t?a.resolveWith(o,[u,t]):a.rejectWith(o,[u,t]),this}}),c=u.props;for(!function(t,e){var n,i,r,o,s;for(n in t)if(r=e[i=S.camelCase(n)],o=t[n],S.isArray(o)&&(r=o[1],o=t[n]=o[0]),n!==i&&(t[i]=o,delete t[n]),(s=S.cssHooks[i])&&"expand"in s)for(n in o=s.expand(o),delete t[i],o)n in t||(t[n]=o[n],e[n]=r);else e[i]=r}(c,u.opts.specialEasing);i<r;i++)if(n=pe[i].call(u,o,c,u.opts))return n;return S.map(c,ye,u),S.isFunction(u.opts.start)&&u.opts.start.call(o,u),S.fx.timer(S.extend(l,{elem:o,anim:u,queue:u.opts.queue})),u.progress(u.opts.progress).done(u.opts.done,u.opts.complete).fail(u.opts.fail).always(u.opts.always)}S.Animation=S.extend(be,{tweener:function(t,e){for(var n,i=0,r=(t=S.isFunction(t)?(e=t,["*"]):t.split(" ")).length;i<r;i++)n=t[i],ge[n]=ge[n]||[],ge[n].unshift(e)},prefilter:function(t,e){e?pe.unshift(t):pe.push(t)}}),S.speed=function(t,e,n){var i=t&&"object"==typeof t?S.extend({},t):{complete:n||!n&&e||S.isFunction(t)&&t,duration:t,easing:n&&e||e&&!S.isFunction(e)&&e};return i.duration=S.fx.off?0:"number"==typeof i.duration?i.duration:i.duration in S.fx.speeds?S.fx.speeds[i.duration]:S.fx.speeds._default,null!=i.queue&&!0!==i.queue||(i.queue="fx"),i.old=i.complete,i.complete=function(){S.isFunction(i.old)&&i.old.call(this),i.queue&&S.dequeue(this,i.queue)},i},S.fn.extend({fadeTo:function(t,e,n,i){return this.filter(U).css("opacity",0).show().end().animate({opacity:e},t,n,i)},animate:function(e,t,n,i){function r(){var t=be(this,S.extend({},e),s);(o||S._data(this,"finish"))&&t.stop(!0)}var o=S.isEmptyObject(e),s=S.speed(t,n,i);return r.finish=r,o||!1===s.queue?this.each(r):this.queue(s.queue,r)},stop:function(r,t,o){function s(t){var e=t.stop;delete t.stop,e(o)}return"string"!=typeof r&&(o=t,t=r,r=void 0),t&&!1!==r&&this.queue(r||"fx",[]),this.each(function(){var t=!0,e=null!=r&&r+"queueHooks",n=S.timers,i=S._data(this);if(e)i[e]&&i[e].stop&&s(i[e]);else for(e in i)i[e]&&i[e].stop&&fe.test(e)&&s(i[e]);for(e=n.length;e--;)n[e].elem!==this||null!=r&&n[e].queue!==r||(n[e].anim.stop(o),t=!1,n.splice(e,1));!t&&o||S.dequeue(this,r)})},finish:function(s){return!1!==s&&(s=s||"fx"),this.each(function(){var t,e=S._data(this),n=e[s+"queue"],i=e[s+"queueHooks"],r=S.timers,o=n?n.length:0;for(e.finish=!0,S.queue(this,s,[]),i&&i.stop&&i.stop.call(this,!0),t=r.length;t--;)r[t].elem===this&&r[t].queue===s&&(r[t].anim.stop(!0),r.splice(t,1));for(t=0;t<o;t++)n[t]&&n[t].finish&&n[t].finish.call(this);delete e.finish})}}),S.each(["toggle","show","hide"],function(t,i){var r=S.fn[i];S.fn[i]=function(t,e,n){return null==t||"boolean"==typeof t?r.apply(this,arguments):this.animate(me(i,!0),t,e,n)}}),S.each({slideDown:me("show"),slideUp:me("hide"),slideToggle:me("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(t,i){S.fn[t]=function(t,e,n){return this.animate(i,t,e,n)}}),S.timers=[],S.fx.tick=function(){var t,e=S.timers,n=0;for(re=S.now();n<e.length;n++)(t=e[n])()||e[n]!==t||e.splice(n--,1);e.length||S.fx.stop(),re=void 0},S.fx.timer=function(t){S.timers.push(t),t()?S.fx.start():S.timers.pop()},S.fx.interval=13,S.fx.start=function(){oe=oe||setInterval(S.fx.tick,S.fx.interval)},S.fx.stop=function(){clearInterval(oe),oe=null},S.fx.speeds={slow:600,fast:200,_default:400},S.fn.delay=function(i,t){return i=S.fx&&S.fx.speeds[i]||i,t=t||"fx",this.queue(t,function(t,e){var n=setTimeout(t,i);e.stop=function(){clearTimeout(n)}})},(ae=T.createElement("div")).setAttribute("className","t"),ae.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",ue=ae.getElementsByTagName("a")[0],ce=(le=T.createElement("select")).appendChild(T.createElement("option")),se=ae.getElementsByTagName("input")[0],ue.style.cssText="top:1px",m.getSetAttribute="t"!==ae.className,m.style=/top/.test(ue.getAttribute("style")),m.hrefNormalized="/a"===ue.getAttribute("href"),m.checkOn=!!se.value,m.optSelected=ce.selected,m.enctype=!!T.createElement("form").enctype,le.disabled=!0,m.optDisabled=!ce.disabled,(se=T.createElement("input")).setAttribute("value",""),m.input=""===se.getAttribute("value"),se.value="t",se.setAttribute("type","radio"),m.radioValue="t"===se.value;var we=/\r/g;S.fn.extend({val:function(n){var i,t,r,e=this[0];return arguments.length?(r=S.isFunction(n),this.each(function(t){var e;1===this.nodeType&&(null==(e=r?n.call(this,t,S(this).val()):n)?e="":"number"==typeof e?e+="":S.isArray(e)&&(e=S.map(e,function(t){return null==t?"":t+""})),(i=S.valHooks[this.type]||S.valHooks[this.nodeName.toLowerCase()])&&"set"in i&&void 0!==i.set(this,e,"value")||(this.value=e))})):e?(i=S.valHooks[e.type]||S.valHooks[e.nodeName.toLowerCase()])&&"get"in i&&void 0!==(t=i.get(e,"value"))?t:"string"==typeof(t=e.value)?t.replace(we,""):null==t?"":t:void 0}}),S.extend({valHooks:{option:{get:function(t){var e=S.find.attr(t,"value");return null!=e?e:S.trim(S.text(t))}},select:{get:function(t){for(var e,n,i=t.options,r=t.selectedIndex,o="select-one"===t.type||r<0,s=o?null:[],a=o?r+1:i.length,l=r<0?a:o?r:0;l<a;l++)if(((n=i[l]).selected||l===r)&&(m.optDisabled?!n.disabled:null===n.getAttribute("disabled"))&&(!n.parentNode.disabled||!S.nodeName(n.parentNode,"optgroup"))){if(e=S(n).val(),o)return e;s.push(e)}return s},set:function(t,e){for(var n,i,r=t.options,o=S.makeArray(e),s=r.length;s--;)if(i=r[s],0<=S.inArray(S.valHooks.option.get(i),o))try{i.selected=n=!0}catch(t){i.scrollHeight}else i.selected=!1;return n||(t.selectedIndex=-1),r}}}}),S.each(["radio","checkbox"],function(){S.valHooks[this]={set:function(t,e){if(S.isArray(e))return t.checked=0<=S.inArray(S(t).val(),e)}},m.checkOn||(S.valHooks[this].get=function(t){return null===t.getAttribute("value")?"on":t.value})});var _e,xe,Se=S.expr.attrHandle,Te=/^(?:checked|selected)$/i,Ce=m.getSetAttribute,Ee=m.input;S.fn.extend({attr:function(t,e){return z(this,S.attr,t,e,1<arguments.length)},removeAttr:function(t){return this.each(function(){S.removeAttr(this,t)})}}),S.extend({attr:function(t,e,n){var i,r,o=t.nodeType;if(t&&3!==o&&8!==o&&2!==o)return typeof t.getAttribute===L?S.prop(t,e,n):(1===o&&S.isXMLDoc(t)||(e=e.toLowerCase(),i=S.attrHooks[e]||(S.expr.match.bool.test(e)?xe:_e)),void 0===n?i&&"get"in i&&null!==(r=i.get(t,e))?r:null==(r=S.find.attr(t,e))?void 0:r:null!==n?i&&"set"in i&&void 0!==(r=i.set(t,n,e))?r:(t.setAttribute(e,n+""),n):void S.removeAttr(t,e))},removeAttr:function(t,e){var n,i,r=0,o=e&&e.match(O);if(o&&1===t.nodeType)for(;n=o[r++];)i=S.propFix[n]||n,S.expr.match.bool.test(n)?Ee&&Ce||!Te.test(n)?t[i]=!1:t[S.camelCase("default-"+n)]=t[i]=!1:S.attr(t,n,""),t.removeAttribute(Ce?n:i)},attrHooks:{type:{set:function(t,e){if(!m.radioValue&&"radio"===e&&S.nodeName(t,"input")){var n=t.value;return t.setAttribute("type",e),n&&(t.value=n),e}}}}}),xe={set:function(t,e,n){return!1===e?S.removeAttr(t,n):Ee&&Ce||!Te.test(n)?t.setAttribute(!Ce&&S.propFix[n]||n,n):t[S.camelCase("default-"+n)]=t[n]=!0,n}},S.each(S.expr.match.bool.source.match(/\w+/g),function(t,e){var o=Se[e]||S.find.attr;Se[e]=Ee&&Ce||!Te.test(e)?function(t,e,n){var i,r;return n||(r=Se[e],Se[e]=i,i=null!=o(t,e,n)?e.toLowerCase():null,Se[e]=r),i}:function(t,e,n){if(!n)return t[S.camelCase("default-"+e)]?e.toLowerCase():null}}),Ee&&Ce||(S.attrHooks.value={set:function(t,e,n){if(!S.nodeName(t,"input"))return _e&&_e.set(t,e,n);t.defaultValue=e}}),Ce||(_e={set:function(t,e,n){var i=t.getAttributeNode(n);if(i||t.setAttributeNode(i=t.ownerDocument.createAttribute(n)),i.value=e+="","value"===n||e===t.getAttribute(n))return e}},Se.id=Se.name=Se.coords=function(t,e,n){var i;if(!n)return(i=t.getAttributeNode(e))&&""!==i.value?i.value:null},S.valHooks.button={get:function(t,e){var n=t.getAttributeNode(e);if(n&&n.specified)return n.value},set:_e.set},S.attrHooks.contenteditable={set:function(t,e,n){_e.set(t,""!==e&&e,n)}},S.each(["width","height"],function(t,n){S.attrHooks[n]={set:function(t,e){if(""===e)return t.setAttribute(n,"auto"),e}}})),m.style||(S.attrHooks.style={get:function(t){return t.style.cssText||void 0},set:function(t,e){return t.style.cssText=e+""}});var ke=/^(?:input|select|textarea|button|object)$/i,Me=/^(?:a|area)$/i;S.fn.extend({prop:function(t,e){return z(this,S.prop,t,e,1<arguments.length)},removeProp:function(t){return t=S.propFix[t]||t,this.each(function(){try{this[t]=void 0,delete this[t]}catch(t){}})}}),S.extend({propFix:{for:"htmlFor",class:"className"},prop:function(t,e,n){var i,r,o=t.nodeType;if(t&&3!==o&&8!==o&&2!==o)return(1!==o||!S.isXMLDoc(t))&&(e=S.propFix[e]||e,r=S.propHooks[e]),void 0!==n?r&&"set"in r&&void 0!==(i=r.set(t,n,e))?i:t[e]=n:r&&"get"in r&&null!==(i=r.get(t,e))?i:t[e]},propHooks:{tabIndex:{get:function(t){var e=S.find.attr(t,"tabindex");return e?parseInt(e,10):ke.test(t.nodeName)||Me.test(t.nodeName)&&t.href?0:-1}}}}),m.hrefNormalized||S.each(["href","src"],function(t,e){S.propHooks[e]={get:function(t){return t.getAttribute(e,4)}}}),m.optSelected||(S.propHooks.selected={get:function(t){var e=t.parentNode;return e&&(e.selectedIndex,e.parentNode&&e.parentNode.selectedIndex),null}}),S.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){S.propFix[this.toLowerCase()]=this}),m.enctype||(S.propFix.enctype="encoding");var De=/[\t\r\n\f]/g;S.fn.extend({addClass:function(e){var t,n,i,r,o,s,a=0,l=this.length,u="string"==typeof e&&e;if(S.isFunction(e))return this.each(function(t){S(this).addClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(O)||[];a<l;a++)if(i=1===(n=this[a]).nodeType&&(n.className?(" "+n.className+" ").replace(De," "):" ")){for(o=0;r=t[o++];)i.indexOf(" "+r+" ")<0&&(i+=r+" ");s=S.trim(i),n.className!==s&&(n.className=s)}return this},removeClass:function(e){var t,n,i,r,o,s,a=0,l=this.length,u=0===arguments.length||"string"==typeof e&&e;if(S.isFunction(e))return this.each(function(t){S(this).removeClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(O)||[];a<l;a++)if(i=1===(n=this[a]).nodeType&&(n.className?(" "+n.className+" ").replace(De," "):"")){for(o=0;r=t[o++];)for(;0<=i.indexOf(" "+r+" ");)i=i.replace(" "+r+" "," ");s=e?S.trim(i):"",n.className!==s&&(n.className=s)}return this},toggleClass:function(r,e){var o=typeof r;return"boolean"==typeof e&&"string"==o?e?this.addClass(r):this.removeClass(r):S.isFunction(r)?this.each(function(t){S(this).toggleClass(r.call(this,t,this.className,e),e)}):this.each(function(){if("string"==o)for(var t,e=0,n=S(this),i=r.match(O)||[];t=i[e++];)n.hasClass(t)?n.removeClass(t):n.addClass(t);else o!==L&&"boolean"!=o||(this.className&&S._data(this,"__className__",this.className),this.className=this.className||!1===r?"":S._data(this,"__className__")||"")})},hasClass:function(t){for(var e=" "+t+" ",n=0,i=this.length;n<i;n++)if(1===this[n].nodeType&&0<=(" "+this[n].className+" ").replace(De," ").indexOf(e))return!0;return!1}}),S.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(t,n){S.fn[n]=function(t,e){return 0<arguments.length?this.on(n,null,t,e):this.trigger(n)}}),S.fn.extend({hover:function(t,e){return this.mouseenter(t).mouseleave(e||t)},bind:function(t,e,n){return this.on(t,null,e,n)},unbind:function(t,e){return this.off(t,null,e)},delegate:function(t,e,n,i){return this.on(e,t,n,i)},undelegate:function(t,e,n){return 1===arguments.length?this.off(t,"**"):this.off(e,t||"**",n)}});var Oe=S.now(),Ae=/\?/,Ne=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;S.parseJSON=function(t){if(p.JSON&&p.JSON.parse)return p.JSON.parse(t+"");var r,o=null,e=S.trim(t+"");return e&&!S.trim(e.replace(Ne,function(t,e,n,i){return r&&e&&(o=0),0===o?t:(r=n||e,o+=!i-!n,"")}))?Function("return "+e)():S.error("Invalid JSON: "+t)},S.parseXML=function(t){var e;if(!t||"string"!=typeof t)return null;try{p.DOMParser?e=(new DOMParser).parseFromString(t,"text/xml"):((e=new ActiveXObject("Microsoft.XMLDOM")).async="false",e.loadXML(t))}catch(t){e=void 0}return e&&e.documentElement&&!e.getElementsByTagName("parsererror").length||S.error("Invalid XML: "+t),e};var je,Re,Le=/#.*$/,Ie=/([?&])_=[^&]*/,Pe=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Fe=/^(?:GET|HEAD)$/,He=/^\/\//,qe=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,$e={},Ue={},Be="*/".concat("*");try{Re=location.href}catch(t){(Re=T.createElement("a")).href="",Re=Re.href}function Ve(o){return function(t,e){"string"!=typeof t&&(e=t,t="*");var n,i=0,r=t.toLowerCase().match(O)||[];if(S.isFunction(e))for(;n=r[i++];)"+"===n.charAt(0)?(n=n.slice(1)||"*",(o[n]=o[n]||[]).unshift(e)):(o[n]=o[n]||[]).push(e)}}function ze(e,r,o,s){var a={},l=e===Ue;function u(t){var i;return a[t]=!0,S.each(e[t]||[],function(t,e){var n=e(r,o,s);return"string"!=typeof n||l||a[n]?l?!(i=n):void 0:(r.dataTypes.unshift(n),u(n),!1)}),i}return u(r.dataTypes[0])||!a["*"]&&u("*")}function We(t,e){var n,i,r=S.ajaxSettings.flatOptions||{};for(i in e)void 0!==e[i]&&((r[i]?t:n=n||{})[i]=e[i]);return n&&S.extend(!0,t,n),t}je=qe.exec(Re.toLowerCase())||[],S.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Re,type:"GET",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(je[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Be,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":S.parseJSON,"text xml":S.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(t,e){return e?We(We(t,S.ajaxSettings),e):We(S.ajaxSettings,t)},ajaxPrefilter:Ve($e),ajaxTransport:Ve(Ue),ajax:function(t,e){"object"==typeof t&&(e=t,t=void 0),e=e||{};var n,i,c,h,d,f,p,r,g=S.ajaxSetup({},e),v=g.context||g,m=g.context&&(v.nodeType||v.jquery)?S(v):S.event,y=S.Deferred(),b=S.Callbacks("once memory"),w=g.statusCode||{},o={},s={},_=0,a="canceled",x={readyState:0,getResponseHeader:function(t){var e;if(2===_){if(!r)for(r={};e=Pe.exec(h);)r[e[1].toLowerCase()]=e[2];e=r[t.toLowerCase()]}return null==e?null:e},getAllResponseHeaders:function(){return 2===_?h:null},setRequestHeader:function(t,e){var n=t.toLowerCase();return _||(t=s[n]=s[n]||t,o[t]=e),this},overrideMimeType:function(t){return _||(g.mimeType=t),this},statusCode:function(t){var e;if(t)if(_<2)for(e in t)w[e]=[w[e],t[e]];else x.always(t[x.status]);return this},abort:function(t){var e=t||a;return p&&p.abort(e),l(0,e),this}};if(y.promise(x).complete=b.add,x.success=x.done,x.error=x.fail,g.url=((t||g.url||Re)+"").replace(Le,"").replace(He,je[1]+"//"),g.type=e.method||e.type||g.method||g.type,g.dataTypes=S.trim(g.dataType||"*").toLowerCase().match(O)||[""],null==g.crossDomain&&(n=qe.exec(g.url.toLowerCase()),g.crossDomain=!(!n||n[1]===je[1]&&n[2]===je[2]&&(n[3]||("http:"===n[1]?"80":"443"))===(je[3]||("http:"===je[1]?"80":"443")))),g.data&&g.processData&&"string"!=typeof g.data&&(g.data=S.param(g.data,g.traditional)),ze($e,g,e,x),2===_)return x;for(i in(f=g.global)&&0==S.active++&&S.event.trigger("ajaxStart"),g.type=g.type.toUpperCase(),g.hasContent=!Fe.test(g.type),c=g.url,g.hasContent||(g.data&&(c=g.url+=(Ae.test(c)?"&":"?")+g.data,delete g.data),!1===g.cache&&(g.url=Ie.test(c)?c.replace(Ie,"$1_="+Oe++):c+(Ae.test(c)?"&":"?")+"_="+Oe++)),g.ifModified&&(S.lastModified[c]&&x.setRequestHeader("If-Modified-Since",S.lastModified[c]),S.etag[c]&&x.setRequestHeader("If-None-Match",S.etag[c])),(g.data&&g.hasContent&&!1!==g.contentType||e.contentType)&&x.setRequestHeader("Content-Type",g.contentType),x.setRequestHeader("Accept",g.dataTypes[0]&&g.accepts[g.dataTypes[0]]?g.accepts[g.dataTypes[0]]+("*"!==g.dataTypes[0]?", "+Be+"; q=0.01":""):g.accepts["*"]),g.headers)x.setRequestHeader(i,g.headers[i]);if(g.beforeSend&&(!1===g.beforeSend.call(v,x,g)||2===_))return x.abort();for(i in a="abort",{success:1,error:1,complete:1})x[i](g[i]);if(p=ze(Ue,g,e,x)){x.readyState=1,f&&m.trigger("ajaxSend",[x,g]),g.async&&0<g.timeout&&(d=setTimeout(function(){x.abort("timeout")},g.timeout));try{_=1,p.send(o,l)}catch(t){if(!(_<2))throw t;l(-1,t)}}else l(-1,"No Transport");function l(t,e,n,i){var r,o,s,a,l,u=e;2!==_&&(_=2,d&&clearTimeout(d),p=void 0,h=i||"",x.readyState=0<t?4:0,r=200<=t&&t<300||304===t,n&&(a=function(t,e,n){for(var i,r,o,s,a=t.contents,l=t.dataTypes;"*"===l[0];)l.shift(),void 0===r&&(r=t.mimeType||e.getResponseHeader("Content-Type"));if(r)for(s in a)if(a[s]&&a[s].test(r)){l.unshift(s);break}if(l[0]in n)o=l[0];else{for(s in n){if(!l[0]||t.converters[s+" "+l[0]]){o=s;break}i=i||s}o=o||i}if(o)return o!==l[0]&&l.unshift(o),n[o]}(g,x,n)),a=function(t,e,n,i){var r,o,s,a,l,u={},c=t.dataTypes.slice();if(c[1])for(s in t.converters)u[s.toLowerCase()]=t.converters[s];for(o=c.shift();o;)if(t.responseFields[o]&&(n[t.responseFields[o]]=e),!l&&i&&t.dataFilter&&(e=t.dataFilter(e,t.dataType)),l=o,o=c.shift())if("*"===o)o=l;else if("*"!==l&&l!==o){if(!(s=u[l+" "+o]||u["* "+o]))for(r in u)if((a=r.split(" "))[1]===o&&(s=u[l+" "+a[0]]||u["* "+a[0]])){!0===s?s=u[r]:!0!==u[r]&&(o=a[0],c.unshift(a[1]));break}if(!0!==s)if(s&&t.throws)e=s(e);else try{e=s(e)}catch(t){return{state:"parsererror",error:s?t:"No conversion from "+l+" to "+o}}}return{state:"success",data:e}}(g,a,x,r),r?(g.ifModified&&((l=x.getResponseHeader("Last-Modified"))&&(S.lastModified[c]=l),(l=x.getResponseHeader("etag"))&&(S.etag[c]=l)),204===t||"HEAD"===g.type?u="nocontent":304===t?u="notmodified":(u=a.state,o=a.data,r=!(s=a.error))):(s=u,!t&&u||(u="error",t<0&&(t=0))),x.status=t,x.statusText=(e||u)+"",r?y.resolveWith(v,[o,u,x]):y.rejectWith(v,[x,u,s]),x.statusCode(w),w=void 0,f&&m.trigger(r?"ajaxSuccess":"ajaxError",[x,g,r?o:s]),b.fireWith(v,[x,u]),f&&(m.trigger("ajaxComplete",[x,g]),--S.active||S.event.trigger("ajaxStop")))}return x},getJSON:function(t,e,n){return S.get(t,e,n,"json")},getScript:function(t,e){return S.get(t,void 0,e,"script")}}),S.each(["get","post"],function(t,r){S[r]=function(t,e,n,i){return S.isFunction(e)&&(i=i||n,n=e,e=void 0),S.ajax({url:t,type:r,dataType:i,data:e,success:n})}}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(t,e){S.fn[e]=function(t){return this.on(e,t)}}),S._evalUrl=function(t){return S.ajax({url:t,type:"GET",dataType:"script",async:!1,global:!1,throws:!0})},S.fn.extend({wrapAll:function(e){if(S.isFunction(e))return this.each(function(t){S(this).wrapAll(e.call(this,t))});if(this[0]){var t=S(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){for(var t=this;t.firstChild&&1===t.firstChild.nodeType;)t=t.firstChild;return t}).append(this)}return this},wrapInner:function(n){return S.isFunction(n)?this.each(function(t){S(this).wrapInner(n.call(this,t))}):this.each(function(){var t=S(this),e=t.contents();e.length?e.wrapAll(n):t.append(n)})},wrap:function(e){var n=S.isFunction(e);return this.each(function(t){S(this).wrapAll(n?e.call(this,t):e)})},unwrap:function(){return this.parent().each(function(){S.nodeName(this,"body")||S(this).replaceWith(this.childNodes)}).end()}}),S.expr.filters.hidden=function(t){return t.offsetWidth<=0&&t.offsetHeight<=0||!m.reliableHiddenOffsets()&&"none"===(t.style&&t.style.display||S.css(t,"display"))},S.expr.filters.visible=function(t){return!S.expr.filters.hidden(t)};var Je=/%20/g,Ge=/\[\]$/,Xe=/\r?\n/g,Ye=/^(?:submit|button|image|reset|file)$/i,Ke=/^(?:input|select|textarea|keygen)/i;function Qe(n,t,i,r){var e;if(S.isArray(t))S.each(t,function(t,e){i||Ge.test(n)?r(n,e):Qe(n+"["+("object"==typeof e?t:"")+"]",e,i,r)});else if(i||"object"!==S.type(t))r(n,t);else for(e in t)Qe(n+"["+e+"]",t[e],i,r)}S.param=function(t,e){function n(t,e){e=S.isFunction(e)?e():null==e?"":e,r[r.length]=encodeURIComponent(t)+"="+encodeURIComponent(e)}var i,r=[];if(void 0===e&&(e=S.ajaxSettings&&S.ajaxSettings.traditional),S.isArray(t)||t.jquery&&!S.isPlainObject(t))S.each(t,function(){n(this.name,this.value)});else for(i in t)Qe(i,t[i],e,n);return r.join("&").replace(Je,"+")},S.fn.extend({serialize:function(){return S.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var t=S.prop(this,"elements");return t?S.makeArray(t):this}).filter(function(){var t=this.type;return this.name&&!S(this).is(":disabled")&&Ke.test(this.nodeName)&&!Ye.test(t)&&(this.checked||!W.test(t))}).map(function(t,e){var n=S(this).val();return null==n?null:S.isArray(n)?S.map(n,function(t){return{name:e.name,value:t.replace(Xe,"\r\n")}}):{name:e.name,value:n.replace(Xe,"\r\n")}}).get()}}),S.ajaxSettings.xhr=void 0!==p.ActiveXObject?function(){return!this.isLocal&&/^(get|post|head|put|delete|options)$/i.test(this.type)&&nn()||function(){try{return new p.ActiveXObject("Microsoft.XMLHTTP")}catch(t){}}()}:nn;var Ze=0,tn={},en=S.ajaxSettings.xhr();function nn(){try{return new p.XMLHttpRequest}catch(t){}}p.ActiveXObject&&S(p).on("unload",function(){for(var t in tn)tn[t](void 0,!0)}),m.cors=!!en&&"withCredentials"in en,(en=m.ajax=!!en)&&S.ajaxTransport(function(l){var u;if(!l.crossDomain||m.cors)return{send:function(t,o){var e,s=l.xhr(),a=++Ze;if(s.open(l.type,l.url,l.async,l.username,l.password),l.xhrFields)for(e in l.xhrFields)s[e]=l.xhrFields[e];for(e in l.mimeType&&s.overrideMimeType&&s.overrideMimeType(l.mimeType),l.crossDomain||t["X-Requested-With"]||(t["X-Requested-With"]="XMLHttpRequest"),t)void 0!==t[e]&&s.setRequestHeader(e,t[e]+"");s.send(l.hasContent&&l.data||null),u=function(t,e){var n,i,r;if(u&&(e||4===s.readyState))if(delete tn[a],u=void 0,s.onreadystatechange=S.noop,e)4!==s.readyState&&s.abort();else{r={},n=s.status,"string"==typeof s.responseText&&(r.text=s.responseText);try{i=s.statusText}catch(t){i=""}n||!l.isLocal||l.crossDomain?1223===n&&(n=204):n=r.text?200:404}r&&o(n,i,r,s.getAllResponseHeaders())},l.async?4===s.readyState?setTimeout(u):s.onreadystatechange=tn[a]=u:u()},abort:function(){u&&u(void 0,!0)}}}),S.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(t){return S.globalEval(t),t}}}),S.ajaxPrefilter("script",function(t){void 0===t.cache&&(t.cache=!1),t.crossDomain&&(t.type="GET",t.global=!1)}),S.ajaxTransport("script",function(e){if(e.crossDomain){var i,r=T.head||S("head")[0]||T.documentElement;return{send:function(t,n){(i=T.createElement("script")).async=!0,e.scriptCharset&&(i.charset=e.scriptCharset),i.src=e.url,i.onload=i.onreadystatechange=function(t,e){!e&&i.readyState&&!/loaded|complete/.test(i.readyState)||(i.onload=i.onreadystatechange=null,i.parentNode&&i.parentNode.removeChild(i),i=null,e||n(200,"success"))},r.insertBefore(i,r.firstChild)},abort:function(){i&&i.onload(void 0,!0)}}}});var rn=[],on=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var t=rn.pop()||S.expando+"_"+Oe++;return this[t]=!0,t}}),S.ajaxPrefilter("json jsonp",function(t,e,n){var i,r,o,s=!1!==t.jsonp&&(on.test(t.url)?"url":"string"==typeof t.data&&!(t.contentType||"").indexOf("application/x-www-form-urlencoded")&&on.test(t.data)&&"data");if(s||"jsonp"===t.dataTypes[0])return i=t.jsonpCallback=S.isFunction(t.jsonpCallback)?t.jsonpCallback():t.jsonpCallback,s?t[s]=t[s].replace(on,"$1"+i):!1!==t.jsonp&&(t.url+=(Ae.test(t.url)?"&":"?")+t.jsonp+"="+i),t.converters["script json"]=function(){return o||S.error(i+" was not called"),o[0]},t.dataTypes[0]="json",r=p[i],p[i]=function(){o=arguments},n.always(function(){p[i]=r,t[i]&&(t.jsonpCallback=e.jsonpCallback,rn.push(i)),o&&S.isFunction(r)&&r(o[0]),o=r=void 0}),"script"}),S.parseHTML=function(t,e,n){if(!t||"string"!=typeof t)return null;"boolean"==typeof e&&(n=e,e=!1),e=e||T;var i=b.exec(t),r=!n&&[];return i?[e.createElement(i[1])]:(i=S.buildFragment([t],e,r),r&&r.length&&S(r).remove(),S.merge([],i.childNodes))};var sn=S.fn.load;S.fn.load=function(t,e,n){if("string"!=typeof t&&sn)return sn.apply(this,arguments);var i,r,o,s=this,a=t.indexOf(" ");return 0<=a&&(i=S.trim(t.slice(a,t.length)),t=t.slice(0,a)),S.isFunction(e)?(n=e,e=void 0):e&&"object"==typeof e&&(o="POST"),0<s.length&&S.ajax({url:t,type:o,dataType:"html",data:e}).done(function(t){r=arguments,s.html(i?S("<div>").append(S.parseHTML(t)).find(i):t)}).complete(n&&function(t,e){s.each(n,r||[t.responseText,e,t])}),this},S.expr.filters.animated=function(e){return S.grep(S.timers,function(t){return e===t.elem}).length};var an=p.document.documentElement;function ln(t){return S.isWindow(t)?t:9===t.nodeType&&(t.defaultView||t.parentWindow)}S.offset={setOffset:function(t,e,n){var i,r,o,s,a,l,u=S.css(t,"position"),c=S(t),h={};"static"===u&&(t.style.position="relative"),a=c.offset(),o=S.css(t,"top"),l=S.css(t,"left"),r=("absolute"===u||"fixed"===u)&&-1<S.inArray("auto",[o,l])?(s=(i=c.position()).top,i.left):(s=parseFloat(o)||0,parseFloat(l)||0),S.isFunction(e)&&(e=e.call(t,n,a)),null!=e.top&&(h.top=e.top-a.top+s),null!=e.left&&(h.left=e.left-a.left+r),"using"in e?e.using.call(t,h):c.css(h)}},S.fn.extend({offset:function(e){if(arguments.length)return void 0===e?this:this.each(function(t){S.offset.setOffset(this,e,t)});var t,n,i={top:0,left:0},r=this[0],o=r&&r.ownerDocument;return o?(t=o.documentElement,S.contains(t,r)?(typeof r.getBoundingClientRect!==L&&(i=r.getBoundingClientRect()),n=ln(o),{top:i.top+(n.pageYOffset||t.scrollTop)-(t.clientTop||0),left:i.left+(n.pageXOffset||t.scrollLeft)-(t.clientLeft||0)}):i):void 0},position:function(){if(this[0]){var t,e,n={top:0,left:0},i=this[0];return"fixed"===S.css(i,"position")?e=i.getBoundingClientRect():(t=this.offsetParent(),e=this.offset(),S.nodeName(t[0],"html")||(n=t.offset()),n.top+=S.css(t[0],"borderTopWidth",!0),n.left+=S.css(t[0],"borderLeftWidth",!0)),{top:e.top-n.top-S.css(i,"marginTop",!0),left:e.left-n.left-S.css(i,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){for(var t=this.offsetParent||an;t&&!S.nodeName(t,"html")&&"static"===S.css(t,"position");)t=t.offsetParent;return t||an})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,r){var o=/Y/.test(r);S.fn[e]=function(t){return z(this,function(t,e,n){var i=ln(t);if(void 0===n)return i?r in i?i[r]:i.document.documentElement[e]:t[e];i?i.scrollTo(o?S(i).scrollLeft():n,o?n:S(i).scrollTop()):t[e]=n},e,t,arguments.length,null)}}),S.each(["top","left"],function(t,n){S.cssHooks[n]=Ut(m.pixelPosition,function(t,e){if(e)return e=At(t,n),qt.test(e)?S(t).position()[n]+"px":e})}),S.each({Height:"height",Width:"width"},function(o,s){S.each({padding:"inner"+o,content:s,"":"outer"+o},function(i,t){S.fn[t]=function(t,e){var n=arguments.length&&(i||"boolean"!=typeof t),r=i||(!0===t||!0===e?"margin":"border");return z(this,function(t,e,n){var i;return S.isWindow(t)?t.document.documentElement["client"+o]:9===t.nodeType?(i=t.documentElement,Math.max(t.body["scroll"+o],i["scroll"+o],t.body["offset"+o],i["offset"+o],i["client"+o])):void 0===n?S.css(t,e,r):S.style(t,e,n,r)},s,n?t:void 0,n,null)}})}),S.fn.size=function(){return this.length},S.fn.andSelf=S.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return S});var un=p.jQuery,cn=p.$;return S.noConflict=function(t){return p.$===S&&(p.$=cn),t&&p.jQuery===S&&(p.jQuery=un),S},typeof t===L&&(p.jQuery=p.$=S),S}),function(){var Xn,t,e,Yn="3.0.0",Kn=1,Qn=2,Zn=4,ti=8,ei=16,ni=32,ii=64,ri=128,oi=256,si=30,ai="...",li=150,ui=16,ci=0,hi=1,di=2,fi="Expected a function",pi="__lodash_placeholder__",gi="[object Arguments]",vi="[object Array]",mi="[object Boolean]",yi="[object Date]",bi="[object Error]",wi="[object Function]",n="[object Map]",_i="[object Number]",xi="[object Object]",Si="[object RegExp]",i="[object Set]",Ti="[object String]",r="[object WeakMap]",Ci="[object ArrayBuffer]",Ei="[object Float32Array]",ki="[object Float64Array]",Mi="[object Int8Array]",Di="[object Int16Array]",Oi="[object Int32Array]",Ai="[object Uint8Array]",Ni="[object Uint8ClampedArray]",ji="[object Uint16Array]",Ri="[object Uint32Array]",Li=/\b__p \+= '';/g,Ii=/\b(__p \+=) '' \+/g,Pi=/(__e\(.*?\)|\b__t\)) \+\n'';/g,Fi=/&(?:amp|lt|gt|quot|#39|#96);/g,Hi=/[&<>"'`]/g,qi=RegExp(Fi.source),$i=RegExp(Hi.source),Ui=/<%-([\s\S]+?)%>/g,Bi=/<%([\s\S]+?)%>/g,Vi=/<%=([\s\S]+?)%>/g,zi=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,Wi=/\w*$/,Ji=/^\s*function[ \n\r\t]+\w/,Gi=/^0[xX]/,Xi=/^\[object .+?Constructor\]$/,Yi=/[\xc0-\xd6\xd8-\xde\xdf-\xf6\xf8-\xff]/g,Ki=/($^)/,Qi=/[.*+?^${}()|[\]\/\\]/g,Zi=RegExp(Qi.source),tr=/\bthis\b/,er=/['\n\r\u2028\u2029\\]/g,nr=(t="[A-Z\\xc0-\\xd6\\xd8-\\xde]",e="[a-z\\xdf-\\xf6\\xf8-\\xff]+",RegExp(t+"{2,}(?="+t+e+")|"+t+"?"+e+"|"+t+"+|[0-9]+","g")),ir=" \t\v\f \ufeff\n\r\u2028\u2029 ᠎              ",rr=["Array","ArrayBuffer","Date","Error","Float32Array","Float64Array","Function","Int8Array","Int16Array","Int32Array","Math","Number","Object","RegExp","Set","String","_","clearTimeout","document","isFinite","parseInt","setTimeout","TypeError","Uint8Array","Uint8ClampedArray","Uint16Array","Uint32Array","WeakMap","window","WinRTError"],or=-1,sr={};sr[Ei]=sr[ki]=sr[Mi]=sr[Di]=sr[Oi]=sr[Ai]=sr[Ni]=sr[ji]=sr[Ri]=!0,sr[gi]=sr[vi]=sr[Ci]=sr[mi]=sr[yi]=sr[bi]=sr[wi]=sr[n]=sr[_i]=sr[xi]=sr[Si]=sr[i]=sr[Ti]=sr[r]=!1;var ar={};ar[gi]=ar[vi]=ar[Ci]=ar[mi]=ar[yi]=ar[Ei]=ar[ki]=ar[Mi]=ar[Di]=ar[Oi]=ar[_i]=ar[xi]=ar[Si]=ar[Ti]=ar[Ai]=ar[Ni]=ar[ji]=ar[Ri]=!0,ar[bi]=ar[wi]=ar[n]=ar[i]=ar[r]=!1;var lr={leading:!1,maxWait:0,trailing:!1},o={"À":"A","Á":"A","Â":"A","Ã":"A","Ä":"A","Å":"A","à":"a","á":"a","â":"a","ã":"a","ä":"a","å":"a","Ç":"C","ç":"c","Ð":"D","ð":"d","È":"E","É":"E","Ê":"E","Ë":"E","è":"e","é":"e","ê":"e","ë":"e","Ì":"I","Í":"I","Î":"I","Ï":"I","ì":"i","í":"i","î":"i","ï":"i","Ñ":"N","ñ":"n","Ò":"O","Ó":"O","Ô":"O","Õ":"O","Ö":"O","Ø":"O","ò":"o","ó":"o","ô":"o","õ":"o","ö":"o","ø":"o","Ù":"U","Ú":"U","Û":"U","Ü":"U","ù":"u","ú":"u","û":"u","ü":"u","Ý":"Y","ý":"y","ÿ":"y","Æ":"Ae","æ":"ae","Þ":"Th","þ":"th","ß":"ss"},s={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","`":"&#96;"},a={"&amp;":"&","&lt;":"<","&gt;":">","&quot;":'"',"&#39;":"'","&#96;":"`"},l={function:!0,object:!0},u={"\\":"\\","'":"'","\n":"n","\r":"r","\u2028":"u2028","\u2029":"u2029"},ur=l[typeof window]&&window!==(this&&this.window)?window:this,c=l[typeof exports]&&exports&&!exports.nodeType&&exports,h=l[typeof module]&&module&&!module.nodeType&&module,d=c&&h&&"object"==typeof global&&global;!d||d.global!==d&&d.window!==d&&d.self!==d||(ur=d);var f=h&&h.exports===c&&c;function cr(t,e){if(t!==e){var n=t==t,i=e==e;if(e<t||!n||void 0===t&&i)return 1;if(t<e||!i||void 0===e&&n)return-1}return 0}function hr(t,e,n){if(e!=e)return xr(t,n);for(var i=(n||0)-1,r=t.length;++i<r;)if(t[i]===e)return i;return-1}function dr(t,e){var n=t.length;for(t.sort(e);n--;)t[n]=t[n].value;return t}function fr(t){return"string"==typeof t?t:null==t?"":t+""}function pr(t){return t.charCodeAt(0)}function gr(t,e){for(var n=-1,i=t.length;++n<i&&-1<e.indexOf(t.charAt(n)););return n}function vr(t,e){for(var n=t.length;n--&&-1<e.indexOf(t.charAt(n)););return n}function mr(t,e){return cr(t.criteria,e.criteria)||t.index-e.index}function yr(t,e){for(var n=-1,i=t.criteria,r=e.criteria,o=i.length;++n<o;){var s=cr(i[n],r[n]);if(s)return s}return t.index-e.index}function br(t){return o[t]}function wr(t){return s[t]}function _r(t){return"\\"+u[t]}function xr(t,e,n){for(var i=t.length,r=n?e||i:(e||0)-1;n?r--:++r<i;){var o=t[r];if(o!=o)return r}return-1}function Sr(t){return t&&"object"==typeof t||!1}function p(t){return t<=160&&9<=t&&t<=13||32==t||160==t||5760==t||6158==t||8192<=t&&(t<=8202||8232==t||8233==t||8239==t||8287==t||12288==t||65279==t)}function Tr(t,e){for(var n=-1,i=t.length,r=-1,o=[];++n<i;)t[n]===e&&(t[n]=pi,o[++r]=n);return o}function Cr(t){for(var e=-1,n=t.length;++e<n&&p(t.charCodeAt(e)););return e}function Er(t){for(var e=t.length;e--&&p(t.charCodeAt(e)););return e}function kr(t){return a[t]}var Mr=function e(n){var M=(n=n?Mr.defaults(ur.Object(),n,Mr.pick(ur,rr)):ur).Array,t=n.Date,i=n.Error,v=n.Function,r=n.Math,o=n.Number,d=n.Object,m=n.RegExp,s=n.String,y=n.TypeError,a=M.prototype,l=d.prototype,u=(u=n.window)&&u.document,c=v.prototype.toString,h=Bt("length"),b=l.hasOwnProperty,f=0,w=l.toString,p=n._,g=m("^"+jn(w).replace(/toString|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),_=yn(_=n.ArrayBuffer)&&_,x=yn(x=_&&new _(0).slice)&&x,S=r.ceil,T=n.clearTimeout,C=r.floor,E=yn(E=d.getPrototypeOf)&&E,k=a.push,D=l.propertyIsEnumerable,O=yn(O=n.Set)&&O,A=n.setTimeout,N=a.splice,j=yn(j=n.Uint8Array)&&j,R=(a.unshift,yn(R=n.WeakMap)&&R),L=function(){try{var t=yn(t=n.Float64Array)&&t,e=new t(new _(10),0,1)&&t}catch(t){}return e}(),I=yn(I=M.isArray)&&I,P=yn(P=d.create)&&P,F=n.isFinite,H=yn(H=d.keys)&&H,q=r.max,$=r.min,U=yn(U=t.now)&&U,B=yn(B=o.isFinite)&&B,V=n.parseInt,z=r.random,W=o.NEGATIVE_INFINITY,J=o.POSITIVE_INFINITY,G=r.pow(2,32)-1,X=G-1,Y=G>>>1,K=L?L.BYTES_PER_ELEMENT:0,Q=r.pow(2,53)-1,Z=R&&new R;function tt(t){if(Sr(t)&&!dn(t)){if(t instanceof et)return t;if(b.call(t,"__wrapped__"))return new et(t.__wrapped__,t.__chain__,at(t.__actions__))}return new et(t)}function et(t,e,n){this.__actions__=n||[],this.__chain__=!!e,this.__wrapped__=t}var nt=tt.support={};function it(t){this.actions=null,this.dir=1,this.dropCount=0,this.filtered=!1,this.iteratees=null,this.takeCount=J,this.views=null,this.wrapped=t}function rt(){this.__data__={}}function ot(t){var e=t?t.length:0;for(this.data={hash:P(null),set:new O};e--;)this.push(t[e])}function st(t,e){var n=t.data;return("string"==typeof e||mn(e)?n.set.has(e):n.hash[e])?0:-1}function at(t,e){var n=-1,i=t.length;for(e=e||M(i);++n<i;)e[n]=t[n];return e}function lt(t,e){for(var n=-1,i=t.length;++n<i&&!1!==e(t[n],n,t););return t}function ut(t,e){for(var n=-1,i=t.length;++n<i;)if(!e(t[n],n,t))return!1;return!0}function ct(t,e){for(var n=-1,i=t.length,r=-1,o=[];++n<i;){var s=t[n];e(s,n,t)&&(o[++r]=s)}return o}function ht(t,e){for(var n=-1,i=t.length,r=M(i);++n<i;)r[n]=e(t[n],n,t);return r}function dt(t){for(var e=-1,n=t.length,i=W;++e<n;){var r=t[e];i<r&&(i=r)}return i}function ft(t,e,n,i){var r=-1,o=t.length;for(i&&o&&(n=t[++r]);++r<o;)n=e(n,t[r],r,t);return n}function pt(t,e,n,i){var r=t.length;for(i&&r&&(n=t[--r]);r--;)n=e(n,t[r],r,t);return n}function gt(t,e){for(var n=-1,i=t.length;++n<i;)if(e(t[n],n,t))return!0;return!1}function vt(t,e){return void 0===t?e:t}function mt(t,e,n,i){return void 0!==t&&b.call(i,n)?t:e}function yt(t,e,n){var i=kn(e);if(!n)return wt(e,t,i);for(var r=-1,o=i.length;++r<o;){var s=i[r],a=t[s],l=n(a,e[s],s,t,e);(l==l?l===a:a!=a)&&(void 0!==a||s in t)||(t[s]=l)}return t}function bt(t,e){for(var n=-1,i=t.length,r=be(i),o=e.length,s=M(o);++n<o;){var a=e[n];r?(a=parseFloat(a),s[n]=me(a,i)?t[a]:Xn):s[n]=t[a]}return s}function wt(t,e,n){n||(n=e,e={});for(var i=-1,r=n.length;++i<r;){var o=n[i];e[o]=t[o]}return e}function _t(t,e,n){var i=typeof t;return"function"==i?void 0!==e&&function(t){var e=tt.support,n=!(e.funcNames?t.name:e.funcDecomp);if(!n){var i=c.call(t);e.funcNames||(n=!Ji.test(i)),n||(n=tr.test(i)||yn(t),Wt(t,n))}return n}(t)?te(t,e,n):t:null==t?Vn:"object"==i?Ut(t,!n):Bt(n?fr(t):t)}function xt(n,i,r,t,e,o,s){var a;if(r&&(a=e?r(n,t,e):r(n)),void 0!==a)return a;if(!mn(n))return n;var l=dn(n);if(l){if(a=function(t){var e=t.length,n=new t.constructor(e);return e&&"string"==typeof t[0]&&b.call(t,"index")&&(n.index=t.index,n.input=t.input),n}(n),!i)return at(n,a)}else{var u=w.call(n),c=u==wi;if(u!=xi&&u!=gi&&(!c||e))return ar[u]?function(t,e,n){var i=t.constructor;switch(e){case Ci:return ee(t);case mi:case yi:return new i(+t);case Ei:case ki:case Mi:case Di:case Oi:case Ai:case Ni:case ji:case Ri:var r=t.buffer;return new i(n?ee(r):r,t.byteOffset,t.length);case _i:case Ti:return new i(t);case Si:var o=new i(t.source,Wi.exec(t));o.lastIndex=t.lastIndex}return o}(n,u,i):e?n:{};if(a=function(t){var e=t.constructor;return"function"==typeof e&&e instanceof e||(e=d),new e}(c?{}:n),!i)return wt(n,a,kn(n))}s=s||[];for(var h=(o=o||[]).length;h--;)if(o[h]==n)return s[h];return o.push(n),s.push(a),(l?lt:It)(n,function(t,e){a[e]=xt(t,i,r,e,n,o,s)}),a}!function(t){nt.funcDecomp=!yn(n.WinRTError)&&tr.test(e),nt.funcNames="string"==typeof v.name;try{nt.dom=11===u.createDocumentFragment().nodeType}catch(t){nt.dom=!1}try{nt.nonEnumArgs=!D.call(arguments,1)}catch(t){nt.nonEnumArgs=!0}}(0,0),tt.templateSettings={escape:Ui,evaluate:Bi,interpolate:Vi,variable:"",imports:{_:tt}};var St=function(t){if(mn(t)){Tt.prototype=t;var e=new Tt;Tt.prototype=null}return e||n.Object()};function Tt(){}function Ct(t,e,n,i){if(!vn(t))throw new y(fi);return A(function(){t.apply(Xn,Jt(n,i))},e)}function Et(t,e){var n=t?t.length:0,i=[];if(!n)return i;var r=-1,o=ve(),s=o==hr,a=s&&200<=e.length&&se(e),l=e.length;a&&(o=st,s=!1,e=a);t:for(;++r<n;){var u=t[r];if(s&&u==u){for(var c=l;c--;)if(e[c]===u)continue t;i.push(u)}else o(e,u)<0&&i.push(u)}return i}function kt(t,e){var n=t?t.length:0;if(!be(n))return It(t,e);for(var i=-1,r=De(t);++i<n&&!1!==e(r[i],i,r););return t}function Mt(t,e){var n=t?t.length:0;if(!be(n))return Pt(t,e);for(var i=De(t);n--&&!1!==e(i[n],n,i););return t}function Dt(t,i){var r=!0;return kt(t,function(t,e,n){return r=!!i(t,e,n)}),r}function Ot(t,i){var r=[];return kt(t,function(t,e,n){i(t,e,n)&&r.push(t)}),r}function At(t,i,e,r){var o;return e(t,function(t,e,n){if(i(t,e,n))return o=r?e:t,!1}),o}function Nt(t,e,n,i){for(var r=(i||0)-1,o=t.length,s=-1,a=[];++r<o;){var l=t[r];if(Sr(l)&&be(l.length)&&(dn(l)||hn(l))){e&&(l=Nt(l,e,n));var u=-1,c=l.length;for(a.length+=c;++u<c;)a[++s]=l[u]}else n||(a[++s]=l)}return a}function jt(t,e,n){for(var i=-1,r=De(t),o=n(t),s=o.length;++i<s;){var a=o[i];if(!1===e(r[a],a,r))break}return t}function Rt(t,e,n){for(var i=De(t),r=n(t),o=r.length;o--;){var s=r[o];if(!1===e(i[s],s,i))break}return t}function Lt(t,e){return jt(t,e,Mn)}function It(t,e){return jt(t,e,kn)}function Pt(t,e){return Rt(t,e,kn)}function Ft(t,e){for(var n=-1,i=e.length,r=-1,o=[];++n<i;){var s=e[n];vn(t[s])&&(o[++r]=s)}return o}function Ht(t,e,n,i,r,o){if(t===e)return 0!==t||1/t==1/e;var s=typeof t,a=typeof e;return"function"!=s&&"object"!=s&&"function"!=a&&"object"!=a||null==t||null==e?t!=t&&e!=e:function(t,e,n,i,r,o,s){var a=dn(t),l=dn(e),u=vi,c=vi;a||((u=w.call(t))==gi?u=xi:u!=xi&&(a=Sn(t))),l||((c=w.call(e))==gi?c=xi:c!=xi&&(l=Sn(e)));var h=u==xi,d=c==xi,f=u==c;if(f&&!a&&!h)return function(t,e,n){switch(n){case mi:case yi:return+t==+e;case bi:return t.name==e.name&&t.message==e.message;case _i:return t!=+t?e!=+e:0==t?1/t==1/e:t==+e;case Si:case Ti:return t==fr(e)}return!1}(t,e,u);var p=h&&b.call(t,"__wrapped__"),g=d&&b.call(e,"__wrapped__");if(p||g)return n(p?t.value():t,g?e.value():e,i,r,o,s);if(!f)return!1;s=s||[];for(var v=(o=o||[]).length;v--;)if(o[v]==t)return s[v]==e;o.push(t),s.push(e);var m=(a?function(t,e,n,i,r,o,s){var a=-1,l=t.length,u=e.length,c=!0;if(l!=u&&!(r&&l<u))return!1;for(;c&&++a<l;){var h=t[a],d=e[a];if(c=Xn,i&&(c=r?i(d,h,a):i(h,d,a)),void 0===c)if(r)for(var f=u;f--&&(d=e[f],!(c=h&&h===d||n(h,d,i,r,o,s))););else c=h&&h===d||n(h,d,i,r,o,s)}return!!c}:function(t,e,n,i,r,o,s){var a=kn(t),l=a.length,u=kn(e).length;if(l!=u&&!r)return!1;for(var c,h=-1;++h<l;){var d=a[h],f=b.call(e,d);if(f){var p=t[d],g=e[d];f=Xn,i&&(f=r?i(g,p,d):i(p,g,d)),void 0===f&&(f=p&&p===g||n(p,g,i,r,o,s))}if(!f)return!1;c=c||"constructor"==d}if(!c){var v=t.constructor,m=e.constructor;if(v!=m&&"constructor"in t&&"constructor"in e&&!("function"==typeof v&&v instanceof v&&"function"==typeof m&&m instanceof m))return!1}return!0})(t,e,n,i,r,o,s);return o.pop(),s.pop(),m}(t,e,Ht,n,i,r,o)}function qt(t,e,n,i,r){var o=e.length;if(null==t)return!o;for(var s=-1,a=!r;++s<o;)if(a&&i[s]?n[s]!==t[e[s]]:!b.call(t,e[s]))return!1;for(s=-1;++s<o;){var l=e[s];if(a&&i[s])var u=b.call(t,l);else{var c=t[l],h=n[s];void 0===(u=r?r(c,h,l):Xn)&&(u=Ht(h,c,r,!0))}if(!u)return!1}return!0}function $t(t,i){var r=[];return kt(t,function(t,e,n){r.push(i(t,e,n))}),r}function Ut(t,e){var n=kn(t),i=n.length;if(1==i){var r=n[0],o=t[r];if(we(o))return function(t){return null!=t&&o===t[r]&&b.call(t,r)}}e&&(t=xt(t,!0));for(var s=M(i),a=M(i);i--;)o=t[n[i]],s[i]=o,a[i]=we(o);return function(t){return qt(t,n,s,a)}}function Bt(e){return function(t){return null==t?Xn:t[e]}}function Vt(t,e){return t+C(z()*(e-t+1))}function zt(t,i,r,o,e){return e(t,function(t,e,n){r=o?(o=!1,t):i(r,t,e,n)}),r}var Wt=Z?function(t,e){return Z.set(t,e),t}:Vn;function Jt(t,e,n){var i=-1,r=t.length;(e=null==e?0:+e||0)<0&&(e=r<-e?0:r+e),(n=void 0===n||r<n?r:+n||0)<0&&(n+=r);for(var o=M(r=n<e?0:n-e);++i<r;)o[i]=t[i+e];return o}function Gt(t,i){var r;return kt(t,function(t,e,n){return!(r=i(t,e,n))}),!!r}function Xt(t,e){var n=-1,i=ve(),r=t.length,o=i==hr,s=o&&200<=r,a=s&&se(),l=[];a?(i=st,o=!1):(s=!1,a=e?[]:l);t:for(;++n<r;){var u=t[n],c=e?e(u,n,t):u;if(o&&u==u){for(var h=a.length;h--;)if(a[h]===c)continue t;e&&a.push(c),l.push(u)}else i(a,c)<0&&((e||s)&&a.push(c),l.push(u))}return l}function Yt(t,e){for(var n=-1,i=e.length,r=M(i);++n<i;)r[n]=t[e[n]];return r}function Kt(t,e){var n=t;n instanceof it&&(n=n.value());for(var i=-1,r=e.length;++i<r;){var o=[n],s=e[i];k.apply(o,s.args),n=s.func.apply(s.thisArg,o)}return n}function Qt(t,e,n){var i=0,r=t?t.length:i;if("number"==typeof e&&e==e&&r<=Y){for(;i<r;){var o=i+r>>>1,s=t[o];(n?s<=e:s<e)?i=1+o:r=o}return r}return Zt(t,e,Vn,n)}function Zt(t,e,n,i){e=n(e);for(var r=0,o=t?t.length:0,s=e!=e,a=void 0===e;r<o;){var l=C((r+o)/2),u=n(t[l]),c=u==u;if(s)var h=c||i;else h=a?c&&(i||void 0!==u):i?u<=e:u<e;h?r=l+1:o=l}return $(o,X)}function te(o,s,t){if("function"!=typeof o)return Vn;if(void 0===s)return o;switch(t){case 1:return function(t){return o.call(s,t)};case 3:return function(t,e,n){return o.call(s,t,e,n)};case 4:return function(t,e,n,i){return o.call(s,t,e,n,i)};case 5:return function(t,e,n,i,r){return o.call(s,t,e,n,i,r)}}return function(){return o.apply(s,arguments)}}function ee(t){return x.call(t,0)}function ne(t,e,n){for(var i=n.length,r=-1,o=q(t.length-i,0),s=-1,a=e.length,l=M(o+a);++s<a;)l[s]=e[s];for(;++r<i;)l[n[r]]=t[r];for(;o--;)l[s++]=t[r++];return l}function ie(t,e,n){for(var i=-1,r=n.length,o=-1,s=q(t.length-r,0),a=-1,l=e.length,u=M(s+l);++o<s;)u[o]=t[o];for(var c=o;++a<l;)u[c+a]=e[a];for(;++i<r;)u[c+n[i]]=t[o++];return u}function re(a,l){return function(t,i,e){var r=l?l():{};if(i=pe(i,e,3),dn(t))for(var n=-1,o=t.length;++n<o;){var s=t[n];a(r,s,i(s,n,t),t)}else kt(t,function(t,e,n){a(r,t,i(t,e,n),n)});return r}}function oe(o){return function(){var t=arguments.length,e=arguments[0];if(t<2||null==e)return e;if(3<t&&ye(arguments[1],arguments[2],arguments[3])&&(t=2),3<t&&"function"==typeof arguments[t-2])var n=te(arguments[--t-1],arguments[t--],5);else 2<t&&"function"==typeof arguments[t-1]&&(n=arguments[--t]);for(var i=0;++i<t;){var r=arguments[i];r&&o(e,r,n)}return e}}x||(ee=_&&j?function(t){var e=t.byteLength,n=L?C(e/K):0,i=n*K,r=new _(e);if(n){var o=new L(r,0,n);o.set(new L(t,0,n))}return e!=i&&(o=new j(r,i)).set(new j(t,i)),r}:Bn(null));var se=P&&O?function(t){return new ot(t)}:Bn(null);function ae(o){return function(t){for(var e=-1,n=qn(Nn(t)),i=n.length,r="";++e<i;)r=o(r,n[e],e);return r}}function le(n){return function(){var t=St(n.prototype),e=n.apply(t,arguments);return mn(e)?e:t}}function ue(s,a){return function(t,e,n){n&&ye(t,e,n)&&(e=null);var i=pe(),r=null==e;if(i===_t&&r||(r=!1,e=i(e,n,3)),r){var o=dn(t);if(o||!xn(t))return s(o?t:Me(t));e=pr}return function(t,r,o){var s=o?J:W,a=s,l=a;return kt(t,function(t,e,n){var i=r(t,e,n);((o?i<a:a<i)||i===s&&i===l)&&(a=i,l=t)}),l}(t,e,a)}}function ce(c,h,d,f,p,g,v,m,y,b){var w=h&oi,_=h&Kn,x=h&Qn,S=h&ti,T=h&Zn,C=h&ei,E=!x&&le(c),k=c;return function t(){for(var e=arguments.length,n=e,i=M(e);n--;)i[n]=arguments[n];if(f&&(i=ne(i,f,p)),g&&(i=ie(i,g,v)),S||C){var r=t.placeholder,o=Tr(i,r);if((e-=o.length)<b){var s=m?at(m):null,a=q(b-e,0);h|=S?ni:ii,h&=~(S?ii:ni),T||(h&=~(Kn|Qn));var l=ce(c,h,d,S?i:null,S?o:null,S?null:i,S?null:o,s,y,a);return l.placeholder=r,l}}var u=_?d:this;return x&&(c=u[k]),m&&(i=function(t,e){for(var n=t.length,i=$(e.length,n),r=at(t);i--;){var o=e[i];t[i]=me(o,n)?r[o]:Xn}return t}(i,m)),w&&y<i.length&&(i.length=y),(this instanceof t?E||le(c):c).apply(u,i)}}function he(t,e,n){var i=t.length;if((e=+e)<=i||!F(e))return"";var r=e-i;return In(n=null==n?" ":fr(n),S(r/n.length)).slice(0,r)}function de(s,t,a,l){var u=t&Kn,c=le(s);return function t(){for(var e=-1,n=arguments.length,i=-1,r=l.length,o=M(n+r);++i<r;)o[i]=l[i];for(;n--;)o[i++]=arguments[++e];return(this instanceof t?c:s).apply(u?a:this,o)}}function fe(t,e,n,i,r,o,s,a){var l=e&Qn;if(!l&&!vn(t))throw new y(fi);var u=i?i.length:0;if(u||(e&=~(ni|ii),i=r=null),u-=r?r.length:0,e&ii){var c=i,h=r;i=r=null}var d=!l&&ge(t),f=[t,e,n,i,r,c,h,o,s,a];if(d&&!0!==d&&(function(t,e){var n=t[1],i=e[1],r=n|i,o=oi|ri,s=Kn|Qn,a=o|s|Zn|ei,l=n&oi&&!(i&oi),u=n&ri&&!(i&ri),c=(u?t:e)[7],h=(l?t:e)[8],d=!(ri<=n&&s<i||s<n&&ri<=i),f=o<=r&&r<=a&&(n<ri||(u||l)&&c.length<=h);if(d||f){i&Kn&&(t[2]=e[2],r|=n&Kn?0:Zn);var p=e[3];if(p){var g=t[3];t[3]=g?ne(g,p,e[4]):at(p),t[4]=g?Tr(t[3],pi):at(e[4])}(p=e[5])&&(g=t[5],t[5]=g?ie(g,p,e[6]):at(p),t[6]=g?Tr(t[5],pi):at(e[6])),(p=e[7])&&(t[7]=at(p)),i&oi&&(t[8]=null==t[8]?e[8]:$(t[8],e[8])),null==t[9]&&(t[9]=e[9]),t[0]=e[0],t[1]=r}}(f,d),e=f[1],a=f[9]),f[9]=null==a?l?0:t.length:q(a-u,0)||0,e==Kn)var p=function(e,n){var i=le(e);return function t(){return(this instanceof t?i:e).apply(n,arguments)}}(f[0],f[2]);else p=e!=ni&&e!=(Kn|ni)||f[4].length?ce.apply(null,f):de.apply(null,f);return(d?Wt:Ce)(p,f)}function pe(t,e,n){var i=tt.callback||Un;return i=i===Un?_t:i,n?i(t,e,n):i}var ge=Z?function(t){return Z.get(t)}:Jn;function ve(t,e,n){var i=tt.indexOf||Re;return i=i===Re?hr:i,t?i(t,e,n):i}function me(t,e){return e=null==e?Q:e,-1<(t=+t)&&t%1==0&&t<e}function ye(t,e,n){if(!mn(n))return!1;var i=typeof e;if("number"==i)var r=n.length,o=be(r)&&me(e,r);else o="string"==i&&e in t;return o&&n[e]===t}function be(t){return"number"==typeof t&&-1<t&&t%1==0&&t<=Q}function we(t){return t==t&&(0===t?0<1/t:!mn(t))}function _e(t,e){t=De(t);for(var n=-1,i=e.length,r={};++n<i;){var o=e[n];o in t&&(r[o]=t[o])}return r}function xe(t,i){var r={};return Lt(t,function(t,e,n){i(t,e,n)&&(r[e]=t)}),r}var Se,Te,Ce=(Te=Se=0,function(t,e){var n=sn(),i=ui-(n-Te);if(Te=n,0<i){if(++Se>=li)return t}else Se=0;return Wt(t,e)});function Ee(t){var e,n;return!(!Sr(t)||w.call(t)!=xi||!(b.call(t,"constructor")||"function"!=typeof(e=t.constructor)||e instanceof e))&&(Lt(t,function(t,e){n=e}),void 0===n||b.call(t,n))}function ke(t){for(var e=Mn(t),n=e.length,i=n&&t.length,r=tt.support,o=i&&be(i)&&(dn(t)||r.nonEnumArgs&&hn(t)),s=-1,a=[];++s<n;){var l=e[s];(o&&me(l,i)||b.call(t,l))&&a.push(l)}return a}function Me(t){return null==t?[]:be(t.length)?mn(t)?t:d(t):On(t)}function De(t){return mn(t)?t:d(t)}function Oe(t,e,n){return t&&t.length?((n?ye(t,e,n):null==e)&&(e=1),Jt(t,e<0?0:e)):[]}function Ae(t,e,n){var i=t?t.length:0;return i?((n?ye(t,e,n):null==e)&&(e=1),Jt(t,0,(e=i-(+e||0))<0?0:e)):[]}function Ne(t,e,n){var i=-1,r=t?t.length:0;for(e=pe(e,n,3);++i<r;)if(e(t[i],i,t))return i;return-1}function je(t){return t?t[0]:Xn}function Re(t,e,n){var i=t?t.length:0;if(!i)return-1;if("number"==typeof n)n=n<0?q(i+n,0):n||0;else if(n){var r=Qt(t,e),o=t[r];return(e==e?e===o:o!=o)?r:-1}return hr(t,e,n)}function Le(t){return Oe(t,1)}function Ie(t,e,n,i){if(!t||!t.length)return[];"boolean"!=typeof e&&null!=e&&(n=ye(t,e,i=n)?null:e,e=!1);var r=pe();return r===_t&&null==n||(n=r(n,i,3)),e&&ve()==hr?function(t,e){for(var n,i=-1,r=t.length,o=-1,s=[];++i<r;){var a=t[i],l=e?e(a,i,t):a;i&&n===l||(n=l,s[++o]=a)}return s}(t,n):Xt(t,n)}function Pe(t){for(var e=-1,n=(t&&t.length&&dt(ht(t,h)))>>>0,i=M(n);++e<n;)i[e]=ht(t,Bt(e));return i}function Fe(t,e){var n=-1,i=t?t.length:0,r={};for(!i||e||dn(t[0])||(e=[]);++n<i;){var o=t[n];e?r[o]=e[n]:o&&(r[o[0]]=o[1])}return r}function He(t){var e=tt(t);return e.__chain__=!0,e}function qe(t,e,n){return e.call(n,t)}function $e(t,e,n){var i=t?t.length:0;return be(i)||(i=(t=On(t)).length),!!i&&(n="number"==typeof n?n<0?q(i+n,0):n||0:0,"string"==typeof t||!dn(t)&&xn(t)?n<i&&-1<t.indexOf(e,n):-1<ve(t,e,n))}var Ue=re(function(t,e,n){b.call(t,n)?++t[n]:t[n]=1});function Be(t,e,n){var i=dn(t)?ut:Dt;return"function"==typeof e&&void 0===n||(e=pe(e,n,3)),i(t,e)}function Ve(t,e,n){return(dn(t)?ct:Ot)(t,e=pe(e,n,3))}function ze(t,e,n){if(dn(t)){var i=Ne(t,e,n);return-1<i?t[i]:Xn}return At(t,e=pe(e,n,3),kt)}function We(t,e,n){return"function"==typeof e&&void 0===n&&dn(t)?lt(t,e):kt(t,te(e,n,3))}function Je(t,e,n){return"function"==typeof e&&void 0===n&&dn(t)?function(t,e){for(var n=t.length;n--&&!1!==e(t[n],n,t););return t}(t,e):Mt(t,te(e,n,3))}var Ge=re(function(t,e,n){b.call(t,n)?t[n].push(e):t[n]=[e]}),Xe=re(function(t,e,n){t[n]=e});function Ye(t,e,n){return(dn(t)?ht:$t)(t,e=pe(e,n,3))}var Ke=ue(dt),Qe=ue(function(t){for(var e=-1,n=t.length,i=J;++e<n;){var r=t[e];r<i&&(i=r)}return i},!0),Ze=re(function(t,e,n){t[n?0:1].push(e)},function(){return[[],[]]});function tn(t,e,n,i){return(dn(t)?ft:zt)(t,pe(e,i,4),n,arguments.length<3,kt)}function en(t,e,n,i){return(dn(t)?pt:zt)(t,pe(e,i,4),n,arguments.length<3,Mt)}function nn(t,e,n){if(n?ye(t,e,n):null==e){var i=(t=Me(t)).length;return 0<i?t[Vt(0,i-1)]:Xn}var r=rn(t);return r.length=$(e<0?0:+e||0,r.length),r}function rn(t){for(var e=-1,n=(t=Me(t)).length,i=M(n);++e<n;){var r=Vt(0,e);e!=r&&(i[e]=i[r]),i[r]=t[e]}return i}function on(t,e,n){var i=dn(t)?gt:Gt;return"function"==typeof e&&void 0===n||(e=pe(e,n,3)),i(t,e)}var sn=U||function(){return(new t).getTime()};function an(t,e){var n;if(!vn(e)){if(!vn(t))throw new y(fi);var i=t;t=e,e=i}return function(){return 0<--t?n=e.apply(this,arguments):e=null,n}}function ln(i,r,t){var o,s,a,l,u,c,h,d=0,f=!1,p=!0;if(!vn(i))throw new y(fi);if(r=r<0?0:r,!0===t){var g=!0;p=!1}else mn(t)&&(g=t.leading,f="maxWait"in t&&q(+t.maxWait||0,r),p="trailing"in t?t.trailing:p);function v(){var t=r-(sn()-l);if(t<=0||r<t){s&&T(s);var e=h;s=c=h=Xn,e&&(d=sn(),a=i.apply(u,o),c||s||(o=u=null))}else c=A(v,t)}function m(){c&&T(c),s=c=h=Xn,!p&&f===r||(d=sn(),a=i.apply(u,o),c||s||(o=u=null))}function e(){if(o=arguments,l=sn(),u=this,h=p&&(c||!g),!1===f)var t=g&&!c;else{s||g||(d=l);var e=f-(l-d),n=e<=0||f<e;n?(s=s&&T(s),d=l,a=i.apply(u,o)):s=s||A(m,e)}return n&&c?c=T(c):c||r===f||(c=A(v,r)),t&&(n=!0,a=i.apply(u,o)),!n||c||s||(o=u=null),a}return e.cancel=function(){c&&T(c),s&&T(s),s=c=h=Xn},e}function un(){var n=arguments,i=n.length-1;if(i<0)return function(){};if(!ut(n,vn))throw new y(fi);return function(){for(var t=i,e=n[t].apply(this,arguments);t--;)e=n[t].call(this,e);return e}}function cn(i,r){if(!vn(i)||r&&!vn(r))throw new y(fi);var o=function(){var t=o.cache,e=r?r.apply(this,arguments):arguments[0];if(t.has(e))return t.get(e);var n=i.apply(this,arguments);return t.set(e,n),n};return o.cache=new cn.Cache,o}function hn(t){return be(Sr(t)?t.length:Xn)&&w.call(t)==gi||!1}var dn=I||function(t){return Sr(t)&&be(t.length)&&w.call(t)==vi||!1};function fn(t){return t&&1===t.nodeType&&Sr(t)&&-1<w.call(t).indexOf("Element")||!1}function pn(t){return Sr(t)&&"string"==typeof t.message&&w.call(t)==bi||!1}nt.dom||(fn=function(t){return t&&1===t.nodeType&&Sr(t)&&!wn(t)||!1});var gn=B||function(t){return"number"==typeof t&&F(t)};function vn(t){return"function"==typeof t||!1}function mn(t){var e=typeof t;return"function"==e||t&&"object"==e||!1}function yn(t){return null!=t&&(w.call(t)==wi?g.test(c.call(t)):Sr(t)&&Xi.test(t)||!1)}function bn(t){return"number"==typeof t||Sr(t)&&w.call(t)==_i||!1}(vn(/x/)||j&&!vn(j))&&(vn=function(t){return w.call(t)==wi});var wn=E?function(t){if(!t||w.call(t)!=xi)return!1;var e=t.valueOf,n=yn(e)&&(n=E(e))&&E(n);return n?t==n||E(t)==n:Ee(t)}:Ee;function _n(t){return Sr(t)&&w.call(t)==Si||!1}function xn(t){return"string"==typeof t||Sr(t)&&w.call(t)==Ti||!1}function Sn(t){return Sr(t)&&be(t.length)&&sr[w.call(t)]||!1}function Tn(t){return wt(t,Mn(t))}var Cn=oe(yt);function En(t){return Ft(t,Mn(t))}var kn=H?function(t){if(t)var e=t.constructor,n=t.length;return"function"==typeof e&&e.prototype===t||"function"!=typeof t&&n&&be(n)?ke(t):mn(t)?H(t):[]}:ke;function Mn(t){if(null==t)return[];mn(t)||(t=d(t));var e=t.length;e=e&&be(e)&&(dn(t)||nt.nonEnumArgs&&hn(t))&&e||0;for(var n=t.constructor,i=-1,r="function"==typeof n&&n.prototype==t,o=M(e),s=0<e;++i<e;)o[i]=i+"";for(var a in t)s&&me(a,e)||"constructor"==a&&(r||!b.call(t,a))||o.push(a);return o}var Dn=oe(function s(a,t,l,u,c){var h=be(t.length)&&(dn(t)||Sn(t));return(h?lt:It)(t,function(t,e,n){if(Sr(t))return function(t,e,n,i,r,o,s){for(var a=o.length,l=e[n];a--;)if(o[a]==l)return void(t[n]=s[a]);var u=t[n],c=r?r(u,l,n,t,e):Xn,h=void 0===c;h&&(be((c=l).length)&&(dn(l)||Sn(l))?c=dn(u)?u:u?at(u):[]:(wn(l)||hn(l))&&(c=hn(u)?Tn(u):wn(u)?u:{})),o.push(l),s.push(c),h?t[n]=i(c,l,r,o,s):(c==c?c!==u:u==u)&&(t[n]=c)}(a,n,e,s,l,u=u||[],c=c||[]);var i=a[e],r=l?l(i,t,e,a,n):Xn,o=void 0===r;o&&(r=t),!h&&void 0===r||!o&&(r==r?r===i:i!=i)||(a[e]=r)}),a});function On(t){return Yt(t,kn(t))}var An=ae(function(t,e,n){return e=e.toLowerCase(),n?t+e.charAt(0).toUpperCase()+e.slice(1):e});function Nn(t){return(t=fr(t))&&t.replace(Yi,br)}function jn(t){return(t=fr(t))&&Zi.test(t)?t.replace(Qi,"\\$&"):t}var Rn=ae(function(t,e,n){return t+(n?"-":"")+e.toLowerCase()});function Ln(t,e,n){return n&&ye(t,e,n)&&(e=0),V(t,e)}function In(t,e){var n="";if(t=fr(t),(e=+e)<1||!t||!F(e))return n;for(;e%2&&(n+=t),t+=t,e=C(e/2););return n}8!=V(ir+"08")&&(Ln=function(t,e,n){return e=(n?ye(t,e,n):null==e)?0:e&&+e,t=Hn(t),V(t,e||(Gi.test(t)?16:10))});var Pn,Fn=ae(function(t,e,n){return t+(n?"_":"")+e.toLowerCase()});function Hn(t,e,n){var i=t;return(t=fr(t))?(n?ye(i,e,n):null==e)?t.slice(Cr(t),Er(t)+1):(e=fr(e),t.slice(gr(t,e),vr(t,e)+1)):t}function qn(t,e,n){return n&&ye(t,e,n)&&(e=null),(t=fr(t)).match(e||nr)||[]}function $n(t){try{return t()}catch(t){return pn(t)?t:i(t)}}function Un(t,e,n){return n&&ye(t,e,n)&&(e=null),_t(t,e)}function Bn(t){return function(){return t}}function Vn(t){return t}function zn(t){return Ut(t,!0)}function Wn(r,t,e){if(null==e){var n=mn(t),i=n&&kn(t),o=i&&i.length&&Ft(t,i);(o?o.length:n)||(o=!1,e=t,t=r,r=this)}o=o||Ft(t,kn(t));var s=!0,a=-1,l=vn(r),u=o.length;!1===e?s=!1:mn(e)&&"chain"in e&&(s=e.chain);for(;++a<u;){var c=o[a],h=t[c];r[c]=h,l&&(r.prototype[c]=function(i){return function(){var t=this.__chain__;if(s||t){var e=r(this.__wrapped__);return(e.__actions__=at(this.__actions__)).push({func:i,args:arguments,thisArg:r}),e.__chain__=t,e}var n=[this.value()];return k.apply(n,arguments),i.apply(r,n)}}(h))}return r}function Jn(){}function Gn(t){return Bt(t+"")}return et.prototype=tt.prototype,rt.prototype.delete=function(t){return this.has(t)&&delete this.__data__[t]},rt.prototype.get=function(t){return"__proto__"==t?Xn:this.__data__[t]},rt.prototype.has=function(t){return"__proto__"!=t&&b.call(this.__data__,t)},rt.prototype.set=function(t,e){return"__proto__"!=t&&(this.__data__[t]=e),this},ot.prototype.push=function(t){var e=this.data;"string"==typeof t||mn(t)?e.set.add(t):e.hash[t]=!0},cn.Cache=rt,tt.after=function(t,e){if(!vn(e)){if(!vn(t))throw new y(fi);var n=t;t=e,e=n}return t=F(t=+t)?t:0,function(){if(--t<1)return e.apply(this,arguments)}},tt.ary=function(t,e,n){return n&&ye(t,e,n)&&(e=null),e=t&&null==e?t.length:q(+e||0,0),fe(t,oi,null,null,null,null,e)},tt.assign=Cn,tt.at=function(t){return be(t?t.length:0)&&(t=Me(t)),bt(t,Nt(arguments,!1,!1,1))},tt.before=an,tt.bind=function t(e,n){var i=Kn;if(2<arguments.length){var r=Jt(arguments,2),o=Tr(r,t.placeholder);i|=ni}return fe(e,i,n,r,o)},tt.bindAll=function(t){return function(t,e){for(var n=-1,i=e.length;++n<i;){var r=e[n];t[r]=fe(t[r],Kn,t)}return t}(t,1<arguments.length?Nt(arguments,!1,!1,1):En(t))},tt.bindKey=function t(e,n){var i=Kn|Qn;if(2<arguments.length){var r=Jt(arguments,2),o=Tr(r,t.placeholder);i|=ni}return fe(n,i,e,r,o)},tt.callback=Un,tt.chain=He,tt.chunk=function(t,e,n){e=(n?ye(t,e,n):null==e)?1:q(+e||1,1);for(var i=0,r=t?t.length:0,o=-1,s=M(S(r/e));i<r;)s[++o]=Jt(t,i,i+=e);return s},tt.compact=function(t){for(var e=-1,n=t?t.length:0,i=-1,r=[];++e<n;){var o=t[e];o&&(r[++i]=o)}return r},tt.constant=Bn,tt.countBy=Ue,tt.create=function(t,e,n){var i=St(t);return n&&ye(t,e,n)&&(e=null),e?wt(e,i,kn(e)):i},tt.curry=function t(e,n,i){i&&ye(e,n,i)&&(n=null);var r=fe(e,ti,null,null,null,null,null,n);return r.placeholder=t.placeholder,r},tt.curryRight=function t(e,n,i){i&&ye(e,n,i)&&(n=null);var r=fe(e,ei,null,null,null,null,null,n);return r.placeholder=t.placeholder,r},tt.debounce=ln,tt.defaults=function(t){if(null==t)return t;var e=at(arguments);return e.push(vt),Cn.apply(Xn,e)},tt.defer=function(t){return Ct(t,1,arguments,1)},tt.delay=function(t,e){return Ct(t,e,arguments,2)},tt.difference=function(){for(var t=-1,e=arguments.length;++t<e;){var n=arguments[t];if(dn(n)||hn(n))break}return Et(n,Nt(arguments,!1,!0,++t))},tt.drop=Oe,tt.dropRight=Ae,tt.dropRightWhile=function(t,e,n){var i=t?t.length:0;if(!i)return[];for(e=pe(e,n,3);i--&&e(t[i],i,t););return Jt(t,0,i+1)},tt.dropWhile=function(t,e,n){var i=t?t.length:0;if(!i)return[];var r=-1;for(e=pe(e,n,3);++r<i&&e(t[r],r,t););return Jt(t,r)},tt.filter=Ve,tt.flatten=function(t,e,n){var i=t?t.length:0;return n&&ye(t,e,n)&&(e=!1),i?Nt(t,e):[]},tt.flattenDeep=function(t){return t&&t.length?Nt(t,!0):[]},tt.flow=function(){var n=arguments,i=n.length;if(!i)return function(){};if(!ut(n,vn))throw new y(fi);return function(){for(var t=0,e=n[t].apply(this,arguments);++t<i;)e=n[t].call(this,e);return e}},tt.flowRight=un,tt.forEach=We,tt.forEachRight=Je,tt.forIn=function(t,e,n){return"function"==typeof e&&void 0===n||(e=te(e,n,3)),jt(t,e,Mn)},tt.forInRight=function(t,e,n){return Rt(t,e=te(e,n,3),Mn)},tt.forOwn=function(t,e,n){return"function"==typeof e&&void 0===n||(e=te(e,n,3)),It(t,e)},tt.forOwnRight=function(t,e,n){return Rt(t,e=te(e,n,3),kn)},tt.functions=En,tt.groupBy=Ge,tt.indexBy=Xe,tt.initial=function(t){return Ae(t,1)},tt.intersection=function(){for(var t=[],e=-1,n=arguments.length,i=[],r=ve(),o=r==hr;++e<n;){var s=arguments[e];(dn(s)||hn(s))&&(t.push(s),i.push(o&&120<=s.length&&se(e&&s)))}n=t.length;var a=t[0],l=-1,u=a?a.length:0,c=[],h=i[0];t:for(;++l<u;)if(s=a[l],(h?st(h,s):r(c,s))<0){for(e=n;--e;){var d=i[e];if((d?st(d,s):r(t[e],s))<0)continue t}h&&h.push(s),c.push(s)}return c},tt.invert=function(t,e,n){n&&ye(t,e,n)&&(e=null);for(var i=-1,r=kn(t),o=r.length,s={};++i<o;){var a=r[i],l=t[a];e?b.call(s,l)?s[l].push(a):s[l]=[a]:s[l]=a}return s},tt.invoke=function(t,e){return function(t,n,i){var r=-1,o="function"==typeof n,e=t?t.length:0,s=be(e)?M(e):[];return kt(t,function(t){var e=o?n:null!=t&&t[n];s[++r]=e?e.apply(t,i):Xn}),s}(t,e,Jt(arguments,2))},tt.keys=kn,tt.keysIn=Mn,tt.map=Ye,tt.mapValues=function(t,i,e){var r={};return i=pe(i,e,3),It(t,function(t,e,n){r[e]=i(t,e,n)}),r},tt.matches=zn,tt.memoize=cn,tt.merge=Dn,tt.mixin=Wn,tt.negate=function(t){if(!vn(t))throw new y(fi);return function(){return!t.apply(this,arguments)}},tt.omit=function(t,i,e){if(null==t)return{};if("function"==typeof i)return i=te(i,e,3),xe(t,function(t,e,n){return!i(t,e,n)});var n=ht(Nt(arguments,!1,!1,1),s);return _e(t,Et(Mn(t),n))},tt.once=function(t){return an(t,2)},tt.pairs=function(t){for(var e=-1,n=kn(t),i=n.length,r=M(i);++e<i;){var o=n[e];r[e]=[o,t[o]]}return r},tt.partial=function t(e){var n=Jt(arguments,1),i=Tr(n,t.placeholder);return fe(e,ni,null,n,i)},tt.partialRight=function t(e){var n=Jt(arguments,1),i=Tr(n,t.placeholder);return fe(e,ii,null,n,i)},tt.partition=Ze,tt.pick=function(t,e,n){return null==t?{}:"function"==typeof e?xe(t,te(e,n,3)):_e(t,Nt(arguments,!1,!1,1))},tt.pluck=function(t,e){return Ye(t,Gn(e))},tt.property=Gn,tt.propertyOf=function(e){return function(t){return null==e?Xn:e[t]}},tt.pull=function(){var t=arguments[0];if(!t||!t.length)return t;for(var e=0,n=ve(),i=arguments.length;++e<i;)for(var r=0,o=arguments[e];-1<(r=n(t,o,r));)N.call(t,r,1);return t},tt.pullAt=function(t){return function(t,e){var n=e.length,i=bt(t,e);for(e.sort(cr);n--;){var r=parseFloat(e[n]);if(r!=o&&me(r)){var o=r;N.call(t,r,1)}}return i}(t||[],Nt(arguments,!1,!1,1))},tt.range=function(t,e,n){n&&ye(t,e,n)&&(e=n=null),t=+t||0,null==e?(e=t,t=0):e=+e||0;for(var i=-1,r=q(S((e-t)/((n=null==n?1:+n||0)||1)),0),o=M(r);++i<r;)o[i]=t,t+=n;return o},tt.rearg=function(t){var e=Nt(arguments,!1,!1,1);return fe(t,ri,null,null,null,e)},tt.reject=function(t,i,e){var n=dn(t)?ct:Ot;return i=pe(i,e,3),n(t,function(t,e,n){return!i(t,e,n)})},tt.remove=function(t,e,n){var i=-1,r=t?t.length:0,o=[];for(e=pe(e,n,3);++i<r;){var s=t[i];e(s,i,t)&&(o.push(s),N.call(t,i--,1),r--)}return o},tt.rest=Le,tt.shuffle=rn,tt.slice=function(t,e,n){var i=t?t.length:0;return i?(n&&"number"!=typeof n&&ye(t,e,n)&&(e=0,n=i),Jt(t,e,n)):[]},tt.sortBy=function(t,i,e){var r=-1,n=t?t.length:0,o=be(n)?M(n):[];return e&&ye(t,i,e)&&(i=null),i=pe(i,e,3),kt(t,function(t,e,n){o[++r]={criteria:i(t,e,n),index:r,value:t}}),dr(o,mr)},tt.sortByAll=function(t){var e=arguments;3<e.length&&ye(e[1],e[2],e[3])&&(e=[t,e[1]]);var o=-1,n=t?t.length:0,s=Nt(e,!1,!1,1),a=be(n)?M(n):[];return kt(t,function(t,e,n){for(var i=s.length,r=M(i);i--;)r[i]=null==t?Xn:t[s[i]];a[++o]={criteria:r,index:o,value:t}}),dr(a,yr)},tt.take=function(t,e,n){return t&&t.length?((n?ye(t,e,n):null==e)&&(e=1),Jt(t,0,e<0?0:e)):[]},tt.takeRight=function(t,e,n){var i=t?t.length:0;return i?((n?ye(t,e,n):null==e)&&(e=1),Jt(t,(e=i-(+e||0))<0?0:e)):[]},tt.takeRightWhile=function(t,e,n){var i=t?t.length:0;if(!i)return[];for(e=pe(e,n,3);i--&&e(t[i],i,t););return Jt(t,i+1)},tt.takeWhile=function(t,e,n){var i=t?t.length:0;if(!i)return[];var r=-1;for(e=pe(e,n,3);++r<i&&e(t[r],r,t););return Jt(t,0,r)},tt.tap=function(t,e,n){return e.call(n,t),t},tt.throttle=function(t,e,n){var i=!0,r=!0;if(!vn(t))throw new y(fi);return!1===n?i=!1:mn(n)&&(i="leading"in n?!!n.leading:i,r="trailing"in n?!!n.trailing:r),lr.leading=i,lr.maxWait=+e,lr.trailing=r,ln(t,e,lr)},tt.thru=qe,tt.times=function(t,e,n){if((t=+t)<1||!F(t))return[];var i=-1,r=M($(t,G));for(e=te(e,n,1);++i<t;)i<G?r[i]=e(i):e(i);return r},tt.toArray=function(t){var e=t?t.length:0;return be(e)?e?at(t):[]:On(t)},tt.toPlainObject=Tn,tt.transform=function(t,i,r,e){var n=dn(t)||Sn(t);if(i=pe(i,e,4),null==r)if(n||mn(t)){var o=t.constructor;r=n?dn(t)?new o:[]:St("function"==typeof o&&o.prototype)}else r={};return(n?lt:It)(t,function(t,e,n){return i(r,t,e,n)}),r},tt.union=function(){return Xt(Nt(arguments,!1,!0))},tt.uniq=Ie,tt.unzip=Pe,tt.values=On,tt.valuesIn=function(t){return Yt(t,Mn(t))},tt.where=function(t,e){return Ve(t,zn(e))},tt.without=function(t){return Et(t,Jt(arguments,1))},tt.wrap=function(t,e){return fe(e=null==e?Vn:e,ni,null,[t],[])},tt.xor=function(){for(var t=-1,e=arguments.length;++t<e;){var n=arguments[t];if(dn(n)||hn(n))var i=i?Et(i,n).concat(Et(n,i)):n}return i?Xt(i):[]},tt.zip=function(){for(var t=arguments.length,e=M(t);t--;)e[t]=arguments[t];return Pe(e)},tt.zipObject=Fe,tt.backflow=un,tt.collect=Ye,tt.compose=un,tt.each=We,tt.eachRight=Je,tt.extend=Cn,tt.iteratee=Un,tt.methods=En,tt.object=Fe,tt.select=Ve,tt.tail=Le,tt.unique=Ie,Wn(tt,tt),tt.attempt=$n,tt.camelCase=An,tt.capitalize=function(t){return(t=fr(t))&&t.charAt(0).toUpperCase()+t.slice(1)},tt.clone=function(t,e,n,i){return"boolean"!=typeof e&&null!=e&&(n=ye(t,e,i=n)?null:e,e=!1),xt(t,e,n="function"==typeof n&&te(n,i,1))},tt.cloneDeep=function(t,e,n){return xt(t,!0,e="function"==typeof e&&te(e,n,1))},tt.deburr=Nn,tt.endsWith=function(t,e,n){e+="";var i=(t=fr(t)).length;return 0<=(n=(void 0===n?i:$(n<0?0:+n||0,i))-e.length)&&t.indexOf(e,n)==n},tt.escape=function(t){return(t=fr(t))&&$i.test(t)?t.replace(Hi,wr):t},tt.escapeRegExp=jn,tt.every=Be,tt.find=ze,tt.findIndex=Ne,tt.findKey=function(t,e,n){return At(t,e=pe(e,n,3),It,!0)},tt.findLast=function(t,e,n){return At(t,e=pe(e,n,3),Mt)},tt.findLastIndex=function(t,e,n){var i=t?t.length:0;for(e=pe(e,n,3);i--;)if(e(t[i],i,t))return i;return-1},tt.findLastKey=function(t,e,n){return At(t,e=pe(e,n,3),Pt,!0)},tt.findWhere=function(t,e){return ze(t,zn(e))},tt.first=je,tt.has=function(t,e){return!!t&&b.call(t,e)},tt.identity=Vn,tt.includes=$e,tt.indexOf=Re,tt.isArguments=hn,tt.isArray=dn,tt.isBoolean=function(t){return!0===t||!1===t||Sr(t)&&w.call(t)==mi||!1},tt.isDate=function(t){return Sr(t)&&w.call(t)==yi||!1},tt.isElement=fn,tt.isEmpty=function(t){if(null==t)return!0;var e=t.length;return be(e)&&(dn(t)||xn(t)||hn(t)||Sr(t)&&vn(t.splice))?!e:!kn(t).length},tt.isEqual=function(t,e,n,i){if(!(n="function"==typeof n&&te(n,i,3))&&we(t)&&we(e))return t===e;var r=n?n(t,e):Xn;return void 0===r?Ht(t,e,n):!!r},tt.isError=pn,tt.isFinite=gn,tt.isFunction=vn,tt.isMatch=function(t,e,n,i){var r=kn(e),o=r.length;if(!(n="function"==typeof n&&te(n,i,3))&&1==o){var s=r[0],a=e[s];if(we(a))return null!=t&&a===t[s]&&b.call(t,s)}for(var l=M(o),u=M(o);o--;)a=l[o]=e[r[o]],u[o]=we(a);return qt(t,r,l,u,n)},tt.isNaN=function(t){return bn(t)&&t!=+t},tt.isNative=yn,tt.isNull=function(t){return null===t},tt.isNumber=bn,tt.isObject=mn,tt.isPlainObject=wn,tt.isRegExp=_n,tt.isString=xn,tt.isTypedArray=Sn,tt.isUndefined=function(t){return void 0===t},tt.kebabCase=Rn,tt.last=function(t){var e=t?t.length:0;return e?t[e-1]:Xn},tt.lastIndexOf=function(t,e,n){var i=t?t.length:0;if(!i)return-1;var r=i;if("number"==typeof n)r=(n<0?q(i+n,0):$(n||0,i-1))+1;else if(n){var o=t[r=Qt(t,e,!0)-1];return(e==e?e===o:o!=o)?r:-1}if(e!=e)return xr(t,r,!0);for(;r--;)if(t[r]===e)return r;return-1},tt.max=Ke,tt.min=Qe,tt.noConflict=function(){return n._=p,this},tt.noop=Jn,tt.now=sn,tt.pad=function(t,e,n){e=+e;var i=(t=fr(t)).length;if(e<=i||!F(e))return t;var r=(e-i)/2,o=C(r);return(n=he("",S(r),n)).slice(0,o)+t+n},tt.padLeft=function(t,e,n){return(t=fr(t))&&he(t,e,n)+t},tt.padRight=function(t,e,n){return(t=fr(t))&&t+he(t,e,n)},tt.parseInt=Ln,tt.random=function(t,e,n){n&&ye(t,e,n)&&(e=n=null);var i=null==t,r=null==e;if(null==n&&(r&&"boolean"==typeof t?(n=t,t=1):"boolean"==typeof e&&(n=e,r=!0)),i&&r&&(r=!(e=1)),t=+t||0,r?(e=t,t=0):e=+e||0,n||t%1||e%1){var o=z();return $(t+o*(e-t+parseFloat("1e-"+((o+"").length-1))),e)}return Vt(t,e)},tt.reduce=tn,tt.reduceRight=en,tt.repeat=In,tt.result=function(t,e,n){var i=null==t?Xn:t[e];return void 0===i&&(i=n),vn(i)?i.call(t):i},tt.runInContext=e,tt.size=function(t){var e=t?t.length:0;return be(e)?e:kn(t).length},tt.snakeCase=Fn,tt.some=on,tt.sortedIndex=function(t,e,n,i){var r=pe(n);return r===_t&&null==n?Qt(t,e):Zt(t,e,r(n,i,1))},tt.sortedLastIndex=function(t,e,n,i){var r=pe(n);return r===_t&&null==n?Qt(t,e,!0):Zt(t,e,r(n,i,1),!0)},tt.startsWith=function(t,e,n){return t=fr(t),n=null==n?0:$(n<0?0:+n||0,t.length),t.lastIndexOf(e,n)==n},tt.template=function(s,t,e){var n=tt.templateSettings;e&&ye(s,t,e)&&(t=e=null),s=fr(s),t=yt(yt({},e||t),n,mt);var a,l,i=yt(yt({},t.imports),n.imports,mt),r=kn(i),o=Yt(i,r),u=0,c=t.interpolate||Ki,h="__p += '",d=m((t.escape||Ki).source+"|"+c.source+"|"+(c===Vi?zi:Ki).source+"|"+(t.evaluate||Ki).source+"|$","g"),f="//# sourceURL="+("sourceURL"in t?t.sourceURL:"lodash.templateSources["+ ++or+"]")+"\n";s.replace(d,function(t,e,n,i,r,o){return n=n||i,h+=s.slice(u,o).replace(er,_r),e&&(a=!0,h+="' +\n__e("+e+") +\n'"),r&&(l=!0,h+="';\n"+r+";\n__p += '"),n&&(h+="' +\n((__t = ("+n+")) == null ? '' : __t) +\n'"),u=o+t.length,t}),h+="';\n";var p=t.variable;p||(h="with (obj) {\n"+h+"\n}\n"),h=(l?h.replace(Li,""):h).replace(Ii,"$1").replace(Pi,"$1;"),h="function("+(p||"obj")+") {\n"+(p?"":"obj || (obj = {});\n")+"var __t, __p = ''"+(a?", __e = _.escape":"")+(l?", __j = Array.prototype.join;\nfunction print() { __p += __j.call(arguments, '') }\n":";\n")+h+"return __p\n}";var g=$n(function(){return v(r,f+"return "+h).apply(Xn,o)});if(g.source=h,pn(g))throw g;return g},tt.trim=Hn,tt.trimLeft=function(t,e,n){var i=t;return(t=fr(t))?(n?ye(i,e,n):null==e)?t.slice(Cr(t)):t.slice(gr(t,fr(e))):t},tt.trimRight=function(t,e,n){var i=t;return(t=fr(t))?(n?ye(i,e,n):null==e)?t.slice(0,Er(t)+1):t.slice(0,vr(t,fr(e))+1):t},tt.trunc=function(t,e,n){n&&ye(t,e,n)&&(e=null);var i=si,r=ai;if(null!=e)if(mn(e)){var o="separator"in e?e.separator:o;i="length"in e?+e.length||0:i,r="omission"in e?fr(e.omission):r}else i=+e||0;if(i>=(t=fr(t)).length)return t;var s=i-r.length;if(s<1)return r;var a=t.slice(0,s);if(null==o)return a+r;if(_n(o)){if(t.slice(s).search(o)){var l,u,c=t.slice(0,s);for(o.global||(o=m(o.source,(Wi.exec(o)||"")+"g")),o.lastIndex=0;l=o.exec(c);)u=l.index;a=a.slice(0,null==u?s:u)}}else if(t.indexOf(o,s)!=s){var h=a.lastIndexOf(o);-1<h&&(a=a.slice(0,h))}return a+r},tt.unescape=function(t){return(t=fr(t))&&qi.test(t)?t.replace(Fi,kr):t},tt.uniqueId=function(t){var e=++f;return fr(t)+e},tt.words=qn,tt.all=Be,tt.any=on,tt.contains=$e,tt.detect=ze,tt.foldl=tn,tt.foldr=en,tt.head=je,tt.include=$e,tt.inject=tn,Wn(tt,(Pn={},It(tt,function(t,e){tt.prototype[e]||(Pn[e]=t)}),Pn),!1),tt.sample=nn,tt.prototype.sample=function(e){return this.__chain__||null!=e?this.thru(function(t){return nn(t,e)}):nn(this.value())},tt.VERSION=Yn,lt(["bind","bindKey","curry","curryRight","partial","partialRight"],function(t){tt[t].placeholder=tt}),lt(["filter","map","takeWhile"],function(t,o){var s=o==ci;it.prototype[t]=function(t,e){var n=this.clone(),i=n.filtered,r=n.iteratees||(n.iteratees=[]);return n.filtered=i||s||o==di&&n.dir<0,r.push({iteratee:pe(t,e,3),type:o}),n}}),lt(["drop","take"],function(i,r){var o=i+"Count",n=i+"While";it.prototype[i]=function(t){t=null==t?1:q(+t||0,0);var e=this.clone();if(e.filtered){var n=e[o];e[o]=r?$(n,t):n+t}else(e.views||(e.views=[])).push({size:t,type:i+(e.dir<0?"Right":"")});return e},it.prototype[i+"Right"]=function(t){return this.reverse()[i](t).reverse()},it.prototype[i+"RightWhile"]=function(t,e){return this.reverse()[n](t,e).reverse()}}),lt(["first","last"],function(t,e){var n="take"+(e?"Right":"");it.prototype[t]=function(){return this[n](1).value()[0]}}),lt(["initial","rest"],function(t,e){var n="drop"+(e?"":"Right");it.prototype[t]=function(){return this[n](1)}}),lt(["pluck","where"],function(t,e){var n=e?"filter":"map",i=e?zn:Gn;it.prototype[t]=function(t){return this[n](i(t))}}),it.prototype.dropWhile=function(i,t){var r,o,s=this.dir<0;return i=pe(i,t,3),this.filter(function(t,e,n){return r=r&&(s?e<o:o<e),o=e,r=r||!i(t,e,n)})},it.prototype.reject=function(i,t){return i=pe(i,t,3),this.filter(function(t,e,n){return!i(t,e,n)})},it.prototype.slice=function(t,e){var n=(t=null==t?0:+t||0)<0?this.takeRight(-t):this.drop(t);return void 0!==e&&(n=(e=+e||0)<0?n.dropRight(-e):n.take(e-t)),n},It(it.prototype,function(u,c){var h=/^(?:first|last)$/.test(c);tt.prototype[c]=function(){var t=this.__wrapped__,n=arguments,e=this.__chain__,i=!!this.__actions__.length,r=t instanceof it,o=r&&!i;if(h&&!e)return o?u.call(t):tt[c](this.value());function s(t){var e=[t];return k.apply(e,n),tt[c].apply(tt,e)}if(r||dn(t)){var a=o?t:new it(this),l=u.apply(a,n);return h||!i&&!l.actions||(l.actions||(l.actions=[])).push({func:qe,args:[s],thisArg:tt}),new et(l,e)}return this.thru(s)}}),lt(["concat","join","pop","push","shift","sort","splice","unshift"],function(t){var n=a[t],i=/^(?:push|sort|unshift)$/.test(t)?"tap":"thru",r=/^(?:join|pop|shift)$/.test(t);tt.prototype[t]=function(){var e=arguments;return r&&!this.__chain__?n.apply(this.value(),e):this[i](function(t){return n.apply(t,e)})}}),it.prototype.clone=function(){var t=this.actions,e=this.iteratees,n=this.views,i=new it(this.wrapped);return i.actions=t?at(t):null,i.dir=this.dir,i.dropCount=this.dropCount,i.filtered=this.filtered,i.iteratees=e?at(e):null,i.takeCount=this.takeCount,i.views=n?at(n):null,i},it.prototype.reverse=function(){var t=this.filtered,e=t?new it(this):this.clone();return e.dir=-1*this.dir,e.filtered=t,e},it.prototype.value=function(){var t=this.wrapped.value();if(!dn(t))return Kt(t,this.actions);var e=this.dir,n=e<0,i=t.length,r=function(t,e,n){for(var i=-1,r=n?n.length:0;++i<r;){var o=n[i],s=o.size;switch(o.type){case"drop":t+=s;break;case"dropRight":e-=s;break;case"take":e=$(e,t+s);break;case"takeRight":t=q(t,e-s)}}return{start:t,end:e}}(0,i,this.views),o=r.start,s=r.end,a=this.dropCount,l=$(s-o,this.takeCount-a),u=n?s:o-1,c=this.iteratees,h=c?c.length:0,d=0,f=[];t:for(;i--&&d<l;){for(var p=-1,g=t[u+=e];++p<h;){var v=c[p],m=(0,v.iteratee)(g,u,t),y=v.type;if(y==hi)g=m;else if(!m){if(y==ci)continue t;break t}}a?a--:f[d++]=g}return n?f.reverse():f},tt.prototype.chain=function(){return He(this)},tt.prototype.reverse=function(){var t=this.__wrapped__;return t instanceof it?new et(t.reverse()):this.thru(function(t){return t.reverse()})},tt.prototype.toString=function(){return this.value()+""},tt.prototype.toJSON=tt.prototype.valueOf=tt.prototype.value=function(){return Kt(this.__wrapped__,this.__actions__)},tt.prototype.collect=tt.prototype.map,tt.prototype.head=tt.prototype.first,tt.prototype.select=tt.prototype.filter,tt.prototype.tail=tt.prototype.rest,tt}();"function"==typeof define&&"object"==typeof define.amd&&define.amd?(ur._=Mr,define(function(){return Mr})):c&&h?f?(h.exports=Mr)._=Mr:c._=Mr:ur._=Mr}.call(this),function(i,r){if("function"==typeof define&&define.amd)define(["../../.","jquery","exports"],function(t,e,n){i.Backbone=r(i,n,t,e)});else if("undefined"!=typeof exports){var t=require("underscore");r(i,exports,t)}else i.Backbone=r(i,{},i._,i.jQuery||i.Zepto||i.ender||i.$)}(this,function(t,a,x,e){var n=t.Backbone,i=[],r=i.slice;a.VERSION="1.1.2",a.$=e,a.noConflict=function(){return t.Backbone=n,this},a.emulateHTTP=!1,a.emulateJSON=!1;var o=a.Events={on:function(t,e,n){return h(this,"on",t,[e,n])&&e&&(this._events||(this._events={}),(this._events[t]||(this._events[t]=[])).push({callback:e,context:n,ctx:n||this})),this},once:function(t,e,n){if(!h(this,"once",t,[e,n])||!e)return this;var i=this,r=x.once(function(){i.off(t,r),e.apply(this,arguments)});return r._callback=e,this.on(t,r,n)},off:function(t,e,n){var i,r,o,s,a,l,u,c;if(!this._events||!h(this,"off",t,[e,n]))return this;if(!t&&!e&&!n)return this._events=void 0,this;for(a=0,l=(s=t?[t]:x.keys(this._events)).length;a<l;a++)if(t=s[a],o=this._events[t]){if(this._events[t]=i=[],e||n)for(u=0,c=o.length;u<c;u++)r=o[u],(e&&e!==r.callback&&e!==r.callback._callback||n&&n!==r.context)&&i.push(r);i.length||delete this._events[t]}return this},trigger:function(t){if(!this._events)return this;var e=r.call(arguments,1);if(!h(this,"trigger",t,e))return this;var n=this._events[t],i=this._events.all;return n&&s(n,e),i&&s(i,arguments),this},stopListening:function(t,e,n){var i=this._listeningTo;if(!i)return this;var r=!e&&!n;for(var o in n||"object"!=typeof e||(n=this),t&&((i={})[t._listenId]=t),i)(t=i[o]).off(e,n,this),(r||x.isEmpty(t._events))&&delete this._listeningTo[o];return this}},l=/\s+/,h=function(t,e,n,i){if(!n)return!0;if("object"==typeof n){for(var r in n)t[e].apply(t,[r,n[r]].concat(i));return!1}if(l.test(n)){for(var o=n.split(l),s=0,a=o.length;s<a;s++)t[e].apply(t,[o[s]].concat(i));return!1}return!0},s=function(t,e){var n,i=-1,r=t.length,o=e[0],s=e[1],a=e[2];switch(e.length){case 0:for(;++i<r;)(n=t[i]).callback.call(n.ctx);return;case 1:for(;++i<r;)(n=t[i]).callback.call(n.ctx,o);return;case 2:for(;++i<r;)(n=t[i]).callback.call(n.ctx,o,s);return;case 3:for(;++i<r;)(n=t[i]).callback.call(n.ctx,o,s,a);return;default:for(;++i<r;)(n=t[i]).callback.apply(n.ctx,e);return}};x.each({listenTo:"on",listenToOnce:"once"},function(r,t){o[t]=function(t,e,n){var i=this._listeningTo||(this._listeningTo={});return n||"object"!=typeof e||(n=this),(i[t._listenId||(t._listenId=x.uniqueId("l"))]=t)[r](e,n,this),this}}),o.bind=o.on,o.unbind=o.off,x.extend(a,o);var S=a.Model=function(t,e){var n=t||{};e=e||{},this.cid=x.uniqueId("c"),this.attributes={},e.collection&&(this.collection=e.collection),e.parse&&(n=this.parse(n,e)||{}),n=x.defaults({},n,x.result(this,"defaults")),this.set(n,e),this.changed={},this.initialize.apply(this,arguments)};x.extend(S.prototype,o,{changed:null,validationError:null,idAttribute:"id",initialize:function(){},toJSON:function(t){return x.clone(this.attributes)},sync:function(){return a.sync.apply(this,arguments)},get:function(t){return this.attributes[t]},escape:function(t){return x.escape(this.get(t))},has:function(t){return null!=this.get(t)},set:function(t,e,n){var i,r,o,s,a,l,u,c;if(null==t)return this;if("object"==typeof t?(r=t,n=e):(r={})[t]=e,n=n||{},!this._validate(r,n))return!1;for(i in o=n.unset,a=n.silent,s=[],l=this._changing,this._changing=!0,l||(this._previousAttributes=x.clone(this.attributes),this.changed={}),c=this.attributes,u=this._previousAttributes,this.idAttribute in r&&(this.id=r[this.idAttribute]),r)e=r[i],x.isEqual(c[i],e)||s.push(i),x.isEqual(u[i],e)?delete this.changed[i]:this.changed[i]=e,o?delete c[i]:c[i]=e;if(!a){s.length&&(this._pending=n);for(var h=0,d=s.length;h<d;h++)this.trigger("change:"+s[h],this,c[s[h]],n)}if(l)return this;if(!a)for(;this._pending;)n=this._pending,this._pending=!1,this.trigger("change",this,n);return this._pending=!1,this._changing=!1,this},unset:function(t,e){return this.set(t,void 0,x.extend({},e,{unset:!0}))},clear:function(t){var e={};for(var n in this.attributes)e[n]=void 0;return this.set(e,x.extend({},t,{unset:!0}))},hasChanged:function(t){return null==t?!x.isEmpty(this.changed):x.has(this.changed,t)},changedAttributes:function(t){if(!t)return!!this.hasChanged()&&x.clone(this.changed);var e,n=!1,i=this._changing?this._previousAttributes:this.attributes;for(var r in t)x.isEqual(i[r],e=t[r])||((n=n||{})[r]=e);return n},previous:function(t){return null!=t&&this._previousAttributes?this._previousAttributes[t]:null},previousAttributes:function(){return x.clone(this._previousAttributes)},fetch:function(e){void 0===(e=e?x.clone(e):{}).parse&&(e.parse=!0);var n=this,i=e.success;return e.success=function(t){if(!n.set(n.parse(t,e),e))return!1;i&&i(n,t,e),n.trigger("sync",n,t,e)},N(this,e),this.sync("read",this,e)},save:function(t,e,n){var i,r,o,s=this.attributes;if(null==t||"object"==typeof t?(i=t,n=e):(i={})[t]=e,n=x.extend({validate:!0},n),i&&!n.wait){if(!this.set(i,n))return!1}else if(!this._validate(i,n))return!1;i&&n.wait&&(this.attributes=x.extend({},s,i)),void 0===n.parse&&(n.parse=!0);var a=this,l=n.success;return n.success=function(t){a.attributes=s;var e=a.parse(t,n);if(n.wait&&(e=x.extend(i||{},e)),x.isObject(e)&&!a.set(e,n))return!1;l&&l(a,t,n),a.trigger("sync",a,t,n)},N(this,n),"patch"==(r=this.isNew()?"create":n.patch?"patch":"update")&&(n.attrs=i),o=this.sync(r,this,n),i&&n.wait&&(this.attributes=s),o},destroy:function(e){e=e?x.clone(e):{};function n(){i.trigger("destroy",i,i.collection,e)}var i=this,r=e.success;if(e.success=function(t){(e.wait||i.isNew())&&n(),r&&r(i,t,e),i.isNew()||i.trigger("sync",i,t,e)},this.isNew())return e.success(),!1;N(this,e);var t=this.sync("delete",this,e);return e.wait||n(),t},url:function(){var t=x.result(this,"urlRoot")||x.result(this.collection,"url")||A();return this.isNew()?t:t.replace(/([^\/])$/,"$1/")+encodeURIComponent(this.id)},parse:function(t,e){return t},clone:function(){return new this.constructor(this.attributes)},isNew:function(){return!this.has(this.idAttribute)},isValid:function(t){return this._validate({},x.extend(t||{},{validate:!0}))},_validate:function(t,e){if(!e.validate||!this.validate)return!0;t=x.extend({},this.attributes,t);var n=this.validationError=this.validate(t,e)||null;return!n||(this.trigger("invalid",this,n,x.extend(e,{validationError:n})),!1)}});x.each(["keys","values","pairs","invert","pick","omit"],function(e){S.prototype[e]=function(){var t=r.call(arguments);return t.unshift(this.attributes),x[e].apply(x,t)}});var u=a.Collection=function(t,e){(e=e||{}).model&&(this.model=e.model),void 0!==e.comparator&&(this.comparator=e.comparator),this._reset(),this.initialize.apply(this,arguments),t&&this.reset(t,x.extend({silent:!0},e))},T={add:!0,remove:!0,merge:!0},c={add:!0,remove:!1};x.extend(u.prototype,o,{model:S,initialize:function(){},toJSON:function(e){return this.map(function(t){return t.toJSON(e)})},sync:function(){return a.sync.apply(this,arguments)},add:function(t,e){return this.set(t,x.extend({merge:!1},e,c))},remove:function(t,e){var n,i,r,o,s=!x.isArray(t);for(e=e||{},n=0,i=(t=s?[t]:x.clone(t)).length;n<i;n++)(o=t[n]=this.get(t[n]))&&(delete this._byId[o.id],delete this._byId[o.cid],r=this.indexOf(o),this.models.splice(r,1),this.length--,e.silent||(e.index=r,o.trigger("remove",o,this,e)),this._removeReference(o,e));return s?t[0]:t},set:function(t,e){(e=x.defaults({},e,T)).parse&&(t=this.parse(t,e));var n,i,r,o,s,a,l,u=!x.isArray(t);t=u?t?[t]:[]:x.clone(t);var c=e.at,h=this.model,d=this.comparator&&null==c&&!1!==e.sort,f=x.isString(this.comparator)?this.comparator:null,p=[],g=[],v={},m=e.add,y=e.merge,b=e.remove,w=!(d||!m||!b)&&[];for(n=0,i=t.length;n<i;n++){if(r=(s=t[n]||{})instanceof S?o=s:s[h.prototype.idAttribute||"id"],a=this.get(r))b&&(v[a.cid]=!0),y&&(s=s===o?o.attributes:s,e.parse&&(s=a.parse(s,e)),a.set(s,e),d&&!l&&a.hasChanged(f)&&(l=!0)),t[n]=a;else if(m){if(!(o=t[n]=this._prepareModel(s,e)))continue;p.push(o),this._addReference(o,e)}o=a||o,!w||!o.isNew()&&v[o.id]||w.push(o),v[o.id]=!0}if(b){for(n=0,i=this.length;n<i;++n)v[(o=this.models[n]).cid]||g.push(o);g.length&&this.remove(g,e)}if(p.length||w&&w.length)if(d&&(l=!0),this.length+=p.length,null!=c)for(n=0,i=p.length;n<i;n++)this.models.splice(c+n,0,p[n]);else{w&&(this.models.length=0);var _=w||p;for(n=0,i=_.length;n<i;n++)this.models.push(_[n])}if(l&&this.sort({silent:!0}),!e.silent){for(n=0,i=p.length;n<i;n++)(o=p[n]).trigger("add",o,this,e);(l||w&&w.length)&&this.trigger("sort",this,e)}return u?t[0]:t},reset:function(t,e){e=e||{};for(var n=0,i=this.models.length;n<i;n++)this._removeReference(this.models[n],e);return e.previousModels=this.models,this._reset(),t=this.add(t,x.extend({silent:!0},e)),e.silent||this.trigger("reset",this,e),t},push:function(t,e){return this.add(t,x.extend({at:this.length},e))},pop:function(t){var e=this.at(this.length-1);return this.remove(e,t),e},unshift:function(t,e){return this.add(t,x.extend({at:0},e))},shift:function(t){var e=this.at(0);return this.remove(e,t),e},slice:function(){return r.apply(this.models,arguments)},get:function(t){if(null!=t)return this._byId[t]||this._byId[t.id]||this._byId[t.cid]},at:function(t){return this.models[t]},where:function(n,t){return x.isEmpty(n)?t?void 0:[]:this[t?"find":"filter"](function(t){for(var e in n)if(n[e]!==t.get(e))return!1;return!0})},findWhere:function(t){return this.where(t,!0)},sort:function(t){if(!this.comparator)throw new Error("Cannot sort a set without a comparator");return t=t||{},x.isString(this.comparator)||1===this.comparator.length?this.models=this.sortBy(this.comparator,this):this.models.sort(x.bind(this.comparator,this)),t.silent||this.trigger("sort",this,t),this},pluck:function(t){return x.invoke(this.models,"get",t)},fetch:function(n){void 0===(n=n?x.clone(n):{}).parse&&(n.parse=!0);var i=n.success,r=this;return n.success=function(t){var e=n.reset?"reset":"set";r[e](t,n),i&&i(r,t,n),r.trigger("sync",r,t,n)},N(this,n),this.sync("read",this,n)},create:function(t,n){if(n=n?x.clone(n):{},!(t=this._prepareModel(t,n)))return!1;n.wait||this.add(t,n);var i=this,r=n.success;return n.success=function(t,e){n.wait&&i.add(t,n),r&&r(t,e,n)},t.save(null,n),t},parse:function(t,e){return t},clone:function(){return new this.constructor(this.models)},_reset:function(){this.length=0,this.models=[],this._byId={}},_prepareModel:function(t,e){if(t instanceof S)return t;var n=new(((e=e?x.clone(e):{}).collection=this).model)(t,e);return n.validationError?(this.trigger("invalid",this,n.validationError,e),!1):n},_addReference:function(t,e){null!=(this._byId[t.cid]=t).id&&(this._byId[t.id]=t),t.collection||(t.collection=this),t.on("all",this._onModelEvent,this)},_removeReference:function(t,e){this===t.collection&&delete t.collection,t.off("all",this._onModelEvent,this)},_onModelEvent:function(t,e,n,i){("add"!==t&&"remove"!==t||n===this)&&("destroy"===t&&this.remove(e,i),e&&t==="change:"+e.idAttribute&&(delete this._byId[e.previous(e.idAttribute)],null!=e.id&&(this._byId[e.id]=e)),this.trigger.apply(this,arguments))}});x.each(["forEach","each","map","collect","reduce","foldl","inject","reduceRight","foldr","find","detect","filter","select","reject","every","all","some","any","include","contains","invoke","max","min","toArray","size","first","head","take","initial","rest","tail","drop","last","without","difference","indexOf","shuffle","lastIndexOf","isEmpty","chain","sample"],function(e){u.prototype[e]=function(){var t=r.call(arguments);return t.unshift(this.models),x[e].apply(x,t)}});x.each(["groupBy","countBy","sortBy","indexBy"],function(i){u.prototype[i]=function(e,t){var n=x.isFunction(e)?e:function(t){return t.get(e)};return x[i](this.models,n,t)}});var d=a.View=function(t){this.cid=x.uniqueId("view"),t=t||{},x.extend(this,x.pick(t,p)),this._ensureElement(),this.initialize.apply(this,arguments),this.delegateEvents()},f=/^(\S+)\s*(.*)$/,p=["model","collection","el","id","attributes","className","tagName","events"];x.extend(d.prototype,o,{tagName:"div",$:function(t){return this.$el.find(t)},initialize:function(){},render:function(){return this},remove:function(){return this.$el.remove(),this.stopListening(),this},setElement:function(t,e){return this.$el&&this.undelegateEvents(),this.$el=t instanceof a.$?t:a.$(t),this.el=this.$el[0],!1!==e&&this.delegateEvents(),this},delegateEvents:function(t){if(!(t=t||x.result(this,"events")))return this;for(var e in this.undelegateEvents(),t){var n=t[e];if(x.isFunction(n)||(n=this[t[e]]),n){var i=e.match(f),r=i[1],o=i[2];n=x.bind(n,this),r+=".delegateEvents"+this.cid,""===o?this.$el.on(r,n):this.$el.on(r,o,n)}}return this},undelegateEvents:function(){return this.$el.off(".delegateEvents"+this.cid),this},_ensureElement:function(){if(this.el)this.setElement(x.result(this,"el"),!1);else{var t=x.extend({},x.result(this,"attributes"));this.id&&(t.id=x.result(this,"id")),this.className&&(t.class=x.result(this,"className"));var e=a.$("<"+x.result(this,"tagName")+">").attr(t);this.setElement(e,!1)}}}),a.sync=function(t,e,n){var i=v[t];x.defaults(n=n||{},{emulateHTTP:a.emulateHTTP,emulateJSON:a.emulateJSON});var r={type:i,dataType:"json"};if(n.url||(r.url=x.result(e,"url")||A()),null!=n.data||!e||"create"!==t&&"update"!==t&&"patch"!==t||(r.contentType="application/json",r.data=JSON.stringify(n.attrs||e.toJSON(n))),n.emulateJSON&&(r.contentType="application/x-www-form-urlencoded",r.data=r.data?{model:r.data}:{}),n.emulateHTTP&&("PUT"===i||"DELETE"===i||"PATCH"===i)){r.type="POST",n.emulateJSON&&(r.data._method=i);var o=n.beforeSend;n.beforeSend=function(t){if(t.setRequestHeader("X-HTTP-Method-Override",i),o)return o.apply(this,arguments)}}"GET"===r.type||n.emulateJSON||(r.processData=!1),"PATCH"===r.type&&g&&(r.xhr=function(){return new ActiveXObject("Microsoft.XMLHTTP")});var s=n.xhr=a.ajax(x.extend(r,n));return e.trigger("request",e,s,n),s};var g=!("undefined"==typeof window||!window.ActiveXObject||window.XMLHttpRequest&&(new XMLHttpRequest).dispatchEvent),v={create:"POST",update:"PUT",patch:"PATCH",delete:"DELETE",read:"GET"};a.ajax=function(){return a.$.ajax.apply(a.$,arguments)};var m=a.Router=function(t){(t=t||{}).routes&&(this.routes=t.routes),this._bindRoutes(),this.initialize.apply(this,arguments)},y=/\((.*?)\)/g,b=/(\(\?)?:\w+/g,w=/\*\w+/g,_=/[\-{}\[\]+?.,\\\^$|#\s]/g;x.extend(m.prototype,o,{initialize:function(){},route:function(n,i,r){x.isRegExp(n)||(n=this._routeToRegExp(n)),x.isFunction(i)&&(r=i,i=""),r=r||this[i];var o=this;return a.history.route(n,function(t){var e=o._extractParameters(n,t);o.execute(r,e),o.trigger.apply(o,["route:"+i].concat(e)),o.trigger("route",i,e),a.history.trigger("route",o,i,e)}),this},execute:function(t,e){t&&t.apply(this,e)},navigate:function(t,e){return a.history.navigate(t,e),this},_bindRoutes:function(){if(this.routes){this.routes=x.result(this,"routes");for(var t,e=x.keys(this.routes);null!=(t=e.pop());)this.route(t,this.routes[t])}},_routeToRegExp:function(t){return t=t.replace(_,"\\$&").replace(y,"(?:$1)?").replace(b,function(t,e){return e?t:"([^/?]+)"}).replace(w,"([^?]*?)"),new RegExp("^"+t+"(?:\\?([\\s\\S]*))?$")},_extractParameters:function(t,e){var n=t.exec(e).slice(1);return x.map(n,function(t,e){return e===n.length-1?t||null:t?decodeURIComponent(t):null})}});var C=a.History=function(){this.handlers=[],x.bindAll(this,"checkUrl"),"undefined"!=typeof window&&(this.location=window.location,this.history=window.history)},E=/^[#\/]|\s+$/g,k=/^\/+|\/+$/g,M=/msie [\w.]+/,D=/\/$/,O=/#.*$/;C.started=!1,x.extend(C.prototype,o,{interval:50,atRoot:function(){return this.location.pathname.replace(/[^\/]$/,"$&/")===this.root},getHash:function(t){var e=(t||this).location.href.match(/#(.*)$/);return e?e[1]:""},getFragment:function(t,e){if(null==t)if(this._hasPushState||!this._wantsHashChange||e){t=decodeURI(this.location.pathname+this.location.search);var n=this.root.replace(D,"");t.indexOf(n)||(t=t.slice(n.length))}else t=this.getHash();return t.replace(E,"")},start:function(t){if(C.started)throw new Error("Backbone.history has already been started");C.started=!0,this.options=x.extend({root:"/"},this.options,t),this.root=this.options.root,this._wantsHashChange=!1!==this.options.hashChange,this._wantsPushState=!!this.options.pushState,this._hasPushState=!!(this.options.pushState&&this.history&&this.history.pushState);var e=this.getFragment(),n=document.documentMode,i=M.exec(navigator.userAgent.toLowerCase())&&(!n||n<=7);if(this.root=("/"+this.root+"/").replace(k,"/"),i&&this._wantsHashChange){var r=a.$('<iframe src="javascript:0" tabindex="-1">');this.iframe=r.hide().appendTo("body")[0].contentWindow,this.navigate(e)}this._hasPushState?a.$(window).on("popstate",this.checkUrl):this._wantsHashChange&&"onhashchange"in window&&!i?a.$(window).on("hashchange",this.checkUrl):this._wantsHashChange&&(this._checkUrlInterval=setInterval(this.checkUrl,this.interval)),this.fragment=e;var o=this.location;if(this._wantsHashChange&&this._wantsPushState){if(!this._hasPushState&&!this.atRoot())return this.fragment=this.getFragment(null,!0),this.location.replace(this.root+"#"+this.fragment),!0;this._hasPushState&&this.atRoot()&&o.hash&&(this.fragment=this.getHash().replace(E,""),this.history.replaceState({},document.title,this.root+this.fragment))}if(!this.options.silent)return this.loadUrl()},stop:function(){a.$(window).off("popstate",this.checkUrl).off("hashchange",this.checkUrl),this._checkUrlInterval&&clearInterval(this._checkUrlInterval),C.started=!1},route:function(t,e){this.handlers.unshift({route:t,callback:e})},checkUrl:function(t){var e=this.getFragment();if(e===this.fragment&&this.iframe&&(e=this.getFragment(this.getHash(this.iframe))),e===this.fragment)return!1;this.iframe&&this.navigate(e),this.loadUrl()},loadUrl:function(e){return e=this.fragment=this.getFragment(e),x.any(this.handlers,function(t){if(t.route.test(e))return t.callback(e),!0})},navigate:function(t,e){if(!C.started)return!1;e&&!0!==e||(e={trigger:!!e});var n=this.root+(t=this.getFragment(t||""));if(t=t.replace(O,""),this.fragment!==t){if(""===(this.fragment=t)&&"/"!==n&&(n=n.slice(0,-1)),this._hasPushState)this.history[e.replace?"replaceState":"pushState"]({},document.title,n);else{if(!this._wantsHashChange)return this.location.assign(n);this._updateHash(this.location,t,e.replace),this.iframe&&t!==this.getFragment(this.getHash(this.iframe))&&(e.replace||this.iframe.document.open().close(),this._updateHash(this.iframe.location,t,e.replace))}return e.trigger?this.loadUrl(t):void 0}},_updateHash:function(t,e,n){if(n){var i=t.href.replace(/(javascript:|#).*$/,"");t.replace(i+"#"+e)}else t.hash="#"+e}}),a.history=new C;S.extend=u.extend=m.extend=d.extend=C.extend=function(t,e){var n,i=this;n=t&&x.has(t,"constructor")?t.constructor:function(){return i.apply(this,arguments)},x.extend(n,i,e);function r(){this.constructor=n}return r.prototype=i.prototype,n.prototype=new r,t&&x.extend(n.prototype,t),n.__super__=i.prototype,n};var A=function(){throw new Error('A "url" property or function must be specified')},N=function(e,n){var i=n.error;n.error=function(t){i&&i(e,t,n),e.trigger("error",e,t,n)}};return a}),"object"!=typeof JSON&&(JSON={}),function(){"use strict";function f(t){return t<10?"0"+t:t}var cx,escapable,gap,indent,meta,rep;function quote(t){return escapable.lastIndex=0,escapable.test(t)?'"'+t.replace(escapable,function(t){var e=meta[t];return"string"==typeof e?e:"\\u"+("0000"+t.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+t+'"'}function str(t,e){var n,i,r,o,s,a=gap,l=e[t];switch(l&&"object"==typeof l&&"function"==typeof l.toJSON&&(l=l.toJSON(t)),"function"==typeof rep&&(l=rep.call(e,t,l)),typeof l){case"string":return quote(l);case"number":return isFinite(l)?String(l):"null";case"boolean":case"null":return String(l);case"object":if(!l)return"null";if(gap+=indent,s=[],"[object Array]"===Object.prototype.toString.apply(l)){for(o=l.length,n=0;n<o;n+=1)s[n]=str(n,l)||"null";return r=0===s.length?"[]":gap?"[\n"+gap+s.join(",\n"+gap)+"\n"+a+"]":"["+s.join(",")+"]",gap=a,r}if(rep&&"object"==typeof rep)for(o=rep.length,n=0;n<o;n+=1)"string"==typeof rep[n]&&(r=str(i=rep[n],l))&&s.push(quote(i)+(gap?": ":":")+r);else for(i in l)Object.prototype.hasOwnProperty.call(l,i)&&(r=str(i,l))&&s.push(quote(i)+(gap?": ":":")+r);return r=0===s.length?"{}":gap?"{\n"+gap+s.join(",\n"+gap)+"\n"+a+"}":"{"+s.join(",")+"}",gap=a,r}}"function"!=typeof Date.prototype.toJSON&&(Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null},String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(){return this.valueOf()}),"function"!=typeof JSON.stringify&&(escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,meta={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},JSON.stringify=function(t,e,n){var i;if(indent=gap="","number"==typeof n)for(i=0;i<n;i+=1)indent+=" ";else"string"==typeof n&&(indent=n);if((rep=e)&&"function"!=typeof e&&("object"!=typeof e||"number"!=typeof e.length))throw new Error("JSON.stringify");return str("",{"":t})}),"function"!=typeof JSON.parse&&(cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,JSON.parse=function(text,reviver){var j;function walk(t,e){var n,i,r=t[e];if(r&&"object"==typeof r)for(n in r)Object.prototype.hasOwnProperty.call(r,n)&&(void 0!==(i=walk(r,n))?r[n]=i:delete r[n]);return reviver.call(t,e,r)}if(text=String(text),cx.lastIndex=0,cx.test(text)&&(text=text.replace(cx,function(t){return"\\u"+("0000"+t.charCodeAt(0).toString(16)).slice(-4)})),/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return j=eval("("+text+")"),"function"==typeof reviver?walk({"":j},""):j;throw new SyntaxError("JSON.parse")})}();var saveAs=saveAs||function(a){"use strict";if(!(void 0===a||"undefined"!=typeof navigator&&/MSIE [1-9]\./.test(navigator.userAgent))){var t=a.document,l=function(){return a.URL||a.webkitURL||a},u=t.createElementNS("http://www.w3.org/1999/xhtml","a"),c="download"in u,h=/constructor/i.test(a.HTMLElement)||a.safari,d=/CriOS\/[\d]+/.test(navigator.userAgent),f=function(t){(a.setImmediate||a.setTimeout)(function(){throw t},0)},p=function(t){setTimeout(function(){"string"==typeof t?l().revokeObjectURL(t):t.remove()},4e4)},g=function(t){return/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(t.type)?new Blob([String.fromCharCode(65279),t],{type:t.type}):t},i=function(t,e,n){n||(t=g(t));function i(){!function(t,e,n){for(var i=(e=[].concat(e)).length;i--;){var r=t["on"+e[i]];if("function"==typeof r)try{r.call(t,n||t)}catch(t){f(t)}}}(o,"writestart progress write writeend".split(" "))}var r,o=this,s="application/octet-stream"===t.type;if(o.readyState=o.INIT,c)return r=l().createObjectURL(t),void setTimeout(function(){u.href=r,u.download=e,function(t){var e=new MouseEvent("click");t.dispatchEvent(e)}(u),i(),p(r),o.readyState=o.DONE});!function(){if((d||s&&h)&&a.FileReader){var e=new FileReader;return e.onloadend=function(){var t=d?e.result:e.result.replace(/^data:[^;]*;/,"data:attachment/file;");a.open(t,"_blank")||(a.location.href=t),t=void 0,o.readyState=o.DONE,i()},e.readAsDataURL(t),o.readyState=o.INIT}(r=r||l().createObjectURL(t),s)?a.location.href=r:a.open(r,"_blank")||(a.location.href=r);o.readyState=o.DONE,i(),p(r)}()},e=i.prototype;return"undefined"!=typeof navigator&&navigator.msSaveOrOpenBlob?function(t,e,n){return e=e||t.name||"download",n||(t=g(t)),navigator.msSaveOrOpenBlob(t,e)}:(e.abort=function(){},e.readyState=e.INIT=0,e.WRITING=1,e.DONE=2,e.error=e.onwritestart=e.onprogress=e.onwrite=e.onabort=e.onerror=e.onwriteend=null,function(t,e,n){return new i(t,e||t.name||"download",n)})}}("undefined"!=typeof self&&self||"undefined"!=typeof window&&window||this.content);"undefined"!=typeof module&&module.exports?module.exports.saveAs=saveAs:"undefined"!=typeof define&&null!==define&&null!==define.amd&&define("FileSaver.js",function(){return saveAs}),function(t,e){if("object"==typeof exports)module.exports=e(require("underscore"),require("backbone"));else if("function"==typeof define&&define.amd)define(["underscore","backbone"],e);else{for(var n="FilteredCollection".split("."),i=t,r=0;r<n.length-1;r++)void 0===i[n[r]]&&(i[n[r]]={}),i=i[n[r]];i[n[n.length-1]]=e(t._,t.Backbone)}}(this,function(e,n){function g(t){return{underscore:e,backbone:n}[t]}function v(t){var e=v.cache[t];if(!e){var n={};e=v.cache[t]={id:t,exports:n},v.modules[t].call(n,e,n)}return e.exports}return v.cache=[],v.modules=[function(t,e){var n=g("underscore"),i=g("backbone"),r=v(1),o=v(2);function s(){this._filterResultCache={}}function a(t){for(var e in this._filterResultCache)this._filterResultCache.hasOwnProperty(e)&&delete this._filterResultCache[e][t]}function l(t){this._filterResultCache[t.cid]||(this._filterResultCache[t.cid]={});var e=this._filterResultCache[t.cid];for(var n in this._filters)if(this._filters.hasOwnProperty(n)&&(e.hasOwnProperty(n)||(e[n]=this._filters[n].fn(t)),!e[n]))return!1;return!0}function u(){var t=[];this._superset&&(t=this._superset.filter(n.bind(l,this))),this._collection.reset(t),this.length=this._collection.length}function c(t){if(this._filterResultCache[t.cid]={},l.call(this,t)){if(!this._collection.get(t.cid)){for(var e=null,n=this.superset().indexOf(t)-1;0<=n;n-=1)if(this.contains(this.superset().at(n))){e=this.indexOf(this.superset().at(n))+1;break}e=e||0,this._collection.add(t,{at:e})}}else this._collection.get(t.cid)&&this._collection.remove(t);this.length=this._collection.length}function h(t,e,n){"change:"===t.slice(0,7)&&function(t){this._filterResultCache[t.cid]={},l.call(this,t)||this._collection.get(t.cid)&&this._collection.remove(t)}.call(this,e)}function d(t){this.contains(t)&&this._collection.remove(t),this.length=this._collection.length}function f(t){this._superset=t,this._collection=new i.Collection(t.toArray()),r(this._collection,this),this.resetFilters(),this.listenTo(this._superset,"reset sort",u),this.listenTo(this._superset,"add change",c),this.listenTo(this._superset,"remove",d),this.listenTo(this._superset,"all",h)}var p={defaultFilterName:"__default",filterBy:function(t,e){return e||(e=t,t=this.defaultFilterName),function(t,e){this._filters[t]&&a.call(this,t),this._filters[t]=e,this.trigger("filtered:add",t)}.call(this,t,o(e)),u.call(this),this},removeFilter:function(t){return t=t||this.defaultFilterName,function(t){delete this._filters[t],a.call(this,t),this.trigger("filtered:remove",t)}.call(this,t),u.call(this),this},resetFilters:function(){return this._filters={},s.call(this),this.trigger("filtered:reset"),u.call(this),this},superset:function(){return this._superset},refilter:function(t){return"object"==typeof t&&t.cid?c.call(this,t):(s.call(this),u.call(this)),this},getFilters:function(){return n.keys(this._filters)},hasFilter:function(t){return n.contains(this.getFilters(),t)},destroy:function(){this.stopListening(),this._collection.reset([]),this._superset=this._collection,this.length=0,this.trigger("filtered:destroy")}};n.extend(f.prototype,p,i.Events),t.exports=f},function(t,e){var o=g("underscore"),n=g("backbone"),s=["_onModelEvent","_prepareModel","_removeReference","_reset","add","initialize","sync","remove","reset","set","push","pop","unshift","shift","sort","parse","fetch","create","model","off","on","listenTo","listenToOnce","bind","trigger","once","stopListening"],a=["add","remove","reset","sort","destroy","sync","request","error"];t.exports=function(i,r){function t(){r.length=i.length}var e={};return o.each(o.functions(n.Collection.prototype),function(t){o.contains(s,t)||(e[t]=function(){return i[t].apply(i,arguments)})}),o.extend(r,n.Events,e),r.listenTo(i,"all",t),r.listenTo(i,"all",function(t){var e=o.toArray(arguments),n="change"===t||"change:"===t.slice(0,7);"reset"===t&&(r.models=i.models),o.contains(a,t)?(o.contains(["add","remove","destroy"],t)?e[2]=r:o.contains(["reset","sort"],t)&&(e[1]=r),r.trigger.apply(this,e)):n&&r.contains(e[1])&&r.trigger.apply(this,e)}),r.models=i.models,t(),r}},function(t,e){var r=g("underscore");function o(t,e){return r.isArray(e)||(e=null),{fn:t,keys:e}}function n(n){var t=r.keys(n),i=r.map(t,function(t){var e=n[t];return r.isFunction(e)?function(e,n){return function(t){return n(t.get(e))}}(t,e):function(e,n){return function(t){return t.get(e)===n}}(t,e)});return o(function(t){for(var e=0;e<i.length;e++)if(!i[e](t))return!1;return!0},t)}t.exports=function(t,e){return r.isFunction(t)?o(t,e):r.isObject(t)?n(t):void 0}}],v(0)}),function(t,n){if("function"==typeof define&&define.amd)define(["backbone","underscore"],function(t,e){return n(t,e)});else if("undefined"!=typeof exports){var e=require("backbone"),i=require("underscore");module.exports=n(e,i)}else n(t.Backbone,t._)}(this,function(t,e){"use strict";var n=t.ChildViewContainer;return t.ChildViewContainer=function(t,i){function n(t){this._views={},this._indexByModel={},this._indexByCustom={},this._updateLength(),i.each(t,this.add,this)}i.extend(n.prototype,{add:function(t,e){var n=t.cid;return(this._views[n]=t).model&&(this._indexByModel[t.model.cid]=n),e&&(this._indexByCustom[e]=n),this._updateLength(),this},findByModel:function(t){return this.findByModelCid(t.cid)},findByModelCid:function(t){var e=this._indexByModel[t];return this.findByCid(e)},findByCustom:function(t){var e=this._indexByCustom[t];return this.findByCid(e)},findByIndex:function(t){return i.values(this._views)[t]},findByCid:function(t){return this._views[t]},remove:function(t){var n=t.cid;return t.model&&delete this._indexByModel[t.model.cid],i.any(this._indexByCustom,function(t,e){if(t===n)return delete this._indexByCustom[e],!0},this),delete this._views[n],this._updateLength(),this},call:function(t){this.apply(t,i.tail(arguments))},apply:function(e,n){i.each(this._views,function(t){i.isFunction(t[e])&&t[e].apply(t,n||[])})},_updateLength:function(){this.length=i.size(this._views)}});return i.each(["forEach","each","map","find","detect","filter","select","reject","every","all","some","any","include","contains","invoke","toArray","first","initial","rest","last","without","isEmpty","pluck","reduce"],function(e){n.prototype[e]=function(){var t=[i.values(this._views)].concat(i.toArray(arguments));return i[e].apply(i,t)}}),n}(0,e),t.ChildViewContainer.VERSION="0.1.6",t.ChildViewContainer.noConflict=function(){return t.ChildViewContainer=n,this},t.ChildViewContainer}),function(i,r){"function"==typeof define&&define.amd?define(["underscore","backbone","jquery"],function(t,e,n){return i.Backbone=r(t,e,n)}):"undefined"!=typeof exports&&"undefined"!=typeof require?module.exports=r(require("underscore"),require("backbone"),require("jquery")):i.Backbone=r(i._,i.Backbone,i.jQuery)}(this,function(h,d,f){var p={modelFetch:d.Model.prototype.fetch,modelSync:d.Model.prototype.sync,collectionFetch:d.Collection.prototype.fetch},n=function(){var e=void 0!==window.localStorage;if(e)try{localStorage.setItem("test_support","test_support"),localStorage.removeItem("test_support")}catch(t){e=!1}return e}();function i(t,e){if(t&&h.isObject(t)){if(h.isFunction(t.getCacheKey))return t.getCacheKey(e);t=e&&e.url?e.url:h.isFunction(t.url)?t.url():t.url}else if(h.isFunction(t))return t(e);return e&&e.data?"string"==typeof e.data?t+"?"+e.data:t+"?"+f.param(e.data):t}function g(t,e){return h.isFunction(t)?t=t():t&&h.isObject(t)&&(t=i(t,e)),d.fetchCache._cache[t]}function a(t,e){h.isFunction(t)?t=t():t&&h.isObject(t)&&(t=i(t,e)),delete d.fetchCache._cache[t],d.fetchCache.setLocalStorage()}function t(){if(n&&d.fetchCache.localStorage){var t=localStorage.getItem(d.fetchCache.getLocalStorageKey())||"{}";d.fetchCache._cache=JSON.parse(t)}}function v(t){return window.setTimeout(t,0)}return d.fetchCache=d.fetchCache||{},d.fetchCache._cache=d.fetchCache._cache||{},d.fetchCache.enabled=!0,d.fetchCache.priorityFn=function(t,e){return t&&t.expires&&e&&e.expires?t.expires-e.expires:t},d.fetchCache._prioritize=function(){var t=h.values(this._cache).sort(this.priorityFn),e=h.indexOf(h.values(this._cache),t[0]);return h.keys(this._cache)[e]},d.fetchCache._deleteCacheWithPriority=function(){d.fetchCache._cache[this._prioritize()]=null,delete d.fetchCache._cache[this._prioritize()],d.fetchCache.setLocalStorage()},d.fetchCache.getLocalStorageKey=function(){return"backboneCache"},void 0===d.fetchCache.localStorage&&(d.fetchCache.localStorage=!0),d.Model.prototype.fetch=function(t){if(!d.fetchCache.enabled)return p.modelFetch.apply(this,arguments);t=h.defaults(t||{},{parse:!0});var e=g(d.fetchCache.getCacheKey(this,t)),n=!1,i=!1,r=!1,o=new f.Deferred,s=t.context||this,a=this;function l(){return t.prefill&&(!t.prefillExpires||i)}function u(){t.parse&&(r=a.parse(r,t)),a.set(r,t),h.isFunction(t.prefillSuccess)&&t.prefillSuccess.call(s,a,r,t),a.trigger("cachesync",a,r,t),a.trigger("sync",a,r,t),l()?o.notifyWith(s,[a]):(h.isFunction(t.success)&&t.success.call(s,a,r,t),o.resolveWith(s,[a]))}if(e&&(n=(n=e.expires)&&e.expires<(new Date).getTime(),i=(i=e.prefillExpires)&&e.prefillExpires<(new Date).getTime(),r=e.value),!n&&(t.cache||t.prefill)&&r&&(null==t.async&&(t.async=!0),t.async?v(u):u(),!l()))return o.promise();var c=p.modelFetch.apply(this,arguments);return void 0!==c&&(c.done(h.bind(o.resolve,s,this)).done(h.bind(d.fetchCache.setCache,null,this,t)).fail(h.bind(o.reject,s,this)),o.abort=c.abort),o.promise()},d.Model.prototype.sync=function(t,e,n){if("read"===t||!d.fetchCache.enabled)return p.modelSync.apply(this,arguments);var i,r,o=e.collection,s=[];for(s.push(d.fetchCache.getCacheKey(e,n)),o&&s.push(d.fetchCache.getCacheKey(o)),i=0,r=s.length;i<r;i++)a(s[i]);return p.modelSync.apply(this,arguments)},d.Collection.prototype.fetch=function(t){if(!d.fetchCache.enabled)return p.collectionFetch.apply(this,arguments);t=h.defaults(t||{},{parse:!0});var e=g(d.fetchCache.getCacheKey(this,t)),n=!1,i=!1,r=!1,o=new f.Deferred,s=t.context||this,a=this;function l(){return t.prefill&&(!t.prefillExpires||i)}function u(){a[t.reset?"reset":"set"](r,t),h.isFunction(t.prefillSuccess)&&t.prefillSuccess.call(s,a),a.trigger("cachesync",a,r,t),a.trigger("sync",a,r,t),l()?o.notifyWith(s,[a]):(h.isFunction(t.success)&&t.success.call(s,a,r,t),o.resolveWith(s,[a]))}if(e&&(n=(n=e.expires)&&e.expires<(new Date).getTime(),i=(i=e.prefillExpires)&&e.prefillExpires<(new Date).getTime(),r=e.value),!n&&(t.cache||t.prefill)&&r&&(null==t.async&&(t.async=!0),t.async?v(u):u(),!l()))return o.promise();var c=p.collectionFetch.apply(this,arguments);return void 0!==c&&(c.done(h.bind(o.resolve,s,this)).done(h.bind(d.fetchCache.setCache,null,this,t)).fail(h.bind(o.reject,s,this)),o.abort=c.abort),o.promise()},t(),d.fetchCache._superMethods=p,d.fetchCache.setCache=function(t,e,n){e=e||{};var i=d.fetchCache.getCacheKey(t,e),r=!1,o=e.lastSync||(new Date).getTime(),s=!1;i&&!1!==e.cache&&(e.cache||e.prefill)&&(!1!==e.expires&&(r=(new Date).getTime()+1e3*(e.expires||300)),!1!==e.prefillExpires&&(s=(new Date).getTime()+1e3*(e.prefillExpires||300)),d.fetchCache._cache[i]={expires:r,lastSync:o,prefillExpires:s,value:n},d.fetchCache.setLocalStorage())},d.fetchCache.getCache=g,d.fetchCache.getCacheKey=i,d.fetchCache.getLastSync=function(t,e){return g(t).lastSync},d.fetchCache.clearItem=a,d.fetchCache.reset=function(){d.fetchCache._cache={}},d.fetchCache.setLocalStorage=function(){if(n&&d.fetchCache.localStorage)try{localStorage.setItem(d.fetchCache.getLocalStorageKey(),JSON.stringify(d.fetchCache._cache))}catch(t){var e=t.code||t.number||t.message;if(22!==e&&1014!==e)throw t;this._deleteCacheWithPriority()}},d.fetchCache.getLocalStorage=t,d}),function(e,n){"object"==typeof exports&&"function"==typeof require?module.exports=n(require("backbone")):"function"==typeof define&&define.amd?define(["backbone"],function(t){return n(t||e.Backbone)}):n(Backbone)}(this,function(a){function e(){return(65536*(1+Math.random())|0).toString(16).substring(1)}function l(t,e){if(null!=t){var n=t[e];return"function"==typeof n?t[e]():n}}return a.LocalStorage=window.Store=function(t,e){if(!this.localStorage)throw"Backbone.localStorage: Environment does not support localStorage.";this.name=t,this.serializer=e||{serialize:function(t){return function(t){return t===Object(t)}(t)?JSON.stringify(t):t},deserialize:function(t){return t&&JSON.parse(t)}};var n=this.localStorage().getItem(this.name);this.records=n&&n.split(",")||[]},function(t,e){for(var n in e)t[n]=e[n]}(a.LocalStorage.prototype,{save:function(){this.localStorage().setItem(this.name,this.records.join(","))},create:function(t){return t.id||0===t.id||(t.id=e()+e()+"-"+e()+"-"+e()+"-"+e()+"-"+e()+e()+e(),t.set(t.idAttribute,t.id)),this.localStorage().setItem(this._itemName(t.id),this.serializer.serialize(t)),this.records.push(t.id.toString()),this.save(),this.find(t)},update:function(t){this.localStorage().setItem(this._itemName(t.id),this.serializer.serialize(t));var e=t.id.toString();return function(t,e){for(var n=t.length;n--;)if(t[n]===e)return!0;return!1}(this.records,e)||(this.records.push(e),this.save()),this.find(t)},find:function(t){return this.serializer.deserialize(this.localStorage().getItem(this._itemName(t.id)))},findAll:function(){for(var t,e,n=[],i=0;i<this.records.length;i++)t=this.records[i],null!=(e=this.serializer.deserialize(this.localStorage().getItem(this._itemName(t))))&&n.push(e);return n},destroy:function(t){this.localStorage().removeItem(this._itemName(t.id));for(var e=t.id.toString(),n=0;n<this.records.length;n++)this.records[n]===e&&this.records.splice(n,1);return this.save(),t},localStorage:function(){return localStorage},_clear:function(){var t=this.localStorage(),e=new RegExp("^"+this.name+"-");for(var n in t.removeItem(this.name),t)e.test(n)&&t.removeItem(n);this.records.length=0},_storageSize:function(){return this.localStorage().length},_itemName:function(t){return this.name+"-"+t}}),a.LocalStorage.sync=window.Store.sync=a.localSync=function(t,e,n){var i,r,o=l(e,"localStorage")||l(e.collection,"localStorage"),s=a.$?a.$.Deferred&&a.$.Deferred():a.Deferred&&a.Deferred();try{switch(t){case"read":i=null!=e.id?o.find(e):o.findAll();break;case"create":i=o.create(e);break;case"update":i=o.update(e);break;case"delete":i=o.destroy(e)}}catch(t){r=22===t.code&&0===o._storageSize()?"Private browsing is unsupported":t.message}return i?(n&&n.success&&("0.9.10"===a.VERSION?n.success(e,i,n):n.success(i)),s&&s.resolve(i)):(r=r||"Record Not Found",n&&n.error&&("0.9.10"===a.VERSION?n.error(e,r,n):n.error(r)),s&&s.reject(r)),n&&n.complete&&n.complete(i),s&&s.promise()},a.ajaxSync=a.sync,a.getSyncMethod=function(t,e){return e&&e.ajaxSync||!l(t,"localStorage")&&!l(t.collection,"localStorage")?a.ajaxSync:a.localSync},a.sync=function(t,e,n){return a.getSyncMethod(e,n).apply(this,[t,e,n])},a.LocalStorage}),function(n,i){if("function"==typeof define&&define.amd)define(["backbone","underscore"],function(t,e){return n.Marionette=n.Mn=i(n,t,e)});else if("undefined"!=typeof exports){var t=require("backbone"),e=require("underscore");module.exports=i(n,t,e)}else n.Marionette=n.Mn=i(n,n.Backbone,n._)}(this,function(t,s,l){"use strict";var e,n,i,r,o,a,u,c,h,d,f,p;function g(t){this.options=t,this._wreqrHandlers={},u.isFunction(this.initialize)&&this.initialize(t)}function v(t){this.options=t,this._commands={},o.isFunction(this.initialize)&&this.initialize(t)}function m(){}function y(t){this.vent=new r.Wreqr.EventAggregator,this.reqres=new r.Wreqr.RequestResponse,this.commands=new r.Wreqr.Commands,this.channelName=t}n=l,i=(e=s).ChildViewContainer,e.ChildViewContainer=function(t,i){function n(t){this._views={},this._indexByModel={},this._indexByCustom={},this._updateLength(),i.each(t,this.add,this)}return i.extend(n.prototype,{add:function(t,e){var n=t.cid;return(this._views[n]=t).model&&(this._indexByModel[t.model.cid]=n),e&&(this._indexByCustom[e]=n),this._updateLength(),this},findByModel:function(t){return this.findByModelCid(t.cid)},findByModelCid:function(t){var e=this._indexByModel[t];return this.findByCid(e)},findByCustom:function(t){var e=this._indexByCustom[t];return this.findByCid(e)},findByIndex:function(t){return i.values(this._views)[t]},findByCid:function(t){return this._views[t]},remove:function(t){var n=t.cid;return t.model&&delete this._indexByModel[t.model.cid],i.any(this._indexByCustom,function(t,e){if(t===n)return delete this._indexByCustom[e],!0},this),delete this._views[n],this._updateLength(),this},call:function(t){this.apply(t,i.tail(arguments))},apply:function(e,n){i.each(this._views,function(t){i.isFunction(t[e])&&t[e].apply(t,n||[])})},_updateLength:function(){this.length=i.size(this._views)}}),i.each(["forEach","each","map","find","detect","filter","select","reject","every","all","some","any","include","contains","invoke","toArray","first","initial","rest","last","without","isEmpty","pluck"],function(e){n.prototype[e]=function(){var t=[i.values(this._views)].concat(i.toArray(arguments));return i[e].apply(i,t)}}),n}(0,n),e.ChildViewContainer.VERSION="0.1.5",e.ChildViewContainer.noConflict=function(){return e.ChildViewContainer=i,this},e.ChildViewContainer,o=l,f=(r=s).Wreqr,p=r.Wreqr={},r.Wreqr.VERSION="1.3.1",r.Wreqr.noConflict=function(){return r.Wreqr=f,this},p.Handlers=(u=o,g.extend=(a=r).Model.extend,u.extend(g.prototype,a.Events,{setHandlers:function(t){u.each(t,function(t,e){var n=null;u.isObject(t)&&!u.isFunction(t)&&(n=t.context,t=t.callback),this.setHandler(e,t,n)},this)},setHandler:function(t,e,n){var i={callback:e,context:n};this._wreqrHandlers[t]=i,this.trigger("handler:add",t,e,n)},hasHandler:function(t){return!!this._wreqrHandlers[t]},getHandler:function(t){var e=this._wreqrHandlers[t];if(e)return function(){var t=Array.prototype.slice.apply(arguments);return e.callback.apply(e.context,t)}},removeHandler:function(t){delete this._wreqrHandlers[t]},removeAllHandlers:function(){this._wreqrHandlers={}}}),g),p.CommandStorage=(o.extend(v.prototype,r.Events,{getCommands:function(t){var e=this._commands[t];return e||(e={command:t,instances:[]},this._commands[t]=e),e},addCommand:function(t,e){this.getCommands(t).instances.push(e)},clearCommands:function(t){this.getCommands(t).instances=[]}}),v),p.Commands=(c=p).Handlers.extend({storageType:c.CommandStorage,constructor:function(t){this.options=t||{},this._initializeStorage(this.options),this.on("handler:add",this._executeCommands,this);var e=Array.prototype.slice.call(arguments);c.Handlers.prototype.constructor.apply(this,e)},execute:function(t,e){t=arguments[0],e=Array.prototype.slice.call(arguments,1),this.hasHandler(t)?this.getHandler(t).apply(this,e):this.storage.addCommand(t,e)},_executeCommands:function(t,e,n){var i=this.storage.getCommands(t);o.each(i.instances,function(t){e.apply(n,t)}),this.storage.clearCommands(t)},_initializeStorage:function(t){var e,n=t.storageType||this.storageType;e=o.isFunction(n)?new n:n,this.storage=e}}),p.RequestResponse=p.Handlers.extend({request:function(){var t=arguments[0],e=Array.prototype.slice.call(arguments,1);if(this.hasHandler(t))return this.getHandler(t).apply(this,e)}}),p.EventAggregator=(d=o,m.extend=(h=r).Model.extend,d.extend(m.prototype,h.Events),m),p.Channel=(o.extend(y.prototype,{reset:function(){return this.vent.off(),this.vent.stopListening(),this.reqres.removeAllHandlers(),this.commands.removeAllHandlers(),this},connectEvents:function(t,e){return this._connect("vent",t,e),this},connectCommands:function(t,e){return this._connect("commands",t,e),this},connectRequests:function(t,e){return this._connect("reqres",t,e),this},_connect:function(n,t,i){if(t){i=i||this;var r="vent"===n?"on":"setHandler";o.each(t,function(t,e){this[n][r](e,o.bind(t,i))},this)}}}),y),p.radio=function(n){function t(){this._channels={},this.vent={},this.commands={},this.reqres={},this._proxyMethods()}o.extend(t.prototype,{channel:function(t){if(!t)throw new Error("Channel must receive a name");return this._getChannel(t)},_getChannel:function(t){var e=this._channels[t];return e||(e=new n.Channel(t),this._channels[t]=e),e},_proxyMethods:function(){o.each(["vent","commands","reqres"],function(e){o.each(i[e],function(t){this[e][t]=r(this,e,t)},this)},this)}});var i={vent:["on","off","trigger","once","stopListening","listenTo","listenToOnce"],commands:["execute","setHandler","setHandlers","removeHandler","removeAllHandlers"],reqres:["request","setHandler","setHandlers","removeHandler","removeAllHandlers"]},r=function(i,r,o){return function(t){var e=i._getChannel(t)[r],n=Array.prototype.slice.call(arguments,1);return e[o].apply(e,n)}};return new t}(p),r.Wreqr;var b=t.Marionette,w=s.Marionette={};w.VERSION="2.3.1",w.noConflict=function(){return t.Marionette=b,this},(s.Marionette=w).Deferred=s.$.Deferred,w.extend=s.Model.extend,w.isNodeAttached=function(t){return s.$.contains(document.documentElement,t)},w.getOption=function(t,e){if(t&&e)return t.options&&void 0!==t.options[e]?t.options[e]:t[e]},w.proxyGetOption=function(t){return w.getOption(this,t)},w._getValue=function(t,e,n){return l.isFunction(t)&&(t=t.apply(e,n)),t},w.normalizeMethods=function(t){return l.reduce(t,function(t,e,n){return l.isFunction(e)||(e=this[e]),e&&(t[n]=e),t},{},this)},w.normalizeUIString=function(t,e){return t.replace(/@ui\.[a-zA-Z_$0-9]*/g,function(t){return e[t.slice(4)]})},w.normalizeUIKeys=function(t,i){return l.reduce(t,function(t,e,n){return t[w.normalizeUIString(n,i)]=e,t},{})},w.normalizeUIValues=function(n,i){return l.each(n,function(t,e){l.isString(t)&&(n[e]=w.normalizeUIString(t,i))}),n},w.actAsCollection=function(t,n){l.each(["forEach","each","map","find","detect","filter","select","reject","every","all","some","any","include","contains","invoke","toArray","first","initial","rest","last","without","isEmpty","pluck"],function(e){t[e]=function(){var t=[l.values(l.result(this,n))].concat(l.toArray(arguments));return l[e].apply(l,t)}})};var _,x,S=w.deprecate=function(t,e){l.isObject(t)&&(t=t.prev+" is going to be removed in the future. Please use "+t.next+" instead."+(t.url?" See: "+t.url:"")),void 0!==e&&e||S._cache[t]||(S._warn("Deprecation warning: "+t),S._cache[t]=!0)};function T(t,e,n){return n.toUpperCase()}function C(n,i,r,t){var e=t.split(/\s+/);l.each(e,function(t){var e=n[t];if(!e)throw new x.Error('Method "'+t+'" was configured as an event handler, but does not exist.');n.listenTo(i,r,e)})}function E(t,e,n,i){t.listenTo(e,n,i)}function k(n,i,r,t){var e=t.split(/\s+/);l.each(e,function(t){var e=n[t];n.stopListening(i,r,e)})}function M(t,e,n,i){t.stopListening(e,n,i)}function D(n,i,t,r,o){if(i&&t){if(!l.isObject(t))throw new x.Error({message:"Bindings must be an object or function.",url:"marionette.functions.html#marionettebindentityevents"});t=x._getValue(t,n),l.each(t,function(t,e){l.isFunction(t)?r(n,i,e,t):o(n,i,e,t)})}}S._warn="undefined"!=typeof console&&(console.warn||console.log)||function(){},S._cache={},w._triggerMethod=(_=/(^|:)(\w)/gi,function(t,e,n){var i=arguments.length<3;i&&(e=(n=e)[0]);var r,o=t["on"+e.replace(_,T)];return l.isFunction(o)&&(r=o.apply(t,i?l.rest(n):n)),l.isFunction(t.trigger)&&(1<i+n.length?t.trigger.apply(t,i?n:[e].concat(l.rest(n,0))):t.trigger(e)),r}),w.triggerMethod=function(t){return w._triggerMethod(this,arguments)},w.triggerMethodOn=function(t){return(l.isFunction(t.triggerMethod)?t.triggerMethod:w.triggerMethod).apply(t,l.rest(arguments))},w.MonitorDOMRefresh=function(t){function e(){t._isShown&&t._isRendered&&w.isNodeAttached(t.el)&&l.isFunction(t.triggerMethod)&&t.triggerMethod("dom:refresh")}t.on({show:function(){t._isShown=!0,e()},render:function(){t._isRendered=!0,e()}})},(x=w).bindEntityEvents=function(t,e,n){D(t,e,n,E,C)},x.unbindEntityEvents=function(t,e,n){D(t,e,n,M,k)},x.proxyBindEntityEvents=function(t,e){return x.bindEntityEvents(this,t,e)},x.proxyUnbindEntityEvents=function(t,e){return x.unbindEntityEvents(this,t,e)};var O,A,N,j,R=["description","fileName","lineNumber","name","message","number"];function L(t,e){return A.isObject(t.behaviors)?(e=L.parseBehaviors(t,e||A.result(t,"behaviors")),L.wrap(t,e,A.keys(j)),e):{}}function I(t,e){this._view=t,this._viewUI=A.result(t,"ui"),this._behaviors=e,this._triggers={}}return w.Error=w.extend.call(Error,{urlRoot:"http://marionettejs.com/docs/v"+w.VERSION+"/",constructor:function(t,e){l.isObject(t)?t=(e=t).message:e=e||{};var n=Error.call(this,t);l.extend(this,l.pick(n,R),l.pick(e,R)),this.captureStackTrace(),e.url&&(this.url=this.urlRoot+e.url)},captureStackTrace:function(){Error.captureStackTrace&&Error.captureStackTrace(this,w.Error)},toString:function(){return this.name+": "+this.message+(this.url?" See: "+this.url:"")}}),w.Error.extend=w.extend,w.Callbacks=function(){this._deferred=w.Deferred(),this._callbacks=[]},l.extend(w.Callbacks.prototype,{add:function(e,n){var t=l.result(this._deferred,"promise");this._callbacks.push({cb:e,ctx:n}),t.then(function(t){n&&(t.context=n),e.call(t.context,t.options)})},run:function(t,e){this._deferred.resolve({options:t,context:e})},reset:function(){var t=this._callbacks;this._deferred=w.Deferred(),this._callbacks=[],l.each(t,function(t){this.add(t.cb,t.ctx)},this)}}),w.Controller=function(t){this.options=t||{},l.isFunction(this.initialize)&&this.initialize(this.options)},w.Controller.extend=w.extend,l.extend(w.Controller.prototype,s.Events,{destroy:function(){return w._triggerMethod(this,"before:destroy",arguments),w._triggerMethod(this,"destroy",arguments),this.stopListening(),this.off(),this},triggerMethod:w.triggerMethod,getOption:w.proxyGetOption}),w.Object=function(t){this.options=l.extend({},l.result(this,"options"),t),this.initialize.apply(this,arguments)},w.Object.extend=w.extend,l.extend(w.Object.prototype,s.Events,{initialize:function(){},destroy:function(){this.triggerMethod("before:destroy"),this.triggerMethod("destroy"),this.stopListening()},triggerMethod:w.triggerMethod,getOption:w.proxyGetOption,bindEntityEvents:w.proxyBindEntityEvents,unbindEntityEvents:w.proxyUnbindEntityEvents}),w.Region=w.Object.extend({constructor:function(t){if(this.options=t||{},this.el=this.getOption("el"),this.el=this.el instanceof s.$?this.el[0]:this.el,!this.el)throw new w.Error({name:"NoElError",message:'An "el" must be specified for a region.'});this.$el=this.getEl(this.el),w.Object.call(this,t)},show:function(t,e){if(this._ensureElement()){this._ensureViewIsIntact(t);var n=e||{},i=t!==this.currentView,r=!!n.preventDestroy,o=!!n.forceShow,s=!!this.currentView,a=i&&!r,l=i||o;if(s&&this.triggerMethod("before:swapOut",this.currentView,this,e),this.currentView&&delete this.currentView._parent,a?this.empty():s&&l&&this.currentView.off("destroy",this.empty,this),l){t.once("destroy",this.empty,this),t.render(),t._parent=this,s&&this.triggerMethod("before:swap",t,this,e),this.triggerMethod("before:show",t,this,e),w.triggerMethodOn(t,"before:show",t,this,e),s&&this.triggerMethod("swapOut",this.currentView,this,e);var u=w.isNodeAttached(this.el),c=[],h=n.triggerBeforeAttach||this.triggerBeforeAttach,d=n.triggerAttach||this.triggerAttach;return u&&h&&(c=this._displayedViews(t),this._triggerAttach(c,"before:")),this.attachHtml(t),this.currentView=t,u&&d&&(c=this._displayedViews(t),this._triggerAttach(c)),s&&this.triggerMethod("swap",t,this,e),this.triggerMethod("show",t,this,e),w.triggerMethodOn(t,"show",t,this,e),this}return this}},triggerBeforeAttach:!0,triggerAttach:!0,_triggerAttach:function(t,e){var n=(e||"")+"attach";l.each(t,function(t){w.triggerMethodOn(t,n,t,this)},this)},_displayedViews:function(t){return l.union([t],l.result(t,"_getNestedViews")||[])},_ensureElement:function(){if(l.isObject(this.el)||(this.$el=this.getEl(this.el),this.el=this.$el[0]),this.$el&&0!==this.$el.length)return!0;if(this.getOption("allowMissingEl"))return!1;throw new w.Error('An "el" '+this.$el.selector+" must exist in DOM")},_ensureViewIsIntact:function(t){if(!t)throw new w.Error({name:"ViewNotValid",message:"The view passed is undefined and therefore invalid. You must pass a view instance to show."});if(t.isDestroyed)throw new w.Error({name:"ViewDestroyedError",message:'View (cid: "'+t.cid+'") has already been destroyed and cannot be used.'})},getEl:function(t){return s.$(t,w._getValue(this.options.parentEl,this))},attachHtml:function(t){this.$el.contents().detach(),this.el.appendChild(t.el)},empty:function(){var t=this.currentView;if(t)return t.off("destroy",this.empty,this),this.triggerMethod("before:empty",t),this._destroyView(),this.triggerMethod("empty",t),delete this.currentView,this},_destroyView:function(){var t=this.currentView;t.destroy&&!t.isDestroyed?t.destroy():t.remove&&(t.remove(),t.isDestroyed=!0)},attachView:function(t){return this.currentView=t,this},hasView:function(){return!!this.currentView},reset:function(){return this.empty(),this.$el&&(this.el=this.$el.selector),delete this.$el,this}},{buildRegion:function(t,e){if(l.isString(t))return this._buildRegionFromSelector(t,e);if(t.selector||t.el||t.regionClass)return this._buildRegionFromObject(t,e);if(l.isFunction(t))return this._buildRegionFromRegionClass(t);throw new w.Error({message:"Improper region configuration type.",url:"marionette.region.html#region-configuration-types"})},_buildRegionFromSelector:function(t,e){return new e({el:t})},_buildRegionFromObject:function(t,e){var n=t.regionClass||e,i=l.omit(t,"selector","regionClass");return t.selector&&!i.el&&(i.el=t.selector),new n(i)},_buildRegionFromRegionClass:function(t){return new t}}),w.RegionManager=w.Controller.extend({constructor:function(t){this._regions={},w.Controller.call(this,t),this.addRegions(this.getOption("regions"))},addRegions:function(t,i){return t=w._getValue(t,this,arguments),l.reduce(t,function(t,e,n){return l.isString(e)&&(e={selector:e}),e.selector&&(e=l.defaults({},e,i)),t[n]=this.addRegion(n,e),t},{},this)},addRegion:function(t,e){var n;return n=e instanceof w.Region?e:w.Region.buildRegion(e,w.Region),this.triggerMethod("before:add:region",t,n),(n._parent=this)._store(t,n),this.triggerMethod("add:region",t,n),n},get:function(t){return this._regions[t]},getRegions:function(){return l.clone(this._regions)},removeRegion:function(t){var e=this._regions[t];return this._remove(t,e),e},removeRegions:function(){var t=this.getRegions();return l.each(this._regions,function(t,e){this._remove(e,t)},this),t},emptyRegions:function(){var t=this.getRegions();return l.invoke(t,"empty"),t},destroy:function(){return this.removeRegions(),w.Controller.prototype.destroy.apply(this,arguments)},_store:function(t,e){this._regions[t]=e,this._setLength()},_remove:function(t,e){this.triggerMethod("before:remove:region",t,e),e.empty(),e.stopListening(),delete e._parent,delete this._regions[t],this._setLength(),this.triggerMethod("remove:region",t,e)},_setLength:function(){this.length=l.size(this._regions)}}),w.actAsCollection(w.RegionManager.prototype,"_regions"),w.TemplateCache=function(t){this.templateId=t},l.extend(w.TemplateCache,{templateCaches:{},get:function(t){var e=this.templateCaches[t];return e||(e=new w.TemplateCache(t),this.templateCaches[t]=e),e.load()},clear:function(){var t,e=l.toArray(arguments),n=e.length;if(0<n)for(t=0;t<n;t++)delete this.templateCaches[e[t]];else this.templateCaches={}}}),l.extend(w.TemplateCache.prototype,{load:function(){if(this.compiledTemplate)return this.compiledTemplate;var t=this.loadTemplate(this.templateId);return this.compiledTemplate=this.compileTemplate(t),this.compiledTemplate},loadTemplate:function(t){var e=s.$(t).html();if(!e||0===e.length)throw new w.Error({name:"NoTemplateError",message:'Could not find template: "'+t+'"'});return e},compileTemplate:function(t){return l.template(t)}}),w.Renderer={render:function(t,e){if(!t)throw new w.Error({name:"TemplateNotFoundError",message:"Cannot render the template since its false, null or undefined."});return(l.isFunction(t)?t:w.TemplateCache.get(t))(e)}},w.View=s.View.extend({isDestroyed:!1,constructor:function(t){l.bindAll(this,"render"),t=w._getValue(t,this),this.options=l.extend({},l.result(this,"options"),t),this._behaviors=w.Behaviors(this),s.View.apply(this,arguments),w.MonitorDOMRefresh(this),this.on("show",this.onShowCalled)},getTemplate:function(){return this.getOption("template")},serializeModel:function(t){return t.toJSON.apply(t,l.rest(arguments))},mixinTemplateHelpers:function(t){t=t||{};var e=this.getOption("templateHelpers");return e=w._getValue(e,this),l.extend(t,e)},normalizeUIKeys:function(t){var e=l.result(this,"_uiBindings");return w.normalizeUIKeys(t,e||l.result(this,"ui"))},normalizeUIValues:function(t){var e=l.result(this,"ui"),n=l.result(this,"_uiBindings");return w.normalizeUIValues(t,n||e)},configureTriggers:function(){if(this.triggers){var t=this.normalizeUIKeys(l.result(this,"triggers"));return l.reduce(t,function(t,e,n){return t[n]=this._buildViewTrigger(e),t},{},this)}},delegateEvents:function(t){return this._delegateDOMEvents(t),this.bindEntityEvents(this.model,this.getOption("modelEvents")),this.bindEntityEvents(this.collection,this.getOption("collectionEvents")),l.each(this._behaviors,function(t){t.bindEntityEvents(this.model,t.getOption("modelEvents")),t.bindEntityEvents(this.collection,t.getOption("collectionEvents"))},this),this},_delegateDOMEvents:function(t){var e=w._getValue(t||this.events,this);e=this.normalizeUIKeys(e),l.isUndefined(t)&&(this.events=e);var n={},i=l.result(this,"behaviorEvents")||{},r=this.configureTriggers(),o=l.result(this,"behaviorTriggers")||{};l.extend(n,i,e,r,o),s.View.prototype.delegateEvents.call(this,n)},undelegateEvents:function(){return s.View.prototype.undelegateEvents.apply(this,arguments),this.unbindEntityEvents(this.model,this.getOption("modelEvents")),this.unbindEntityEvents(this.collection,this.getOption("collectionEvents")),l.each(this._behaviors,function(t){t.unbindEntityEvents(this.model,t.getOption("modelEvents")),t.unbindEntityEvents(this.collection,t.getOption("collectionEvents"))},this),this},onShowCalled:function(){},_ensureViewIsIntact:function(){if(this.isDestroyed)throw new w.Error({name:"ViewDestroyedError",message:'View (cid: "'+this.cid+'") has already been destroyed and cannot be used.'})},destroy:function(){if(!this.isDestroyed){var t=l.toArray(arguments);return this.triggerMethod.apply(this,["before:destroy"].concat(t)),this.isDestroyed=!0,this.triggerMethod.apply(this,["destroy"].concat(t)),this.unbindUIElements(),this.remove(),l.invoke(this._behaviors,"destroy",t),this}},bindUIElements:function(){this._bindUIElements(),l.invoke(this._behaviors,this._bindUIElements)},_bindUIElements:function(){if(this.ui){this._uiBindings||(this._uiBindings=this.ui);var t=l.result(this,"_uiBindings");this.ui={},l.each(t,function(t,e){this.ui[e]=this.$(t)},this)}},unbindUIElements:function(){this._unbindUIElements(),l.invoke(this._behaviors,this._unbindUIElements)},_unbindUIElements:function(){this.ui&&this._uiBindings&&(l.each(this.ui,function(t,e){delete this.ui[e]},this),this.ui=this._uiBindings,delete this._uiBindings)},_buildViewTrigger:function(t){var e=l.isObject(t),n=l.defaults({},e?t:{},{preventDefault:!0,stopPropagation:!0}),i=e?n.event:t;return function(t){t&&(t.preventDefault&&n.preventDefault&&t.preventDefault(),t.stopPropagation&&n.stopPropagation&&t.stopPropagation());var e={view:this,model:this.model,collection:this.collection};this.triggerMethod(i,e)}},setElement:function(){var t=s.View.prototype.setElement.apply(this,arguments);return l.invoke(this._behaviors,"proxyViewProperties",this),t},triggerMethod:function(){for(var t=w._triggerMethod,e=t(this,arguments),n=this._behaviors,i=0,r=n&&n.length;i<r;i++)t(n[i],arguments);return e},_getImmediateChildren:function(){return[]},_getNestedViews:function(){var t=this._getImmediateChildren();return t.length?l.reduce(t,function(t,e){return e._getNestedViews?t.concat(e._getNestedViews()):t},t):t},normalizeMethods:w.normalizeMethods,getOption:w.proxyGetOption,bindEntityEvents:w.proxyBindEntityEvents,unbindEntityEvents:w.proxyUnbindEntityEvents}),w.ItemView=w.View.extend({constructor:function(){w.View.apply(this,arguments)},serializeData:function(){if(!this.model&&!this.collection)return{};var t=[this.model||this.collection];return arguments.length&&t.push.apply(t,arguments),this.model?this.serializeModel.apply(this,t):{items:this.serializeCollection.apply(this,t)}},serializeCollection:function(t){return t.toJSON.apply(t,l.rest(arguments))},render:function(){return this._ensureViewIsIntact(),this.triggerMethod("before:render",this),this._renderTemplate(),this.bindUIElements(),this.triggerMethod("render",this),this},_renderTemplate:function(){var t=this.getTemplate();if(!1!==t){if(!t)throw new w.Error({name:"UndefinedTemplateError",message:"Cannot render the template since it is null or undefined."});var e=this.serializeData();e=this.mixinTemplateHelpers(e);var n=w.Renderer.render(t,e,this);return this.attachElContent(n),this}},attachElContent:function(t){return this.$el.html(t),this}}),w.CollectionView=w.View.extend({childViewEventPrefix:"childview",constructor:function(t){var e=t||{};l.isUndefined(this.sort)&&(this.sort=!!l.isUndefined(e.sort)||e.sort),this.once("render",this._initialEvents),this._initChildViewStorage(),w.View.apply(this,arguments),this.initRenderBuffer()},initRenderBuffer:function(){this.elBuffer=document.createDocumentFragment(),this._bufferedChildren=[]},startBuffering:function(){this.initRenderBuffer(),this.isBuffering=!0},endBuffering:function(){this.isBuffering=!1,this._triggerBeforeShowBufferedChildren(),this.attachBuffer(this,this.elBuffer),this._triggerShowBufferedChildren(),this.initRenderBuffer()},_triggerBeforeShowBufferedChildren:function(){this._isShown&&l.each(this._bufferedChildren,l.partial(this._triggerMethodOnChild,"before:show"))},_triggerShowBufferedChildren:function(){this._isShown&&(l.each(this._bufferedChildren,l.partial(this._triggerMethodOnChild,"show")),this._bufferedChildren=[])},_triggerMethodOnChild:function(t,e){w.triggerMethodOn(e,t)},_initialEvents:function(){this.collection&&(this.listenTo(this.collection,"add",this._onCollectionAdd),this.listenTo(this.collection,"remove",this._onCollectionRemove),this.listenTo(this.collection,"reset",this.render),this.sort&&this.listenTo(this.collection,"sort",this._sortViews))},_onCollectionAdd:function(t){this.destroyEmptyView();var e=this.getChildView(t),n=this.collection.indexOf(t);this.addChild(t,e,n)},_onCollectionRemove:function(t){var e=this.children.findByModel(t);this.removeChildView(e),this.checkEmpty()},onShowCalled:function(){this.children.each(l.partial(this._triggerMethodOnChild,"show"))},render:function(){return this._ensureViewIsIntact(),this.triggerMethod("before:render",this),this._renderChildren(),this.triggerMethod("render",this),this},resortView:function(){this.render()},_sortViews:function(){this.collection.find(function(t,e){var n=this.children.findByModel(t);return!n||n._index!==e},this)&&this.resortView()},_emptyViewIndex:-1,_renderChildren:function(){this.destroyEmptyView(),this.destroyChildren(),this.isEmpty(this.collection)?this.showEmptyView():(this.triggerMethod("before:render:collection",this),this.startBuffering(),this.showCollection(),this.endBuffering(),this.triggerMethod("render:collection",this))},showCollection:function(){var n;this.collection.each(function(t,e){n=this.getChildView(t),this.addChild(t,n,e)},this)},showEmptyView:function(){var t=this.getEmptyView();if(t&&!this._showingEmptyView){this.triggerMethod("before:render:empty"),this._showingEmptyView=!0;var e=new s.Model;this.addEmptyView(e,t),this.triggerMethod("render:empty")}},destroyEmptyView:function(){this._showingEmptyView&&(this.triggerMethod("before:remove:empty"),this.destroyChildren(),delete this._showingEmptyView,this.triggerMethod("remove:empty"))},getEmptyView:function(){return this.getOption("emptyView")},addEmptyView:function(t,e){var n=this.getOption("emptyViewOptions")||this.getOption("childViewOptions");l.isFunction(n)&&(n=n.call(this,t,this._emptyViewIndex));var i=this.buildChildView(t,e,n);(i._parent=this).proxyChildEvents(i),this._isShown&&w.triggerMethodOn(i,"before:show"),this.children.add(i),this.renderChildView(i,this._emptyViewIndex),this._isShown&&w.triggerMethodOn(i,"show")},getChildView:function(t){var e=this.getOption("childView");if(!e)throw new w.Error({name:"NoChildViewError",message:'A "childView" must be specified'});return e},addChild:function(t,e,n){var i=this.getOption("childViewOptions");i=w._getValue(i,this,[t,n]);var r=this.buildChildView(t,e,i);return this._updateIndices(r,!0,n),this._addChildView(r,n),r._parent=this,r},_updateIndices:function(e,n,t){this.sort&&(n&&(e._index=t),this.children.each(function(t){t._index>=e._index&&(t._index+=n?1:-1)}))},_addChildView:function(t,e){this.proxyChildEvents(t),this.triggerMethod("before:add:child",t),this.children.add(t),this.renderChildView(t,e),this._isShown&&!this.isBuffering&&w.triggerMethodOn(t,"show"),this.triggerMethod("add:child",t)},renderChildView:function(t,e){return t.render(),this.attachHtml(this,t,e),t},buildChildView:function(t,e,n){return new e(l.extend({model:t},n))},removeChildView:function(t){return t&&(this.triggerMethod("before:remove:child",t),t.destroy?t.destroy():t.remove&&t.remove(),delete t._parent,this.stopListening(t),this.children.remove(t),this.triggerMethod("remove:child",t),this._updateIndices(t,!1)),t},isEmpty:function(){return!this.collection||0===this.collection.length},checkEmpty:function(){this.isEmpty(this.collection)&&this.showEmptyView()},attachBuffer:function(t,e){t.$el.append(e)},attachHtml:function(t,e,n){t.isBuffering?(t.elBuffer.appendChild(e.el),t._bufferedChildren.push(e)):t._insertBefore(e,n)||t._insertAfter(e)},_insertBefore:function(t,e){var n;return this.sort&&e<this.children.length-1&&(n=this.children.find(function(t){return t._index===e+1})),!!n&&(n.$el.before(t.el),!0)},_insertAfter:function(t){this.$el.append(t.el)},_initChildViewStorage:function(){this.children=new s.ChildViewContainer},destroy:function(){if(!this.isDestroyed)return this.triggerMethod("before:destroy:collection"),this.destroyChildren(),this.triggerMethod("destroy:collection"),w.View.prototype.destroy.apply(this,arguments)},destroyChildren:function(){var t=this.children.map(l.identity);return this.children.each(this.removeChildView,this),this.checkEmpty(),t},proxyChildEvents:function(i){var r=this.getOption("childViewEventPrefix");this.listenTo(i,"all",function(){var t=l.toArray(arguments),e=t[0],n=this.normalizeMethods(l.result(this,"childEvents"));t[0]=r+":"+e,t.splice(1,0,i),void 0!==n&&l.isFunction(n[e])&&n[e].apply(this,t.slice(1)),this.triggerMethod.apply(this,t)},this)},_getImmediateChildren:function(){return l.values(this.children._views)}}),w.CompositeView=w.CollectionView.extend({constructor:function(){w.CollectionView.apply(this,arguments)},_initialEvents:function(){this.collection&&(this.listenTo(this.collection,"add",this._onCollectionAdd),this.listenTo(this.collection,"remove",this._onCollectionRemove),this.listenTo(this.collection,"reset",this._renderChildren),this.sort&&this.listenTo(this.collection,"sort",this._sortViews))},getChildView:function(t){return this.getOption("childView")||this.constructor},serializeData:function(){var t={};return this.model&&(t=l.partial(this.serializeModel,this.model).apply(this,arguments)),t},render:function(){return this._ensureViewIsIntact(),this.isRendered=!0,this.resetChildViewContainer(),this.triggerMethod("before:render",this),this._renderTemplate(),this._renderChildren(),this.triggerMethod("render",this),this},_renderChildren:function(){this.isRendered&&w.CollectionView.prototype._renderChildren.call(this)},_renderTemplate:function(){var t={};t=this.serializeData(),t=this.mixinTemplateHelpers(t),this.triggerMethod("before:render:template");var e=this.getTemplate(),n=w.Renderer.render(e,t,this);this.attachElContent(n),this.bindUIElements(),this.triggerMethod("render:template")},attachElContent:function(t){return this.$el.html(t),this},attachBuffer:function(t,e){this.getChildViewContainer(t).append(e)},_insertAfter:function(t){this.getChildViewContainer(this,t).append(t.el)},getChildViewContainer:function(t,e){if("$childViewContainer"in t)return t.$childViewContainer;var n,i=w.getOption(t,"childViewContainer");if(i){var r=w._getValue(i,t);if((n="@"===r.charAt(0)&&t.ui?t.ui[r.substr(4)]:t.$(r)).length<=0)throw new w.Error({name:"ChildViewContainerMissingError",message:'The specified "childViewContainer" was not found: '+t.childViewContainer})}else n=t.$el;return t.$childViewContainer=n},resetChildViewContainer:function(){this.$childViewContainer&&delete this.$childViewContainer}}),w.LayoutView=w.ItemView.extend({regionClass:w.Region,constructor:function(t){t=t||{},this._firstRender=!0,this._initializeRegions(t),w.ItemView.call(this,t)},render:function(){return this._ensureViewIsIntact(),this._firstRender?this._firstRender=!1:this._reInitializeRegions(),w.ItemView.prototype.render.apply(this,arguments)},destroy:function(){return this.isDestroyed?this:(this.regionManager.destroy(),w.ItemView.prototype.destroy.apply(this,arguments))},addRegion:function(t,e){var n={};return n[t]=e,this._buildRegions(n)[t]},addRegions:function(t){return this.regions=l.extend({},this.regions,t),this._buildRegions(t)},removeRegion:function(t){return delete this.regions[t],this.regionManager.removeRegion(t)},getRegion:function(t){return this.regionManager.get(t)},getRegions:function(){return this.regionManager.getRegions()},_buildRegions:function(t){var e={regionClass:this.getOption("regionClass"),parentEl:l.partial(l.result,this,"el")};return this.regionManager.addRegions(t,e)},_initializeRegions:function(t){var e;this._initRegionManager(),e=w._getValue(this.regions,this,[t])||{};var n=this.getOption.call(t,"regions");n=w._getValue(n,this,[t]),l.extend(e,n),e=this.normalizeUIValues(e),this.addRegions(e)},_reInitializeRegions:function(){this.regionManager.invoke("reset")},getRegionManager:function(){return new w.RegionManager},_initRegionManager:function(){this.regionManager=this.getRegionManager(),(this.regionManager._parent=this).listenTo(this.regionManager,"before:add:region",function(t){this.triggerMethod("before:add:region",t)}),this.listenTo(this.regionManager,"add:region",function(t,e){this[t]=e,this.triggerMethod("add:region",t,e)}),this.listenTo(this.regionManager,"before:remove:region",function(t){this.triggerMethod("before:remove:region",t)}),this.listenTo(this.regionManager,"remove:region",function(t,e){delete this[t],this.triggerMethod("remove:region",t,e)})},_getImmediateChildren:function(){return l.chain(this.regionManager.getRegions()).pluck("currentView").compact().value()}}),w.Behavior=w.Object.extend({constructor:function(t,e){this.view=e,this.defaults=l.result(this,"defaults")||{},this.options=l.extend({},this.defaults,t),w.Object.apply(this,arguments)},$:function(){return this.view.$.apply(this.view,arguments)},destroy:function(){this.stopListening()},proxyViewProperties:function(t){this.$el=t.$el,this.el=t.el}}),w.Behaviors=(O=w,N=/^(\S+)\s*(.*)$/,j={behaviorTriggers:function(t,e){return new I(this,e).buildBehaviorTriggers()},behaviorEvents:function(t,e){var i={},r=this._uiBindings||A.result(this,"ui");return A.each(e,function(o,s){var a={},t=A.clone(A.result(o,"events"))||{},e=o._uiBindings||A.result(o,"ui"),n=A.extend({},r,e);t=O.normalizeUIKeys(t,n);var l=0;A.each(t,function(t,e){var n=e.match(N),i=n[1]+"."+[this.cid,s,l++," "].join("")+n[2],r=A.isFunction(t)?t:o[t];a[i]=A.bind(r,o)},this),i=A.extend(i,a)},this),i}},(A=l).extend(L,{behaviorsLookup:function(){throw new O.Error({message:"You must define where your behaviors are stored.",url:"marionette.behaviors.html#behaviorslookup"})},getBehaviorClass:function(t,e){return t.behaviorClass?t.behaviorClass:O._getValue(L.behaviorsLookup,this,[t,e])[e]},parseBehaviors:function(r,t){return A.chain(t).map(function(t,e){var n=new(L.getBehaviorClass(t,e))(t,r),i=L.parseBehaviors(r,A.result(n,"behaviors"));return[n].concat(i)}).flatten().value()},wrap:function(e,n,t){A.each(t,function(t){e[t]=A.partial(j[t],e[t],n)})}}),A.extend(I.prototype,{buildBehaviorTriggers:function(){return A.each(this._behaviors,this._buildTriggerHandlersForBehavior,this),this._triggers},_buildTriggerHandlersForBehavior:function(t,e){var n=A.extend({},this._viewUI,A.result(t,"ui")),i=A.clone(A.result(t,"triggers"))||{};i=O.normalizeUIKeys(i,n),A.each(i,A.bind(this._setHandlerForBehavior,this,t,e))},_setHandlerForBehavior:function(t,e,n,i){var r=i.replace(/^\S+/,function(t){return t+".behaviortriggers"+e});this._triggers[r]=this._view._buildViewTrigger(n)}}),L),w.AppRouter=s.Router.extend({constructor:function(t){this.options=t||{},s.Router.apply(this,arguments);var e=this.getOption("appRoutes"),n=this._getController();this.processAppRoutes(n,e),this.on("route",this._processOnRoute,this)},appRoute:function(t,e){var n=this._getController();this._addAppRoute(n,t,e)},_processOnRoute:function(t,e){if(l.isFunction(this.onRoute)){var n=l.invert(this.getOption("appRoutes"))[t];this.onRoute(t,n,e)}},processAppRoutes:function(e,n){if(n){var t=l.keys(n).reverse();l.each(t,function(t){this._addAppRoute(e,t,n[t])},this)}},_getController:function(){return this.getOption("controller")},_addAppRoute:function(t,e,n){var i=t[n];if(!i)throw new w.Error('Method "'+n+'" was not found on the controller');this.route(e,n,l.bind(i,t))},getOption:w.proxyGetOption,triggerMethod:w.triggerMethod,bindEntityEvents:w.proxyBindEntityEvents,unbindEntityEvents:w.proxyUnbindEntityEvents}),w.Application=w.Object.extend({constructor:function(t){this._initializeRegions(t),this._initCallbacks=new w.Callbacks,this.submodules={},l.extend(this,t),this._initChannel(),w.Object.call(this,t)},execute:function(){this.commands.execute.apply(this.commands,arguments)},request:function(){return this.reqres.request.apply(this.reqres,arguments)},addInitializer:function(t){this._initCallbacks.add(t)},start:function(t){this.triggerMethod("before:start",t),this._initCallbacks.run(t,this),this.triggerMethod("start",t)},addRegions:function(t){return this._regionManager.addRegions(t)},emptyRegions:function(){return this._regionManager.emptyRegions()},removeRegion:function(t){return this._regionManager.removeRegion(t)},getRegion:function(t){return this._regionManager.get(t)},getRegions:function(){return this._regionManager.getRegions()},module:function(t,e){var n=w.Module.getClass(e),i=l.toArray(arguments);return i.unshift(this),n.create.apply(n,i)},getRegionManager:function(){return new w.RegionManager},_initializeRegions:function(t){var e=l.isFunction(this.regions)?this.regions(t):this.regions||{};this._initRegionManager();var n=w.getOption(t,"regions");return l.isFunction(n)&&(n=n.call(this,t)),l.extend(e,n),this.addRegions(e),this},_initRegionManager:function(){this._regionManager=this.getRegionManager(),(this._regionManager._parent=this).listenTo(this._regionManager,"before:add:region",function(){w._triggerMethod(this,"before:add:region",arguments)}),this.listenTo(this._regionManager,"add:region",function(t,e){this[t]=e,w._triggerMethod(this,"add:region",arguments)}),this.listenTo(this._regionManager,"before:remove:region",function(){w._triggerMethod(this,"before:remove:region",arguments)}),this.listenTo(this._regionManager,"remove:region",function(t){delete this[t],w._triggerMethod(this,"remove:region",arguments)})},_initChannel:function(){this.channelName=l.result(this,"channelName")||"global",this.channel=l.result(this,"channel")||s.Wreqr.radio.channel(this.channelName),this.vent=l.result(this,"vent")||this.channel.vent,this.commands=l.result(this,"commands")||this.channel.commands,this.reqres=l.result(this,"reqres")||this.channel.reqres}}),w.Module=function(t,e,n){this.moduleName=t,this.options=l.extend({},this.options,n),this.initialize=n.initialize||this.initialize,this.submodules={},this._setupInitializersAndFinalizers(),this.app=e,l.isFunction(this.initialize)&&this.initialize(t,e,this.options)},w.Module.extend=w.extend,l.extend(w.Module.prototype,s.Events,{startWithParent:!0,initialize:function(){},addInitializer:function(t){this._initializerCallbacks.add(t)},addFinalizer:function(t){this._finalizerCallbacks.add(t)},start:function(e){this._isInitialized||(l.each(this.submodules,function(t){t.startWithParent&&t.start(e)}),this.triggerMethod("before:start",e),this._initializerCallbacks.run(e,this),this._isInitialized=!0,this.triggerMethod("start",e))},stop:function(){this._isInitialized&&(this._isInitialized=!1,this.triggerMethod("before:stop"),l.invoke(this.submodules,"stop"),this._finalizerCallbacks.run(void 0,this),this._initializerCallbacks.reset(),this._finalizerCallbacks.reset(),this.triggerMethod("stop"))},addDefinition:function(t,e){this._runModuleDefinition(t,e)},_runModuleDefinition:function(t,e){if(t){var n=l.flatten([this,this.app,s,w,s.$,l,e]);t.apply(this,n)}},_setupInitializersAndFinalizers:function(){this._initializerCallbacks=new w.Callbacks,this._finalizerCallbacks=new w.Callbacks},triggerMethod:w.triggerMethod}),l.extend(w.Module,{create:function(i,t,r){var o=i,s=l.rest(arguments,3),e=(t=t.split(".")).length,a=[];return a[e-1]=r,l.each(t,function(t,e){var n=o;o=this._getModule(n,t,i,r),this._addModuleDefinition(n,o,a[e],s)},this),o},_getModule:function(t,e,n,i,r){var o=l.extend({},i),s=this.getClass(i),a=t[e];return a||(a=new s(e,n,o),t[e]=a,t.submodules[e]=a),a},getClass:function(t){var e=w.Module;return t?t.prototype instanceof e?t:t.moduleClass||e:e},_addModuleDefinition:function(t,e,n,i){var r=this._getDefine(n),o=this._getStartWithParent(n,e);r&&e.addDefinition(r,i),this._addStartWithParent(t,e,o)},_getStartWithParent:function(t,e){var n;return l.isFunction(t)&&t.prototype instanceof w.Module?(n=e.constructor.prototype.startWithParent,!!l.isUndefined(n)||n):!l.isObject(t)||(n=t.startWithParent,!!l.isUndefined(n)||n)},_getDefine:function(t){return!l.isFunction(t)||t.prototype instanceof w.Module?l.isObject(t)?t.define:null:t},_addStartWithParent:function(t,e,n){e.startWithParent=e.startWithParent&&n,e.startWithParent&&!e.startWithParentIsConfigured&&(e.startWithParentIsConfigured=!0,t.addInitializer(function(t){e.startWithParent&&e.start(t)}))}}),w}),function(t){"function"==typeof define&&define.amd?define(["backbone","underscore"],t):"object"==typeof exports?module.exports=t(require("backbone"),require("underscore")):t(window.Backbone,window._)}(function(t,r){function o(){}var s=t.Router.prototype.route;r.extend(t.Router.prototype,{before:o,after:o,route:function(e,t,n){n=n||this[t];var i=r.bind(function(){var t=[e,r.toArray(arguments)];!1!==(r.isFunction(this.before)?this.before:void 0!==this.before[e]?this.before[e]:o).apply(this,t)&&(n&&n.apply(this,arguments),(r.isFunction(this.after)?this.after:void 0!==this.after[e]?this.after[e]:o).apply(this,t))},this);return s.call(this,e,t,i)}})}),function(i,t,e,n,r,o){"use strict";"object"==typeof n?r.exports=o(e("underscore"),e("backbone"),e("jquery")):"function"==typeof t&&t.amd?t(["underscore","backbone","jquery"],function(t,e,n){return t=void 0===t?i._:t,e=void 0===e?i.Backbone:e,n=void 0===n?i.$:n,i.Backbone=o(t,e,n)}):i.returnExportsGlobal=o(i._,i.Backbone,i.$)}(this,this.define,this.require,this.exports,this.module,function(v,m,n,y){"use strict";function t(t){this.options=t!==y?t:{},this.namespaceDelimiter=t!==y&&t.namespaceDelimiter!==y?t.namespaceDelimiter:this.namespaceDelimiter,this.contentType=t!==y&&t.contentType!==y?t.contentType:this.contentType,v.bindAll(this)}var o,s,a,e=m.Model.prototype.constructor,i=m.sync,b={};return t.prototype={options:{},charset:"iso-8859-1",namespace:"",namespaceDelimiter:"/",contentType:"application/json",url:null,responseID:null,exceptions:{404:{code:-1,message:"404"},500:{code:-2,message:"500"},typeMissmatch:{code:-3,message:"Type missmatch"},badResponseId:{code:-4,message:"Bad response ID"},noResponse:{code:-5,message:"No response"},noDefError:{code:-6,message:"No error defined"},renderError:function(t,e){return{code:e!==y?-7:e,message:t?"No error defined":t}}},onSuccess:function(t,e,n){if(!0===v.isFunction(t)){if(null===n||n===y)return this.handleExceptions(this.exceptions.noResponse),this;null!==n&&e!==String(n.id)&&this.handleExceptions(this.exceptions.badResponseId),t.apply(this,[n.result,n.error])}else this.onError(n)},onError:function(t,e){if(null===e||e===y)return this.handleExceptions(this.exceptions.noResponse),this;null!==e.error&&y!==e.error?this.handleExceptions(e.error):this.handleExceptions(this.exceptions.noDefError)},query:function(t,e,i){var r=String((new Date).getTime());return this.responseID=r,(v.isArray(e)||v.isObject(e))&&v.isString(t)?n.ajax({contentType:this.contentType+"; charset="+this.charset,type:"POST",dataType:"json",url:this.url,data:JSON.stringify({jsonrpc:"2.0",method:this.namespace+this.namespaceDelimiter+t,id:r,params:e}),statusCode:{404:v.bind(function(){this.handleExceptions(this.exceptions[404])},this),500:v.bind(function(){this.handleExceptions(this.exceptions[500])},this)},success:v.bind(function(t,e,n){null!==t&&t.error!==y?this.onError(i,t,e,n):this.onSuccess(i,r,t,e,n)},this),error:v.bind(function(t,e,n){404!==t.status&&500!==t.status&&this.onError(i,t,e,n)},this)}):this.handleExceptions(this.exceptions.typeMissmatch)},checkMethods:function(n,t,i,e,r,o,s){var a=this,l=this.options&&!0===this.options.useNamedParameters,u=null,c=!1,h=null,d=[],f={},p=null,g=i;return(l=i.options&&i.options.useNamedParameters?i.options.useNamedParameters:l)&&(d={},v.isFunction(i.args)&&(g=i.args())),e="delete"===e?"remove":e,v.isArray(i.methods[e])||v.isFunction(i.methods[e])?(u=v.isFunction(i.methods[e])?(v.isString(b[i.get("_rpcId")])||v.each(b[i.get("_rpcId")],function(t,e){i.get(e)!==t&&(f[e]=!0)}),b[i.get("_rpcId")]=i.toJSON(),v.bind(i.methods[e],i)(f,r)):i.methods[e],v.isArray(u[0])&&(c=!0),!0!==c?(p=v.clone(u),h=p.shift(),0<p.length?v.each(p,function(t){""===t?v.isArray(d)&&d.push(""):i instanceof m.Collection?g!==y&&g[t]!==y?d=a.addParam(d,g,t,v.isFunction(g[t]),!1):r[t]!==y&&(d=a.addParam(d,r,t,!1,!1)):i.get(t)!==y?d=a.addParam(d,i,t,!1,!0):r[t]!==y&&(d=a.addParam(d,r,t,!1,!1))}):d=v.isArray(d)?[]:{},n(h,d,o,s)):(v.each(u,function(t){var e=v.clone(t);return h=null,d=[],h=e.shift(),v.each(e,function(t){d.push(i.get(t))}),n(h,d,o,s)}),null)):this.handleExceptions(this.exceptions.typeMissmatch)},addParam:function(t,e,n,i,r){var o;return o=r?e.get(n):i?e[n]():e[n],v.isArray(t)?t.push(o):o!==y&&(t[n]=o),t},invoke:function(n,e,i){var t={success:function(t){e.trigger("called:"+n,e,t),i!==y&&v.isFunction(i.success)&&i.success(e,t)},error:function(t,e){t.trigger("error",t,e),t.trigger("error:"+n,t,e),i!==y&&v.isFunction(i.error)&&i.error(t,e)}};return m.sync(n,e,t),this},defaultExceptionHandler:function(t){throw"Error code: "+t.code+" - message: "+t.message},handleExceptions:function(t){return(v.isFunction(this.options.exceptionHandler)?this.options.exceptionHandler:this.defaultExceptionHandler).call(this,t),this}},m.Rpc=t,m.Model=m.Model.extend({constructor:function(t){this.rpc!==y&&!0===v.isFunction(this.rpc.invoke)&&this.methods!==y&&v.each(this.methods,v.bind(function(t,e){1!=={read:1,create:1,remove:1,update:1}[e]&&(this[e]=v.bind(function(t){return this.rpc.invoke(e,this,t),this},this))},this)),e.apply(this,arguments)}}),m.sync=(o=t,s=null,(a=function(e,i,r){if(i.rpc instanceof o){if((s=i.rpc).url=v.isFunction(i.url)?i.url():i.url,!0===v.isString(i.namespace)&&(s.namespace=i.namespace),i.methods===y)throw"Backbone.Rpc Error: No Method(s) given!";return"object"!=typeof i.params&&(i.params={}),s.checkMethods(s.query,i.params,i,e,r,function(n,t){if(null!==t&&t!==y)return r.error(i,t),this;i instanceof m.Collection&&n!==y&&null!==n&&("object"==typeof n[0]?v.each(n,function(t,e){t._rpcId=v.uniqueId("rpc_"),n[e]=t,b[t._rpcId]=t}):v.each(n,function(t,e){b[e]=t})),i instanceof m.Model&&n!==y&&null!==n&&(n._rpcId=v.uniqueId("rpc_"),b[n._rpcId]=n),n!==y&&null!==n||(n=[]),i.parsers!==y&&i.parsers[e]!==y&&v.isFunction(i.parsers[e])&&i.parsers[e].apply(i,[n]),r.success(n)},function(t){r.error(i,t)})}return a.previous.apply(i,arguments)}).previous=i,a),m}),function(t,i){if("function"==typeof define&&define.amd)define(["underscore","backbone","jquery"],function(t,e,n){return i(t,e,n)});else if("undefined"!=typeof exports){var e=require("underscore"),n=require("backbone"),r=require("jquery");module.exports=i(e,n,r)}else i(t._,t.Backbone,t.jQuery)}(this,function(l,t,u){"use strict";var e=t.Syphon,n=t.Syphon={};n.VERSION="0.5.0",n.noConflict=function(){return t.Syphon=e,this},n.ignoredTypes=["button","submit","reset","fieldset"],n.serialize=function(t,e){var s={},a=d(e),n=c(t,a);return l.each(n,function(t){var e=u(t),n=h(e),i=a.keyExtractors.get(n)(e),r=a.inputReaders.get(n)(e);if(a.keyAssignmentValidators.get(n)(e,i,r)){var o=a.keySplitter(i);s=f(s,o,r)}}),s},n.deserialize=function(t,e,n){var r=d(n),i=c(t,r),o=s(r,e);l.each(i,function(t){var e=u(t),n=h(e),i=r.keyExtractors.get(n)(e);r.inputWriters.get(n)(e,o[i])})};var c=function(t,s){var e=i(t).elements;return e=l.reject(e,function(t){var e=h(t),n=s.keyExtractors.get(e)(u(t)),i=l.include(s.ignoredTypes,e),r=l.include(s.include,n),o=l.include(s.exclude,n);return!r&&(!!s.include||(o||i))})},h=function(t){var e=u(t),n=e[0].tagName,i=n;return"input"===n.toLowerCase()&&(i=e.attr("type")||"text"),i.toLowerCase()},i=function(t){return l.isUndefined(t.$el)&&"form"===t.tagName.toLowerCase()?t:t.$el.is("form")?t.el:t.$("form")[0]},d=function(t){var e=l.clone(t)||{};return e.ignoredTypes=l.clone(n.ignoredTypes),e.inputReaders=e.inputReaders||n.InputReaders,e.inputWriters=e.inputWriters||n.InputWriters,e.keyExtractors=e.keyExtractors||n.KeyExtractors,e.keySplitter=e.keySplitter||n.KeySplitter,e.keyJoiner=e.keyJoiner||n.KeyJoiner,e.keyAssignmentValidators=e.keyAssignmentValidators||n.KeyAssignmentValidators,e},f=function(t,e,n){if(!e)return t;var i=e.shift();return t[i]||(t[i]=l.isArray(i)?[]:{}),0===e.length&&(l.isArray(t[i])?t[i].push(n):t[i]=n),0<e.length&&f(t[i],e,n),t},s=function(i,t,r){var o={};return l.each(t,function(t,e){var n={};r&&(e=i.keyJoiner(r,e)),l.isArray(t)?n[e+="[]"]=t:l.isObject(t)?n=s(i,t,e):n[e]=t,l.extend(o,n)}),o},r=n.TypeRegistry=function(){this.registeredTypes={}};r.extend=t.Model.extend,l.extend(r.prototype,{get:function(t){return this.registeredTypes[t]||this.registeredTypes.default},register:function(t,e){this.registeredTypes[t]=e},registerDefault:function(t){this.registeredTypes.default=t},unregister:function(t){this.registeredTypes[t]&&delete this.registeredTypes[t]}});var o=n.KeyExtractorSet=r.extend();(n.KeyExtractors=new o).registerDefault(function(t){return t.prop("name")||""});var a=n.InputReaderSet=r.extend(),p=n.InputReaders=new a;p.registerDefault(function(t){return t.val()}),p.register("checkbox",function(t){return t.prop("checked")});var g=n.InputWriterSet=r.extend(),v=n.InputWriters=new g;v.registerDefault(function(t,e){t.val(e)}),v.register("checkbox",function(t,e){t.prop("checked",e)}),v.register("radio",function(t,e){t.prop("checked",t.val()===e.toString())});var m=n.KeyAssignmentValidatorSet=r.extend(),y=n.KeyAssignmentValidators=new m;return y.registerDefault(function(){return!0}),y.register("radio",function(t,e,n){return t.prop("checked")}),n.KeySplitter=function(t){var e,n=t.match(/[^\[\]]+/g);return t.indexOf("[]")===t.length-2&&(e=n.pop(),n.push([e])),n},n.KeyJoiner=function(t,e){return t+"["+e+"]"},t.Syphon}),function(t,n){if("function"==typeof define&&define.amd)define(["backbone","underscore"],function(t,e){return n(t,e)});else if("undefined"!=typeof exports){var e=require("backbone"),i=require("underscore");module.exports=n(e,i)}else n(t.Backbone,t._)}(this,function(e,o){"use strict";var t,i,n,r,s,a,l,u=e.Wreqr,c=e.Wreqr={};function h(t){this.options=t,this._wreqrHandlers={},i.isFunction(this.initialize)&&this.initialize(t)}function d(t){this.options=t,this._commands={},o.isFunction(this.initialize)&&this.initialize(t)}function f(){}function p(t){this.vent=new e.Wreqr.EventAggregator,this.reqres=new e.Wreqr.RequestResponse,this.commands=new e.Wreqr.Commands,this.channelName=t}return e.Wreqr.VERSION="1.3.2",e.Wreqr.noConflict=function(){return e.Wreqr=u,this},c.Handlers=(i=o,h.extend=(t=e).Model.extend,i.extend(h.prototype,t.Events,{setHandlers:function(t){i.each(t,function(t,e){var n=null;i.isObject(t)&&!i.isFunction(t)&&(n=t.context,t=t.callback),this.setHandler(e,t,n)},this)},setHandler:function(t,e,n){var i={callback:e,context:n};this._wreqrHandlers[t]=i,this.trigger("handler:add",t,e,n)},hasHandler:function(t){return!!this._wreqrHandlers[t]},getHandler:function(t){var e=this._wreqrHandlers[t];if(e)return function(){return e.callback.apply(e.context,arguments)}},removeHandler:function(t){delete this._wreqrHandlers[t]},removeAllHandlers:function(){this._wreqrHandlers={}}}),h),c.CommandStorage=(o.extend(d.prototype,e.Events,{getCommands:function(t){var e=this._commands[t];return e||(e={command:t,instances:[]},this._commands[t]=e),e},addCommand:function(t,e){this.getCommands(t).instances.push(e)},clearCommands:function(t){this.getCommands(t).instances=[]}}),d),c.Commands=(r=o,(n=c).Handlers.extend({storageType:n.CommandStorage,constructor:function(t){this.options=t||{},this._initializeStorage(this.options),this.on("handler:add",this._executeCommands,this),n.Handlers.prototype.constructor.apply(this,arguments)},execute:function(t){t=arguments[0];var e=r.rest(arguments);this.hasHandler(t)?this.getHandler(t).apply(this,e):this.storage.addCommand(t,e)},_executeCommands:function(t,e,n){var i=this.storage.getCommands(t);r.each(i.instances,function(t){e.apply(n,t)}),this.storage.clearCommands(t)},_initializeStorage:function(t){var e,n=t.storageType||this.storageType;e=r.isFunction(n)?new n:n,this.storage=e}})),c.RequestResponse=(s=o,c.Handlers.extend({request:function(t){if(this.hasHandler(t))return this.getHandler(t).apply(this,s.rest(arguments))}})),c.EventAggregator=(l=o,f.extend=(a=e).Model.extend,l.extend(f.prototype,a.Events),f),c.Channel=(o.extend(p.prototype,{reset:function(){return this.vent.off(),this.vent.stopListening(),this.reqres.removeAllHandlers(),this.commands.removeAllHandlers(),this},connectEvents:function(t,e){return this._connect("vent",t,e),this},connectCommands:function(t,e){return this._connect("commands",t,e),this},connectRequests:function(t,e){return this._connect("reqres",t,e),this},_connect:function(n,t,i){if(t){i=i||this;var r="vent"===n?"on":"setHandler";o.each(t,function(t,e){this[n][r](e,o.bind(t,i))},this)}}}),p),c.radio=function(n,o){function t(){this._channels={},this.vent={},this.commands={},this.reqres={},this._proxyMethods()}o.extend(t.prototype,{channel:function(t){if(!t)throw new Error("Channel must receive a name");return this._getChannel(t)},_getChannel:function(t){var e=this._channels[t];return e||(e=new n.Channel(t),this._channels[t]=e),e},_proxyMethods:function(){o.each(["vent","commands","reqres"],function(e){o.each(i[e],function(t){this[e][t]=r(this,e,t)},this)},this)}});var i={vent:["on","off","trigger","once","stopListening","listenTo","listenToOnce"],commands:["execute","setHandler","setHandlers","removeHandler","removeAllHandlers"],reqres:["request","setHandler","setHandlers","removeHandler","removeAllHandlers"]},r=function(n,i,r){return function(t){var e=n._getChannel(t)[i];return e[r].apply(e,o.rest(arguments))}};return new t}(c,o),e.Wreqr});var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(t){var e,n,i,r,o,s,a,l="",u=0;for(t=Base64._utf8_encode(t);u<t.length;)r=(e=t.charCodeAt(u++))>>2,o=(3&e)<<4|(n=t.charCodeAt(u++))>>4,s=(15&n)<<2|(i=t.charCodeAt(u++))>>6,a=63&i,isNaN(n)?s=a=64:isNaN(i)&&(a=64),l=l+this._keyStr.charAt(r)+this._keyStr.charAt(o)+this._keyStr.charAt(s)+this._keyStr.charAt(a);return l},decode:function(t){var e,n,i,r,o,s,a="",l=0;for(t=t.replace(/[^A-Za-z0-9\+\/\=]/g,"");l<t.length;)e=this._keyStr.indexOf(t.charAt(l++))<<2|(r=this._keyStr.indexOf(t.charAt(l++)))>>4,n=(15&r)<<4|(o=this._keyStr.indexOf(t.charAt(l++)))>>2,i=(3&o)<<6|(s=this._keyStr.indexOf(t.charAt(l++))),a+=String.fromCharCode(e),64!=o&&(a+=String.fromCharCode(n)),64!=s&&(a+=String.fromCharCode(i));return a=Base64._utf8_decode(a)},_utf8_encode:function(t){t=t.replace(/\r\n/g,"\n");for(var e="",n=0;n<t.length;n++){var i=t.charCodeAt(n);i<128?e+=String.fromCharCode(i):(127<i&&i<2048?e+=String.fromCharCode(i>>6|192):(e+=String.fromCharCode(i>>12|224),e+=String.fromCharCode(i>>6&63|128)),e+=String.fromCharCode(63&i|128))}return e},_utf8_decode:function(t){for(var e="",n=0,i=c1=c2=0;n<t.length;)(i=t.charCodeAt(n))<128?(e+=String.fromCharCode(i),n++):191<i&&i<224?(c2=t.charCodeAt(n+1),e+=String.fromCharCode((31&i)<<6|63&c2),n+=2):(c2=t.charCodeAt(n+1),c3=t.charCodeAt(n+2),e+=String.fromCharCode((15&i)<<12|(63&c2)<<6|63&c3),n+=3);return e}};Date.CultureInfo={name:"en-US",englishName:"English (United States)",nativeName:"English (United States)",dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],abbreviatedDayNames:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],shortestDayNames:["Su","Mo","Tu","We","Th","Fr","Sa"],firstLetterDayNames:["S","M","T","W","T","F","S"],monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],abbreviatedMonthNames:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],amDesignator:"AM",pmDesignator:"PM",firstDayOfWeek:0,twoDigitYearMax:2029,dateElementOrder:"mdy",formatPatterns:{shortDate:"M/d/yyyy",longDate:"dddd, MMMM dd, yyyy",shortTime:"h:mm tt",longTime:"h:mm:ss tt",fullDateTime:"dddd, MMMM dd, yyyy h:mm:ss tt",sortableDateTime:"yyyy-MM-ddTHH:mm:ss",universalSortableDateTime:"yyyy-MM-dd HH:mm:ssZ",rfc1123:"ddd, dd MMM yyyy HH:mm:ss GMT",monthDay:"MMMM dd",yearMonth:"MMMM, yyyy"},regexPatterns:{jan:/^jan(uary)?/i,feb:/^feb(ruary)?/i,mar:/^mar(ch)?/i,apr:/^apr(il)?/i,may:/^may/i,jun:/^jun(e)?/i,jul:/^jul(y)?/i,aug:/^aug(ust)?/i,sep:/^sep(t(ember)?)?/i,oct:/^oct(ober)?/i,nov:/^nov(ember)?/i,dec:/^dec(ember)?/i,sun:/^su(n(day)?)?/i,mon:/^mo(n(day)?)?/i,tue:/^tu(e(s(day)?)?)?/i,wed:/^we(d(nesday)?)?/i,thu:/^th(u(r(s(day)?)?)?)?/i,fri:/^fr(i(day)?)?/i,sat:/^sa(t(urday)?)?/i,future:/^next/i,past:/^last|past|prev(ious)?/i,add:/^(\+|aft(er)?|from|hence)/i,subtract:/^(\-|bef(ore)?|ago)/i,yesterday:/^yes(terday)?/i,today:/^t(od(ay)?)?/i,tomorrow:/^tom(orrow)?/i,now:/^n(ow)?/i,millisecond:/^ms|milli(second)?s?/i,second:/^sec(ond)?s?/i,minute:/^mn|min(ute)?s?/i,hour:/^h(our)?s?/i,week:/^w(eek)?s?/i,month:/^m(onth)?s?/i,day:/^d(ay)?s?/i,year:/^y(ear)?s?/i,shortMeridian:/^(a|p)/i,longMeridian:/^(a\.?m?\.?|p\.?m?\.?)/i,timezone:/^((e(s|d)t|c(s|d)t|m(s|d)t|p(s|d)t)|((gmt)?\s*(\+|\-)\s*\d\d\d\d?)|gmt|utc)/i,ordinalSuffix:/^\s*(st|nd|rd|th)/i,timeContext:/^\s*(\:|a(?!u|p)|p)/i},timezones:[{name:"UTC",offset:"-000"},{name:"GMT",offset:"-000"},{name:"EST",offset:"-0500"},{name:"EDT",offset:"-0400"},{name:"CST",offset:"-0600"},{name:"CDT",offset:"-0500"},{name:"MST",offset:"-0700"},{name:"MDT",offset:"-0600"},{name:"PST",offset:"-0800"},{name:"PDT",offset:"-0700"}]},function(){function i(t,e){return("000"+t).slice(-1*(e=e||2))}var a,l,u,r=Date,t=r.prototype,s=r.CultureInfo;t.clearTime=function(){return this.setHours(0),this.setMinutes(0),this.setSeconds(0),this.setMilliseconds(0),this},t.setTimeToNow=function(){var t=new Date;return this.setHours(t.getHours()),this.setMinutes(t.getMinutes()),this.setSeconds(t.getSeconds()),this.setMilliseconds(t.getMilliseconds()),this},r.today=function(){return(new Date).clearTime()},r.compare=function(t,e){if(isNaN(t)||isNaN(e))throw new Error(t+" - "+e);if(t instanceof Date&&e instanceof Date)return t<e?-1:e<t?1:0;throw new TypeError(t+" - "+e)},r.equals=function(t,e){return 0===t.compareTo(e)},r.getDayNumberFromName=function(t){for(var e=s.dayNames,n=s.abbreviatedDayNames,i=s.shortestDayNames,r=t.toLowerCase(),o=0;o<e.length;o++)if(e[o].toLowerCase()==r||n[o].toLowerCase()==r||i[o].toLowerCase()==r)return o;return-1},r.getMonthNumberFromName=function(t){for(var e=s.monthNames,n=s.abbreviatedMonthNames,i=t.toLowerCase(),r=0;r<e.length;r++)if(e[r].toLowerCase()==i||n[r].toLowerCase()==i)return r;return-1},r.isLeapYear=function(t){return t%4==0&&t%100!=0||t%400==0},r.getDaysInMonth=function(t,e){return[31,r.isLeapYear(t)?29:28,31,30,31,30,31,31,30,31,30,31][e]},r.getTimezoneAbbreviation=function(t){for(var e=s.timezones,n=0;n<e.length;n++)if(e[n].offset===t)return e[n].name;return null},r.getTimezoneOffset=function(t){for(var e=s.timezones,n=0;n<e.length;n++)if(e[n].name===t.toUpperCase())return e[n].offset;return null},t.clone=function(){return new Date(this.getTime())},t.compareTo=function(t){return Date.compare(this,t)},t.equals=function(t){return Date.equals(this,t||new Date)},t.between=function(t,e){return this.getTime()>=t.getTime()&&this.getTime()<=e.getTime()},t.isAfter=function(t){return 1===this.compareTo(t||new Date)},t.isBefore=function(t){return-1===this.compareTo(t||new Date)},t.isToday=function(){return this.isSameDay(new Date)},t.isSameDay=function(t){return this.clone().clearTime().equals(t.clone().clearTime())},t.addMilliseconds=function(t){return this.setMilliseconds(this.getMilliseconds()+t),this},t.addSeconds=function(t){return this.addMilliseconds(1e3*t)},t.addMinutes=function(t){return this.addMilliseconds(6e4*t)},t.addHours=function(t){return this.addMilliseconds(36e5*t)},t.addDays=function(t){return this.setDate(this.getDate()+t),this},t.addWeeks=function(t){return this.addDays(7*t)},t.addMonths=function(t){var e=this.getDate();return this.setDate(1),this.setMonth(this.getMonth()+t),this.setDate(Math.min(e,r.getDaysInMonth(this.getFullYear(),this.getMonth()))),this},t.addYears=function(t){return this.addMonths(12*t)},t.add=function(t){if("number"==typeof t)return this._orient=t,this;var e=t;return e.milliseconds&&this.addMilliseconds(e.milliseconds),e.seconds&&this.addSeconds(e.seconds),e.minutes&&this.addMinutes(e.minutes),e.hours&&this.addHours(e.hours),e.weeks&&this.addWeeks(e.weeks),e.months&&this.addMonths(e.months),e.years&&this.addYears(e.years),e.days&&this.addDays(e.days),this},t.getWeek=function(){var t,e,n,i,r,o,s;return a=a||this.getFullYear(),l=l||this.getMonth()+1,u=u||this.getDate(),i=l<=2?(s=(e=((t=a-1)/4|0)-(t/100|0)+(t/400|0))-(((t-1)/4|0)-((t-1)/100|0)+((t-1)/400|0)),n=0,u-1+31*(l-1)):(n=(s=(e=((t=a)/4|0)-(t/100|0)+(t/400|0))-(((t-1)/4|0)-((t-1)/100|0)+((t-1)/400|0)))+1,u+(153*(l-3)+2)/5+58+s),a=l=u=null,(o=i+3-(i+(r=(t+e)%7)-n)%7|0)<0?53-((r-s)/5|0):364+s<o?1:1+(o/7|0)},t.getISOWeek=function(){return a=this.getUTCFullYear(),l=this.getUTCMonth()+1,u=this.getUTCDate(),i(this.getWeek())},t.setWeek=function(t){return this.moveToDayOfWeek(1).addWeeks(t-this.getWeek())},r._validate=function(t,e,n,i){if(void 0===t)return!1;if("number"!=typeof t)throw new TypeError(t+" is not a Number.");if(t<e||n<t)throw new RangeError(t+" is not a valid value for "+i+".");return!0},r.validateMillisecond=function(t){return r._validate(t,0,999,"millisecond")},r.validateSecond=function(t){return r._validate(t,0,59,"second")},r.validateMinute=function(t){return r._validate(t,0,59,"minute")},r.validateHour=function(t){return r._validate(t,0,23,"hour")},r.validateDay=function(t,e,n){return r._validate(t,1,r.getDaysInMonth(e,n),"day")},r.validateMonth=function(t){return r._validate(t,0,11,"month")},r.validateYear=function(t){return r._validate(t,0,9999,"year")},t.set=function(t){return r.validateMillisecond(t.millisecond)&&this.addMilliseconds(t.millisecond-this.getMilliseconds()),r.validateSecond(t.second)&&this.addSeconds(t.second-this.getSeconds()),r.validateMinute(t.minute)&&this.addMinutes(t.minute-this.getMinutes()),r.validateHour(t.hour)&&this.addHours(t.hour-this.getHours()),r.validateMonth(t.month)&&this.addMonths(t.month-this.getMonth()),r.validateYear(t.year)&&this.addYears(t.year-this.getFullYear()),r.validateDay(t.day,this.getFullYear(),this.getMonth())&&this.addDays(t.day-this.getDate()),t.timezone&&this.setTimezone(t.timezone),t.timezoneOffset&&this.setTimezoneOffset(t.timezoneOffset),t.week&&r._validate(t.week,0,53,"week")&&this.setWeek(t.week),this},t.moveToFirstDayOfMonth=function(){return this.set({day:1})},t.moveToLastDayOfMonth=function(){return this.set({day:r.getDaysInMonth(this.getFullYear(),this.getMonth())})},t.moveToNthOccurrence=function(t,e){var n=0;if(0<e)n=e-1;else if(-1===e)return this.moveToLastDayOfMonth(),this.getDay()!==t&&this.moveToDayOfWeek(t,-1),this;return this.moveToFirstDayOfMonth().addDays(-1).moveToDayOfWeek(t,1).addWeeks(n)},t.moveToDayOfWeek=function(t,e){var n=(t-this.getDay()+7*(e||1))%7;return this.addDays(0===n?n+=7*(e||1):n)},t.moveToMonth=function(t,e){var n=(t-this.getMonth()+12*(e||1))%12;return this.addMonths(0===n?n+=12*(e||1):n)},t.getOrdinalNumber=function(){return Math.ceil((this.clone().clearTime()-new Date(this.getFullYear(),0,1))/864e5)+1},t.getTimezone=function(){return r.getTimezoneAbbreviation(this.getUTCOffset())},t.setTimezoneOffset=function(t){var e=this.getTimezoneOffset(),n=-6*Number(t)/10;return this.addMinutes(n-e)},t.setTimezone=function(t){return this.setTimezoneOffset(r.getTimezoneOffset(t))},t.hasDaylightSavingTime=function(){return Date.today().set({month:0,day:1}).getTimezoneOffset()!==Date.today().set({month:6,day:1}).getTimezoneOffset()},t.isDaylightSavingTime=function(){return this.hasDaylightSavingTime()&&(new Date).getTimezoneOffset()===Date.today().set({month:6,day:1}).getTimezoneOffset()},t.getUTCOffset=function(){var t,e=-10*this.getTimezoneOffset()/6;return e<0?(t=(e-1e4).toString()).charAt(0)+t.substr(2):"+"+(t=(1e4+e).toString()).substr(1)},t.getElapsed=function(t){return(t||new Date)-this},t.toISOString||(t.toISOString=function(){function t(t){return t<10?"0"+t:t}return'"'+this.getUTCFullYear()+"-"+t(this.getUTCMonth()+1)+"-"+t(this.getUTCDate())+"T"+t(this.getUTCHours())+":"+t(this.getUTCMinutes())+":"+t(this.getUTCSeconds())+'Z"'}),t._toString=t.toString,t.toString=function(t){var e=this;if(t&&1==t.length){var n=s.formatPatterns;switch(e.t=e.toString,t){case"d":return e.t(n.shortDate);case"D":return e.t(n.longDate);case"F":return e.t(n.fullDateTime);case"m":return e.t(n.monthDay);case"r":return e.t(n.rfc1123);case"s":return e.t(n.sortableDateTime);case"t":return e.t(n.shortTime);case"T":return e.t(n.longTime);case"u":return e.t(n.universalSortableDateTime);case"y":return e.t(n.yearMonth)}}return t?t.replace(/(\\)?(dd?d?d?|MM?M?M?|yy?y?y?|hh?|HH?|mm?|ss?|tt?|S)/g,function(t){if("\\"===t.charAt(0))return t.replace("\\","");switch(e.h=e.getHours,t){case"hh":return i(e.h()<13?0===e.h()?12:e.h():e.h()-12);case"h":return e.h()<13?0===e.h()?12:e.h():e.h()-12;case"HH":return i(e.h());case"H":return e.h();case"mm":return i(e.getMinutes());case"m":return e.getMinutes();case"ss":return i(e.getSeconds());case"s":return e.getSeconds();case"yyyy":return i(e.getFullYear(),4);case"yy":return i(e.getFullYear());case"dddd":return s.dayNames[e.getDay()];case"ddd":return s.abbreviatedDayNames[e.getDay()];case"dd":return i(e.getDate());case"d":return e.getDate();case"MMMM":return s.monthNames[e.getMonth()];case"MMM":return s.abbreviatedMonthNames[e.getMonth()];case"MM":return i(e.getMonth()+1);case"M":return e.getMonth()+1;case"t":return e.h()<12?s.amDesignator.substring(0,1):s.pmDesignator.substring(0,1);case"tt":return e.h()<12?s.amDesignator:s.pmDesignator;case"S":return function(t){switch(1*t){case 1:case 21:case 31:return"st";case 2:case 22:return"nd";case 3:case 23:return"rd";default:return"th"}}(e.getDate());default:return t}}):this._toString()}}(),function(){var i=Date,t=i.prototype,r=i.CultureInfo,e=Number.prototype;t._orient=1,t._nth=null,t._is=!1,t._same=!1,t._isSecond=!1,e._dateElement="day",t.next=function(){return this._orient=1,this},i.next=function(){return i.today().next()},t.last=t.prev=t.previous=function(){return this._orient=-1,this},i.last=i.prev=i.previous=function(){return i.today().last()},t.is=function(){return this._is=!0,this},t.same=function(){return this._same=!0,this._isSecond=!1,this},t.today=function(){return this.same().day()},t.weekday=function(){return!!this._is&&(this._is=!1,!this.is().sat()&&!this.is().sun())},t.at=function(t){return"string"==typeof t?i.parse(this.toString("d")+" "+t):this.set(t)},e.fromNow=e.after=function(t){var e={};return e[this._dateElement]=this,(t?t.clone():new Date).add(e)},e.ago=e.before=function(t){var e={};return e[this._dateElement]=-1*this,(t?t.clone():new Date).add(e)};var n,o="sunday monday tuesday wednesday thursday friday saturday".split(/\s/),s="january february march april may june july august september october november december".split(/\s/),a="Millisecond Second Minute Hour Day Week Month Year".split(/\s/),l="Milliseconds Seconds Minutes Hours Date Week Month FullYear".split(/\s/),u="final first second third fourth fifth".split(/\s/);t.toObject=function(){for(var t={},e=0;e<a.length;e++)t[a[e].toLowerCase()]=this["get"+l[e]]();return t},i.fromObject=function(t){return t.week=null,Date.today().set(t)};function c(n){return function(){if(this._is)return this._is=!1,this.getDay()==n;if(null===this._nth)return this.moveToDayOfWeek(n,this._orient);this._isSecond&&this.addSeconds(-1*this._orient),this._isSecond=!1;var t=this._nth;this._nth=null;var e=this.clone().moveToLastDayOfMonth();if(this.moveToNthOccurrence(n,t),e<this)throw new RangeError(i.getDayName(n)+" does not occur "+t+" times in the month of "+i.getMonthName(e.getMonth())+" "+e.getFullYear()+".");return this}}function h(n){return function(){var t=i.today(),e=n-t.getDay();return 0===n&&1===r.firstDayOfWeek&&0!==t.getDay()&&(e+=7),t.addDays(e)}}for(var d=0;d<o.length;d++)i[o[d].toUpperCase()]=i[o[d].toUpperCase().substring(0,3)]=d,i[o[d]]=i[o[d].substring(0,3)]=h(d),t[o[d]]=t[o[d].substring(0,3)]=c(d);function f(t){return function(){return this._is?(this._is=!1,this.getMonth()===t):this.moveToMonth(t,this._orient)}}function p(t){return function(){return i.today().set({month:t,day:1})}}for(var g=0;g<s.length;g++)i[s[g].toUpperCase()]=i[s[g].toUpperCase().substring(0,3)]=g,i[s[g]]=i[s[g].substring(0,3)]=p(g),t[s[g]]=t[s[g].substring(0,3)]=f(g);function v(o){return function(){if(this._isSecond)return this._isSecond=!1,this;if(this._same){this._same=this._is=!1;for(var t=this.toObject(),e=(arguments[0]||new Date).toObject(),n="",i=o.toLowerCase(),r=a.length-1;-1<r;r--){if(t[n=a[r].toLowerCase()]!=e[n])return!1;if(i==n)break}return!0}return"s"!=o.substring(o.length-1)&&(o+="s"),this["add"+o](this._orient)}}function m(t){return function(){return this._dateElement=t,this}}for(var y=0;y<a.length;y++)t[n=a[y].toLowerCase()]=t[n+"s"]=v(a[y]),e[n]=e[n+"s"]=m(n);t._ss=v("Second");function b(e){return function(t){return this._same?this._ss(t):t||0===t?this.moveToNthOccurrence(t,e):2===(this._nth=e)&&null==t?(this._isSecond=!0,this.addSeconds(this._orient)):this}}for(var w=0;w<u.length;w++)t[u[w]]=b(0===w?-1:w)}(),function(){Date.Parsing={Exception:function(t){this.message="Parse error at '"+t.substring(0,10)+" ...'"}};function t(r){return function(){var t=null,e=[];if(1<arguments.length?t=Array.prototype.slice.call(arguments):arguments[0]instanceof Array&&(t=arguments[0]),!t)return r.apply(null,arguments);for(var n=0,i=t.shift();n<i.length;n++)return t.unshift(i[n]),e.push(r.apply(null,t)),t.shift(),e}}for(var f=Date.Parsing,p=f.Operators={rtoken:function(n){return function(t){var e=t.match(n);if(e)return[e[0],t.substring(e[0].length)];throw new f.Exception(t)}},token:function(t){return function(t){return p.rtoken(new RegExp("^s*"+t+"s*"))(t)}},stoken:function(t){return p.rtoken(new RegExp("^"+t))},until:function(t){return function(e){for(var n=[],i=null;e.length;){try{i=t.call(this,e)}catch(t){n.push(i[0]),e=i[1];continue}break}return[n,e]}},many:function(i){return function(e){for(var n=[],t=null;e.length;){try{t=i.call(this,e)}catch(t){return[n,e]}n.push(t[0]),e=t[1]}return[n,e]}},optional:function(n){return function(e){var t=null;try{t=n.call(this,e)}catch(t){return[null,e]}return[t[0],t[1]]}},not:function(t){return function(e){try{t.call(this,e)}catch(t){return[null,e]}throw new f.Exception(e)}},ignore:function(e){return e?function(t){return[null,e.call(this,t)[1]]}:null},product:function(){for(var t=arguments[0],e=Array.prototype.slice.call(arguments,1),n=[],i=0;i<t.length;i++)n.push(p.each(t[i],e));return n},cache:function(t){var n={},i=null;return function(e){try{i=n[e]=n[e]||t.call(this,e)}catch(t){i=n[e]=t}if(i instanceof f.Exception)throw i;return i}},any:function(){var i=arguments;return function(t){for(var e=null,n=0;n<i.length;n++)if(null!=i[n]){try{e=i[n].call(this,t)}catch(t){e=null}if(e)return e}throw new f.Exception(t)}},each:function(){var r=arguments;return function(e){for(var t=[],n=null,i=0;i<r.length;i++)if(null!=r[i]){try{n=r[i].call(this,e)}catch(t){throw new f.Exception(e)}t.push(n[0]),e=n[1]}return[t,e]}},all:function(){var t=arguments,e=e;return e.each(e.optional(t))},sequence:function(o,s,a){return s=s||p.rtoken(/^\s*/),a=a||null,1==o.length?o[0]:function(t){for(var e=null,n=null,i=[],r=0;r<o.length;r++){try{e=o[r].call(this,t)}catch(t){break}i.push(e[0]);try{n=s.call(this,e[1])}catch(t){n=null;break}t=n[1]}if(!e)throw new f.Exception(t);if(n)throw new f.Exception(n[1]);if(a)try{e=a.call(this,e[1])}catch(t){throw new f.Exception(e[1])}return[i,e?e[1]:t]}},between:function(t,e,n){n=n||t;var i=p.each(p.ignore(t),e,p.ignore(n));return function(t){var e=i.call(this,t);return[[e[0][0],r[0][2]],e[1]]}},list:function(t,e,n){return e=e||p.rtoken(/^\s*/),n=n||null,t instanceof Array?p.each(p.product(t.slice(0,-1),p.ignore(e)),t.slice(-1),p.ignore(n)):p.each(p.many(p.each(t,p.ignore(e))),px,p.ignore(n))},set:function(c,h,d){return h=h||p.rtoken(/^\s*/),d=d||null,function(t){for(var e=null,n=null,i=null,r=null,o=[[],t],s=!1,a=0;a<c.length;a++){e=n=i=null,s=1==c.length;try{e=c[a].call(this,t)}catch(t){continue}if(r=[[e[0]],e[1]],0<e[1].length&&!s)try{i=h.call(this,e[1])}catch(t){s=!0}else s=!0;if(s||0!==i[1].length||(s=!0),!s){for(var l=[],u=0;u<c.length;u++)a!=u&&l.push(c[u]);0<(n=p.set(l,h).call(this,i[1]))[0].length&&(r[0]=r[0].concat(n[0]),r[1]=n[1])}if(r[1].length<o[1].length&&(o=r),0===o[1].length)break}if(0===o[0].length)return o;if(d){try{i=d.call(this,o[1])}catch(t){throw new f.Exception(o[1])}o[1]=i[1]}return o}},forward:function(e,n){return function(t){return e[n].call(this,t)}},replace:function(n,i){return function(t){var e=n.call(this,t);return[i,e[1]]}},process:function(n,i){return function(t){var e=n.call(this,t);return[i.call(this,e[0]),e[1]]}},min:function(n,i){return function(t){var e=i.call(this,t);if(e[0].length<n)throw new f.Exception(t);return e}}},e="optional not ignore cache".split(/\s/),n=0;n<e.length;n++)p[e[n]]=t(p[e[n]]);function i(t){return function(){return arguments[0]instanceof Array?t.apply(null,arguments[0]):t.apply(null,arguments)}}for(var o="each any all".split(/\s/),s=0;s<o.length;s++)p[o[s]]=i(p[o[s]])}(),function(){var l=Date,s=(l.prototype,l.CultureInfo),u=function(t){for(var e=[],n=0;n<t.length;n++)t[n]instanceof Array?e=e.concat(u(t[n])):t[n]&&e.push(t[n]);return e};l.Grammar={},l.Translator={hour:function(t){return function(){this.hour=Number(t)}},minute:function(t){return function(){this.minute=Number(t)}},second:function(t){return function(){this.second=Number(t)}},meridian:function(t){return function(){this.meridian=t.slice(0,1).toLowerCase()}},timezone:function(e){return function(){var t=e.replace(/[^\d\+\-]/g,"");t.length?this.timezoneOffset=Number(t):this.timezone=e.toLowerCase()}},day:function(t){var e=t[0];return function(){this.day=Number(e.match(/\d+/)[0])}},month:function(t){return function(){this.month=3==t.length?"jan feb mar apr may jun jul aug sep oct nov dec".indexOf(t)/4:Number(t)-1}},year:function(e){return function(){var t=Number(e);this.year=2<e.length?t:t+(t+2e3<s.twoDigitYearMax?2e3:1900)}},rday:function(t){return function(){switch(t){case"yesterday":this.days=-1;break;case"tomorrow":this.days=1;break;case"today":this.days=0;break;case"now":this.days=0,this.now=!0}}},finishExact:function(t){t=t instanceof Array?t:[t];for(var e=0;e<t.length;e++)t[e]&&t[e].call(this);var n=new Date;if(!this.hour&&!this.minute||this.month||this.year||this.day||(this.day=n.getDate()),this.year||(this.year=n.getFullYear()),this.month||0===this.month||(this.month=n.getMonth()),this.day||(this.day=1),this.hour||(this.hour=0),this.minute||(this.minute=0),this.second||(this.second=0),this.meridian&&this.hour&&("p"==this.meridian&&this.hour<12?this.hour=this.hour+12:"a"==this.meridian&&12==this.hour&&(this.hour=0)),this.day>l.getDaysInMonth(this.year,this.month))throw new RangeError(this.day+" is not a valid value for days.");var i=new Date(this.year,this.month,this.day,this.hour,this.minute,this.second);return this.timezone?i.set({timezone:this.timezone}):this.timezoneOffset&&i.set({timezoneOffset:this.timezoneOffset}),i},finish:function(t){if(0===(t=t instanceof Array?u(t):[t]).length)return null;for(var e=0;e<t.length;e++)"function"==typeof t[e]&&t[e].call(this);var n=l.today();if(this.now&&!this.unit&&!this.operator)return new Date;this.now&&(n=new Date);var i,r,o,s=!!(this.days&&null!==this.days||this.orient||this.operator);if(o="past"==this.orient||"subtract"==this.operator?-1:1,this.now||-1=="hour minute second".indexOf(this.unit)||n.setTimeToNow(),!this.month&&0!==this.month||-1=="year day hour minute second".indexOf(this.unit)||(this.value=this.month+1,s=!(this.month=null)),!s&&this.weekday&&!this.day&&!this.days){var a=Date[this.weekday]();this.day=a.getDate(),this.month||(this.month=a.getMonth()),this.year=a.getFullYear()}if(s&&this.weekday&&"month"!=this.unit&&(this.unit="day",i=l.getDayNumberFromName(this.weekday)-n.getDay(),r=7,this.days=i?(i+o*r)%r:o*r),this.month&&"day"==this.unit&&this.operator&&(this.value=this.month+1,this.month=null),null!=this.value&&null!=this.month&&null!=this.year&&(this.day=1*this.value),this.month&&!this.day&&this.value&&(n.set({day:1*this.value}),s||(this.day=1*this.value)),this.month||!this.value||"month"!=this.unit||this.now||(this.month=this.value,s=!0),s&&(this.month||0===this.month)&&"year"!=this.unit&&(this.unit="month",i=this.month-n.getMonth(),r=12,this.months=i?(i+o*r)%r:o*r,this.month=null),this.unit||(this.unit="day"),!this.value&&this.operator&&null!==this.operator&&this[this.unit+"s"]&&null!==this[this.unit+"s"]?this[this.unit+"s"]=this[this.unit+"s"]+("add"==this.operator?1:-1)+(this.value||0)*o:null!=this[this.unit+"s"]&&null==this.operator||(this.value||(this.value=1),this[this.unit+"s"]=this.value*o),this.meridian&&this.hour&&("p"==this.meridian&&this.hour<12?this.hour=this.hour+12:"a"==this.meridian&&12==this.hour&&(this.hour=0)),this.weekday&&!this.day&&!this.days){a=Date[this.weekday]();this.day=a.getDate(),a.getMonth()!==n.getMonth()&&(this.month=a.getMonth())}return!this.month&&0!==this.month||this.day||(this.day=1),this.orient||this.operator||"week"!=this.unit||!this.value||this.day||this.month?(s&&this.timezone&&this.day&&this.days&&(this.day=this.days),s?n.add(this):n.set(this)):Date.today().setWeek(this.value)}};var t,a=l.Parsing.Operators,n=l.Grammar,e=l.Translator;n.datePartDelimiter=a.rtoken(/^([\s\-\.\,\/\x27]+)/),n.timePartDelimiter=a.stoken(":"),n.whiteSpace=a.rtoken(/^\s*/),n.generalDelimiter=a.rtoken(/^(([\s\,]|at|@|on)+)/);var c={};n.ctoken=function(t){var e=c[t];if(!e){for(var n=s.regexPatterns,i=t.split(/\s+/),r=[],o=0;o<i.length;o++)r.push(a.replace(a.rtoken(n[i[o]]),i[o]));e=c[t]=a.any.apply(null,r)}return e},n.ctoken2=function(t){return a.rtoken(s.regexPatterns[t])},n.h=a.cache(a.process(a.rtoken(/^(0[0-9]|1[0-2]|[1-9])/),e.hour)),n.hh=a.cache(a.process(a.rtoken(/^(0[0-9]|1[0-2])/),e.hour)),n.H=a.cache(a.process(a.rtoken(/^([0-1][0-9]|2[0-3]|[0-9])/),e.hour)),n.HH=a.cache(a.process(a.rtoken(/^([0-1][0-9]|2[0-3])/),e.hour)),n.m=a.cache(a.process(a.rtoken(/^([0-5][0-9]|[0-9])/),e.minute)),n.mm=a.cache(a.process(a.rtoken(/^[0-5][0-9]/),e.minute)),n.s=a.cache(a.process(a.rtoken(/^([0-5][0-9]|[0-9])/),e.second)),n.ss=a.cache(a.process(a.rtoken(/^[0-5][0-9]/),e.second)),n.hms=a.cache(a.sequence([n.H,n.m,n.s],n.timePartDelimiter)),n.t=a.cache(a.process(n.ctoken2("shortMeridian"),e.meridian)),n.tt=a.cache(a.process(n.ctoken2("longMeridian"),e.meridian)),n.z=a.cache(a.process(a.rtoken(/^((\+|\-)\s*\d\d\d\d)|((\+|\-)\d\d\:?\d\d)/),e.timezone)),n.zz=a.cache(a.process(a.rtoken(/^((\+|\-)\s*\d\d\d\d)|((\+|\-)\d\d\:?\d\d)/),e.timezone)),n.zzz=a.cache(a.process(n.ctoken2("timezone"),e.timezone)),n.timeSuffix=a.each(a.ignore(n.whiteSpace),a.set([n.tt,n.zzz])),n.time=a.each(a.optional(a.ignore(a.stoken("T"))),n.hms,n.timeSuffix),n.d=a.cache(a.process(a.each(a.rtoken(/^([0-2]\d|3[0-1]|\d)/),a.optional(n.ctoken2("ordinalSuffix"))),e.day)),n.dd=a.cache(a.process(a.each(a.rtoken(/^([0-2]\d|3[0-1])/),a.optional(n.ctoken2("ordinalSuffix"))),e.day)),n.ddd=n.dddd=a.cache(a.process(n.ctoken("sun mon tue wed thu fri sat"),function(t){return function(){this.weekday=t}})),n.M=a.cache(a.process(a.rtoken(/^(1[0-2]|0\d|\d)/),e.month)),n.MM=a.cache(a.process(a.rtoken(/^(1[0-2]|0\d)/),e.month)),n.MMM=n.MMMM=a.cache(a.process(n.ctoken("jan feb mar apr may jun jul aug sep oct nov dec"),e.month)),n.y=a.cache(a.process(a.rtoken(/^(\d\d?)/),e.year)),n.yy=a.cache(a.process(a.rtoken(/^(\d\d)/),e.year)),n.yyy=a.cache(a.process(a.rtoken(/^(\d\d?\d?\d?)/),e.year)),n.yyyy=a.cache(a.process(a.rtoken(/^(\d\d\d\d)/),e.year)),t=function(){return a.each(a.any.apply(null,arguments),a.not(n.ctoken2("timeContext")))},n.day=t(n.d,n.dd),n.month=t(n.M,n.MMM),n.year=t(n.yyyy,n.yy),n.orientation=a.process(n.ctoken("past future"),function(t){return function(){this.orient=t}}),n.operator=a.process(n.ctoken("add subtract"),function(t){return function(){this.operator=t}}),n.rday=a.process(n.ctoken("yesterday tomorrow today now"),e.rday),n.unit=a.process(n.ctoken("second minute hour day week month year"),function(t){return function(){this.unit=t}}),n.value=a.process(a.rtoken(/^\d\d?(st|nd|rd|th)?/),function(t){return function(){this.value=t.replace(/\D/g,"")}}),n.expression=a.set([n.rday,n.operator,n.value,n.unit,n.orientation,n.ddd,n.MMM]),t=function(){return a.set(arguments,n.datePartDelimiter)},n.mdy=t(n.ddd,n.month,n.day,n.year),n.ymd=t(n.ddd,n.year,n.month,n.day),n.dmy=t(n.ddd,n.day,n.month,n.year),n.date=function(t){return(n[s.dateElementOrder]||n.mdy).call(this,t)},n.format=a.process(a.many(a.any(a.process(a.rtoken(/^(dd?d?d?|MM?M?M?|yy?y?y?|hh?|HH?|mm?|ss?|tt?|zz?z?)/),function(t){if(n[t])return n[t];throw l.Parsing.Exception(t)}),a.process(a.rtoken(/^[^dMyhHmstz]+/),function(t){return a.ignore(a.stoken(t))}))),function(t){return a.process(a.each.apply(null,t),e.finishExact)});function i(t){return r[t]=r[t]||n.format(t)[0]}var r={};n.formats=function(t){if(t instanceof Array){for(var e=[],n=0;n<t.length;n++)e.push(i(t[n]));return a.any.apply(null,e)}return i(t)},n._formats=n.formats(['"yyyy-MM-ddTHH:mm:ssZ"',"yyyy-MM-ddTHH:mm:ssZ","yyyy-MM-ddTHH:mm:ssz","yyyy-MM-ddTHH:mm:ss","yyyy-MM-ddTHH:mmZ","yyyy-MM-ddTHH:mmz","yyyy-MM-ddTHH:mm","ddd, MMM dd, yyyy H:mm:ss tt","ddd MMM d yyyy HH:mm:ss zzz","MMddyyyy","ddMMyyyy","Mddyyyy","ddMyyyy","Mdyyyy","dMyyyy","yyyy","Mdyy","dMyy","d"]),n._start=a.process(a.set([n.date,n.time,n.expression],n.generalDelimiter,n.whiteSpace),e.finish),n.start=function(t){try{var e=n._formats.call({},t);if(0===e[1].length)return e}catch(t){}return n._start.call({},t)},l._parse=l.parse,l.parse=function(t){var e=null;if(!t)return null;if(t instanceof Date)return t;try{e=l.Grammar.start.call({},t.replace(/^\s*(\S*(\s+\S+)*)\s*$/,"$1"))}catch(t){return null}return 0===e[1].length?e[0]:null},l.getParseFunction=function(t){var n=l.Grammar.formats(t);return function(t){var e=null;try{e=n.call({},t)}catch(t){return null}return 0===e[1].length?e[0]:null}},l.parseExact=function(t,e){return l.getParseFunction(e)(t)}}(),function(t,r){function p(t){if(this.defaults={locale_data:{messages:{"":{domain:"messages",lang:"en",plural_forms:"nplurals=2; plural=(n != 1);"}}},domain:"messages",debug:!1},this.options=o.extend({},this.defaults,t),this.textdomain(this.options.domain),t.domain&&!this.options.locale_data[this.options.domain])throw new Error("Text domain set to non-existent domain: `"+t.domain+"`")}var e=Array.prototype,n=Object.prototype,i=e.slice,s=n.hasOwnProperty,a=e.forEach,l={},o={forEach:function(t,e,n){var i,r,o;if(null!==t)if(a&&t.forEach===a)t.forEach(e,n);else if(t.length===+t.length){for(i=0,r=t.length;i<r;i++)if(i in t&&e.call(n,t[i],i,t)===l)return}else for(o in t)if(s.call(t,o)&&e.call(n,t[o],o,t)===l)return},extend:function(n){return this.forEach(i.call(arguments,1),function(t){for(var e in t)n[e]=t[e]}),n}};function g(t){return p.PF.compile(t||"nplurals=2; plural=(n != 1);")}function u(t,e){this._key=t,this._i18n=e}p.context_delimiter=String.fromCharCode(4),o.extend(u.prototype,{onDomain:function(t){return this._domain=t,this},withContext:function(t){return this._context=t,this},ifPlural:function(t,e){return this._val=t,this._pkey=e,this},fetch:function(t){return"[object Array]"!={}.toString.call(t)&&(t=[].slice.call(arguments,0)),(t&&t.length?p.sprintf:function(t){return t})(this._i18n.dcnpgettext(this._domain,this._context,this._key,this._pkey,this._val),t)}}),o.extend(p.prototype,{translate:function(t){return new u(t,this)},textdomain:function(t){if(!t)return this._textdomain;this._textdomain=t},gettext:function(t){return this.dcnpgettext.call(this,r,r,t)},dgettext:function(t,e){return this.dcnpgettext.call(this,t,r,e)},dcgettext:function(t,e){return this.dcnpgettext.call(this,t,r,e)},ngettext:function(t,e,n){return this.dcnpgettext.call(this,r,r,t,e,n)},dngettext:function(t,e,n,i){return this.dcnpgettext.call(this,t,r,e,n,i)},dcngettext:function(t,e,n,i){return this.dcnpgettext.call(this,t,r,e,n,i)},pgettext:function(t,e){return this.dcnpgettext.call(this,r,t,e)},dpgettext:function(t,e,n){return this.dcnpgettext.call(this,t,e,n)},dcpgettext:function(t,e,n){return this.dcnpgettext.call(this,t,e,n)},npgettext:function(t,e,n,i){return this.dcnpgettext.call(this,r,t,e,n,i)},dnpgettext:function(t,e,n,i,r){return this.dcnpgettext.call(this,t,e,n,i,r)},dcnpgettext:function(t,e,n,i,r){var o;if(i=i||n,t=t||this._textdomain,!this.options)return(o=new p).dcnpgettext.call(o,void 0,void 0,n,i,r);if(!this.options.locale_data)throw new Error("No locale data provided.");if(!this.options.locale_data[t])throw new Error("Domain `"+t+"` was not found.");if(!this.options.locale_data[t][""])throw new Error("No locale meta information provided.");if(!n)throw new Error("No translation key found.");var s,a,l,u=e?e+p.context_delimiter+n:n,c=this.options.locale_data,h=c[t],d=(c.messages||this.defaults.locale_data.messages)[""],f=h[""].plural_forms||h[""]["Plural-Forms"]||h[""]["plural-forms"]||d.plural_forms||d["Plural-Forms"]||d["plural-forms"];if(void 0===r)l=0;else{if("number"!=typeof r&&(r=parseInt(r,10),isNaN(r)))throw new Error("The number that was passed in is not a number.");l=g(f)(r)}if(!h)throw new Error("No domain named `"+t+"` could be found.");return!(s=h[u])||l>s.length?(this.options.missing_key_callback&&this.options.missing_key_callback(u,t),a=[n,i],!0===this.options.debug&&console.log(a[g(f)(r)]),a[g()(r)]):(a=s[l])||(a=[n,i])[g()(r)]}});var c,f=((c=function(){return c.cache.hasOwnProperty(arguments[0])||(c.cache[arguments[0]]=c.parse(arguments[0])),c.format.call(null,c.cache[arguments[0]],arguments)}).format=function(t,e){var n,i,r,o,s,a,l,u=1,c=t.length,h="",d=[];for(i=0;i<c;i++)if("string"===(h=v(t[i])))d.push(t[i]);else if("array"===h){if((o=t[i])[2])for(n=e[u],r=0;r<o[2].length;r++){if(!n.hasOwnProperty(o[2][r]))throw f('[sprintf] property "%s" does not exist',o[2][r]);n=n[o[2][r]]}else n=o[1]?e[o[1]]:e[u++];if(/[^s]/.test(o[8])&&"number"!=v(n))throw f("[sprintf] expecting number but found %s",v(n));switch(null==n&&(n=""),o[8]){case"b":n=n.toString(2);break;case"c":n=String.fromCharCode(n);break;case"d":n=parseInt(n,10);break;case"e":n=o[7]?n.toExponential(o[7]):n.toExponential();break;case"f":n=o[7]?parseFloat(n).toFixed(o[7]):parseFloat(n);break;case"o":n=n.toString(8);break;case"s":n=(n=String(n))&&o[7]?n.substring(0,o[7]):n;break;case"u":n=Math.abs(n);break;case"x":n=n.toString(16);break;case"X":n=n.toString(16).toUpperCase()}n=/[def]/.test(o[8])&&o[3]&&0<=n?"+"+n:n,a=o[4]?"0"==o[4]?"0":o[4].charAt(1):" ",l=o[6]-String(n).length,s=o[6]?m(a,l):"",d.push(o[5]?n+s:s+n)}return d.join("")},c.cache={},c.parse=function(t){for(var e=t,n=[],i=[],r=0;e;){if(null!==(n=/^[^\x25]+/.exec(e)))i.push(n[0]);else if(null!==(n=/^\x25{2}/.exec(e)))i.push("%");else{if(null===(n=/^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(e)))throw"[sprintf] huh?";if(n[2]){r|=1;var o=[],s=n[2],a=[];if(null===(a=/^([a-z_][a-z_\d]*)/i.exec(s)))throw"[sprintf] huh?";for(o.push(a[1]);""!==(s=s.substring(a[0].length));)if(null!==(a=/^\.([a-z_][a-z_\d]*)/i.exec(s)))o.push(a[1]);else{if(null===(a=/^\[(\d+)\]/.exec(s)))throw"[sprintf] huh?";o.push(a[1])}n[2]=o}else r|=2;if(3===r)throw"[sprintf] mixing positional and named placeholders is not (yet) supported";i.push(n)}e=e.substring(n[0].length)}return i},c);function v(t){return Object.prototype.toString.call(t).slice(8,-1).toLowerCase()}function m(t,e){for(var n=[];0<e;n[--e]=t);return n.join("")}var h;p.parse_plural=function(t,e){return t=t.replace(/n/g,e),p.parse_expression(t)},p.sprintf=function(t,e){return"[object Array]"=={}.toString.call(e)?function(t,e){return e.unshift(t),f.apply(null,e)}(t,[].slice.call(e)):f.apply(this,[].slice.call(arguments))},p.prototype.sprintf=function(){return p.sprintf.apply(this,arguments)},(p.PF={}).parse=function(t){var e=p.PF.extractPluralExpr(t);return p.PF.parser.parse.call(p.PF.parser,e)},p.PF.compile=function(t){var e=p.PF.parse(t);return function(t){return function(t){return!0===t?1:t||0}(p.PF.interpreter(e)(t))}},p.PF.interpreter=function(e){return function(t){switch(e.type){case"GROUP":return p.PF.interpreter(e.expr)(t);case"TERNARY":return p.PF.interpreter(e.expr)(t)?p.PF.interpreter(e.truthy)(t):p.PF.interpreter(e.falsey)(t);case"OR":return p.PF.interpreter(e.left)(t)||p.PF.interpreter(e.right)(t);case"AND":return p.PF.interpreter(e.left)(t)&&p.PF.interpreter(e.right)(t);case"LT":return p.PF.interpreter(e.left)(t)<p.PF.interpreter(e.right)(t);case"GT":return p.PF.interpreter(e.left)(t)>p.PF.interpreter(e.right)(t);case"LTE":return p.PF.interpreter(e.left)(t)<=p.PF.interpreter(e.right)(t);case"GTE":return p.PF.interpreter(e.left)(t)>=p.PF.interpreter(e.right)(t);case"EQ":return p.PF.interpreter(e.left)(t)==p.PF.interpreter(e.right)(t);case"NEQ":return p.PF.interpreter(e.left)(t)!=p.PF.interpreter(e.right)(t);case"MOD":return p.PF.interpreter(e.left)(t)%p.PF.interpreter(e.right)(t);case"VAR":return t;case"NUM":return e.val;default:throw new Error("Invalid Token found.")}}},p.PF.extractPluralExpr=function(t){t=t.replace(/^\s\s*/,"").replace(/\s\s*$/,""),/;\s*$/.test(t)||(t=t.concat(";"));var e,n=/nplurals\=(\d+);/,i=t.match(n);if(!(1<i.length))throw new Error("nplurals not found in plural_forms string: "+t);if(i[1],!((e=(t=t.replace(n,"")).match(/plural\=(.*);/))&&1<e.length))throw new Error("`plural` expression not found: "+t);return e[1]},p.PF.parser=((h={trace:function(){},yy:{},symbols_:{error:2,expressions:3,e:4,EOF:5,"?":6,":":7,"||":8,"&&":9,"<":10,"<=":11,">":12,">=":13,"!=":14,"==":15,"%":16,"(":17,")":18,n:19,NUMBER:20,$accept:0,$end:1},terminals_:{2:"error",5:"EOF",6:"?",7:":",8:"||",9:"&&",10:"<",11:"<=",12:">",13:">=",14:"!=",15:"==",16:"%",17:"(",18:")",19:"n",20:"NUMBER"},productions_:[0,[3,2],[4,5],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,1],[4,1]],performAction:function(t,e,n,i,r,o){var s=o.length-1;switch(r){case 1:return{type:"GROUP",expr:o[s-1]};case 2:this.$={type:"TERNARY",expr:o[s-4],truthy:o[s-2],falsey:o[s]};break;case 3:this.$={type:"OR",left:o[s-2],right:o[s]};break;case 4:this.$={type:"AND",left:o[s-2],right:o[s]};break;case 5:this.$={type:"LT",left:o[s-2],right:o[s]};break;case 6:this.$={type:"LTE",left:o[s-2],right:o[s]};break;case 7:this.$={type:"GT",left:o[s-2],right:o[s]};break;case 8:this.$={type:"GTE",left:o[s-2],right:o[s]};break;case 9:this.$={type:"NEQ",left:o[s-2],right:o[s]};break;case 10:this.$={type:"EQ",left:o[s-2],right:o[s]};break;case 11:this.$={type:"MOD",left:o[s-2],right:o[s]};break;case 12:this.$={type:"GROUP",expr:o[s-1]};break;case 13:this.$={type:"VAR"};break;case 14:this.$={type:"NUM",val:Number(t)}}},table:[{3:1,4:2,17:[1,3],19:[1,4],20:[1,5]},{1:[3]},{5:[1,6],6:[1,7],8:[1,8],9:[1,9],10:[1,10],11:[1,11],12:[1,12],13:[1,13],14:[1,14],15:[1,15],16:[1,16]},{4:17,17:[1,3],19:[1,4],20:[1,5]},{5:[2,13],6:[2,13],7:[2,13],8:[2,13],9:[2,13],10:[2,13],11:[2,13],12:[2,13],13:[2,13],14:[2,13],15:[2,13],16:[2,13],18:[2,13]},{5:[2,14],6:[2,14],7:[2,14],8:[2,14],9:[2,14],10:[2,14],11:[2,14],12:[2,14],13:[2,14],14:[2,14],15:[2,14],16:[2,14],18:[2,14]},{1:[2,1]},{4:18,17:[1,3],19:[1,4],20:[1,5]},{4:19,17:[1,3],19:[1,4],20:[1,5]},{4:20,17:[1,3],19:[1,4],20:[1,5]},{4:21,17:[1,3],19:[1,4],20:[1,5]},{4:22,17:[1,3],19:[1,4],20:[1,5]},{4:23,17:[1,3],19:[1,4],20:[1,5]},{4:24,17:[1,3],19:[1,4],20:[1,5]},{4:25,17:[1,3],19:[1,4],20:[1,5]},{4:26,17:[1,3],19:[1,4],20:[1,5]},{4:27,17:[1,3],19:[1,4],20:[1,5]},{6:[1,7],8:[1,8],9:[1,9],10:[1,10],11:[1,11],12:[1,12],13:[1,13],14:[1,14],15:[1,15],16:[1,16],18:[1,28]},{6:[1,7],7:[1,29],8:[1,8],9:[1,9],10:[1,10],11:[1,11],12:[1,12],13:[1,13],14:[1,14],15:[1,15],16:[1,16]},{5:[2,3],6:[2,3],7:[2,3],8:[2,3],9:[1,9],10:[1,10],11:[1,11],12:[1,12],13:[1,13],14:[1,14],15:[1,15],16:[1,16],18:[2,3]},{5:[2,4],6:[2,4],7:[2,4],8:[2,4],9:[2,4],10:[1,10],11:[1,11],12:[1,12],13:[1,13],14:[1,14],15:[1,15],16:[1,16],18:[2,4]},{5:[2,5],6:[2,5],7:[2,5],8:[2,5],9:[2,5],10:[2,5],11:[2,5],12:[2,5],13:[2,5],14:[2,5],15:[2,5],16:[1,16],18:[2,5]},{5:[2,6],6:[2,6],7:[2,6],8:[2,6],9:[2,6],10:[2,6],11:[2,6],12:[2,6],13:[2,6],14:[2,6],15:[2,6],16:[1,16],18:[2,6]},{5:[2,7],6:[2,7],7:[2,7],8:[2,7],9:[2,7],10:[2,7],11:[2,7],12:[2,7],13:[2,7],14:[2,7],15:[2,7],16:[1,16],18:[2,7]},{5:[2,8],6:[2,8],7:[2,8],8:[2,8],9:[2,8],10:[2,8],11:[2,8],12:[2,8],13:[2,8],14:[2,8],15:[2,8],16:[1,16],18:[2,8]},{5:[2,9],6:[2,9],7:[2,9],8:[2,9],9:[2,9],10:[2,9],11:[2,9],12:[2,9],13:[2,9],14:[2,9],15:[2,9],16:[1,16],18:[2,9]},{5:[2,10],6:[2,10],7:[2,10],8:[2,10],9:[2,10],10:[2,10],11:[2,10],12:[2,10],13:[2,10],14:[2,10],15:[2,10],16:[1,16],18:[2,10]},{5:[2,11],6:[2,11],7:[2,11],8:[2,11],9:[2,11],10:[2,11],11:[2,11],12:[2,11],13:[2,11],14:[2,11],15:[2,11],16:[2,11],18:[2,11]},{5:[2,12],6:[2,12],7:[2,12],8:[2,12],9:[2,12],10:[2,12],11:[2,12],12:[2,12],13:[2,12],14:[2,12],15:[2,12],16:[2,12],18:[2,12]},{4:30,17:[1,3],19:[1,4],20:[1,5]},{5:[2,2],6:[1,7],7:[2,2],8:[1,8],9:[1,9],10:[1,10],11:[1,11],12:[1,12],13:[1,13],14:[1,14],15:[1,15],16:[1,16],18:[2,2]}],defaultActions:{6:[2,1]},parseError:function(t){throw new Error(t)},parse:function(t){var e=this,n=[0],i=[null],r=[],o=this.table,s="",a=0,l=0,u=0;this.lexer.setInput(t),this.lexer.yy=this.yy,this.yy.lexer=this.lexer,void 0===this.lexer.yylloc&&(this.lexer.yylloc={});var c=this.lexer.yylloc;function h(){var t;return"number"!=typeof(t=e.lexer.lex()||1)&&(t=e.symbols_[t]||t),t}r.push(c),"function"==typeof this.yy.parseError&&(this.parseError=this.yy.parseError);for(var d,f,p,g,v,m,y,b,w,_,x={};;){if(p=n[n.length-1],void 0===(g=this.defaultActions[p]?this.defaultActions[p]:(null==d&&(d=h()),o[p]&&o[p][d]))||!g.length||!g[0]){if(!u){for(m in w=[],o[p])this.terminals_[m]&&2<m&&w.push("'"+this.terminals_[m]+"'");var S="";S=this.lexer.showPosition?"Parse error on line "+(a+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+w.join(", ")+", got '"+this.terminals_[d]+"'":"Parse error on line "+(a+1)+": Unexpected "+(1==d?"end of input":"'"+(this.terminals_[d]||d)+"'"),this.parseError(S,{text:this.lexer.match,token:this.terminals_[d]||d,line:this.lexer.yylineno,loc:c,expected:w})}if(3==u){if(1==d)throw new Error(S||"Parsing halted.");l=this.lexer.yyleng,s=this.lexer.yytext,a=this.lexer.yylineno,c=this.lexer.yylloc,d=h()}for(;!(2..toString()in o[p]);){if(0==p)throw new Error(S||"Parsing halted.");_=1,n.length=n.length-2*_,i.length=i.length-_,r.length=r.length-_,p=n[n.length-1]}f=d,d=2,g=o[p=n[n.length-1]]&&o[p][2],u=3}if(g[0]instanceof Array&&1<g.length)throw new Error("Parse Error: multiple actions possible at state: "+p+", token: "+d);switch(g[0]){case 1:n.push(d),i.push(this.lexer.yytext),r.push(this.lexer.yylloc),n.push(g[1]),d=null,f?(d=f,f=null):(l=this.lexer.yyleng,s=this.lexer.yytext,a=this.lexer.yylineno,c=this.lexer.yylloc,0<u&&u--);break;case 2:if(y=this.productions_[g[1]][1],x.$=i[i.length-y],x._$={first_line:r[r.length-(y||1)].first_line,last_line:r[r.length-1].last_line,first_column:r[r.length-(y||1)].first_column,last_column:r[r.length-1].last_column},void 0!==(v=this.performAction.call(x,s,l,a,this.yy,g[1],i,r)))return v;y&&(n=n.slice(0,-1*y*2),i=i.slice(0,-1*y),r=r.slice(0,-1*y)),n.push(this.productions_[g[1]][0]),i.push(x.$),r.push(x._$),b=o[n[n.length-2]][n[n.length-1]],n.push(b);break;case 3:return!0}}return!0}}).lexer={EOF:1,parseError:function(t,e){if(!this.yy.parseError)throw new Error(t);this.yy.parseError(t,e)},setInput:function(t){return this._input=t,this._more=this._less=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.match+=t,this.matched+=t,t.match(/\n/)&&this.yylineno++,this._input=this._input.slice(1),t},unput:function(t){return this._input=t+this._input,this},more:function(){return this._more=!0,this},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(20<t.length?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(20<t.length?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},next:function(){if(this.done)return this.EOF;var t,e;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var n=this._currentRules(),i=0;i<n.length;i++)if(t=this._input.match(this.rules[n[i]]))return(e=t[0].match(/\n.*/g))&&(this.yylineno+=e.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:e?e[e.length-1].length-1:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this._more=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],this.performAction.call(this,this.yy,this,n[i],this.conditionStack[this.conditionStack.length-1])||void 0;if(""===this._input)return this.EOF;this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var t=this.next();return void 0!==t?t:this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.pop()},_currentRules:function(){return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules},topState:function(){return this.conditionStack[this.conditionStack.length-2]},pushState:function(t){this.begin(t)},performAction:function(t,e,n){switch(n){case 0:break;case 1:return 20;case 2:return 19;case 3:return 8;case 4:return 9;case 5:return 6;case 6:return 7;case 7:return 11;case 8:return 13;case 9:return 10;case 10:return 12;case 11:return 14;case 12:return 15;case 13:return 16;case 14:return 17;case 15:return 18;case 16:return 5;case 17:return"INVALID"}},rules:[/^\s+/,/^[0-9]+(\.[0-9]+)?\b/,/^n\b/,/^\|\|/,/^&&/,/^\?/,/^:/,/^<=/,/^>=/,/^</,/^>/,/^!=/,/^==/,/^%/,/^\(/,/^\)/,/^$/,/^./],conditions:{INITIAL:{rules:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17],inclusive:!0}}},h),"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=p),exports.Jed=p):("function"==typeof define&&define.amd&&define(function(){return p}),t.Jed=p)}(this),function(a){function l(){}var e=1;function u(t){return{jsonrpc:"2.0",method:t.method||"",params:t.params||{},id:e++}}function c(t,e){return t.id<e.id?-1:1}a.extend({jsonrpc:function(t,e){var o=new a.Deferred,n=(e=e||{}).success||l,s=e.error||l;delete e.success,delete e.error;var i=a.isArray(t),r=a.extend({url:(i?t[0].url:t.url)||a.jsonrpc.defaultUrl,contentType:"application/json",dataType:"text",dataFilter:function(t,e){return JSON.parse(t)},type:"POST",processData:!1,data:function(t){var e=a.isArray(t)?t.map(u):u(t);return JSON.stringify(e)}(t),success:function(t){if(i){var e=function(t){return t.sort(c)}(t);return n(e),void o.resolve(e)}if(t.hasOwnProperty("error"))return s(t.error),void o.reject(t.error);if(t.hasOwnProperty("result"))return n(t.result),void o.resolve(t.result);throw"Invalid response returned"},error:function(e,n,i){var r=null;if("timeout"===i)r={status:n,code:-32e3,message:"Request Timeout",data:null};else try{r=JSON.parse(e.responseText).error}catch(t){r={status:n,code:-32603,message:i,data:e.responseText}}s(r),o.reject(r)}},e);return a.ajax(r),o.promise()}}),a.jsonrpc.defaultUrl="/jsonrpc"}(jQuery),function(a,i,r,l){var u=a(i);a.fn.lazyload=function(t){function e(){var e=0;o.each(function(){var t=a(this);if((!s.skip_invisible||t.is(":visible"))&&!a.abovethetop(this,s)&&!a.leftofbegin(this,s))if(a.belowthefold(this,s)||a.rightoffold(this,s)){if(++e>s.failure_limit)return!1}else t.trigger("appear"),e=0})}var n,o=this,s={threshold:0,failure_limit:0,event:"scroll",effect:"show",container:i,data_attribute:"original",skip_invisible:!0,appear:null,load:null,placeholder:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC"};return t&&(l!==t.failurelimit&&(t.failure_limit=t.failurelimit,delete t.failurelimit),l!==t.effectspeed&&(t.effect_speed=t.effectspeed,delete t.effectspeed),a.extend(s,t)),n=s.container===l||s.container===i?u:a(s.container),0===s.event.indexOf("scroll")&&n.bind(s.event,function(){return e()}),this.each(function(){var i=this,r=a(i);i.loaded=!1,r.attr("src")!==l&&!1!==r.attr("src")||!r.is("img")||r.attr("src",s.placeholder),r.one("appear",function(){if(!this.loaded){if(s.appear){var t=o.length;s.appear.call(i,t,s)}a("<img />").bind("load",function(){var t=r.attr("data-"+s.data_attribute);r.hide(),r.is("img")?r.attr("src",t):r.css("background-image","url('"+t+"')"),r[s.effect](s.effect_speed),i.loaded=!0;var e=a.grep(o,function(t){return!t.loaded});if(o=a(e),s.load){var n=o.length;s.load.call(i,n,s)}}).attr("src",r.attr("data-"+s.data_attribute))}}),0!==s.event.indexOf("scroll")&&r.bind(s.event,function(){i.loaded||r.trigger("appear")})}),u.bind("resize",function(){e()}),/(?:iphone|ipod|ipad).*os 5/gi.test(navigator.appVersion)&&u.bind("pageshow",function(t){t.originalEvent&&t.originalEvent.persisted&&o.each(function(){a(this).trigger("appear")})}),a(r).ready(function(){e()}),this},a.belowthefold=function(t,e){return(e.container===l||e.container===i?(i.innerHeight?i.innerHeight:u.height())+u.scrollTop():a(e.container).offset().top+a(e.container).height())<=a(t).offset().top-e.threshold},a.rightoffold=function(t,e){return(e.container===l||e.container===i?u.width()+u.scrollLeft():a(e.container).offset().left+a(e.container).width())<=a(t).offset().left-e.threshold},a.abovethetop=function(t,e){return(e.container===l||e.container===i?u.scrollTop():a(e.container).offset().top)>=a(t).offset().top+e.threshold+a(t).height()},a.leftofbegin=function(t,e){return(e.container===l||e.container===i?u.scrollLeft():a(e.container).offset().left)>=a(t).offset().left+e.threshold+a(t).width()},a.inviewport=function(t,e){return!(a.rightoffold(t,e)||a.leftofbegin(t,e)||a.belowthefold(t,e)||a.abovethetop(t,e))},a.extend(a.expr[":"],{"below-the-fold":function(t){return a.belowthefold(t,{threshold:0})},"above-the-top":function(t){return!a.belowthefold(t,{threshold:0})},"right-of-screen":function(t){return a.rightoffold(t,{threshold:0})},"left-of-screen":function(t){return!a.rightoffold(t,{threshold:0})},"in-viewport":function(t){return a.inviewport(t,{threshold:0})},"above-the-fold":function(t){return!a.belowthefold(t,{threshold:0})},"right-of-fold":function(t){return a.rightoffold(t,{threshold:0})},"left-of-fold":function(t){return!a.rightoffold(t,{threshold:0})}})}(jQuery,window,document),function(w){"use strict";function r(t){return"number"==typeof t&&!isNaN(t)&&isFinite(t)}function _(t,e,n){t.addClass(e),setTimeout(function(){t.removeClass(e)},n)}function x(t){return Math.max(Math.min(t,100),0)}function S(t){return w.isArray(t)?t:[t]}var T=w(document),e=w.fn.val,C=".nui",E=window.navigator.pointerEnabled?{start:"pointerdown",move:"pointermove",end:"pointerup"}:window.navigator.msPointerEnabled?{start:"MSPointerDown",move:"MSPointerMove",end:"MSPointerUp"}:{start:"mousedown touchstart",move:"mousemove touchmove",end:"mouseup touchend"},k=["noUi-target","noUi-base","noUi-origin","noUi-handle","noUi-horizontal","noUi-vertical","noUi-background","noUi-connect","noUi-ltr","noUi-rtl","noUi-dragable","","noUi-state-drag","","noUi-state-tap","noUi-active","","noUi-stacking"];function l(t,e){return 100/(e-t)}function u(t,e){return 100*e/(t[1]-t[0])}function c(t,e){for(var n=1;t>=e[n];)n+=1;return n}function n(t,e,n){if(n>=t.slice(-1)[0])return 100;var i,r,o,s,a=c(n,t);return i=t[a-1],r=t[a],o=e[a-1],s=e[a],o+function(t,e){return u(t,t[0]<0?e+Math.abs(t[0]):e-t[0])}([i,r],n)/l(o,s)}function i(t,e,n){if(100<=n)return t.slice(-1)[0];var i,r=c(n,e);return function(t,e){return e*(t[1]-t[0])/100+t[0]}([t[r-1],t[r]],(n-(i=e[r-1]))*l(i,e[r]))}function o(t,e,n,i){if(100===i)return i;var r,o,s=c(i,t);return n?(r=t[s-1],((o=t[s])-r)/2<i-r?o:r):e[s-1]?t[s-1]+function(t,e){return Math.round(t/e)*e}(i-t[s-1],e[s-1]):i}function s(t,e,n){var i;if("number"==typeof e&&(e=[e]),"[object Array]"!==Object.prototype.toString.call(e))throw new Error("noUiSlider: 'range' contains invalid value.");if(!r(i="min"===t?0:"max"===t?100:parseFloat(t))||!r(e[0]))throw new Error("noUiSlider: 'range' value isn't numeric.");n.xPct.push(i),n.xVal.push(e[0]),i?n.xSteps.push(!isNaN(e[1])&&e[1]):isNaN(e[1])||(n.xSteps[0]=e[1])}function a(t,e,n){if(!e)return!0;n.xSteps[t]=u([n.xVal[t],n.xVal[t+1]],e)/l(n.xPct[t],n.xPct[t+1])}function h(t,e,n,i){this.xPct=[],this.xVal=[],this.xSteps=[i||!1],this.xNumSteps=[!1],this.snap=e,this.direction=n;var r,o=[];for(r in t)t.hasOwnProperty(r)&&o.push([t[r],r]);for(o.sort(function(t,e){return t[0]-e[0]}),r=0;r<o.length;r++)s(o[r][1],o[r][0],this);for(this.xNumSteps=this.xSteps.slice(0),r=0;r<this.xNumSteps.length;r++)a(r,this.xNumSteps[r],this)}h.prototype.getMargin=function(t){return 2===this.xPct.length&&u(this.xVal,t)},h.prototype.toStepping=function(t){return t=n(this.xVal,this.xPct,t),this.direction&&(t=100-t),t},h.prototype.fromStepping=function(t){return this.direction&&(t=100-t),function(t){var e=Math.pow(10,7);return Number((Math.round(t*e)/e).toFixed(7))}(i(this.xVal,this.xPct,t))},h.prototype.getStep=function(t){return this.direction&&(t=100-t),t=o(this.xPct,this.xSteps,this.snap,t),this.direction&&(t=100-t),t},h.prototype.getApplicableStep=function(t){var e=c(t,this.xPct),n=100===t?2:1;return[this.xNumSteps[e-2],this.xVal[e-n],this.xNumSteps[e-n]]},h.prototype.convert=function(t){return this.getStep(this.toStepping(t))};var d={to:function(t){return t.toFixed(2)},from:Number};function f(t,e){if(!r(e))throw new Error("noUiSlider: 'step' is not numeric.");t.singleStep=e}function p(t,e){if("object"!=typeof e||w.isArray(e))throw new Error("noUiSlider: 'range' is not an object.");if(void 0===e.min||void 0===e.max)throw new Error("noUiSlider: Missing 'min' or 'max' in 'range'.");t.spectrum=new h(e,t.snap,t.dir,t.singleStep)}function g(t,e){if(e=S(e),!w.isArray(e)||!e.length||2<e.length)throw new Error("noUiSlider: 'start' option is incorrect.");t.handles=e.length,t.start=e}function v(t,e){if("boolean"!=typeof(t.snap=e))throw new Error("noUiSlider: 'snap' option must be a boolean.")}function m(t,e){if("boolean"!=typeof(t.animate=e))throw new Error("noUiSlider: 'animate' option must be a boolean.")}function y(t,e){if("lower"===e&&1===t.handles)t.connect=1;else if("upper"===e&&1===t.handles)t.connect=2;else if(!0===e&&2===t.handles)t.connect=3;else{if(!1!==e)throw new Error("noUiSlider: 'connect' option doesn't match handle count.");t.connect=0}}function b(t,e){switch(e){case"horizontal":t.ort=0;break;case"vertical":t.ort=1;break;default:throw new Error("noUiSlider: 'orientation' option is invalid.")}}function M(t,e){if(!r(e))throw new Error("noUiSlider: 'margin' option must be numeric.");if(t.margin=t.spectrum.getMargin(e),!t.margin)throw new Error("noUiSlider: 'margin' option is only supported on linear sliders.")}function D(t,e){if(!r(e))throw new Error("noUiSlider: 'limit' option must be numeric.");if(t.limit=t.spectrum.getMargin(e),!t.limit)throw new Error("noUiSlider: 'limit' option is only supported on linear sliders.")}function O(t,e){switch(e){case"ltr":t.dir=0;break;case"rtl":t.dir=1,t.connect=[0,2,1,3][t.connect];break;default:throw new Error("noUiSlider: 'direction' option was not recognized.")}}function A(t,e){if("string"!=typeof e)throw new Error("noUiSlider: 'behaviour' must be a string containing options.");var n=0<=e.indexOf("tap"),i=0<=e.indexOf("drag"),r=0<=e.indexOf("fixed"),o=0<=e.indexOf("snap");t.events={tap:n||o,drag:i,fixed:r,snap:o}}function N(t,e){if("function"==typeof(t.format=e).to&&"function"==typeof e.from)return!0;throw new Error("noUiSlider: 'format' requires 'to' and 'from' methods.")}function j(t,l,e){var r,u,o=w(t),c=[-1,-1],h=l.spectrum,d=[],f=["lower","upper"].slice(0,l.handles);function a(){return r[["width","height"][l.ort]]()}function p(t){var e,n=[o.val()];for(e=0;e<t.length;e+=1)o.trigger(t[e],n)}function n(t){return 1===t.length?t[0]:l.dir?t.reverse():t}function g(t){var e=w.inArray(t,f);o[0].linkAPI&&o[0].linkAPI[t]&&o[0].linkAPI[t].change(d[e],u[e].children(),o)}function i(t,e,n,i){return t=t.replace(/\s/g,C+" ")+C,e.on(t,function(t){return!o.attr("disabled")&&(!o.hasClass(k[14])&&((t=function(t){t.preventDefault();var e,n,i=0===t.type.indexOf("touch"),r=0===t.type.indexOf("mouse"),o=0===t.type.indexOf("pointer"),s=t;return 0===t.type.indexOf("MSPointer")&&(o=!0),t.originalEvent&&(t=t.originalEvent),i&&(e=t.changedTouches[0].pageX,n=t.changedTouches[0].pageY),(r||o)&&(o||void 0!==window.pageXOffset||(window.pageXOffset=document.documentElement.scrollLeft,window.pageYOffset=document.documentElement.scrollTop),e=t.clientX+window.pageXOffset,n=t.clientY+window.pageYOffset),s.points=[e,n],s.cursor=r,s}(t)).calcPoint=t.points[l.ort],void n(t,i)))})}function s(t,e){var n,i=e.handles||u,r=!1,o=100*(t.calcPoint-e.start)/a(),s=i[0][0]!==u[0][0]?1:0;n=function(t,e,n){var i=t+e[0],r=t+e[1];return n?(i<0&&(r+=Math.abs(i)),100<r&&(i-=r-100),[x(i),x(r)]):[i,r]}(o,e.positions,1<i.length),r=b(i[0],n[s],1===i.length),1<i.length&&(r=b(i[1],n[s?0:1],!1)||r),r&&p(["slide"])}function v(t){w("."+k[15]).removeClass(k[15]),t.cursor&&w("body").css("cursor","").off(C),T.off(C),o.removeClass(k[12]),p(["set","change"])}function m(t,e){1===e.handles.length&&e.handles[0].children().addClass(k[15]),t.stopPropagation(),i(E.move,T,s,{start:t.calcPoint,handles:e.handles,positions:[c[0],c[u.length-1]]}),i(E.end,T,v,null),t.cursor&&(w("body").css("cursor",w(t.target).css("cursor")),1<u.length&&o.addClass(k[12]),w("body").on("selectstart"+C,!1))}function y(t){var e,n=t.calcPoint,i=0;t.stopPropagation(),w.each(u,function(){i+=this.offset()[l.style]}),i=n<i/2||1===u.length?0:1,e=100*(n-=r.offset()[l.style])/a(),l.events.snap||_(o,k[14],300),b(u[i],e),p(["slide","set","change"]),l.events.snap&&m(t,{handles:[u[i]]})}function b(t,e,n){var i=t[0]!==u[0][0]?1:0,r=c[0]+l.margin,o=c[1]-l.margin,s=c[0]+l.limit,a=c[1]-l.limit;return 1<u.length&&(e=i?Math.max(e,r):Math.min(e,o)),!1!==n&&l.limit&&1<u.length&&(e=i?Math.min(e,s):Math.max(e,a)),e=h.getStep(e),(e=x(parseFloat(e.toFixed(7))))!==c[i]&&(t.css(l.style,e+"%"),t.is(":first-child")&&t.toggleClass(k[17],50<e),c[i]=e,d[i]=h.fromStepping(e),g(f[i]),!0)}if(l.dir&&f.reverse(),t.LinkUpdate=g,t.LinkConfirm=function(t,e){var n=w.inArray(t,f);return e&&e.appendTo(u[n].children()),l.dir&&1<l.handles&&(n=1===n?0:1),function(n){return function(t,e){o.val([n?null:e,n?e:null],!0)}}(n)},t.LinkDefaultFormatter=l.format,t.LinkDefaultFlag="lower",t.reappend=function(){var t,e;for(t=0;t<f.length;t+=1)this.linkAPI&&this.linkAPI[e=f[t]]&&this.linkAPI[e].reconfirm(e)},o.hasClass(k[0]))throw new Error("Slider was already initialized.");r=function(t,e,n){return n.addClass([k[0],k[8+t],k[4+e]].join(" ")),w("<div/>").appendTo(n).addClass(k[1])}(l.dir,l.ort,o),u=function(t,e,n){var i,r,o,s,a,l=[];for(i=0;i<t;i+=1)l.push((r=e,o=i,a=s=void 0,s=w("<div><div/></div>").addClass(k[2]),a=["-lower","-upper"],r&&a.reverse(),s.children().addClass(k[3]+" "+k[3]+a[o]),s).appendTo(n));return l}(l.handles,l.dir,r),function(t,e,n){switch(t){case 1:e.addClass(k[7]),n[0].addClass(k[6]);break;case 3:n[1].addClass(k[6]);case 2:n[0].addClass(k[7]);case 0:e.addClass(k[6])}}(l.connect,o,u),function(t){var e,n;if(!t.fixed)for(e=0;e<u.length;e+=1)i(E.start,u[e].children(),m,{handles:[u[e]]});t.tap&&i(E.start,r,y,{handles:u}),t.drag&&(n=r.find("."+k[7]).addClass(k[10]),t.fixed&&(n=n.add(r.children().not(n).children())),i(E.start,n,m,{handles:u}))}(l.events),t.vSet=function(t){if(o[0].LinkIsEmitting)return this;var e,n=S(t);return l.dir&&1<l.handles&&n.reverse(),l.animate&&-1!==c[0]&&_(o,k[14],300),e=1<u.length?3:1,1===n.length&&(e=1),function(t,e){var n,i,r;for(l.limit&&(t+=1),n=0;n<t;n+=1)null!==(r=e[i=n%2])&&!1!==r&&("number"==typeof r&&(r=String(r)),!1!==(r=l.format.from(r))&&!isNaN(r)&&!1!==b(u[i],h.toStepping(r),n===3-l.dir)||g(f[i]))}(e,n),p(["set"]),this},t.vGet=function(){var t,e=[];for(t=0;t<l.handles;t+=1)e[t]=l.format.to(d[t]);return n(e)},t.destroy=function(){return w(this).off(C).removeClass(k.join(" ")).empty(),delete this.LinkUpdate,delete this.LinkConfirm,delete this.LinkDefaultFormatter,delete this.LinkDefaultFlag,delete this.reappend,delete this.vGet,delete this.vSet,delete this.getCurrentStep,delete this.getInfo,delete this.destroy,e},t.getCurrentStep=function(){return n(w.map(c,function(t,e){var n=h.getApplicableStep(t),i=function(t){var e=t.split(".");return 1<e.length?e[1].length:0}(String(n[2])),r=d[e],o=100===t?null:n[2],s=Number((r-n[2]).toFixed(i));return[[0===t?null:s>=n[1]?n[2]:n[0]||!1,o]]}))},t.getOriginalOptions=function(){return e},t.getInfo=function(){return[h,l.style,l.ort]},o.val(l.start)}function R(t){var e=function(n){var t,i={margin:0,limit:0,animate:!0,format:d};return t={step:{r:!1,t:f},start:{r:!0,t:g},connect:{r:!0,t:y},direction:{r:!0,t:O},snap:{r:!1,t:v},animate:{r:!1,t:m},range:{r:!0,t:p},orientation:{r:!1,t:b},margin:{r:!1,t:M},limit:{r:!1,t:D},behaviour:{r:!0,t:A},format:{r:!1,t:N}},n=w.extend({connect:!1,direction:"ltr",behaviour:"tap",orientation:"horizontal"},n),w.each(t,function(t,e){if(void 0===n[t]){if(e.r)throw new Error("noUiSlider: '"+t+"' is required.");return!0}e.t(i,n[t])}),i.style=i.ort?"top":"left",i}(t);return this.each(function(){j(this,e,t)})}function L(){return this[0][arguments.length?"vSet":"vGet"].apply(this[0],arguments)}w.fn.val=function(i){function r(t){return t.hasClass(k[0])?L:e}if(!arguments.length){var t=w(this[0]);return r(t).call(t)}var o=w.isFunction(i);return this.each(function(t){var e=i,n=w(this);o&&(e=i.call(this,t,n.val())),r(n).call(n,e)})},w.fn.noUiSlider=function(t,e){switch(t){case"step":return this[0].getCurrentStep();case"options":return this[0].getOriginalOptions()}return(e?function(i){return this.each(function(){if(this.destroy){var t=w(this).val(),e=this.destroy(),n=w.extend({},e,i);w(this).noUiSlider(n),this.reappend(),e.start===n.start&&w(this).val(t)}else w(this).noUiSlider(i)})}:R).call(this,t)}}(window.jQuery||window.Zepto),function(t){"use strict";"object"==typeof exports?t(require("jquery")):"function"==typeof define&&define.amd?define(["jquery"],t):t(jQuery)}(function(n){"use strict";function i(t){if("string"!=typeof(t=t||"once"))throw new Error("The jQuery Once id parameter must be a string");return t}n.fn.once=function(t){var e="jquery-once-"+i(t);return this.filter(function(){return!0!==n(this).data(e)}).data(e,!0)},n.fn.removeOnce=function(t){return this.findOnce(t).removeData("jquery-once-"+i(t))},n.fn.findOnce=function(t){var e="jquery-once-"+i(t);return this.filter(function(){return!0===n(this).data(e)})}}),function(t){var M=t(window);t.fn.visible=function(t,e,n){if(!(this.length<1)){var i=1<this.length?this.eq(0):this,r=i.get(0),o=M.width(),s=M.height(),a=(n=n||"both",!0!==e||r.offsetWidth*r.offsetHeight);if("function"==typeof r.getBoundingClientRect){var l=r.getBoundingClientRect(),u=0<=l.top&&l.top<s,c=0<l.bottom&&l.bottom<=s,h=0<=l.left&&l.left<o,d=0<l.right&&l.right<=o,f=t?u||c:u&&c,p=t?h||d:h&&d;if("both"===n)return a&&f&&p;if("vertical"===n)return a&&f;if("horizontal"===n)return a&&p}else{var g=M.scrollTop(),v=g+s,m=M.scrollLeft(),y=m+o,b=i.offset(),w=b.top,_=w+i.height(),x=b.left,S=x+i.width(),T=!0===t?_:w,C=!0===t?w:_,E=!0===t?S:x,k=!0===t?x:S;if("both"===n)return!!a&&C<=v&&g<=T&&k<=y&&m<=E;if("vertical"===n)return!!a&&C<=v&&g<=T;if("horizontal"===n)return!!a&&k<=y&&m<=E}}}}(jQuery),window.JST||(window.JST={});var prettyPrint=function(){var p={el:function(t,e){var n,i=document.createElement(t);if((e=p.merge({},e))&&e.style){e.style;p.applyCSS(i,e.style),delete e.style}for(n in e)e.hasOwnProperty(n)&&(i[n]=e[n]);return i},applyCSS:function(t,e){for(var n in e)if(e.hasOwnProperty(n))try{t.style[n]=e[n]}catch(t){}},txt:function(t){return document.createTextNode(t)},row:function(t,e,n){n=n||"td";var i,r=p.count(t,null)+1,o=p.el("tr"),s={style:p.getStyles(n,e),colSpan:r,onmouseover:function(){var t=this.parentNode.childNodes;p.forEach(t,function(t){"td"===t.nodeName.toLowerCase()&&p.applyCSS(t,p.getStyles("td_hover",e))})},onmouseout:function(){var t=this.parentNode.childNodes;p.forEach(t,function(t){"td"===t.nodeName.toLowerCase()&&p.applyCSS(t,p.getStyles("td",e))})}};return p.forEach(t,function(t){null!==t&&(i=p.el(n,s),t.nodeType?i.appendChild(t):i.innerHTML=p.shorten(t.toString()),o.appendChild(i))}),o},hRow:function(t,e){return p.row(t,e,"th")},table:function(t,i){t=t||[];var e={thead:{style:p.getStyles("thead",i)},tbody:{style:p.getStyles("tbody",i)},table:{style:p.getStyles("table",i)}},n=p.el("table",e.table),r=p.el("thead",e.thead),o=p.el("tbody",e.tbody);return t.length&&(n.appendChild(r),r.appendChild(p.hRow(t,i))),n.appendChild(o),{node:n,tbody:o,thead:r,appendChild:function(t){this.tbody.appendChild(t)},addRow:function(t,e,n){return this.appendChild(p.row.call(p,t,e||i,n)),this}}},shorten:function(t){return t},htmlentities:function(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")},merge:function(t,e){for(var n in"object"!=typeof t&&(t={}),e)if(e.hasOwnProperty(n)){var i=e[n];if("object"==typeof i){t[n]=p.merge(t[n],i);continue}t[n]=i}for(var r=2,o=arguments.length;r<o;r++)p.merge(t,arguments[r]);return t},count:function(t,e){for(var n=0,i=0,r=t.length;i<r;i++)t[i]===e&&n++;return n},thead:function(t){return t.getElementsByTagName("thead")[0]},forEach:function(t,e,n){n=n||e;for(var i=t.length,r=-1;++r<i&&!1!==n(t[r],r,t););return!0},type:function(t){try{if(null===t)return"null";if(void 0===t)return"undefined";var e=Object.prototype.toString.call(t).match(/\s(.+?)\]/)[1].toLowerCase();return t.nodeType?1===t.nodeType?"domelement":"domnode":/^(string|number|array|regexp|function|date|boolean)$/.test(e)?e:"object"==typeof t?t.jquery&&"string"==typeof t.jquery?"jquery":"object":t===window||t===document?"object":"default"}catch(t){return"default"}},within:function(n){return{is:function(t){for(var e in n)if(n[e]===t)return e;return""}}},common:{circRef:function(t,e,n){return p.expander("[POINTS BACK TO <strong>"+e+"</strong>]","Click to show this item anyway",function(){this.parentNode.appendChild(i(t,{maxDepth:1}))})},depthReached:function(t,e){return p.expander("[DEPTH REACHED]","Click to show this item anyway",function(){try{this.parentNode.appendChild(i(t,{maxDepth:1}))}catch(t){this.parentNode.appendChild(p.table(["ERROR OCCURED DURING OBJECT RETRIEVAL"],"error").addRow([t.message]).node)}})}},getStyles:function(t,e){return e=i.settings.styles[e]||{},p.merge({},i.settings.styles.default[t],e[t])},expander:function(t,e,n){return p.el("a",{innerHTML:p.shorten(t)+' <b style="visibility:hidden;">[+]</b>',title:e,onmouseover:function(){this.getElementsByTagName("b")[0].style.visibility="visible"},onmouseout:function(){this.getElementsByTagName("b")[0].style.visibility="hidden"},onclick:function(){return this.style.display="none",n.call(this),!1},style:{cursor:"pointer"}})},stringify:function(t){var n,e=p.type(t),i=!0;if("array"===e)return n="[",p.forEach(t,function(t,e){n+=(0===e?"":", ")+p.stringify(t)}),n+"]";if("object"!=typeof t)return"regexp"===e?"/"+t.source+"/":"string"===e?'"'+t.replace(/"/g,'\\"')+'"':t.toString();for(var r in n="{",t)t.hasOwnProperty(r)&&(n+=(i?"":", ")+r+":"+p.stringify(t[r]),i=!1);return n+"}"},headerGradient:function(){var t=document.createElement("canvas");if(!t.getContext)return"";var e=t.getContext("2d");t.height=30,t.width=1;var n=e.createLinearGradient(0,0,0,30);return n.addColorStop(0,"rgba(0,0,0,0)"),n.addColorStop(1,"rgba(0,0,0,0.25)"),e.fillStyle=n,e.fillRect(0,0,1,30),"url("+(t.toDataURL&&t.toDataURL()||"")+")"}()},i=function(t,e){e=e||{};var c=p.merge({},i.config,e),n=p.el("div"),h=(i.config,{}),d=!1;i.settings=c;var f={string:function(t){return p.txt('"'+p.shorten(t.replace(/"/g,'\\"'))+'"')},number:function(t){return p.txt(t)},regexp:function(t){var e=p.table(["RegExp",null],"regexp"),n=p.table(),i=p.expander("/"+t.source+"/","Click to show more",function(){this.parentNode.appendChild(e.node)});return n.addRow(["g",t.global]).addRow(["i",t.ignoreCase]).addRow(["m",t.multiline]),e.addRow(["source","/"+t.source+"/"]).addRow(["flags",n.node]).addRow(["lastIndex",t.lastIndex]),c.expanded?e.node:i},domelement:function(e,t){var n=p.table(["DOMElement",null],"domelement"),i=e.nodeName||"";return n.addRow(["tag","&lt;"+i.toLowerCase()+"&gt;"]),p.forEach(["id","className","innerHTML","src","href"],function(t){e[t]&&n.addRow([t,p.htmlentities(e[t])])}),c.expanded?n.node:p.expander("DOMElement ("+i.toLowerCase()+")","Click to show more",function(){this.parentNode.appendChild(n.node)})},domnode:function(t){var e=p.table(["DOMNode",null],"domelement"),n=p.htmlentities((t.data||"UNDEFINED").replace(/\n/g,"\\n"));return e.addRow(["nodeType",t.nodeType+" ("+t.nodeName+")"]).addRow(["data",n]),c.expanded?e.node:p.expander("DOMNode","Click to show more",function(){this.parentNode.appendChild(e.node)})},jquery:function(t,e,n){return f.array(t,e,n,!0)},object:function(t,e,n){var i=p.within(h).is(t);if(i)return p.common.circRef(t,i,c);if(h[n||"TOP"]=t,e===c.maxDepth)return p.common.depthReached(t,c);var r=p.table(["Object",null],"object"),o=!0;for(var s in t)if(!t.hasOwnProperty||t.hasOwnProperty(s)){var a=t[s],l=p.type(a);o=!1;try{r.addRow([s,f[l](a,e+1,s)],l)}catch(t){window.console&&window.console.log&&console.log(t.message)}}o?r.addRow(["<small>[empty]</small>"]):r.thead.appendChild(p.hRow(["key","value"],"colHeader"));var u=c.expanded||d?r.node:p.expander(p.stringify(t),"Click to show more",function(){this.parentNode.appendChild(r.node)});return d=!0,u},array:function(n,i,t,e){var r=p.within(h).is(n);if(r)return p.common.circRef(n,r);if(h[t||"TOP"]=n,i===c.maxDepth)return p.common.depthReached(n);var o=e?"jQuery":"Array",s=p.table([o+"("+n.length+")",null],e?"jquery":o.toLowerCase()),a=!0,l=0;return e&&s.addRow(["selector",n.selector]),p.forEach(n,function(t,e){if(0<=c.maxArray&&++l>c.maxArray)return s.addRow([e+".."+(n.length-1),f[p.type(t)]("...",i+1,e)]),!1;a=!1,s.addRow([e,f[p.type(t)](t,i+1,e)])}),e||(a?s.addRow(["<small>[empty]</small>"]):s.thead.appendChild(p.hRow(["index","value"],"colHeader"))),c.expanded?s.node:p.expander(p.stringify(n),"Click to show more",function(){this.parentNode.appendChild(s.node)})},function:function(t,e,n){var i=p.within(h).is(t);if(i)return p.common.circRef(t,i);h[n||"TOP"]=t;var r=p.table(["Function",null],"function"),o=(p.table(["Arguments"]),t.toString().match(/\((.+?)\)/)),s=t.toString().match(/\(.*?\)\s+?\{?([\S\s]+)/)[1].replace(/\}?$/,"");return r.addRow(["arguments",o?o[1].replace(/[^\w_,\s]/g,""):"<small>[none/native]</small>"]).addRow(["body",s]),c.expanded?r.node:p.expander("function(){...}","Click to see more about this function.",function(){this.parentNode.appendChild(r.node)})},date:function(t){var e=p.table(["Date",null],"date"),n=t.toString().split(/\s/);return e.addRow(["Time",n[4]]).addRow(["Date",n.slice(0,4).join("-")]),c.expanded?e.node:p.expander("Date (timestamp): "+ +t,"Click to see a little more info about this date",function(){this.parentNode.appendChild(e.node)})},boolean:function(t){return p.txt(t.toString().toUpperCase())},undefined:function(){return p.txt("UNDEFINED")},null:function(){return p.txt("NULL")},default:function(){return p.txt("prettyPrint: TypeNotFound Error")}};return n.appendChild(f[c.forceObject?"object":p.type(t)](t,0)),n};return i.config={expanded:!0,forceObject:!1,maxDepth:3,maxArray:-1,styles:{array:{th:{backgroundColor:"#A4C18B",color:"white"}},function:{th:{backgroundColor:"#D82525"}},regexp:{th:{backgroundColor:"#E2F3FB",color:"#000"}},object:{th:{backgroundColor:"#8DA3AD"}},jquery:{th:{backgroundColor:"#FBF315"}},error:{th:{backgroundColor:"red",color:"yellow"}},domelement:{th:{backgroundColor:"#F3801E"}},date:{th:{backgroundColor:"#A725D8"}},colHeader:{th:{backgroundColor:"#EEE",color:"#aaa",textTransform:"uppercase",fontSize:"80%",padding:"2px 5px"}},default:{table:{borderCollapse:"collapse",width:"100%"},td:{padding:"5px",fontSize:"12px",backgroundColor:"rgba(255,255,255,0.5)",color:"#222",border:"1px solid #ddd",verticalAlign:"top",fontFamily:'"Consolas","Lucida Console",Courier,mono',whiteSpace:"nowrap"},td_hover:{},th:{padding:"5px",fontSize:"12px",backgroundColor:"#222",color:"#EEE",textAlign:"left",border:"1px solid #ddd",verticalAlign:"top",fontFamily:'"Consolas","Lucida Console",Courier,mono',backgroundRepeat:"repeat-x"}}}},i}();!function(t){"use strict";"function"==typeof define&&define.amd?define(t):"undefined"!=typeof module&&void 0!==module.exports?module.exports=t():"undefined"!=typeof Package?Sortable=t():window.Sortable=t()}(function(){"use strict";function l(t,e,n,i,r,o){var s=h.createEvent("Event");s.initEvent(e,!0,!0),s.item=n||t,s.from=i||t,s.clone=_,s.oldIndex=r,s.newIndex=o,t.dispatchEvent(s)}function s(){}var b,w,_,x,S,f,p,T,C,u,i,E,c,o,g={},k="Sortable"+(new Date).getTime(),v=window,h=v.document,a=v.parseInt,d=!!("draggable"in h.createElement("div")),M=!1,m="onAdd onUpdate onRemove onStart onEnd onFilter onSort".split(" "),y=Math.abs,r=[].slice,D=[],O=t(function(t,e,n){if(n&&e.scroll){var i,r,o,s,a=e.scrollSensitivity,l=e.scrollSpeed,u=t.clientX,c=t.clientY,h=window.innerWidth,d=window.innerHeight;if(p!==n&&(f=e.scroll,p=n,!0===f)){f=n;do{if(f.offsetWidth<f.scrollWidth||f.offsetHeight<f.scrollHeight)break}while(f=f.parentNode)}f&&(r=(i=f).getBoundingClientRect(),o=(y(r.right-u)<=a)-(y(r.left-u)<=a),s=(y(r.bottom-c)<=a)-(y(r.top-c)<=a)),o||s||(s=(d-c<=a)-(c<=a),((o=(h-u<=a)-(u<=a))||s)&&(i=v)),g.vx===o&&g.vy===s&&g.el===i||(g.el=i,g.vx=o,g.vy=s,clearInterval(g.pid),i&&(g.pid=setInterval(function(){i===v?v.scrollTo(v.scrollX+o*l,v.scrollY+s*l):(s&&(i.scrollTop+=s*l),o&&(i.scrollLeft+=o*l))},24)))}},30);function A(e,n){this.el=e,this.options=n=n||{};var t={group:Math.random(),sort:!0,disabled:!1,store:null,handle:null,scroll:!0,scrollSensitivity:30,scrollSpeed:10,draggable:/[uo]l/i.test(e.nodeName)?"li":">*",ghostClass:"sortable-ghost",ignore:"a, img",filter:null,animation:0,setData:function(t,e){t.setData("Text",e.textContent)},dropBubble:!1,dragoverBubble:!1};for(var i in t)i in n||(n[i]=t[i]);var r=n.group;for(var o in r&&"object"==typeof r||(r=n.group={name:r}),["pull","put"].forEach(function(t){t in r||(r[t]=!0)}),m.forEach(function(t){n[t]=j(this,n[t]||s),L(e,t.substr(2).toLowerCase(),n[t])},this),n.groups=" "+r.name+(r.put.join?" "+r.put.join(" "):"")+" ",e[k]=n,this)"_"===o.charAt(0)&&(this[o]=j(this,this[o]));L(e,"mousedown",this._onTapStart),L(e,"touchstart",this._onTapStart),L(e,"dragover",this),L(e,"dragenter",this),D.push(this._onDragOver),n.store&&this.sort(n.store.get(this))}function N(t){_&&_.state!==t&&(F(_,"display",t?"none":""),!t&&_.state&&x.insertBefore(_,b),_.state=t)}function j(t,e){var n=r.call(arguments,2);return e.bind?e.bind.apply(e,[t].concat(n)):function(){return e.apply(t,n.concat(r.call(arguments)))}}function R(t,e,n){if(t){n=n||h;var i=(e=e.split(".")).shift().toUpperCase(),r=new RegExp("\\s("+e.join("|")+")\\s","g");do{if(">*"===i&&t.parentNode===n||(""===i||t.nodeName.toUpperCase()==i)&&(!e.length||((" "+t.className+" ").match(r)||[]).length==e.length))return t}while(t!==n&&(t=t.parentNode))}return null}function L(t,e,n){t.addEventListener(e,n,!1)}function I(t,e,n){t.removeEventListener(e,n,!1)}function P(t,e,n){if(t)if(t.classList)t.classList[n?"add":"remove"](e);else{var i=(" "+t.className+" ").replace(/\s+/g," ").replace(" "+e+" ","");t.className=i+(n?" "+e:"")}}function F(t,e,n){var i=t&&t.style;if(i){if(void 0===n)return h.defaultView&&h.defaultView.getComputedStyle?n=h.defaultView.getComputedStyle(t,""):t.currentStyle&&(n=t.currentStyle),void 0===e?n:n[e];e in i||(e="-webkit-"+e),i[e]=n+("string"==typeof n?"":"px")}}function H(t,e,n){if(t){var i=t.getElementsByTagName(e),r=0,o=i.length;if(n)for(;r<o;r++)n(i[r],r);return i}return[]}function q(t){t.draggable=!1}function $(){M=!1}function U(t){for(var e=t.tagName+t.className+t.src+t.href+t.textContent,n=e.length,i=0;n--;)i+=e.charCodeAt(n);return i.toString(36)}function B(t){for(var e=0;t=t&&t.previousElementSibling;)"TEMPLATE"!==t.nodeName.toUpperCase()&&e++;return e}function t(t,e){var n,i;return function(){void 0===n&&(n=arguments,i=this,setTimeout(function(){1===n.length?t.call(i,n[0]):t.apply(i,n),n=void 0},e))}}return A.prototype={constructor:A,_dragStarted:function(){x&&b&&(P(b,this.options.ghostClass,!0),A.active=this,l(x,"start",b,x,u))},_onTapStart:function(t){var e=t.type,n=t.touches&&t.touches[0],i=(n||t).target,r=i,o=this.options,s=this.el,a=o.filter;if(!("mousedown"===e&&0!==t.button||o.disabled)&&(i=R(i,o.draggable,s))){if(u=B(i),"function"==typeof a){if(a.call(this,t,i,this))return l(r,"filter",i,s,u),void t.preventDefault()}else if(a=a&&a.split(",").some(function(t){if(t=R(r,t.trim(),s))return l(t,"filter",i,s,u),!0}))return void t.preventDefault();if((!o.handle||R(r,o.handle,s))&&i&&!b&&i.parentNode===s){c=t,x=this.el,S=(b=i).nextSibling,E=this.options.group,b.draggable=!0,o.ignore.split(",").forEach(function(t){H(i,t.trim(),q)}),n&&(c={target:i,clientX:n.clientX,clientY:n.clientY},this._onDragStart(c,"touch"),t.preventDefault()),L(h,"mouseup",this._onDrop),L(h,"touchend",this._onDrop),L(h,"touchcancel",this._onDrop),L(b,"dragend",this),L(x,"dragstart",this._onDragStart),d||this._onDragStart(c,!0);try{h.selection?h.selection.empty():window.getSelection().removeAllRanges()}catch(t){}}}},_emulateDragOver:function(){if(o){F(w,"display","none");var t=h.elementFromPoint(o.clientX,o.clientY),e=t,n=" "+this.options.group.name,i=D.length;if(e)do{if(e[k]&&-1<e[k].groups.indexOf(n)){for(;i--;)D[i]({clientX:o.clientX,clientY:o.clientY,target:t,rootEl:e});break}t=e}while(e=e.parentNode);F(w,"display","")}},_onTouchMove:function(t){if(c){var e=t.touches?t.touches[0]:t,n=e.clientX-c.clientX,i=e.clientY-c.clientY,r=t.touches?"translate3d("+n+"px,"+i+"px,0)":"translate("+n+"px,"+i+"px)";o=e,F(w,"webkitTransform",r),F(w,"mozTransform",r),F(w,"msTransform",r),F(w,"transform",r),t.preventDefault()}},_onDragStart:function(t,e){var n=t.dataTransfer,i=this.options;if(this._offUpEvents(),"clone"==E.pull&&(F(_=b.cloneNode(!0),"display","none"),x.insertBefore(_,b)),e){var r,o=b.getBoundingClientRect(),s=F(b);F(w=b.cloneNode(!0),"top",o.top-a(s.marginTop,10)),F(w,"left",o.left-a(s.marginLeft,10)),F(w,"width",o.width),F(w,"height",o.height),F(w,"opacity","0.8"),F(w,"position","fixed"),F(w,"zIndex","100000"),x.appendChild(w),r=w.getBoundingClientRect(),F(w,"width",2*o.width-r.width),F(w,"height",2*o.height-r.height),"touch"===e?(L(h,"touchmove",this._onTouchMove),L(h,"touchend",this._onDrop),L(h,"touchcancel",this._onDrop)):(L(h,"mousemove",this._onTouchMove),L(h,"mouseup",this._onDrop)),this._loopId=setInterval(this._emulateDragOver,150)}else n&&(n.effectAllowed="move",i.setData&&i.setData.call(this,n,b)),L(h,"drop",this);setTimeout(this._dragStarted,0)},_onDragOver:function(t){var e,n,i,r=this.el,o=this.options,s=o.group,a=s.put,l=E===s,u=o.sort;if(b&&(void 0!==t.preventDefault&&(t.preventDefault(),o.dragoverBubble||t.stopPropagation()),E&&!o.disabled&&(l?u||(i=!x.contains(b)):E.pull&&a&&(E.name===s.name||a.indexOf&&~a.indexOf(E.name)))&&(void 0===t.rootEl||t.rootEl===this.el))){if(O(t,o,this.el),M)return;if(e=R(t.target,o.draggable,r),n=b.getBoundingClientRect(),i)return N(!0),void(_||S?x.insertBefore(b,_||S):u||x.appendChild(b));if(0===r.children.length||r.children[0]===w||r===t.target&&(e=function(t,e){var n=t.lastElementChild,i=n.getBoundingClientRect();return 5<e.clientY-(i.top+i.height)&&n}(r,t))){if(e){if(e.animated)return;h=e.getBoundingClientRect()}N(l),r.appendChild(b),this._animate(n,b),e&&this._animate(h,e)}else if(e&&!e.animated&&e!==b&&void 0!==e.parentNode[k]){T!==e&&(C=F(T=e));var c,h=e.getBoundingClientRect(),d=h.right-h.left,f=h.bottom-h.top,p=/left|right|inline/.test(C.cssFloat+C.display),g=e.offsetWidth>b.offsetWidth,v=e.offsetHeight>b.offsetHeight,m=.5<(p?(t.clientX-h.left)/d:(t.clientY-h.top)/f),y=e.nextElementSibling;M=!0,setTimeout($,30),N(l),(c=p?e.previousElementSibling===b&&!g||m&&g:y!==b&&!v||m&&v)&&!y?r.appendChild(b):e.parentNode.insertBefore(b,c?y:e),this._animate(n,b),this._animate(h,e)}}},_animate:function(t,e){var n=this.options.animation;if(n){var i=e.getBoundingClientRect();F(e,"transition","none"),F(e,"transform","translate3d("+(t.left-i.left)+"px,"+(t.top-i.top)+"px,0)"),e.offsetWidth,F(e,"transition","all "+n+"ms"),F(e,"transform","translate3d(0,0,0)"),clearTimeout(e.animated),e.animated=setTimeout(function(){F(e,"transition",""),F(e,"transform",""),e.animated=!1},n)}},_offUpEvents:function(){I(h,"mouseup",this._onDrop),I(h,"touchmove",this._onTouchMove),I(h,"touchend",this._onDrop),I(h,"touchcancel",this._onDrop)},_onDrop:function(t){var e=this.el,n=this.options;clearInterval(this._loopId),clearInterval(g.pid),I(h,"drop",this),I(h,"mousemove",this._onTouchMove),I(e,"dragstart",this._onDragStart),this._offUpEvents(),t&&(t.preventDefault(),n.dropBubble||t.stopPropagation(),w&&w.parentNode.removeChild(w),b&&(I(b,"dragend",this),q(b),P(b,this.options.ghostClass,!1),x!==b.parentNode?(i=B(b),l(b.parentNode,"sort",b,x,u,i),l(x,"sort",b,x,u,i),l(b,"add",b,x,u,i),l(x,"remove",b,x,u,i)):(_&&_.parentNode.removeChild(_),b.nextSibling!==S&&(i=B(b),l(x,"update",b,x,u,i),l(x,"sort",b,x,u,i))),A.active&&l(x,"end",b,x,u,i)),x=b=w=S=_=f=p=c=o=T=C=E=A.active=null,this.save())},handleEvent:function(t){var e=t.type;"dragover"===e||"dragenter"===e?(this._onDragOver(t),function(t){t.dataTransfer.dropEffect="move",t.preventDefault()}(t)):"drop"!==e&&"dragend"!==e||this._onDrop(t)},toArray:function(){for(var t,e=[],n=this.el.children,i=0,r=n.length;i<r;i++)R(t=n[i],this.options.draggable,this.el)&&e.push(t.getAttribute("data-id")||U(t));return e},sort:function(t){var i={},r=this.el;this.toArray().forEach(function(t,e){var n=r.children[e];R(n,this.options.draggable,r)&&(i[t]=n)},this),t.forEach(function(t){i[t]&&(r.removeChild(i[t]),r.appendChild(i[t]))})},save:function(){var t=this.options.store;t&&t.set(this)},closest:function(t,e){return R(t,e||this.options.draggable,this.el)},option:function(t,e){var n=this.options;if(void 0===e)return n[t];n[t]=e},destroy:function(){var e=this.el,n=this.options;m.forEach(function(t){I(e,t.substr(2).toLowerCase(),n[t])}),I(e,"mousedown",this._onTapStart),I(e,"touchstart",this._onTapStart),I(e,"dragover",this),I(e,"dragenter",this),Array.prototype.forEach.call(e.querySelectorAll("[draggable]"),function(t){t.removeAttribute("draggable")}),D.splice(D.indexOf(this._onDragOver),1),this._onDrop(),this.el=null}},A.utils={on:L,off:I,css:F,find:H,bind:j,is:function(t,e){return!!R(t,e,t)},throttle:t,closest:R,toggleClass:P,dispatchEvent:l,index:B},A.version="1.1.1",A.create=function(t,e){return new A(t,e)},A}),function(t){"use strict";"function"==typeof define&&define.amd?define(["jquery"],t):t(jQuery)}(function(r){"use strict";r.fn.sortable=function(n){var i;return this.each(function(){var t=r(this),e=t.data("sortable");if(e||!(n instanceof Object)&&n||(e=new Sortable(this,n),t.data("sortable",e)),e){if("widget"===n)return e;"destroy"===n?(e.destroy(),t.removeData("sortable")):n in e&&(i=e[e].apply(e,[].slice.call(arguments,1)))}}),void 0===i?this:i}});var XBBCODE=function(){var v,m,y,i,r,o,s,t={},a=/^(?:https?|file|c):(?:\/{1,3}|\\{1})[-a-zA-Z0-9:;@#%&()~_?\+=\/\\\.]*$/,l=/^(?:aliceblue|antiquewhite|aqua|aquamarine|azure|beige|bisque|black|blanchedalmond|blue|blueviolet|brown|burlywood|cadetblue|chartreuse|chocolate|coral|cornflowerblue|cornsilk|crimson|cyan|darkblue|darkcyan|darkgoldenrod|darkgray|darkgreen|darkkhaki|darkmagenta|darkolivegreen|darkorange|darkorchid|darkred|darksalmon|darkseagreen|darkslateblue|darkslategray|darkturquoise|darkviolet|deeppink|deepskyblue|dimgray|dodgerblue|firebrick|floralwhite|forestgreen|fuchsia|gainsboro|ghostwhite|gold|goldenrod|gray|green|greenyellow|honeydew|hotpink|indianred|indigo|ivory|khaki|lavender|lavenderblush|lawngreen|lemonchiffon|lightblue|lightcoral|lightcyan|lightgoldenrodyellow|lightgray|lightgreen|lightpink|lightsalmon|lightseagreen|lightskyblue|lightslategray|lightsteelblue|lightyellow|lime|limegreen|linen|magenta|maroon|mediumaquamarine|mediumblue|mediumorchid|mediumpurple|mediumseagreen|mediumslateblue|mediumspringgreen|mediumturquoise|mediumvioletred|midnightblue|mintcream|mistyrose|moccasin|navajowhite|navy|oldlace|olive|olivedrab|orange|orangered|orchid|palegoldenrod|palegreen|paleturquoise|palevioletred|papayawhip|peachpuff|peru|pink|plum|powderblue|purple|red|rosybrown|royalblue|saddlebrown|salmon|sandybrown|seagreen|seashell|sienna|silver|skyblue|slateblue|slategray|snow|springgreen|steelblue|tan|teal|thistle|tomato|turquoise|violet|wheat|white|whitesmoke|yellow|yellowgreen)$/,u=/^#?[a-fA-F0-9]{6}$/,c=/[^\s@]+@[^\s@]+\.[^\s@]+/,h=/^([a-z][a-z0-9_]+|"[a-z][a-z0-9_\s]+")$/i,d=[];function n(){var t,e,n;for(t in m=[],v)if(v.hasOwnProperty(t)){for("*"===t?m.push("\\"+t):(m.push(t),v[t].noParse&&d.push(t)),v[t].validChildLookup={},v[t].validParentLookup={},v[t].restrictParentsTo=v[t].restrictParentsTo||[],v[t].restrictChildrenTo=v[t].restrictChildrenTo||[],n=v[t].restrictChildrenTo.length,e=0;e<n;e++)v[t].validChildLookup[v[t].restrictChildrenTo[e]]=!0;for(n=v[t].restrictParentsTo.length,e=0;e<n;e++)v[t].validParentLookup[v[t].restrictParentsTo[e]]=!0}y=new RegExp("<bbcl=([0-9]+) ("+m.join("|")+")([ =][^>]*?)?>((?:.|[\\r\\n])*?)<bbcl=\\1 /\\2>","gi"),i=new RegExp("\\[("+m.join("|")+")([ =][^\\]]*?)?\\]([^\\[]*?)\\[/\\1\\]","gi"),r=new RegExp("\\[("+d.join("|")+")([ =][^\\]]*?)?\\]([\\s\\S]*?)\\[/\\1\\]","gi"),function(){for(var t=[],e=0;e<m.length;e++)"\\*"!==m[e]&&t.push("/"+m[e]);o=new RegExp("(\\[)((?:"+m.join("|")+")(?:[ =][^\\]]*?)?)(\\])","gi"),s=new RegExp("(\\[)("+t.join("|")+")(\\])","gi")}()}v={b:{openTag:function(t,e){return'<span class="xbbcode-b">'},closeTag:function(t,e){return"</span>"}},bbcode:{openTag:function(t,e){return""},closeTag:function(t,e){return""}},center:{openTag:function(t,e){return'<span class="xbbcode-center">'},closeTag:function(t,e){return"</span>"}},code:{openTag:function(t,e){return'<span class="xbbcode-code">'},closeTag:function(t,e){return"</span>"},noParse:!0},color:{openTag:function(t,e){var n=t.substr(1).toLowerCase()||"black";return l.lastIndex=0,u.lastIndex=0,l.test(n)||(u.test(n)?"#"!==n.substr(0,1)&&(n="#"+n):n="black"),'<span style="color:'+n+'">'},closeTag:function(t,e){return"</span>"}},email:{openTag:function(t,e){var n;return n=t?t.substr(1):e.replace(/<.*?>/g,""),c.lastIndex=0,c.test(n)?'<a href="mailto:'+n+'">':"<a>"},closeTag:function(t,e){return"</a>"}},face:{openTag:function(t,e){var n=t.substr(1)||"inherit";return h.lastIndex=0,h.test(n)||(n="inherit"),'<span style="font-family:'+n+'">'},closeTag:function(t,e){return"</span>"}},font:{openTag:function(t,e){var n=t.substr(1)||"inherit";return h.lastIndex=0,h.test(n)||(n="inherit"),'<span style="font-family:'+n+'">'},closeTag:function(t,e){return"</span>"}},i:{openTag:function(t,e){return'<span class="xbbcode-i">'},closeTag:function(t,e){return"</span>"}},img:{openTag:function(t,e){var n=e;return a.lastIndex=0,a.test(n)||(n=""),'<img src="'+n+'" />'},closeTag:function(t,e){return""},displayContent:!1},justify:{openTag:function(t,e){return'<span class="xbbcode-justify">'},closeTag:function(t,e){return"</span>"}},large:{openTag:function(t,e){var n=(t=t||"").substr(1)||"inherit";return l.lastIndex=0,u.lastIndex=0,l.test(n)||(u.test(n)?"#"!==n.substr(0,1)&&(n="#"+n):n="inherit"),'<span class="xbbcode-size-36" style="color:'+n+'">'},closeTag:function(t,e){return"</span>"}},left:{openTag:function(t,e){return'<span class="xbbcode-left">'},closeTag:function(t,e){return"</span>"}},li:{openTag:function(t,e){return"<li>"},closeTag:function(t,e){return"</li>"},restrictParentsTo:["list","ul","ol"]},list:{openTag:function(t,e){return"<ul>"},closeTag:function(t,e){return"</ul>"},restrictChildrenTo:["*","li"]},noparse:{openTag:function(t,e){return""},closeTag:function(t,e){return""},noParse:!0},ol:{openTag:function(t,e){return"<ol>"},closeTag:function(t,e){return"</ol>"},restrictChildrenTo:["*","li"]},php:{openTag:function(t,e){return'<span class="xbbcode-code">'},closeTag:function(t,e){return"</span>"},noParse:!0},quote:{openTag:function(t,e){return'<blockquote class="xbbcode-blockquote">'},closeTag:function(t,e){return"</blockquote>"}},right:{openTag:function(t,e){return'<span class="xbbcode-right">'},closeTag:function(t,e){return"</span>"}},s:{openTag:function(t,e){return'<span class="xbbcode-s">'},closeTag:function(t,e){return"</span>"}},size:{openTag:function(t,e){var n=parseInt(t.substr(1),10)||0;return(n<4||40<n)&&(n=14),'<span class="xbbcode-size-'+n+'">'},closeTag:function(t,e){return"</span>"}},small:{openTag:function(t,e){var n=(t=t||"").substr(1)||"inherit";return l.lastIndex=0,u.lastIndex=0,l.test(n)||(u.test(n)?"#"!==n.substr(0,1)&&(n="#"+n):n="inherit"),'<span class="xbbcode-size-10" style="color:'+n+'">'},closeTag:function(t,e){return"</span>"}},sub:{openTag:function(t,e){return"<sub>"},closeTag:function(t,e){return"</sub>"}},sup:{openTag:function(t,e){return"<sup>"},closeTag:function(t,e){return"</sup>"}},table:{openTag:function(t,e){return'<table class="xbbcode-table">'},closeTag:function(t,e){return"</table>"},restrictChildrenTo:["tbody","thead","tfoot","tr"]},tbody:{openTag:function(t,e){return"<tbody>"},closeTag:function(t,e){return"</tbody>"},restrictChildrenTo:["tr"],restrictParentsTo:["table"]},tfoot:{openTag:function(t,e){return"<tfoot>"},closeTag:function(t,e){return"</tfoot>"},restrictChildrenTo:["tr"],restrictParentsTo:["table"]},thead:{openTag:function(t,e){return'<thead class="xbbcode-thead">'},closeTag:function(t,e){return"</thead>"},restrictChildrenTo:["tr"],restrictParentsTo:["table"]},td:{openTag:function(t,e){return'<td class="xbbcode-td">'},closeTag:function(t,e){return"</td>"},restrictParentsTo:["tr"]},th:{openTag:function(t,e){return'<th class="xbbcode-th">'},closeTag:function(t,e){return"</th>"},restrictParentsTo:["tr"]},tr:{openTag:function(t,e){return'<tr class="xbbcode-tr">'},closeTag:function(t,e){return"</tr>"},restrictChildrenTo:["td","th"],restrictParentsTo:["table","tbody","tfoot","thead"]},u:{openTag:function(t,e){return'<span class="xbbcode-u">'},closeTag:function(t,e){return"</span>"}},ul:{openTag:function(t,e){return"<ul>"},closeTag:function(t,e){return"</ul>"},restrictChildrenTo:["*","li"]},url:{openTag:function(t,e){var n;return n=t?t.substr(1):e.replace(/<.*?>/g,""),a.lastIndex=0,a.test(n)||(n="#"),'<a href="'+n+'">'},closeTag:function(t,e){return"</a>"}},"*":{openTag:function(t,e){return"<li>"},closeTag:function(t,e){return"</li>"},restrictParentsTo:["list","ul","ol"]}},n();var f=function(t,e,n,i,r){n=n.toLowerCase();var o=v[n].noParse?function(t){return t.replace(/<bbcl=[0-9]+ \/\*>/gi,"").replace(/<bbcl=[0-9]+ /gi,"&#91;").replace(/>/gi,"&#93;")}(r):r.replace(y,f),s=v[n].openTag(i,o),a=v[n].closeTag(i,o);return!1===v[n].displayContent&&(o=""),s+o+a};function p(t){for(;t!==(t=t.replace(i,function(t,e,n,i){return t=(t=t.replace(/\[/g,"<")).replace(/\]/g,">"),t.replace(/\<([^\>][^\>]*?)\>/gi,function(t,e){return null===e.match(/^bbcl=([0-9]+) /)?"<bbcl=0 "+e+">":"<"+e.replace(/^(bbcl=)([0-9]+)/,function(t,e,n){return e+(parseInt(n,10)+1)})+">"})})););return t}return t.tags=function(){return v},t.addTags=function(t){var e;for(e in t)v[e]=t[e];n()},t.process=function(t){var e={html:"",error:!1},n=[];for(t.text=t.text.replace(/</g,"&lt;"),t.text=t.text.replace(/>/g,"&gt;"),t.text=t.text.replace(o,function(t,e,n,i){return"<"+n+">"}),t.text=t.text.replace(s,function(t,e,n,i){return"<"+n+">"}),t.text=t.text.replace(/\[/g,"&#91;"),t.text=t.text.replace(/\]/g,"&#93;"),t.text=t.text.replace(/</g,"["),t.text=t.text.replace(/>/g,"]");t.text!==(t.text=t.text.replace(r,function(t,e,n,i){return"["+e+(n=n||"")+"]"+(i=(i=(i=i.replace(/\[/g,"&#91;")).replace(/\]/g,"&#93;"))||"")+"[/"+e+"]"})););return t.text=function(t){for(t=(t=t.replace(/\[(?!\*[ =\]]|list([ =][^\]]*)?\]|\/list[\]])/gi,"<")).replace(/\[(?=list([ =][^\]]*)?\]|\/list[\]])/gi,">");t!==(t=t.replace(/>list([ =][^\]]*)?\]([^>]*?)(>\/list])/gi,function(t,e,n){for(var i=t;i!==(i=i.replace(/\[\*\]([^\[]*?)(\[\*\]|>\/list])/i,function(t,e,n){return"<*]"+e+(n=">/list]"===n.toLowerCase()?"</*]</list]":"</*][*]")})););return i=i.replace(/>/g,"<")})););return t=t.replace(/</g,"[")}(t.text),t.text=p(t.text),n=function o(t,e,n,i,r,s,a){a=a||[],n++;var l,u,c,h,d=new RegExp("(<bbcl="+n+" )("+m.join("|")+")([ =>])","gi"),f=new RegExp("(<bbcl="+n+" )("+m.join("|")+")([ =>])","i"),p=s.match(d)||[],g=v[t]||{};for(p||(s=""),c=d.lastIndex=0;c<p.length;c++)f.lastIndex=0,h=p[c].match(f)[2].toLowerCase(),g&&g.restrictChildrenTo&&0<g.restrictChildrenTo.length&&(g.validChildLookup[h]||(u='The tag "'+h+'" is not allowed as a child of the tag "'+t+'".',a.push(u))),0<(l=v[h]||{}).restrictParentsTo.length&&(l.validParentLookup[t]||(u='The tag "'+t+'" is not allowed as a parent of the tag "'+h+'".',a.push(u)));return s=s.replace(y,function(t,e,n,i,r){return a=o(n.toLowerCase(),t,e,n,i,r,a),t}),a}("bbcode",t.text,-1,0,0,t.text),e.html=function(t){var e=t.text;return e=e.replace(y,f)}(t),-1===e.html.indexOf("[")&&-1===e.html.indexOf("]")||n.push("Some tags appear to be misaligned."),t.removeMisalignedTags&&(e.html=e.html.replace(/\[.*?\]/g,"")),t.addInLineBreaks&&(e.html='<div style="white-space:pre;">'+e.html+"</div>"),e.html=e.html.replace("&#91;","["),e.html=e.html.replace("&#93;","]"),e.error=0!==n.length,e.errorQueue=n,e},t}();!function(l){"use strict";var u=function(t,e){this.options=l.extend({},u.DEFAULTS,e),this.$target=l(this.options.target).on("scroll.bs.affix.data-api",l.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",l.proxy(this.checkPositionWithEventLoop,this)),this.$element=l(t),this.affixed=this.unpin=this.pinnedOffset=null,this.checkPosition()};function n(i){return this.each(function(){var t=l(this),e=t.data("bs.affix"),n="object"==typeof i&&i;e||t.data("bs.affix",e=new u(this,n)),"string"==typeof i&&e[i]()})}u.VERSION="3.3.1",u.RESET="affix affix-top affix-bottom",u.DEFAULTS={offset:0,target:window},u.prototype.getState=function(t,e,n,i){var r=this.$target.scrollTop(),o=this.$element.offset(),s=this.$target.height();if(null!=n&&"top"==this.affixed)return r<n&&"top";if("bottom"==this.affixed)return null!=n?!(r+this.unpin<=o.top)&&"bottom":!(r+s<=t-i)&&"bottom";var a=null==this.affixed,l=a?r:o.top;return null!=n&&l<=n?"top":null!=i&&t-i<=l+(a?s:e)&&"bottom"},u.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(u.RESET).addClass("affix");var t=this.$target.scrollTop(),e=this.$element.offset();return this.pinnedOffset=e.top-t},u.prototype.checkPositionWithEventLoop=function(){setTimeout(l.proxy(this.checkPosition,this),1)},u.prototype.checkPosition=function(){if(this.$element.is(":visible")){var t=this.$element.height(),e=this.options.offset,n=e.top,i=e.bottom,r=l("body").height();"object"!=typeof e&&(i=n=e),"function"==typeof n&&(n=e.top(this.$element)),"function"==typeof i&&(i=e.bottom(this.$element));var o=this.getState(r,t,n,i);if(this.affixed!=o){null!=this.unpin&&this.$element.css("top","");var s="affix"+(o?"-"+o:""),a=l.Event(s+".bs.affix");if(this.$element.trigger(a),a.isDefaultPrevented())return;this.affixed=o,this.unpin="bottom"==o?this.getPinnedOffset():null,this.$element.removeClass(u.RESET).addClass(s).trigger(s.replace("affix","affixed")+".bs.affix")}"bottom"==o&&this.$element.offset({top:r-t-i})}};var t=l.fn.affix;l.fn.affix=n,l.fn.affix.Constructor=u,l.fn.affix.noConflict=function(){return l.fn.affix=t,this},l(window).on("load",function(){l('[data-spy="affix"]').each(function(){var t=l(this),e=t.data();e.offset=e.offset||{},null!=e.offsetBottom&&(e.offset.bottom=e.offsetBottom),null!=e.offsetTop&&(e.offset.top=e.offsetTop),n.call(t,e)})})}(jQuery),function(o){"use strict";function s(t){o(t).on("click",e,this.close)}var e='[data-dismiss="alert"]';s.VERSION="3.3.1",s.TRANSITION_DURATION=150,s.prototype.close=function(t){var e=o(this),n=e.attr("data-target");n=n||(n=e.attr("href"))&&n.replace(/.*(?=#[^\s]*$)/,"");var i=o(n);function r(){i.detach().trigger("closed.bs.alert").remove()}t&&t.preventDefault(),i.length||(i=e.closest(".alert")),i.trigger(t=o.Event("close.bs.alert")),t.isDefaultPrevented()||(i.removeClass("in"),o.support.transition&&i.hasClass("fade")?i.one("bsTransitionEnd",r).emulateTransitionEnd(s.TRANSITION_DURATION):r())};var t=o.fn.alert;o.fn.alert=function(n){return this.each(function(){var t=o(this),e=t.data("bs.alert");e||t.data("bs.alert",e=new s(this)),"string"==typeof n&&e[n].call(t)})},o.fn.alert.Constructor=s,o.fn.alert.noConflict=function(){return o.fn.alert=t,this},o(document).on("click.bs.alert.data-api",e,s.prototype.close)}(jQuery),function(o){"use strict";var r=function(t,e){this.$element=o(t),this.options=o.extend({},r.DEFAULTS,e),this.isLoading=!1};function n(i){return this.each(function(){var t=o(this),e=t.data("bs.button"),n="object"==typeof i&&i;e||t.data("bs.button",e=new r(this,n)),"toggle"==i?e.toggle():i&&e.setState(i)})}r.VERSION="3.3.1",r.DEFAULTS={loadingText:"loading..."},r.prototype.setState=function(t){var e="disabled",n=this.$element,i=n.is("input")?"val":"html",r=n.data();t+="Text",null==r.resetText&&n.data("resetText",n[i]()),setTimeout(o.proxy(function(){n[i](null==r[t]?this.options[t]:r[t]),"loadingText"==t?(this.isLoading=!0,n.addClass(e).attr(e,e)):this.isLoading&&(this.isLoading=!1,n.removeClass(e).removeAttr(e))},this),0)},r.prototype.toggle=function(){var t=!0,e=this.$element.closest('[data-toggle="buttons"]');if(e.length){var n=this.$element.find("input");"radio"==n.prop("type")&&(n.prop("checked")&&this.$element.hasClass("active")?t=!1:e.find(".active").removeClass("active")),t&&n.prop("checked",!this.$element.hasClass("active")).trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active"));t&&this.$element.toggleClass("active")};var t=o.fn.button;o.fn.button=n,o.fn.button.Constructor=r,o.fn.button.noConflict=function(){return o.fn.button=t,this},o(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(t){var e=o(t.target);e.hasClass("btn")||(e=e.closest(".btn")),n.call(e,"toggle"),t.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(t){o(t.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(t.type))})}(jQuery),function(d){"use strict";function f(t,e){this.$element=d(t),this.$indicators=this.$element.find(".carousel-indicators"),this.options=e,this.paused=this.sliding=this.interval=this.$active=this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",d.proxy(this.keydown,this)),"hover"!=this.options.pause||"ontouchstart"in document.documentElement||this.$element.on("mouseenter.bs.carousel",d.proxy(this.pause,this)).on("mouseleave.bs.carousel",d.proxy(this.cycle,this))}function s(r){return this.each(function(){var t=d(this),e=t.data("bs.carousel"),n=d.extend({},f.DEFAULTS,t.data(),"object"==typeof r&&r),i="string"==typeof r?r:n.slide;e||t.data("bs.carousel",e=new f(this,n)),"number"==typeof r?e.to(r):i?e[i]():n.interval&&e.pause().cycle()})}f.VERSION="3.3.1",f.TRANSITION_DURATION=600,f.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},f.prototype.keydown=function(t){if(!/input|textarea/i.test(t.target.tagName)){switch(t.which){case 37:this.prev();break;case 39:this.next();break;default:return}t.preventDefault()}},f.prototype.cycle=function(t){return t||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(d.proxy(this.next,this),this.options.interval)),this},f.prototype.getItemIndex=function(t){return this.$items=t.parent().children(".item"),this.$items.index(t||this.$active)},f.prototype.getItemForDirection=function(t,e){var n="prev"==t?-1:1,i=(this.getItemIndex(e)+n)%this.$items.length;return this.$items.eq(i)},f.prototype.to=function(t){var e=this,n=this.getItemIndex(this.$active=this.$element.find(".item.active"));if(!(t>this.$items.length-1||t<0))return this.sliding?this.$element.one("slid.bs.carousel",function(){e.to(t)}):n==t?this.pause().cycle():this.slide(n<t?"next":"prev",this.$items.eq(t))},f.prototype.pause=function(t){return t||(this.paused=!0),this.$element.find(".next, .prev").length&&d.support.transition&&(this.$element.trigger(d.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},f.prototype.next=function(){if(!this.sliding)return this.slide("next")},f.prototype.prev=function(){if(!this.sliding)return this.slide("prev")},f.prototype.slide=function(t,e){var n=this.$element.find(".item.active"),i=e||this.getItemForDirection(t,n),r=this.interval,o="next"==t?"left":"right",s="next"==t?"first":"last",a=this;if(!i.length){if(!this.options.wrap)return;i=this.$element.find(".item")[s]()}if(i.hasClass("active"))return this.sliding=!1;var l=i[0],u=d.Event("slide.bs.carousel",{relatedTarget:l,direction:o});if(this.$element.trigger(u),!u.isDefaultPrevented()){if(this.sliding=!0,r&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var c=d(this.$indicators.children()[this.getItemIndex(i)]);c&&c.addClass("active")}var h=d.Event("slid.bs.carousel",{relatedTarget:l,direction:o});return d.support.transition&&this.$element.hasClass("slide")?(i.addClass(t),i[0].offsetWidth,n.addClass(o),i.addClass(o),n.one("bsTransitionEnd",function(){i.removeClass([t,o].join(" ")).addClass("active"),n.removeClass(["active",o].join(" ")),a.sliding=!1,setTimeout(function(){a.$element.trigger(h)},0)}).emulateTransitionEnd(f.TRANSITION_DURATION)):(n.removeClass("active"),i.addClass("active"),this.sliding=!1,this.$element.trigger(h)),r&&this.cycle(),this}};var t=d.fn.carousel;d.fn.carousel=s,d.fn.carousel.Constructor=f,d.fn.carousel.noConflict=function(){return d.fn.carousel=t,this};function e(t){var e,n=d(this),i=d(n.attr("data-target")||(e=n.attr("href"))&&e.replace(/.*(?=#[^\s]+$)/,""));if(i.hasClass("carousel")){var r=d.extend({},i.data(),n.data()),o=n.attr("data-slide-to");o&&(r.interval=!1),s.call(i,r),o&&i.data("bs.carousel").to(o),t.preventDefault()}}d(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),d(window).on("load",function(){d('[data-ride="carousel"]').each(function(){var t=d(this);s.call(t,t.data())})})}(jQuery),function(s){"use strict";var a=function(t,e){this.$element=s(t),this.options=s.extend({},a.DEFAULTS,e),this.$trigger=s(this.options.trigger).filter('[href="#'+t.id+'"], [data-target="#'+t.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};function r(t){var e,n=t.attr("data-target")||(e=t.attr("href"))&&e.replace(/.*(?=#[^\s]+$)/,"");return s(n)}function l(i){return this.each(function(){var t=s(this),e=t.data("bs.collapse"),n=s.extend({},a.DEFAULTS,t.data(),"object"==typeof i&&i);!e&&n.toggle&&"show"==i&&(n.toggle=!1),e||t.data("bs.collapse",e=new a(this,n)),"string"==typeof i&&e[i]()})}a.VERSION="3.3.1",a.TRANSITION_DURATION=350,a.DEFAULTS={toggle:!0,trigger:'[data-toggle="collapse"]'},a.prototype.dimension=function(){return this.$element.hasClass("width")?"width":"height"},a.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var t,e=this.$parent&&this.$parent.find("> .panel").children(".in, .collapsing");if(!(e&&e.length&&(t=e.data("bs.collapse"))&&t.transitioning)){var n=s.Event("show.bs.collapse");if(this.$element.trigger(n),!n.isDefaultPrevented()){e&&e.length&&(l.call(e,"hide"),t||e.data("bs.collapse",null));var i=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[i](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var r=function(){this.$element.removeClass("collapsing").addClass("collapse in")[i](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!s.support.transition)return r.call(this);var o=s.camelCase(["scroll",i].join("-"));this.$element.one("bsTransitionEnd",s.proxy(r,this)).emulateTransitionEnd(a.TRANSITION_DURATION)[i](this.$element[0][o])}}}},a.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var t=s.Event("hide.bs.collapse");if(this.$element.trigger(t),!t.isDefaultPrevented()){var e=this.dimension();this.$element[e](this.$element[e]())[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 n=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};if(!s.support.transition)return n.call(this);this.$element[e](0).one("bsTransitionEnd",s.proxy(n,this)).emulateTransitionEnd(a.TRANSITION_DURATION)}}},a.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},a.prototype.getParent=function(){return s(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(s.proxy(function(t,e){var n=s(e);this.addAriaAndCollapsedClass(r(n),n)},this)).end()},a.prototype.addAriaAndCollapsedClass=function(t,e){var n=t.hasClass("in");t.attr("aria-expanded",n),e.toggleClass("collapsed",!n).attr("aria-expanded",n)};var t=s.fn.collapse;s.fn.collapse=l,s.fn.collapse.Constructor=a,s.fn.collapse.noConflict=function(){return s.fn.collapse=t,this},s(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(t){var e=s(this);e.attr("data-target")||t.preventDefault();var n=r(e),i=n.data("bs.collapse")?"toggle":s.extend({},e.data(),{trigger:this});l.call(n,i)})}(jQuery),function(a){"use strict";function i(t){a(t).on("click.bs.dropdown",this.toggle)}var l='[data-toggle="dropdown"]';function o(i){i&&3===i.which||(a(".dropdown-backdrop").remove(),a(l).each(function(){var t=a(this),e=u(t),n={relatedTarget:this};e.hasClass("open")&&(e.trigger(i=a.Event("hide.bs.dropdown",n)),i.isDefaultPrevented()||(t.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",n)))}))}function u(t){var e=t.attr("data-target"),n=(e=e||(e=t.attr("href"))&&/#[A-Za-z]/.test(e)&&e.replace(/.*(?=#[^\s]*$)/,""))&&a(e);return n&&n.length?n:t.parent()}i.VERSION="3.3.1",i.prototype.toggle=function(t){var e=a(this);if(!e.is(".disabled, :disabled")){var n=u(e),i=n.hasClass("open");if(o(),!i){"ontouchstart"in document.documentElement&&!n.closest(".navbar-nav").length&&a('<div class="dropdown-backdrop"/>').insertAfter(a(this)).on("click",o);var r={relatedTarget:this};if(n.trigger(t=a.Event("show.bs.dropdown",r)),t.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),n.toggleClass("open").trigger("shown.bs.dropdown",r)}return!1}},i.prototype.keydown=function(t){if(/(38|40|27|32)/.test(t.which)&&!/input|textarea/i.test(t.target.tagName)){var e=a(this);if(t.preventDefault(),t.stopPropagation(),!e.is(".disabled, :disabled")){var n=u(e),i=n.hasClass("open");if(!i&&27!=t.which||i&&27==t.which)return 27==t.which&&n.find(l).trigger("focus"),e.trigger("click");var r=" li:not(.divider):visible a",o=n.find('[role="menu"]'+r+', [role="listbox"]'+r);if(o.length){var s=o.index(t.target);38==t.which&&0<s&&s--,40==t.which&&s<o.length-1&&s++,~s||(s=0),o.eq(s).trigger("focus")}}}};var t=a.fn.dropdown;a.fn.dropdown=function(n){return this.each(function(){var t=a(this),e=t.data("bs.dropdown");e||t.data("bs.dropdown",e=new i(this)),"string"==typeof n&&e[n].call(t)})},a.fn.dropdown.Constructor=i,a.fn.dropdown.noConflict=function(){return a.fn.dropdown=t,this},a(document).on("click.bs.dropdown.data-api",o).on("click.bs.dropdown.data-api",".dropdown form",function(t){t.stopPropagation()}).on("click.bs.dropdown.data-api",l,i.prototype.toggle).on("keydown.bs.dropdown.data-api",l,i.prototype.keydown).on("keydown.bs.dropdown.data-api",'[role="menu"]',i.prototype.keydown).on("keydown.bs.dropdown.data-api",'[role="listbox"]',i.prototype.keydown)}(jQuery),function(a){"use strict";function s(t){this.element=a(t)}function e(n){return this.each(function(){var t=a(this),e=t.data("bs.tab");e||t.data("bs.tab",e=new s(this)),"string"==typeof n&&e[n]()})}s.VERSION="3.3.1",s.TRANSITION_DURATION=150,s.prototype.show=function(){var t=this.element,e=t.closest("ul:not(.dropdown-menu)"),n=t.data("target");if(n=n||(n=t.attr("href"))&&n.replace(/.*(?=#[^\s]*$)/,""),!t.parent("li").hasClass("active")){var i=e.find(".active:last a"),r=a.Event("hide.bs.tab",{relatedTarget:t[0]}),o=a.Event("show.bs.tab",{relatedTarget:i[0]});if(i.trigger(r),t.trigger(o),!o.isDefaultPrevented()&&!r.isDefaultPrevented()){var s=a(n);this.activate(t.closest("li"),e),this.activate(s,s.parent(),function(){i.trigger({type:"hidden.bs.tab",relatedTarget:t[0]}),t.trigger({type:"shown.bs.tab",relatedTarget:i[0]})})}}},s.prototype.activate=function(t,e,n){var i=e.find("> .active"),r=n&&a.support.transition&&(i.length&&i.hasClass("fade")||!!e.find("> .fade").length);function o(){i.removeClass("active").find("> .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),t.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),r?(t[0].offsetWidth,t.addClass("in")):t.removeClass("fade"),t.parent(".dropdown-menu")&&t.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),n&&n()}i.length&&r?i.one("bsTransitionEnd",o).emulateTransitionEnd(s.TRANSITION_DURATION):o(),i.removeClass("in")};var t=a.fn.tab;a.fn.tab=e,a.fn.tab.Constructor=s,a.fn.tab.noConflict=function(){return a.fn.tab=t,this};function n(t){t.preventDefault(),e.call(a(this),"show")}a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',n).on("click.bs.tab.data-api",'[data-toggle="pill"]',n)}(jQuery),function(i){"use strict";i.fn.emulateTransitionEnd=function(t){var e=!1,n=this;i(this).one("bsTransitionEnd",function(){e=!0});return setTimeout(function(){e||i(n).trigger(i.support.transition.end)},t),this},i(function(){i.support.transition=function(){var t=document.createElement("bootstrap"),e={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var n in e)if(void 0!==t.style[n])return{end:e[n]};return!1}(),i.support.transition&&(i.event.special.bsTransitionEnd={bindType:i.support.transition.end,delegateType:i.support.transition.end,handle:function(t){if(i(t.target).is(this))return t.handleObj.handler.apply(this,arguments)}})})}(jQuery),function(o){"use strict";function r(t,e){var n=o.proxy(this.process,this);this.$body=o("body"),this.$scrollElement=o(t).is("body")?o(window):o(t),this.options=o.extend({},r.DEFAULTS,e),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",n),this.refresh(),this.process()}function e(i){return this.each(function(){var t=o(this),e=t.data("bs.scrollspy"),n="object"==typeof i&&i;e||t.data("bs.scrollspy",e=new r(this,n)),"string"==typeof i&&e[i]()})}r.VERSION="3.3.1",r.DEFAULTS={offset:10},r.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},r.prototype.refresh=function(){var i="offset",r=0;o.isWindow(this.$scrollElement[0])||(i="position",r=this.$scrollElement.scrollTop()),this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight();var t=this;this.$body.find(this.selector).map(function(){var t=o(this),e=t.data("target")||t.attr("href"),n=/^#./.test(e)&&o(e);return n&&n.length&&n.is(":visible")&&[[n[i]().top+r,e]]||null}).sort(function(t,e){return t[0]-e[0]}).each(function(){t.offsets.push(this[0]),t.targets.push(this[1])})},r.prototype.process=function(){var t,e=this.$scrollElement.scrollTop()+this.options.offset,n=this.getScrollHeight(),i=this.options.offset+n-this.$scrollElement.height(),r=this.offsets,o=this.targets,s=this.activeTarget;if(this.scrollHeight!=n&&this.refresh(),i<=e)return s!=(t=o[o.length-1])&&this.activate(t);if(s&&e<r[0])return this.activeTarget=null,this.clear();for(t=r.length;t--;)s!=o[t]&&e>=r[t]&&(!r[t+1]||e<=r[t+1])&&this.activate(o[t])},r.prototype.activate=function(t){this.activeTarget=t,this.clear();var e=this.selector+'[data-target="'+t+'"],'+this.selector+'[href="'+t+'"]',n=o(e).parents("li").addClass("active");n.parent(".dropdown-menu").length&&(n=n.closest("li.dropdown").addClass("active")),n.trigger("activate.bs.scrollspy")},r.prototype.clear=function(){o(this.selector).parentsUntil(this.options.target,".active").removeClass("active")};var t=o.fn.scrollspy;o.fn.scrollspy=e,o.fn.scrollspy.Constructor=r,o.fn.scrollspy.noConflict=function(){return o.fn.scrollspy=t,this},o(window).on("load.bs.scrollspy.data-api",function(){o('[data-spy="scroll"]').each(function(){var t=o(this);e.call(t,t.data())})})}(jQuery),function(o){"use strict";function s(t,e){this.options=e,this.$body=o(document.body),this.$element=o(t),this.$backdrop=this.isShown=null,this.scrollbarWidth=0,this.options.remote&&this.$element.find(".modal-content").load(this.options.remote,o.proxy(function(){this.$element.trigger("loaded.bs.modal")},this))}function a(i,r){return this.each(function(){var t=o(this),e=t.data("bs.modal"),n=o.extend({},s.DEFAULTS,t.data(),"object"==typeof i&&i);e||t.data("bs.modal",e=new s(this,n)),"string"==typeof i?e[i](r):n.show&&e.show(r)})}s.VERSION="3.3.1",s.TRANSITION_DURATION=300,s.BACKDROP_TRANSITION_DURATION=150,s.DEFAULTS={backdrop:!0,keyboard:!0,show:!0},s.prototype.toggle=function(t){return this.isShown?this.hide():this.show(t)},s.prototype.show=function(n){var i=this,t=o.Event("show.bs.modal",{relatedTarget:n});this.$element.trigger(t),this.isShown||t.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"]',o.proxy(this.hide,this)),this.backdrop(function(){var t=o.support.transition&&i.$element.hasClass("fade");i.$element.parent().length||i.$element.appendTo(i.$body),i.$element.show().scrollTop(0),i.options.backdrop&&i.adjustBackdrop(),i.adjustDialog(),t&&i.$element[0].offsetWidth,i.$element.addClass("in").attr("aria-hidden",!1),i.enforceFocus();var e=o.Event("shown.bs.modal",{relatedTarget:n});t?i.$element.find(".modal-dialog").one("bsTransitionEnd",function(){i.$element.trigger("focus").trigger(e)}).emulateTransitionEnd(s.TRANSITION_DURATION):i.$element.trigger("focus").trigger(e)}))},s.prototype.hide=function(t){t&&t.preventDefault(),t=o.Event("hide.bs.modal"),this.$element.trigger(t),this.isShown&&!t.isDefaultPrevented()&&(this.isShown=!1,this.escape(),this.resize(),o(document).off("focusin.bs.modal"),this.$element.removeClass("in").attr("aria-hidden",!0).off("click.dismiss.bs.modal"),o.support.transition&&this.$element.hasClass("fade")?this.$element.one("bsTransitionEnd",o.proxy(this.hideModal,this)).emulateTransitionEnd(s.TRANSITION_DURATION):this.hideModal())},s.prototype.enforceFocus=function(){o(document).off("focusin.bs.modal").on("focusin.bs.modal",o.proxy(function(t){this.$element[0]===t.target||this.$element.has(t.target).length||this.$element.trigger("focus")},this))},s.prototype.escape=function(){this.isShown&&this.options.keyboard?this.$element.on("keydown.dismiss.bs.modal",o.proxy(function(t){27==t.which&&this.hide()},this)):this.isShown||this.$element.off("keydown.dismiss.bs.modal")},s.prototype.resize=function(){this.isShown?o(window).on("resize.bs.modal",o.proxy(this.handleUpdate,this)):o(window).off("resize.bs.modal")},s.prototype.hideModal=function(){var t=this;this.$element.hide(),this.backdrop(function(){t.$body.removeClass("modal-open"),t.resetAdjustments(),t.resetScrollbar(),t.$element.trigger("hidden.bs.modal")})},s.prototype.removeBackdrop=function(){this.$backdrop&&this.$backdrop.remove(),this.$backdrop=null},s.prototype.backdrop=function(t){var e=this,n=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var i=o.support.transition&&n;if(this.$backdrop=o('<div class="modal-backdrop '+n+'" />').prependTo(this.$element).on("click.dismiss.bs.modal",o.proxy(function(t){t.target===t.currentTarget&&("static"==this.options.backdrop?this.$element[0].focus.call(this.$element[0]):this.hide.call(this))},this)),i&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),!t)return;i?this.$backdrop.one("bsTransitionEnd",t).emulateTransitionEnd(s.BACKDROP_TRANSITION_DURATION):t()}else if(!this.isShown&&this.$backdrop){this.$backdrop.removeClass("in");var r=function(){e.removeBackdrop(),t&&t()};o.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one("bsTransitionEnd",r).emulateTransitionEnd(s.BACKDROP_TRANSITION_DURATION):r()}else t&&t()},s.prototype.handleUpdate=function(){this.options.backdrop&&this.adjustBackdrop(),this.adjustDialog()},s.prototype.adjustBackdrop=function(){this.$backdrop.css("height",0).css("height",this.$element[0].scrollHeight)},s.prototype.adjustDialog=function(){var t=this.$element[0].scrollHeight>document.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&t?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!t?this.scrollbarWidth:""})},s.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},s.prototype.checkScrollbar=function(){this.bodyIsOverflowing=document.body.scrollHeight>document.documentElement.clientHeight,this.scrollbarWidth=this.measureScrollbar()},s.prototype.setScrollbar=function(){var t=parseInt(this.$body.css("padding-right")||0,10);this.bodyIsOverflowing&&this.$body.css("padding-right",t+this.scrollbarWidth)},s.prototype.resetScrollbar=function(){this.$body.css("padding-right","")},s.prototype.measureScrollbar=function(){var t=document.createElement("div");t.className="modal-scrollbar-measure",this.$body.append(t);var e=t.offsetWidth-t.clientWidth;return this.$body[0].removeChild(t),e};var t=o.fn.modal;o.fn.modal=a,o.fn.modal.Constructor=s,o.fn.modal.noConflict=function(){return o.fn.modal=t,this},o(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',function(t){var e=o(this),n=e.attr("href"),i=o(e.attr("data-target")||n&&n.replace(/.*(?=#[^\s]+$)/,"")),r=i.data("bs.modal")?"toggle":o.extend({remote:!/#/.test(n)&&n},i.data(),e.data());e.is("a")&&t.preventDefault(),i.one("show.bs.modal",function(t){t.isDefaultPrevented()||i.one("hidden.bs.modal",function(){e.is(":visible")&&e.trigger("focus")})}),a.call(i,r,this)})}(jQuery),function(v){"use strict";function m(t,e){this.type=this.options=this.enabled=this.timeout=this.hoverState=this.$element=null,this.init("tooltip",t,e)}m.VERSION="3.3.1",m.TRANSITION_DURATION=150,m.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}},m.prototype.init=function(t,e,n){this.enabled=!0,this.type=t,this.$element=v(e),this.options=this.getOptions(n),this.$viewport=this.options.viewport&&v(this.options.viewport.selector||this.options.viewport);for(var i=this.options.trigger.split(" "),r=i.length;r--;){var o=i[r];if("click"==o)this.$element.on("click."+this.type,this.options.selector,v.proxy(this.toggle,this));else if("manual"!=o){var s="hover"==o?"mouseenter":"focusin",a="hover"==o?"mouseleave":"focusout";this.$element.on(s+"."+this.type,this.options.selector,v.proxy(this.enter,this)),this.$element.on(a+"."+this.type,this.options.selector,v.proxy(this.leave,this))}}this.options.selector?this._options=v.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},m.prototype.getDefaults=function(){return m.DEFAULTS},m.prototype.getOptions=function(t){return(t=v.extend({},this.getDefaults(),this.$element.data(),t)).delay&&"number"==typeof t.delay&&(t.delay={show:t.delay,hide:t.delay}),t},m.prototype.getDelegateOptions=function(){var n={},i=this.getDefaults();return this._options&&v.each(this._options,function(t,e){i[t]!=e&&(n[t]=e)}),n},m.prototype.enter=function(t){var e=t instanceof this.constructor?t:v(t.currentTarget).data("bs."+this.type);if(e&&e.$tip&&e.$tip.is(":visible"))e.hoverState="in";else{if(e||(e=new this.constructor(t.currentTarget,this.getDelegateOptions()),v(t.currentTarget).data("bs."+this.type,e)),clearTimeout(e.timeout),e.hoverState="in",!e.options.delay||!e.options.delay.show)return e.show();e.timeout=setTimeout(function(){"in"==e.hoverState&&e.show()},e.options.delay.show)}},m.prototype.leave=function(t){var e=t instanceof this.constructor?t:v(t.currentTarget).data("bs."+this.type);if(e||(e=new this.constructor(t.currentTarget,this.getDelegateOptions()),v(t.currentTarget).data("bs."+this.type,e)),clearTimeout(e.timeout),e.hoverState="out",!e.options.delay||!e.options.delay.hide)return e.hide();e.timeout=setTimeout(function(){"out"==e.hoverState&&e.hide()},e.options.delay.hide)},m.prototype.show=function(){var t=v.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(t);var e=v.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(t.isDefaultPrevented()||!e)return;var n=this,i=this.tip(),r=this.getUID(this.type);this.setContent(),i.attr("id",r),this.$element.attr("aria-describedby",r),this.options.animation&&i.addClass("fade");var o="function"==typeof this.options.placement?this.options.placement.call(this,i[0],this.$element[0]):this.options.placement,s=/\s?auto?\s?/i,a=s.test(o);a&&(o=o.replace(s,"")||"top"),i.detach().css({top:0,left:0,display:"block"}).addClass(o).data("bs."+this.type,this),this.options.container?i.appendTo(this.options.container):i.insertAfter(this.$element);var l=this.getPosition(),u=i[0].offsetWidth,c=i[0].offsetHeight;if(a){var h=o,d=this.options.container?v(this.options.container):this.$element.parent(),f=this.getPosition(d);o="bottom"==o&&l.bottom+c>f.bottom?"top":"top"==o&&l.top-c<f.top?"bottom":"right"==o&&l.right+u>f.width?"left":"left"==o&&l.left-u<f.left?"right":o,i.removeClass(h).addClass(o)}var p=this.getCalculatedOffset(o,l,u,c);this.applyPlacement(p,o);var g=function(){var t=n.hoverState;n.$element.trigger("shown.bs."+n.type),n.hoverState=null,"out"==t&&n.leave(n)};v.support.transition&&this.$tip.hasClass("fade")?i.one("bsTransitionEnd",g).emulateTransitionEnd(m.TRANSITION_DURATION):g()}},m.prototype.applyPlacement=function(t,e){var n=this.tip(),i=n[0].offsetWidth,r=n[0].offsetHeight,o=parseInt(n.css("margin-top"),10),s=parseInt(n.css("margin-left"),10);isNaN(o)&&(o=0),isNaN(s)&&(s=0),t.top=t.top+o,t.left=t.left+s,v.offset.setOffset(n[0],v.extend({using:function(t){n.css({top:Math.round(t.top),left:Math.round(t.left)})}},t),0),n.addClass("in");var a=n[0].offsetWidth,l=n[0].offsetHeight;"top"==e&&l!=r&&(t.top=t.top+r-l);var u=this.getViewportAdjustedDelta(e,t,a,l);u.left?t.left+=u.left:t.top+=u.top;var c=/top|bottom/.test(e),h=c?2*u.left-i+a:2*u.top-r+l,d=c?"offsetWidth":"offsetHeight";n.offset(t),this.replaceArrow(h,n[0][d],c)},m.prototype.replaceArrow=function(t,e,n){this.arrow().css(n?"left":"top",50*(1-t/e)+"%").css(n?"top":"left","")},m.prototype.setContent=function(){var t=this.tip(),e=this.getTitle();t.find(".tooltip-inner")[this.options.html?"html":"text"](e),t.removeClass("fade in top bottom left right")},m.prototype.hide=function(t){var e=this,n=this.tip(),i=v.Event("hide.bs."+this.type);function r(){"in"!=e.hoverState&&n.detach(),e.$element.removeAttr("aria-describedby").trigger("hidden.bs."+e.type),t&&t()}if(this.$element.trigger(i),!i.isDefaultPrevented())return n.removeClass("in"),v.support.transition&&this.$tip.hasClass("fade")?n.one("bsTransitionEnd",r).emulateTransitionEnd(m.TRANSITION_DURATION):r(),this.hoverState=null,this},m.prototype.fixTitle=function(){var t=this.$element;!t.attr("title")&&"string"==typeof t.attr("data-original-title")||t.attr("data-original-title",t.attr("title")||"").attr("title","")},m.prototype.hasContent=function(){return this.getTitle()},m.prototype.getPosition=function(t){var e=(t=t||this.$element)[0],n="BODY"==e.tagName,i=e.getBoundingClientRect();null==i.width&&(i=v.extend({},i,{width:i.right-i.left,height:i.bottom-i.top}));var r=n?{top:0,left:0}:t.offset(),o={scroll:n?document.documentElement.scrollTop||document.body.scrollTop:t.scrollTop()},s=n?{width:v(window).width(),height:v(window).height()}:null;return v.extend({},i,o,s,r)},m.prototype.getCalculatedOffset=function(t,e,n,i){return"bottom"==t?{top:e.top+e.height,left:e.left+e.width/2-n/2}:"top"==t?{top:e.top-i,left:e.left+e.width/2-n/2}:"left"==t?{top:e.top+e.height/2-i/2,left:e.left-n}:{top:e.top+e.height/2-i/2,left:e.left+e.width}},m.prototype.getViewportAdjustedDelta=function(t,e,n,i){var r={top:0,left:0};if(!this.$viewport)return r;var o=this.options.viewport&&this.options.viewport.padding||0,s=this.getPosition(this.$viewport);if(/right|left/.test(t)){var a=e.top-o-s.scroll,l=e.top+o-s.scroll+i;a<s.top?r.top=s.top-a:l>s.top+s.height&&(r.top=s.top+s.height-l)}else{var u=e.left-o,c=e.left+o+n;u<s.left?r.left=s.left-u:c>s.width&&(r.left=s.left+s.width-c)}return r},m.prototype.getTitle=function(){var t=this.$element,e=this.options;return t.attr("data-original-title")||("function"==typeof e.title?e.title.call(t[0]):e.title)},m.prototype.getUID=function(t){for(;t+=~~(1e6*Math.random()),document.getElementById(t););return t},m.prototype.tip=function(){return this.$tip=this.$tip||v(this.options.template)},m.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},m.prototype.enable=function(){this.enabled=!0},m.prototype.disable=function(){this.enabled=!1},m.prototype.toggleEnabled=function(){this.enabled=!this.enabled},m.prototype.toggle=function(t){var e=this;t&&((e=v(t.currentTarget).data("bs."+this.type))||(e=new this.constructor(t.currentTarget,this.getDelegateOptions()),v(t.currentTarget).data("bs."+this.type,e))),e.tip().hasClass("in")?e.leave(e):e.enter(e)},m.prototype.destroy=function(){var t=this;clearTimeout(this.timeout),this.hide(function(){t.$element.off("."+t.type).removeData("bs."+t.type)})};var t=v.fn.tooltip;v.fn.tooltip=function(r){return this.each(function(){var t=v(this),e=t.data("bs.tooltip"),n="object"==typeof r&&r,i=n&&n.selector;!e&&"destroy"==r||(i?(e||t.data("bs.tooltip",e={}),e[i]||(e[i]=new m(this,n))):e||t.data("bs.tooltip",e=new m(this,n)),"string"==typeof r&&e[r]())})},v.fn.tooltip.Constructor=m,v.fn.tooltip.noConflict=function(){return v.fn.tooltip=t,this}}(jQuery),function(o){"use strict";function s(t,e){this.init("popover",t,e)}if(!o.fn.tooltip)throw new Error("Popover requires tooltip.js");s.VERSION="3.3.1",s.DEFAULTS=o.extend({},o.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>'}),((s.prototype=o.extend({},o.fn.tooltip.Constructor.prototype)).constructor=s).prototype.getDefaults=function(){return s.DEFAULTS},s.prototype.setContent=function(){var t=this.tip(),e=this.getTitle(),n=this.getContent();t.find(".popover-title")[this.options.html?"html":"text"](e),t.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof n?"html":"append":"text"](n),t.removeClass("fade top bottom left right in"),t.find(".popover-title").html()||t.find(".popover-title").hide()},s.prototype.hasContent=function(){return this.getTitle()||this.getContent()},s.prototype.getContent=function(){var t=this.$element,e=this.options;return t.attr("data-content")||("function"==typeof e.content?e.content.call(t[0]):e.content)},s.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},s.prototype.tip=function(){return this.$tip||(this.$tip=o(this.options.template)),this.$tip};var t=o.fn.popover;o.fn.popover=function(r){return this.each(function(){var t=o(this),e=t.data("bs.popover"),n="object"==typeof r&&r,i=n&&n.selector;!e&&"destroy"==r||(i?(e||t.data("bs.popover",e={}),e[i]||(e[i]=new s(this,n))):e||t.data("bs.popover",e=new s(this,n)),"string"==typeof r&&e[r]())})},o.fn.popover.Constructor=s,o.fn.popover.noConflict=function(){return o.fn.popover=t,this}}(jQuery),function(i){i.expr[":"].notmdproc=function(t){return!i(t).data("mdproc")},i.material={options:{input:!0,ripples:!0,checkbox:!0,togglebutton:!0,radio:!0,arrive:!0,autofill:!0,withRipples:[".btn:not(.btn-link)",".card-image",".navbar a:not(.withoutripple)",".dropdown-menu a",".nav-tabs a:not(.withoutripple)",".withripple"].join(","),inputElements:"input.form-control, textarea.form-control, select.form-control",checkboxElements:".checkbox > label > input[type=checkbox]",togglebuttonElements:".togglebutton > label > input[type=checkbox]",radioElements:".radio > label > input[type=radio]"},checkbox:function(t){i(t||this.options.checkboxElements).filter(":notmdproc").data("mdproc",!0).after("<span class=ripple></span><span class=check></span>")},togglebutton:function(t){i(t||this.options.togglebuttonElements).filter(":notmdproc").data("mdproc",!0).after("<span class=toggle></span>")},radio:function(t){i(t||this.options.radioElements).filter(":notmdproc").data("mdproc",!0).after("<span class=circle></span><span class=check></span>")},input:function(t){i(t||this.options.inputElements).filter(":notmdproc").data("mdproc",!0).each(function(){var t=i(this);if(t.wrap("<div class=form-control-wrapper></div>"),t.after("<span class=material-input></span>"),t.hasClass("floating-label")){var e=t.attr("placeholder");t.attr("placeholder",null).removeClass("floating-label"),t.after("<div class=floating-label>"+e+"</div>")}if(t.attr("data-hint")&&t.after("<div class=hint>"+t.attr("data-hint")+"</div>"),null!==t.val()&&"undefined"!=t.val()&&""!==t.val()||t.addClass("empty"),t.parent().next().is("[type=file]")){t.parent().addClass("fileinput");var n=t.parent().next().detach();t.after(n)}}),i(document).on("change",".checkbox input[type=checkbox]",function(){i(this).blur()}).on("keydown paste",".form-control",function(t){!function(t){return void 0===t.which||"number"==typeof t.which&&0<t.which&&(!t.ctrlKey&&!t.metaKey&&!t.altKey&&8!=t.which)}(t)||i(this).removeClass("empty")}).on("keyup change",".form-control",function(){var t=i(this);""===t.val()&&t[0].checkValidity()?t.addClass("empty"):t.removeClass("empty")}).on("focus",".form-control-wrapper.fileinput",function(){i(this).find("input").addClass("focus")}).on("blur",".form-control-wrapper.fileinput",function(){i(this).find("input").removeClass("focus")}).on("change",".form-control-wrapper.fileinput [type=file]",function(){var n="";i.each(i(this)[0].files,function(t,e){console.log(e),n+=e.name+", "}),(n=n.substring(0,n.length-2))?i(this).prev().removeClass("empty"):i(this).prev().addClass("empty"),i(this).prev().val(n)})},ripples:function(t){i(t||this.options.withRipples).ripples()},autofill:function(){var e,t=setInterval(function(){i("input[type!=checkbox]").each(function(){i(this).val()&&i(this).val()!==i(this).attr("value")&&i(this).trigger("change")})},100);setTimeout(function(){clearInterval(t)},1e4),i(document).on("focus","input",function(){var t=i(this).parents("form").find("input").not("[type=file]");e=setInterval(function(){t.each(function(){i(this).val()!==i(this).attr("value")&&i(this).trigger("change")})},100)}).on("blur","input",function(){clearInterval(e)})},init:function(){i.ripples&&this.options.ripples&&this.ripples(),this.options.input&&this.input(),this.options.checkbox&&this.checkbox(),this.options.togglebutton&&this.togglebutton(),this.options.radio&&this.radio(),this.options.autofill&&this.autofill(),document.arrive&&this.options.arrive&&(i(document).arrive(this.options.inputElements,function(){i.material.input(i(this))}),i(document).arrive(this.options.checkboxElements,function(){i.material.checkbox(i(this))}),i(document).arrive(this.options.radioElements,function(){i.material.radio(i(this))}),i(document).arrive(this.options.togglebuttonElements,function(){i.material.togglebutton(i(this))}))}}}(jQuery),function(a,l,e,n){"use strict";var i="ripples",u=null,r={};function o(t,e){(u=this).element=a(t),this.options=a.extend({},r,e),this._defaults=r,this._name=i,this.init()}o.prototype.init=function(){var s=this.element;s.on("mousedown touchstart",function(t){if(u.isTouch()&&"mousedown"===t.type)return!1;s.find(".ripple-wrapper").length||s.append('<div class="ripple-wrapper"></div>');var e=s.children(".ripple-wrapper"),n=u.getRelY(e,t),i=u.getRelX(e,t);if(n||i){var r=u.getRipplesColor(),o=a("<div></div>");o.addClass("ripple").css({left:i,top:n,"background-color":r}),e.append(o),l.getComputedStyle(o[0]).opacity,u.rippleOn(o),setTimeout(function(){u.rippleEnd(o)},500),s.on("mouseup mouseleave touchend",function(){o.data("mousedown","off"),"off"===o.data("animating")&&u.rippleOut(o)})}})},o.prototype.getNewSize=function(t){var e=this.element;return Math.max(e.outerWidth(),e.outerHeight())/t.outerWidth()*2.5},o.prototype.getRelX=function(t,e){var n=t.offset();return u.isTouch()?1!==(e=e.originalEvent).touches.length&&e.touches[0].pageX-n.left:e.pageX-n.left},o.prototype.getRelY=function(t,e){var n=t.offset();return u.isTouch()?1!==(e=e.originalEvent).touches.length&&e.touches[0].pageY-n.top:e.pageY-n.top},o.prototype.getRipplesColor=function(){var t=this.element;return this.options&&this.options.color?this.options.color:t.data("ripple-color")?t.data("ripple-color"):l.getComputedStyle(t[0]).color},o.prototype.hasTransitionSupport=function(){var t=(e.body||e.documentElement).style;return t.transition!==n||t.WebkitTransition!==n||t.MozTransition!==n||t.MsTransition!==n||t.OTransition!==n},o.prototype.isTouch=function(){return/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)},o.prototype.rippleEnd=function(t){t.data("animating","off"),"off"===t.data("mousedown")&&u.rippleOut(t)},o.prototype.rippleOut=function(t){t.off(),u.hasTransitionSupport()?t.addClass("ripple-out"):t.animate({opacity:0},100,function(){t.trigger("transitionend")}),t.on("transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd",function(){t.remove()})},o.prototype.rippleOn=function(t){var e=u.getNewSize(t),n=this.element;u.hasTransitionSupport()?t.css({"-ms-transform":"scale("+e+")","-moz-transform":"scale("+e+")","-webkit-transform":"scale("+e+")",transform:"scale("+e+")"}).addClass("ripple-on").data("animating","on").data("mousedown","on"):t.animate({width:2*Math.max(n.outerWidth(),n.outerHeight()),height:2*Math.max(n.outerWidth(),n.outerHeight()),"margin-left":-1*Math.max(n.outerWidth(),n.outerHeight()),"margin-top":-1*Math.max(n.outerWidth(),n.outerHeight()),opacity:.2},500,function(){t.trigger("transitionend")})},a.fn.ripples=function(t){return this.each(function(){a.data(this,"plugin_"+i)||a.data(this,"plugin_"+i,new o(this,t))})}}(jQuery,window,document),function(o){function s(t){return null!=t}o(document).ready(function(){o("body").append("<div id=snackbar-container/>")}),o(document).on("click","[data-toggle=snackbar]",function(){o(this).snackbar("toggle")}).on("click","#snackbar-container .snackbar",function(){o(this).snackbar("hide")}),o.snackbar=function(t){if(s(t)&&t===Object(t)){var e,n=(e=s(t.id)?o("#"+t.id):o("<div/>").attr("id","snackbar"+Date.now()).attr("class","snackbar")).hasClass("snackbar-opened");s(t.style)?e.attr("class","snackbar "+t.style):e.attr("class","snackbar"),t.timeout=s(t.timeout)?t.timeout:3e3,s(t.content)&&(e.find(".snackbar-content").length?e.find(".snackbar-content").text(t.content):e.prepend("<span class=snackbar-content>"+t.content+"</span>")),s(t.id)?e.insertAfter("#snackbar-container .snackbar:last-child"):e.appendTo("#snackbar-container"),s(t.action)&&"toggle"==t.action&&(t.action=n?"hide":"show");var i=Date.now();e.data("animationId1",i),setTimeout(function(){e.data("animationId1")===i&&(s(t.action)&&"show"!=t.action?s(t.action)&&"hide"==t.action&&e.removeClass("snackbar-opened"):e.addClass("snackbar-opened"))},50);var r=Date.now();return e.data("animationId2",r),0!==t.timeout&&setTimeout(function(){e.data("animationId2")===r&&e.removeClass("snackbar-opened")},t.timeout),e}return!1},o.fn.snackbar=function(t){var e={};if(this.hasClass("snackbar"))return e.id=this.attr("id"),"show"!==t&&"hide"!==t&&"toggle"!=t||(e.action=t),o.snackbar(e);s(t)&&"show"!==t&&"hide"!==t&&"toggle"!=t||(e={content:o(this).attr("data-content"),style:o(this).attr("data-style"),timeout:o(this).attr("data-timeout")}),s(t)&&(e.id=this.attr("data-snackbar-id"),"show"!==t&&"hide"!==t&&"toggle"!=t||(e.action=t));var n=o.snackbar(e);return this.attr("data-snackbar-id",n.attr("id")),n}}(jQuery),function o(s,a,l){function u(n,t){if(!a[n]){if(!s[n]){var e="function"==typeof require&&require;if(!t&&e)return e(n,!0);if(c)return c(n,!0);var i=new Error("Cannot find module '"+n+"'");throw i.code="MODULE_NOT_FOUND",i}var r=a[n]={exports:{}};s[n][0].call(r.exports,function(t){var e=s[n][1][t];return u(e||t)},r,r.exports,o,s,a,l)}return a[n].exports}for(var c="function"==typeof require&&require,t=0;t<l.length;t++)u(l[t]);return u}({1:[function(t,e,n){if(!p)var p={map:function(t,n){var i={};return n?t.map(function(t,e){return i.index=e,n.call(i,t)}):t.slice()},naturalOrder:function(t,e){return t<e?-1:e<t?1:0},sum:function(t,i){var r={};return t.reduce(i?function(t,e,n){return r.index=n,t+i.call(r,e)}:function(t,e){return t+e},0)},max:function(t,e){return Math.max.apply(null,e?p.map(t,e):t)}};var d,h,i=(h=8-(d=5),f.prototype={volume:function(t){return this._volume&&!t||(this._volume=(this.r2-this.r1+1)*(this.g2-this.g1+1)*(this.b2-this.b1+1)),this._volume},count:function(t){var e=this,n=e.histo;if(!e._count_set||t){var i,r,o,s=0;for(i=e.r1;i<=e.r2;i++)for(r=e.g1;r<=e.g2;r++)for(o=e.b1;o<=e.b2;o++)index=g(i,r,o),s+=n[index]||0;e._count=s,e._count_set=!0}return e._count},copy:function(){return new f(this.r1,this.r2,this.g1,this.g2,this.b1,this.b2,this.histo)},avg:function(t){var e=this,n=e.histo;if(!e._avg||t){var i,r,o,s,a=0,l=1<<8-d,u=0,c=0,h=0;for(r=e.r1;r<=e.r2;r++)for(o=e.g1;o<=e.g2;o++)for(s=e.b1;s<=e.b2;s++)a+=i=n[g(r,o,s)]||0,u+=i*(r+.5)*l,c+=i*(o+.5)*l,h+=i*(s+.5)*l;e._avg=a?[~~(u/a),~~(c/a),~~(h/a)]:[~~(l*(e.r1+e.r2+1)/2),~~(l*(e.g1+e.g2+1)/2),~~(l*(e.b1+e.b2+1)/2)]}return e._avg},contains:function(t){var e=t[0]>>h;return gval=t[1]>>h,bval=t[2]>>h,e>=this.r1&&e<=this.r2&&gval>=this.g1&&gval<=this.g2&&bval>=this.b1&&bval<=this.b2}},u.prototype={push:function(t){this.vboxes.push({vbox:t,color:t.avg()})},palette:function(){return this.vboxes.map(function(t){return t.color})},size:function(){return this.vboxes.size()},map:function(t){for(var e=this.vboxes,n=0;n<e.size();n++)if(e.peek(n).vbox.contains(t))return e.peek(n).color;return this.nearest(t)},nearest:function(t){for(var e,n,i,r=this.vboxes,o=0;o<r.size();o++)((n=Math.sqrt(Math.pow(t[0]-r.peek(o).color[0],2)+Math.pow(t[1]-r.peek(o).color[1],2)+Math.pow(t[2]-r.peek(o).color[2],2)))<e||void 0===e)&&(e=n,i=r.peek(o).color);return i},forcebw:function(){var t=this.vboxes;t.sort(function(t,e){return p.naturalOrder(p.sum(t.color),p.sum(e.color))});var e=t[0].color;e[0]<5&&e[1]<5&&e[2]<5&&(t[0].color=[0,0,0]);var n=t.length-1,i=t[n].color;251<i[0]&&251<i[1]&&251<i[2]&&(t[n].color=[255,255,255])}},{quantize:function(t,e){if(!t.length||e<2||256<e)return!1;var l=function(t){var e,n,i,r,o=new Array(1<<3*d);return t.forEach(function(t){n=t[0]>>h,i=t[1]>>h,r=t[2]>>h,e=g(n,i,r),o[e]=(o[e]||0)+1}),o}(t);l.forEach(function(){});var n=function(t,e){var n,i,r,o=1e6,s=0,a=1e6,l=0,u=1e6,c=0;return t.forEach(function(t){n=t[0]>>h,i=t[1]>>h,r=t[2]>>h,n<o?o=n:s<n&&(s=n),i<a?a=i:l<i&&(l=i),r<u?u=r:c<r&&(c=r)}),new f(o,s,a,l,u,c,e)}(t,l),i=new a(function(t,e){return p.naturalOrder(t.count(),e.count())});function r(t,e){for(var n,i=1,r=0;r<1e3;)if((n=t.pop()).count()){var o=c(l,n),s=o[0],a=o[1];if(!s)return;if(t.push(s),a&&(t.push(a),i++),e<=i)return;if(1e3<r++)return}else t.push(n),r++}i.push(n),r(i,.75*e);for(var o=new a(function(t,e){return p.naturalOrder(t.count()*t.volume(),e.count()*e.volume())});i.size();)o.push(i.pop());r(o,e-o.size());for(var s=new u;o.size();)s.push(o.pop());return s}});function g(t,e,n){return(t<<2*d)+(e<<d)+n}function a(t){var e=[],n=!1;function i(){e.sort(t),n=!0}return{push:function(t){e.push(t),n=!1},peek:function(t){return n||i(),void 0===t&&(t=e.length-1),e[t]},pop:function(){return n||i(),e.pop()},size:function(){return e.length},map:function(t){return e.map(t)},debug:function(){return n||i(),e}}}function f(t,e,n,i,r,o,s){this.r1=t,this.r2=e,this.g1=n,this.g2=i,this.b1=r,this.b2=o,this.histo=s}function u(){this.vboxes=new a(function(t,e){return p.naturalOrder(t.vbox.count()*t.vbox.volume(),e.vbox.count()*e.vbox.volume())})}function c(t,u){if(u.count()){var e=u.r2-u.r1+1,n=u.g2-u.g1+1,i=u.b2-u.b1+1,r=p.max([e,n,i]);if(1==u.count())return[u.copy()];var c,o,s,a,h=0,d=[],f=[];if(r==e)for(c=u.r1;c<=u.r2;c++){for(a=0,o=u.g1;o<=u.g2;o++)for(s=u.b1;s<=u.b2;s++)a+=t[g(c,o,s)]||0;h+=a,d[c]=h}else if(r==n)for(c=u.g1;c<=u.g2;c++){for(a=0,o=u.r1;o<=u.r2;o++)for(s=u.b1;s<=u.b2;s++)a+=t[g(o,c,s)]||0;h+=a,d[c]=h}else for(c=u.b1;c<=u.b2;c++){for(a=0,o=u.r1;o<=u.r2;o++)for(s=u.g1;s<=u.g2;s++)a+=t[g(o,s,c)]||0;h+=a,d[c]=h}return d.forEach(function(t,e){f[e]=h-t}),l(r==e?"r":r==n?"g":"b")}function l(t){var e,n,i,r,o,s=t+"1",a=t+"2",l=0;for(c=u[s];c<=u[a];c++)if(d[c]>h/2){for(i=u.copy(),r=u.copy(),o=(e=c-u[s])<=(n=u[a]-c)?Math.min(u[a]-1,~~(c+n/2)):Math.max(u[s],~~(c-1-e/2));!d[o];)o++;for(l=f[o];!l&&d[o-1];)l=f[--o];return i[a]=o,r[s]=i[a]+1,[i,r]}}}e.exports=i.quantize},{}],2:[function(r,t,e){(function(){var f,p,t,o=[].slice;function e(t,e){this.rgb=t,this.population=e}function n(t,e,n){var i,r,o,s,a,l,u,c,h,d;for(this.swatches=function(t,e){return function(){return t.apply(e,arguments)}}(this.swatches,this),void 0===e&&(e=64),void 0===n&&(n=5),h=(l=new f(t)).getImageData().data,c=l.getPixelCount(),i=[],a=0;a<c;)d=h[0+(u=4*a)],s=h[1+u],r=h[2+u],125<=h[3+u]&&(250<d&&250<s&&250<r||i.push([d,s,r])),a+=n;o=this.quantize(i,e),this._swatches=o.vboxes.map(function(t){return new p(t.color,t.vbox.count())}),this.maxPopulation=this.findMaxPopulation,this.generateVarationColors(),this.generateEmptySwatches(),l.removeCanvas()}function i(t){this.canvas=document.createElement("canvas"),this.context=this.canvas.getContext("2d"),document.body.appendChild(this.canvas),this.width=this.canvas.width=t.width,this.height=this.canvas.height=t.height,this.context.drawImage(t,0,0,this.width,this.height)}window.Swatch=(e.prototype.hsl=void 0,e.prototype.rgb=void 0,e.prototype.population=1,e.yiq=0,e.prototype.getHsl=function(){return this.hsl?this.hsl:this.hsl=t.rgbToHsl(this.rgb[0],this.rgb[1],this.rgb[2])},e.prototype.getPopulation=function(){return this.population},e.prototype.getRgb=function(){return this.rgb},e.prototype.getHex=function(){return"#"+((1<<24)+(this.rgb[0]<<16)+(this.rgb[1]<<8)+this.rgb[2]).toString(16).slice(1,7)},e.prototype.getTitleTextColor=function(){return this._ensureTextColors(),this.yiq<200?"#fff":"#000"},e.prototype.getBodyTextColor=function(){return this._ensureTextColors(),this.yiq<150?"#fff":"#000"},e.prototype._ensureTextColors=function(){if(!this.yiq)return this.yiq=(299*this.rgb[0]+587*this.rgb[1]+114*this.rgb[2])/1e3},p=e),window.Vibrant=(n.prototype.quantize=r("quantize"),n.prototype._swatches=[],n.prototype.TARGET_DARK_LUMA=.26,n.prototype.MAX_DARK_LUMA=.45,n.prototype.MIN_LIGHT_LUMA=.55,n.prototype.TARGET_LIGHT_LUMA=.74,n.prototype.MIN_NORMAL_LUMA=.3,n.prototype.TARGET_NORMAL_LUMA=.5,n.prototype.MAX_NORMAL_LUMA=.7,n.prototype.TARGET_MUTED_SATURATION=.3,n.prototype.MAX_MUTED_SATURATION=.4,n.prototype.TARGET_VIBRANT_SATURATION=1,n.prototype.MIN_VIBRANT_SATURATION=.35,n.prototype.WEIGHT_SATURATION=3,n.prototype.WEIGHT_LUMA=6,n.prototype.WEIGHT_POPULATION=1,n.prototype.VibrantSwatch=void 0,n.prototype.MutedSwatch=void 0,n.prototype.DarkVibrantSwatch=void 0,n.prototype.DarkMutedSwatch=void 0,n.prototype.LightVibrantSwatch=void 0,n.prototype.LightMutedSwatch=void 0,n.prototype.HighestPopulation=0,n.prototype.generateVarationColors=function(){return this.VibrantSwatch=this.findColorVariation(this.TARGET_NORMAL_LUMA,this.MIN_NORMAL_LUMA,this.MAX_NORMAL_LUMA,this.TARGET_VIBRANT_SATURATION,this.MIN_VIBRANT_SATURATION,1),this.LightVibrantSwatch=this.findColorVariation(this.TARGET_LIGHT_LUMA,this.MIN_LIGHT_LUMA,1,this.TARGET_VIBRANT_SATURATION,this.MIN_VIBRANT_SATURATION,1),this.DarkVibrantSwatch=this.findColorVariation(this.TARGET_DARK_LUMA,0,this.MAX_DARK_LUMA,this.TARGET_VIBRANT_SATURATION,this.MIN_VIBRANT_SATURATION,1),this.MutedSwatch=this.findColorVariation(this.TARGET_NORMAL_LUMA,this.MIN_NORMAL_LUMA,this.MAX_NORMAL_LUMA,this.TARGET_MUTED_SATURATION,0,this.MAX_MUTED_SATURATION),this.LightMutedSwatch=this.findColorVariation(this.TARGET_LIGHT_LUMA,this.MIN_LIGHT_LUMA,1,this.TARGET_MUTED_SATURATION,0,this.MAX_MUTED_SATURATION),this.DarkMutedSwatch=this.findColorVariation(this.TARGET_DARK_LUMA,0,this.MAX_DARK_LUMA,this.TARGET_MUTED_SATURATION,0,this.MAX_MUTED_SATURATION)},n.prototype.generateEmptySwatches=function(){var t;if(void 0===this.VibrantSwatch&&void 0!==this.DarkVibrantSwatch&&((t=this.DarkVibrantSwatch.getHsl())[2]=this.TARGET_NORMAL_LUMA,this.VibrantSwatch=new p(n.hslToRgb(t[0],t[1],t[2]),0)),void 0===this.DarkVibrantSwatch&&void 0!==this.VibrantSwatch)return(t=this.VibrantSwatch.getHsl())[2]=this.TARGET_DARK_LUMA,this.DarkVibrantSwatch=new p(n.hslToRgb(t[0],t[1],t[2]),0)},n.prototype.findMaxPopulation=function(){var t,e,n,i,r;for(t=n=0,e=(i=this._swatches).length;t<e;t++)r=i[t],n=Math.max(n,r.getPopulation());return n},n.prototype.findColorVariation=function(t,e,n,i,r,o){var s,a,l,u,c,h,d,f,p;for(u=void 0,s=c=0,a=(h=this._swatches).length;s<a;s++)d=(f=h[s]).getHsl()[1],l=f.getHsl()[2],r<=d&&d<=o&&e<=l&&l<=n&&!this.isAlreadySelected(f)&&(p=this.createComparisonValue(d,i,l,t,f.getPopulation(),this.HighestPopulation),(void 0===u||c<p)&&(u=f,c=p));return u},n.prototype.createComparisonValue=function(t,e,n,i,r,o){return this.weightedMean(this.invertDiff(t,e),this.WEIGHT_SATURATION,this.invertDiff(n,i),this.WEIGHT_LUMA,r/o,this.WEIGHT_POPULATION)},n.prototype.invertDiff=function(t,e){return 1-Math.abs(t-e)},n.prototype.weightedMean=function(){var t,e,n,i,r;for(i=1<=arguments.length?o.call(arguments,0):[],t=n=e=0;t<i.length;)e+=i[t]*(r=i[t+1]),n+=r,t+=2;return e/n},n.prototype.swatches=function(){return{Vibrant:this.VibrantSwatch,Muted:this.MutedSwatch,DarkVibrant:this.DarkVibrantSwatch,DarkMuted:this.DarkMutedSwatch,LightVibrant:this.LightVibrantSwatch,LightMuted:this.LightMuted}},n.prototype.isAlreadySelected=function(t){return this.VibrantSwatch===t||this.DarkVibrantSwatch===t||this.LightVibrantSwatch===t||this.MutedSwatch===t||this.DarkMutedSwatch===t||this.LightMutedSwatch===t},n.rgbToHsl=function(t,e,n){var i,r,o,s,a,l;if(t/=255,e/=255,n/=255,l=r=void 0,o=((s=Math.max(t,e,n))+(a=Math.min(t,e,n)))/2,s===a)r=l=0;else{switch(i=s-a,l=.5<o?i/(2-s-a):i/(s+a),s){case t:r=(e-n)/i+(e<n?6:0);break;case e:r=(n-t)/i+2;break;case n:r=(t-e)/i+4}r/=6}return[r,l,o]},n.hslToRgb=function(t,e,n){var i,r,o,s,a,l;return i=r=l=void 0,o=function(t,e,n){return n<0&&(n+=1),1<n&&(n-=1),n<1/6?t+6*(e-t)*n:n<.5?e:n<2/3?t+(e-t)*(2/3-n)*6:t},0===e?l=r=i=n:(l=o(s=2*n-(a=n<.5?n*(1+e):n+e-n*e),a,t+1/3),r=o(s,a,t),i=o(s,a,t-1/3)),[255*l,255*r,255*i]},t=n),window.CanvasImage=(i.prototype.clear=function(){return this.context.clearRect(0,0,this.width,this.height)},i.prototype.update=function(t){return this.context.putImageData(t,0,0)},i.prototype.getPixelCount=function(){return this.width*this.height},i.prototype.getImageData=function(){return this.context.getImageData(0,0,this.width,this.height)},i.prototype.removeCanvas=function(){return this.canvas.parentNode.removeChild(this.canvas)},f=i)}).call(this)},{quantize:1}]},{},[2]),function(fe,pe){"use strict";if(!fe||!fe.document)throw new Error("SoundManager requires a browser with window and document objects.");var t=null;function n(t,e){this.setupOptions={url:t||null,flashVersion:8,debugMode:!0,debugFlash:!1,useConsole:!0,consoleOnly:!0,waitForWindowLoad:!1,bgColor:"#ffffff",useHighPerformance:!1,flashPollingInterval:null,html5PollingInterval:null,flashLoadTimeout:1e3,wmode:null,allowScriptAccess:"always",useFlashBlock:!1,useHTML5Audio:!0,forceUseGlobalHTML5Audio:!1,ignoreMobileRestrictions:!1,html5Test:/^(probably|maybe)$/i,preferFlash:!1,noSWFCache:!1,idPrefix:"sound"},this.defaultOptions={autoLoad:!1,autoPlay:!1,from:null,loops:1,onid3:null,onerror:null,onload:null,whileloading:null,onplay:null,onpause:null,onresume:null,whileplaying:null,onposition:null,onstop:null,onfinish:null,multiShot:!0,multiShotEvents:!1,position:null,pan:0,playbackRate:1,stream:!0,to:null,type:null,usePolicyFile:!1,volume:100},this.flash9Options={onfailure:null,isMovieStar:null,usePeakData:!1,useWaveformData:!1,useEQData:!1,onbufferchange:null,ondataerror:null},this.movieStarOptions={bufferTime:3,serverURL:null,onconnect:null,duration:null},this.audioFormats={mp3:{type:['audio/mpeg; codecs="mp3"',"audio/mpeg","audio/mp3","audio/MPA","audio/mpa-robust"],required:!0},mp4:{related:["aac","m4a","m4b"],type:['audio/mp4; codecs="mp4a.40.2"',"audio/aac","audio/x-m4a","audio/MP4A-LATM","audio/mpeg4-generic"],required:!1},ogg:{type:["audio/ogg; codecs=vorbis"],required:!1},opus:{type:["audio/ogg; codecs=opus","audio/opus"],required:!1},wav:{type:['audio/wav; codecs="1"',"audio/wav","audio/wave","audio/x-wav"],required:!1},flac:{type:["audio/flac"],required:!1}},this.movieID="sm2-container",this.id=e||"sm2movie",this.debugID="soundmanager-debug",this.debugURLParam=/([#?&])debug=1/i,this.versionNumber="V2.97a.20170601",this.version=null,this.movieURL=null,this.altURL=null,this.swfLoaded=!1,this.enabled=!1,this.oMC=null,this.sounds={},this.soundIDs=[],this.muted=!1,this.didFlashBlock=!1,this.filePattern=null,this.filePatterns={flash8:/\.mp3(\?.*)?$/i,flash9:/\.mp3(\?.*)?$/i},this.features={buffering:!1,peakData:!1,waveformData:!1,eqData:!1,movieStar:!1},this.sandbox={type:null,types:{remote:"remote (domain-based) rules",localWithFile:"local with file access (no internet access)",localWithNetwork:"local with network (internet access only, no local access)",localTrusted:"local, trusted (local+internet access)"},description:null,noRemote:null,noLocal:null},this.html5={usingFlash:null},this.flash={},this.html5Only=!1,this.ignoreFlash=!1;var a,y,n,i,b,m,w,_,r,x,l,u,o,s,c,h,d,f,S,p,T,g,v,C,E,k,M,D,O,A,N,j,R,L,I,P,F,H,q,$,U,B,V,z,W,J,G,X,Y,K,Q,Z,tt,et,nt,it,rt,ot,st,at,lt,ut,ct,ht,dt,ft,pt,gt=this,vt=null,mt=null,yt="soundManager",bt=yt+": ",wt="HTML5::",_t=navigator.userAgent,xt=fe.location.href.toString(),St=document,Tt=[],Ct=!0,Et=!1,kt=!1,Mt=!1,Dt=!1,Ot=!1,At=0,Nt=["log","info","warn","error"],jt=null,Rt=null,Lt=!1,It=!1,Pt=0,Ft=null,Ht=[],qt=null,$t=Array.prototype.slice,Ut=!1,Bt=0,Vt=_t.match(/(ipad|iphone|ipod)/i),zt=_t.match(/android/i),Wt=_t.match(/msie|trident/i),Jt=_t.match(/webkit/i),Gt=_t.match(/safari/i)&&!_t.match(/chrome/i),Xt=_t.match(/opera/i),Yt=_t.match(/(mobile|pre\/|xoom)/i)||Vt||zt,Kt=!xt.match(/usehtml5audio/i)&&!xt.match(/sm2-ignorebadua/i)&&Gt&&!_t.match(/silk/i)&&_t.match(/OS\sX\s10_6_([3-7])/i),Qt=fe.console!==pe&&console.log!==pe,Zt=St.hasFocus!==pe?St.hasFocus():null,te=Gt&&(St.hasFocus===pe||!St.hasFocus()),ee=!te,ne=/(mp3|mp4|mpa|m4a|m4b)/i,ie="about:blank",re=St.location?St.location.protocol.match(/http/i):null,oe=re?"":"//",se=/^\s*audio\/(?:x-)?(?:mpeg4|aac|flv|mov|mp4|m4v|m4a|m4b|mp4v|3gp|3g2)\s*(?:$|;)/i,ae=["mpeg4","aac","flv","mov","mp4","m4v","f4v","m4a","m4b","mp4v","3gp","3g2"],le=new RegExp("\\.("+ae.join("|")+")(\\?.*)?$","i");function ue(t){return gt.preferFlash&&ot&&!gt.ignoreFlash&&gt.flash[t]!==pe&&gt.flash[t]}function ce(t){var e=$t.call(t),n=e.length;return ft?(e[1]="on"+e[1],3<n&&e.pop()):3===n&&e.push(!1),e}function he(t,e){var n=t.shift(),i=[pt[e]];ft?n[i](t[0],t[1]):n[i].apply(n,t)}function de(n){return function(t){var e=this._s;return e&&e._a?n.call(this,t):(e&&e.id?gt._wD(e.id+": Ignoring "+t.type):gt._wD(wt+"Ignoring "+t.type),null)}}this.mimePattern=/^\s*audio\/(?:x-)?(?:mp(?:eg|3))\s*(?:$|;)/i,this.useAltURL=!re,F={swfBox:"sm2-object-box",swfDefault:"movieContainer",swfError:"swf_error",swfTimedout:"swf_timedout",swfLoaded:"swf_loaded",swfUnblocked:"swf_unblocked",sm2Debug:"sm2_debug",highPerf:"high_performance",flashDebug:"flash_debug"},Q=[null,"MEDIA_ERR_ABORTED","MEDIA_ERR_NETWORK","MEDIA_ERR_DECODE","MEDIA_ERR_SRC_NOT_SUPPORTED"],this.hasHTML5=function(){try{return Audio!==pe&&(Xt&&opera!==pe&&opera.version()<10?new Audio(null):new Audio).canPlayType!==pe}catch(t){return!1}}(),this.setup=function(t){var e=!gt.url;return t!==pe&&Mt&&qt&&gt.ok()&&(t.flashVersion!==pe||t.url!==pe||t.html5Test!==pe)&&U(L("setupLate")),l(t),Ut||(Yt?gt.setupOptions.ignoreMobileRestrictions&&!gt.setupOptions.forceUseGlobalHTML5Audio||(Ht.push(T.globalHTML5),Ut=!0):gt.setupOptions.forceUseGlobalHTML5Audio&&(Ht.push(T.globalHTML5),Ut=!0)),!dt&&Yt&&(gt.setupOptions.ignoreMobileRestrictions?Ht.push(T.ignoreMobile):(gt.setupOptions.useHTML5Audio&&!gt.setupOptions.preferFlash||gt._wD(T.mobileUA),gt.setupOptions.useHTML5Audio=!0,gt.setupOptions.preferFlash=!1,Vt?gt.ignoreFlash=!0:(!zt||_t.match(/android\s2\.3/i))&&zt||(gt._wD(T.globalHTML5),Ut=!0))),t&&(e&&E&&t.url!==pe&&gt.beginDelayedInit(),E||t.url===pe||"complete"!==St.readyState||setTimeout(v,1)),dt=!0,gt},this.ok=function(){return qt?Mt&&!Dt:gt.useHTML5Audio&&gt.hasHTML5},this.supported=this.ok,this.getMovie=function(t){return y(t)||St[t]||fe[t]},this.createSound=function(t,e){var n,i,r,o=null;if(i=(n=yt+".createSound(): ")+L(Mt?"notOK":"notReady"),!Mt||!gt.ok())return U(i),!1;if(e!==pe&&(t={id:t,url:e}),(r=x(t)).url=J(r.url),r.id===pe&&(r.id=gt.setupOptions.idPrefix+Bt++),r.id.toString().charAt(0).match(/^[0-9]$/)&&gt._wD(n+L("badID",r.id),2),gt._wD(n+r.id+(r.url?" ("+r.url+")":""),1),B(r.id,!0))return gt._wD(n+r.id+" exists",1),gt.sounds[r.id];function s(){return r=q(r),gt.sounds[r.id]=new a(r),gt.soundIDs.push(r.id),gt.sounds[r.id]}if(Y(r))o=s(),gt.html5Only||gt._wD(r.id+": Using HTML5"),o._setup_html5(r);else{if(gt.html5Only)return gt._wD(r.id+": No HTML5 support for this sound, and no Flash. Exiting."),s();if(gt.html5.usingFlash&&r.url&&r.url.match(/data:/i))return gt._wD(r.id+": data: URIs not supported via Flash. Exiting."),s();8<m&&(null===r.isMovieStar&&(r.isMovieStar=!!(r.serverURL||r.type&&r.type.match(se)||r.url&&r.url.match(le))),r.isMovieStar&&(gt._wD(n+"using MovieStar handling"),1<r.loops&&_("noNSLoop"))),r=$(r,n),o=s(),8===m?mt._createSound(r.id,r.loops||1,r.usePolicyFile):(mt._createSound(r.id,r.url,r.usePeakData,r.useWaveformData,r.useEQData,r.isMovieStar,!!r.isMovieStar&&r.bufferTime,r.loops||1,r.serverURL,r.duration||null,r.autoPlay,!0,r.autoLoad,r.usePolicyFile),r.serverURL||(o.connected=!0,r.onconnect&&r.onconnect.apply(o))),r.serverURL||!r.autoLoad&&!r.autoPlay||o.load(r)}return!r.serverURL&&r.autoPlay&&o.play(),o},this.destroySound=function(t,e){if(!B(t))return!1;var n,i=gt.sounds[t];for(i.stop(),i._iO={},i.unload(),n=0;n<gt.soundIDs.length;n++)if(gt.soundIDs[n]===t){gt.soundIDs.splice(n,1);break}return e||i.destruct(!0),i=null,delete gt.sounds[t],!0},this.load=function(t,e){return!!B(t)&&gt.sounds[t].load(e)},this.unload=function(t){return!!B(t)&&gt.sounds[t].unload()},this.onPosition=function(t,e,n,i){return!!B(t)&&gt.sounds[t].onposition(e,n,i)},this.onposition=this.onPosition,this.clearOnPosition=function(t,e,n){return!!B(t)&&gt.sounds[t].clearOnPosition(e,n)},this.play=function(t,e){var n=null,i=e&&!(e instanceof Object);if(!Mt||!gt.ok())return U(yt+".play(): "+L(Mt?"notOK":"notReady")),!1;if(B(t,i))i&&(e={url:e});else{if(!i)return!1;i&&(e={url:e}),e&&e.url&&(gt._wD(yt+'.play(): Attempting to create "'+t+'"',1),e.id=t,n=gt.createSound(e).play())}return null===n&&(n=gt.sounds[t].play(e)),n},this.start=this.play,this.setPlaybackRate=function(t,e,n){return!!B(t)&&gt.sounds[t].setPlaybackRate(e,n)},this.setPosition=function(t,e){return!!B(t)&&gt.sounds[t].setPosition(e)},this.stop=function(t){return!!B(t)&&(gt._wD(yt+".stop("+t+")",1),gt.sounds[t].stop())},this.stopAll=function(){var t;for(t in gt._wD(yt+".stopAll()",1),gt.sounds)gt.sounds.hasOwnProperty(t)&&gt.sounds[t].stop()},this.pause=function(t){return!!B(t)&&gt.sounds[t].pause()},this.pauseAll=function(){var t;for(t=gt.soundIDs.length-1;0<=t;t--)gt.sounds[gt.soundIDs[t]].pause()},this.resume=function(t){return!!B(t)&&gt.sounds[t].resume()},this.resumeAll=function(){var t;for(t=gt.soundIDs.length-1;0<=t;t--)gt.sounds[gt.soundIDs[t]].resume()},this.togglePause=function(t){return!!B(t)&&gt.sounds[t].togglePause()},this.setPan=function(t,e){return!!B(t)&&gt.sounds[t].setPan(e)},this.setVolume=function(t,e){var n,i;if(t===pe||isNaN(t)||e!==pe)return!!B(t)&&gt.sounds[t].setVolume(e);for(n=0,i=gt.soundIDs.length;n<i;n++)gt.sounds[gt.soundIDs[n]].setVolume(t);return!1},this.mute=function(t){var e=0;if(t instanceof String&&(t=null),t)return!!B(t)&&(gt._wD(yt+'.mute(): Muting "'+t+'"'),gt.sounds[t].mute());for(gt._wD(yt+".mute(): Muting all sounds"),e=gt.soundIDs.length-1;0<=e;e--)gt.sounds[gt.soundIDs[e]].mute();return gt.muted=!0},this.muteAll=function(){gt.mute()},this.unmute=function(t){var e;if(t instanceof String&&(t=null),t)return!!B(t)&&(gt._wD(yt+'.unmute(): Unmuting "'+t+'"'),gt.sounds[t].unmute());for(gt._wD(yt+".unmute(): Unmuting all sounds"),e=gt.soundIDs.length-1;0<=e;e--)gt.sounds[gt.soundIDs[e]].unmute();return!(gt.muted=!1)},this.unmuteAll=function(){gt.unmute()},this.toggleMute=function(t){return!!B(t)&&gt.sounds[t].toggleMute()},this.getMemoryUse=function(){var t=0;return mt&&8!==m&&(t=parseInt(mt._getMemoryUse(),10)),t},this.disable=function(t){var e;if(t===pe&&(t=!1),Dt)return!1;for(Dt=!0,_("shutdown",1),e=gt.soundIDs.length-1;0<=e;e--)N(gt.sounds[gt.soundIDs[e]]);return N(gt),r(t),it.remove(fe,"load",c),!0},this.canPlayMIME=function(t){var e;return gt.hasHTML5&&(e=K({type:t})),!e&&qt&&(e=t&&gt.ok()?!!(8<m&&t.match(se)||t.match(gt.mimePattern)):null),e},this.canPlayURL=function(t){var e;return gt.hasHTML5&&(e=K({url:t})),!e&&qt&&(e=t&&gt.ok()?!!t.match(gt.filePattern):null),e},this.canPlayLink=function(t){return!(t.type===pe||!t.type||!gt.canPlayMIME(t.type))||gt.canPlayURL(t.href)},this.getSoundById=function(t,e){if(!t)return null;var n=gt.sounds[t];return n||e||gt._wD(yt+'.getSoundById(): Sound "'+t+'" not found.',2),n},this.onready=function(t,e){var n="onready";if("function"!=typeof t)throw L("needFunction",n);return Mt&&gt._wD(L("queue",n)),o(n,t,e=e||fe),s(),!0},this.ontimeout=function(t,e){var n="ontimeout";if("function"!=typeof t)throw L("needFunction",n);return Mt&&gt._wD(L("queue",n)),o(n,t,e=e||fe),s({type:n}),!0},this._writeDebug=function(t,e){var n,i;return!!gt.setupOptions.debugMode&&(!!(Qt&&gt.useConsole&&(e&&"object"==typeof e?console.log(t,e):Nt[e]!==pe?console[Nt[e]](t):console.log(t),gt.consoleOnly))||!!(n=y("soundmanager-debug"))&&(i=St.createElement("div"),++At%2==0&&(i.className="sm2-alt"),e=e===pe?0:parseInt(e,10),i.appendChild(St.createTextNode(t)),e&&(2<=e&&(i.style.fontWeight="bold"),3===e&&(i.style.color="#ff3333")),n.insertBefore(i,n.firstChild),!(n=null)))},-1!==xt.indexOf("sm2-debug=alert")&&(this._writeDebug=function(t){fe.alert(t)}),this._wD=this._writeDebug,this._debug=function(){var t,e;for(_("currentObj",1),t=0,e=gt.soundIDs.length;t<e;t++)gt.sounds[gt.soundIDs[t]]._debug()},this.reboot=function(t,e){var n,i,r;for(gt.soundIDs.length&&gt._wD("Destroying "+gt.soundIDs.length+" SMSound object"+(1!==gt.soundIDs.length?"s":"")+"..."),n=gt.soundIDs.length-1;0<=n;n--)gt.sounds[gt.soundIDs[n]].destruct();if(mt)try{Wt&&(Rt=mt.innerHTML),jt=mt.parentNode.removeChild(mt)}catch(t){_("badRemove",2)}if(Rt=jt=qt=mt=null,gt.enabled=E=Mt=Lt=It=Et=kt=Dt=Ut=gt.swfLoaded=!1,gt.soundIDs=[],gt.sounds={},Bt=0,dt=!1,t)Tt=[];else for(n in Tt)if(Tt.hasOwnProperty(n))for(i=0,r=Tt[n].length;i<r;i++)Tt[n][i].fired=!1;return e||gt._wD(yt+": Rebooting..."),gt.html5={usingFlash:null},gt.flash={},gt.html5Only=!1,gt.ignoreFlash=!1,fe.setTimeout(function(){e||gt.beginDelayedInit()},20),gt},this.reset=function(){return _("reset"),gt.reboot(!0,!0)},this.getMoviePercent=function(){return mt&&"PercentLoaded"in mt?mt.PercentLoaded():null},this.beginDelayedInit=function(){Ot=!0,v(),setTimeout(function(){return!It&&(M(),g(),It=!0)},20),h()},this.destruct=function(){gt._wD(yt+".destruct()"),gt.disable(!0)},a=function(t){var o,s,e,i,u,c,r,h,a,d,f=this,p=!1,l=[],g=0,v=null;a={duration:null,time:null},this.id=t.id,this.sID=this.id,this.url=t.url,this.options=x(t),this.instanceOptions=this.options,this._iO=this.instanceOptions,this.pan=this.options.pan,this.volume=this.options.volume,this.isHTML5=!1,this._a=null,d=!this.url,this.id3={},this._debug=function(){gt._wD(f.id+": Merged options:",f.options)},this.load=function(t){var e;if(t!==pe?f._iO=x(t,f.options):(t=f.options,f._iO=t,v&&v!==f.url&&(_("manURL"),f._iO.url=f.url,f.url=null)),f._iO.url||(f._iO.url=f.url),f._iO.url=J(f._iO.url),f.instanceOptions=f._iO,e=f._iO,gt._wD(f.id+": load ("+e.url+")"),!e.url&&!f.url)return gt._wD(f.id+": load(): url is unassigned. Exiting.",2),f;if(f.isHTML5||8!==m||f.url||e.autoPlay||gt._wD(f.id+": Flash 8 load() limitation: Wait for onload() before calling play().",1),e.url===f.url&&0!==f.readyState&&2!==f.readyState)return _("onURL",1),3===f.readyState&&e.onload&&ht(f,function(){e.onload.apply(f,[!!f.duration])}),f;if(f.loaded=!1,f.readyState=1,f.playState=0,f.id3={},Y(e))f._setup_html5(e)._called_load?gt._wD(f.id+": Ignoring request to load again"):(f._html5_canplay=!1,f.url!==e.url&&(gt._wD(_("manURL")+": "+e.url),f._a.src=e.url,f.setPosition(0)),f._a.autobuffer="auto",f._a.preload="auto",f._a._called_load=!0);else{if(gt.html5Only)return gt._wD(f.id+": No flash support. Exiting."),f;if(f._iO.url&&f._iO.url.match(/data:/i))return gt._wD(f.id+": data: URIs not supported via Flash. Exiting."),f;try{f.isHTML5=!1,f._iO=$(q(e)),f._iO.autoPlay&&(f._iO.position||f._iO.from)&&(gt._wD(f.id+": Disabling autoPlay because of non-zero offset case"),f._iO.autoPlay=!1),e=f._iO,8===m?mt._load(f.id,e.url,e.stream,e.autoPlay,e.usePolicyFile):mt._load(f.id,e.url,!!e.stream,!!e.autoPlay,e.loops||1,!!e.autoLoad,e.usePolicyFile)}catch(t){_("smError",2),w("onload",!1),D({type:"SMSOUND_LOAD_JS_EXCEPTION",fatal:!0})}}return f.url=e.url,f},this.unload=function(){return 0!==f.readyState&&(gt._wD(f.id+": unload()"),f.isHTML5?(i(),f._a&&(f._a.pause(),v=tt(f._a))):8===m?mt._unload(f.id,ie):mt._unload(f.id),o()),f},this.destruct=function(t){gt._wD(f.id+": Destruct"),f.isHTML5?(i(),f._a&&(f._a.pause(),tt(f._a),Ut||e(),f._a._s=null,f._a=null)):(f._iO.onfailure=null,mt._destroySound(f.id)),t||gt.destroySound(f.id,!0)},this.play=function(t,e){var n,i,r,o,s,a,l=!0;if(n=f.id+": play(): ",e=e===pe||e,t=t||{},f.url&&(f._iO.url=f.url),f._iO=x(f._iO,f.options),f._iO=x(t,f._iO),f._iO.url=J(f._iO.url),f.instanceOptions=f._iO,!f.isHTML5&&f._iO.serverURL&&!f.connected)return f.getAutoPlay()||(gt._wD(n+" Netstream not connected yet - setting autoPlay"),f.setAutoPlay(!0)),f;if(Y(f._iO)&&(f._setup_html5(f._iO),u()),1===f.playState&&!f.paused){if(!f._iO.multiShot)return gt._wD(n+"Already playing (one-shot)",1),f.isHTML5&&f.setPosition(f._iO.position),f;gt._wD(n+"Already playing (multi-shot)",1)}if(t.url&&t.url!==f.url&&(f.readyState||f.isHTML5||8!==m||!d?f.load(f._iO):d=!1),f.loaded)gt._wD(n.substr(0,n.lastIndexOf(":")));else if(0===f.readyState){if(gt._wD(n+"Attempting to load"),f.isHTML5||gt.html5Only){if(!f.isHTML5)return gt._wD(n+"Unsupported type. Exiting."),f;f.load(f._iO)}else f._iO.autoPlay=!0,f.load(f._iO);f.instanceOptions=f._iO}else{if(2===f.readyState)return gt._wD(n+"Could not load - exiting",2),f;gt._wD(n+"Loading - attempting to play...")}return!f.isHTML5&&9===m&&0<f.position&&f.position===f.duration&&(gt._wD(n+"Sound at end, resetting to position: 0"),t.position=0),f.paused&&0<=f.position&&(!f._iO.serverURL||0<f.position)?(gt._wD(n+"Resuming from paused state",1),f.resume()):(f._iO=x(t,f._iO),(!f.isHTML5&&null!==f._iO.position&&0<f._iO.position||null!==f._iO.from&&0<f._iO.from||null!==f._iO.to)&&0===f.instanceCount&&0===f.playState&&!f._iO.serverURL&&(r=function(){f._iO=x(t,f._iO),f.play(f._iO)},f.isHTML5&&!f._html5_canplay?(gt._wD(n+"Beginning load for non-zero offset case"),f.load({_oncanplay:r})):f.isHTML5||f.loaded||f.readyState&&2===f.readyState||(gt._wD(n+"Preloading for non-zero offset case"),f.load({onload:r})),f._iO=h()),(!f.instanceCount||f._iO.multiShotEvents||f.isHTML5&&f._iO.multiShot&&!Ut||!f.isHTML5&&8<m&&!f.getAutoPlay())&&f.instanceCount++,f._iO.onposition&&0===f.playState&&c(f),f.playState=1,f.paused=!1,f.position=f._iO.position===pe||isNaN(f._iO.position)?0:f._iO.position,f.isHTML5||(f._iO=$(q(f._iO))),f._iO.onplay&&e&&(f._iO.onplay.apply(f),p=!0),f.setVolume(f._iO.volume,!0),f.setPan(f._iO.pan,!0),1!==f._iO.playbackRate&&f.setPlaybackRate(f._iO.playbackRate),f.isHTML5?f.instanceCount<2?(u(),i=f._setup_html5(),f.setPosition(f._iO.position),i.play()):(gt._wD(f.id+": Cloning Audio() for instance #"+f.instanceCount+"..."),o=new Audio(f._iO.url),s=function(){it.remove(o,"ended",s),f._onfinish(f),tt(o),o=null},a=function(){it.remove(o,"canplay",a);try{o.currentTime=f._iO.position/1e3}catch(t){U(f.id+": multiShot play() failed to apply position of "+f._iO.position/1e3)}o.play()},it.add(o,"ended",s),f._iO.volume!==pe&&(o.volume=Math.max(0,Math.min(1,f._iO.volume/100))),f.muted&&(o.muted=!0),f._iO.position?it.add(o,"canplay",a):o.play()):(l=mt._start(f.id,f._iO.loops||1,9===m?f.position:f.position/1e3,f._iO.multiShot||!1),9!==m||l||(gt._wD(n+"No sound hardware, or 32-sound ceiling hit",2),f._iO.onplayerror&&f._iO.onplayerror.apply(f)))),f},this.start=this.play,this.stop=function(t){var e,n=f._iO;return 1===f.playState&&(gt._wD(f.id+": stop()"),f._onbufferchange(0),f._resetOnPosition(0),f.paused=!1,f.isHTML5||(f.playState=0),r(),n.to&&f.clearOnPosition(n.to),f.isHTML5?f._a&&(e=f.position,f.setPosition(0),f.position=e,f._a.pause(),f.playState=0,f._onTimer(),i()):(mt._stop(f.id,t),n.serverURL&&f.unload()),f.instanceCount=0,f._iO={},n.onstop&&n.onstop.apply(f)),f},this.setAutoPlay=function(t){gt._wD(f.id+": Autoplay turned "+(t?"on":"off")),f._iO.autoPlay=t,f.isHTML5||(mt._setAutoPlay(f.id,t),t&&(f.instanceCount||1!==f.readyState||(f.instanceCount++,gt._wD(f.id+": Incremented instance count to "+f.instanceCount))))},this.getAutoPlay=function(){return f._iO.autoPlay},this.setPlaybackRate=function(t){var e=Math.max(.5,Math.min(4,t));if(e!==t&&gt._wD(f.id+": setPlaybackRate("+t+"): limiting rate to "+e,2),f.isHTML5)try{f._iO.playbackRate=e,f._a.playbackRate=e}catch(t){gt._wD(f.id+": setPlaybackRate("+e+") failed: "+t.message,2)}return f},this.setPosition=function(t){t===pe&&(t=0);var e,n,i=f.isHTML5?Math.max(t,0):Math.min(f.duration||f._iO.duration,Math.max(t,0));if(f.position=i,n=f.position/1e3,f._resetOnPosition(f.position),f._iO.position=i,f.isHTML5){if(f._a){if(f._html5_canplay){if(f._a.currentTime.toFixed(3)!==n.toFixed(3)){gt._wD(f.id+": setPosition("+n+")");try{f._a.currentTime=n,0!==f.playState&&!f.paused||f._a.pause()}catch(t){gt._wD(f.id+": setPosition("+n+") failed: "+t.message,2)}}}else if(n)return gt._wD(f.id+": setPosition("+n+"): Cannot seek yet, sound not ready",2),f;f.paused&&f._onTimer(!0)}}else e=9===m?f.position:n,f.readyState&&2!==f.readyState&&mt._setPosition(f.id,e,f.paused||!f.playState,f._iO.multiShot);return f},this.pause=function(t){return f.paused||0===f.playState&&1!==f.readyState||(gt._wD(f.id+": pause()"),f.paused=!0,f.isHTML5?(f._setup_html5().pause(),i()):!t&&t!==pe||mt._pause(f.id,f._iO.multiShot),f._iO.onpause&&f._iO.onpause.apply(f)),f},this.resume=function(){var t=f._iO;return f.paused&&(gt._wD(f.id+": resume()"),f.paused=!1,f.playState=1,f.isHTML5?(f._setup_html5().play(),u()):(t.isMovieStar&&!t.serverURL&&f.setPosition(f.position),mt._pause(f.id,t.multiShot)),!p&&t.onplay?(t.onplay.apply(f),p=!0):t.onresume&&t.onresume.apply(f)),f},this.togglePause=function(){return gt._wD(f.id+": togglePause()"),0===f.playState?f.play({position:9!==m||f.isHTML5?f.position/1e3:f.position}):f.paused?f.resume():f.pause(),f},this.setPan=function(t,e){return t===pe&&(t=0),e===pe&&(e=!1),f.isHTML5||mt._setPan(f.id,t),f._iO.pan=t,e||(f.pan=t,f.options.pan=t),f},this.setVolume=function(t,e){return t===pe&&(t=100),e===pe&&(e=!1),f.isHTML5?f._a&&(gt.muted&&!f.muted&&(f.muted=!0,f._a.muted=!0),f._a.volume=Math.max(0,Math.min(1,t/100))):mt._setVolume(f.id,gt.muted&&!f.muted||f.muted?0:t),f._iO.volume=t,e||(f.volume=t,f.options.volume=t),f},this.mute=function(){return f.muted=!0,f.isHTML5?f._a&&(f._a.muted=!0):mt._setVolume(f.id,0),f},this.unmute=function(){f.muted=!1;var t=f._iO.volume!==pe;return f.isHTML5?f._a&&(f._a.muted=!1):mt._setVolume(f.id,t?f._iO.volume:f.options.volume),f},this.toggleMute=function(){return f.muted?f.unmute():f.mute()},this.onPosition=function(t,e,n){return l.push({position:parseInt(t,10),method:e,scope:n!==pe?n:f,fired:!1}),f},this.onposition=this.onPosition,this.clearOnPosition=function(t,e){var n;if(t=parseInt(t,10),!isNaN(t))for(n=0;n<l.length;n++)t===l[n].position&&(e&&e!==l[n].method||(l[n].fired&&g--,l.splice(n,1)))},this._processOnPosition=function(){var t,e,n=l.length;if(!n||!f.playState||n<=g)return!1;for(t=n-1;0<=t;t--)!(e=l[t]).fired&&f.position>=e.position&&(e.fired=!0,g++,e.method.apply(e.scope,[e.position]),n=l.length);return!0},this._resetOnPosition=function(t){var e,n,i=l.length;if(!i)return!1;for(e=i-1;0<=e;e--)(n=l[e]).fired&&t<=n.position&&(n.fired=!1,g--);return!0},h=function(){var t,e,n=f._iO,i=n.from,r=n.to;return e=function(){gt._wD(f.id+': "To" time of '+r+" reached."),f.clearOnPosition(r,e),f.stop()},t=function(){gt._wD(f.id+': Playing "from" '+i),null===r||isNaN(r)||f.onPosition(r,e)},null===i||isNaN(i)||(n.position=i,n.multiShot=!1,t()),n},c=function(){var t,e=f._iO.onposition;if(e)for(t in e)e.hasOwnProperty(t)&&f.onPosition(parseInt(t,10),e[t])},r=function(){var t,e=f._iO.onposition;if(e)for(t in e)e.hasOwnProperty(t)&&f.clearOnPosition(parseInt(t,10))},u=function(){f.isHTML5&&V(f)},i=function(){f.isHTML5&&z(f)},(o=function(t){t||(l=[],g=0),p=!1,f._hasTimer=null,f._a=null,f._html5_canplay=!1,f.bytesLoaded=null,f.bytesTotal=null,f.duration=f._iO&&f._iO.duration?f._iO.duration:null,f.durationEstimate=null,f.buffered=[],f.eqData=[],f.eqData.left=[],f.eqData.right=[],f.failures=0,f.isBuffering=!1,f.instanceOptions={},f.instanceCount=0,f.loaded=!1,f.metadata={},f.readyState=0,f.muted=!1,f.paused=!1,f.peakData={left:0,right:0},f.waveformData={left:[],right:[]},f.playState=0,f.position=null,f.id3={}})(),this._onTimer=function(t){var e,n,i=!1,r={};return(f._hasTimer||t)&&f._a&&(t||(0<f.playState||1===f.readyState)&&!f.paused)&&((e=f._get_html5_duration())!==a.duration&&(a.duration=e,f.duration=e,i=!0),f.durationEstimate=f.duration,(n=1e3*f._a.currentTime||0)!==a.time&&(a.time=n,i=!0),(i||t)&&f._whileplaying(n,r,r,r,r)),i},this._get_html5_duration=function(){var t=f._iO,e=f._a&&f._a.duration?1e3*f._a.duration:t&&t.duration?t.duration:null;return e&&!isNaN(e)&&e!==1/0?e:null},this._apply_loop=function(t,e){!t.loop&&1<e&&gt._wD("Note: Native HTML5 looping is infinite.",1),t.loop=1<e?"loop":""},this._setup_html5=function(t){var e,n=x(f._iO,t),i=Ut?vt:f._a,r=decodeURI(n.url);if(Ut?r===decodeURI(rt)&&(e=!0):r===decodeURI(v)&&(e=!0),i){if(i._s)if(Ut)i._s&&i._s.playState&&!e&&i._s.stop();else if(!Ut&&r===decodeURI(v))return f._apply_loop(i,n.loops),i;e||(v&&o(!1),i.src=n.url,f.url=n.url,v=n.url,rt=n.url,i._called_load=!1)}else n.autoLoad||n.autoPlay?(f._a=new Audio(n.url),f._a.load()):f._a=Xt&&opera.version()<10?new Audio(null):new Audio,(i=f._a)._called_load=!1,Ut&&(vt=i);return f.isHTML5=!0,(f._a=i)._s=f,s(),f._apply_loop(i,n.loops),n.autoLoad||n.autoPlay?f.load():(i.autobuffer=!1,i.preload="auto"),i},s=function(){if(f._a._added_events)return!1;var t,e,n,i;for(t in f._a._added_events=!0,lt)lt.hasOwnProperty(t)&&(n=lt[e=t],i=void 0,f._a&&f._a.addEventListener(e,n,i||!1));return!0},e=function(){var t,e,n,i;for(t in gt._wD(f.id+": Removing event listeners"),f._a._added_events=!1,lt)lt.hasOwnProperty(t)&&(n=lt[e=t],i=void 0,f._a&&f._a.removeEventListener(e,n,i||!1))},this._onload=function(t){var e,n=!!t||!f.isHTML5&&8===m&&f.duration;return e=f.id+": ",gt._wD(e+(n?"onload()":"Failed to load / invalid sound?"+(f.duration?" -":" Zero-length duration reported.")+" ("+f.url+")"),n?1:2),n||f.isHTML5||(!0===gt.sandbox.noRemote&&gt._wD(e+L("noNet"),1),!0===gt.sandbox.noLocal&&gt._wD(e+L("noLocal"),1)),f.loaded=n,f.readyState=n?3:2,f._onbufferchange(0),n||f.isHTML5||f._onerror(),f._iO.onload&&ht(f,function(){f._iO.onload.apply(f,[n])}),!0},this._onerror=function(t,e){f._iO.onerror&&ht(f,function(){f._iO.onerror.apply(f,[t,e])})},this._onbufferchange=function(t){return 0!==f.playState&&(!(t&&f.isBuffering||!t&&!f.isBuffering)&&(f.isBuffering=1===t,f._iO.onbufferchange&&(gt._wD(f.id+": Buffer state change: "+t),f._iO.onbufferchange.apply(f,[t])),!0))},this._onsuspend=function(){return f._iO.onsuspend&&(gt._wD(f.id+": Playback suspended"),f._iO.onsuspend.apply(f)),!0},this._onfailure=function(t,e,n){f.failures++,gt._wD(f.id+": Failure ("+f.failures+"): "+t),f._iO.onfailure&&1===f.failures?f._iO.onfailure(t,e,n):gt._wD(f.id+": Ignoring failure")},this._onwarning=function(t,e,n){f._iO.onwarning&&f._iO.onwarning(t,e,n)},this._onfinish=function(){var t=f._iO.onfinish;f._onbufferchange(0),f._resetOnPosition(0),f.instanceCount&&(f.instanceCount--,f.instanceCount||(r(),f.playState=0,f.paused=!1,f.instanceCount=0,f.instanceOptions={},f._iO={},i(),f.isHTML5&&(f.position=0)),f.instanceCount&&!f._iO.multiShotEvents||t&&(gt._wD(f.id+": onfinish()"),ht(f,function(){t.apply(f)})))},this._whileloading=function(t,e,n,i){var r=f._iO;f.bytesLoaded=t,f.bytesTotal=e,f.duration=Math.floor(n),f.bufferLength=i,f.isHTML5||r.isMovieStar?f.durationEstimate=f.duration:r.duration?f.durationEstimate=f.duration>r.duration?f.duration:r.duration:f.durationEstimate=parseInt(f.bytesTotal/f.bytesLoaded*f.duration,10),f.isHTML5||(f.buffered=[{start:0,end:f.duration}]),(3!==f.readyState||f.isHTML5)&&r.whileloading&&r.whileloading.apply(f)},this._whileplaying=function(t,e,n,i,r){var o,s=f._iO;return!isNaN(t)&&null!==t&&(f.position=Math.max(0,t),f._processOnPosition(),!f.isHTML5&&8<m&&(s.usePeakData&&e!==pe&&e&&(f.peakData={left:e.leftPeak,right:e.rightPeak}),s.useWaveformData&&n!==pe&&n&&(f.waveformData={left:n.split(","),right:i.split(",")}),s.useEQData&&r!==pe&&r&&r.leftEQ&&(o=r.leftEQ.split(","),f.eqData=o,f.eqData.left=o,r.rightEQ!==pe&&r.rightEQ&&(f.eqData.right=r.rightEQ.split(",")))),1===f.playState&&(f.isHTML5||8!==m||f.position||!f.isBuffering||f._onbufferchange(0),s.whileplaying&&s.whileplaying.apply(f)),!0)},this._oncaptiondata=function(t){gt._wD(f.id+": Caption data received."),f.captiondata=t,f._iO.oncaptiondata&&f._iO.oncaptiondata.apply(f,[t])},this._onmetadata=function(t,e){gt._wD(f.id+": Metadata received.");var n,i,r={};for(n=0,i=t.length;n<i;n++)r[t[n]]=e[n];f.metadata=r,f._iO.onmetadata&&f._iO.onmetadata.call(f,f.metadata)},this._onid3=function(t,e){gt._wD(f.id+": ID3 data received.");var n,i,r=[];for(n=0,i=t.length;n<i;n++)r[t[n]]=e[n];f.id3=x(f.id3,r),f._iO.onid3&&f._iO.onid3.apply(f)},this._onconnect=function(t){t=1===t,gt._wD(f.id+": "+(t?"Connected.":"Failed to connect? - "+f.url),t?1:2),(f.connected=t)&&(f.failures=0,B(f.id)&&(f.getAutoPlay()?f.play(pe,f.getAutoPlay()):f._iO.autoLoad&&f.load()),f._iO.onconnect&&f._iO.onconnect.apply(f,[t]))},this._ondataerror=function(t){0<f.playState&&(gt._wD(f.id+": Data error: "+t),f._iO.ondataerror&&f._iO.ondataerror.apply(f))},this._debug()},k=function(){return St.body||St.getElementsByTagName("div")[0]},y=function(t){return St.getElementById(t)},x=function(t,e){var n,i,r=t||{};for(i in n=e===pe?gt.defaultOptions:e)n.hasOwnProperty(i)&&r[i]===pe&&("object"!=typeof n[i]||null===n[i]?r[i]=n[i]:r[i]=x(r[i],n[i]));return r},ht=function(t,e){t.isHTML5||8!==m?e():fe.setTimeout(e,0)},u={onready:1,ontimeout:1,defaultOptions:1,flash9Options:1,movieStarOptions:1},l=function(t,e){var n,i=!0,r=e!==pe,o=gt.setupOptions,s=u;if(t===pe){for(n in i=[],o)o.hasOwnProperty(n)&&i.push(n);for(n in s)s.hasOwnProperty(n)&&("object"==typeof gt[n]?i.push(n+": {...}"):gt[n]instanceof Function?i.push(n+": function() {...}"):i.push(n));return gt._wD(L("setup",i.join(", "))),!1}for(n in t)if(t.hasOwnProperty(n))if("object"!=typeof t[n]||null===t[n]||t[n]instanceof Array||t[n]instanceof RegExp)r&&s[e]!==pe?gt[e][n]=t[n]:o[n]!==pe?(gt.setupOptions[n]=t[n],gt[n]=t[n]):s[n]===pe?(U(L(gt[n]===pe?"setupUndef":"setupError",n),2),i=!1):gt[n]instanceof Function?gt[n].apply(gt,t[n]instanceof Array?t[n]:[t[n]]):gt[n]=t[n];else{if(s[n]!==pe)return l(t[n],n);U(L(gt[n]===pe?"setupUndef":"setupError",n),2),i=!1}return i},ft=fe.attachEvent,pt={add:ft?"attachEvent":"addEventListener",remove:ft?"detachEvent":"removeEventListener"},it={add:function(){he(ce(arguments),"add")},remove:function(){he(ce(arguments),"remove")}},lt={abort:de(function(){gt._wD(this._s.id+": abort")}),canplay:de(function(){var e,n=this._s;if(!n._html5_canplay){if(n._html5_canplay=!0,gt._wD(n.id+": canplay"),n._onbufferchange(0),e=n._iO.position===pe||isNaN(n._iO.position)?null:n._iO.position/1e3,this.currentTime!==e){gt._wD(n.id+": canplay: Setting position to "+e);try{this.currentTime=e}catch(t){gt._wD(n.id+": canplay: Setting position of "+e+" failed: "+t.message,2)}}n._iO._oncanplay&&n._iO._oncanplay()}}),canplaythrough:de(function(){var t=this._s;t.loaded||(t._onbufferchange(0),t._whileloading(t.bytesLoaded,t.bytesTotal,t._get_html5_duration()),t._onload(!0))}),durationchange:de(function(){var t,e=this._s;t=e._get_html5_duration(),isNaN(t)||t===e.duration||(gt._wD(this._s.id+": durationchange ("+t+")"+(e.duration?", previously "+e.duration:"")),e.durationEstimate=e.duration=t)}),ended:de(function(){var t=this._s;gt._wD(t.id+": ended"),t._onfinish()}),error:de(function(){var t=Q[this.error.code]||null;gt._wD(this._s.id+": HTML5 error, code "+this.error.code+(t?" ("+t+")":"")),this._s._onload(!1),this._s._onerror(this.error.code,t)}),loadeddata:de(function(){var t=this._s;gt._wD(t.id+": loadeddata"),t._loaded||Gt||(t.duration=t._get_html5_duration())}),loadedmetadata:de(function(){gt._wD(this._s.id+": loadedmetadata")}),loadstart:de(function(){gt._wD(this._s.id+": loadstart"),this._s._onbufferchange(1)}),play:de(function(){this._s._onbufferchange(0)}),playing:de(function(){gt._wD(this._s.id+": playing "+String.fromCharCode(9835)),this._s._onbufferchange(0)}),progress:de(function(t){var e,n,i,r=this._s,o=0,s="progress"===t.type,a=t.target.buffered,l=t.loaded||0,u=t.total||1;if(r.buffered=[],a&&a.length){for(e=0,n=a.length;e<n;e++)r.buffered.push({start:1e3*a.start(e),end:1e3*a.end(e)});if(o=1e3*(a.end(0)-a.start(0)),l=Math.min(1,o/(1e3*t.target.duration)),s&&1<a.length){for(i=[],n=a.length,e=0;e<n;e++)i.push(1e3*t.target.buffered.start(e)+"-"+1e3*t.target.buffered.end(e));gt._wD(this._s.id+": progress, timeRanges: "+i.join(", "))}s&&!isNaN(l)&&gt._wD(this._s.id+": progress, "+Math.floor(100*l)+"% loaded")}isNaN(l)||(r._whileloading(l,u,r._get_html5_duration()),l&&u&&l===u&&lt.canplaythrough.call(this,t))}),ratechange:de(function(){gt._wD(this._s.id+": ratechange")}),suspend:de(function(t){var e=this._s;gt._wD(this._s.id+": suspend"),lt.progress.call(this,t),e._onsuspend()}),stalled:de(function(){gt._wD(this._s.id+": stalled")}),timeupdate:de(function(){this._s._onTimer()}),waiting:de(function(){var t=this._s;gt._wD(this._s.id+": waiting"),t._onbufferchange(1)})},Y=function(t){return!(!t||!(t.type||t.url||t.serverURL))&&(!(t.serverURL||t.type&&ue(t.type))&&(t.type?K({type:t.type}):K({url:t.url})||gt.html5Only||t.url.match(/data:/i)))},tt=function(t){var e;return t&&(e=Gt?ie:gt.html5.canPlayType("audio/wav")?"data:audio/wave;base64,/UklGRiYAAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQIAAAD//w==":ie,t.src=e,t._called_unload!==pe&&(t._called_load=!1)),Ut&&(rt=null),e},K=function(t){if(!gt.useHTML5Audio||!gt.hasHTML5)return!1;var e,n,i,r,o=t.url||null,s=t.type||null,a=gt.audioFormats;if(s&&gt.html5[s]!==pe)return gt.html5[s]&&!ue(s);if(!Z){for(r in Z=[],a)a.hasOwnProperty(r)&&(Z.push(r),a[r].related&&(Z=Z.concat(a[r].related)));Z=new RegExp("\\.("+Z.join("|")+")(\\?.*)?$","i")}return(i=o?o.toLowerCase().match(Z):null)&&i.length?i=i[1]:s?i=(-1!==(n=s.indexOf(";"))?s.substr(0,n):s).substr(6):e=!1,e=i&&gt.html5[i]!==pe?gt.html5[i]&&!ue(i):(s="audio/"+i,e=gt.html5.canPlayType({type:s}),(gt.html5[i]=e)&&gt.html5[s]&&!ue(s))},nt=function(){if(!gt.useHTML5Audio||!gt.hasHTML5)return gt.html5.usingFlash=!0,!(qt=!0);var t,e,n,o,s=Audio!==pe?Xt&&opera.version()<10?new Audio(null):new Audio:null,i={};function r(t){var e,n,i=!1,r=!1;if(!s||"function"!=typeof s.canPlayType)return i;if(t instanceof Array){for(o=0,n=t.length;o<n;o++)(gt.html5[t[o]]||s.canPlayType(t[o]).match(gt.html5Test))&&(r=!0,gt.html5[t[o]]=!0,gt.flash[t[o]]=!!t[o].match(ne));i=r}else i=!(!(e=!(!s||"function"!=typeof s.canPlayType)&&s.canPlayType(t))||!e.match(gt.html5Test));return i}for(t in n=gt.audioFormats)if(n.hasOwnProperty(t)&&(e="audio/"+t,i[t]=r(n[t].type),i[e]=i[t],t.match(ne)?(gt.flash[t]=!0,gt.flash[e]=!0):(gt.flash[t]=!1,gt.flash[e]=!1),n[t]&&n[t].related))for(o=n[t].related.length-1;0<=o;o--)i["audio/"+n[t].related[o]]=i[t],gt.html5[n[t].related[o]]=i[t],gt.flash[n[t].related[o]]=i[t];return i.canPlayType=s?r:null,gt.html5=x(gt.html5,i),gt.html5.usingFlash=X(),qt=gt.html5.usingFlash,!0},T={notReady:"Unavailable - wait until onready() has fired.",notOK:"Audio support is not available.",domError:yt+"exception caught while appending SWF to DOM.",spcWmode:"Removing wmode, preventing known SWF loading issue(s)",swf404:bt+"Verify that %s is a valid path.",tryDebug:"Try "+yt+".debugFlash = true for more security details (output goes to SWF.)",checkSWF:"See SWF output for more debug info.",localFail:bt+"Non-HTTP page ("+St.location.protocol+" URL?) Review Flash player security settings for this special case:\nhttp://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html\nMay need to add/allow path, eg. c:/sm2/ or /users/me/sm2/",waitFocus:bt+"Special case: Waiting for SWF to load with window focus...",waitForever:bt+"Waiting indefinitely for Flash (will recover if unblocked)...",waitSWF:bt+"Waiting for 100% SWF load...",needFunction:bt+"Function object expected for %s",badID:'Sound ID "%s" should be a string, starting with a non-numeric character',currentObj:bt+"_debug(): Current sound objects",waitOnload:bt+"Waiting for window.onload()",docLoaded:bt+"Document already loaded",onload:bt+"initComplete(): calling soundManager.onload()",onloadOK:yt+".onload() complete",didInit:bt+"init(): Already called?",secNote:"Flash security note: Network/internet URLs will not load due to security restrictions. Access can be configured via Flash Player Global Security Settings Page: http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html",badRemove:bt+"Failed to remove Flash node.",shutdown:yt+".disable(): Shutting down",queue:bt+"Queueing %s handler",smError:"SMSound.load(): Exception: JS-Flash communication failed, or JS error.",fbTimeout:"No flash response, applying ."+F.swfTimedout+" CSS...",fbLoaded:"Flash loaded",fbHandler:bt+"flashBlockHandler()",manURL:"SMSound.load(): Using manually-assigned URL",onURL:yt+".load(): current URL already assigned.",badFV:yt+'.flashVersion must be 8 or 9. "%s" is invalid. Reverting to %s.',as2loop:"Note: Setting stream:false so looping can work (flash 8 limitation)",noNSLoop:"Note: Looping not implemented for MovieStar formats",needfl9:"Note: Switching to flash 9, required for MP4 formats.",mfTimeout:"Setting flashLoadTimeout = 0 (infinite) for off-screen, mobile flash case",needFlash:bt+"Fatal error: Flash is needed to play some required formats, but is not available.",gotFocus:bt+"Got window focus.",policy:"Enabling usePolicyFile for data access",setup:yt+".setup(): allowed parameters: %s",setupError:yt+'.setup(): "%s" cannot be assigned with this method.',setupUndef:yt+'.setup(): Could not find option "%s"',setupLate:yt+".setup(): url, flashVersion and html5Test property changes will not take effect until reboot().",noURL:bt+"Flash URL required. Call soundManager.setup({url:...}) to get started.",sm2Loaded:"SoundManager 2: Ready. "+String.fromCharCode(10003),reset:yt+".reset(): Removing event callbacks",mobileUA:"Mobile UA detected, preferring HTML5 by default.",globalHTML5:"Using singleton HTML5 Audio() pattern for this device.",ignoreMobile:"Ignoring mobile restrictions for this device."},L=function(){var t,e,n,i,r;if(i=(t=$t.call(arguments)).shift(),(r=T&&T[i]?T[i]:"")&&t&&t.length)for(e=0,n=t.length;e<n;e++)r=r.replace("%s",t[e]);return r},q=function(t){return 8===m&&1<t.loops&&t.stream&&(_("as2loop"),t.stream=!1),t},$=function(t,e){return t&&!t.usePolicyFile&&(t.onid3||t.usePeakData||t.useWaveformData||t.useEQData)&&(gt._wD((e||"")+L("policy")),t.usePolicyFile=!0),t},U=function(t){Qt&&console.warn!==pe?console.warn(t):gt._wD(t)},n=function(){return!1},N=function(t){var e;for(e in t)t.hasOwnProperty(e)&&"function"==typeof t[e]&&(t[e]=n);e=null},j=function(t){t===pe&&(t=!1),(Dt||t)&&gt.disable(t)},R=function(t){var e;if(t)if(t.match(/\.swf(\?.*)?$/i)){if(t.substr(t.toLowerCase().lastIndexOf(".swf?")+4))return t}else t.lastIndexOf("/")!==t.length-1&&(t+="/");return e=(t&&-1!==t.lastIndexOf("/")?t.substr(0,t.lastIndexOf("/")+1):"./")+gt.movieURL,gt.noSWFCache&&(e+="?ts="+(new Date).getTime()),e},S=function(){8!==(m=parseInt(gt.flashVersion,10))&&9!==m&&(gt._wD(L("badFV",m,8)),gt.flashVersion=m=8);var t=gt.debugMode||gt.debugFlash?"_debug.swf":".swf";gt.useHTML5Audio&&!gt.html5Only&&gt.audioFormats.mp4.required&&m<9&&(gt._wD(L("needfl9")),gt.flashVersion=m=9),gt.version=gt.versionNumber+(gt.html5Only?" (HTML5-only mode)":9===m?" (AS3/Flash 9)":" (AS2/Flash 8)"),8<m?(gt.defaultOptions=x(gt.defaultOptions,gt.flash9Options),gt.features.buffering=!0,gt.defaultOptions=x(gt.defaultOptions,gt.movieStarOptions),gt.filePatterns.flash9=new RegExp("\\.(mp3|"+ae.join("|")+")(\\?.*)?$","i"),gt.features.movieStar=!0):gt.features.movieStar=!1,gt.filePattern=gt.filePatterns[8!==m?"flash9":"flash8"],gt.movieURL=(8===m?"soundmanager2.swf":"soundmanager2_flash9.swf").replace(".swf",t),gt.features.peakData=gt.features.waveformData=gt.features.eqData=8<m},O=function(t,e){mt&&mt._setPolling(t,e)},A=function(){if(gt.debugURLParam.test(xt)&&(gt.setupOptions.debugMode=gt.debugMode=!0),!y(gt.debugID)){var t,e,n,i,r;if(gt.debugMode&&!y(gt.debugID)&&(!Qt||!gt.useConsole||!gt.consoleOnly)){for(r in(t=St.createElement("div")).id=gt.debugID+"-toggle",i={position:"fixed",bottom:"0px",right:"0px",width:"1.2em",height:"1.2em",lineHeight:"1.2em",margin:"2px",textAlign:"center",border:"1px solid #999",cursor:"pointer",background:"#fff",color:"#333",zIndex:10001},t.appendChild(St.createTextNode("-")),t.onclick=H,t.title="Toggle SM2 debug console",_t.match(/msie 6/i)&&(t.style.position="absolute",t.style.cursor="hand"),i)i.hasOwnProperty(r)&&(t.style[r]=i[r]);if((e=St.createElement("div")).id=gt.debugID,e.style.display=gt.debugMode?"block":"none",gt.debugMode&&!y(t.id)){try{(n=k()).appendChild(t)}catch(t){throw new Error(L("domError")+" \n"+t.toString())}n.appendChild(e)}}n=null}},B=this.getSoundById,_=function(t,e){return t?gt._wD(L(t),e):""},H=function(){var t=y(gt.debugID),e=y(gt.debugID+"-toggle");t&&(Ct?(e.innerHTML="+",t.style.display="none"):(e.innerHTML="-",t.style.display="block"),Ct=!Ct)},w=function(t,e,n){if(fe.sm2Debugger!==pe)try{sm2Debugger.handleEvent(t,e,n)}catch(t){return!1}return!0},P=function(){var t=[];return gt.debugMode&&t.push(F.sm2Debug),gt.debugFlash&&t.push(F.flashDebug),gt.useHighPerformance&&t.push(F.highPerf),t.join(" ")},I=function(){var t=L("fbHandler"),e=gt.getMoviePercent(),n=F,i={type:"FLASHBLOCK"};gt.html5Only||(gt.ok()?(gt.didFlashBlock&&gt._wD(t+": Unblocked"),gt.oMC&&(gt.oMC.className=[P(),n.swfDefault,n.swfLoaded+(gt.didFlashBlock?" "+n.swfUnblocked:"")].join(" "))):(qt&&(gt.oMC.className=P()+" "+n.swfDefault+" "+(null===e?n.swfTimedout:n.swfError),gt._wD(t+": "+L("fbTimeout")+(e?" ("+L("fbLoaded")+")":""))),gt.didFlashBlock=!0,s({type:"ontimeout",ignoreInit:!0,error:i}),D(i)))},o=function(t,e,n){Tt[t]===pe&&(Tt[t]=[]),Tt[t].push({method:e,scope:n||null,fired:!1})},s=function(t){if(t=t||{type:gt.ok()?"onready":"ontimeout"},!Mt&&t&&!t.ignoreInit)return!1;if("ontimeout"===t.type&&(gt.ok()||Dt&&!t.ignoreInit))return!1;var e,n,i={success:t&&t.ignoreInit?gt.ok():!Dt},r=t&&t.type&&Tt[t.type]||[],o=[],s=[i],a=qt&&!gt.ok();for(t.error&&(s[0].error=t.error),e=0,n=r.length;e<n;e++)!0!==r[e].fired&&o.push(r[e]);if(o.length)for(e=0,n=o.length;e<n;e++)o[e].scope?o[e].method.apply(o[e].scope,s):o[e].method.apply(this,s),a||(o[e].fired=!0);return!0},c=function(){fe.setTimeout(function(){gt.useFlashBlock&&I(),s(),"function"==typeof gt.onload&&(_("onload",1),gt.onload.apply(fe),_("onloadOK",1)),gt.waitForWindowLoad&&it.add(fe,"load",c)},1)},st=function(){if(ot!==pe)return ot;var e,t,n,i,r=!1,o=navigator,s=fe.ActiveXObject;try{i=o.plugins}catch(t){i=void 0}if(i&&i.length)t="application/x-shockwave-flash",(n=o.mimeTypes)&&n[t]&&n[t].enabledPlugin&&n[t].enabledPlugin.description&&(r=!0);else if(s!==pe&&!_t.match(/MSAppHost/i)){try{e=new s("ShockwaveFlash.ShockwaveFlash")}catch(t){e=null}r=!!e,e=null}return ot=r},X=function(){var t,e,n=gt.audioFormats;if(Vt&&!!_t.match(/os (1|2|3_0|3_1)\s/i)?(gt.hasHTML5=!1,gt.html5Only=!0,gt.oMC&&(gt.oMC.style.display="none")):gt.useHTML5Audio&&(gt.html5&&gt.html5.canPlayType||(gt._wD("SoundManager: No HTML5 Audio() support detected."),gt.hasHTML5=!1),Kt&&gt._wD(bt+"Note: Buggy HTML5 Audio in Safari on this OS X release, see https://bugs.webkit.org/show_bug.cgi?id=32159 - "+(ot?"will use flash fallback for MP3/MP4, if available":" would use flash fallback for MP3/MP4, but none detected."),1)),gt.useHTML5Audio&&gt.hasHTML5)for(e in G=!0,n)n.hasOwnProperty(e)&&n[e].required&&(gt.html5.canPlayType(n[e].type)?gt.preferFlash&&(gt.flash[e]||gt.flash[n[e].type])&&(t=!0):t=!(G=!1));return gt.ignoreFlash&&(G=!(t=!1)),gt.html5Only=gt.hasHTML5&&gt.useHTML5Audio&&!t,!gt.html5Only},J=function(t){var e,n,i,r=0;if(t instanceof Array){for(e=0,n=t.length;e<n;e++)if(t[e]instanceof Object){if(gt.canPlayMIME(t[e].type)){r=e;break}}else if(gt.canPlayURL(t[e])){r=e;break}t[r].url&&(t[r]=t[r].url),i=t[r]}else i=t;return i},V=function(t){t._hasTimer||(t._hasTimer=!0,!Yt&&gt.html5PollingInterval&&(null===Ft&&0===Pt&&(Ft=setInterval(W,gt.html5PollingInterval)),Pt++))},z=function(t){t._hasTimer&&(t._hasTimer=!1,!Yt&&gt.html5PollingInterval&&Pt--)},W=function(){var t;if(null!==Ft&&!Pt)return clearInterval(Ft),void(Ft=null);for(t=gt.soundIDs.length-1;0<=t;t--)gt.sounds[gt.soundIDs[t]].isHTML5&&gt.sounds[gt.soundIDs[t]]._hasTimer&&gt.sounds[gt.soundIDs[t]]._onTimer()},D=function(t){t=t!==pe?t:{},"function"==typeof gt.onerror&&gt.onerror.apply(fe,[{type:t.type!==pe?t.type:null}]),t.fatal!==pe&&t.fatal&&gt.disable()},at=function(){if(Kt&&st()){var t,e,n=gt.audioFormats;for(e in n)if(n.hasOwnProperty(e)&&("mp3"===e||"mp4"===e)&&(gt._wD(yt+": Using flash fallback for "+e+" format"),gt.html5[e]=!1,n[e]&&n[e].related))for(t=n[e].related.length-1;0<=t;t--)gt.html5[n[e].related[t]]=!1}},this._setSandboxType=function(t){var e=gt.sandbox;e.type=t,e.description=e.types[e.types[t]!==pe?t:"unknown"],"localWithFile"===e.type?(e.noRemote=!0,e.noLocal=!1,_("secNote",2)):"localWithNetwork"===e.type?(e.noRemote=!1,e.noLocal=!0):"localTrusted"===e.type&&(e.noRemote=!1,e.noLocal=!1)},this._externalInterfaceOK=function(t){if(!gt.swfLoaded){var e;if(w("swf",!0),w("flashtojs",!0),gt.swfLoaded=!0,te=!1,Kt&&at(),!t||t.replace(/\+dev/i,"")!==gt.versionNumber.replace(/\+dev/i,""))return e=yt+': Fatal: JavaScript file build "'+gt.versionNumber+'" does not match Flash SWF build "'+t+'" at '+gt.url+". Ensure both are up-to-date.",void setTimeout(function(){throw new Error(e)},0);setTimeout(b,Wt?100:1)}},M=function(t,e){if(Et&&kt)return!1;function n(){var t,e=[],n=[];t="SoundManager "+gt.version+(!gt.html5Only&&gt.useHTML5Audio?gt.hasHTML5?" + HTML5 audio":", no HTML5 audio support":""),gt.html5Only?gt.html5PollingInterval&&e.push("html5PollingInterval ("+gt.html5PollingInterval+"ms)"):(gt.preferFlash&&e.push("preferFlash"),gt.useHighPerformance&&e.push("useHighPerformance"),gt.flashPollingInterval&&e.push("flashPollingInterval ("+gt.flashPollingInterval+"ms)"),gt.html5PollingInterval&&e.push("html5PollingInterval ("+gt.html5PollingInterval+"ms)"),gt.wmode&&e.push("wmode ("+gt.wmode+")"),gt.debugFlash&&e.push("debugFlash"),gt.useFlashBlock&&e.push("flashBlock")),e.length&&(n=n.concat([e.join(" + ")])),gt._wD(t+(n.length?" + "+n.join(", "):""),1),ut()}if(gt.html5Only)return S(),n(),gt.oMC=y(gt.movieID),b(),!(kt=Et=!0);var i,r,o,s,a,l,u,c,h,d=e||gt.url,f=gt.altURL||d,p=k(),g=P(),v=St.getElementsByTagName("html")[0];function m(t,e){return'<param name="'+t+'" value="'+e+'" />'}if(i=v&&v.dir&&v.dir.match(/rtl/i),t=t===pe?gt.id:t,S(),gt.url=R(re?d:f),e=gt.url,gt.wmode=!gt.wmode&&gt.useHighPerformance?"transparent":gt.wmode,null!==gt.wmode&&(_t.match(/msie 8/i)||!Wt&&!gt.useHighPerformance)&&navigator.platform.match(/win32|win64/i)&&(Ht.push(T.spcWmode),gt.wmode=null),r={name:t,id:t,src:e,quality:"high",allowScriptAccess:gt.allowScriptAccess,bgcolor:gt.bgColor,pluginspage:oe+"www.macromedia.com/go/getflashplayer",title:"JS/Flash audio component (SoundManager 2)",type:"application/x-shockwave-flash",wmode:gt.wmode,hasPriority:"true"},gt.debugFlash&&(r.FlashVars="debug=1"),gt.wmode||delete r.wmode,Wt)o=St.createElement("div"),a=['<object id="'+t+'" data="'+e+'" type="'+r.type+'" title="'+r.title+'" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0">',m("movie",e),m("AllowScriptAccess",gt.allowScriptAccess),m("quality",r.quality),gt.wmode?m("wmode",gt.wmode):"",m("bgcolor",gt.bgColor),m("hasPriority","true"),gt.debugFlash?m("FlashVars",r.FlashVars):"","</object>"].join("");else for(s in o=St.createElement("embed"),r)r.hasOwnProperty(s)&&o.setAttribute(s,r[s]);if(A(),g=P(),p=k())if(gt.oMC=y(gt.movieID)||St.createElement("div"),gt.oMC.id)h=gt.oMC.className,gt.oMC.className=(h?h+" ":F.swfDefault)+(g?" "+g:""),gt.oMC.appendChild(o),Wt&&((l=gt.oMC.appendChild(St.createElement("div"))).className=F.swfBox,l.innerHTML=a),kt=!0;else{if(gt.oMC.id=gt.movieID,gt.oMC.className=F.swfDefault+" "+g,l=u=null,gt.useFlashBlock||(gt.useHighPerformance?u={position:"fixed",width:"8px",height:"8px",bottom:"0px",left:"0px",overflow:"hidden"}:(u={position:"absolute",width:"6px",height:"6px",top:"-9999px",left:"-9999px"},i&&(u.left=Math.abs(parseInt(u.left,10))+"px"))),Jt&&(gt.oMC.style.zIndex=1e4),!gt.debugFlash)for(c in u)u.hasOwnProperty(c)&&(gt.oMC.style[c]=u[c]);try{Wt||gt.oMC.appendChild(o),p.appendChild(gt.oMC),Wt&&((l=gt.oMC.appendChild(St.createElement("div"))).className=F.swfBox,l.innerHTML=a),kt=!0}catch(t){throw new Error(L("domError")+" \n"+t.toString())}}return Et=!0,n(),!0},g=function(){return gt.html5Only?(M(),!1):!mt&&(gt.url?((mt=gt.getMovie(gt.id))||(jt?(Wt?gt.oMC.innerHTML=Rt:gt.oMC.appendChild(jt),Et=!(jt=null)):M(gt.id,gt.url),mt=gt.getMovie(gt.id)),"function"==typeof gt.oninitmovie&&setTimeout(gt.oninitmovie,1),ct(),!0):(_("noURL"),!1))},h=function(){setTimeout(d,1e3)},f=function(){fe.setTimeout(function(){U(bt+"useFlashBlock is false, 100% HTML5 mode is possible. Rebooting with preferFlash: false..."),gt.setup({preferFlash:!1}).reboot(),gt.didFlashBlock=!0,gt.beginDelayedInit()},1)},d=function(){var t,e=!1;gt.url&&(Lt||(Lt=!0,it.remove(fe,"load",h),ot&&te&&!Zt?_("waitFocus"):(Mt||0<(t=gt.getMoviePercent())&&t<100&&(e=!0),setTimeout(function(){if(t=gt.getMoviePercent(),e)return Lt=!1,gt._wD(L("waitSWF")),void fe.setTimeout(h,1);Mt||(gt._wD(yt+": No Flash response within expected time. Likely causes: "+(0===t?"SWF load failed, ":"")+"Flash blocked or JS-Flash security error."+(gt.debugFlash?" "+L("checkSWF"):""),2),!re&&t&&(_("localFail",2),gt.debugFlash||_("tryDebug",2)),0===t&&gt._wD(L("swf404",gt.url),1),w("flashtojs",!1,": Timed out"+(re?" (Check flash security or flash blockers)":" (No plugin/missing SWF?)"))),!Mt&&ee&&(null===t?gt.useFlashBlock||0===gt.flashLoadTimeout?(gt.useFlashBlock&&I(),_("waitForever")):!gt.useFlashBlock&&G?f():(_("waitForever"),s({type:"ontimeout",ignoreInit:!0,error:{type:"INIT_FLASHBLOCK"}})):0===gt.flashLoadTimeout?_("waitForever"):!gt.useFlashBlock&&G?f():j(!0))},gt.flashLoadTimeout))))},p=function(){function t(){it.remove(fe,"focus",p)}return Zt||!te||(Zt=ee=!0,_("gotFocus"),Lt=!1,h()),t(),!0},ct=function(){Ht.length&&(gt._wD("SoundManager 2: "+Ht.join(" "),1),Ht=[])},ut=function(){ct();var t,e=[];if(gt.useHTML5Audio&&gt.hasHTML5){for(t in gt.audioFormats)gt.audioFormats.hasOwnProperty(t)&&e.push(t+" = "+gt.html5[t]+(!gt.html5[t]&&qt&&gt.flash[t]?" (using flash)":gt.preferFlash&&gt.flash[t]&&qt?" (preferring flash)":gt.html5[t]?"":" ("+(gt.audioFormats[t].required?"required, ":"")+"and no flash support)"));gt._wD("SoundManager 2 HTML5 support: "+e.join(", "),1)}},r=function(t){if(Mt)return!1;if(gt.html5Only)return _("sm2Loaded",1),Mt=!0,c(),w("onload",!0),!0;var e,n=!0;return gt.useFlashBlock&&gt.flashLoadTimeout&&!gt.getMoviePercent()||(Mt=!0),e={type:!ot&&qt?"NO_FLASH":"INIT_TIMEOUT"},gt._wD("SoundManager 2 "+(Dt?"failed to load":"loaded")+" ("+(Dt?"Flash security/load error":"OK")+") "+String.fromCharCode(Dt?10006:10003),Dt?2:1),Dt||t?(gt.useFlashBlock&&gt.oMC&&(gt.oMC.className=P()+" "+(null===gt.getMoviePercent()?F.swfTimedout:F.swfError)),s({type:"ontimeout",error:e,ignoreInit:!0}),w("onload",!1),D(e),n=!1):w("onload",!0),Dt||(gt.waitForWindowLoad&&!Ot?(_("waitOnload"),it.add(fe,"load",c)):(gt.waitForWindowLoad&&Ot&&_("docLoaded"),c())),n},i=function(){var t,e=gt.setupOptions;for(t in e)e.hasOwnProperty(t)&&(gt[t]===pe?gt[t]=e[t]:gt[t]!==e[t]&&(gt.setupOptions[t]=gt[t]))},b=function(){if(Mt)return _("didInit"),!1;function t(){it.remove(fe,"load",gt.beginDelayedInit)}if(gt.html5Only)return Mt||(t(),gt.enabled=!0,r()),!0;g();try{mt._externalInterfaceTest(!1),O(!0,gt.flashPollingInterval||(gt.useHighPerformance?10:50)),gt.debugMode||mt._disableDebug(),gt.enabled=!0,w("jstoflash",!0),gt.html5Only||it.add(fe,"unload",n)}catch(t){return gt._wD("js/flash exception: "+t.toString()),w("jstoflash",!1),D({type:"JS_TO_FLASH_EXCEPTION",fatal:!0}),j(!0),r(),!1}return r(),t(),!0},v=function(){return!E&&(E=!0,i(),A(),!ot&&gt.hasHTML5&&(gt._wD("SoundManager 2: No Flash detected"+(gt.useHTML5Audio?". Trying HTML5-only mode.":", enabling HTML5."),1),gt.setup({useHTML5Audio:!0,preferFlash:!1})),nt(),!ot&&qt&&(Ht.push(T.needFlash),gt.setup({flashLoadTimeout:1})),St.removeEventListener&&St.removeEventListener("DOMContentLoaded",v,!1),g(),!0)},et=function(){return"complete"===St.readyState&&(v(),St.detachEvent("onreadystatechange",et)),!0},C=function(){Ot=!0,v(),it.remove(fe,"load",C)},st(),it.add(fe,"focus",p),it.add(fe,"load",h),it.add(fe,"load",C),St.addEventListener?St.addEventListener("DOMContentLoaded",v,!1):St.attachEvent?St.attachEvent("onreadystatechange",et):(w("onload",!1),D({type:"NO_DOM2_EVENTS",fatal:!0}))}fe.SM2_DEFER!==pe&&SM2_DEFER||(t=new n),"object"==typeof module&&module&&"object"==typeof module.exports?(module.exports.SoundManager=n,module.exports.soundManager=t):"function"==typeof define&&define.amd&&define(function(){return{constructor:n,getInstance:function(t){if(!fe.soundManager&&t instanceof Function){var e=t(n);e instanceof n&&(fe.soundManager=e)}return fe.soundManager}}}),fe.SoundManager=n,fe.soundManager=t}(window),window.JST["apps/album/show/tpl/album_with_songs.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){var e=[],n=this;return function(){!function(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}(i('<div class="album album--with-songs">\n <div class="region-album-side">\n <div class="region-album-meta"></div>\n </div>\n <div class="region-album-content">\n <div class="region-album-songs"></div>\n </div>\n</div>\n'))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/album/show/tpl/details_meta.jst"]=function(n){function r(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function e(t){void 0!==t&&null!=t&&n.push(t.ecoSafe?t:i.escape(t))}var n=[],i=this;return function(){e(r('<div class="region-details-top">\n <div class="region-details-title">\n <h2><span class="title">')),e(this.label),e(r('</span> <span class="sub"><a href="#music/albums?year=')),e(this.year),e(r('">')),e(this.year),e(r("</a></span></h2>\n </div>\n ")),this.rating&&(e(r('\n <div class="region-details-rating">\n ')),e(this.rating),e(r(" <i></i>\n </div>\n "))),e(r('\n</div>\n\n<div class="region-details-meta-below">\n\n <ul class="meta">\n ')),this.artist&&(e(r("\n <li><label>")),e(tr("artist")),e(r(':</label> <span><a href="#music/artist/')),e(this.artistid),e(r('">')),e(this.artist),e(r("</a></span></li>\n "))),e(r("\n ")),0<this.genre.length&&(e(r("\n <li><label>")),e(t.ngettext("genre","genres",this.genre.length)),e(r(":</label> <span>")),e(r(helpers.url.filterLinks("music/albums","genre",this.genre))),e(r("</span></li>\n "))),e(r("\n ")),this.style.length&&(e(r("\n <li><label>")),e(t.ngettext("style","styles",this.style.length)),e(r(":</label> <span>")),e(r(helpers.url.filterLinks("music/albums","style",this.style))),e(r("</span></li>\n "))),e(r("\n ")),this.albumlabel&&(e(r("\n <li><label>")),e(tr("label")),e(r(":</label> <span>")),e(r(helpers.url.filterLinks("music/albums","albumlabel",[this.albumlabel]))),e(r("</span></li>\n "))),e(r('\n </ul>\n\n <div class="description">')),e(this.description),e(r('</div>\n\n <ul class="inline-links">\n <li class="btn-flat-play play">')),e(tr("Play")),e(r('</li>\n <li class="btn-flat-add add">')),e(tr("Queue")),e(r('</li>\n <li class="btn-flat-stream localplay">')),e(tr("Stream")),e(r('</li>\n <li class="more-actions dropdown">\n <span class="btn-flat-more" data-toggle="dropdown" aria-expanded="true">')),e(tr("more")),e(r('</span>\n <ul class="dropdown-menu pull-right">\n <li class="localadd">')),e(tr("Add to playlist")),e(r('</li>\n <li class="divider"></li>\n <li class="dropdown-submenu internal-search">')),e(tr("Chorus Search")),e(r('\n <ul class="dropdown-menu">\n ')),e(r(helpers.entities.getAddonSearchMenuItems(this.label))),e(r('\n </ul>\n </li>\n <li class="dropdown-submenu external-search">')),e(tr("External Search")),e(r('\n <ul class="dropdown-menu">\n <li data-type="google" data-query="')),e(this.artist),e(r(" ")),e(this.label),e(r('">Google</li>\n <li data-type="soundcloud" data-query="')),e(this.artist),e(r('">SoundCloud</li>\n </ul>\n </li>\n <li class="youtube-search" data-query="')),e(this.artist),e(r(" ")),e(this.label),e(r('">')),e(tr("YouTube Search")),e(r('</li>\n <li class="divider"></li>\n <li class="edit">')),e(tr("Edit")),e(r("</li>\n </ul>\n </li>\n </ul>\n\n</div>\n"))}.call(this),n.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:r};for(t in n)e[t]=n[t];return e}())},window.JST["apps/artist/show/tpl/details_meta.jst"]=function(n){function r(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function e(t){void 0!==t&&null!=t&&n.push(t.ecoSafe?t:i.escape(t))}var n=[],i=this;return function(){e(r('<div class="region-details-top">\n <div class="region-details-title">\n <h2>')),e(this.label),e(r('</h2>\n </div>\n</div>\n\n<div class="region-details-meta-below">\n\n <ul class="meta">\n ')),0<this.genre.length&&(e(r("\n <li><label>")),e(t.ngettext("genre","genres",this.genre.length)),e(r(":</label> <span>")),e(r(helpers.url.filterLinks("music/artists","genre",this.genre))),e(r("</span></li>\n "))),e(r("\n ")),this.style.length&&(e(r("\n <li><label>")),e(t.ngettext("style","styles",this.style.length)),e(r(":</label> <span>")),e(r(helpers.url.filterLinks("music/artists","style",this.style))),e(r("</span></li>\n "))),e(r("\n ")),this.formed&&(e(r("\n <li><label>")),e(tr("formed")),e(r(":</label> <span>")),e(this.formed),e(r("</span></li>\n "))),e(r("\n ")),this.yearsactive&&0<this.yearsactive.length&&(e(r("\n <li><label>")),e(tr("years active")),e(r(":</label> <span>")),e(this.yearsactive),e(r("</span></li>\n "))),e(r("\n ")),this.born&&(e(r("\n <li><label>")),e(tr("born")),e(r(":</label> <span>")),e(this.born),e(r("</span></li>\n "))),e(r("\n ")),this.died&&(e(r("\n <li><label>")),e(tr("died")),e(r(":</label> <span>")),e(this.died),e(r("</span></li>\n "))),e(r("\n ")),this.disbanded&&(e(r("\n <li><label>")),e(tr("disbanded")),e(r(":</label> <span>")),e(this.disbanded),e(r("</span></li>\n "))),e(r('\n </ul>\n\n <div class="description">')),e(this.description),e(r('</div>\n\n <ul class="inline-links">\n <li class="btn-flat-play play">')),e(tr("Play")),e(r('</li>\n <li class="btn-flat-add add">')),e(tr("Queue")),e(r('</li>\n <li class="btn-flat-stream localplay">')),e(tr("Stream")),e(r('</li>\n <li class="more-actions dropdown">\n <span class="btn-flat-more" data-toggle="dropdown" aria-expanded="true">')),e(tr("more")),e(r('</span>\n <ul class="dropdown-menu pull-right">\n <li class="localadd">')),e(tr("Add to playlist")),e(r('</li>\n <li class="divider"></li>\n <li class="dropdown-submenu internal-search">')),e(tr("Chorus Search")),e(r('\n <ul class="dropdown-menu">\n ')),e(r(helpers.entities.getAddonSearchMenuItems(this.label))),e(r('\n </ul>\n </li>\n <li class="dropdown-submenu external-search">')),e(tr("External Search")),e(r('\n <ul class="dropdown-menu">\n <li data-type="google" data-query="')),e(this.label),e(r('">Google</li>\n <li data-type="soundcloud" data-query="')),e(this.label),e(r('">SoundCloud</li>\n </ul>\n </li>\n <li class="youtube-search" data-query="')),e(this.label),e(r('">')),e(tr("YouTube Search")),e(r('</li>\n <li class="divider"></li>\n <li class="edit">')),e(tr("Edit")),e(r("</li>\n </ul>\n </li>\n </ul>\n\n</div>\n"))}.call(this),n.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:r};for(t in n)e[t]=n[t];return e}())},window.JST["apps/browser/list/tpl/back_button.jst"]=function(n){function r(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function e(t){void 0!==t&&null!=t&&n.push(t.ecoSafe?t:i.escape(t))}var n=[],i=this;return function(){e(r('<i class="mdi thumb"></i><div class="title">')),e(t.gettext("Back")),e(r("</div>\n"))}.call(this),n.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:r};for(t in n)e[t]=n[t];return e}())},window.JST["apps/browser/list/tpl/file.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function t(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){t(i('<div class="thumb" style="background-image: url(\'')),t(this.thumbnail),t(i('\')"><div class="mdi play"></div></div>\n<div class="title" title="')),t(helpers.global.stripTags(this.labelHtml)),t(i('">')),t(i(this.labelHtml)),t(i('</div>\n<ul class="actions">\n <li class="menu dropdown">\n <i data-toggle="dropdown" class="mdi"></i>\n <ul class="dropdown-menu pull-right"></ul>\n </li>\n</ul>\n'))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/browser/list/tpl/folder_layout.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function t(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){t(i('<div class="folder-layout">\n <div class="loading-bar"><div class="inner"><div class="loader-small-inline"></div> <span>')),t(tr("Loading folder...")),t(i('</span></div></div>\n <div class="path"></div>\n <ul class="actions">\n <li class="dropdown sort-wrapper">\n <i class="sort-toggle" data-toggle="dropdown" title="')),t(tr("Sort")),t(i('"></i>\n <ul class="sorts dropdown-menu pull-right">\n <li data-sort="none">')),t(tr("default")),t(i('<i></i></li>\n <li data-sort="label">')),t(tr("title")),t(i('<i></i></li>\n <li data-sort="dateadded">')),t(tr("date added")),t(i('<i></i></li>\n <li data-sort="year">')),t(tr("year")),t(i('<i></i></li>\n <li data-sort="random">')),t(tr("random")),t(i('<i></i></li>\n </ul>\n </li>\n <li class="dropdown context-wrapper">\n <i class="context-toggle" data-toggle="dropdown" title="')),t(tr("Actions")),t(i('"></i>\n <ul class="dropdown-menu pull-right">\n <li class="play">')),t(tr("play files")),t(i('<i></i></li>\n <li class="queue">')),t(tr("queue files")),t(i('<i></i></li>\n </ul>\n </li>\n </ul>\n\n\n <div class="folder-container">\n <div class="files">\n </div>\n <div class="folders-pane">\n <div class="back"></div>\n <div class="folders">\n <div class="intro">\n <h3><span class="mdi-navigation-arrow-back text-dim"></span> ')),t(tr("Browse files and add-ons")),t(i("</h3>\n <p>")),t(tr("This is where you can browse all Kodi content, not just what is in the library. Browse by source or add-on.")),t(i("</p>\n </div>\n </div>\n </div>\n </div>\n</div>\n"))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/browser/list/tpl/path.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function t(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){t(i('<div class="title">')),t(this.label),t(i("</div>\n"))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/browser/list/tpl/source.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function t(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){t(i('<div class="source source-')),t(this.media),t(i('">\n ')),t(this.label),t(i("\n</div>\n"))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/browser/list/tpl/source_set.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function t(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){t(i("<h3>")),t(this.label),t(i('</h3>\n<ul class="sources"></ul>\n'))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/cast/list/tpl/cast.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function t(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){t(i('<a href="#')),t(this.origin),t(i("?cast=")),t(this.name),t(i('" title="')),t(this.name),t(i(" (")),t(this.role),t(i(')">\n <div class="thumb">\n <img src="')),t(this.thumbnail),t(i('" />\n </div>\n <div class="meta">\n <strong>')),t(this.name),t(i('</strong>\n <span title="')),t(this.role),t(i('">')),t(this.role),t(i('</span>\n </div>\n</a>\n<ul class="actions">\n <li class="imdb" title="IMDb search ')),t(this.name),t(i('"></li>\n <li class="google" title="Google search ')),t(this.name),t(i('"></li>\n</ul>\n'))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/category/list/tpl/item.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function t(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){t(i('<a href="#')),t(this.url),t(i('">\n <span>')),t(this.title),t(i("</span>\n</a>\n"))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/epg/list/tpl/channel.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function t(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){t(i("<h3>")),t(this.channel),t(i('</h3>\n<ul class="items">\n <li class="play">')),t(tr("Play")),t(i('</li>\n <li class="record">')),t(tr("Record")),t(i("</li>\n</ul>\n"))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/epg/list/tpl/programme.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function t(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){t(i('<div class="title">\n <strong>')),t(this.label),t(i('</strong>\n</div>\n<div class="date">\n ')),t(this.start.toString("h:mmtt").toLowerCase()),t(i(" - ")),t(this.end.toString("h:mmtt").toLowerCase()),t(i(" (")),t(this.runtime),t(i("min)<br />\n ")),t(this.start.toString("dddd, dS MMM")),t(i('\n</div>\n<div class="plot">')),t(this.plot),t(i('</div>\n<div class="entity-progress">\n <div class="current-progress" style="width: ')),t(this.progresspercentage),t(i('%" title="')),t(Math.round(this.progresspercentage)),t(i("% ")),t(tr("complete")),t(i('"></div>\n</div>\n<ul class="actions">\n ')),this.isactive?(t(i('\n <li class="play" title="')),t(tr("Play")),t(i('"></li>\n <li class="record" title="')),t(tr("Record")),t(i('"></li>\n '))):(t(i("\n ")),this.wasactive||(t(i('\n <li class="toggle-timer" title="')),t(tr("Toggle timer")),t(i('"></li>\n '))),t(i("\n "))),t(i("\n</ul>\n"))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/external/youtube/tpl/youtube.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function t(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){t(i('<img src="')),t(this.thumbnail),t(i('" class="thumb" />\n<h3>')),t(this.title),t(i("</h3>\n")),this.addonEnabled?t(i('\n <span class="play flat-btn action">Play in Kodi</span>\n <span class="localplay flat-btn action">Play in browser</span>\n')):t(i('\n <span class="play flat-btn action">Play in browser</span>\n')),t(i("\n"))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/filter/show/tpl/filter_options.jst"]=function(n){function r(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function e(t){void 0!==t&&null!=t&&n.push(t.ecoSafe?t:i.escape(t))}var n=[],i=this;return function(){e(r('<div class="options-search-wrapper">\n <input class="options-search" value="" />\n</div>\n<div class="deselect-all">')),e(t.gettext("Deselect all")),e(r('</div>\n<ul class="selection-list"></ul>\n'))}.call(this),n.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:r};for(t in n)e[t]=n[t];return e}())},window.JST["apps/filter/show/tpl/filters_bar.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function t(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){t(i('<span class="filters-active-all">')),t(this.filters),t(i('</span><i class="remove"></i>\n'))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/filter/show/tpl/filters_ui.jst"]=function(n){function r(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function e(t){void 0!==t&&null!=t&&n.push(t.ecoSafe?t:i.escape(t))}var n=[],i=this;return function(){e(r('<div class="filters-container">\n\n <div class="filters-current filter-pane">\n <div class="nav-section"></div>\n\n <h3 class="open-filters">')),e(t.gettext("Filters")),e(r('<i></i></h3>\n <div class="filters-active"></div>\n\n <h3>')),e(t.gettext("Sort")),e(r('</h3>\n <div class="list sort-options"></div>\n </div>\n\n <div class="filters-page filter-pane">\n <h3 class="close-filters">')),e(t.gettext("Select a filter")),e(r('</h3>\n <div class="list filters-list"></div>\n </div>\n\n <div class="filters-options filter-pane">\n <h3 class="close-options">')),e(t.gettext("Select an option")),e(r('</h3>\n <div class="list filter-options-list"></div>\n </div>\n\n</div>\n'))}.call(this),n.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:r};for(t in n)e[t]=n[t];return e}())},window.JST["apps/filter/show/tpl/list_item.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function t(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){t(i(this.title)),t(i("\n"))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/help/overview/tpl/overview.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function t(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){t(i("<h1>")),t(tr("About Chorus")),t(i("</h1>\n<h2>")),t(tr("Status report")),t(i('</h2>\n<div class="help--overview--report">\n <ul>\n\t<li class="report-chorus-version"><strong>Chorus ')),t(tr("version")),t(i('</strong><span></span></li>\n\t<li class="report-kodi-version"><strong>Kodi ')),t(tr("version")),t(i('</strong><span></span></li>\n\t<li class="report-websockets"><strong>')),t(tr("Remote control")),t(i('</strong><span></span></li>\n\t<li class="report-local-audio"><strong>')),t(tr("Local audio")),t(i('</strong><span></span></li>\n </ul>\n</div>\n<div class="help--overview--header"></div>\n'))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/input/remote/tpl/remote_control.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){var e=[],n=this;return function(){!function(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}(i('<div id="remote-background" class="close-remote"></div>\n<div class="remote kodi-remote">\n <div class="toggle-visibility"></div>\n <div class="playing-area">\n\n </div>\n <div class="main-controls">\n <div class="direction">\n <div class="pad">\n <div class="ibut mdi-hardware-keyboard-arrow-left left input-button" data-type="Left"></div>\n <div class="ibut mdi-hardware-keyboard-arrow-up up input-button" data-type="Up"></div>\n <div class="ibut mdi-hardware-keyboard-arrow-down down input-button" data-type="Down"></div>\n <div class="ibut mdi-hardware-keyboard-arrow-right right input-button" data-type="Right"></div>\n <div class="ibut mdi-image-brightness-1 ok input-button" data-type="Select"></div>\n </div>\n </div>\n <div class="buttons">\n <div class="ibut mdi-action-settings-power power-button"></div>\n <div class="ibut mdi-navigation-more-vert input-button" data-type="ContextMenu"></div>\n <div class="ibut mdi-action-info info-button" data-type="Info"></div>\n </div>\n </div>\n <div class="secondary-controls">\n <div class="ibut mdi-hardware-keyboard-return input-button" data-type="Back"></div>\n <div class="ibut mdi-av-stop player-button" data-type="Stop"></div>\n <div class="ibut mdi-maps-store-mall-directory input-button" data-type="Home"></div>\n </div>\n\n</div>\n'))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/input/remote/tpl/system.jst"]=function(n){function o(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function r(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){var t,e,n,i;for(r(o('<ul class="system-menu__options options">\n ')),e=0,n=(i=this.actions).length;e<n;e++)t=i[e],r(o('\n <li data-action="')),r(t.id),r(o('">')),r(t.title),r(o("</li>\n "));r(o("\n</ul>\n"))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:o};for(t in n)e[t]=n[t];return e}())},window.JST["apps/lab/apiBrowser/tpl/api_browser_landing.jst"]=function(n){function r(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function e(t){void 0!==t&&null!=t&&n.push(t.ecoSafe?t:i.escape(t))}var n=[],i=this;return function(){e(r('<div class="api-browser--landing page">\n <h2>')),e(t.gettext("Kodi API browser")),e(r('</h2>\n <h4><a href="#lab">')),e(t.gettext("Chorus lab")),e(r('</a></h4>\n <div class="api-browser--content">\n <p>')),e(t.gettext("This is a tool to test out the api. Select a method then execute it with parameters.")),e(r('</p>\n <br />\n <div class="alert alert-dismissable alert-warning">\n <button type="button" class="close" data-dismiss="alert">×</button>\n <h4>')),e(t.gettext("Warning")),e(r("</h4>\n <p>")),e(t.gettext("You could potentially damage your system with this and there are no sanity checks. Use at own risk.")),e(r("<br /></p>\n </div>\n </div>\n</div>\n"))}.call(this),n.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:r};for(t in n)e[t]=n[t];return e}())},window.JST["apps/lab/apiBrowser/tpl/api_method_item.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function t(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){t(i('<div class="api-method--item">\n <h4 class="method">')),t(this.method),t(i('</h4>\n <p class="description">')),t(this.description),t(i("</p>\n</div>\n\n"))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/lab/apiBrowser/tpl/api_method_list.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){var e=[],n=this;return function(){!function(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}(i('<div class="api-methods--list">\n <p class="search-box"><input type="text" id="api-search" class="api-methods--search" /></p>\n <ul class="items"></ul>\n</div>\n'))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/lab/apiBrowser/tpl/api_method_page.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function t(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){t(i('\n<div class="api-method--info page">\n <h2 class="method"><a href="http://kodi.wiki/view/JSON-RPC_API/v6#')),t(this.method),t(i('" target="_blank">')),t(this.method),t(i('</a></h2>\n <p class="description">')),t(this.description),t(i("</p>\n\n</div>\n\n")),"method"===this.type&&(t(i('\n <div class="api-method--execute">\n <h3>Execute <strong>')),t(this.method),t(i('</strong> with these params:</h3>\n <textarea class="api-method--params" placeholder=\'Eg. ["arg", "foo", true]\'></textarea>\n <p class="description">Parameters get parsed by\n <a href="https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse" target="_blank">JSON.parse</a>.\n Check the console for response objects, you will get an \'unexpected token\' error if parsing failed.\n Params should be an array \'[]\' matching below \'Method params\'. Only use double quotes for strings/keys.\n </p>\n <p class="description">\n Pass params as array Eg. [true] or [255, ["born", "formed", "thumbnail"]] or [] or [255]. Brackets required.<br />\n Pass params as object Eg. {songid: 255} or {songid: 255, fields: ["born", "formed", "thumbnail"]}. Braces required.<br />\n </p>\n <p><button class="btn btn-primary" id="send-command">Send Command</button></p>\n\n </div>\n'))),t(i('\n\n<div class="api-method--result" id="api-result"></div>\n\n<h3>')),"method"===this.type&&t(i("Method ")),t(i('Params</h3>\n<div class="api-method--params"></div>\n\n')),"method"===this.type&&t(i('\n <hr />\n <h3>Method Returns</h3>\n <div class="api-method--return"></div>\n')),t(i("\n"))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/lab/iconBrowser/tpl/icon_browser_page.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){var e=[],n=this;return function(){!function(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}(i('<h3>Material Icons</h3>\n<ul id="icons-material"></ul>\n\n<h3>Custom Icons</h3>\n<ul id="icons-custom"></ul>\n'))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/lab/lab/tpl/lab_item.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function t(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){t(i('<a class="lab-item" href="#')),t(this.path),t(i('">\n <h4>')),t(this.title),t(i("</h4>\n <p>")),t(this.description),t(i("</p>\n</a>\n"))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/landing/show/tpl/landing_page.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){var e=[],n=this;return function(){!function(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}(i('<a id="landing-hero"></a>\n<div class="landing-sections">\n <div id="landing-section-1"></div>\n <div id="landing-section-2"></div>\n <div id="landing-section-3"></div>\n <div id="landing-section-4"></div>\n <div id="landing-section-5"></div>\n <div id="landing-section-6"></div>\n</div>\n'))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/loading/show/tpl/loading_page.jst"]=function(n){function r(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function e(t){void 0!==t&&null!=t&&n.push(t.ecoSafe?t:i.escape(t))}var n=[],i=this;return function(){e(r('<div id="loading-page">\n <div class="spinner-double-section-far"></div>\n <h2>')),e(t.gettext("Just a sec...")),e(r("</h2>\n</div>\n\n"))}.call(this),n.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:r};for(t in n)e[t]=n[t];return e}())},window.JST["apps/localPlaylist/list/tpl/playlist.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function t(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){t(i('<span class="item">\n ')),this.path?(t(i('\n <a href="#')),t(this.path),t(i('"')),this.active&&t(i(' class="active"')),t(i(">\n ")),t(this.title),t(i("\n </a>\n "))):(t(i("\n ")),t(this.title),t(i("\n "))),t(i("\n</span>\n"))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/localPlaylist/list/tpl/playlist_layout.jst"]=function(n){function r(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function e(t){void 0!==t&&null!=t&&n.push(t.ecoSafe?t:i.escape(t))}var n=[],i=this;return function(){e(r('<div class="local-playlist-header">\n <h2></h2>\n <div class="dropdown">\n <i data-toggle="dropdown"></i>\n <ul class="dropdown-menu">\n <li class="play">')),e(tr("Play in Kodi")),e(r('</li>\n <li class="localplay">')),e(tr("Play in browser")),e(r('</li>\n <li class="export">')),e(tr("Export list")),e(r('</li>\n <div class="divider"></div>\n <li class="rename">')),e(tr("Rename playlist")),e(r('</li>\n <li class="clear">')),e(tr("Clear playlist")),e(r('</li>\n <li class="delete">')),e(tr("Delete playlist")),e(r('</li>\n </ul>\n </div>\n</div>\n<div class="item-container">\n <div class="empty-content">')),e(t.gettext("Empty playlist, you should probably add something to it?")),e(r("</div>\n</div>\n"))}.call(this),n.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:r};for(t in n)e[t]=n[t];return e}())},window.JST["apps/localPlaylist/list/tpl/playlist_list.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){var e=[],n=this;return function(){!function(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}(i('<h3></h3>\n<ul class="lists options"></ul>\n'))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/localPlaylist/list/tpl/playlist_sidebar_layout.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function t(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){t(i('<div class="current-lists"></div>\n<div class="new-list">')),t(tr("New playlist")),t(i("</div>\n"))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/movie/show/tpl/content.jst"]=function(n){function r(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function e(t){void 0!==t&&null!=t&&n.push(t.ecoSafe?t:i.escape(t))}var n=[],i=this;return function(){e(r('<div class="section-content">\n <h2>')),e(t.gettext("Synopsis")),e(r("</h2>\n ")),this.mediaTrailer&&"youtube"===this.mediaTrailer.source&&(e(r('\n <div class="trailer ')),e(this.mediaTrailer.source),e(r('">\n <img src="')),e(r(this.mediaTrailer.img)),e(r('" />\n </div>\n '))),e(r("\n <p>")),e(this.plot),e(r('</p>\n <ul class="inline-links">\n <li>')),e(r(helpers.url.imdbUrl(this.imdbnumber,"View on IMDb"))),e(r("</li>\n </ul>\n</div>\n\n")),0<this.cast.length&&(e(r('\n <div class="section-content">\n <h2>')),e(t.gettext("Full cast")),e(r('</h2>\n <div class="region-cast"></div>\n </div>\n'))),e(r('\n\n<div class="region-more-1"></div>\n<div class="region-more-2"></div>\n<div class="region-more-3"></div>\n<div class="region-more-4"></div>\n<div class="region-more-5"></div>\n'))}.call(this),n.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:r};for(t in n)e[t]=n[t];return e}())},window.JST["apps/movie/show/tpl/details_meta.jst"]=function(n){function s(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function o(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){var e,n,i,r;if(o(s('<div class="region-details-top">\n <div class="region-details-title">\n <h2><span class="title">')),o(this.label),o(s('</span> <span class="sub"><a href="#movies?year=')),o(this.year),o(s('">')),o(this.year),o(s('</a></span></h2>\n </div>\n <div class="region-details-rating">\n ')),o(this.rating),o(s(' <i></i>\n </div>\n</div>\n\n<div class="region-details-meta-below">\n\n <div class="region-details-subtext">\n <div class="runtime">\n ')),o(helpers.global.formatTime(helpers.global.secToTime(this.runtime))),o(s('\n </div>\n </div>\n\n <div class="tagline">')),o(this.plotoutline),o(s('</div>\n\n <ul class="meta">\n ')),0<this.genre.length&&(o(s("\n <li><label>")),o(t.gettext("genre")),o(s(":</label> <span>")),o(s(helpers.url.filterLinks("movies","genre",this.genre))),o(s("</span></li>\n "))),o(s("\n ")),0<this.director.length&&(o(s("\n <li><label>")),o(t.ngettext("Director","Directors",this.director.length)),o(s(":</label> <span>")),o(s(helpers.url.filterLinks("movies","director",this.director))),o(s("</span></li>\n "))),o(s("\n ")),0<this.writer.length&&(o(s("\n <li><label>")),o(t.ngettext("Writer","Writers",this.writer.length)),o(s(":</label> <span>")),o(s(helpers.url.filterLinks("movies","writer",this.writer))),o(s("</span></li>\n "))),o(s("\n ")),0<this.cast.length&&(o(s("\n <li><label>")),o(t.gettext("Cast")),o(s(":</label> <span>")),o(s(helpers.url.filterLinks("movies","cast",_.pluck(this.cast,"name")))),o(s("</span></li>\n "))),o(s("\n ")),this.mpaa&&(o(s("\n <li><label>")),o(t.gettext("rated")),o(s(":</label> <span>")),o(s(helpers.url.filterLinks("movies","mpaa",[this.mpaa]))),o(s("</span></li>\n "))),o(s('\n </ul>\n\n <ul class="streams">\n ')),0<this.streamdetails.video.length&&(o(s("\n <li><label>")),o(t.gettext("Video")),o(s(":</label> <span>")),o(_.pluck(this.streamdetails.video,"label").join(", ")),o(s("</span></li>\n "))),o(s("\n ")),0<this.streamdetails.audio.length&&(o(s("\n <li><label>")),o(t.gettext("Audio")),o(s(":</label> <span>")),o(_.pluck(this.streamdetails.audio,"label").join(", ")),o(s("</span></li>\n "))),o(s("\n ")),0<this.streamdetails.subtitle.length&&""!==this.streamdetails.subtitle[0].label){for(o(s("\n <li><label>")),o(t.ngettext("Subtitle","Subtitles",this.streamdetails.subtitle.length)),o(s(':</label>\n <span class="dropdown"><span data-toggle="dropdown">')),o(_.pluck(this.streamdetails.subtitle,"label").join(", ")),o(s('</span>\n <ul class="dropdown-menu">\n ')),n=0,i=(r=this.streamdetails.subtitle).length;n<i;n++)e=r[n],o(s("\n <li>")),o(e.label),o(s("</li>\n "));o(s("\n </ul>\n </span>\n </li>\n "))}o(s('\n </ul>\n\n <ul class="inline-links">\n <li class="btn-flat-play play">')),o(t.gettext("Play")),o(s('</li>\n <li class="btn-flat-add add">')),o(tr("Queue")),o(s('</li>\n <li class="btn-flat-stream stream">')),o(t.gettext("Stream")),o(s('</li>\n <li class="btn-flat-watched watched">\n ')),o(t.gettext("set")),o(s(' <span class="action-watched">')),o(t.gettext("watched")),o(s('</span><span class="action-unwatched">')),o(t.gettext("unwatched")),o(s('</span>\n </li>\n <li class="more-actions dropdown">\n <span class="btn-flat-more" data-toggle="dropdown" aria-expanded="true">')),o(tr("more")),o(s('</span>\n <ul class="dropdown-menu pull-right">\n <li class="download">')),o(tr("Download")),o(s('</li>\n <li class="divider"></li>\n <li class="dropdown-submenu internal-search">')),o(tr("Chorus Search")),o(s('\n <ul class="dropdown-menu">\n ')),o(s(helpers.entities.getAddonSearchMenuItems(this.label))),o(s('\n </ul>\n </li>\n <li class="dropdown-submenu external-search">')),o(tr("External Search")),o(s('\n <ul class="dropdown-menu">\n <li data-type="google" data-query="')),o(this.label),o(s('">Google</li>\n <li data-type="imdb" data-query="')),o(this.label),o(s('">IMDb</li>\n <li data-type="tmdb" data-query="')),o(this.label),o(s('">TVDb</li>\n </ul>\n </li>\n <li class="youtube-search" data-query="')),o(this.label),o(s('">')),o(tr("YouTube Search")),o(s('</li>\n <li class="divider"></li>\n <li class="refresh">')),o(tr("Refresh")),o(s('</li>\n <li class="edit">')),o(tr("Edit")),o(s("</li>\n </ul>\n </li>\n </ul>\n</div>\n"))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:s};for(t in n)e[t]=n[t];return e}())},window.JST["apps/movie/show/tpl/set.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){var e=[],n=this;return function(){!function(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}(i('<div class="section-content">\n <div class="set-collection">\n <h2 class="set-name"></h2>\n <div class="collection-items"></div>\n </div>\n</div>\n'))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/musicvideo/show/tpl/details_meta.jst"]=function(n){function r(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function e(t){void 0!==t&&null!=t&&n.push(t.ecoSafe?t:i.escape(t))}var n=[],i=this;return function(){e(r('<div class="region-details-top">\n <div class="region-details-title">\n <h2><span class="title">')),e(this.label),e(r("</span></h2>\n </div>\n ")),this.rating&&(e(r('\n <div class="region-details-rating">\n ')),e(this.rating),e(r(" <i></i>\n </div>\n "))),e(r('\n</div>\n\n<div class="region-details-meta-below">\n\n <ul class="meta">\n ')),this.artist&&(e(r("\n <li><label>")),e(tr("artist")),e(r(":</label> <span>")),e(r(helpers.url.filterLinks("music/videos","artist",[this.artist]))),e(r("</span></li>\n "))),e(r("\n ")),this.album&&(e(r("\n <li><label>")),e(tr("album")),e(r(':</label> <span><a href="#music/videos?album=')),e(this.album),e(r('">')),e(this.album),e(r("</a></span></li>\n "))),e(r("\n ")),0<this.genre.length&&(e(r("\n <li><label>")),e(t.ngettext("genre","genres",this.genre.length)),e(r(":</label> <span>")),e(r(helpers.url.filterLinks("music/videos","genre",this.genre))),e(r("</span></li>\n "))),e(r("\n ")),0<this.director.length&&(e(r("\n <li><label>")),e(t.ngettext("Director","Directors",this.director.length)),e(r(":</label> <span>")),e(r(helpers.url.filterLinks("music/videos","director",this.director))),e(r("</span></li>\n "))),e(r("\n ")),0<this.studio.length&&(e(r("\n <li><label>")),e(t.ngettext("Studio","Studios",this.studio.length)),e(r(":</label> <span>")),e(r(helpers.url.filterLinks("music/videos","studio",this.studio))),e(r("</span></li>\n "))),e(r('\n </ul>\n\n <div class="description">')),e(this.plot),e(r('</div>\n\n <ul class="inline-links">\n <li class="btn-flat-play play">')),e(tr("Play")),e(r('</li>\n <li class="btn-flat-add add">')),e(tr("Queue")),e(r('</li>\n <li class="btn-flat-stream localplay">')),e(tr("Stream")),e(r('</li>\n <li class="more-actions dropdown">\n <span class="btn-flat-more" data-toggle="dropdown" aria-expanded="true">')),e(tr("more")),e(r('</span>\n <ul class="dropdown-menu pull-right">\n <li class="download">')),e(tr("Download")),e(r('</li>\n <li class="divider"></li>\n <li class="dropdown-submenu internal-search">')),e(tr("Chorus Search")),e(r('\n <ul class="dropdown-menu">\n ')),e(r(helpers.entities.getAddonSearchMenuItems(this.title))),e(r('\n </ul>\n </li>\n <li class="dropdown-submenu external-search">')),e(tr("External Search")),e(r('\n <ul class="dropdown-menu">\n <li data-type="google" data-query="')),e(this.title),e(r(" ")),e(this.artist),e(r('">Google</li>\n </ul>\n </li>\n <li class="youtube-search" data-query="')),e(this.title),e(r(" ")),e(this.artist),e(r('">')),e(tr("YouTube Search")),e(r('</li>\n <li class="divider"></li>\n <li class="edit">')),e(tr("Edit")),e(r("</li>\n </ul>\n </li>\n </ul>\n\n</div>\n"))}.call(this),n.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:r};for(t in n)e[t]=n[t];return e}())},window.JST["apps/navMain/show/tpl/navMain.jst"]=function(n){function u(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function l(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){var t,e,n,i,r,o,s,a;for(l(u('<div id="nav-header"></div>\n<nav>\n <ul>\n ')),n=0,r=(s=this.items).length;n<r;n++)if("undefined"!==(e=s[n]).path&&0===e.parent){if(l(u('\n <li class="')),l(e.class),l(u('">\n <a href="#')),l(e.path),l(u('">\n <i class="')),l(e.icon),l(u('"></i>\n <span>')),l(e.title),l(u("</span>\n </a>\n\n ")),0!==e.children.length){for(l(u("\n <ul>\n ")),i=0,o=(a=e.children).length;i<o;i++)"undefined"!==(t=a[i]).path&&(l(u('\n <li><a href="#')),l(t.path),l(u('">')),l(t.title),l(u("</a></li>\n ")));l(u("\n </ul>\n "))}l(u("\n </li>\n "))}l(u("\n </ul>\n</nav>\n"))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:u};for(t in n)e[t]=n[t];return e}())},window.JST["apps/navMain/show/tpl/nav_item.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function t(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){t(i(this.link)),t(i("\n"))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/navMain/show/tpl/nav_sub.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function t(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){t(i("<h3>")),t(this.title),t(i('</h3>\n<ul class="items"></ul>\n'))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/player/show/tpl/player.jst"]=function(n){function r(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function e(t){void 0!==t&&null!=t&&n.push(t.ecoSafe?t:i.escape(t))}var n=[],i=this;return function(){e(r('<div class="player">\n\n <div class="controls-primary">\n <div class="controls-primary-buttons">\n <div class="control control-prev"></div>\n <div class="control control-play"></div>\n <div class="control control-next"></div>\n </div>\n </div>\n\n <div class="controls-secondary">\n <div class="volume slider-bar"></div>\n <div class="controls-secondary-buttons">\n <div class="control control-mute"></div>\n <div class="control control-repeat"></div>\n <div class="control control-shuffle"></div>\n <div class="control control-menu"></div>\n </div>\n </div>\n\n <div class="now-playing">\n <div class="playing-thumb thumb">\n <div class="mdi remote-toggle"></div>\n </div>\n <div class="playing-info">\n <div class="playing-progress slider-bar"></div>\n <div class="playing-time">\n <div class="playing-time-current">0</div>\n <div class="playing-time-duration">0:00</div>\n </div>\n <div class="playing-meta">\n <div class="playing-title">')),e(t.gettext("Nothing playing")),e(r('</div>\n <div class="playing-subtitle"></div>\n </div>\n </div>\n </div>\n\n</div>\n'))}.call(this),n.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:r};for(t in n)e[t]=n[t];return e}())},window.JST["apps/playlist/list/tpl/playlist_bar.jst"]=function(n){function r(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function e(t){void 0!==t&&null!=t&&n.push(t.ecoSafe?t:i.escape(t))}var n=[],i=this;return function(){e(r('<div class="playlist-header">\n <ul class="player-toggle">\n <li class="kodi">')),e(t.gettext("Kodi")),e(r('</li>\n <li class="local">')),e(t.gettext("Local")),e(r('</li>\n </ul>\n <div class="playlist-menu dropdown">\n <i data-toggle="dropdown" class="menu-toggle"></i>\n <ul class="dropdown-menu pull-right">\n <li class="dropdown-header">')),e(t.gettext("Current playlist")),e(r('</li>\n <li><a href="#" class="clear-playlist">')),e(t.gettext("Clear playlist")),e(r('</a></li>\n <li><a href="#" class="refresh-playlist">')),e(t.gettext("Refresh playlist")),e(r('</a></li>\n <li><a href="#" class="party-mode">')),e(t.gettext("Party mode")),e(r(' <i class="mdi-navigation-check"></i></a></li>\n <li class="dropdown-header">')),e(t.gettext("Kodi")),e(r('</li>\n <li><a href="#" class="save-playlist">')),e(t.gettext("Save Kodi playlist")),e(r('</a></li>\n </li>\n </ul>\n </div>\n</div>\n<div class="playlists-wrapper">\n <div class="kodi-playlists">\n <ul class="media-toggle">\n <li class="audio">')),e(t.gettext("Audio")),e(r('</li>\n <li class="video">')),e(t.gettext("Video")),e(r('</li>\n </ul>\n <div class="kodi-playlist"></div>\n </div>\n <div class="local-playlists">\n <div class="local-playlist"></div>\n </div>\n</div>\n'))}.call(this),n.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:r};for(t in n)e[t]=n[t];return e}())},window.JST["apps/playlist/list/tpl/playlist_item.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function t(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){t(i('<div class="item-inner item-')),t(this.type),t(i('">\n <div class="artwork">\n <div class="thumb" title="')),t(this.label),t(i('" style="background-image: url(\'')),t(this.thumbnail),t(i('\')">\n <div class="mdi play"></div>\n ')),this.canThumbsUp&&t(i('\n <div class="mdi thumbs"></div>\n ')),t(i('\n </div>\n </div>\n <div class="meta">\n <div class="title"><a href="#')),t(this.url),t(i('" title="')),t(this.label),t(i('">')),t(this.label),t(i("</a></div>\n ")),this.subtitle&&(t(i('\n <div class="subtitle">')),t(this.subtitle),t(i("</div>\n "))),t(i('\n </div>\n <div class="remove"></div>\n</div>\n'))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/playlist/m3u/tpl/list.jst"]=function(n){function o(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function r(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){var t,e,n,i;for(r(o("#EXTCPlayListM3U::M3U\n")),e=0,n=(i=this.items).length;e<n;e++)t=i[e],r(o("#EXTINF:")),r(t.duration),r(o(",")),r(t.artist.join("/")),r(o(" - ")),r(t.label),r(o("\n")),r(t.file),r(o("\n"));r(o("\n"))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:o};for(t in n)e[t]=n[t];return e}())},window.JST["apps/playlist/show/tpl/landing.jst"]=function(n){function r(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function e(t){void 0!==t&&null!=t&&n.push(t.ecoSafe?t:i.escape(t))}var n=[],i=this;return function(){e(r('<div class="playlist-page playlist-page__empty set-page">\n <h3>')),e(t.gettext("Now playing - Playlists")),e(r("</h3>\n <p>")),e(t.gettext("Switch between Kodi and local playback via the tabs. You can toggle visibility with the arrow in the top right")),e(r("</p>\n</div>\n"))}.call(this),n.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:r};for(t in n)e[t]=n[t];return e}())},window.JST["apps/pvr/recordingList/tpl/recording.jst"]=function(n){function r(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function e(t){void 0!==t&&null!=t&&n.push(t.ecoSafe?t:i.escape(t))}var n=[],i=this;return function(){e(r('<div class="title">\n <strong>')),e(this.label),e(r("</strong> <span>- ")),e(this.channel),e(r('</span>\n</div>\n<div class="date">\n ')),e(this.start.toString("h:mmtt").toLowerCase()),e(r(" -\n ")),"1970"!==this.end.toString("yyyy")?(e(r("\n ")),e(this.end.toString("h:mmtt").toLowerCase()),e(r("\n (")),e(helpers.global.formatTime(helpers.global.secToTime(this.runtime))),e(r(")\n "))):(e(r("\n ")),e(tr("Now")),e(r("\n "))),e(r("\n\n <br />")),e(this.start.toString("dddd, dS MMM")),e(r('\n</div>\n<div class="plot">')),e(this.plot),e(r('</div>\n<div class="entity-progress">\n <div class="current-progress" style="width: ')),e(this.progress),e(r('%" title="')),e(this.progress),e(r("% ")),e(t.gettext("complete")),e(r('"></div>\n</div>\n<ul class="actions">\n <li class="play"></li>\n</ul>\n'))}.call(this),n.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:r};for(t in n)e[t]=n[t];return e}())},window.JST["apps/search/list/tpl/search_layout.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){var e=[],n=this;return function(){!function(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}(i('<div class="search-inner">\n <div class="entity-set entity-set-movie"></div>\n <div class="entity-set entity-set-tvshow"></div>\n <div class="entity-set entity-set-artist"></div>\n <div class="entity-set entity-set-album"></div>\n <div class="entity-set entity-set-song"></div>\n <div class="entity-set entity-set-musicvideo"></div>\n <div class="entity-set entity-set-loading"></div>\n <div class="entity-set entity-set-addons"></div>\n</div>\n'))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/search/list/tpl/search_sidebar.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){var e=[],n=this;return function(){!function(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}(i('<div class="sidebar-section sidebar-section-media">\n <h3>Local media</h3>\n <ul class="search-media-links"></ul>\n</div>\n\n<div class="sidebar-section sidebar-section-addon">\n <h3>Addons</h3>\n <ul class="search-addon-links"></ul>\n</div>\n'))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/search/show/tpl/landing.jst"]=function(n){function r(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function e(t){void 0!==t&&null!=t&&n.push(t.ecoSafe?t:i.escape(t))}var n=[],i=this;return function(){e(r('<div class="search-page search-page__empty set-page">\n <h3>')),e(t.gettext("Enter your search above")),e(r("</h3>\n</div>\n"))}.call(this),n.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:r};for(t in n)e[t]=n[t];return e}())},window.JST["apps/settings/show/tpl/settings_sidebar.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){var e=[],n=this;return function(){!function(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}(i('<div class="settings-sidebar">\n <div class="settings-sidebar--section local-nav nav-sub"></div>\n <div class="settings-sidebar--section kodi-nav"></div>\n</div>\n'))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/shell/show/tpl/homepage.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){var e=[],n=this;return function(){!function(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}(i('<div id="homepage"></div>\n'))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/shell/show/tpl/shell.jst"]=function(n){function r(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function e(t){void 0!==t&&null!=t&&n.push(t.ecoSafe?t:i.escape(t))}var n=[],i=this;return function(){e(r('<div id="shell">\n\n <a id="logo" href="#"></a>\n\n <div id="nav-bar"></div>\n\n <div id="header">\n\n <h1 id="page-title">\n <span class="context"></span>\n <span class="title"></span>\n </h1>\n\n <ul class="mobile-menu">\n <li><a href="#remote" class="mobile-menu--link__remote remote-toggle"><i></i></a></li>\n <li><a href="#search" class="mobile-menu--link__search"><i></i></a></li>\n <li><a href="#playlist" class="mobile-menu--link__playlist"><i></i></a></li>\n </ul>\n\n <div id="selected-region">\n <div class="selected-text">\n <span id="selected-count"></span>\n </div>\n <i data-toggle="dropdown" class="menu-toggle"></i>\n <ul class="dropdown-menu pull-right">\n <li class="selected-play">')),e(tr("Play in Kodi")),e(r('</li>\n <li class="selected-add">')),e(tr("Queue in Kodi")),e(r('</li>\n <li class="selected-localadd">')),e(tr("Add to playlist")),e(r('</li>\n </ul>\n </div>\n\n <div id="search-region">\n <input id="search" title="Search">\n <span id="do-search"></span>\n </div>\n\n </div>\n\n <div id="main">\n\n <div id="sidebar-one"></div>\n\n <div id="content">')),e(tr("Loading things...")),e(r('</div>\n\n </div>\n\n <div id="sidebar-two">\n <div class="playlist-toggle-open"></div>\n <div id="playlist-summary"></div>\n <div id="playlist-bar"></div>\n </div>\n\n <div id="remote"></div>\n\n <div id="player-wrapper">\n <footer id="player-kodi"></footer>\n <footer id="player-local"></footer>\n </div>\n\n <div class="player-menu-wrapper">\n <ul class="player-menu">\n <li class="video-scan">')),e(t.gettext("Scan video library")),e(r('</li>\n <li class="audio-scan">')),e(t.gettext("Scan audio library")),e(r('</li>\n <li class="send-input">')),e(t.gettext("Send text to Kodi")),e(r('</li>\n <li class="goto-lab">')),e(t.gettext("The lab")),e(r('</li>\n <li class="about">')),e(t.gettext("About Chorus")),e(r('</li>\n </ul>\n </div>\n\n</div>\n\n<div id="fanart"></div>\n<div id="fanart-overlay"></div>\n\n<div id="snackbar-container"></div>\n\n<div class="modal fade" id="modal-window">\n <div class="modal-dialog">\n <div class="modal-content">\n <div class="modal-header">\n <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>\n <h4 class="modal-title"></h4>\n </div>\n <div class="modal-body"></div>\n <div class="modal-footer"></div>\n </div>\n </div>\n</div>\n\n<div id="disconnected">\n <div class="message">\n <i class="mdi-file-cloud-off"></i>\n <h2>')),e(tr("Lost connection to Kodi")),e(r('</h2>\n <p class="try-connect"><button class="reconnect btn btn-primary">')),e(tr("Attempt to reconnect")),e(r('</button></p>\n <p class="load-connect"><span class="loader-small-inline"></span><br />')),e(tr("Attempting reconnect")),e(r('</p>\n </div>\n</div>\n\n<div id="offscreen"></div>\n'))}.call(this),n.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:r};for(t in n)e[t]=n[t];return e}())},window.JST["apps/song/list/tpl/song.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function t(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){t(i('<td class="cell-first">\n <div class="thumb" style="background-image: url(\'')),t(this.thumbnail),t(i('\')">\n </div>\n <div class="track">')),t(this.track),t(i('</div>\n <div class="mdi play"></div>\n</td>\n<td class="cell-label song-title"><span class="crop">')),t(this.label),t(i('</span></td>\n<td class="cell-label song-album"><a class="crop" href="#music/album/')),t(this.albumid),t(i('">')),t(this.album),t(i('</a></td>\n<td class="cell-label song-artist"><a class="crop" href="#music/artist/')),t(this.artistid),t(i('">')),t(this.artist),t(i('</a></td>\n<td class="cell-last">\n <li class="thumbed-up"></li>\n <div class="duration">')),t(this.displayDuration),t(i('</div>\n <ul class="actions">\n <li class="mdi thumbs"></li>\n <li class="mdi add"></li>\n <li class="menu dropdown">\n <i data-toggle="dropdown" class="mdi"></i>\n <ul class="dropdown-menu pull-right"></ul>\n </li>\n </ul>\n</td>\n<td class="cell-remove song-remove">\n <i class="mdi mdi-navigation-close"></i>\n</td>\n'))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/thumbs/list/tpl/thumbs_layout.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){var e=[],n=this;return function(){!function(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}(i('<div class="thumbs-inner">\n <div class="entity-set entity-set-movie"></div>\n <div class="entity-set entity-set-tvshow"></div>\n <div class="entity-set entity-set-episode"></div>\n <div class="entity-set entity-set-artist"></div>\n <div class="entity-set entity-set-album"></div>\n <div class="entity-set entity-set-song"></div>\n <div class="entity-set entity-set-musicvideo"></div>\n</div>\n'))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/thumbs/list/tpl/thumbs_set.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){var e=[],n=this;return function(){!function(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}(i('<h2 class="set-header"></h2>\n<div class="set-results"></div>\n<div class="more"></div>\n'))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/tvshow/episode/tpl/content.jst"]=function(n){function r(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function e(t){void 0!==t&&null!=t&&n.push(t.ecoSafe?t:i.escape(t))}var n=[],i=this;return function(){e(r('\n<div class="section-content">\n <h2>')),e(t.gettext("Synopsis")),e(r("</h2>\n <p>")),e(this.plot),e(r("</p>\n</div>\n\n")),0<this.cast.length&&(e(r('\n <div class="section-content">\n <h2>')),e(tr("Full cast")),e(r('</h2>\n <div class="region-cast"></div>\n </div>\n'))),e(r('\n\n<div class="section-content section-full-width">\n <h2>')),e(tr("Season")),e(r(" ")),e(this.season),e(r('</h2>\n <div class="region-season"></div>\n</div>\n'))}.call(this),n.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:r};for(t in n)e[t]=n[t];return e}())},window.JST["apps/tvshow/episode/tpl/details_meta.jst"]=function(n){function s(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function o(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){var e,n,i,r;if(o(s('<div class="region-details-top">\n ')),null!=this.showtitle&&o(s("\n\n ")),o(s('\n <div class="region-details-title">\n <h2>\n <span class="title">')),o(this.label),o(s('</span>\n <span class="sub show-title"><a href="#')),o(this.url.split("/",2).join("/")),o(s('">')),o(this.showtitle),o(s('</a></span>\n <span class="sub">S')),o(this.season),o(s(" E")),o(this.episode),o(s('</span>\n </h2>\n </div>\n <div class="region-details-rating">\n ')),o(this.rating),o(s(' <i></i>\n </div>\n</div>\n<div class="region-details-meta-below">\n\n <div class="region-details-subtext">\n\n ')),0<this.runtime&&(o(s('\n <div class="runtime">\n ')),o(helpers.global.formatTime(helpers.global.secToTime(this.runtime))),o(s("\n </div>\n "))),o(s('\n\n </div>\n\n <ul class="meta">\n <li><label>')),o(t.gettext("Season")),o(s(':</label> <span><a href="#tvshow/')),o(this.tvshowid),o(s("/")),o(this.season),o(s('">')),o(t.gettext("Season")),o(s(" ")),o(this.season),o(s("</a></span></li>\n ")),this.firstaired&&(o(s("\n <li><label>")),o(t.gettext("First aired")),o(s(":</label> <span>")),o(this.firstaired),o(s(" </span></li>\n "))),o(s("\n ")),0<this.director.length&&(o(s("\n <li><label>")),o(t.ngettext("Director","Directors",this.director.length)),o(s(":</label> <span>")),o(s(helpers.url.filterLinks("tvshows","director",this.director))),o(s("</span></li>\n "))),o(s("\n ")),0<this.writer.length&&(o(s("\n <li><label>")),o(t.ngettext("Writer","Writers",this.writer.length)),o(s(":</label> <span>")),o(s(helpers.url.filterLinks("tvshows","writer",this.writer))),o(s("</span></li>\n "))),o(s("\n ")),0<this.cast.length&&(o(s("\n <li><label>")),o(t.gettext("Cast")),o(s(":</label> <span>")),o(s(helpers.url.filterLinks("tvshows","cast",_.pluck(this.cast,"name")))),o(s("</span></li>\n "))),o(s('\n </ul>\n\n <ul class="streams">\n ')),0<this.streamdetails.video.length&&(o(s("\n <li><label>")),o(t.gettext("Video")),o(s(":</label> <span>")),o(_.pluck(this.streamdetails.video,"label").join(", ")),o(s("</span></li>\n "))),o(s("\n ")),0<this.streamdetails.audio.length&&(o(s("\n <li><label>")),o(t.gettext("Audio")),o(s(":</label> <span>")),o(_.pluck(this.streamdetails.audio,"label").join(", ")),o(s("</span></li>\n "))),o(s("\n ")),0<this.streamdetails.subtitle.length&&""!==this.streamdetails.subtitle[0].label){for(o(s("\n <li><label>")),o(t.ngettext("Subtitle","Subtitles",this.streamdetails.subtitle.length)),o(s(':</label>\n <span class="dropdown"><span data-toggle="dropdown">')),o(_.first(_.pluck(this.streamdetails.subtitle,"label"))),o(s('</span>\n <ul class="dropdown-menu">\n ')),n=0,i=(r=this.streamdetails.subtitle).length;n<i;n++)e=r[n],o(s("\n <li>")),o(e.label),o(s("</li>\n "));o(s("\n </ul>\n </span>\n </li>\n "))}o(s('\n </ul>\n\n <ul class="inline-links">\n <li class="btn-flat-play play">')),o(tr("Play")),o(s('</li>\n <li class="btn-flat-add add">')),o(tr("Queue")),o(s('</li>\n <li class="btn-flat-stream stream">')),o(tr("Stream")),o(s('</li>\n <li class="btn-flat-watched watched">\n ')),o(t.gettext("set")),o(s(' <span class="action-watched">')),o(tr("watched")),o(s('</span><span class="action-unwatched">')),o(tr("unwatched")),o(s('</span>\n </li>\n <li class="more-actions dropdown">\n <span class="btn-flat-more" data-toggle="dropdown" aria-expanded="true">')),o(tr("more")),o(s('</span>\n <ul class="dropdown-menu pull-right">\n <li class="download">')),o(tr("Download")),o(s('</li>\n <li class="divider"></li>\n <li class="dropdown-submenu internal-search">')),o(tr("Chorus Search")),o(s('\n <ul class="dropdown-menu">\n ')),o(s(helpers.entities.getAddonSearchMenuItems(this.showtitle))),o(s('\n </ul>\n </li>\n <li class="dropdown-submenu external-search">')),o(tr("External Search")),o(s('\n <ul class="dropdown-menu">\n <li data-type="google" data-query="')),o(this.showtitle),o(s(" ")),o(this.label),o(s('">Google</li>\n <li data-type="imdb" data-query="')),o(this.showtitle),o(s('">IMDb</li>\n <li data-type="tvdb" data-query="')),o(this.showtitle),o(s('">TVDb</li>\n <li data-type="tmdb" data-query="')),o(this.showtitle),o(s('">TMDb</li>\n </ul>\n </li>\n <li class="youtube-search" data-query="')),o(this.showtitle),o(s('">')),o(tr("YouTube Search")),o(s('</li>\n <li class="divider"></li>\n <li class="refresh">')),o(tr("Refresh")),o(s('</li>\n <li class="edit">')),o(tr("Edit")),o(s("</li>\n </ul>\n </li>\n </ul>\n</div>\n"))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:s};for(t in n)e[t]=n[t];return e}())},window.JST["apps/tvshow/season/tpl/details_meta.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function t(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){t(i('<div class="region-details-top">\n <div class="region-details-title">\n <h2>\n <span class="title">')),t(tr("Season")),t(i(" ")),t(this.season),t(i('</span>\n <span class="sub"><a href="#tvshow/')),t(this.tvshowid),t(i('">')),t(this.label),t(i('</a></span>\n </h2>\n </div>\n <div class="region-details-rating">\n ')),t(this.rating),t(i(' <i></i>\n </div>\n</div>\n<div class="region-details-meta-below">\n\n <ul class="meta">\n ')),0<this.genre.length&&(t(i("\n <li><label>")),t(tr("genre")),t(i(":</label> <span>")),t(i(helpers.url.filterLinks("tvshows","genre",this.genre))),t(i("</span></li>\n "))),t(i("\n ")),0<this.cast.length&&(t(i("\n <li><label>")),t(tr("cast")),t(i(":</label> <span>")),t(i(helpers.url.filterLinks("tvshows","cast",_.pluck(this.cast,"name")))),t(i("</span></li>\n "))),t(i("\n ")),0<this.studio.length&&(t(i("\n <li><label>")),t(tr("studio")),t(i(":</label> <span>")),t(i(helpers.url.filterLinks("tvshows","studio",this.studio))),t(i("</span></li>\n "))),t(i("\n ")),this.mpaa&&(t(i("\n <li><label>")),t(tr("rated")),t(i(":</label> <span>")),t(i(helpers.url.filterLinks("tvshows","mpaa",[this.mpaa]))),t(i("</span></li>\n "))),t(i("\n <li><label>")),t(tr("episodes")),t(i(':</label> <span><span class="episode-total">')),t(this.episode),t(i("</span> ")),t(tr("total")),t(i(' (<span class="episode-unwatched">')),t(this.unwatched),t(i("</span> ")),t(tr("unwatched")),t(i(')</span></li>\n </ul>\n\n <div class="description">')),t(this.plot),t(i('</div>\n\n <ul class="inline-links">\n <li class="btn-flat-play play">')),t(tr("Play")),t(i('</li>\n <li class="btn-flat-add add">')),t(tr("Queue")),t(i('</li>\n <li class="btn-flat-watched watched">\n ')),t(tr("set")),t(i(' <span class="action-watched">')),t(tr("watched")),t(i('</span><span class="action-unwatched">')),t(tr("unwatched")),t(i("</span>\n </li>\n </ul>\n\n</div>\n"))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["apps/tvshow/show/tpl/details_meta.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function t(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){t(i('<div class="region-details-top">\n <div class="region-details-title">\n <h2><span class="title">')),t(this.label),t(i('</span> <span class="sub">')),t(this.year),t(i('</span></h2>\n </div>\n <div class="region-details-rating">\n ')),t(this.rating),t(i(' <i></i>\n </div>\n</div>\n<div class="region-details-meta-below">\n\n <ul class="meta">\n ')),0<this.genre.length&&(t(i("\n <li><label>")),t(tr("genre")),t(i(":</label> <span>")),t(i(helpers.url.filterLinks("tvshows","genre",this.genre))),t(i("</span></li>\n "))),t(i("\n ")),0<this.cast.length&&(t(i("\n <li><label>")),t(tr("cast")),t(i(":</label> <span>")),t(i(helpers.url.filterLinks("tvshows","cast",_.pluck(this.cast,"name")))),t(i("</span></li>\n "))),t(i("\n ")),0<this.studio.length&&(t(i("\n <li><label>")),t(tr("studio")),t(i(":</label> <span>")),t(i(helpers.url.filterLinks("tvshows","studio",this.studio))),t(i("</span></li>\n "))),t(i("\n ")),this.mpaa&&(t(i("\n <li><label>")),t(tr("rated")),t(i(":</label> <span>")),t(i(helpers.url.filterLinks("tvshows","mpaa",[this.mpaa]))),t(i("</span></li>\n "))),t(i("\n <li><label>")),t(tr("episodes")),t(i(':</label> <span><span class="episode-total">')),t(this.episode),t(i("</span> ")),t(tr("total")),t(i(' (<span class="episode-unwatched">')),t(this.unwatched),t(i("</span> ")),t(tr("unwatched")),t(i(')</span></li>\n </ul>\n\n <div class="description">')),t(this.plot),t(i('</div>\n\n <ul class="inline-links">\n <li class="btn-flat-play play">')),t(tr("Play")),t(i('</li>\n <li class="btn-flat-add add">')),t(tr("Queue")),t(i('</li>\n <li class="btn-flat-watched watched">\n ')),t(tr("set")),t(i(' <span class="action-watched">')),t(tr("watched")),t(i('</span><span class="action-unwatched">')),t(tr("unwatched")),t(i('</span>\n </li>\n <li class="more-actions dropdown">\n <span class="btn-flat-more" data-toggle="dropdown" aria-expanded="true">')),t(tr("more")),t(i('</span>\n <ul class="dropdown-menu pull-right">\n <li class="dropdown-submenu internal-search">')),t(tr("Chorus Search")),t(i('\n <ul class="dropdown-menu">\n ')),t(i(helpers.entities.getAddonSearchMenuItems(this.label))),t(i('\n </ul>\n </li>\n <li class="dropdown-submenu external-search">')),t(tr("External Search")),t(i('\n <ul class="dropdown-menu">\n <li data-type="google" data-query="')),t(this.label),t(i('">Google</li>\n <li data-type="imdb" data-query="')),t(this.label),t(i('">IMDb</li>\n <li data-type="tvdb" data-query="')),t(this.label),t(i('">TVDb</li>\n <li data-type="tmdb" data-query="')),t(this.label),t(i('">TMDb</li>\n </ul>\n </li>\n <li class="youtube-search" data-query="')),t(this.label),t(i('">')),t(tr("YouTube Search")),t(i('</li>\n <li class="divider"></li>\n <li class="dropdown-submenu">')),t(tr("Refresh")),t(i('\n <ul class="dropdown-menu">\n <li class="refresh">')),t(tr("Show only")),t(i('</li>\n <li class="refresh-episodes">')),t(tr("Show and episodes")),t(i('</li>\n </ul>\n </li>\n <li class="edit">')),t(tr("Edit")),t(i("</li>\n </ul>\n </li>\n </ul>\n\n</div>\n"))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["components/form/tpl/form.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){var e=[],n=this;return function(){!function(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}(i('<div class="form-inner">\n <div class="form-content-region"></div>\n <footer>\n <ul class="inline-list">\n <li>\n <button type="submit" data-form-button="submit" class="btn btn-primary form-save">Save</button>\n </li>\n <li class="response">\n\n </li>\n </ul>\n </footer>\n</div>\n'))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["components/form/tpl/form_item.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function t(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){this.titleHtml&&(t(i('\n <label class="control-label">')),t(i(this.titleHtml)),t(i("</label>\n"))),t(i("\n\n")),"markup"===this.type?(t(i("\n ")),t(i(this.element)),t(i("\n"))):(t(i('\n <div class="element">\n ')),"checkbox"!==this.type?(t(i("\n ")),t(i(this.element)),t(i("\n "))):(t(i('\n <div class="togglebutton">\n <label>')),t(i(this.element)),t(i("</label>\n </div>\n "))),t(i("\n ")),this.description&&(t(i('\n <div class="help-block description">')),t(i(this.description)),t(i("</div>\n "))),t(i("\n </div>\n"))),t(i("\n"))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["components/form/tpl/form_item_group.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function t(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){this.title&&(t(i('\n <h3 class="group-title">')),this.icon&&(t(i('<i class="')),t(this.icon),t(i('"></i> '))),t(this.title),t(i("</h3>\n"))),t(i('\n<div class="form-items"></div>\n'))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["components/form/tpl/form_item_imageselect.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function t(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){t(i('<div class="form-imageselect">\n <ul class="form-imageselect__tabs">\n <li data-pane="select" class="active">')),t(tr("Selector")),t(i('</li>\n <li data-pane="url">')),t(tr("URL")),t(i('</li>\n </ul>\n <div class="form-imageselect__panes">\n <div class="pane active" rel="select">\n <ul class="form-imageselect__thumbs">\n ')),this.image&&(t(i('\n <li data-original="')),t(this.image.original),t(i('" class="selected" style="background-image: url(\'')),t(this.image.thumb),t(i("')\"></li>\n "))),t(i('\n </ul>\n <div class="form-imageselect__loader"><div class="loader-small-inline"></div> <span>')),t(tr("Searching for more images")),t(i('</span></div>\n </div>\n <div class="pane" rel="url">\n ')),this.title&&(t(i('\n <label class="control-label">')),t(this.title),t(i("</label>\n "))),t(i('\n <div class="form-imageselect__url">\n ')),t(i(this.element)),t(i("\n </div>\n ")),this.description&&(t(i('\n <div class="help-block description">')),t(i(this.description)),t(i("</div>\n "))),t(i("\n </div>\n </div>\n</div>\n"))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["views/card/tpl/card.jst"]=function(n){function o(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function r(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){var e,n,i;if(r(o('<div class="card-')),r(this.type),r(o('">\n <div class="artwork">\n <a href="#')),r(this.url),r(o('" class="thumb" title="')),r(helpers.global.stripTags(this.labelHtml)),r(o('" style="background-image: url(\'')),r(this.thumbnail),r(o('\')"></a>\n <div class="mdi play" title="')),r(tr("Play")),r(o('"></div>\n ')),"channeltv"!==this.type&&"channelradio"!==this.type||r(o('\n <div class="mdi record"></div>\n ')),r(o('\n </div>\n <div class="meta">\n <div class="title"><a href="#')),r(this.url),r(o('" title="')),r(helpers.global.stripTags(this.labelHtml)),r(o('">')),r(o(this.labelHtml)),r(o("</a></div>\n ")),this.subtitleHtml&&(r(o('\n <div class="subtitle">')),r(o(this.subtitleHtml)),r(o("</div>\n "))),r(o("\n </div>\n ")),this.actions){for(e in r(o('\n <ul class="actions">\n ')),i=this.actions)n=i[e],r(o('<li class="mdi ')),r(e),r(o('" title="')),r(n),r(o('"></li>'));r(o("\n </ul>\n "))}r(o("\n ")),this.menu&&r(o('\n <div class="dropdown">\n <i data-toggle="dropdown" class="mdi"></i>\n <ul class="dropdown-menu"></ul>\n </div>\n ')),r(o("\n ")),this.progress=null!=this.progress?this.progress:0,r(o('\n <div class="entity-progress"><div class="current-progress" style="width: ')),r(this.progress),r(o('%" title="')),r(this.progress),r(o("% ")),r(t.gettext("complete")),r(o('"></div></div>\n</div>\n'))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:o};for(t in n)e[t]=n[t];return e}())},window.JST["views/card/tpl/card_placeholder.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){var e=[],n=this;return function(){!function(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}(i("<i></i>\n"))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["views/empty/tpl/empty_page.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function t(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){t(i('<div class="empty--page">\n ')),this.title&&(t(i('\n <h2 class="empty--page-title">')),t(title),t(i("</h2>\n "))),t(i("\n\n ")),this.content&&(t(i('\n <div class="empty--page-content">')),t(this.content),t(i("</div>\n "))),t(i("\n</div>\n"))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["views/empty/tpl/empty_results.jst"]=function(n){function r(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function e(t){void 0!==t&&null!=t&&n.push(t.ecoSafe?t:i.escape(t))}var n=[],i=this;return function(){e(r('<div class="empty-result">\n <h2>')),e(r(t.sprintf(tr("No %1$s found"),'<span class="empty-key">'+tr("results")+"</span>"))),e(r('</h2>\n <div class="empty-actions">\n <div class="back-link"></div>\n </div>\n</div>\n'))}.call(this),n.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:r};for(t in n)e[t]=n[t];return e}())},window.JST["views/layouts/tpl/layout_details_header.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function t(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){t(i('<div class="layout-container details-header">\n\n <div class="region-details-side"></div>\n\n <div class="region-details-meta-wrapper"><div class="region-details-meta">\n\n <div class="region-details-title"><span class="title"></span> <span class="sub"></span></div>\n\n <div class="region-details-meta-below">\n <div class="region-details-subtext"></div>\n <div class="description"></div>\n </div>\n\n </div></div>\n\n ')),this.fanart&&(t(i('\n <div class="region-details-fanart" style="background-image: url(\'')),t(this.fanart),t(i('\')"><div class="gradient"></div></div>\n '))),t(i("\n</div>\n"))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["views/layouts/tpl/layout_with_header.jst"]=function(n){function r(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function e(t){void 0!==t&&null!=t&&n.push(t.ecoSafe?t:i.escape(t))}var n=[],i=this;return function(){e(r('<div class="layout-container with-header">\n\n <header class="region-header"></header>\n\n <div class="region-content-wrapper">\n <div class="entity-progress"><div class="current-progress" style="width: ')),e(this.progress),e(r('%" title="')),e(this.progress),e(r("% ")),e(t.gettext("complete")),e(r('"></div></div>\n <section class="region-content"></section>\n </div>\n\n</div>\n'))}.call(this),n.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:r};for(t in n)e[t]=n[t];return e}())},window.JST["views/layouts/tpl/layout_with_sidebar_first.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){var e=[],n=this;return function(){!function(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}(i('<div class="layout-container with-sidebar-first">\n\n <div class="region-first-toggle"></div>\n <section class="region-first">\n <div class="region-first-primary"></div>\n <div class="region-first-secondary"></div>\n </section>\n\n <section class="region-content-wrapper">\n <div class="region-content-top"></div>\n <div class="region-content"></div>\n </section>\n\n</div>\n'))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["views/set/tpl/set.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){function t(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}var e=[],n=this;return function(){t(i('<div class="set">\n ')),this.title&&(t(i('\n <div class="set__header">\n <h2 class="set__title">')),t(this.title),t(i('</h2>\n <div class="set__actions">\n ')),this.menu&&t(i('\n <div class="dropdown">\n <i data-toggle="dropdown" class="mdi"></i>\n <ul class="dropdown-menu pull-right"></ul>\n </div>\n ')),t(i("\n </div>\n </div>\n "))),t(i('\n <div class="set__items">\n <')),t(this.childViewTag),t(i(' class="set__collection ')),t(this.childViewClass),t(i('"></')),t(this.childViewTag),t(i(">\n </div>\n ")),this.more&&(t(i('\n <div class="set__more">')),t(i(this.more)),t(i("</div>\n "))),t(i("\n</div>\n"))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())},window.JST["views/song/tpl/song_placeholder.jst"]=function(n){function i(t){void 0===t&&null==t&&(t="");var e=new String(t);return e.ecoSafe=!0,e}return function(){var e=[],n=this;return function(){!function(t){void 0!==t&&null!=t&&e.push(t.ecoSafe?t:n.escape(t))}(i('<td colspan="6"><i></i></td>\n'))}.call(this),e.join("")}.call(function(){var t,e={escape:function(t){return(""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},safe:i};for(t in n)e[t]=n[t];return e}())};;var tr,
4 extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
5 hasProp = {}.hasOwnProperty,
6 bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
7 slice = [].slice;
9 this.helpers = {};
11 this.config = {
12 "static": {
13 appTitle: 'Kodi',
14 jsonRpcEndpoint: 'jsonrpc',
15 socketsHost: location.hostname,
16 socketsPort: 9090,
17 ajaxTimeout: 5000,
18 connected: true,
19 hashKey: 'kodi',
20 defaultPlayer: 'auto',
21 ignoreArticle: true,
22 pollInterval: 10000,
23 reverseProxy: false,
24 albumArtistsOnly: true,
25 searchIndexCacheExpiry: 24 * 60 * 60,
26 collectionCacheExpiry: 7 * 24 * 60 * 60,
27 addOnsLoaded: false,
28 vibrantHeaders: false,
29 lang: "en",
30 kodiSettingsLevel: 'standard',
31 playlistFocusPlaying: true,
32 keyboardControl: 'kodi',
33 disableThumbs: false,
34 showDeviceName: false,
35 refreshIgnoreNFO: true,
36 largeBreakpoint: 910,
37 apiKeyTMDB: '',
38 apiKeyTVDB: '',
39 apiKeyFanartTv: '',
40 apiKeyYouTube: ''
44 this.Kodi = (function(Backbone, Marionette) {
45 var App;
46 App = new Backbone.Marionette.Application();
47 App.addRegions({
48 root: "body"
49 });
50 App.on("before:start", function() {
51 return config["static"] = _.extend(config["static"], config.get('app', 'config:local', config["static"]));
52 });
53 App.vent.on("shell:ready", (function(_this) {
54 return function(options) {
55 return App.startHistory();
57 })(this));
58 return App;
59 })(Backbone, Marionette);
61 $(document).ready((function(_this) {
62 return function() {
63 return helpers.translate.init(function() {
64 Kodi.start();
65 $.material.init();
66 return helpers.ui.bindOnScrollResize();
67 });
69 })(this));
73 Helper to return you to the same scroll position on the last page.
76 helpers.backscroll = {
77 lastPath: '',
78 lastScroll: 0,
79 setLast: function() {
80 this.lastPath = location.hash;
81 return this.lastScroll = document.body.scrollTop;
83 scrollToLast: function() {
84 var scrollPos;
85 scrollPos = this.lastPath === location.hash ? this.lastScroll : 0;
86 if (scrollPos > 0) {
87 return window.scrollTo(0, scrollPos);
94 Our cache storage, persists only for app lifecycle
95 Eg. gets wiped when page reloaded.
98 helpers.cache = {
99 store: {},
100 defaultExpiry: 406800
103 helpers.cache.set = function(key, data, expires) {
104 if (expires == null) {
105 expires = helpers.cache.defaultExpiry;
107 helpers.cache.store[key] = {
108 data: data,
109 expires: expires + helpers.global.time(),
110 key: key
112 return data;
115 helpers.cache.get = function(key, fallback) {
116 if (fallback == null) {
117 fallback = false;
119 if ((helpers.cache.store[key] != null) && helpers.cache.store[key].expires >= helpers.global.time()) {
120 return helpers.cache.store[key].data;
121 } else {
122 return fallback;
126 helpers.cache.del = function(key) {
127 if (helpers.cache.store[key] != null) {
128 return delete helpers.cache.store[key];
132 helpers.cache.clear = function() {
133 return helpers.cache.store = {};
136 helpers.cache.updateCollection = function(collectionKey, responseKey, modelId, property, value) {
137 var i, item, ref, results1;
138 if ((Backbone.fetchCache._cache != null) && (Backbone.fetchCache._cache[collectionKey] != null) && (Backbone.fetchCache._cache[collectionKey].value.result != null)) {
139 if (Backbone.fetchCache._cache[collectionKey].value.result[responseKey] != null) {
140 ref = Backbone.fetchCache._cache[collectionKey].value.result[responseKey];
141 results1 = [];
142 for (i in ref) {
143 item = ref[i];
144 if (item.id === modelId) {
145 results1.push(Backbone.fetchCache._cache[collectionKey].value.result[responseKey][parseInt(i)][property] = value);
146 } else {
147 results1.push(void 0);
150 return results1;
157 Config Helpers.
158 - Use config.get/set with 'app' as the type, to get/set persistent settings (localstorage)
159 - Use config.getLocal/setLocal to get set temp storage
162 config.get = function(type, id, defaultData, callback) {
163 var data;
164 if (defaultData == null) {
165 defaultData = '';
167 data = Kodi.request("config:" + type + ":get", id, defaultData);
168 if (callback != null) {
169 callback(data);
171 return data;
174 config.set = function(type, id, data, callback) {
175 var resp;
176 resp = Kodi.request("config:" + type + ":set", id, data);
177 if (callback != null) {
178 callback(resp);
180 return resp;
183 config.getLocal = function(id, defaultData, callback) {
184 return config.get('static', id, defaultData, callback);
187 config.setLocal = function(id, data, callback) {
188 return config.set('static', id, data, callback);
191 config.setLocalApp = function() {
192 return config.set('static', id, data, callback);
195 config.getAPIKey = function(id, defaultData) {
196 var key;
197 if (defaultData == null) {
198 defaultData = '';
200 key = config.getLocal(id, '');
201 if (key === '') {
202 return atob(defaultData);
203 } else {
204 return key;
208 config.preStartGet = function(id, defaultData) {
209 var config;
210 if (defaultData == null) {
211 defaultData = '';
213 if ((typeof localStorage !== "undefined" && localStorage !== null) && (localStorage.getItem('config:app-config:local') != null)) {
214 config = JSON.parse(localStorage.getItem('config:app-config:local')).data;
215 if (config[id] != null) {
216 return config[id];
219 return defaultData;
224 Connection helpers. For live connecting and disconnecting from Kodi.
227 helpers.connection = {};
229 helpers.connection.reconnect = function(success) {
230 return helpers.connection.ping((function() {
231 config.setLocal('connected', true);
232 Kodi.execute('state:ws:init');
233 return success();
234 }), function() {
235 return Kodi.execute('shell:disconnect');
239 helpers.connection.disconnect = function() {
240 return config.setLocal('connected', false);
243 helpers.connection.ping = function(success, fail) {
244 var d;
245 d = new Date();
246 return $.ajax({
247 url: helpers.url.baseKodiUrl("Ping"),
248 timeout: 3000,
249 contentType: 'application/json',
250 type: 'POST',
251 data: JSON.stringify({
252 jsonrpc: '2.0',
253 method: 'JSONRPC.Ping',
254 params: {},
255 id: d.getTime()
257 success: success,
258 error: fail
264 Entities mixins, all the common things we do/need on almost every collection
266 example of usage:
268 collection = new KodiCollection()
269 .setEntityType 'collection'
270 .setEntityKey 'movie'
271 .setEntityFields 'small', ['thumbnail', 'title']
272 .setEntityFields 'full', ['fanart', 'genre']
273 .setMethod 'VideoLibrary.GetMovies'
274 .setArgHelper 'fields'
275 .setArgHelper 'limit'
276 .setArgHelper 'sort'
277 .applySettings()
280 if (this.KodiMixins == null) {
281 this.KodiMixins = {};
284 KodiMixins.Entities = {
285 url: function() {
286 return helpers.url.baseKodiUrl("Mixins");
288 rpc: new Backbone.Rpc({
289 useNamedParameters: true,
290 namespaceDelimiter: ''
294 Overrides!
298 Apply all the defined settings.
300 applySettings: function() {
301 if (this.entityType === 'model') {
302 return this.setModelDefaultFields();
307 What kind of entity are we dealing with. collection or model
309 entityType: 'model',
310 setEntityType: function(type) {
311 this.entityType = type;
312 return this;
316 Entity Keys, properties that change between the entities
318 entityKeys: {
319 type: '',
320 modelResponseProperty: '',
321 collectionResponseProperty: '',
322 idProperty: ''
324 setEntityKey: function(key, value) {
325 this.entityKeys[key] = value;
326 return this;
328 getEntityKey: function(key) {
329 var ret, type;
330 type = this.entityKeys.type;
331 switch (key) {
332 case 'modelResponseProperty':
333 ret = this.entityKeys[key] != null ? this.entityKeys[key] : type + 'details';
334 break;
335 case 'collectionResponseProperty':
336 ret = this.entityKeys[key] != null ? this.entityKeys[key] : type + 's';
337 break;
338 case 'idProperty':
339 ret = this.entityKeys[key] != null ? this.entityKeys[key] : type + 'id';
340 break;
341 default:
342 ret = type;
344 return ret;
348 The types of fields we request, minimal for search, small for list, full for page.
350 entityFields: {
351 minimal: [],
352 small: [],
353 full: []
355 setEntityFields: function(type, fields) {
356 if (fields == null) {
357 fields = [];
359 this.entityFields[type] = fields;
360 return this;
362 getEntityFields: function(type) {
363 var fields;
364 fields = this.entityFields.minimal;
365 if (type === 'full') {
366 return fields.concat(this.entityFields.small).concat(this.entityFields.full);
367 } else if (type === 'small') {
368 return fields.concat(this.entityFields.small);
369 } else {
370 return fields;
373 modelDefaults: {
374 id: 0,
375 fullyloaded: false,
376 thumbnail: '',
377 thumbsUp: false
379 setModelDefaultFields: function(defaultFields) {
380 var field, len, n, ref, results1;
381 if (defaultFields == null) {
382 defaultFields = {};
384 defaultFields = _.extend(this.modelDefaults, defaultFields);
385 ref = this.getEntityFields('full');
386 results1 = [];
387 for (n = 0, len = ref.length; n < len; n++) {
388 field = ref[n];
389 results1.push(this.defaults[field] = '');
391 return results1;
395 JsonRPC common patterns and helpers.
397 callMethodName: '',
398 callArgs: [],
399 callIgnoreArticle: true,
400 setMethod: function(method) {
401 this.callMethodName = method;
402 return this;
404 setArgStatic: function(callback) {
405 this.callArgs.push(callback);
406 return this;
408 setArgHelper: function(helper, param1, param2) {
409 var func;
410 func = 'argHelper' + helper;
411 this.callArgs.push(this[func](param1, param2));
412 return this;
414 argCheckOption: function(option, fallback) {
415 if ((this.options != null) && (this.options[option] != null)) {
416 return this.options[option];
417 } else {
418 return fallback;
421 argHelperfields: function(type) {
422 var arg;
423 if (type == null) {
424 type = 'small';
426 arg = this.getEntityFields(type);
427 return this.argCheckOption('fields', arg);
429 argHelpersort: function(method, order) {
430 var arg;
431 if (order == null) {
432 order = 'ascending';
434 arg = {
435 method: method,
436 order: order,
437 ignorearticle: this.callIgnoreArticle
439 return this.argCheckOption('sort', arg);
441 argHelperlimit: function(start, end) {
442 var arg;
443 if (start == null) {
444 start = 0;
446 if (end == null) {
447 end = 'all';
449 arg = {
450 start: start
452 if (end !== 'all') {
453 arg.end = end;
455 return this.argCheckOption('limit', arg);
457 argHelperfilter: function(name, value) {
458 var arg;
459 arg = {};
460 if (name != null) {
461 arg[name] = value;
463 return this.argCheckOption('filter', arg);
465 buildRpcRequest: function(type) {
466 var arg, func, key, len, n, ref, req;
467 if (type == null) {
468 type = 'read';
470 req = [this.callMethodName];
471 ref = this.callArgs;
472 for (n = 0, len = ref.length; n < len; n++) {
473 arg = ref[n];
474 func = 'argHelper' + arg;
475 if (typeof func === 'function') {
476 key = 'arg' + req.length;
477 req.push(key);
478 this[key] = func;
479 } else {
480 req.push(arg);
483 return req;
489 Handle errors.
492 helpers.debug = {
493 verbose: false
498 Debug styles
500 @param severity
501 The severity level: info, success, warning, error
504 helpers.debug.consoleStyle = function(severity) {
505 var defaults, mods, prop, styles;
506 if (severity == null) {
507 severity = 'error';
509 defaults = {
510 background: "#ccc",
511 padding: "0 5px",
512 color: "#444",
513 "font-weight": "bold",
514 "font-size": "110%"
516 styles = [];
517 mods = {
518 info: "#D8FEFE",
519 success: "#CCFECD",
520 warning: "#FFFDD9",
521 error: "#FFCECD"
523 defaults.background = mods[severity];
524 for (prop in defaults) {
525 styles.push(prop + ": " + defaults[prop]);
527 return styles.join("; ");
532 Basic debug message
535 helpers.debug.msg = function(msg, severity, data) {
536 if (severity == null) {
537 severity = 'info';
539 if (typeof console !== "undefined" && console !== null) {
540 console.log("%c " + msg, helpers.debug.consoleStyle(severity));
541 if (data != null) {
542 return console.log(data);
549 Log a debug error message.
552 helpers.debug.log = function(msg, data, severity, caller) {
553 if (data == null) {
554 data = 'No data provided';
556 if (severity == null) {
557 severity = 'error';
559 if (caller == null) {
560 caller = arguments.callee.caller.toString();
562 if ((data[0] != null) && data[0].error === "Internal server error") {
564 } else {
565 if (typeof console !== "undefined" && console !== null) {
566 console.log("%c Error in: " + msg, helpers.debug.consoleStyle(severity), data);
567 if (helpers.debug.verbose && caller !== false) {
568 return console.log(caller);
576 Request Error.
579 helpers.debug.rpcError = function(commands, data) {
580 var detail, msg;
581 detail = {
582 called: commands
584 msg = '';
585 if (data.error && data.error.message) {
586 msg = '"' + data.error.message + '"';
587 detail.error = data.error;
588 } else {
589 detail.error = data;
591 return helpers.debug.log("jsonRPC request - " + msg, detail, 'error');
596 JSON Dump (pretty print)
599 helpers.debug.syntaxHighlight = function(json) {
600 var out;
601 json = JSON.stringify(json, null, 4);
602 json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
603 out = json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function(match) {
604 var cls;
605 cls = 'number';
606 if (/^"/.test(match)) {
607 if (/:$/.test(match)) {
608 cls = 'key';
609 } else {
610 cls = 'string';
612 } else if (/true|false/.test(match)) {
613 cls = 'boolean';
614 } else if (/null/.test(match)) {
615 cls = 'null';
617 return '<span class="' + cls + '">' + match + '</span>';
619 return '<pre class="json-syntax-highlight">' + out + '</pre>';
624 Entity Helpers
627 helpers.entities = {};
629 helpers.entities.createUid = function(model, type) {
630 var file, hash, id, uid;
631 type = type ? type : model.type;
632 id = model.id;
633 uid = 'none';
634 if (typeof id === 'number' || type === 'season') {
635 uid = id;
636 } else {
637 file = model.file;
638 if (file) {
639 hash = helpers.global.hashEncode(file);
640 uid = 'path-' + hash.substring(0, 26);
643 return type + '-' + uid;
646 helpers.entities.getFields = function(set, type) {
647 var fields;
648 if (type == null) {
649 type = 'small';
651 if (!_.isObject(set) || !set[type]) {
654 fields = set.minimal;
655 if (type === 'full') {
656 return fields.concat(set.small).concat(set.full);
657 } else if (type === 'small') {
658 return fields.concat(set.small);
659 } else {
660 return fields;
664 helpers.entities.getSubtitle = function(model) {
665 var subtitle;
666 subtitle = '';
667 switch (model.type) {
668 case 'song':
669 if (model.artist) {
670 subtitle = model.artist.join(',');
672 break;
673 default:
674 subtitle = '';
676 return subtitle;
679 helpers.entities.playingLink = function(model) {
680 return "<a href='#" + model.url + "'>" + (_.escape(model.label)) + "</a>";
683 helpers.entities.isWatched = function(model) {
684 var watched;
685 watched = false;
686 if ((model != null) && model.get('playcount')) {
687 watched = model.get('playcount') > 0 ? true : false;
689 return watched;
692 helpers.entities.setProgress = function($el, progress) {
693 progress = progress + '%';
694 return $el.find('.current-progress').css('width', progress).attr('title', progress + ' ' + t.gettext('complete'));
697 helpers.entities.buildOptions = function(options) {
698 var defaultOptions;
699 defaultOptions = {
700 useNamedParameters: true
702 if (!options.filter) {
703 defaultOptions.cache = true;
704 defaultOptions.expires = config.get('static', 'collectionCacheExpiry');
706 return _.extend(defaultOptions, options);
709 helpers.entities.getAddonSearchMenuItems = function(query) {
710 var addonSearch, addonSearches, len, n, ret;
711 addonSearches = Kodi.request("addon:search:enabled");
712 ret = '<li data-type="all" data-query="' + query + '">' + tr('Local media') + '</li>';
713 if (addonSearches.length > 0) {
714 ret += '<li class="divider"></li>';
715 for (n = 0, len = addonSearches.length; n < len; n++) {
716 addonSearch = addonSearches[n];
717 ret += '<li data-type="' + addonSearch.id + '" data-query="' + query + '">' + tr(addonSearch.title) + '</li>';
720 return ret;
723 helpers.entities.refreshEntity = function(model, controller, method, params) {
724 var baseUrl, originalPath, refreshTimeout, thumbs, title, type;
725 if (params == null) {
726 params = {};
728 title = model.get('label');
729 type = model.get('type');
730 originalPath = model.get('url');
731 refreshTimeout = type === 'tvshow' ? 10000 : 3000;
732 baseUrl = model.get('url').split('/').slice(0, -1).join('/');
733 thumbs = Kodi.request("thumbsup:check", model);
734 params.ignorenfo = config.getLocal('refreshIgnoreNFO', true);
735 return Kodi.execute("ui:modal:confirm", tr('Are you sure?'), _.escape(t.sprintf(tr('Confirm refresh'), title)), function() {
736 Backbone.fetchCache.clearItem(model);
737 if (thumbs) {
738 Kodi.request("thumbsup:toggle:entity", model);
740 return controller[method](model.get('id'), params, function(resp) {
741 Kodi.execute("notification:show", tr("Refreshed media. Additional updates may still be occurring in the background"));
742 return setTimeout(function() {
743 var opts;
744 if (title) {
745 opts = {
746 limits: {
747 start: 0,
748 end: 1
750 filter: {
751 'operator': 'is',
752 'field': 'title',
753 'value': title
755 sort: {
756 method: 'none',
757 order: 'descending'
759 success: function(collection) {
760 var newModel;
761 if (collection.length) {
762 newModel = collection.first();
763 if (thumbs) {
764 Kodi.request("thumbsup:toggle:entity", newModel);
766 if (originalPath === helpers.url.path()) {
767 return Kodi.navigate(baseUrl + "/" + newModel.get('id'), {
768 trigger: true
771 } else {
772 Kodi.execute("notification:show", tr("Refreshed media not found, redirecting to search"));
773 return Kodi.navigate("search/" + type + "/" + title, {
774 trigger: true
779 return Kodi.request(type + ":entities", opts);
781 }, refreshTimeout);
788 Our generic global helpers so we dont have add complexity to our app.
791 helpers.global = {};
793 helpers.global.shuffle = function(array) {
794 var i, j, temp;
795 i = array.length - 1;
796 while (i > 0) {
797 j = Math.floor(Math.random() * (i + 1));
798 temp = array[i];
799 array[i] = array[j];
800 array[j] = temp;
801 i--;
803 return array;
806 helpers.global.getRandomInt = function(min, max) {
807 return Math.floor(Math.random() * (max - min + 1)) + min;
810 helpers.global.time = function() {
811 var timestamp;
812 timestamp = new Date().getTime();
813 return timestamp / 1000;
816 helpers.global.inArray = function(needle, haystack) {
817 return _.indexOf(haystack, needle) > -1;
820 helpers.global.loading = (function(_this) {
821 return function(state) {
822 var op;
823 if (state == null) {
824 state = 'start';
826 op = state === 'start' ? 'add' : 'remove';
827 if (_this.Kodi != null) {
828 return _this.Kodi.execute("body:state", op, "loading");
831 })(this);
833 helpers.global.numPad = function(num, size) {
834 var s;
835 s = "000000000" + num;
836 return s.substr(s.length - size);
839 helpers.global.secToTime = function(totalSec) {
840 var hours, minutes, seconds;
841 if (totalSec == null) {
842 totalSec = 0;
844 totalSec = Math.round(totalSec);
845 hours = parseInt(totalSec / 3600) % 24;
846 minutes = parseInt(totalSec / 60) % 60;
847 seconds = totalSec % 60;
848 return {
849 hours: hours,
850 minutes: minutes,
851 seconds: seconds
855 helpers.global.timeToSec = function(time) {
856 var hours, minutes;
857 hours = parseInt(time.hours) * (60 * 60);
858 minutes = parseInt(time.minutes) * 60;
859 return parseInt(hours) + parseInt(minutes) + parseInt(time.seconds);
862 helpers.global.dateStringToObj = function(datetime) {
863 if (!datetime) {
864 return new Date(0);
865 } else {
866 return new Date(datetime.replace(" ", "T"));
870 helpers.global.formatTime = function(time) {
871 var hrStr;
872 if (time == null) {
873 return 0;
874 } else {
875 hrStr = "";
876 if (time.hours > 0) {
877 if (time.hours < 10) {
878 hrStr = "0";
880 hrStr += time.hours + ':';
882 return hrStr + (time.minutes<10 ? '0' : '') + time.minutes + ':' + (time.seconds<10 ? '0' : '') + time.seconds;
886 helpers.global.paramObj = function(key, value) {
887 var obj;
888 obj = {};
889 obj[key] = value;
890 return obj;
893 helpers.global.regExpEscape = function(str) {
894 return str.replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
897 helpers.global.stringStartsWith = function(start, data) {
898 return new RegExp('^' + helpers.global.regExpEscape(start)).test(data);
901 helpers.global.stringStripStartsWith = function(start, data) {
902 return data.substring(start.length);
905 helpers.global.arrayToSentence = function(arr, pluralise) {
906 var i, item, last, prefix, str;
907 if (pluralise == null) {
908 pluralise = true;
910 str = '';
911 prefix = pluralise ? 's' : '';
912 last = arr.pop();
913 if (arr.length > 0) {
914 for (i in arr) {
915 item = arr[i];
916 str += '<strong>' + _.escape(item + prefix) + '</strong>';
917 str += parseInt(i) !== (arr.length - 1) ? ', ' : '';
919 str += ' ' + t.gettext('and') + ' ';
921 return str += '<strong>' + _.escape(last + prefix) + '</strong>';
924 helpers.global.hashEncode = function(value) {
925 return Base64.encode(value);
928 helpers.global.hashDecode = function(value) {
929 return Base64.decode(value);
932 helpers.global.rating = function(rating) {
933 return Math.round(rating * 10) / 10;
936 helpers.global.appTitle = function(playingItem) {
937 var titlePrefix;
938 if (playingItem == null) {
939 playingItem = false;
941 titlePrefix = '';
942 if (_.isObject(playingItem) && (playingItem.label != null)) {
943 titlePrefix = '▶ ' + playingItem.label + ' | ';
945 return document.title = titlePrefix + config.get('static', 'appTitle');
948 helpers.global.localVideoPopup = function(path, height) {
949 if (height == null) {
950 height = 590;
952 return window.open(path, "_blank", "toolbar=no, scrollbars=no, resizable=yes, width=925, height=" + height + ", top=100, left=100");
955 helpers.global.stripTags = function(string) {
956 if (string != null) {
957 return string.replace(/(<([^>]+)>)/ig, "");
958 } else {
959 return '';
963 helpers.global.round = function(x, places) {
964 if (places == null) {
965 places = 0;
967 return parseFloat(x.toFixed(places));
970 helpers.global.getPercent = function(pos, total, places) {
971 if (places == null) {
972 places = 2;
974 return Math.floor((pos / total) * (100 * Math.pow(10, places))) / 100;
977 helpers.global.saveFileText = function(content, filename) {
978 var blob, error, error1, isFileSaverSupported;
979 if (filename == null) {
980 filename = 'untitled.txt';
982 try {
983 isFileSaverSupported = !!new Blob;
984 if (isFileSaverSupported) {
985 content = content.replace(String.fromCharCode(65279), "");
986 blob = new Blob([content], {
987 type: "text/plain;charset=utf-8"
989 return saveAs(blob, filename, true);
991 } catch (error1) {
992 error = error1;
993 return Kodi.execute("notification:show", tr('Saving is not supported by your browser'));
997 helpers.global.removeBBCode = function(string) {
998 return string.replace(/\[\/?(?:b|i|u|s|left|center|right|quote|code|list|img|spoil|color).*?\]/ig, '');
1003 A collection of small jquery plugin helpers.
1006 $.fn.removeClassRegex = function(regex) {
1007 return $(this).removeClass(function(index, classes) {
1008 return classes.split(/\s+/).filter(function(c) {
1009 return regex.test(c);
1010 }).join(' ');
1014 $.fn.removeClassStartsWith = function(startsWith) {
1015 return this.each(function(i, el) {
1016 var classes;
1017 classes = el.className.split(" ").filter(function(c) {
1018 return c.lastIndexOf(startsWith, 0) !== 0;
1020 el.className = $.trim(classes.join(" "));
1021 return this;
1025 $.fn.scrollStopped = function(callback) {
1026 var $this, self;
1027 $this = $(this);
1028 self = this;
1029 return $this.scroll(function() {
1030 if ($this.data('scrollTimeout')) {
1031 clearTimeout($this.data('scrollTimeout'));
1033 return $this.data('scrollTimeout', setTimeout(callback, 250, self));
1037 $.fn.resizeStopped = function(callback) {
1038 var $this, self;
1039 $this = $(this);
1040 self = this;
1041 return $this.resize(function() {
1042 if ($this.data('resizeTimeout')) {
1043 clearTimeout($this.data('resizeTimeout'));
1045 return $this.data('resizeTimeout', setTimeout(callback, 250, self));
1049 $.fn.filterList = function(settings, callback) {
1050 var $this, defaults;
1051 $this = $(this);
1052 defaults = {
1053 hiddenClass: 'hidden',
1054 items: '.filter-options-list li',
1055 textSelector: '.option'
1057 settings = $.extend(defaults, settings);
1058 return $this.on('keyup', (function(_this) {
1059 return function() {
1060 var $list, val;
1061 val = $this.val().toLocaleLowerCase();
1062 $list = $(settings.items).removeClass(settings.hiddenClass);
1063 if (val.length > 0) {
1064 $list.each(function(i, d) {
1065 var text;
1066 text = $(d).find(settings.textSelector).text().toLowerCase();
1067 if (text.indexOf(val) === -1) {
1068 return $(d).addClass(settings.hiddenClass);
1072 if (typeof callback === "function") {
1073 return callback();
1076 })(this));
1079 $(document).ready(function() {
1080 $('.dropdown li').on('click', function() {
1081 return $(this).closest('.dropdown').removeClass('open');
1083 return $('.dropdown').on('click', function() {
1084 return $(this).removeClass('open').trigger('hide.bs.dropdown');
1090 Stream helpers
1093 helpers.stream = {};
1097 Maps.
1100 helpers.stream.videoSizeMap = [
1102 min: 0,
1103 max: 360,
1104 label: 'SD'
1105 }, {
1106 min: 361,
1107 max: 480,
1108 label: '480'
1109 }, {
1110 min: 481,
1111 max: 576,
1112 label: '576'
1113 }, {
1114 min: 577,
1115 max: 720,
1116 label: '720p'
1117 }, {
1118 min: 721,
1119 max: 1080,
1120 label: '1080p'
1121 }, {
1122 min: 1081,
1123 max: 100000,
1124 label: '4K'
1128 helpers.stream.audioChannelMap = [
1130 channels: 6,
1131 label: '5.1'
1132 }, {
1133 channels: 7,
1134 label: '6.1'
1135 }, {
1136 channels: 8,
1137 label: '7.1'
1138 }, {
1139 channels: 2,
1140 label: '2.1'
1141 }, {
1142 channels: 1,
1143 label: 'mono'
1147 helpers.stream.langMap = {
1148 'und': 'Unknown',
1149 'aar': 'Afar',
1150 'abk': 'Abkhazian',
1151 'afr': 'Afrikaans',
1152 'aka': 'Akan',
1153 'alb': 'Albanian',
1154 'amh': 'Amharic',
1155 'ara': 'Arabic',
1156 'arg': 'Aragonese',
1157 'arm': 'Armenian',
1158 'asm': 'Assamese',
1159 'ava': 'Avaric',
1160 'ave': 'Avestan',
1161 'aym': 'Aymara',
1162 'aze': 'Azerbaijani',
1163 'bak': 'Bashkir',
1164 'bam': 'Bambara',
1165 'baq': 'Basque',
1166 'bel': 'Belarusian',
1167 'ben': 'Bengali',
1168 'bis': 'Bislama',
1169 'bos': 'Bosnian',
1170 'bre': 'Breton',
1171 'bul': 'Bulgarian',
1172 'bur': 'Burmese',
1173 'cat': 'Catalan',
1174 'cha': 'Chamorro',
1175 'che': 'Chechen',
1176 'chi': 'Chinese',
1177 'chv': 'Chuvash',
1178 'cor': 'Cornish',
1179 'cos': 'Corsican',
1180 'cre': 'Cree',
1181 'cze': 'Czech',
1182 'dan': 'Danish',
1183 'div': 'Maldivian',
1184 'dut': 'Dutch',
1185 'dzo': 'Dzongkha',
1186 'eng': 'English',
1187 'epo': 'Esperanto',
1188 'est': 'Estonian',
1189 'ewe': 'Ewe',
1190 'fao': 'Faroese',
1191 'fij': 'Fijian',
1192 'fin': 'Finnish',
1193 'fre': 'French',
1194 'fry': 'Western Frisian',
1195 'ful': 'Fulah',
1196 'geo': 'Georgian',
1197 'ger': 'German',
1198 'gla': 'Gaelic',
1199 'gle': 'Irish',
1200 'glg': 'Galician',
1201 'glv': 'Manx',
1202 'gre': 'Greek',
1203 'grn': 'Guarani',
1204 'guj': 'Gujarati',
1205 'hat': 'Haitian',
1206 'hau': 'Hausa',
1207 'heb': 'Hebrew',
1208 'her': 'Herero',
1209 'hin': 'Hindi',
1210 'hmo': 'Hiri Motu',
1211 'hrv': 'Croatian',
1212 'hun': 'Hungarian',
1213 'ibo': 'Igbo',
1214 'ice': 'Icelandic',
1215 'ido': 'Ido',
1216 'iii': 'Sichuan Yi',
1217 'iku': 'Inuktitut',
1218 'ile': 'Interlingue',
1219 'ina': 'Interlingua',
1220 'ind': 'Indonesian',
1221 'ipk': 'Inupiaq',
1222 'ita': 'Italian',
1223 'jav': 'Javanese',
1224 'jpn': 'Japanese',
1225 'kal': 'Kalaallisut',
1226 'kan': 'Kannada',
1227 'kas': 'Kashmiri',
1228 'kau': 'Kanuri',
1229 'kaz': 'Kazakh',
1230 'khm': 'Central Khmer',
1231 'kik': 'Kikuyu',
1232 'kin': 'Kinyarwanda',
1233 'kir': 'Kirghiz',
1234 'kom': 'Komi',
1235 'kon': 'Kongo',
1236 'kor': 'Korean',
1237 'kua': 'Kuanyama',
1238 'kur': 'Kurdish',
1239 'lao': 'Lao',
1240 'lat': 'Latin',
1241 'lav': 'Latvian',
1242 'lim': 'Limburgish',
1243 'lin': 'Lingala',
1244 'lit': 'Lithuanian',
1245 'ltz': 'Luxembourgish',
1246 'lub': 'Luba-Katanga',
1247 'lug': 'Ganda',
1248 'mac': 'Macedonian',
1249 'mah': 'Marshallese',
1250 'mal': 'Malayalam',
1251 'mao': 'Maori',
1252 'mar': 'Marathi',
1253 'may': 'Malay',
1254 'mlg': 'Malagasy',
1255 'mlt': 'Maltese',
1256 'mon': 'Mongolian',
1257 'nau': 'Nauru',
1258 'nav': 'Navajo',
1259 'nbl': 'South Ndebele',
1260 'nde': 'North Ndebele',
1261 'ndo': 'Ndonga',
1262 'nep': 'Nepali',
1263 'nno': 'Norwegian Nynorsk',
1264 'nob': 'Norwegian Bokmål',
1265 'nor': 'Norwegian',
1266 'nya': 'Chichewa',
1267 'oci': 'Occitan',
1268 'oji': 'Ojibwa',
1269 'ori': 'Oriya',
1270 'orm': 'Oromo',
1271 'oss': 'Ossetian',
1272 'pan': 'Punjabi',
1273 'per': 'Persian',
1274 'pli': 'Pali',
1275 'pol': 'Polish',
1276 'por': 'Portuguese',
1277 'pus': 'Pashto',
1278 'que': 'Quechua',
1279 'roh': 'Romansh',
1280 'rum': 'Romanian',
1281 'run': 'Rundi',
1282 'rus': 'Russian',
1283 'sag': 'Sango',
1284 'san': 'Sanskrit',
1285 'sin': 'Sinhalese',
1286 'slo': 'Slovak',
1287 'slv': 'Slovenian',
1288 'sme': 'Northern Sami',
1289 'smo': 'Samoan',
1290 'sna': 'Shona',
1291 'snd': 'Sindhi',
1292 'som': 'Somali',
1293 'sot': 'Southern Sotho',
1294 'spa': 'Spanish',
1295 'srd': 'Sardinian',
1296 'srp': 'Serbian',
1297 'ssw': 'Swati',
1298 'sun': 'Sundanese',
1299 'swa': 'Swahili',
1300 'swe': 'Swedish',
1301 'tah': 'Tahitian',
1302 'tam': 'Tamil',
1303 'tat': 'Tatar',
1304 'tel': 'Telugu',
1305 'tgk': 'Tajik',
1306 'tgl': 'Tagalog',
1307 'tha': 'Thai',
1308 'tib': 'Tibetan',
1309 'tir': 'Tigrinya',
1310 'ton': 'Tonga',
1311 'tsn': 'Tswana',
1312 'tso': 'Tsonga',
1313 'tuk': 'Turkmen',
1314 'tur': 'Turkish',
1315 'twi': 'Twi',
1316 'uig': 'Uighur',
1317 'ukr': 'Ukrainian',
1318 'urd': 'Urdu',
1319 'uzb': 'Uzbek',
1320 'ven': 'Venda',
1321 'vie': 'Vietnamese',
1322 'vol': 'Volapük',
1323 'wel': 'Welsh',
1324 'wln': 'Walloon',
1325 'wol': 'Wolof',
1326 'xho': 'Xhosa',
1327 'yid': 'Yiddish',
1328 'yor': 'Yoruba',
1329 'zha': 'Zhuang',
1330 'zul': 'Zulu'
1335 Formatters.
1338 helpers.stream.aspectRatio = function(rawAspect) {
1339 if (rawAspect < 1.3499) {
1340 return '1.33:1';
1342 if (rawAspect < 1.5080) {
1343 return '1.37:1';
1345 if (rawAspect < 1.719) {
1346 return '1.66:1';
1348 if (rawAspect < 1.8147) {
1349 return '16:9';
1351 if (rawAspect < 2.0174) {
1352 return '1.85:1';
1354 if (rawAspect < 2.2738) {
1355 return '2.20:1';
1357 if (rawAspect < 2.3749) {
1358 return '2.35:1';
1360 if (rawAspect < 2.4739) {
1361 return '2.40:1';
1363 if (rawAspect < 2.6529) {
1364 return '2.55:1';
1366 return 'Unknown Aspect Ratio';
1369 helpers.stream.videoFormat = function(videoStreams) {
1370 var i, match, stream;
1371 for (i in videoStreams) {
1372 stream = videoStreams[i];
1373 match = {
1374 label: 'SD'
1376 if (stream.height && stream.height > 0) {
1377 match = _.find(helpers.stream.videoSizeMap, function(res) {
1378 var ref;
1379 if ((res.min < (ref = stream.height) && ref <= res.max)) {
1380 return true;
1381 } else {
1382 return false;
1386 videoStreams[i].label = stream.codec + ' ' + match.label + ' (' + stream.width + ' x ' + stream.height + ') [' + helpers.stream.aspectRatio(stream.aspect) + ']';
1387 videoStreams[i].shortlabel = stream.codec + ' ' + match.label;
1388 videoStreams[i].res = match.label;
1390 return videoStreams;
1393 helpers.stream.formatLanguage = function(lang) {
1394 if (helpers.stream.langMap[lang]) {
1395 return helpers.stream.langMap[lang];
1396 } else {
1397 return lang;
1401 helpers.stream.audioFormat = function(audioStreams) {
1402 var ch, i, lang, stream;
1403 for (i in audioStreams) {
1404 stream = audioStreams[i];
1405 ch = _.findWhere(helpers.stream.audioChannelMap, {
1406 channels: stream.channels
1408 ch = ch ? ch.label : stream.channels;
1409 lang = '';
1410 if (stream.language !== '' && stream.language !== 'und') {
1411 lang = ' (' + helpers.stream.formatLanguage(stream.language) + ')';
1413 audioStreams[i].label = stream.codec + ' ' + ch + lang;
1414 audioStreams[i].shortlabel = stream.codec + ' ' + ch;
1415 audioStreams[i].ch = ch;
1417 return audioStreams;
1420 helpers.stream.subtitleFormat = function(subtitleStreams) {
1421 var distinctLanguages, i, stream, uniqueStreams;
1422 distinctLanguages = [];
1423 uniqueStreams = [];
1424 for (i in subtitleStreams) {
1425 stream = subtitleStreams[i];
1426 if (distinctLanguages.indexOf(subtitleStreams[i].language) === -1) {
1427 distinctLanguages.push(subtitleStreams[i].language);
1428 uniqueStreams.push(subtitleStreams[i]);
1431 for (i in uniqueStreams) {
1432 stream = uniqueStreams[i];
1433 uniqueStreams[i].label = helpers.stream.formatLanguage(stream.language);
1434 uniqueStreams[i].shortlabel = helpers.stream.formatLanguage(stream.language);
1436 return uniqueStreams;
1439 helpers.stream.streamFormat = function(streams) {
1440 var len, n, streamTypes, type;
1441 streamTypes = ['audio', 'video', 'subtitle'];
1442 for (n = 0, len = streamTypes.length; n < len; n++) {
1443 type = streamTypes[n];
1444 if (streams[type] && streams[type].length > 0) {
1445 streams[type] = helpers.stream[type + 'Format'](streams[type]);
1446 } else {
1447 streams[type] = [];
1450 return streams;
1455 For everything translatable.
1458 helpers.translate = {};
1460 helpers.translate.getLanguages = function() {
1461 return {
1462 af: "Afrikaans (South Africa)",
1463 am: "Amharic (Ethiopia)",
1464 ar: "Arabic (Saudi Arabia)",
1465 ast: "Asturian (Spain)",
1466 az: "Azerbaijani",
1467 be: "Belarusian",
1468 bg: "Bulgarian",
1469 bs: "Bosnian",
1470 ca: "Catalan (Spain)",
1471 cs: "Czech",
1472 cy: "Welsh (United Kingdom)",
1473 da: "Danish",
1474 de: "German",
1475 el: "Greek",
1476 en: "English (United Kingdom)",
1477 en_au: "English (Australia)",
1478 en_nz: "English (New Zealand)",
1479 en_us: "English (United States)",
1480 eo: "Esperanto",
1481 es: "Spanish (Spain)",
1482 es_ar: "Spanish (Argentina)",
1483 es_mx: "Spanish (Mexico)",
1484 et: "Estonian",
1485 eu: "Basque (Spain)",
1486 fa: "Persian (Afghanistan)",
1487 fa_ir: "Persian (Iran)",
1488 fi: "Finnish",
1489 fo: "Faroese",
1490 fr: "French (France)",
1491 fr_ca: "French (Canada)",
1492 gl: "Galician (Spain)",
1493 he: "Hebrew (Israel)",
1494 hi: "Hindi (India)",
1495 hr: "Croatian",
1496 hu: "Hungarian",
1497 hy: "Armenian",
1498 id: "Indonesian",
1499 is: "Icelandic",
1500 it: "Italian",
1501 ja: "Japanese",
1502 kn: "Kannada (India)",
1503 ko: "Korean",
1504 lt: "Lithuanian",
1505 lv: "Latvian",
1506 mi: "Maori",
1507 mk: "Macedonian",
1508 ml: "Malayalam (India)",
1509 mn: "Mongolian",
1510 ms: "Malay",
1511 mt: "Maltese",
1512 my: "Burmese",
1513 nb: "Norwegian",
1514 nl: "Dutch",
1515 pl: "Polish",
1516 pt: "Portuguese (Portugal)",
1517 pt_br: "Portuguese (Brazil)",
1518 ro: "Romanian",
1519 ru: "Russian",
1520 si: "Sinhala (Sri Lanka)",
1521 sk: "Slovak",
1522 sl: "Slovenian",
1523 sq: "Albanian",
1524 sr: "Serbian",
1525 "sr_rs@latin": "Serbian (latin)",
1526 sv: "Swedish",
1527 szl: "Silesian",
1528 ta: "Tamil (India)",
1529 te: "Telugu (India)",
1530 tg: "Tajik",
1531 th: "Thai",
1532 tr: "Turkish",
1533 uk: "Ukrainian",
1534 uz: "Uzbek",
1535 vi: "Vietnamese",
1536 zh_cn: "Chinese (Simplified)",
1537 zh_tw: "Chinese (Traditional)"
1541 helpers.translate.init = function(callback) {
1542 var defaultLang, lang;
1543 defaultLang = config.get("static", "lang", "en");
1544 lang = config.preStartGet("lang", defaultLang);
1545 return $.getJSON("lang/_strings/" + lang + ".json", function(data) {
1546 window.t = new Jed(data);
1547 t.options["missing_key_callback"] = function(key) {
1548 return helpers.translate.missingKeyLog(key);
1550 return callback();
1554 helpers.translate.missingKeyLog = function(key) {
1555 var item;
1556 item = '\n\n' + 'msgctxt ""\n' + 'msgid "' + key + '"\n' + 'msgstr "' + key + '"\n';
1557 return helpers.debug.msg(item, 'warning');
1562 Translate aliases.
1565 tr = function(text) {
1566 return t.gettext(text);
1571 UI helpers for the app.
1574 helpers.ui = {};
1576 helpers.ui.bindOnScrollResize = function() {
1577 $(window).scrollStopped((function(_this) {
1578 return function() {
1579 return helpers.ui.requestTick();
1581 })(this));
1582 return $(window).resizeStopped((function(_this) {
1583 return function() {
1584 return helpers.ui.requestTick();
1586 })(this));
1589 helpers.ui.isTicking = false;
1591 helpers.ui.requestTick = function() {
1592 if (!helpers.ui.isTicking) {
1593 requestAnimationFrame((function(_this) {
1594 return function() {
1595 Kodi.vent.trigger("ui:animate:stop");
1596 return helpers.ui.isTicking = false;
1598 })(this));
1600 return helpers.ui.isTicking = true;
1603 helpers.ui.getSwatch = function(imgSrc, callback) {
1604 var img, ret;
1605 ret = {};
1606 img = document.createElement('img');
1607 img.setAttribute('src', imgSrc);
1608 return img.addEventListener('load', function() {
1609 var sw, swatch, swatches, vibrant;
1610 vibrant = new Vibrant(img);
1611 swatches = vibrant.swatches();
1612 for (swatch in swatches) {
1613 if (swatches.hasOwnProperty(swatch) && swatches[swatch]) {
1614 sw = swatches[swatch];
1615 ret[swatch] = {
1616 hex: sw.getHex(),
1617 rgb: _.map(sw.getRgb(), function(c) {
1618 return Math.round(c);
1620 title: sw.getTitleTextColor(),
1621 body: sw.getBodyTextColor()
1625 return callback(ret);
1629 helpers.ui.applyHeaderSwatch = function(swatches) {
1630 var $header, color, gradient, rgb;
1631 if ((swatches != null) && (swatches.DarkVibrant != null) && (swatches.DarkVibrant.hex != null)) {
1632 if (config.get('static', 'vibrantHeaders') === true) {
1633 color = swatches.DarkVibrant;
1634 $header = $('.details-header');
1635 $header.css('background-color', color.hex);
1636 rgb = color.rgb.join(',');
1637 gradient = 'linear-gradient(to right, ' + color.hex + ' 0%, rgba(' + rgb + ',0.9) 30%, rgba(' + rgb + ',0) 100%)';
1638 return $('.region-details-fanart .gradient', $header).css('background-image', gradient);
1645 Handle urls.
1648 helpers.url = {};
1650 helpers.url.map = {
1651 artist: 'music/artist/:id',
1652 album: 'music/album/:id',
1653 song: 'music/song/:id',
1654 movie: 'movie/:id',
1655 tvshow: 'tvshow/:id',
1656 season: 'tvshow/:id',
1657 episode: 'tvshow/:tvshowid/:season/:id',
1658 channeltv: 'pvr/tv/:id',
1659 channelradio: 'pvr/radio/:id',
1660 file: 'browser/file/:id',
1661 playlist: 'playlist/:id',
1662 musicvideo: 'music/video/:id'
1665 helpers.url.baseKodiUrl = function(query) {
1666 var path;
1667 if (query == null) {
1668 query = 'Kodi';
1670 path = (config.getLocal('jsonRpcEndpoint')) + "?" + query;
1671 if (config.getLocal('reverseProxy')) {
1672 return path;
1673 } else {
1674 return "/" + path;
1678 helpers.url.get = function(type, id, replacements) {
1679 var path, token;
1680 if (id == null) {
1681 id = '';
1683 if (replacements == null) {
1684 replacements = {};
1686 path = '';
1687 if (helpers.url.map[type] != null) {
1688 path = helpers.url.map[type];
1690 replacements[':id'] = id;
1691 for (token in replacements) {
1692 id = replacements[token];
1693 path = path.replace(token, id);
1695 return path;
1698 helpers.url.filterLinks = function(entities, key, items, limit) {
1699 var baseUrl, i, item, links;
1700 if (limit == null) {
1701 limit = 5;
1703 baseUrl = '#' + entities + '?' + key + '=';
1704 links = [];
1705 for (i in items) {
1706 item = items[i];
1707 if (i < limit) {
1708 links.push('<a href="' + baseUrl + encodeURIComponent(item) + '">' + item + '</a>');
1711 return links.join(', ');
1714 helpers.url.playlistUrl = function(item) {
1715 if (item.type === 'song') {
1716 if (item.albumid !== '') {
1717 item.url = helpers.url.get('album', item.albumid);
1718 } else {
1719 item.url('music/albums');
1722 return item.url;
1725 helpers.url.arg = function(arg) {
1726 var args, hash, hashParts;
1727 if (arg == null) {
1728 arg = 'none';
1730 hash = location.hash;
1731 hashParts = hash.split('?');
1732 args = hashParts[0].substring(1).split('/');
1733 if (arg === 'none') {
1734 return args;
1735 } else if (args[arg] != null) {
1736 return args[arg];
1737 } else {
1738 return '';
1742 helpers.url.params = function(params) {
1743 var p, path, query, ref;
1744 if (params == null) {
1745 params = 'auto';
1747 if (params === 'auto') {
1748 p = document.location.href;
1749 if (p.indexOf('?') === -1) {
1750 return {};
1751 } else {
1752 ref = p.split('?'), path = ref[0], query = ref[1];
1755 if (query == null) {
1756 query = params;
1758 return _.object(_.compact(_.map(query.split('&'), function(item) {
1759 if (item) {
1760 return item.split('=');
1762 })));
1765 helpers.url.buildParams = function(params) {
1766 var key, q, val;
1767 q = [];
1768 for (key in params) {
1769 val = params[key];
1770 q.push(key + '=' + encodeURIComponent(val));
1772 return '?' + q.join('&');
1775 helpers.url.alterParams = function(add, remove) {
1776 var curParams, k, len, n, params;
1777 if (add == null) {
1778 add = {};
1780 if (remove == null) {
1781 remove = [];
1783 curParams = helpers.url.params();
1784 if (remove.length > 0) {
1785 for (n = 0, len = remove.length; n < len; n++) {
1786 k = remove[n];
1787 delete curParams[k];
1790 params = _.extend(curParams, add);
1791 return helpers.url.path() + helpers.url.buildParams(params);
1794 helpers.url.path = function() {
1795 var p, path, query, ref;
1796 p = document.location.hash;
1797 ref = p.split('?'), path = ref[0], query = ref[1];
1798 return path.substring(1);
1801 helpers.url.imdbUrl = function(imdbNumber, text) {
1802 var url;
1803 url = "http://www.imdb.com/title/" + imdbNumber + "/";
1804 return "<a href='" + url + "' class='imdblink' target='_blank'>" + (t.gettext(text)) + "</a>";
1807 helpers.url.parseTrailerUrl = function(trailer) {
1808 var ret, urlParts;
1809 ret = {
1810 source: '',
1811 id: '',
1812 img: '',
1813 url: ''
1815 urlParts = helpers.url.params(trailer);
1816 if (trailer.indexOf('youtube') > -1) {
1817 ret.source = 'youtube';
1818 ret.id = urlParts.videoid;
1819 ret.img = "http://img.youtube.com/vi/" + ret.id + "/0.jpg";
1820 ret.url = "https://www.youtube.com/watch?v=" + ret.id;
1822 return ret;
1825 helpers.url.isSecureProtocol = function() {
1826 var secure;
1827 secure = (typeof document !== "undefined" && document !== null) && document.location.protocol === "https:" ? true : false;
1828 return secure;
1831 (function(Backbone) {
1832 var _sync, methods;
1833 _sync = Backbone.sync;
1834 Backbone.sync = function(method, entity, options) {
1835 var sync;
1836 if (options == null) {
1837 options = {};
1839 _.defaults(options, {
1840 beforeSend: _.bind(methods.beforeSend, entity),
1841 complete: _.bind(methods.complete, entity)
1843 sync = _sync(method, entity, options);
1844 if (!entity._fetch && method === "read") {
1845 return entity._fetch = sync;
1848 return methods = {
1849 beforeSend: function() {
1850 return this.trigger("sync:start", this);
1852 complete: function() {
1853 return this.trigger("sync:stop", this);
1856 })(Backbone);
1858 (function(Backbone) {
1859 return _.extend(Backbone.Marionette.Application.prototype, {
1860 navigate: function(route, options) {
1861 if (options == null) {
1862 options = {};
1864 return Backbone.history.navigate(route, options);
1866 getCurrentRoute: function() {
1867 var frag;
1868 frag = Backbone.history.fragment;
1869 if (_.isEmpty(frag)) {
1870 return null;
1871 } else {
1872 return frag;
1875 startHistory: function() {
1876 if (Backbone.history) {
1877 return Backbone.history.start();
1880 register: function(instance, id) {
1881 if (this._registry == null) {
1882 this._registry = {};
1884 return this._registry[id] = instance;
1886 unregister: function(instance, id) {
1887 return delete this._registry[id];
1889 resetRegistry: function() {
1890 var controller, key, msg, oldCount, ref;
1891 oldCount = this.getRegistrySize();
1892 ref = this._registry;
1893 for (key in ref) {
1894 controller = ref[key];
1895 controller.region.close();
1897 msg = "There were " + oldCount + " controllers in the registry, there are now " + (this.getRegistrySize());
1898 if (this.getRegistrySize() > 0) {
1899 return console.warn(msg, this._registry);
1900 } else {
1901 return console.log(msg);
1904 getRegistrySize: function() {
1905 return _.size(this._registry);
1908 })(Backbone);
1910 (function(Marionette) {
1911 return _.extend(Marionette.Renderer, {
1912 extension: [".jst"],
1913 render: function(template, data) {
1914 var path;
1915 path = this.getTemplate(template);
1916 if (!path) {
1917 throw "Template " + template + " not found!";
1919 return path(data);
1921 getTemplate: function(template) {
1922 var path;
1923 path = this.insertAt(template.split("/"), -1, "tpl").join("/");
1924 path = path + this.extension;
1925 if (JST[path]) {
1926 return JST[path];
1929 insertAt: function(array, index, item) {
1930 array.splice(index, 0, item);
1931 return array;
1934 })(Marionette);
1936 this.Kodi.module("Entities", function(Entities, App, Backbone, Marionette, $, _) {
1937 return Entities.Collection = (function(superClass) {
1938 extend(Collection, superClass);
1940 function Collection() {
1941 return Collection.__super__.constructor.apply(this, arguments);
1944 Collection.prototype.getRawCollection = function() {
1945 var len, model, n, objs, ref;
1946 objs = [];
1947 if (this.models.length > 0) {
1948 ref = this.models;
1949 for (n = 0, len = ref.length; n < len; n++) {
1950 model = ref[n];
1951 objs.push(model.attributes);
1954 return objs;
1957 Collection.prototype.getCacheKey = function(options) {
1958 var key;
1959 key = this.constructor.name;
1960 return key;
1963 Collection.prototype.autoSort = function(params) {
1964 var order;
1965 if (params.sort) {
1966 order = params.order ? params.order : 'asc';
1967 return this.sortCollection(params.sort, order);
1971 Collection.prototype.sortCollection = function(property, order) {
1972 if (order == null) {
1973 order = 'asc';
1975 if (property === 'random') {
1976 this.comparator = false;
1977 this.reset(this.shuffle(), {
1978 silent: true
1980 } else {
1981 this.comparator = (function(_this) {
1982 return function(model) {
1983 return _this.ignoreArticleParse(model.get(property));
1985 })(this);
1986 if (order === 'desc') {
1987 this.comparator = this.reverseSortBy(this.comparator);
1989 this.sort();
1993 Collection.prototype.reverseSortBy = function(sortByFunction) {
1994 return function(left, right) {
1995 var l, r;
1996 l = sortByFunction(left);
1997 r = sortByFunction(right);
1998 if (l === void 0) {
1999 return -1;
2001 if (r === void 0) {
2002 return 1;
2004 if (l < r) {
2005 return 1;
2006 } else if (l > r) {
2007 return -1;
2008 } else {
2009 return 0;
2014 Collection.prototype.ignoreArticleParse = function(string) {
2015 var articles, len, n, parsed, s;
2016 articles = ["'", '"', 'the ', 'a '];
2017 if (typeof string === 'string' && config.get('static', 'ignoreArticle', true)) {
2018 string = string.toLowerCase();
2019 parsed = false;
2020 for (n = 0, len = articles.length; n < len; n++) {
2021 s = articles[n];
2022 if (parsed) {
2023 continue;
2025 if (helpers.global.stringStartsWith(s, string)) {
2026 string = helpers.global.stringStripStartsWith(s, string);
2027 parsed = true;
2031 return string;
2034 return Collection;
2036 })(Backbone.Collection);
2039 this.Kodi.module("Entities", function(Entities, App, Backbone, Marionette, $, _) {
2040 return Entities.Filtered = (function(superClass) {
2041 extend(Filtered, superClass);
2043 function Filtered() {
2044 return Filtered.__super__.constructor.apply(this, arguments);
2047 Filtered.prototype.filterByMultiple = function(key, values) {
2048 if (values == null) {
2049 values = [];
2051 return this.filterBy(key, function(model) {
2052 return helpers.global.inArray(model.get(key), values);
2056 Filtered.prototype.filterByMultipleArray = function(key, values) {
2057 if (values == null) {
2058 values = [];
2060 return this.filterBy(key, function(model) {
2061 var len, match, n, ref, v;
2062 match = false;
2063 ref = model.get(key);
2064 for (n = 0, len = ref.length; n < len; n++) {
2065 v = ref[n];
2066 if (helpers.global.inArray(v, values)) {
2067 match = true;
2070 return match;
2074 Filtered.prototype.filterByMultipleObject = function(key, property, values) {
2075 if (values == null) {
2076 values = [];
2078 return this.filterBy(key, function(model) {
2079 var items, len, match, n, v;
2080 match = false;
2081 items = _.pluck(model.get(key), property);
2082 for (n = 0, len = items.length; n < len; n++) {
2083 v = items[n];
2084 if (helpers.global.inArray(v, values)) {
2085 match = true;
2088 return match;
2092 Filtered.prototype.filterByUnwatched = function() {
2093 return this.filterBy('unwatched', function(model) {
2094 var unwatched;
2095 unwatched = 1;
2096 if (model.get('type') === 'tvshow') {
2097 unwatched = model.get('episode') - model.get('watchedepisodes');
2098 } else if (model.get('type') === 'movie' || model.get('type') === 'episode') {
2099 unwatched = model.get('playcount') > 0 ? 0 : 1;
2101 return unwatched > 0;
2105 Filtered.prototype.filterByWatched = function() {
2106 return this.filterBy('watched', function(model) {
2107 var watched;
2108 watched = 1;
2109 if (model.get('type') === 'tvshow') {
2110 watched = model.get('watchedepisodes');
2111 } else if (model.get('type') === 'movie' || model.get('type') === 'episode') {
2112 watched = model.get('playcount') > 0 ? 1 : 0;
2114 return watched > 0;
2118 Filtered.prototype.filterByThumbsUp = function(key) {
2119 return this.filterBy(key, function(model) {
2120 return App.request("thumbsup:check", model);
2124 Filtered.prototype.filterByInProgress = function(key) {
2125 return this.filterBy(key, function(model) {
2126 var inprogress;
2127 inprogress = model.get('progress') > 0 && model.get('progress') < 100 ? true : false;
2128 return inprogress;
2132 Filtered.prototype.filterByString = function(key, query) {
2133 return this.filterBy('search', function(model) {
2134 var value;
2135 if (query.length < 3) {
2136 return false;
2137 } else {
2138 value = model.get(key).toLowerCase();
2139 return value.indexOf(query.toLowerCase()) > -1;
2144 return Filtered;
2146 })(FilteredCollection);
2149 this.Kodi.module("Entities", function(Entities, App, Backbone, Marionette, $, _) {
2150 return Entities.Model = (function(superClass) {
2151 extend(Model, superClass);
2153 function Model() {
2154 this.saveError = bind(this.saveError, this);
2155 this.saveSuccess = bind(this.saveSuccess, this);
2156 return Model.__super__.constructor.apply(this, arguments);
2159 Model.prototype.getCacheKey = function(options) {
2160 var key;
2161 key = this.constructor.name;
2162 return key;
2165 Model.prototype.destroy = function(options) {
2166 if (options == null) {
2167 options = {};
2169 _.defaults(options, {
2170 wait: true
2172 this.set({
2173 _destroy: true
2175 return Model.__super__.destroy.call(this, options);
2178 Model.prototype.isDestroyed = function() {
2179 return this.get("_destroy");
2182 Model.prototype.save = function(data, options) {
2183 var isNew;
2184 if (options == null) {
2185 options = {};
2187 isNew = this.isNew();
2188 _.defaults(options, {
2189 wait: true,
2190 success: _.bind(this.saveSuccess, this, isNew, options.collection),
2191 error: _.bind(this.saveError, this)
2193 this.unset("_errors");
2194 return Model.__super__.save.call(this, data, options);
2197 Model.prototype.saveSuccess = function(isNew, collection) {
2198 if (isNew) {
2199 if (collection) {
2200 collection.add(this);
2202 if (collection) {
2203 collection.trigger("model:created", this);
2205 return this.trigger("created", this);
2206 } else {
2207 if (collection == null) {
2208 collection = this.collection;
2210 if (collection) {
2211 collection.trigger("model:updated", this);
2213 return this.trigger("updated", this);
2217 Model.prototype.saveError = function(model, xhr, options) {
2218 var ref;
2219 if (!(xhr.status === 500 || xhr.status === 404)) {
2220 return this.set({
2221 _errors: (ref = $.parseJSON(xhr.responseText)) != null ? ref.errors : void 0
2226 return Model;
2228 })(Backbone.Model);
2233 App configuration settings, items stored in local storage and are
2234 specific to the browser/user instance. Not Kodi settings.
2237 this.Kodi.module("Entities", function(Entities, App, Backbone, Marionette, $, _) {
2238 var API;
2239 API = {
2240 storageKey: 'config:app',
2241 getCollection: function() {
2242 var collection;
2243 collection = new Entities.ConfigAppCollection();
2244 collection.fetch();
2245 return collection;
2247 getConfig: function(id, collection) {
2248 if (collection == null) {
2249 collection = API.getCollection();
2251 return collection.find({
2252 id: id
2256 Entities.ConfigApp = (function(superClass) {
2257 extend(ConfigApp, superClass);
2259 function ConfigApp() {
2260 return ConfigApp.__super__.constructor.apply(this, arguments);
2263 ConfigApp.prototype.defaults = {
2264 data: {}
2267 return ConfigApp;
2269 })(Entities.Model);
2270 Entities.ConfigAppCollection = (function(superClass) {
2271 extend(ConfigAppCollection, superClass);
2273 function ConfigAppCollection() {
2274 return ConfigAppCollection.__super__.constructor.apply(this, arguments);
2277 ConfigAppCollection.prototype.model = Entities.ConfigApp;
2279 ConfigAppCollection.prototype.localStorage = new Backbone.LocalStorage(API.storageKey);
2281 return ConfigAppCollection;
2283 })(Entities.Collection);
2284 App.reqres.setHandler("config:app:get", function(configId, defaultData) {
2285 var model;
2286 model = API.getConfig(configId);
2287 if (model != null) {
2288 return model.get('data');
2289 } else {
2290 return defaultData;
2293 App.reqres.setHandler("config:app:set", function(configId, configData) {
2294 var collection, model;
2295 collection = API.getCollection();
2296 model = API.getConfig(configId, collection);
2297 if (model != null) {
2298 return model.save({
2299 data: configData
2301 } else {
2302 collection.create({
2303 id: configId,
2304 data: configData
2306 return configData;
2309 App.reqres.setHandler("config:static:get", function(configId, defaultData) {
2310 var data;
2311 data = config["static"][configId] != null ? config["static"][configId] : defaultData;
2312 return data;
2314 return App.reqres.setHandler("config:static:set", function(configId, data) {
2315 config["static"][configId] = data;
2316 return data;
2320 this.Kodi.module("Entities", function(Entities, App, Backbone, Marionette, $, _) {
2321 Entities.ExternalEntity = (function(superClass) {
2322 extend(ExternalEntity, superClass);
2324 function ExternalEntity() {
2325 return ExternalEntity.__super__.constructor.apply(this, arguments);
2328 ExternalEntity.prototype.defaults = {
2329 id: '',
2330 title: '',
2331 desc: '',
2332 thumbnail: '',
2333 url: '',
2334 type: '',
2335 provider: ''
2338 return ExternalEntity;
2340 })(Entities.Model);
2341 return Entities.ExternalCollection = (function(superClass) {
2342 extend(ExternalCollection, superClass);
2344 function ExternalCollection() {
2345 return ExternalCollection.__super__.constructor.apply(this, arguments);
2348 ExternalCollection.prototype.model = Entities.ExternalEntity;
2350 return ExternalCollection;
2352 })(Entities.Collection);
2355 this.Kodi.module("Entities", function(Entities, App, Backbone, Marionette, $, _) {
2356 var API;
2357 API = {
2358 apiKey: 'ZWQ0Yjc4NGY5NzIyNzM1OGIzMWNhNGRkOTY2YTA0ZjE=',
2359 baseURL: 'http://webservice.fanart.tv/v3/',
2360 maxImageCount: 15,
2361 artistFieldTranslate: {
2362 artistbackground: 'fanart',
2363 artistthumb: 'thumbnail'
2365 call: function(path, params, callback) {
2366 var defaultParams, req, url;
2367 defaultParams = {
2368 api_key: config.getAPIKey('apiKeyFanartTv', this.apiKey)
2370 params = _.extend(defaultParams, params);
2371 url = this.baseURL + path + helpers.url.buildParams(params);
2372 req = $.getJSON(url, function(resp) {
2373 return callback(resp);
2375 return req.fail(function(err) {
2376 return callback({
2377 status: 'error'
2381 parseImageUrls: function(artType, collection) {
2382 var artTypes, field, i, item, items, ref, row, type;
2383 if (collection.status && collection.status === 'error') {
2384 return [];
2386 items = [];
2387 artTypes = this[artType + 'FieldTranslate'];
2388 for (type in artTypes) {
2389 field = artTypes[type];
2390 if (collection[type] != null) {
2391 collection[type] = collection[type].slice(0, this.maxImageCount);
2392 ref = collection[type];
2393 for (i in ref) {
2394 item = ref[i];
2395 row = item;
2396 row.thumbnail = this.getThumbnailUrl(item.url);
2397 row.provider = 'fanarttv';
2398 row.type = field;
2399 items.push(row);
2403 return items;
2405 getThumbnailUrl: (function(_this) {
2406 return function(url) {
2407 return url.replace('assets.fanart.tv/', 'fanart.tv/detailpreview/');
2409 })(this),
2410 images: function(type, id, callback) {
2411 return this.call(type + '/' + id, {}, (function(_this) {
2412 return function(resp) {
2413 return callback(_this.parseImageUrls('artist', resp));
2415 })(this));
2417 getMusicArtCollection: function(name, callback) {
2418 var cache, cacheKey;
2419 cacheKey = 'fanarttv:' + encodeURIComponent(name);
2420 cache = config.getLocal(cacheKey, []);
2421 if (cache.length > 0) {
2422 return API.createCollection(cache, callback);
2423 } else {
2424 return App.execute("musicbrainz:artist:entity", name, function(model) {
2425 if (model.get('id')) {
2426 return API.images('music', model.get('id'), function(results) {
2427 return API.createCollection(results, callback);
2429 } else {
2430 return API.createCollection([], callback);
2435 createCollection: function(items, callback) {
2436 return callback(new Entities.ExternalCollection(items));
2439 return App.commands.setHandler("fanarttv:artist:image:entities", function(name, callback) {
2440 return API.getMusicArtCollection(name, callback);
2444 this.Kodi.module("Entities", function(Entities, App, Backbone, Marionette, $, _) {
2445 var API;
2446 API = {
2447 baseURL: 'http://musicbrainz.org/ws/2/',
2448 maxCount: 1,
2449 call: function(path, params, callback) {
2450 var url;
2451 url = this.baseURL + path + helpers.url.buildParams(params) + '&fmt=json';
2452 return $.getJSON(url, function(resp) {
2453 return callback(resp);
2456 findArtist: function(key, id, callback) {
2457 return this.call('artist/', {
2458 query: key + ':' + id
2459 }, function(resp) {
2460 var collection;
2461 collection = new Entities.ExternalCollection(API.parseArtist(resp));
2462 return callback(collection);
2465 parseArtist: function(resp) {
2466 var items;
2467 items = [];
2468 if (resp.artists && resp.artists.length) {
2469 items = resp.artists;
2470 items = _.map(items, function(item) {
2471 item.artistType = item.type;
2472 item.title = item.name;
2473 item.type = 'artist';
2474 item.provider = 'musicbrainz';
2475 return item;
2478 return items;
2481 return App.commands.setHandler("musicbrainz:artist:entity", function(name, callback) {
2482 return API.findArtist('artist', name, function(collection) {
2483 return callback(collection.first());
2488 this.Kodi.module("Entities", function(Entities, App, Backbone, Marionette, $, _) {
2489 var API;
2490 API = {
2491 apiKey: 'NzFiYTFmMDdlZDBmYzhmYjM2MWNmMDRhNThkNzUwNTE=',
2492 baseURL: 'https://api.themoviedb.org/3/',
2493 baseImageURL: 'https://image.tmdb.org/t/p/',
2494 defaultLang: 'en',
2495 maxImageCount: 15,
2496 thumbSize: {
2497 backdrops: 'w300',
2498 posters: 'w185'
2500 fieldTranslate: {
2501 backdrops: 'fanart',
2502 posters: 'thumbnail'
2504 call: function(path, params, callback) {
2505 var defaultParams, url;
2506 defaultParams = {
2507 api_key: config.getAPIKey('apiKeyTMDB', this.apiKey)
2509 params = _.extend(defaultParams, params);
2510 url = this.baseURL + path + helpers.url.buildParams(params) + '&callback=?';
2511 return $.getJSON(url, function(resp) {
2512 return callback(resp);
2515 getImageURL: function(path, size) {
2516 if (size == null) {
2517 size = 'original';
2519 return this.baseImageURL + size + path;
2521 parseImages: function(collection) {
2522 var field, i, item, items, ref, ref1, type;
2523 items = [];
2524 ref = API.fieldTranslate;
2525 for (type in ref) {
2526 field = ref[type];
2527 collection[type] = collection[type].slice(0, this.maxImageCount);
2528 ref1 = collection[type];
2529 for (i in ref1) {
2530 item = ref1[i];
2531 item.id = item.file_path;
2532 item.url = this.getImageURL(item.file_path, 'original');
2533 item.thumbnail = this.getImageURL(item.file_path, this.thumbSize[type]);
2534 item.type = field;
2535 item.provider = 'moviedb';
2536 items.push(item);
2539 return items;
2541 find: function(id, source, callback) {
2542 if (source == null) {
2543 source = 'imdb_id';
2545 return this.call('find/' + id, {
2546 external_source: source
2547 }, callback);
2549 images: function(type, tmdbId, callback) {
2550 return this.call(type + '/' + tmdbId + '/images', {
2551 include_image_language: this.defaultLang + ',null'
2552 }, (function(_this) {
2553 return function(resp) {
2554 return callback(_this.parseImages(resp));
2556 })(this));
2558 getCollection: function(options, callback) {
2559 var cache, cacheKey, opts;
2560 opts = _.extend({
2561 lookupType: 'imdb_id',
2562 lookupId: '',
2563 type: 'movie'
2564 }, options);
2565 cacheKey = 'moviedb:' + JSON.stringify(opts);
2566 cache = config.getLocal(cacheKey, []);
2567 if (cache.length > 0) {
2568 return API.createCollection(cache, callback);
2569 } else {
2570 return API.find(opts.lookupId, opts.lookupType, function(resp) {
2571 var item, resKey;
2572 resKey = opts.type + '_results';
2573 if (resp[resKey] && resp[resKey].length > 0) {
2574 item = _.first(resp[resKey]);
2575 return API.images(opts.type, item.id, function(resp) {
2576 config.setLocal(cacheKey, resp);
2577 return API.createCollection(resp, callback);
2579 } else {
2580 return API.createCollection([], callback);
2585 createCollection: function(items, callback) {
2586 return callback(new Entities.ExternalCollection(items));
2589 App.commands.setHandler("themoviedb:movie:image:entities", function(lookupId, callback) {
2590 var options;
2591 options = {
2592 lookupId: lookupId
2594 return API.getCollection(options, callback);
2596 return App.commands.setHandler("themoviedb:tv:image:entities", function(lookupId, callback) {
2597 var lookupType, options;
2598 lookupType = lookupId.lastIndexOf('tt', 0) === 0 ? 'imdb_id' : 'tvdb_id';
2599 options = {
2600 lookupId: lookupId,
2601 lookupType: lookupType,
2602 type: 'tv'
2604 return API.getCollection(options, callback);
2610 Youtube collection
2613 this.Kodi.module("Entities", function(Entities, App, Backbone, Marionette, $, _) {
2614 var API;
2615 API = {
2616 apiKey: 'QUl6YVN5Qnh2YVI2bUNuVVdOOGN2MlRpUFJtdUVoMEZ5a0JUQUgw',
2617 searchUrl: 'https://www.googleapis.com/youtube/v3/search?part=snippet&type=video&videoDefinition=any&videoEmbeddable=true&order=relevance&safeSearch=none',
2618 maxResults: 5,
2619 kodiUrl: 'plugin://plugin.video.youtube/?action=play_video&videoid=',
2620 ytURL: 'https://youtu.be/',
2621 getSearchUrl: function() {
2622 return this.searchUrl + '&key=' + config.getAPIKey('apiKeyYouTube', this.apiKey);
2624 parseItems: function(response) {
2625 var enabled, i, item, items, ref, resp;
2626 items = [];
2627 enabled = App.request("addon:isEnabled", {
2628 addonid: 'plugin.video.youtube'
2629 }) ? true : false;
2630 ref = response.items;
2631 for (i in ref) {
2632 item = ref[i];
2633 resp = {
2634 id: item.id.videoId,
2635 title: item.snippet.title,
2636 label: item.snippet.title,
2637 desc: item.snippet.description,
2638 thumbnail: item.snippet.thumbnails.medium.url,
2639 url: API.ytURL + item.id.videoId,
2640 addonEnabled: enabled
2642 items.push(resp);
2644 return items;
2647 Entities.YouTubeCollection = (function(superClass) {
2648 extend(YouTubeCollection, superClass);
2650 function YouTubeCollection() {
2651 return YouTubeCollection.__super__.constructor.apply(this, arguments);
2654 YouTubeCollection.prototype.model = Entities.ExternalEntity;
2656 YouTubeCollection.prototype.url = API.getSearchUrl();
2658 YouTubeCollection.prototype.sync = function(method, collection, options) {
2659 options.dataType = "jsonp";
2660 options.timeout = 5000;
2661 return Backbone.sync(method, collection, options);
2664 YouTubeCollection.prototype.parse = function(resp) {
2665 return API.parseItems(resp);
2668 return YouTubeCollection;
2670 })(Entities.ExternalCollection);
2671 App.commands.setHandler("youtube:search:entities", function(query, options, callback) {
2672 var data, yt;
2673 if (options == null) {
2674 options = {};
2676 yt = new Entities.YouTubeCollection();
2677 data = _.extend({
2678 q: query,
2679 maxResults: API.maxResults
2680 }, options);
2681 return yt.fetch({
2682 data: data,
2683 success: function(collection) {
2684 return callback(collection);
2686 error: function(collection) {
2687 return helpers.debug.log('Youtube search error', 'error', collection);
2691 return App.commands.setHandler("youtube:trailer:entities", function(title, callback) {
2692 return App.execute("youtube:search:entities", title + ' trailer', {}, function(collection) {
2693 collection.map(function(item) {
2694 item.set({
2695 type: 'trailer',
2696 url: API.kodiUrl + item.id
2698 return item;
2700 return callback(collection);
2705 this.Kodi.module("Entities", function(Entities, App, Backbone, Marionette, $, _) {
2706 Entities.Filter = (function(superClass) {
2707 extend(Filter, superClass);
2709 function Filter() {
2710 return Filter.__super__.constructor.apply(this, arguments);
2713 Filter.prototype.defaults = {
2714 alias: '',
2715 type: 'string',
2716 key: '',
2717 sortOrder: 'asc',
2718 title: '',
2719 active: false
2722 return Filter;
2724 })(Entities.Model);
2725 Entities.FilterCollection = (function(superClass) {
2726 extend(FilterCollection, superClass);
2728 function FilterCollection() {
2729 return FilterCollection.__super__.constructor.apply(this, arguments);
2732 FilterCollection.prototype.model = Entities.Filter;
2734 return FilterCollection;
2736 })(Entities.Collection);
2737 Entities.FilterOption = (function(superClass) {
2738 extend(FilterOption, superClass);
2740 function FilterOption() {
2741 return FilterOption.__super__.constructor.apply(this, arguments);
2744 FilterOption.prototype.defaults = {
2745 key: '',
2746 value: '',
2747 title: ''
2750 return FilterOption;
2752 })(Entities.Model);
2753 Entities.FilterOptionCollection = (function(superClass) {
2754 extend(FilterOptionCollection, superClass);
2756 function FilterOptionCollection() {
2757 return FilterOptionCollection.__super__.constructor.apply(this, arguments);
2760 FilterOptionCollection.prototype.model = Entities.Filter;
2762 return FilterOptionCollection;
2764 })(Entities.Collection);
2765 Entities.FilterSort = (function(superClass) {
2766 extend(FilterSort, superClass);
2768 function FilterSort() {
2769 return FilterSort.__super__.constructor.apply(this, arguments);
2772 FilterSort.prototype.defaults = {
2773 alias: '',
2774 type: 'string',
2775 defaultSort: false,
2776 defaultOrder: 'asc',
2777 key: '',
2778 active: false,
2779 order: 'asc',
2780 title: ''
2783 return FilterSort;
2785 })(Entities.Model);
2786 Entities.FilterSortCollection = (function(superClass) {
2787 extend(FilterSortCollection, superClass);
2789 function FilterSortCollection() {
2790 return FilterSortCollection.__super__.constructor.apply(this, arguments);
2793 FilterSortCollection.prototype.model = Entities.FilterSort;
2795 return FilterSortCollection;
2797 })(Entities.Collection);
2798 Entities.FilterActive = (function(superClass) {
2799 extend(FilterActive, superClass);
2801 function FilterActive() {
2802 return FilterActive.__super__.constructor.apply(this, arguments);
2805 FilterActive.prototype.defaults = {
2806 key: '',
2807 values: [],
2808 title: ''
2811 return FilterActive;
2813 })(Entities.Model);
2814 Entities.FilterActiveCollection = (function(superClass) {
2815 extend(FilterActiveCollection, superClass);
2817 function FilterActiveCollection() {
2818 return FilterActiveCollection.__super__.constructor.apply(this, arguments);
2821 FilterActiveCollection.prototype.model = Entities.FilterActive;
2823 return FilterActiveCollection;
2825 })(Entities.Collection);
2826 App.reqres.setHandler('filter:filters:entities', function(collection) {
2827 return new Entities.FilterCollection(collection);
2829 App.reqres.setHandler('filter:filters:options:entities', function(collection) {
2830 return new Entities.FilterOptionCollection(collection);
2832 App.reqres.setHandler('filter:sort:entities', function(collection) {
2833 return new Entities.FilterSortCollection(collection);
2835 return App.reqres.setHandler('filter:active:entities', function(collection) {
2836 return new Entities.FilterActiveCollection(collection);
2840 this.Kodi.module("Entities", function(Entities, App, Backbone, Marionette, $, _) {
2841 var API;
2842 Entities.FormItem = (function(superClass) {
2843 extend(FormItem, superClass);
2845 function FormItem() {
2846 return FormItem.__super__.constructor.apply(this, arguments);
2849 FormItem.prototype.defaults = {
2850 id: 0,
2851 title: '',
2852 type: '',
2853 element: '',
2854 options: [],
2855 defaultValue: '',
2856 description: '',
2857 children: [],
2858 attributes: {},
2859 "class": '',
2860 suffix: '',
2861 prefix: '',
2862 formState: {}
2865 return FormItem;
2867 })(Entities.Model);
2868 Entities.Form = (function(superClass) {
2869 extend(Form, superClass);
2871 function Form() {
2872 return Form.__super__.constructor.apply(this, arguments);
2875 Form.prototype.model = Entities.FormItem;
2877 return Form;
2879 })(Entities.Collection);
2880 API = {
2881 applyState: function(item, formState) {
2882 var property;
2883 item.formState = formState;
2884 item.defaultValue = item.defaultValue ? item.defaultValue : '';
2885 property = item.valueProperty ? item.valueProperty : item.id;
2886 if (formState[property] != null) {
2887 item.defaultValue = this.formatDefaultValue(item.format, formState[property]);
2888 item.defaultsApplied = true;
2890 return item;
2892 formatDefaultValue: function(format, value) {
2893 if (format === 'array.string' || format === 'array.integer') {
2894 return value.join('; ');
2895 } else if (format === 'integer' && value !== '') {
2896 return parseInt(value);
2897 } else {
2898 return value;
2901 formatSubmittedValues: function(item, values) {
2902 if (item.format && (values[item.id] != null)) {
2903 if (item.format === 'array.string') {
2904 values[item.id] = values[item.id] !== '' ? _.map(values[item.id].split(';'), function(v) {
2905 return v.trim();
2906 }) : [];
2907 } else if (item.format === 'array.integer') {
2908 values[item.id] = values[item.id] !== '' ? _.map(values[item.id].split(';'), function(v) {
2909 return parseInt(v.trim());
2910 }) : [];
2911 } else if (item.format === 'integer') {
2912 values[item.id] = parseInt(values[item.id]);
2913 } else if (item.format === 'float') {
2914 values[item.id] = parseFloat(values[item.id]);
2915 } else if (item.format === 'prevent.submit') {
2916 delete values[item.id];
2919 return values;
2921 processItems: function(items, formState, isChild) {
2922 var collection, item, len, n;
2923 if (formState == null) {
2924 formState = {};
2926 if (isChild == null) {
2927 isChild = false;
2929 collection = [];
2930 for (n = 0, len = items.length; n < len; n++) {
2931 item = items[n];
2932 item = this.applyState(item, formState);
2933 if (item.children && item.children.length > 0) {
2934 item.children = API.processItems(item.children, formState, true);
2936 collection.push(item);
2938 return collection;
2940 processSubmitted: function(items, formState, isChild) {
2941 var item, len, n;
2942 if (formState == null) {
2943 formState = {};
2945 if (isChild == null) {
2946 isChild = false;
2948 for (n = 0, len = items.length; n < len; n++) {
2949 item = items[n];
2950 formState = this.formatSubmittedValues(item, formState);
2951 if (item.children && item.children.length > 0) {
2952 formState = API.processSubmitted(item.children.toJSON(), formState, true);
2955 return formState;
2957 toCollection: function(items) {
2958 var childCollection, i, item;
2959 for (i in items) {
2960 item = items[i];
2961 if (item.children && item.children.length > 0) {
2962 childCollection = new Entities.Form(item.children);
2963 items[i].children = childCollection;
2966 return new Entities.Form(items);
2969 App.reqres.setHandler("form:item:entities", function(form, formState) {
2970 if (form == null) {
2971 form = [];
2973 if (formState == null) {
2974 formState = {};
2976 return API.toCollection(API.processItems(form, formState));
2978 return App.reqres.setHandler("form:value:entities", function(form, formState) {
2979 if (form == null) {
2980 form = [];
2982 if (formState == null) {
2983 formState = {};
2985 return API.processSubmitted(form, formState);
2989 this.Kodi.module("KodiEntities", function(KodiEntities, App, Backbone, Marionette, $, _) {
2990 var API;
2991 API = {
2992 cacheSynced: function(entities, callback) {
2993 return entities.on('cachesync', function() {
2994 callback();
2995 return helpers.global.loading("end");
2998 xhrsFetch: function(entities, callback) {
2999 var xhrs;
3000 xhrs = _.chain([entities]).flatten().pluck("_fetch").value();
3001 return $.when.apply($, xhrs).done(function() {
3002 callback();
3003 return helpers.global.loading("end");
3007 return App.commands.setHandler("when:entity:fetched", function(entities, callback) {
3008 helpers.global.loading("start");
3009 if (!entities.params) {
3010 return API.cacheSynced(entities, callback);
3011 } else {
3012 return API.xhrsFetch(entities, callback);
3017 this.Kodi.module("KodiEntities", function(KodiEntities, App, Backbone, Marionette, $, _) {
3018 Backbone.fetchCache.localStorage = false;
3019 return KodiEntities.Collection = (function(superClass) {
3020 extend(Collection, superClass);
3022 function Collection() {
3023 return Collection.__super__.constructor.apply(this, arguments);
3026 Collection.prototype.url = function() {
3027 return helpers.url.baseKodiUrl(this.constructor.name);
3030 Collection.prototype.rpc = new Backbone.Rpc({
3031 namespaceDelimiter: ''
3034 Collection.prototype.sync = function(method, model, options) {
3035 if (method === 'read') {
3036 this.options = options;
3038 return Backbone.sync(method, model, options);
3041 Collection.prototype.getCacheKey = function(options) {
3042 var k, key, len, n, prop, ref, ref1, val;
3043 this.options = options;
3044 key = this.constructor.name;
3045 ref = ['filter', 'sort', 'limit', 'file'];
3046 for (n = 0, len = ref.length; n < len; n++) {
3047 k = ref[n];
3048 if (options[k]) {
3049 ref1 = options[k];
3050 for (prop in ref1) {
3051 val = ref1[prop];
3052 key += ':' + prop + ':' + val;
3056 return key;
3059 Collection.prototype.getResult = function(response, key) {
3060 var result;
3061 this.responseKey = key;
3062 result = response.jsonrpc && response.result ? response.result : response;
3063 return result[key];
3066 Collection.prototype.argCheckOption = function(option, fallback) {
3067 if ((this.options != null) && (this.options[option] != null)) {
3068 return this.options[option];
3069 } else {
3070 return fallback;
3074 Collection.prototype.argSort = function(method, order) {
3075 var arg;
3076 if (order == null) {
3077 order = 'ascending';
3079 arg = {
3080 method: method,
3081 order: order,
3082 ignorearticle: this.isIgnoreArticle()
3084 return this.argCheckOption('sort', arg);
3087 Collection.prototype.argLimit = function(start, end) {
3088 var arg;
3089 if (start == null) {
3090 start = 0;
3092 if (end == null) {
3093 end = 'all';
3095 arg = {
3096 start: start
3098 if (end !== 'all') {
3099 arg.end = end;
3101 return this.argCheckOption('limit', arg);
3104 Collection.prototype.argFilter = function(name, value) {
3105 var arg;
3106 arg = {};
3107 if (name != null) {
3108 arg[name] = value;
3109 } else {
3110 arg = void 0;
3112 return this.argCheckOption('filter', arg);
3115 Collection.prototype.argFields = function(fields) {
3116 var field, len, n, ref;
3117 if ((this.options != null) && (this.options.fields != null)) {
3118 fields = this.options.fields;
3120 if ((this.options != null) && (this.options.addFields != null)) {
3121 ref = this.options.addFields;
3122 for (n = 0, len = ref.length; n < len; n++) {
3123 field = ref[n];
3124 if (!helpers.global.inArray(field, fields)) {
3125 fields.push(field);
3129 return fields;
3132 Collection.prototype.isIgnoreArticle = function() {
3133 return config.getLocal('ignoreArticle', true);
3136 Collection.prototype.getArgs = function(defaults) {
3137 var args;
3138 args = this.options != null ? _.extend(defaults, this.options) : defaults;
3139 return args;
3142 return Collection;
3144 })(App.Entities.Collection);
3147 this.Kodi.module("KodiEntities", function(KodiEntities, App, Backbone, Marionette, $, _) {
3148 return KodiEntities.Model = (function(superClass) {
3149 extend(Model, superClass);
3151 function Model() {
3152 return Model.__super__.constructor.apply(this, arguments);
3155 Model.prototype.initialize = function() {
3156 if (this.methods) {
3157 return App.vent.on('entity:kodi:update', (function(_this) {
3158 return function(uid) {
3159 var fields;
3160 if (_this.get('uid') === uid) {
3161 fields = App.request(_this.get('type') + ":fields");
3162 if (fields && fields.length > 0) {
3163 return _this.fetch({
3164 properties: fields,
3165 success: function(updatedModel) {
3166 return Backbone.fetchCache.clearItem(updatedModel);
3172 })(this));
3176 Model.prototype.url = function() {
3177 return helpers.url.baseKodiUrl(this.constructor.name);
3180 Model.prototype.rpc = new Backbone.Rpc({
3181 useNamedParameters: true,
3182 namespaceDelimiter: ''
3185 Model.prototype.modelDefaults = {
3186 fullyloaded: false,
3187 thumbnail: '',
3188 thumbsUp: 0,
3189 parsed: false,
3190 progress: 0
3193 Model.prototype.parseModel = function(type, model, id) {
3194 if (!model.parsed) {
3195 if (id !== 'mixed') {
3196 model.id = id;
3198 if (model.rating) {
3199 model.rating = helpers.global.rating(model.rating);
3201 if (model.streamdetails && _.isObject(model.streamdetails)) {
3202 model.streamdetails = helpers.stream.streamFormat(model.streamdetails);
3204 if (model.resume) {
3205 model.progress = model.resume.position === 0 ? 0 : Math.round((model.resume.position / model.resume.total) * 100);
3207 if (model.trailer) {
3208 model.mediaTrailer = helpers.url.parseTrailerUrl(model.trailer);
3210 if (model.starttime) {
3211 model.start = helpers.global.dateStringToObj(model.starttime);
3213 if (model.endtime) {
3214 model.end = helpers.global.dateStringToObj(model.endtime);
3216 if (type === 'movie' || type === 'tvshow' || type === 'season') {
3217 model.fanart = 'fanart' in model.art ? model.art.fanart : void 0;
3218 model.thumbnail = 'poster' in model.art ? model.art.poster : 'thumb' in model.art ? model.art.thumb : void 0;
3220 if (type === 'tvshow' || type === 'season') {
3221 model.progress = helpers.global.round((model.watchedepisodes / model.episode) * 100, 2);
3223 if (type === 'episode' || type === 'movie' && model.progress === 0) {
3224 model.progress = model.playcount === 0 ? 0 : 100;
3226 if (type === 'album' || type === 'artist') {
3227 model.progress = 0;
3229 if (type === 'episode') {
3230 model.url = helpers.url.get(type, id, {
3231 ':tvshowid': model.tvshowid,
3232 ':season': model.season
3234 } else if (type === 'channel') {
3235 if (model.channeltype === 'tv') {
3236 type = "channeltv";
3237 } else {
3238 type = "channelradio";
3240 model.url = helpers.url.get(type, id);
3241 } else {
3242 model.url = helpers.url.get(type, id);
3244 model = App.request("images:path:entity", model);
3245 model.type = type;
3246 model.uid = helpers.entities.createUid(model, type);
3247 model.parsed = true;
3249 return model;
3252 Model.prototype.parseFieldsToDefaults = function(fields, defaults) {
3253 var field, len, n;
3254 if (defaults == null) {
3255 defaults = {};
3257 for (n = 0, len = fields.length; n < len; n++) {
3258 field = fields[n];
3259 defaults[field] = '';
3261 return defaults;
3264 Model.prototype.checkResponse = function(response, checkKey) {
3265 var obj;
3266 obj = response[checkKey] != null ? response[checkKey] : response;
3267 if (response[checkKey] != null) {
3268 obj.fullyloaded = true;
3270 return obj;
3273 return Model;
3275 })(App.Entities.Model);
3278 this.Kodi.module("KodiEntities", function(KodiEntities, App, Backbone, Marionette, $, _) {
3281 API Helpers
3283 var API;
3284 API = {
3285 availableProviders: ['video', 'audio', 'executable'],
3286 fields: {
3287 minimal: ['addonid', 'name', 'type', 'thumbnail', 'label'],
3288 small: ['author', 'broken', 'description', 'version', 'enabled', 'extrainfo', 'summary'],
3289 full: ['fanart', 'path']
3291 getCollection: function(type, callback) {
3292 var addonController;
3293 addonController = App.request("command:kodi:controller", 'auto', 'AddOn');
3294 return addonController.getEnabledAddons(true, function(addons) {
3295 var collection;
3296 collection = new KodiEntities.AddonCollection(API.parseAddons(addons, type));
3297 return callback(collection);
3300 parseAddons: function(addons, type) {
3301 var addon, extra, i, len, n, ref, ret;
3302 ret = [];
3303 for (i in addons) {
3304 addon = addons[i];
3305 addon.provides = [];
3306 addon.label = addon.name;
3307 addon = App.request("images:path:entity", addon);
3308 ref = addon.extrainfo;
3309 for (n = 0, len = ref.length; n < len; n++) {
3310 extra = ref[n];
3311 if (_.isObject(extra) && extra.key === 'provides' && extra.value && helpers.global.inArray(extra.value, API.availableProviders)) {
3312 addon.provides.push(extra.value);
3315 if (addon.provides.length > 0 && (helpers.global.inArray(type, addon.provides) || type === 'all')) {
3316 addon.providesDefault = _.first(addon.provides);
3317 addon.subtitle = tr(addon.providesDefault);
3318 ret.push(API.parsePath(addon));
3321 return ret;
3323 parsePath: function(addon) {
3324 var media;
3325 if (helpers.global.inArray('executable', addon.provides)) {
3326 addon.url = 'addon/execute/' + addon.addonid;
3327 } else {
3328 media = addon.providesDefault.replace('audio', 'music');
3329 addon.url = 'browser/' + media + '/' + encodeURIComponent('plugin://' + addon.addonid + '/');
3331 return addon;
3336 Models and collections.
3338 KodiEntities.Addon = (function(superClass) {
3339 extend(Addon, superClass);
3341 function Addon() {
3342 return Addon.__super__.constructor.apply(this, arguments);
3345 Addon.prototype.idAttribute = "addonid";
3347 Addon.prototype.defaults = function() {
3348 return this.parseFieldsToDefaults(helpers.entities.getFields(API.fields, 'full'), {});
3351 return Addon;
3353 })(App.KodiEntities.Model);
3354 KodiEntities.AddonCollection = (function(superClass) {
3355 extend(AddonCollection, superClass);
3357 function AddonCollection() {
3358 return AddonCollection.__super__.constructor.apply(this, arguments);
3361 AddonCollection.prototype.model = KodiEntities.Addon;
3363 return AddonCollection;
3365 })(App.KodiEntities.Collection);
3368 Request Handlers.
3370 return App.reqres.setHandler("addon:entities", function(type, callback) {
3371 if (type == null) {
3372 type = 'all';
3374 return API.getCollection(type, callback);
3378 this.Kodi.module("KodiEntities", function(KodiEntities, App, Backbone, Marionette, $, _) {
3379 var API;
3380 API = {
3381 fields: {
3382 minimal: ['thumbnail'],
3383 small: ['playcount', 'artistid', 'artist', 'genre', 'albumlabel', 'year', 'dateadded', 'style'],
3384 full: ['fanart', 'mood', 'description', 'rating', 'type', 'theme']
3386 getAlbum: function(id, options) {
3387 var album;
3388 album = new App.KodiEntities.Album();
3389 album.set({
3390 albumid: parseInt(id),
3391 properties: helpers.entities.getFields(API.fields, 'full')
3393 album.fetch(options);
3394 return album;
3396 getAlbums: function(options) {
3397 var collection;
3398 collection = new KodiEntities.AlbumCollection();
3399 collection.fetch(helpers.entities.buildOptions(options));
3400 return collection;
3405 Models and collections.
3407 KodiEntities.Album = (function(superClass) {
3408 extend(Album, superClass);
3410 function Album() {
3411 return Album.__super__.constructor.apply(this, arguments);
3414 Album.prototype.defaults = function() {
3415 var fields;
3416 fields = _.extend(this.modelDefaults, {
3417 albumid: 1,
3418 album: ''
3420 return this.parseFieldsToDefaults(helpers.entities.getFields(API.fields, 'full'), fields);
3423 Album.prototype.methods = {
3424 read: ['AudioLibrary.GetAlbumDetails', 'albumid', 'properties']
3427 Album.prototype.parse = function(resp, xhr) {
3428 var obj;
3429 obj = resp.albumdetails != null ? resp.albumdetails : resp;
3430 obj.title = obj.label;
3431 if (resp.albumdetails != null) {
3432 obj.fullyloaded = true;
3434 return this.parseModel('album', obj, obj.albumid);
3437 return Album;
3439 })(App.KodiEntities.Model);
3440 KodiEntities.AlbumCollection = (function(superClass) {
3441 extend(AlbumCollection, superClass);
3443 function AlbumCollection() {
3444 return AlbumCollection.__super__.constructor.apply(this, arguments);
3447 AlbumCollection.prototype.model = KodiEntities.Album;
3449 AlbumCollection.prototype.methods = {
3450 read: ['AudioLibrary.GetAlbums', 'properties', 'limits', 'sort', 'filter']
3453 AlbumCollection.prototype.args = function() {
3454 return this.getArgs({
3455 properties: this.argFields(helpers.entities.getFields(API.fields, 'small')),
3456 limits: this.argLimit(),
3457 sort: this.argSort('title', 'ascending'),
3458 filter: this.argFilter()
3462 AlbumCollection.prototype.parse = function(resp, xhr) {
3463 return this.getResult(resp, 'albums');
3466 return AlbumCollection;
3468 })(App.KodiEntities.Collection);
3471 Request Handlers.
3473 App.reqres.setHandler("album:entity", function(id, options) {
3474 if (options == null) {
3475 options = {};
3477 return API.getAlbum(id, options);
3479 App.reqres.setHandler("album:entities", function(options) {
3480 if (options == null) {
3481 options = {};
3483 return API.getAlbums(options);
3485 return App.reqres.setHandler("album:fields", function(type) {
3486 if (type == null) {
3487 type = 'full';
3489 return helpers.entities.getFields(API.fields, type);
3493 this.Kodi.module("KodiEntities", function(KodiEntities, App, Backbone, Marionette, $, _) {
3494 var API;
3495 API = {
3496 fields: {
3497 minimal: [],
3498 small: ['thumbnail', 'mood', 'genre', 'style'],
3499 full: ['fanart', 'born', 'formed', 'description', 'died', 'disbanded', 'yearsactive', 'instrument', 'musicbrainzartistid']
3501 getArtist: function(id, options) {
3502 var artist;
3503 artist = new App.KodiEntities.Artist();
3504 artist.set({
3505 artistid: parseInt(id),
3506 properties: helpers.entities.getFields(API.fields, 'full')
3508 artist.fetch(options);
3509 return artist;
3511 getArtists: function(options) {
3512 var collection;
3513 collection = new KodiEntities.ArtistCollection();
3514 collection.fetch(helpers.entities.buildOptions(options));
3515 return collection;
3520 Models and collections.
3522 KodiEntities.Artist = (function(superClass) {
3523 extend(Artist, superClass);
3525 function Artist() {
3526 return Artist.__super__.constructor.apply(this, arguments);
3529 Artist.prototype.defaults = function() {
3530 var fields;
3531 fields = _.extend(this.modelDefaults, {
3532 artistid: 1,
3533 artist: ''
3535 return this.parseFieldsToDefaults(helpers.entities.getFields(API.fields, 'full'), fields);
3538 Artist.prototype.methods = {
3539 read: ['AudioLibrary.GetArtistDetails', 'artistid', 'properties']
3542 Artist.prototype.parse = function(resp, xhr) {
3543 var obj;
3544 obj = resp.artistdetails != null ? resp.artistdetails : resp;
3545 if (resp.artistdetails != null) {
3546 obj.fullyloaded = true;
3548 return this.parseModel('artist', obj, obj.artistid);
3551 return Artist;
3553 })(App.KodiEntities.Model);
3554 KodiEntities.ArtistCollection = (function(superClass) {
3555 extend(ArtistCollection, superClass);
3557 function ArtistCollection() {
3558 return ArtistCollection.__super__.constructor.apply(this, arguments);
3561 ArtistCollection.prototype.model = KodiEntities.Artist;
3563 ArtistCollection.prototype.methods = {
3564 read: ['AudioLibrary.GetArtists', 'albumartistsonly', 'properties', 'limits', 'sort', 'filter']
3567 ArtistCollection.prototype.args = function() {
3568 return this.getArgs({
3569 albumartistsonly: config.getLocal('albumArtistsOnly', true),
3570 properties: this.argFields(helpers.entities.getFields(API.fields, 'small')),
3571 limits: this.argLimit(),
3572 sort: this.argSort('title', 'ascending'),
3573 filter: this.argFilter()
3577 ArtistCollection.prototype.parse = function(resp, xhr) {
3578 return this.getResult(resp, 'artists');
3581 return ArtistCollection;
3583 })(App.KodiEntities.Collection);
3586 Request Handlers.
3588 App.reqres.setHandler("artist:entity", function(id, options) {
3589 if (options == null) {
3590 options = {};
3592 return API.getArtist(id, options);
3594 App.reqres.setHandler("artist:entities", function(options) {
3595 if (options == null) {
3596 options = {};
3598 if (options.filter && options.albumartistsonly !== true) {
3599 options.albumartistsonly = false;
3601 return API.getArtists(options);
3603 return App.reqres.setHandler("artist:fields", function(type) {
3604 if (type == null) {
3605 type = 'full';
3607 return helpers.entities.getFields(API.fields, type);
3611 this.Kodi.module("KodiEntities", function(KodiEntities, App, Backbone, Marionette, $, _) {
3614 API Helpers
3616 var API;
3617 API = {
3618 fields: {
3619 minimal: ['name'],
3620 small: ['order', 'role', 'thumbnail', 'origin', 'url'],
3621 full: []
3623 getCollection: function(cast, origin) {
3624 var collection, i, item;
3625 for (i in cast) {
3626 item = cast[i];
3627 cast[i].origin = origin;
3629 collection = new KodiEntities.CastCollection(cast);
3630 return collection;
3635 Models and collections.
3637 KodiEntities.Cast = (function(superClass) {
3638 extend(Cast, superClass);
3640 function Cast() {
3641 return Cast.__super__.constructor.apply(this, arguments);
3644 Cast.prototype.idAttribute = "order";
3646 Cast.prototype.defaults = function() {
3647 return this.parseFieldsToDefaults(helpers.entities.getFields(API.fields, 'small'), {});
3650 Cast.prototype.parse = function(obj, xhr) {
3651 obj.url = '?cast=' + obj.name;
3652 return obj;
3655 return Cast;
3657 })(App.KodiEntities.Model);
3658 KodiEntities.CastCollection = (function(superClass) {
3659 extend(CastCollection, superClass);
3661 function CastCollection() {
3662 return CastCollection.__super__.constructor.apply(this, arguments);
3665 CastCollection.prototype.model = KodiEntities.Cast;
3667 return CastCollection;
3669 })(App.KodiEntities.Collection);
3672 Request Handlers.
3674 return App.reqres.setHandler("cast:entities", function(cast, origin) {
3675 return API.getCollection(cast, origin);
3679 this.Kodi.module("KodiEntities", function(KodiEntities, App, Backbone, Marionette, $, _) {
3682 API Helpers
3684 var API;
3685 API = {
3686 fields: {
3687 minimal: [],
3688 small: ['title', 'runtime', 'starttime', 'endtime', 'genre', 'progress'],
3689 full: ["plot", "plotoutline", "progresspercentage", "episodename", "episodenum", "episodepart", "firstaired", "hastimer", "isactive", "parentalrating", "wasactive", "thumbnail", "rating", "originaltitle", "cast", "director", "writer", "year", "imdbnumber", "hastimerrule", "hasrecording", "recording", "isseries"]
3691 getEntity: function(channelid, options) {
3692 var entity;
3693 entity = new App.KodiEntities.Broadcast();
3694 entity.set({
3695 channelid: parseInt(channelid),
3696 properties: helpers.entities.getFields(API.fields, 'full')
3698 entity.fetch(options);
3699 return entity;
3701 getCollection: function(options) {
3702 var collection, defaultOptions;
3703 defaultOptions = {
3704 useNamedParameters: true
3706 options = _.extend(defaultOptions, options);
3707 collection = new KodiEntities.BroadcastCollection();
3708 collection.fetch(options);
3709 return collection;
3714 Models and collections.
3716 KodiEntities.Broadcast = (function(superClass) {
3717 extend(Broadcast, superClass);
3719 function Broadcast() {
3720 return Broadcast.__super__.constructor.apply(this, arguments);
3723 Broadcast.prototype.defaults = function() {
3724 var fields;
3725 fields = _.extend(this.modelDefaults, {
3726 channelid: 1,
3727 channel: ''
3729 return this.parseFieldsToDefaults(helpers.entities.getFields(API.fields, 'full'), fields);
3732 Broadcast.prototype.methods = {
3733 read: ['PVR.GetBroadcasts', 'channelid', 'properties']
3736 Broadcast.prototype.parse = function(resp, xhr) {
3737 var obj;
3738 obj = resp.broadcasts != null ? resp.broadcasts : resp;
3739 if (resp.broadcasts != null) {
3740 obj.fullyloaded = true;
3742 return this.parseModel('broadcast', obj, obj.broadcastid);
3745 return Broadcast;
3747 })(App.KodiEntities.Model);
3748 KodiEntities.BroadcastCollection = (function(superClass) {
3749 extend(BroadcastCollection, superClass);
3751 function BroadcastCollection() {
3752 return BroadcastCollection.__super__.constructor.apply(this, arguments);
3755 BroadcastCollection.prototype.model = KodiEntities.Broadcast;
3757 BroadcastCollection.prototype.methods = {
3758 read: ['PVR.GetBroadcasts', 'channelid', 'properties', 'limits']
3761 BroadcastCollection.prototype.args = function() {
3762 return this.getArgs({
3763 channelid: this.argCheckOption('channelid', 0),
3764 properties: helpers.entities.getFields(API.fields, 'full'),
3765 limits: this.argLimit()
3769 BroadcastCollection.prototype.parse = function(resp, xhr) {
3770 return this.getResult(resp, 'broadcasts');
3773 return BroadcastCollection;
3775 })(App.KodiEntities.Collection);
3778 Request Handlers.
3780 App.reqres.setHandler("broadcast:entity", function(collection, channelid) {
3781 return API.getEntity(collection, parseInt(channelid));
3783 return App.reqres.setHandler("broadcast:entities", function(channelid, options) {
3784 if (options == null) {
3785 options = {};
3787 options.channelid = parseInt(channelid);
3788 return API.getCollection(options);
3792 this.Kodi.module("KodiEntities", function(KodiEntities, App, Backbone, Marionette, $, _) {
3795 API Helpers
3797 var API;
3798 API = {
3799 fields: {
3800 minimal: ['title'],
3801 small: ['thumbnail', 'playcount', 'lastplayed', 'dateadded', 'episode', 'season', 'rating', 'file', 'cast', 'showtitle', 'tvshowid', 'uniqueid', 'resume', 'firstaired'],
3802 full: ['fanart', 'plot', 'director', 'writer', 'runtime', 'streamdetails']
3804 getEntity: function(id, options) {
3805 var entity;
3806 entity = new App.KodiEntities.Episode();
3807 entity.set({
3808 episodeid: parseInt(id),
3809 properties: helpers.entities.getFields(API.fields, 'full')
3811 entity.fetch(options);
3812 return entity;
3814 getCollection: function(options) {
3815 var collection, defaultOptions;
3816 defaultOptions = {
3817 cache: false,
3818 expires: config.get('static', 'collectionCacheExpiry'),
3819 useNamedParameters: true
3821 options = _.extend(defaultOptions, options);
3822 collection = new KodiEntities.EpisodeCollection();
3823 collection.fetch(options);
3824 return collection;
3829 Models and collections.
3831 KodiEntities.Episode = (function(superClass) {
3832 extend(Episode, superClass);
3834 function Episode() {
3835 return Episode.__super__.constructor.apply(this, arguments);
3838 Episode.prototype.defaults = function() {
3839 var fields;
3840 fields = _.extend(this.modelDefaults, {
3841 episodeid: 1,
3842 episode: ''
3844 return this.parseFieldsToDefaults(helpers.entities.getFields(API.fields, 'full'), fields);
3847 Episode.prototype.methods = {
3848 read: ['VideoLibrary.GetEpisodeDetails', 'episodeid', 'properties']
3851 Episode.prototype.parse = function(resp, xhr) {
3852 var obj;
3853 obj = resp.episodedetails != null ? resp.episodedetails : resp;
3854 if (resp.episodedetails != null) {
3855 obj.fullyloaded = true;
3857 obj.unwatched = obj.playcount > 0 ? 0 : 1;
3858 return this.parseModel('episode', obj, obj.episodeid);
3861 return Episode;
3863 })(App.KodiEntities.Model);
3864 KodiEntities.EpisodeCollection = (function(superClass) {
3865 extend(EpisodeCollection, superClass);
3867 function EpisodeCollection() {
3868 return EpisodeCollection.__super__.constructor.apply(this, arguments);
3871 EpisodeCollection.prototype.model = KodiEntities.Episode;
3873 EpisodeCollection.prototype.methods = {
3874 read: ['VideoLibrary.GetEpisodes', 'tvshowid', 'season', 'properties', 'limits', 'sort', 'filter']
3877 EpisodeCollection.prototype.args = function() {
3878 return this.getArgs({
3879 tvshowid: this.argCheckOption('tvshowid', void 0),
3880 season: this.argCheckOption('season', void 0),
3881 properties: this.argFields(helpers.entities.getFields(API.fields, 'small')),
3882 limits: this.argLimit(),
3883 sort: this.argSort("episode", "ascending"),
3884 filter: this.argCheckOption('filter', void 0)
3888 EpisodeCollection.prototype.parse = function(resp, xhr) {
3889 return this.getResult(resp, 'episodes');
3892 return EpisodeCollection;
3894 })(App.KodiEntities.Collection);
3895 KodiEntities.EpisodeCustomCollection = (function(superClass) {
3896 extend(EpisodeCustomCollection, superClass);
3898 function EpisodeCustomCollection() {
3899 return EpisodeCustomCollection.__super__.constructor.apply(this, arguments);
3902 EpisodeCustomCollection.prototype.model = KodiEntities.Episode;
3904 return EpisodeCustomCollection;
3906 })(App.KodiEntities.Collection);
3909 Request Handlers.
3911 App.reqres.setHandler("episode:entity", function(id, options) {
3912 if (options == null) {
3913 options = {};
3915 return API.getEntity(id, options);
3917 App.reqres.setHandler("episode:entities", function(options) {
3918 if (options == null) {
3919 options = {};
3921 return API.getCollection(options);
3923 App.reqres.setHandler("episode:tvshow:entities", function(tvshowid, season, options) {
3924 if (options == null) {
3925 options = {};
3927 if (tvshowid !== 'all') {
3928 options.tvshowid = tvshowid;
3929 if (season !== 'all') {
3930 options.season = season;
3933 return API.getCollection(options);
3935 App.reqres.setHandler("episode:build:collection", function(items) {
3936 return new KodiEntities.EpisodeCustomCollection(items);
3938 return App.reqres.setHandler("episode:fields", function(type) {
3939 if (type == null) {
3940 type = 'full';
3942 return helpers.entities.getFields(API.fields, type);
3946 this.Kodi.module("KodiEntities", function(KodiEntities, App, Backbone, Marionette, $, _) {
3949 API Helpers
3951 var API;
3952 API = {
3953 fields: {
3954 minimal: ['title', 'file', 'mimetype'],
3955 small: ['thumbnail', 'dateadded'],
3956 full: ['fanart', 'streamdetails']
3958 addonFields: ['path', 'name'],
3959 sources: [
3961 media: 'video',
3962 label: 'Video',
3963 type: 'source',
3964 provides: 'video'
3965 }, {
3966 media: 'music',
3967 label: 'Music',
3968 type: 'source',
3969 provides: 'audio'
3970 }, {
3971 media: 'music',
3972 label: 'Audio add-ons',
3973 type: 'addon',
3974 provides: 'audio',
3975 addonType: 'xbmc.addon.audio',
3976 content: 'unknown'
3977 }, {
3978 media: 'video',
3979 label: 'Video add-ons',
3980 type: 'addon',
3981 provides: 'files',
3982 addonType: 'xbmc.addon.video',
3983 content: 'unknown'
3986 directorySeparator: '/',
3987 getEntity: function(id, options) {
3988 var entity;
3989 entity = new App.KodiEntities.File();
3990 entity.set({
3991 file: id,
3992 properties: helpers.entities.getFields(API.fields, 'full')
3994 entity.fetch(options);
3995 return entity;
3997 getCollection: function(type, options) {
3998 var collection, defaultOptions;
3999 defaultOptions = {
4000 cache: true,
4001 useNamedParameters: true
4003 options = _.extend(defaultOptions, options);
4004 if (type === 'sources') {
4005 collection = new KodiEntities.SourceCollection();
4006 } else {
4007 collection = new KodiEntities.FileCollection();
4009 collection.fetch(options);
4010 return collection;
4012 parseToFilesAndFolders: function(collection) {
4013 var all, collections;
4014 all = collection.getRawCollection();
4015 collections = {};
4016 collections.file = new KodiEntities.FileCustomCollection(_.where(all, {
4017 filetype: 'file'
4018 }));
4019 collections.directory = new KodiEntities.FileCustomCollection(_.where(all, {
4020 filetype: 'directory'
4021 }));
4022 return collections;
4024 getSources: function() {
4025 var collection, commander, commands, len, n, ref, source;
4026 commander = App.request("command:kodi:controller", 'auto', 'Commander');
4027 commands = [];
4028 collection = new KodiEntities.SourceCollection();
4029 ref = this.sources;
4030 for (n = 0, len = ref.length; n < len; n++) {
4031 source = ref[n];
4032 if (source.type === 'source') {
4033 commands.push({
4034 method: 'Files.GetSources',
4035 params: [source.media]
4038 if (source.type === 'addon') {
4039 commands.push({
4040 method: 'Addons.GetAddons',
4041 params: [source.addonType, source.content, true, this.addonFields]
4045 commander.multipleCommands(commands, (function(_this) {
4046 return function(resp) {
4047 var i, item, len1, model, o, ref1, responseKey;
4048 for (i in resp) {
4049 item = resp[i];
4050 source = _this.sources[i];
4051 responseKey = source.type + 's';
4052 if (item[responseKey]) {
4053 ref1 = item[responseKey];
4054 for (o = 0, len1 = ref1.length; o < len1; o++) {
4055 model = ref1[o];
4056 model.media = source.media;
4057 model.sourcetype = source.type;
4058 if (source.type === 'addon') {
4059 model.file = _this.createAddonFile(model);
4060 model.label = model.name;
4062 model.url = _this.createFileUrl(source.media, model.file);
4063 collection.add(model);
4067 collection = _this.addPlaylists(collection);
4068 return collection.trigger('cachesync');
4070 })(this));
4071 return collection;
4073 parseSourceCollection: function(collection) {
4074 var all, items, len, n, ref, source;
4075 all = collection.getRawCollection();
4076 collection = [];
4077 ref = this.sources;
4078 for (n = 0, len = ref.length; n < len; n++) {
4079 source = ref[n];
4080 items = _.where(all, {
4081 media: source.media
4083 if (items.length > 0 && source.type === 'source') {
4084 source.sources = new KodiEntities.SourceCollection(items);
4085 source.url = 'browser/' + source.media;
4086 collection.push(source);
4089 return new KodiEntities.SourceSetCollection(collection);
4091 createFileUrl: function(media, file) {
4092 return 'browser/' + media + '/' + encodeURIComponent(file);
4094 createAddonFile: function(addon) {
4095 return 'plugin://' + addon.addonid + this.directorySeparator;
4097 parseFiles: function(items, media) {
4098 var i, item;
4099 for (i in items) {
4100 item = items[i];
4101 if (!item.parsed) {
4102 item = App.request("images:path:entity", item);
4103 items[i] = this.correctFileType(item);
4104 items[i].media = media;
4105 items[i].player = this.getPlayer(media);
4106 items[i].url = this.createFileUrl(media, item.file);
4107 items[i].parsed = true;
4108 items[i].defaultSort = parseInt(i);
4109 items[i].label = helpers.global.removeBBCode(item.label);
4112 return items;
4114 addPlaylists: function(collection) {
4115 var len, model, n, type, types;
4116 types = ['video', 'music'];
4117 for (n = 0, len = types.length; n < len; n++) {
4118 type = types[n];
4119 model = this.createPathModel(type, t.gettext('Playlists'), 'special://profile/playlists/' + type);
4120 model.sourcetype = 'playlist';
4121 collection.add(model);
4123 return collection;
4125 correctFileType: function(item) {
4126 var directoryMimeTypes;
4127 directoryMimeTypes = ['x-directory/normal'];
4128 if (item.mimetype && helpers.global.inArray(item.mimetype, directoryMimeTypes)) {
4129 item.filetype = 'directory';
4131 return item;
4133 createPathCollection: function(file, sourcesCollection) {
4134 var allSources, basePath, excludedPaths, items, len, len1, n, o, parentSource, part, pathParts, source;
4135 items = [];
4136 parentSource = {};
4137 allSources = sourcesCollection.getRawCollection();
4138 for (n = 0, len = allSources.length; n < len; n++) {
4139 source = allSources[n];
4140 if (parentSource.file) {
4141 continue;
4143 if (helpers.global.stringStartsWith(source.file, file)) {
4144 parentSource = source;
4147 if (parentSource.file) {
4148 items.push(parentSource);
4149 basePath = parentSource.file;
4150 pathParts = helpers.global.stringStripStartsWith(parentSource.file, file).split(this.directorySeparator);
4151 excludedPaths = App.request("addon:excludedPaths", parentSource.addonid);
4152 for (o = 0, len1 = pathParts.length; o < len1; o++) {
4153 part = pathParts[o];
4154 if (part !== '') {
4155 basePath += part + this.directorySeparator;
4156 if (excludedPaths.indexOf(basePath) === -1) {
4157 items.push(this.createPathModel(parentSource.media, part, basePath));
4162 return new KodiEntities.FileCustomCollection(items);
4164 createPathModel: function(media, label, file) {
4165 var model;
4166 model = {
4167 label: label,
4168 file: file,
4169 media: media,
4170 url: this.createFileUrl(media, file)
4172 return model;
4174 getPlayer: function(media) {
4175 if (media === 'music') {
4176 'audio';
4178 return media;
4183 Models and collections.
4185 KodiEntities.EmptyFile = (function(superClass) {
4186 extend(EmptyFile, superClass);
4188 function EmptyFile() {
4189 return EmptyFile.__super__.constructor.apply(this, arguments);
4192 EmptyFile.prototype.idAttribute = "file";
4194 EmptyFile.prototype.defaults = function() {
4195 var fields;
4196 fields = _.extend(this.modelDefaults, {
4197 filetype: 'directory',
4198 media: '',
4199 label: '',
4200 url: ''
4202 return this.parseFieldsToDefaults(helpers.entities.getFields(API.fields, 'full'), fields);
4205 return EmptyFile;
4207 })(App.KodiEntities.Model);
4208 KodiEntities.File = (function(superClass) {
4209 extend(File, superClass);
4211 function File() {
4212 return File.__super__.constructor.apply(this, arguments);
4215 File.prototype.methods = {
4216 read: ['Files.GetFileDetails', 'file', 'properties']
4219 File.prototype.parse = function(resp, xhr) {
4220 var obj;
4221 obj = resp.filedetails != null ? resp.filedetails : resp;
4222 if (resp.filedetails != null) {
4223 obj.fullyloaded = true;
4225 return obj;
4228 return File;
4230 })(KodiEntities.EmptyFile);
4231 KodiEntities.FileCollection = (function(superClass) {
4232 extend(FileCollection, superClass);
4234 function FileCollection() {
4235 return FileCollection.__super__.constructor.apply(this, arguments);
4238 FileCollection.prototype.model = KodiEntities.File;
4240 FileCollection.prototype.methods = {
4241 read: ['Files.GetDirectory', 'directory', 'media', 'properties', 'sort']
4244 FileCollection.prototype.args = function() {
4245 return this.getArgs({
4246 directory: this.argCheckOption('file', ''),
4247 media: this.argCheckOption('media', ''),
4248 properties: this.argFields(helpers.entities.getFields(API.fields, 'small')),
4249 sort: this.argSort('none', 'ascending')
4253 FileCollection.prototype.parse = function(resp, xhr) {
4254 var items;
4255 items = this.getResult(resp, 'files');
4256 return API.parseFiles(items, this.options.media);
4259 return FileCollection;
4261 })(App.KodiEntities.Collection);
4262 KodiEntities.FileCustomCollection = (function(superClass) {
4263 extend(FileCustomCollection, superClass);
4265 function FileCustomCollection() {
4266 return FileCustomCollection.__super__.constructor.apply(this, arguments);
4269 FileCustomCollection.prototype.model = KodiEntities.File;
4271 return FileCustomCollection;
4273 })(App.KodiEntities.Collection);
4274 KodiEntities.Source = (function(superClass) {
4275 extend(Source, superClass);
4277 function Source() {
4278 return Source.__super__.constructor.apply(this, arguments);
4281 Source.prototype.idAttribute = "file";
4283 Source.prototype.defaults = {
4284 label: '',
4285 file: '',
4286 media: '',
4287 url: ''
4290 return Source;
4292 })(App.KodiEntities.Model);
4293 KodiEntities.SourceCollection = (function(superClass) {
4294 extend(SourceCollection, superClass);
4296 function SourceCollection() {
4297 return SourceCollection.__super__.constructor.apply(this, arguments);
4300 SourceCollection.prototype.model = KodiEntities.Source;
4302 return SourceCollection;
4304 })(App.KodiEntities.Collection);
4305 KodiEntities.SourceSet = (function(superClass) {
4306 extend(SourceSet, superClass);
4308 function SourceSet() {
4309 return SourceSet.__super__.constructor.apply(this, arguments);
4312 SourceSet.prototype.idAttribute = "file";
4314 SourceSet.prototype.defaults = {
4315 label: '',
4316 sources: ''
4319 return SourceSet;
4321 })(App.KodiEntities.Model);
4322 KodiEntities.SourceSetCollection = (function(superClass) {
4323 extend(SourceSetCollection, superClass);
4325 function SourceSetCollection() {
4326 return SourceSetCollection.__super__.constructor.apply(this, arguments);
4329 SourceSetCollection.prototype.model = KodiEntities.Source;
4331 return SourceSetCollection;
4333 })(App.KodiEntities.Collection);
4336 Request Handlers.
4338 App.reqres.setHandler("file:entity", function(id, options) {
4339 if (options == null) {
4340 options = {};
4342 return API.getEntity(id, options);
4344 App.reqres.setHandler("file:url:entity", function(media, hash) {
4345 var file;
4346 file = decodeURIComponent(hash);
4347 return new KodiEntities.EmptyFile({
4348 media: media,
4349 file: file,
4350 url: API.createFileUrl(media, file)
4353 App.reqres.setHandler("file:entities", function(options) {
4354 if (options == null) {
4355 options = {};
4357 return API.getCollection('files', options);
4359 App.reqres.setHandler("file:path:entities", function(file, sourceCollection) {
4360 return API.createPathCollection(file, sourceCollection);
4362 App.reqres.setHandler("file:parsed:entities", function(collection) {
4363 return API.parseToFilesAndFolders(collection);
4365 App.reqres.setHandler("file:source:entities", function(media) {
4366 return API.getSources();
4368 App.reqres.setHandler("file:source:media:entities", function(collection) {
4369 return API.parseSourceCollection(collection);
4371 return App.reqres.setHandler("file:source:mediatypes", function() {
4372 return API.availableSources;
4376 this.Kodi.module("KodiEntities", function(KodiEntities, App, Backbone, Marionette, $, _) {
4379 API Helpers
4381 var API;
4382 API = {
4383 fields: {
4384 minimal: ['title'],
4385 small: ['thumbnail'],
4386 full: []
4388 getEntity: function(collection, genre) {
4389 return collection.findWhere({
4390 title: genre
4393 getCollection: function(type, options) {
4394 var collection;
4395 collection = new KodiEntities.GenreAudioCollection();
4396 collection.fetch(helpers.entities.buildOptions(options));
4397 return collection;
4402 Models and collections.
4404 KodiEntities.Genre = (function(superClass) {
4405 extend(Genre, superClass);
4407 function Genre() {
4408 return Genre.__super__.constructor.apply(this, arguments);
4411 Genre.prototype.defaults = function() {
4412 return this.parseFieldsToDefaults(helpers.entities.getFields(API.fields, 'full'), {});
4415 Genre.prototype.parse = function(obj, xhr) {
4416 obj.fullyloaded = true;
4417 obj.url = 'music/genre/' + encodeURIComponent(obj.title);
4418 return obj;
4421 return Genre;
4423 })(App.KodiEntities.Model);
4424 KodiEntities.GenreAudioCollection = (function(superClass) {
4425 extend(GenreAudioCollection, superClass);
4427 function GenreAudioCollection() {
4428 return GenreAudioCollection.__super__.constructor.apply(this, arguments);
4431 GenreAudioCollection.prototype.model = KodiEntities.Genre;
4433 GenreAudioCollection.prototype.methods = {
4434 read: ['AudioLibrary.GetGenres', 'properties', 'limits', 'sort']
4437 GenreAudioCollection.prototype.args = function() {
4438 return this.getArgs({
4439 properties: helpers.entities.getFields(API.fields, 'small'),
4440 limits: this.argLimit(),
4441 sort: this.argSort('title', 'ascending')
4445 GenreAudioCollection.prototype.parse = function(resp, xhr) {
4446 return this.getResult(resp, 'genres');
4449 return GenreAudioCollection;
4451 })(App.KodiEntities.Collection);
4454 Request Handlers.
4456 App.reqres.setHandler("genre:entity", function(collection, genre) {
4457 return API.getEntity(collection, genre);
4459 return App.reqres.setHandler("genre:entities", function(type, options) {
4460 if (type == null) {
4461 type = 'audio';
4463 if (options == null) {
4464 options = {};
4466 return API.getCollection(type, options);
4470 this.Kodi.module("KodiEntities", function(KodiEntities, App, Backbone, Marionette, $, _) {
4473 API Helpers
4475 var API;
4476 API = {
4477 fields: {
4478 minimal: ['title', 'art'],
4479 small: ['playcount', 'lastplayed', 'dateadded', 'resume', 'rating', 'year', 'file', 'genre', 'writer', 'director', 'cast', 'set', 'studio', 'mpaa', 'tag'],
4480 full: ['plotoutline', 'imdbnumber', 'runtime', 'streamdetails', 'plot', 'trailer', 'sorttitle', 'originaltitle', 'country']
4482 getEntity: function(id, options) {
4483 var entity;
4484 entity = new App.KodiEntities.Movie();
4485 entity.set({
4486 movieid: parseInt(id),
4487 properties: helpers.entities.getFields(API.fields, 'full')
4489 entity.fetch(options);
4490 return entity;
4492 getCollection: function(options) {
4493 var collection;
4494 collection = new KodiEntities.MovieCollection();
4495 collection.fetch(helpers.entities.buildOptions(options));
4496 return collection;
4501 Models and collections.
4503 KodiEntities.Movie = (function(superClass) {
4504 extend(Movie, superClass);
4506 function Movie() {
4507 return Movie.__super__.constructor.apply(this, arguments);
4510 Movie.prototype.defaults = function() {
4511 var fields;
4512 fields = _.extend(this.modelDefaults, {
4513 movieid: 1,
4514 movie: ''
4516 return this.parseFieldsToDefaults(helpers.entities.getFields(API.fields, 'full'), fields);
4519 Movie.prototype.methods = {
4520 read: ['VideoLibrary.GetMovieDetails', 'movieid', 'properties']
4523 Movie.prototype.parse = function(resp, xhr) {
4524 var obj;
4525 obj = resp.moviedetails != null ? resp.moviedetails : resp;
4526 if (resp.moviedetails != null) {
4527 obj.fullyloaded = true;
4529 obj.unwatched = obj.playcount > 0 ? 0 : 1;
4530 return this.parseModel('movie', obj, obj.movieid);
4533 return Movie;
4535 })(App.KodiEntities.Model);
4536 KodiEntities.MovieCollection = (function(superClass) {
4537 extend(MovieCollection, superClass);
4539 function MovieCollection() {
4540 return MovieCollection.__super__.constructor.apply(this, arguments);
4543 MovieCollection.prototype.model = KodiEntities.Movie;
4545 MovieCollection.prototype.methods = {
4546 read: ['VideoLibrary.GetMovies', 'properties', 'limits', 'sort', 'filter']
4549 MovieCollection.prototype.args = function() {
4550 return this.getArgs({
4551 properties: this.argFields(helpers.entities.getFields(API.fields, 'small')),
4552 limits: this.argLimit(),
4553 sort: this.argSort('title', 'ascending'),
4554 filter: this.argFilter()
4558 MovieCollection.prototype.parse = function(resp, xhr) {
4559 return this.getResult(resp, 'movies');
4562 return MovieCollection;
4564 })(App.KodiEntities.Collection);
4565 KodiEntities.MovieCustomCollection = (function(superClass) {
4566 extend(MovieCustomCollection, superClass);
4568 function MovieCustomCollection() {
4569 return MovieCustomCollection.__super__.constructor.apply(this, arguments);
4572 MovieCustomCollection.prototype.model = KodiEntities.Movie;
4574 return MovieCustomCollection;
4576 })(App.KodiEntities.Collection);
4579 Request Handlers.
4581 App.reqres.setHandler("movie:entity", function(id, options) {
4582 if (options == null) {
4583 options = {};
4585 return API.getEntity(id, options);
4587 App.reqres.setHandler("movie:entities", function(options) {
4588 if (options == null) {
4589 options = {};
4591 return API.getCollection(options);
4593 App.reqres.setHandler("movie:build:collection", function(items) {
4594 return new KodiEntities.MovieCustomCollection(items);
4596 return App.reqres.setHandler("movie:fields", function(type) {
4597 if (type == null) {
4598 type = 'full';
4600 return helpers.entities.getFields(API.fields, type);
4604 this.Kodi.module("KodiEntities", function(KodiEntities, App, Backbone, Marionette, $, _) {
4605 var API;
4606 API = {
4607 fields: {
4608 minimal: ['title'],
4609 small: ['thumbnail', 'file', 'genre', 'artist', 'year', 'playcount', 'dateadded', 'streamdetails', 'album', 'resume', 'director', 'rating'],
4610 full: ['fanart', 'studio', 'plot', 'track', 'tag']
4612 getVideo: function(id, options) {
4613 var artist;
4614 artist = new App.KodiEntities.MusicVideo();
4615 artist.set({
4616 musicvideoid: parseInt(id),
4617 properties: helpers.entities.getFields(API.fields, 'full')
4619 artist.fetch(options);
4620 return artist;
4622 getVideos: function(options) {
4623 var collection;
4624 collection = new KodiEntities.MusicVideoCollection();
4625 collection.fetch(helpers.entities.buildOptions(options));
4626 return collection;
4631 Models and collections.
4633 KodiEntities.MusicVideo = (function(superClass) {
4634 extend(MusicVideo, superClass);
4636 function MusicVideo() {
4637 return MusicVideo.__super__.constructor.apply(this, arguments);
4640 MusicVideo.prototype.defaults = function() {
4641 var fields;
4642 fields = _.extend(this.modelDefaults, {
4643 musicvideoid: 1,
4644 title: ''
4646 return this.parseFieldsToDefaults(helpers.entities.getFields(API.fields, 'full'), fields);
4649 MusicVideo.prototype.methods = {
4650 read: ['VideoLibrary.GetMusicVideoDetails', 'musicvideoid', 'properties']
4653 MusicVideo.prototype.parse = function(resp, xhr) {
4654 var obj;
4655 obj = resp.musicvideodetails != null ? resp.musicvideodetails : resp;
4656 if (resp.musicvideodetails != null) {
4657 obj.fullyloaded = true;
4659 return this.parseModel('musicvideo', obj, obj.musicvideoid);
4662 return MusicVideo;
4664 })(App.KodiEntities.Model);
4665 KodiEntities.MusicVideoCollection = (function(superClass) {
4666 extend(MusicVideoCollection, superClass);
4668 function MusicVideoCollection() {
4669 return MusicVideoCollection.__super__.constructor.apply(this, arguments);
4672 MusicVideoCollection.prototype.model = KodiEntities.MusicVideo;
4674 MusicVideoCollection.prototype.methods = {
4675 read: ['VideoLibrary.GetMusicVideos', 'properties', 'limits', 'sort', 'filter']
4678 MusicVideoCollection.prototype.args = function() {
4679 return this.getArgs({
4680 properties: this.argFields(helpers.entities.getFields(API.fields, 'full')),
4681 limits: this.argLimit(),
4682 sort: this.argSort('title', 'ascending'),
4683 filter: this.argFilter()
4687 MusicVideoCollection.prototype.parse = function(resp, xhr) {
4688 return this.getResult(resp, 'musicvideos');
4691 return MusicVideoCollection;
4693 })(App.KodiEntities.Collection);
4694 KodiEntities.MusicVideoCustomCollection = (function(superClass) {
4695 extend(MusicVideoCustomCollection, superClass);
4697 function MusicVideoCustomCollection() {
4698 return MusicVideoCustomCollection.__super__.constructor.apply(this, arguments);
4701 MusicVideoCustomCollection.prototype.model = KodiEntities.MusicVideo;
4703 return MusicVideoCustomCollection;
4705 })(App.KodiEntities.Collection);
4708 Request Handlers.
4710 App.reqres.setHandler("musicvideo:entity", function(id, options) {
4711 if (options == null) {
4712 options = {};
4714 return API.getVideo(id, options);
4716 App.reqres.setHandler("musicvideo:entities", function(options) {
4717 if (options == null) {
4718 options = {};
4720 return API.getVideos(options);
4722 App.reqres.setHandler("musicvideo:fields", function(type) {
4723 if (type == null) {
4724 type = 'full';
4726 return helpers.entities.getFields(API.fields, type);
4728 return App.reqres.setHandler("musicvideo:build:collection", function(items) {
4729 return new KodiEntities.MusicVideoCustomCollection(items);
4733 this.Kodi.module("KodiEntities", function(KodiEntities, App, Backbone, Marionette, $, _) {
4736 API Helpers
4738 var API;
4739 API = {
4740 fields: {
4741 minimal: ['title', 'thumbnail', 'file'],
4742 small: ['artist', 'genre', 'year', 'rating', 'album', 'track', 'duration', 'playcount', 'dateadded', 'episode', 'artistid', 'albumid', 'tvshowid'],
4743 full: ['fanart']
4745 canThumbsUp: ['song', 'movie', 'episode'],
4746 getCollection: function(options) {
4747 var collection, defaultOptions;
4748 defaultOptions = {
4749 cache: false,
4750 useNamedParameters: true
4752 options = _.extend(defaultOptions, options);
4753 collection = new KodiEntities.PlaylistCollection();
4754 collection.fetch(options);
4755 return collection;
4757 getType: function(item, media) {
4758 var type;
4759 type = 'file';
4760 if (item.id !== void 0 && item.id !== '') {
4761 if (media === 'audio') {
4762 type = 'song';
4763 } else if (media === 'video') {
4764 if (item.episode !== '') {
4765 type = 'episode';
4766 } else {
4767 type = 'movie';
4771 return type;
4773 parseItems: function(items, options) {
4774 var i, item;
4775 for (i in items) {
4776 item = items[i];
4777 item.position = parseInt(i);
4778 items[i] = this.parseItem(item, options);
4780 return items;
4782 parseItem: function(item, options) {
4783 item.playlistid = options.playlistid;
4784 item.media = options.media;
4785 item.player = 'kodi';
4786 if (!item.type || item.type === 'unknown') {
4787 item.type = API.getType(item, options.media);
4789 if (item.type === 'file') {
4790 item.id = item.file;
4792 item.uid = helpers.entities.createUid(item);
4793 item.canThumbsUp = helpers.global.inArray(item.type, API.canThumbsUp);
4794 item.thumbsUp = false;
4795 return item;
4800 Models and collections.
4802 KodiEntities.PlaylistItem = (function(superClass) {
4803 extend(PlaylistItem, superClass);
4805 function PlaylistItem() {
4806 return PlaylistItem.__super__.constructor.apply(this, arguments);
4809 PlaylistItem.prototype.idAttribute = "position";
4811 PlaylistItem.prototype.defaults = function() {
4812 var fields;
4813 fields = _.extend(this.modelDefaults, {
4814 position: 0
4816 return this.parseFieldsToDefaults(helpers.entities.getFields(API.fields, 'full'), fields);
4819 PlaylistItem.prototype.parse = function(resp, xhr) {
4820 var model;
4821 resp.fullyloaded = true;
4822 model = this.parseModel(resp.type, resp, resp.id);
4823 model.url = helpers.url.playlistUrl(model);
4824 return model;
4827 return PlaylistItem;
4829 })(App.KodiEntities.Model);
4830 KodiEntities.PlaylistCollection = (function(superClass) {
4831 extend(PlaylistCollection, superClass);
4833 function PlaylistCollection() {
4834 return PlaylistCollection.__super__.constructor.apply(this, arguments);
4837 PlaylistCollection.prototype.model = KodiEntities.PlaylistItem;
4839 PlaylistCollection.prototype.methods = {
4840 read: ['Playlist.GetItems', 'playlistid', 'properties', 'limits']
4843 PlaylistCollection.prototype.args = function() {
4844 return this.getArgs({
4845 playlistid: this.argCheckOption('playlistid', 0),
4846 properties: this.argFields(helpers.entities.getFields(API.fields, 'small')),
4847 limits: this.argLimit()
4851 PlaylistCollection.prototype.parse = function(resp, xhr) {
4852 var items;
4853 items = this.getResult(resp, 'items');
4854 return API.parseItems(items, this.options);
4857 return PlaylistCollection;
4859 })(App.KodiEntities.Collection);
4862 Request Handlers.
4864 App.reqres.setHandler("playlist:kodi:entities", function(media) {
4865 var collection, options, playlist;
4866 if (media == null) {
4867 media = 'audio';
4869 playlist = App.request("command:kodi:controller", media, 'PlayList');
4870 options = {};
4871 options.media = media;
4872 options.playlistid = playlist.getPlayer();
4873 collection = API.getCollection(options);
4874 collection.sortCollection('position', 'asc');
4875 return collection;
4877 return App.reqres.setHandler("playlist:kodi:entity:api", function() {
4878 return API;
4882 this.Kodi.module("KodiEntities", function(KodiEntities, App, Backbone, Marionette, $, _) {
4885 API Helpers
4887 var API;
4888 API = {
4889 fieldsChannel: {
4890 minimal: ['thumbnail'],
4891 small: ['channeltype', 'hidden', 'locked', 'channel', 'lastplayed', 'broadcastnow', 'isrecording'],
4892 full: []
4894 fieldsRecording: {
4895 minimal: ['channel', 'file', 'title'],
4896 small: ['resume', 'plot', 'genre', 'playcount', 'starttime', 'endtime', 'runtime', 'icon', 'art', 'streamurl', 'directory', 'radio', 'isdeleted', 'channeluid'],
4897 full: []
4899 getChannelEntity: function(id, options) {
4900 var entity;
4901 if (options == null) {
4902 options = {};
4904 entity = new App.KodiEntities.Channel();
4905 entity.set({
4906 channelid: parseInt(id),
4907 properties: helpers.entities.getFields(API.fieldsChannel, 'full')
4909 entity.fetch(options);
4910 return entity;
4912 getChannelCollection: function(options) {
4913 var collection, defaultOptions;
4914 defaultOptions = {
4915 useNamedParameters: true
4917 options = _.extend(defaultOptions, options);
4918 collection = new KodiEntities.ChannelCollection();
4919 collection.fetch(options);
4920 return collection;
4922 getRecordingEntity: function(id, options) {
4923 var entity;
4924 if (options == null) {
4925 options = {};
4927 entity = new App.KodiEntities.Recording();
4928 entity.set({
4929 recordingid: parseInt(id),
4930 properties: helpers.entities.getFields(API.fieldsRecording, 'full')
4932 entity.fetch(options);
4933 return entity;
4935 getRecordingCollection: function(options) {
4936 var collection, defaultOptions;
4937 defaultOptions = {
4938 useNamedParameters: true
4940 options = _.extend(defaultOptions, options);
4941 collection = new KodiEntities.RecordingCollection();
4942 collection.fetch(options);
4943 return collection;
4948 Models and collections.
4950 KodiEntities.Channel = (function(superClass) {
4951 extend(Channel, superClass);
4953 function Channel() {
4954 return Channel.__super__.constructor.apply(this, arguments);
4957 Channel.prototype.defaults = function() {
4958 return this.parseFieldsToDefaults(helpers.entities.getFields(API.fieldsChannel, 'full'), {});
4961 Channel.prototype.methods = {
4962 read: ['PVR.GetChannelDetails', 'channelid', 'properties']
4965 Channel.prototype.parse = function(resp, xhr) {
4966 var obj;
4967 obj = resp.channeldetails != null ? resp.channeldetails : resp;
4968 if (resp.channeldetails != null) {
4969 obj.fullyloaded = true;
4971 return this.parseModel('channel', obj, obj.channelid);
4974 return Channel;
4976 })(App.KodiEntities.Model);
4977 KodiEntities.ChannelCollection = (function(superClass) {
4978 extend(ChannelCollection, superClass);
4980 function ChannelCollection() {
4981 return ChannelCollection.__super__.constructor.apply(this, arguments);
4984 ChannelCollection.prototype.model = KodiEntities.Channel;
4986 ChannelCollection.prototype.methods = {
4987 read: ['PVR.GetChannels', 'channelgroupid', 'properties', 'limits']
4990 ChannelCollection.prototype.args = function() {
4991 return this.getArgs({
4992 channelgroupid: this.argCheckOption('group', 0),
4993 properties: helpers.entities.getFields(API.fieldsChannel, 'small'),
4994 limits: this.argLimit()
4998 ChannelCollection.prototype.parse = function(resp, xhr) {
4999 return this.getResult(resp, 'channels');
5002 return ChannelCollection;
5004 })(App.KodiEntities.Collection);
5005 KodiEntities.Recording = (function(superClass) {
5006 extend(Recording, superClass);
5008 function Recording() {
5009 return Recording.__super__.constructor.apply(this, arguments);
5012 Recording.prototype.defaults = function() {
5013 return this.parseFieldsToDefaults(helpers.entities.getFields(API.fieldsRecording, 'full'), {});
5016 Recording.prototype.methods = {
5017 read: ['PVR.GetRecordingDetails', 'recordingid', 'properties']
5020 Recording.prototype.parse = function(obj, xhr) {
5021 obj.fullyloaded = true;
5022 obj.player = obj.radio ? 'audio' : 'video';
5023 return this.parseModel('recording', obj, obj.recordingid);
5026 return Recording;
5028 })(App.KodiEntities.Model);
5029 KodiEntities.RecordingCollection = (function(superClass) {
5030 extend(RecordingCollection, superClass);
5032 function RecordingCollection() {
5033 return RecordingCollection.__super__.constructor.apply(this, arguments);
5036 RecordingCollection.prototype.model = KodiEntities.Recording;
5038 RecordingCollection.prototype.methods = {
5039 read: ['PVR.GetRecordings', 'properties', 'limits']
5042 RecordingCollection.prototype.args = function() {
5043 return this.getArgs({
5044 properties: helpers.entities.getFields(API.fieldsRecording, 'small'),
5045 limits: this.argLimit()
5049 RecordingCollection.prototype.parse = function(resp, xhr) {
5050 return this.getResult(resp, 'recordings');
5053 return RecordingCollection;
5055 })(App.KodiEntities.Collection);
5058 Request Handlers.
5060 App.reqres.setHandler("channel:entity", function(channelid, options) {
5061 if (options == null) {
5062 options = {};
5064 return API.getChannelEntity(channelid, options);
5066 App.reqres.setHandler("channel:entities", function(group, options) {
5067 if (group == null) {
5068 group = 'alltv';
5070 if (options == null) {
5071 options = {};
5073 options.group = group;
5074 return API.getChannelCollection(options);
5076 App.reqres.setHandler("recording:entity", function(channelid, options) {
5077 if (options == null) {
5078 options = {};
5080 return API.getRecordingEntity(channelid, options);
5082 return App.reqres.setHandler("recording:entities", function(group, options) {
5083 if (group == null) {
5084 group = 'alltv';
5086 if (options == null) {
5087 options = {};
5089 options.group = group;
5090 return API.getRecordingCollection(options);
5094 this.Kodi.module("KodiEntities", function(KodiEntities, App, Backbone, Marionette, $, _) {
5097 API Helpers
5099 var API;
5100 API = {
5101 fields: {
5102 minimal: ['season', 'art'],
5103 small: ['showtitle', 'playcount', 'thumbnail', 'tvshowid', 'episode', 'watchedepisodes'],
5104 full: []
5106 getEntity: function(collection, season) {
5107 return collection.findWhere({
5108 season: season
5111 getCollection: function(options) {
5112 var collection, defaultOptions;
5113 defaultOptions = {
5114 cache: false,
5115 expires: config.get('static', 'collectionCacheExpiry'),
5116 useNamedParameters: true
5118 options = _.extend(defaultOptions, options);
5119 collection = new KodiEntities.SeasonCollection();
5120 collection.fetch(options);
5121 return collection;
5126 Models and collections.
5128 KodiEntities.Season = (function(superClass) {
5129 extend(Season, superClass);
5131 function Season() {
5132 return Season.__super__.constructor.apply(this, arguments);
5135 Season.prototype.defaults = function() {
5136 var fields;
5137 fields = _.extend(this.modelDefaults, {
5138 seasonid: 1,
5139 season: ''
5141 return this.parseFieldsToDefaults(helpers.entities.getFields(API.fields, 'full'), fields);
5144 Season.prototype.parse = function(resp, xhr) {
5145 var obj;
5146 obj = resp.seasondetails != null ? resp.seasondetails : resp;
5147 if (resp.seasondetails != null) {
5148 obj.fullyloaded = true;
5150 obj.unwatched = obj.episode - obj.watchedepisodes;
5151 return this.parseModel('season', obj, obj.tvshowid + '/' + obj.season);
5154 return Season;
5156 })(App.KodiEntities.Model);
5157 KodiEntities.SeasonCollection = (function(superClass) {
5158 extend(SeasonCollection, superClass);
5160 function SeasonCollection() {
5161 return SeasonCollection.__super__.constructor.apply(this, arguments);
5164 SeasonCollection.prototype.model = KodiEntities.Season;
5166 SeasonCollection.prototype.methods = {
5167 read: ['VideoLibrary.GetSeasons', 'tvshowid', 'properties', 'limits', 'sort']
5170 SeasonCollection.prototype.args = function() {
5171 return this.getArgs({
5172 tvshowid: this.argCheckOption('tvshowid', 0),
5173 properties: this.argFields(helpers.entities.getFields(API.fields, 'small')),
5174 limits: this.argLimit(),
5175 sort: this.argSort("season", "ascending")
5179 SeasonCollection.prototype.parse = function(resp, xhr) {
5180 return this.getResult(resp, 'seasons');
5183 return SeasonCollection;
5185 })(App.KodiEntities.Collection);
5188 Request Handlers.
5190 App.reqres.setHandler("season:entity", function(collection, season) {
5191 return API.getEntity(collection, season);
5193 App.reqres.setHandler("season:entities", function(tvshowid, options) {
5194 if (options == null) {
5195 options = {};
5197 options.tvshowid = tvshowid;
5198 return API.getCollection(options);
5200 return App.reqres.setHandler("season:fields", function(type) {
5201 if (type == null) {
5202 type = 'full';
5204 return helpers.entities.getFields(API.fields, type);
5208 this.Kodi.module("KodiEntities", function(KodiEntities, App, Backbone, Marionette, $, _) {
5211 API Helpers
5213 var API;
5214 API = {
5215 settingsType: {
5216 sections: "SettingSectionCollection",
5217 categories: "SettingCategoryCollection",
5218 settings: "SettingCollection"
5220 ignoreKeys: ['weather'],
5221 fields: {
5222 minimal: ['settingstype'],
5223 small: ['title', 'control', 'options', 'parent', 'enabled', 'type', 'value', 'enabled', 'default', 'help', 'path', 'description', 'section', 'category'],
5224 full: []
5226 getSettingsLevel: function() {
5227 return config.getLocal('kodiSettingsLevel', 'standard');
5229 getEntity: function(id, collection) {
5230 var model;
5231 model = collection.where({
5232 method: id
5233 }).shift();
5234 return model;
5236 getCollection: function(options) {
5237 var collection, collectionMethod;
5238 if (options == null) {
5239 options = {
5240 type: 'sections'
5243 collectionMethod = this.settingsType[options.type];
5244 collection = new KodiEntities[collectionMethod]();
5245 options.useNamedParameters = true;
5246 collection.fetch(options);
5247 if (options.section && options.type === 'settings') {
5248 collection.where({
5249 section: options.section
5252 return collection;
5254 getSettings: function(section, categories, callback) {
5255 var commander, commands, items;
5256 if (categories == null) {
5257 categories = [];
5259 commander = App.request("command:kodi:controller", 'auto', 'Commander');
5260 commands = [];
5261 items = [];
5262 $(categories).each((function(_this) {
5263 return function(i, category) {
5264 return commands.push({
5265 method: 'Settings.GetSettings',
5266 params: [
5267 _this.getSettingsLevel(), {
5268 "section": section,
5269 "category": category
5274 })(this));
5275 return commander.multipleCommands(commands, (function(_this) {
5276 return function(resp) {
5277 var catId, i, item;
5278 for (i in resp) {
5279 item = resp[i];
5280 catId = categories[i];
5281 items[catId] = _this.parseCollection(item.settings, 'settings');
5283 return callback(items);
5285 })(this));
5287 parseCollection: function(itemsRaw, type) {
5288 var item, items, method;
5289 if (itemsRaw == null) {
5290 itemsRaw = [];
5292 if (type == null) {
5293 type = 'settings';
5295 items = [];
5296 for (method in itemsRaw) {
5297 item = itemsRaw[method];
5298 if (_.lastIndexOf(this.ignoreKeys, item.id) === -1) {
5299 items.push(this.parseItem(item, type));
5302 return items;
5304 parseItem: function(item, type) {
5305 if (type == null) {
5306 type = 'settings';
5308 item.settingstype = type;
5309 item.title = item.label;
5310 item.description = item.help;
5311 item.path = 'settings/kodi/' + item.id;
5312 return item;
5314 saveSettings: function(data, callback) {
5315 var commander, commands, key, val;
5316 commander = App.request("command:kodi:controller", 'auto', 'Commander');
5317 commands = [];
5318 for (key in data) {
5319 val = data[key];
5320 commands.push({
5321 method: 'Settings.SetSettingValue',
5322 params: [key, this.valuePreSave(val)]
5325 return commander.multipleCommands(commands, (function(_this) {
5326 return function(resp) {
5327 if (callback) {
5328 return callback(resp);
5331 })(this));
5333 valuePreSave: function(val) {
5334 if (val === String(parseInt(val))) {
5335 val = parseInt(val);
5337 return val;
5342 Models and collections.
5344 KodiEntities.Setting = (function(superClass) {
5345 extend(Setting, superClass);
5347 function Setting() {
5348 return Setting.__super__.constructor.apply(this, arguments);
5351 Setting.prototype.defaults = function() {
5352 var fields;
5353 fields = _.extend(this.modelDefaults, {
5354 id: 0,
5355 params: {}
5357 return this.parseFieldsToDefaults(helpers.entities.getFields(API.fields, 'small'), fields);
5360 return Setting;
5362 })(App.KodiEntities.Model);
5363 KodiEntities.SettingSectionCollection = (function(superClass) {
5364 extend(SettingSectionCollection, superClass);
5366 function SettingSectionCollection() {
5367 return SettingSectionCollection.__super__.constructor.apply(this, arguments);
5370 SettingSectionCollection.prototype.model = KodiEntities.Setting;
5372 SettingSectionCollection.prototype.methods = {
5373 read: ['Settings.GetSections']
5376 SettingSectionCollection.prototype.parse = function(resp, xhr) {
5377 var items;
5378 items = this.getResult(resp, this.options.type);
5379 return API.parseCollection(items, this.options.type);
5382 return SettingSectionCollection;
5384 })(App.KodiEntities.Collection);
5385 KodiEntities.SettingCategoryCollection = (function(superClass) {
5386 extend(SettingCategoryCollection, superClass);
5388 function SettingCategoryCollection() {
5389 return SettingCategoryCollection.__super__.constructor.apply(this, arguments);
5392 SettingCategoryCollection.prototype.model = KodiEntities.Setting;
5394 SettingCategoryCollection.prototype.methods = {
5395 read: ['Settings.GetCategories', 'level', 'section']
5398 SettingCategoryCollection.prototype.args = function() {
5399 return this.getArgs({
5400 level: API.getSettingsLevel(),
5401 section: this.argCheckOption('section', 0)
5405 SettingCategoryCollection.prototype.parse = function(resp, xhr) {
5406 var items;
5407 items = this.getResult(resp, this.options.type);
5408 return API.parseCollection(items, this.options.type);
5411 return SettingCategoryCollection;
5413 })(App.KodiEntities.Collection);
5414 KodiEntities.SettingCollection = (function(superClass) {
5415 extend(SettingCollection, superClass);
5417 function SettingCollection() {
5418 return SettingCollection.__super__.constructor.apply(this, arguments);
5421 SettingCollection.prototype.model = KodiEntities.Setting;
5423 SettingCollection.prototype.methods = {
5424 read: ['Settings.GetSettings', 'level']
5427 SettingCollection.prototype.args = function() {
5428 return this.getArgs({
5429 level: API.getSettingsLevel()
5433 SettingCollection.prototype.parse = function(resp, xhr) {
5434 var items;
5435 items = this.getResult(resp, this.options.type);
5436 return API.parseCollection(items, this.options.type);
5439 return SettingCollection;
5441 })(App.KodiEntities.Collection);
5444 Request Handlers.
5446 App.reqres.setHandler("settings:kodi:entities", function(options) {
5447 if (options == null) {
5448 options = {};
5450 return API.getCollection(options);
5452 App.reqres.setHandler("settings:kodi:filtered:entities", function(options) {
5453 if (options == null) {
5454 options = {};
5456 return API.getSettings(options.section, options.categories, function(items) {
5457 return options.callback(items);
5460 return App.commands.setHandler("settings:kodi:save:entities", function(data, callback) {
5461 if (data == null) {
5462 data = {};
5464 return API.saveSettings(data, callback);
5468 this.Kodi.module("KodiEntities", function(KodiEntities, App, Backbone, Marionette, $, _) {
5469 var API;
5470 API = {
5471 songsByIdMax: 50,
5472 fields: {
5473 minimal: ['title', 'file'],
5474 small: ['thumbnail', 'artist', 'artistid', 'album', 'albumid', 'lastplayed', 'track', 'year', 'duration'],
5475 full: ['fanart', 'genre', 'disc', 'rating', 'albumartist']
5477 getSong: function(id, options) {
5478 var artist;
5479 artist = new App.KodiEntities.Song();
5480 artist.set({
5481 songid: parseInt(id),
5482 properties: helpers.entities.getFields(API.fields, 'full')
5484 artist.fetch(options);
5485 return artist;
5487 getFilteredSongs: function(options) {
5488 var songs;
5489 songs = new KodiEntities.SongFilteredCollection();
5490 songs.fetch(helpers.entities.buildOptions(options));
5491 return songs;
5493 getCustomSongsCollection: function(type, ids, callback) {
5494 var i, id, items, options, req, results1;
5495 if (type === 'songid') {
5496 return this.getSongsByIds(ids, -1, callback);
5497 } else {
5498 items = [];
5499 options = {
5500 filter: {}
5502 req = 0;
5503 results1 = [];
5504 for (i in ids) {
5505 id = ids[i];
5506 options.filter[type] = id;
5507 options.success = function(collection) {
5508 items = items.concat(collection.toJSON());
5509 req++;
5510 if (req === ids.length) {
5511 collection = new KodiEntities.SongCustomCollection(items);
5512 return callback(collection);
5515 results1.push(this.getFilteredSongs(options));
5517 return results1;
5520 parseSongsToAlbumSongs: function(songs) {
5521 var albumid, collections, len, n, parsedRaw, song, songSet, songsRaw, year;
5522 songsRaw = songs.getRawCollection();
5523 parsedRaw = {};
5524 collections = [];
5525 for (n = 0, len = songsRaw.length; n < len; n++) {
5526 song = songsRaw[n];
5527 if (!parsedRaw[song.albumid]) {
5528 parsedRaw[song.albumid] = [];
5530 parsedRaw[song.albumid].push(song);
5532 for (albumid in parsedRaw) {
5533 songSet = parsedRaw[albumid];
5534 year = songSet[0].year ? songSet[0].year : 0;
5535 collections.push({
5536 songs: new KodiEntities.SongCustomCollection(songSet),
5537 albumid: parseInt(albumid),
5538 sort: 0 - parseInt(year)
5541 collections = _.sortBy(collections, 'sort');
5542 return collections;
5544 getSongsByIds: function(songIds, max, callback) {
5545 var cache, cacheKey, collection, commander, commands, id, items, len, model, n;
5546 if (songIds == null) {
5547 songIds = [];
5549 if (max == null) {
5550 max = -1;
5552 commander = App.request("command:kodi:controller", 'auto', 'Commander');
5553 songIds = this.getLimitIds(songIds, max);
5554 cacheKey = 'songs-' + songIds.join('-');
5555 items = [];
5556 cache = helpers.cache.get(cacheKey, false);
5557 if (cache) {
5558 collection = new KodiEntities.SongCustomCollection(cache);
5559 if (callback) {
5560 callback(collection);
5562 } else {
5563 model = new KodiEntities.Song();
5564 commands = [];
5565 for (n = 0, len = songIds.length; n < len; n++) {
5566 id = songIds[n];
5567 commands.push({
5568 method: 'AudioLibrary.GetSongDetails',
5569 params: [id, helpers.entities.getFields(API.fields, 'small')]
5572 if (commands.length > 0) {
5573 commander.multipleCommands(commands, (function(_this) {
5574 return function(resp) {
5575 var item, len1, o, ref;
5576 ref = _.flatten([resp]);
5577 for (o = 0, len1 = ref.length; o < len1; o++) {
5578 item = ref[o];
5579 items.push(model.parseModel('song', item.songdetails, item.songdetails.songid));
5581 helpers.cache.set(cacheKey, items);
5582 collection = new KodiEntities.SongCustomCollection(items);
5583 if (callback) {
5584 return callback(collection);
5587 })(this));
5590 return collection;
5592 getLimitIds: function(ids, max) {
5593 var i, id, ret;
5594 max = max === -1 ? this.songsByIdMax : max;
5595 ret = [];
5596 for (i in ids) {
5597 id = ids[i];
5598 if (i < max) {
5599 ret.push(id);
5602 return ret;
5605 KodiEntities.Song = (function(superClass) {
5606 extend(Song, superClass);
5608 function Song() {
5609 return Song.__super__.constructor.apply(this, arguments);
5612 Song.prototype.defaults = function() {
5613 var fields;
5614 fields = _.extend(this.modelDefaults, {
5615 songid: 1,
5616 artist: ''
5618 return this.parseFieldsToDefaults(helpers.entities.getFields(API.fields, 'full'), fields);
5621 Song.prototype.methods = {
5622 read: ['AudioLibrary.GetSongDetails', 'songid', 'properties']
5625 Song.prototype.parse = function(resp, xhr) {
5626 var obj;
5627 obj = resp.songdetails != null ? resp.songdetails : resp;
5628 if (resp.songdetails != null) {
5629 obj.fullyloaded = true;
5631 return this.parseModel('song', obj, obj.songid);
5634 return Song;
5636 })(App.KodiEntities.Model);
5637 KodiEntities.SongFilteredCollection = (function(superClass) {
5638 extend(SongFilteredCollection, superClass);
5640 function SongFilteredCollection() {
5641 return SongFilteredCollection.__super__.constructor.apply(this, arguments);
5644 SongFilteredCollection.prototype.model = KodiEntities.Song;
5646 SongFilteredCollection.prototype.methods = {
5647 read: ['AudioLibrary.GetSongs', 'properties', 'limits', 'sort', 'filter']
5650 SongFilteredCollection.prototype.args = function() {
5651 return this.getArgs({
5652 properties: this.argFields(helpers.entities.getFields(API.fields, 'small')),
5653 limits: this.argLimit(),
5654 sort: this.argSort("track", "ascending"),
5655 filter: this.argFilter()
5659 SongFilteredCollection.prototype.parse = function(resp, xhr) {
5660 return this.getResult(resp, 'songs');
5663 return SongFilteredCollection;
5665 })(App.KodiEntities.Collection);
5666 KodiEntities.SongCustomCollection = (function(superClass) {
5667 extend(SongCustomCollection, superClass);
5669 function SongCustomCollection() {
5670 return SongCustomCollection.__super__.constructor.apply(this, arguments);
5673 SongCustomCollection.prototype.model = KodiEntities.Song;
5675 return SongCustomCollection;
5677 })(App.KodiEntities.Collection);
5678 KodiEntities.SongSearchIndexCollection = (function(superClass) {
5679 extend(SongSearchIndexCollection, superClass);
5681 function SongSearchIndexCollection() {
5682 return SongSearchIndexCollection.__super__.constructor.apply(this, arguments);
5685 SongSearchIndexCollection.prototype.methods = {
5686 read: ['AudioLibrary.GetSongs']
5689 return SongSearchIndexCollection;
5691 })(KodiEntities.SongFilteredCollection);
5692 App.reqres.setHandler("song:entity", function(id, options) {
5693 if (options == null) {
5694 options = {};
5696 return API.getSong(id, options);
5698 App.reqres.setHandler("song:entities", function(options) {
5699 if (options == null) {
5700 options = {};
5702 return API.getFilteredSongs(options);
5704 App.reqres.setHandler("song:custom:entities", function(type, ids, callback) {
5705 return API.getCustomSongsCollection(type, ids, callback);
5707 App.reqres.setHandler("song:build:collection", function(items) {
5708 return new KodiEntities.SongCustomCollection(items);
5710 App.reqres.setHandler("song:byid:entities", function(songIds, callback) {
5711 if (songIds == null) {
5712 songIds = [];
5714 return API.getSongsByIds(songIds, -1, callback);
5716 App.reqres.setHandler("song:albumparse:entities", function(songs) {
5717 return API.parseSongsToAlbumSongs(songs);
5719 return App.reqres.setHandler("song:fields", function(type) {
5720 if (type == null) {
5721 type = 'full';
5723 return helpers.entities.getFields(API.fields, type);
5727 this.Kodi.module("KodiEntities", function(KodiEntities, App, Backbone, Marionette, $, _) {
5730 API Helpers
5732 var API;
5733 API = {
5734 fields: {
5735 minimal: ['title', 'art'],
5736 small: ['playcount', 'lastplayed', 'dateadded', 'episode', 'rating', 'year', 'file', 'genre', 'watchedepisodes', 'cast', 'studio', 'mpaa', 'tag'],
5737 full: ['imdbnumber', 'episodeguide', 'plot', 'sorttitle', 'originaltitle', 'premiered']
5739 getEntity: function(id, options) {
5740 var entity;
5741 entity = new App.KodiEntities.TVShow();
5742 entity.set({
5743 tvshowid: parseInt(id),
5744 properties: helpers.entities.getFields(API.fields, 'full')
5746 entity.fetch(options);
5747 return entity;
5749 getCollection: function(options) {
5750 var collection;
5751 collection = new KodiEntities.TVShowCollection();
5752 collection.fetch(helpers.entities.buildOptions(options));
5753 return collection;
5758 Models and collections.
5760 KodiEntities.TVShow = (function(superClass) {
5761 extend(TVShow, superClass);
5763 function TVShow() {
5764 return TVShow.__super__.constructor.apply(this, arguments);
5767 TVShow.prototype.defaults = function() {
5768 var fields;
5769 fields = _.extend(this.modelDefaults, {
5770 tvshowid: 1,
5771 tvshow: ''
5773 return this.parseFieldsToDefaults(helpers.entities.getFields(API.fields, 'full'), fields);
5776 TVShow.prototype.methods = {
5777 read: ['VideoLibrary.GetTVShowDetails', 'tvshowid', 'properties']
5780 TVShow.prototype.parse = function(resp, xhr) {
5781 var obj;
5782 obj = resp.tvshowdetails != null ? resp.tvshowdetails : resp;
5783 if (resp.tvshowdetails != null) {
5784 obj.fullyloaded = true;
5786 obj.unwatched = obj.episode - obj.watchedepisodes;
5787 return this.parseModel('tvshow', obj, obj.tvshowid);
5790 return TVShow;
5792 })(App.KodiEntities.Model);
5793 KodiEntities.TVShowCollection = (function(superClass) {
5794 extend(TVShowCollection, superClass);
5796 function TVShowCollection() {
5797 return TVShowCollection.__super__.constructor.apply(this, arguments);
5800 TVShowCollection.prototype.model = KodiEntities.TVShow;
5802 TVShowCollection.prototype.methods = {
5803 read: ['VideoLibrary.GetTVShows', 'properties', 'limits', 'sort', 'filter']
5806 TVShowCollection.prototype.args = function() {
5807 return this.getArgs({
5808 properties: this.argFields(helpers.entities.getFields(API.fields, 'small')),
5809 limits: this.argLimit(),
5810 sort: this.argSort('title', 'ascending'),
5811 filter: this.argFilter()
5815 TVShowCollection.prototype.parse = function(resp, xhr) {
5816 return this.getResult(resp, 'tvshows');
5819 return TVShowCollection;
5821 })(App.KodiEntities.Collection);
5824 Request Handlers.
5826 App.reqres.setHandler("tvshow:entity", function(id, options) {
5827 if (options == null) {
5828 options = {};
5830 return API.getEntity(id, options);
5832 App.reqres.setHandler("tvshow:entities", function(options) {
5833 if (options == null) {
5834 options = {};
5836 return API.getCollection(options);
5838 return App.reqres.setHandler("tvshow:fields", function(type) {
5839 if (type == null) {
5840 type = 'full';
5842 return helpers.entities.getFields(API.fields, type);
5846 this.Kodi.module("KodiEntities", function(KodiEntities, App, Backbone, Marionette, $, _) {
5849 API Helpers
5851 var API;
5852 API = {
5853 dictionary: {},
5854 fields: {
5855 minimal: [],
5856 small: ['method', 'description', 'thumbnail', 'params', 'permission', 'returns', 'type', 'namespace', 'methodname'],
5857 full: []
5859 getEntity: function(id, collection) {
5860 var model;
5861 model = collection.where({
5862 id: id
5863 }).shift();
5864 return model;
5866 getCollection: function(options) {
5867 var collection;
5868 if (options == null) {
5869 options = {};
5871 collection = new KodiEntities.ApiMethodCollection();
5872 collection.fetch(helpers.entities.buildOptions(options));
5873 return collection;
5875 parseCollection: function(itemsRaw, type) {
5876 var item, items, method, methodParts;
5877 if (itemsRaw == null) {
5878 itemsRaw = [];
5880 if (type == null) {
5881 type = 'method';
5883 items = [];
5884 for (method in itemsRaw) {
5885 item = itemsRaw[method];
5886 item.method = method;
5887 item.id = method;
5888 API.dictionary[item.id] = item.id;
5889 if (type === 'type') {
5890 item.params = _.extend({}, item);
5891 item.description = 'API Type';
5893 item.type = type;
5894 methodParts = method.replace('.', '[SPLIT]').split('[SPLIT]');
5895 item.namespace = methodParts[0];
5896 item.methodname = methodParts[1];
5897 items.push(item);
5899 return items;
5904 Models and collections.
5906 KodiEntities.ApiMethod = (function(superClass) {
5907 extend(ApiMethod, superClass);
5909 function ApiMethod() {
5910 return ApiMethod.__super__.constructor.apply(this, arguments);
5913 ApiMethod.prototype.defaults = function() {
5914 var fields;
5915 fields = _.extend(this.modelDefaults, {
5916 id: 1,
5917 params: {}
5919 return this.parseFieldsToDefaults(helpers.entities.getFields(API.fields, 'small'), fields);
5922 return ApiMethod;
5924 })(App.KodiEntities.Model);
5925 KodiEntities.ApiMethodCollection = (function(superClass) {
5926 extend(ApiMethodCollection, superClass);
5928 function ApiMethodCollection() {
5929 return ApiMethodCollection.__super__.constructor.apply(this, arguments);
5932 ApiMethodCollection.prototype.model = KodiEntities.ApiMethod;
5934 ApiMethodCollection.prototype.methods = {
5935 read: ['JSONRPC.Introspect', 'getdescriptions', 'getmetadata']
5938 ApiMethodCollection.prototype.args = function() {
5939 return this.getArgs({
5940 getdescriptions: true,
5941 getmetadata: true
5945 ApiMethodCollection.prototype.parse = function(resp, xhr) {
5946 var methods, types;
5947 methods = API.parseCollection(this.getResult(resp, 'methods'), 'method');
5948 types = API.parseCollection(this.getResult(resp, 'types'), 'type');
5949 return methods.concat(types);
5952 return ApiMethodCollection;
5954 })(App.KodiEntities.Collection);
5957 Request Handlers.
5959 App.reqres.setHandler("introspect:entity", function(id, collection) {
5960 return API.getEntity(id, collection);
5962 App.reqres.setHandler("introspect:entities", function(options) {
5963 if (options == null) {
5964 options = {};
5966 return API.getCollection(options);
5968 return App.reqres.setHandler("introspect:dictionary", function() {
5969 return API.dictionary;
5975 Custom saved playlists, saved in local storage
5978 this.Kodi.module("Entities", function(Entities, App, Backbone, Marionette, $, _) {
5979 var API;
5980 API = {
5981 savedFields: ['id', 'uid', 'position', 'file', 'type', 'label', 'thumbnail', 'artist', 'album', 'albumid', 'artistid', 'artistid', 'tvshowid', 'tvshow', 'year', 'rating', 'duration', 'track', 'url', 'season', 'episode', 'title'],
5982 playlistKey: 'localplaylist:list',
5983 playlistItemNamespace: 'localplaylist:item:',
5984 thumbsUpNamespace: 'thumbs:',
5985 localPlayerNamespace: 'localplayer:',
5986 getPlaylistKey: function(key) {
5987 return this.playlistItemNamespace + key;
5989 getThumbsKey: function(media) {
5990 return this.thumbsUpNamespace + media;
5992 getlocalPlayerKey: function(media) {
5993 if (media == null) {
5994 media = 'audio';
5996 return this.localPlayerNamespace + media;
5998 getListCollection: function(type) {
5999 var collection;
6000 if (type == null) {
6001 type = 'list';
6003 collection = new Entities.localPlaylistCollection();
6004 collection.fetch();
6005 collection.where({
6006 type: type
6008 return collection;
6010 addList: function(model) {
6011 var collection;
6012 collection = this.getListCollection();
6013 model.id = this.getNextId();
6014 collection.create(model);
6015 return model.id;
6017 getNextId: function() {
6018 var collection, items, lastItem, nextId;
6019 collection = API.getListCollection();
6020 items = collection.getRawCollection();
6021 if (items.length === 0) {
6022 nextId = 1;
6023 } else {
6024 lastItem = _.max(items, function(item) {
6025 return item.id;
6027 nextId = lastItem.id + 1;
6029 return nextId;
6031 getItemCollection: function(listId) {
6032 var collection;
6033 collection = new Entities.localPlaylistItemCollection([], {
6034 key: listId
6036 collection.fetch();
6037 return collection;
6039 addItemsToPlaylist: function(playlistId, collection) {
6040 var item, items, len, n, pos;
6041 if (_.isArray(collection)) {
6042 items = collection;
6043 } else {
6044 items = collection.getRawCollection();
6046 collection = this.getItemCollection(playlistId);
6047 pos = collection.length;
6048 for (n = 0, len = items.length; n < len; n++) {
6049 item = items[n];
6050 collection.create(API.getSavedModelFromSource(item, pos));
6051 pos++;
6053 return collection;
6055 getSavedModelFromSource: function(item, position) {
6056 var fieldName, idfield, len, n, newItem, ref;
6057 newItem = {};
6058 ref = this.savedFields;
6059 for (n = 0, len = ref.length; n < len; n++) {
6060 fieldName = ref[n];
6061 if (item[fieldName]) {
6062 newItem[fieldName] = item[fieldName];
6065 newItem.position = parseInt(position);
6066 idfield = item.type + 'id';
6067 newItem[idfield] = item[idfield];
6068 return newItem;
6070 clearPlaylist: function(playlistId) {
6071 var collection, model;
6072 collection = this.getItemCollection(playlistId);
6073 while (model = collection.first()) {
6074 model.destroy();
6078 Entities.localPlaylist = (function(superClass) {
6079 extend(localPlaylist, superClass);
6081 function localPlaylist() {
6082 return localPlaylist.__super__.constructor.apply(this, arguments);
6085 localPlaylist.prototype.defaults = {
6086 id: 0,
6087 name: '',
6088 media: '',
6089 type: 'list'
6092 return localPlaylist;
6094 })(Entities.Model);
6095 Entities.localPlaylistCollection = (function(superClass) {
6096 extend(localPlaylistCollection, superClass);
6098 function localPlaylistCollection() {
6099 return localPlaylistCollection.__super__.constructor.apply(this, arguments);
6102 localPlaylistCollection.prototype.model = Entities.localPlaylist;
6104 localPlaylistCollection.prototype.localStorage = new Backbone.LocalStorage(API.playlistKey);
6106 return localPlaylistCollection;
6108 })(Entities.Collection);
6109 Entities.localPlaylistItem = (function(superClass) {
6110 extend(localPlaylistItem, superClass);
6112 function localPlaylistItem() {
6113 return localPlaylistItem.__super__.constructor.apply(this, arguments);
6116 localPlaylistItem.prototype.idAttribute = "position";
6118 localPlaylistItem.prototype.defaults = function() {
6119 var f, fields, len, n, ref;
6120 fields = {};
6121 ref = API.savedFields;
6122 for (n = 0, len = ref.length; n < len; n++) {
6123 f = ref[n];
6124 fields[f] = '';
6126 return fields;
6129 return localPlaylistItem;
6131 })(Entities.Model);
6132 Entities.localPlaylistItemCollection = (function(superClass) {
6133 extend(localPlaylistItemCollection, superClass);
6135 function localPlaylistItemCollection() {
6136 return localPlaylistItemCollection.__super__.constructor.apply(this, arguments);
6139 localPlaylistItemCollection.prototype.model = Entities.localPlaylistItem;
6141 localPlaylistItemCollection.prototype.initialize = function(model, options) {
6142 return this.localStorage = new Backbone.LocalStorage(API.getPlaylistKey(options.key));
6145 return localPlaylistItemCollection;
6147 })(Entities.Collection);
6150 Saved Playlists
6152 App.reqres.setHandler("localplaylist:add:entity", function(name, media, type) {
6153 if (type == null) {
6154 type = 'list';
6156 return API.addList({
6157 name: name,
6158 media: media,
6159 type: type
6162 App.commands.setHandler("localplaylist:remove:entity", function(id) {
6163 var collection, model;
6164 collection = API.getListCollection();
6165 model = collection.findWhere({
6166 id: parseInt(id)
6168 return model.destroy();
6170 App.reqres.setHandler("localplaylist:entities", function() {
6171 return API.getListCollection();
6173 App.commands.setHandler("localplaylist:clear:entities", function(playlistId) {
6174 return API.clearPlaylist(playlistId);
6176 App.reqres.setHandler("localplaylist:entity", function(id) {
6177 var collection;
6178 collection = API.getListCollection();
6179 return collection.findWhere({
6180 id: parseInt(id)
6183 App.reqres.setHandler("localplaylist:item:entities", function(playlistId) {
6184 return API.getItemCollection(playlistId);
6186 App.reqres.setHandler("localplaylist:item:add:entities", function(playlistId, collection) {
6187 return API.addItemsToPlaylist(playlistId, collection);
6189 App.reqres.setHandler("localplaylist:item:updateorder", function(playlistId, order) {
6190 var collection, model, newList, newPos, oldPos;
6191 newList = [];
6192 collection = API.getItemCollection(playlistId);
6193 for (newPos in order) {
6194 oldPos = order[newPos];
6195 model = collection.findWhere({
6196 position: parseInt(oldPos)
6197 }).toJSON();
6198 model.position = newPos;
6199 model.id = newPos;
6200 newList.push(model);
6202 API.clearPlaylist(playlistId);
6203 return API.addItemsToPlaylist(playlistId, newList);
6207 Thumbs up lists
6209 App.reqres.setHandler("thumbsup:toggle:entity", function(model) {
6210 var collection, existing, media;
6211 media = model.get('type');
6212 collection = API.getItemCollection(API.getThumbsKey(media));
6213 existing = collection.findWhere({
6214 id: model.get('id')
6216 if (existing) {
6217 existing.destroy();
6218 } else {
6219 collection.create(API.getSavedModelFromSource(model.attributes, model.get('id')));
6221 return collection;
6223 App.reqres.setHandler("thumbsup:get:entities", function(media) {
6224 return API.getItemCollection(API.getThumbsKey(media));
6226 App.reqres.setHandler("thumbsup:check", function(model) {
6227 var collection, existing;
6228 if (model != null) {
6229 collection = API.getItemCollection(API.getThumbsKey(model.get('type')));
6230 existing = collection.findWhere({
6231 id: model.get('id')
6233 return _.isObject(existing);
6234 } else {
6235 return false;
6240 Local player lists
6242 App.reqres.setHandler("localplayer:get:entities", function(media) {
6243 if (media == null) {
6244 media = 'audio';
6246 return API.getItemCollection(API.getlocalPlayerKey(media));
6248 App.commands.setHandler("localplayer:clear:entities", function(media) {
6249 if (media == null) {
6250 media = 'audio';
6252 return API.clearPlaylist(API.getlocalPlayerKey(media));
6254 return App.reqres.setHandler("localplayer:item:add:entities", function(collection, media) {
6255 if (media == null) {
6256 media = 'audio';
6258 return API.addItemsToPlaylist(API.getlocalPlayerKey(media), collection);
6262 this.Kodi.module("Entities", function(Entities, App, Backbone, Marionette, $, _) {
6263 var API;
6264 API = {
6265 localKey: 'mainNav',
6266 getItems: function() {
6267 var items, navCollection;
6268 navCollection = this.getLocalCollection();
6269 items = navCollection.getRawCollection();
6270 if (items.length === 0) {
6271 items = this.getDefaultItems();
6273 return items;
6275 getDefaultItems: function(onlyVisible) {
6276 var nav;
6277 if (onlyVisible == null) {
6278 onlyVisible = true;
6280 nav = [];
6281 nav.push({
6282 id: 1,
6283 title: tr("Music"),
6284 path: 'music',
6285 icon: 'mdi-av-my-library-music',
6286 classes: 'nav-music',
6287 parent: 0
6289 nav.push({
6290 id: 2,
6291 title: tr("Music"),
6292 path: 'music',
6293 icon: '',
6294 classes: '',
6295 parent: 1
6297 nav.push({
6298 id: 6,
6299 title: tr("Genres"),
6300 path: 'music/genres',
6301 icon: '',
6302 classes: '',
6303 parent: 1
6305 nav.push({
6306 id: 7,
6307 title: tr("Top music"),
6308 path: 'music/top',
6309 icon: '',
6310 classes: '',
6311 parent: 1
6313 nav.push({
6314 id: 3,
6315 title: tr("Artists"),
6316 path: 'music/artists',
6317 icon: '',
6318 classes: '',
6319 parent: 1
6321 nav.push({
6322 id: 4,
6323 title: tr("Albums"),
6324 path: 'music/albums',
6325 icon: '',
6326 classes: '',
6327 parent: 1
6329 nav.push({
6330 id: 8,
6331 title: tr("Videos"),
6332 path: 'music/videos',
6333 icon: '',
6334 classes: '',
6335 parent: 1
6337 nav.push({
6338 id: 11,
6339 title: tr("Movies"),
6340 path: 'movies/recent',
6341 icon: 'mdi-av-movie',
6342 classes: 'nav-movies',
6343 parent: 0
6345 nav.push({
6346 id: 12,
6347 title: tr("Movies"),
6348 path: 'movies/recent',
6349 icon: '',
6350 classes: '',
6351 parent: 11
6353 nav.push({
6354 id: 13,
6355 title: tr("All movies"),
6356 path: 'movies',
6357 icon: '',
6358 classes: '',
6359 parent: 11
6361 nav.push({
6362 id: 21,
6363 title: tr("TV shows"),
6364 path: 'tvshows/recent',
6365 icon: 'mdi-hardware-tv',
6366 classes: 'nav-tv',
6367 parent: 0
6369 nav.push({
6370 id: 22,
6371 title: tr("TV shows"),
6372 path: 'tvshows/recent',
6373 icon: '',
6374 classes: '',
6375 parent: 21
6377 nav.push({
6378 id: 23,
6379 title: tr("All TV shows"),
6380 path: 'tvshows',
6381 icon: '',
6382 classes: '',
6383 parent: 21
6385 nav.push({
6386 id: 31,
6387 title: tr("Browser"),
6388 path: 'browser',
6389 icon: 'mdi-action-view-list',
6390 classes: 'nav-browser',
6391 parent: 0
6393 nav.push({
6394 id: 81,
6395 title: tr("PVR"),
6396 path: 'pvr/tv',
6397 icon: 'mdi-action-settings-input-antenna',
6398 classes: 'pvr-link',
6399 parent: 0,
6400 visibility: "addon:pvr:enabled"
6402 nav.push({
6403 id: 82,
6404 title: tr("TV Channels"),
6405 path: 'pvr/tv',
6406 icon: '',
6407 classes: '',
6408 parent: 81
6410 nav.push({
6411 id: 83,
6412 title: tr("Radio Stations"),
6413 path: 'pvr/radio',
6414 icon: '',
6415 classes: '',
6416 parent: 81
6418 nav.push({
6419 id: 84,
6420 title: tr("Recordings"),
6421 path: 'pvr/recordings',
6422 icon: '',
6423 classes: '',
6424 parent: 81
6426 nav.push({
6427 id: 71,
6428 title: tr("Add-ons"),
6429 path: 'addons/all',
6430 icon: 'mdi-action-extension',
6431 classes: 'nav-addons',
6432 parent: 0
6434 nav.push({
6435 id: 72,
6436 title: tr("all"),
6437 path: 'addons/all',
6438 icon: '',
6439 classes: '',
6440 parent: 71
6442 nav.push({
6443 id: 73,
6444 title: tr("video"),
6445 path: 'addons/video',
6446 icon: '',
6447 classes: '',
6448 parent: 71
6450 nav.push({
6451 id: 74,
6452 title: tr("audio"),
6453 path: 'addons/audio',
6454 icon: '',
6455 classes: '',
6456 parent: 71
6458 nav.push({
6459 id: 76,
6460 title: tr("executable"),
6461 path: 'addons/executable',
6462 icon: '',
6463 classes: '',
6464 parent: 71
6466 nav.push({
6467 id: 77,
6468 title: tr("settings"),
6469 path: 'settings/addons',
6470 icon: '',
6471 classes: '',
6472 parent: 71
6474 nav.push({
6475 id: 41,
6476 title: tr("Thumbs up"),
6477 path: 'thumbsup',
6478 icon: 'mdi-action-thumb-up',
6479 classes: 'nav-thumbs-up',
6480 parent: 0
6482 nav.push({
6483 id: 42,
6484 title: tr("Playlists"),
6485 path: 'playlists',
6486 icon: 'mdi-action-assignment',
6487 classes: 'playlists',
6488 parent: 0
6490 nav.push({
6491 id: 51,
6492 title: tr("Settings"),
6493 path: 'settings/web',
6494 icon: 'mdi-action-settings',
6495 classes: 'nav-settings',
6496 parent: 0
6498 nav.push({
6499 id: 52,
6500 title: tr("Web interface"),
6501 path: 'settings/web',
6502 icon: '',
6503 classes: '',
6504 parent: 51
6506 nav.push({
6507 id: 54,
6508 title: tr("Main Menu"),
6509 path: 'settings/nav',
6510 icon: '',
6511 classes: '',
6512 parent: 51
6514 nav.push({
6515 id: 53,
6516 title: tr("Add-ons"),
6517 path: 'settings/addons',
6518 icon: '',
6519 classes: '',
6520 parent: 51
6522 nav.push({
6523 id: 55,
6524 title: tr("Search"),
6525 path: 'settings/search',
6526 icon: '',
6527 classes: '',
6528 parent: 51
6530 nav.push({
6531 id: 61,
6532 title: tr("Help"),
6533 path: 'help',
6534 icon: 'mdi-action-help',
6535 classes: 'nav-help',
6536 parent: 0
6538 if (onlyVisible) {
6539 return this.checkVisibility(nav);
6540 } else {
6541 return nav;
6544 checkVisibility: function(items) {
6545 var item, len, n, newItems;
6546 newItems = [];
6547 for (n = 0, len = items.length; n < len; n++) {
6548 item = items[n];
6549 if (item.visibility != null) {
6550 if (App.request(item.visibility)) {
6551 newItems.push(item);
6553 } else {
6554 newItems.push(item);
6557 return newItems;
6559 getLocalCollection: function() {
6560 var collection;
6561 collection = new Entities.LocalNavMainCollection([], {
6562 key: this.localKey
6564 collection.fetch();
6565 return collection;
6567 getStructure: function() {
6568 var navCollection, navParsed;
6569 navParsed = this.sortStructure(this.getItems());
6570 navCollection = new Entities.NavMainCollection(navParsed);
6571 return navCollection;
6573 getChildStructure: function(parentId) {
6574 var childItems, nav, parent;
6575 nav = this.getDefaultItems(false);
6576 parent = _.findWhere(nav, {
6577 id: parentId
6579 childItems = _.where(nav, {
6580 parent: parentId
6582 parent.items = new Entities.NavMainCollection(childItems);
6583 return new Entities.NavMain(parent);
6585 sortStructure: function(structure) {
6586 var children, i, len, model, n, name1, newParents;
6587 children = {};
6588 for (n = 0, len = structure.length; n < len; n++) {
6589 model = structure[n];
6590 if (!((model.path != null) && model.parent !== 0)) {
6591 continue;
6593 model.title = t.gettext(model.title);
6594 if (children[name1 = model.parent] == null) {
6595 children[name1] = [];
6597 children[model.parent].push(model);
6599 newParents = [];
6600 for (i in structure) {
6601 model = structure[i];
6602 if (model.path != null) {
6603 if (model.parent === 0) {
6604 model.children = children[model.id];
6605 newParents.push(model);
6609 return newParents;
6611 getIdfromPath: function(path) {
6612 var model;
6613 model = _.findWhere(this.getDefaultItems(), {
6614 path: path
6616 if (model != null) {
6617 return model.id;
6618 } else {
6619 return 1;
6622 saveLocal: function(items) {
6623 var collection, i, item;
6624 collection = this.clearLocal();
6625 for (i in items) {
6626 item = items[i];
6627 collection.create(item);
6629 return collection;
6631 clearLocal: function() {
6632 var collection, model;
6633 collection = this.getLocalCollection();
6634 while (model = collection.first()) {
6635 model.destroy();
6637 return collection;
6640 Entities.NavMain = (function(superClass) {
6641 extend(NavMain, superClass);
6643 function NavMain() {
6644 return NavMain.__super__.constructor.apply(this, arguments);
6647 NavMain.prototype.defaults = {
6648 id: 0,
6649 title: 'Untitled',
6650 path: '',
6651 description: '',
6652 icon: '',
6653 classes: '',
6654 parent: 0,
6655 children: []
6658 return NavMain;
6660 })(App.Entities.Model);
6661 Entities.NavMainCollection = (function(superClass) {
6662 extend(NavMainCollection, superClass);
6664 function NavMainCollection() {
6665 return NavMainCollection.__super__.constructor.apply(this, arguments);
6668 NavMainCollection.prototype.model = Entities.NavMain;
6670 return NavMainCollection;
6672 })(App.Entities.Collection);
6673 Entities.LocalNavMainCollection = (function(superClass) {
6674 extend(LocalNavMainCollection, superClass);
6676 function LocalNavMainCollection() {
6677 return LocalNavMainCollection.__super__.constructor.apply(this, arguments);
6680 LocalNavMainCollection.prototype.model = Entities.NavMain;
6682 LocalNavMainCollection.prototype.localStorage = new Backbone.LocalStorage(API.localKey);
6684 return LocalNavMainCollection;
6686 })(App.Entities.Collection);
6687 App.reqres.setHandler("navMain:entities", function(parent) {
6688 var parentId;
6689 if (parent == null) {
6690 parent = 'all';
6692 if (parent === 'all') {
6693 return API.getStructure();
6694 } else {
6695 parentId = API.getIdfromPath(parent);
6696 return API.getChildStructure(parentId);
6699 App.reqres.setHandler("navMain:array:entities", function(items) {
6700 var i, item;
6701 for (i in items) {
6702 item = items[i];
6703 items[i].id = item.path;
6705 return new Entities.NavMainCollection(items);
6707 App.reqres.setHandler("navMain:update:entities", function(items) {
6708 return API.saveLocal(items);
6710 return App.reqres.setHandler("navMain:update:defaults", function(items) {
6711 return API.clearLocal();
6715 this.Kodi.module("Entities", function(Entities, App, Backbone, Marionette, $, _) {
6716 var API;
6717 API = {
6718 localKey: 'searchAddons',
6719 getLocalCollection: function() {
6720 var collection;
6721 collection = new Entities.LocalSearchAddonsCollection([], {
6722 key: this.localKey
6724 collection.fetch();
6725 return collection;
6727 saveLocal: function(items) {
6728 var collection, i, item;
6729 collection = this.clearLocal();
6730 for (i in items) {
6731 item = items[i];
6732 collection.create(item);
6734 return collection;
6736 clearLocal: function() {
6737 var collection, model;
6738 collection = this.getLocalCollection();
6739 while (model = collection.first()) {
6740 model.destroy();
6742 return collection;
6745 Entities.SearchAddons = (function(superClass) {
6746 extend(SearchAddons, superClass);
6748 function SearchAddons() {
6749 return SearchAddons.__super__.constructor.apply(this, arguments);
6752 SearchAddons.prototype.defaults = {
6753 id: '',
6754 url: '',
6755 title: 'Untitled',
6756 media: 'music'
6759 return SearchAddons;
6761 })(App.Entities.Model);
6762 Entities.LocalSearchAddonsCollection = (function(superClass) {
6763 extend(LocalSearchAddonsCollection, superClass);
6765 function LocalSearchAddonsCollection() {
6766 return LocalSearchAddonsCollection.__super__.constructor.apply(this, arguments);
6769 LocalSearchAddonsCollection.prototype.model = Entities.SearchAddons;
6771 LocalSearchAddonsCollection.prototype.localStorage = new Backbone.LocalStorage(API.localKey);
6773 return LocalSearchAddonsCollection;
6775 })(App.Entities.Collection);
6776 App.reqres.setHandler("searchAddons:entities", function(items) {
6777 return API.getLocalCollection();
6779 App.reqres.setHandler("searchAddons:update:entities", function(items) {
6780 return API.saveLocal(items);
6782 return App.reqres.setHandler("searchAddons:update:defaults", function() {
6783 return API.clearLocal();
6787 this.Kodi.module("Controllers", function(Controllers, App, Backbone, Marionette, $, _) {
6788 return Controllers.Base = (function(superClass) {
6789 extend(Base, superClass);
6791 Base.prototype.params = {};
6793 function Base(options) {
6794 if (options == null) {
6795 options = {};
6797 Base.__super__.constructor.call(this, options);
6798 this.region = options.region || App.request("default:region");
6799 this.params = helpers.url.params();
6800 this._instance_id = _.uniqueId("controller");
6801 App.execute("register:instance", this, this._instance_id);
6804 Base.prototype.close = function() {
6805 var args;
6806 args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
6807 delete this.region;
6808 delete this.options;
6809 Base.__super__.close.call(this, args);
6810 return App.execute("unregister:instance", this, this._instance_id);
6813 Base.prototype.show = function(view) {
6814 this.listenTo(view, "close", this.close);
6815 return this.region.show(view);
6818 return Base;
6820 })(Backbone.Marionette.Controller);
6823 this.Kodi.module("Router", function(Router, App, Backbone, Marionette, $, _) {
6824 return Router.Base = (function(superClass) {
6825 extend(Base, superClass);
6827 function Base() {
6828 return Base.__super__.constructor.apply(this, arguments);
6831 Base.prototype.before = function(route, params) {
6832 App.execute("loading:show:page");
6833 return App.execute("selected:clear:items");
6836 Base.prototype.after = function(route, params) {
6837 return this.setBodyClasses();
6840 Base.prototype.setBodyClasses = function() {
6841 var $body, section;
6842 $body = App.getRegion('root').$el;
6843 $body.removeClassRegex(/^section-/);
6844 $body.removeClassRegex(/^page-/);
6845 section = helpers.url.arg(0);
6846 if (section === '') {
6847 section = 'home';
6849 $body.addClass('section-' + section);
6850 return $body.addClass('page-' + helpers.url.arg().join('-'));
6853 return Base;
6855 })(Marionette.AppRouter);
6858 this.Kodi.module("Views", function(Views, App, Backbone, Marionette, $, _) {
6859 return Views.CollectionView = (function(superClass) {
6860 extend(CollectionView, superClass);
6862 function CollectionView() {
6863 return CollectionView.__super__.constructor.apply(this, arguments);
6866 CollectionView.prototype.itemViewEventPrefix = "childview";
6868 CollectionView.prototype.onShow = function() {
6869 return $("img.lazy").lazyload({
6870 threshold: 200
6874 return CollectionView;
6876 })(Backbone.Marionette.CollectionView);
6879 this.Kodi.module("Views", function(Views, App, Backbone, Marionette, $, _) {
6880 return Views.CompositeView = (function(superClass) {
6881 extend(CompositeView, superClass);
6883 function CompositeView() {
6884 return CompositeView.__super__.constructor.apply(this, arguments);
6887 CompositeView.prototype.itemViewEventPrefix = "childview";
6889 return CompositeView;
6891 })(Backbone.Marionette.CompositeView);
6894 this.Kodi.module("Views", function(Views, App, Backbone, Marionette, $, _) {
6895 return Views.ItemView = (function(superClass) {
6896 extend(ItemView, superClass);
6898 function ItemView() {
6899 return ItemView.__super__.constructor.apply(this, arguments);
6902 ItemView.prototype.onShow = function() {
6903 return this.menuBlur();
6906 ItemView.prototype.menuBlur = function() {
6907 $('.dropdown', this.$el).on('show.bs.dropdown', (function(_this) {
6908 return function() {
6909 return _this.$el.addClass('menu-open');
6911 })(this));
6912 $('.dropdown', this.$el).on('hide.bs.dropdown', (function(_this) {
6913 return function() {
6914 return _this.$el.removeClass('menu-open');
6916 })(this));
6917 return $('.dropdown', this.$el).on('click', function() {
6918 return $(this).removeClass('open').trigger('hide.bs.dropdown');
6922 ItemView.prototype.toggleWatched = function(e) {
6923 return this.trigger("toggle:watched", {
6924 view: this
6928 ItemView.prototype.watchedAttributes = function(baseClass) {
6929 var classes;
6930 if (baseClass == null) {
6931 baseClass = '';
6933 classes = [baseClass];
6934 if (App.request("thumbsup:check", this.model)) {
6935 classes.push('thumbs-up');
6937 if (helpers.entities.isWatched(this.model)) {
6938 classes.push('is-watched');
6940 return {
6941 "class": classes.join(' ')
6945 ItemView.prototype.toggleSelect = function(e) {
6946 var op;
6947 if (e.ctrlKey || e.metaKey) {
6948 e.preventDefault();
6949 if (!this.$el.hasClass('prevent-select') && helpers.url.arg(0) !== 'thumbsup') {
6950 this.$el.toggleClass('selected');
6951 op = this.$el.hasClass('selected') ? 'add' : 'remove';
6952 return App.execute("selected:update:items", op, this.model.toJSON());
6957 return ItemView;
6959 })(Backbone.Marionette.ItemView);
6962 this.Kodi.module("Views", function(Views, App, Backbone, Marionette, $, _) {
6963 return Views.LayoutView = (function(superClass) {
6964 extend(LayoutView, superClass);
6966 function LayoutView() {
6967 return LayoutView.__super__.constructor.apply(this, arguments);
6970 LayoutView.prototype.onShow = function() {
6971 return App.execute("ui:dropdown:bind:close", this.$el);
6974 return LayoutView;
6976 })(Backbone.Marionette.LayoutView);
6979 this.Kodi.module("Views", function(Views, App, Backbone, Marionette, $, _) {
6980 var _remove;
6981 _remove = Marionette.View.prototype.remove;
6982 return _.extend(Marionette.View.prototype, {
6983 themeLink: function(name, url, options) {
6984 var attrs;
6985 if (options == null) {
6986 options = {};
6988 _.defaults(options, {
6989 external: false,
6990 className: ''
6992 attrs = {};
6993 if (options.external) {
6994 attrs.target = '_blank';
6995 attrs.href = url;
6996 } else {
6997 attrs.href = '#' + url;
6999 if (options.className !== '') {
7000 attrs["class"] = options.className;
7002 return $("<a>").attr(attrs).text(name).wrap('<div/>').parent().html();
7004 themeTag: function(el, attrs, value) {
7005 return $("<" + el + ">").attr(attrs).html(value).wrap('<div/>').parent().html();
7007 formatText: function(text, addInLineBreaks) {
7008 var res;
7009 if (addInLineBreaks == null) {
7010 addInLineBreaks = false;
7012 res = XBBCODE.process({
7013 text: text,
7014 removeMisalignedTags: true,
7015 addInLineBreaks: addInLineBreaks
7017 if (res.error === !false) {
7018 helpers.debug.msg('formatText error: ' + res.errorQueue.join(', '), 'warning', res);
7020 return res.html;
7022 populateMenu: function(type) {
7023 var baseSelector, key, menu, ref, selector, val;
7024 if (type == null) {
7025 type = '';
7027 menu = '';
7028 baseSelector = 'dropdown-menu';
7029 if (this.model.get('menu')) {
7030 ref = this.model.get('menu');
7031 for (key in ref) {
7032 val = ref[key];
7033 if (key.lastIndexOf('divider', 0) === 0) {
7034 key = 'divider';
7036 menu += this.themeTag('li', {
7037 "class": key
7038 }, val);
7040 selector = type !== '' ? type + ' .' + baseSelector : baseSelector;
7041 return this.$el.find('.' + selector).html(menu);
7044 populateModelMenu: function() {
7045 return this.populateMenu();
7047 populateSetMenu: function() {
7048 return this.populateMenu('set__actions');
7053 this.Kodi.module("Views", function(Views, App, Backbone, Marionette, $, _) {
7054 return Views.VirtualListView = (function(superClass) {
7055 extend(VirtualListView, superClass);
7057 function VirtualListView() {
7058 return VirtualListView.__super__.constructor.apply(this, arguments);
7061 VirtualListView.prototype.originalCollection = {};
7063 VirtualListView.prototype.preload = 20;
7065 VirtualListView.prototype.originalChildView = {};
7067 VirtualListView.prototype.buffer = 30;
7069 VirtualListView.prototype.cardSelector = '.card';
7071 VirtualListView.prototype.animateFrameTrigger = "ui:animate:stop";
7073 VirtualListView.prototype.placeHolderViewName = 'CardViewPlaceholder';
7075 VirtualListView.prototype.addChild = function(child, ChildView, index) {
7076 if (index > this.preload) {
7077 ChildView = App.Views[this.placeHolderViewName];
7079 return Backbone.Marionette.CollectionView.prototype.addChild.apply(this, arguments);
7082 VirtualListView.prototype.initialize = function() {
7083 this.originalChildView = this.getOption('childView');
7084 this.placeholderChildView = App.Views[this.placeHolderViewName];
7085 return App.vent.on(this.animateFrameTrigger, (function(_this) {
7086 return function() {
7087 return _this.renderItemsInViewport();
7089 })(this));
7092 VirtualListView.prototype.onRender = function() {
7093 return this.renderItemsInViewport();
7096 VirtualListView.prototype.renderItemsInViewport = function() {
7097 var $cards, max, min, n, results1, visibleIndexes, visibleRange;
7098 $cards = $(this.cardSelector, this.$el);
7099 visibleIndexes = [];
7100 $cards.each((function(_this) {
7101 return function(i, d) {
7102 if ($cards.length <= _this.preload || $(d).visible(true)) {
7103 return visibleIndexes.push(i);
7106 })(this));
7107 visibleRange = [];
7108 if (visibleIndexes.length > 0) {
7109 min = _.min(visibleIndexes);
7110 max = _.max(visibleIndexes);
7111 min = (min - this.buffer) < 0 ? 0 : min - this.buffer;
7112 max = (max + this.buffer) >= $cards.length ? $cards.length - 1 : max + this.buffer;
7113 visibleRange = (function() {
7114 results1 = [];
7115 for (var n = min; min <= max ? n <= max : n >= max; min <= max ? n++ : n--){ results1.push(n); }
7116 return results1;
7117 }).apply(this);
7119 return $cards.each((function(_this) {
7120 return function(i, d) {
7121 if ($(d).hasClass('ph') && helpers.global.inArray(i, visibleRange)) {
7122 return $(d).replaceWith(_this.getRenderedChildView($(d).data('model'), _this.originalChildView, i));
7123 } else if (!$(d).hasClass('ph') && !helpers.global.inArray(i, visibleRange)) {
7124 return $(d).replaceWith(_this.getRenderedChildView($(d).data('model'), _this.placeholderChildView, i));
7127 })(this));
7130 VirtualListView.prototype.getRenderedChildView = function(child, ChildView, index) {
7131 var childViewOptions, view;
7132 childViewOptions = this.getOption('childViewOptions');
7133 childViewOptions = Marionette._getValue(childViewOptions, this, [child, index]);
7134 view = this.buildChildView(child, ChildView, childViewOptions);
7135 this.proxyChildEvents(view);
7136 return view.render().$el;
7139 VirtualListView.prototype.events = {
7140 "click a": "storeScroll"
7143 VirtualListView.prototype.storeScroll = function() {
7144 return helpers.backscroll.setLast();
7147 VirtualListView.prototype.onShow = function() {
7148 return helpers.backscroll.scrollToLast();
7151 return VirtualListView;
7153 })(Views.CollectionView);
7156 this.Kodi.module("Views", function(Views, App, Backbone, Marionette, $, _) {
7157 Views.CardView = (function(superClass) {
7158 extend(CardView, superClass);
7160 function CardView() {
7161 return CardView.__super__.constructor.apply(this, arguments);
7164 CardView.prototype.template = "views/card/card";
7166 CardView.prototype.tagName = "li";
7168 CardView.prototype.events = {
7169 "click .dropdown > i": "populateModelMenu",
7170 "click .thumbs": "toggleThumbs",
7171 "click": "toggleSelect"
7174 CardView.prototype.modelEvents = {
7175 'change': 'modelChange'
7178 CardView.prototype.toggleThumbs = function() {
7179 App.request("thumbsup:toggle:entity", this.model);
7180 return this.$el.toggleClass('thumbs-up');
7183 CardView.prototype.attributes = function() {
7184 var classes;
7185 classes = ['card', 'card-loaded'];
7186 if (App.request("thumbsup:check", this.model)) {
7187 classes.push('thumbs-up');
7189 return {
7190 "class": classes.join(' ')
7194 CardView.prototype.onBeforeRender = function() {
7195 if (this.model.get('labelHtml') == null) {
7196 this.model.set('labelHtml', this.model.escape('label'));
7198 if (this.model.get('subtitleHtml') == null) {
7199 return this.model.set('subtitleHtml', this.model.escape('subtitle'));
7203 CardView.prototype.onRender = function() {
7204 return this.$el.data('model', this.model);
7207 CardView.prototype.onShow = function() {
7208 return $('.dropdown', this.$el).on('click', function() {
7209 return $(this).removeClass('open').trigger('hide.bs.dropdown');
7213 CardView.prototype.makeLinksExternal = function() {
7214 return $('.title, .thumb', this.$el).attr('href', this.model.get('url')).attr('target', '_blank');
7217 CardView.prototype.modelChange = function() {
7218 if (_.isFunction(this.setMeta)) {
7219 this.setMeta();
7221 return this.render();
7224 return CardView;
7226 })(App.Views.ItemView);
7227 return Views.CardViewPlaceholder = (function(superClass) {
7228 extend(CardViewPlaceholder, superClass);
7230 function CardViewPlaceholder() {
7231 return CardViewPlaceholder.__super__.constructor.apply(this, arguments);
7234 CardViewPlaceholder.prototype.template = "views/card/card_placeholder";
7236 CardViewPlaceholder.prototype.attributes = function() {
7237 return {
7238 "class": 'card ph'
7242 CardViewPlaceholder.prototype.onRender = function() {
7243 return this.$el.data('model', this.model);
7246 return CardViewPlaceholder;
7248 })(App.Views.ItemView);
7251 this.Kodi.module("Views", function(Views, App, Backbone, Marionette, $, _) {
7252 return Views.DetailsItem = (function(superClass) {
7253 extend(DetailsItem, superClass);
7255 function DetailsItem() {
7256 return DetailsItem.__super__.constructor.apply(this, arguments);
7259 DetailsItem.prototype.events = {
7260 "click .watched": "toggleWatched",
7261 "click .internal-search li": "internalSearch",
7262 "click .external-search li": "externalSearch",
7263 "click .youtube-search": "youtubeSearch"
7266 DetailsItem.prototype.modelEvents = {
7267 'change': 'modelChange'
7270 DetailsItem.prototype.modelChange = function() {
7271 return this.render();
7274 DetailsItem.prototype.onRender = function() {
7275 if (this.model.get('fanart')) {
7276 this.$el.closest('.detail-container').find('.region-details-fanart').css('background-image', 'url(' + this.model.get('fanart') + ')');
7278 return $('.description', this.$el).attr('title', tr('Click for more')).on('click', function(e) {
7279 return $(this).toggleClass('expanded');
7283 DetailsItem.prototype.internalSearch = function(e) {
7284 var $li;
7285 $li = $(e.target);
7286 return App.execute('search:go', 'internal', $li.data('query'), $li.data('type'));
7289 DetailsItem.prototype.externalSearch = function(e) {
7290 var $li;
7291 $li = $(e.target);
7292 return App.execute('search:go', 'external', $li.data('query'), $li.data('type'));
7295 DetailsItem.prototype.youtubeSearch = function(e) {
7296 var $li;
7297 $li = $(e.target);
7298 return App.execute("youtube:search:popup", $li.data('query'));
7301 return DetailsItem;
7303 })(App.Views.ItemView);
7306 this.Kodi.module("Views", function(Views, App, Backbone, Marionette, $, _) {
7307 Views.EmptyViewPage = (function(superClass) {
7308 extend(EmptyViewPage, superClass);
7310 function EmptyViewPage() {
7311 return EmptyViewPage.__super__.constructor.apply(this, arguments);
7314 EmptyViewPage.prototype.template = "views/empty/empty_page";
7316 EmptyViewPage.prototype.regions = {
7317 regionEmptyContent: ".empty--page"
7320 return EmptyViewPage;
7322 })(App.Views.ItemView);
7323 return Views.EmptyViewResults = (function(superClass) {
7324 extend(EmptyViewResults, superClass);
7326 function EmptyViewResults() {
7327 return EmptyViewResults.__super__.constructor.apply(this, arguments);
7330 EmptyViewResults.prototype.template = "views/empty/empty_results";
7332 EmptyViewResults.prototype.regions = {
7333 regionEmptyContent: ".empty-result"
7336 EmptyViewResults.prototype.onRender = function() {
7337 if (this.options && this.options.emptyKey) {
7338 $('.empty-key', this.$el).text(tr(this.options.emptyKey));
7340 if (this.options && this.options.emptyBackUrl) {
7341 return $('.back-link', this.$el).html(this.themeLink(tr('Go back'), this.options.emptyBackUrl));
7345 return EmptyViewResults;
7347 })(App.Views.ItemView);
7350 this.Kodi.module("Views", function(Views, App, Backbone, Marionette, $, _) {
7351 Views.LayoutWithSidebarFirstView = (function(superClass) {
7352 extend(LayoutWithSidebarFirstView, superClass);
7354 function LayoutWithSidebarFirstView() {
7355 return LayoutWithSidebarFirstView.__super__.constructor.apply(this, arguments);
7358 LayoutWithSidebarFirstView.prototype.template = "views/layouts/layout_with_sidebar_first";
7360 LayoutWithSidebarFirstView.prototype.regions = {
7361 regionSidebarFirst: ".region-first-primary",
7362 regionContent: ".region-content"
7365 LayoutWithSidebarFirstView.prototype.events = {
7366 "click .region-first-toggle": "toggleRegionFirst"
7369 LayoutWithSidebarFirstView.prototype.toggleRegionFirst = function() {
7370 return this.$el.toggleClass('region-first-open');
7373 LayoutWithSidebarFirstView.prototype.appendSidebarView = function(viewId, appendView) {
7374 $('.region-first-secondary', this.$el).append('<div id="' + viewId + '">');
7375 this.regionManager.addRegion(viewId, '#' + viewId);
7376 return this[viewId].show(appendView);
7379 return LayoutWithSidebarFirstView;
7381 })(App.Views.LayoutView);
7382 Views.LayoutWithHeaderView = (function(superClass) {
7383 extend(LayoutWithHeaderView, superClass);
7385 function LayoutWithHeaderView() {
7386 return LayoutWithHeaderView.__super__.constructor.apply(this, arguments);
7389 LayoutWithHeaderView.prototype.template = "views/layouts/layout_with_header";
7391 LayoutWithHeaderView.prototype.regions = {
7392 regionHeader: ".region-header",
7393 regionContentTop: ".region-content-top",
7394 regionContent: ".region-content"
7397 return LayoutWithHeaderView;
7399 })(App.Views.LayoutView);
7400 return Views.LayoutDetailsHeaderView = (function(superClass) {
7401 extend(LayoutDetailsHeaderView, superClass);
7403 function LayoutDetailsHeaderView() {
7404 return LayoutDetailsHeaderView.__super__.constructor.apply(this, arguments);
7407 LayoutDetailsHeaderView.prototype.template = "views/layouts/layout_details_header";
7409 LayoutDetailsHeaderView.prototype.regions = {
7410 regionSide: ".region-details-side",
7411 regionTitle: ".region-details-title",
7412 regionMeta: ".region-details-meta",
7413 regionMetaSideFirst: ".region-details-meta-side-first",
7414 regionMetaSideSecond: ".region-details-meta-side-second",
7415 regionMetaBelow: ".region-details-meta-below",
7416 regionFanart: ".region-details-fanart"
7419 LayoutDetailsHeaderView.prototype.onRender = function() {
7420 return helpers.ui.getSwatch(this.model.get('thumbnail'), function(swatches) {
7421 return helpers.ui.applyHeaderSwatch(swatches);
7425 LayoutDetailsHeaderView.prototype.initialize = function() {
7426 if (!this.model.get('progress')) {
7427 return this.model.set({
7428 progress: 0
7433 return LayoutDetailsHeaderView;
7435 })(App.Views.LayoutView);
7438 this.Kodi.module("Views", function(Views, App, Backbone, Marionette, $, _) {
7439 var API;
7440 API = {
7441 createModel: function(options) {
7442 var defaultMenu, model;
7443 if (options == null) {
7444 options = {};
7446 defaultMenu = {
7447 selectall: tr('Toggle select all')
7449 model = _.extend({
7450 childViewTag: 'div',
7451 ChildViewClass: ''
7452 }, options);
7453 if (!options.noMenuDefault) {
7454 model.menu = _.isObject(options.menu) ? _.extend(defaultMenu, options.menu) : defaultMenu;
7456 return new Backbone.Model(model);
7458 toggleSelectAll: function($el) {
7459 var $ctx;
7460 $ctx = $('.set__collection', $el);
7461 return $('.card, .song', $ctx).toggleClass('selected').each(function(i, d) {
7462 var $d, op;
7463 $d = $(d);
7464 op = $(d).hasClass('selected') ? 'add' : 'remove';
7465 return App.execute("selected:update:items", op, $d.data('model').toJSON());
7469 Views.SetCompositeView = (function(superClass) {
7470 extend(SetCompositeView, superClass);
7472 function SetCompositeView() {
7473 return SetCompositeView.__super__.constructor.apply(this, arguments);
7476 SetCompositeView.prototype.template = 'views/set/set';
7478 SetCompositeView.prototype.childView = App.Views.CardView;
7480 SetCompositeView.prototype.tagName = 'div';
7482 SetCompositeView.prototype.className = "set__wrapper";
7484 SetCompositeView.prototype.childViewContainer = ".set__collection";
7486 SetCompositeView.prototype.events = {
7487 "click .dropdown > i": "populateSetMenu",
7488 "click .selectall": "toggleSelectAll"
7491 SetCompositeView.prototype.initialize = function() {
7492 return this.createModel();
7495 SetCompositeView.prototype.createModel = function() {
7496 return this.model = API.createModel(this.options);
7499 SetCompositeView.prototype.toggleSelectAll = function() {
7500 return API.toggleSelectAll(this.$el);
7503 return SetCompositeView;
7505 })(App.Views.CompositeView);
7506 return Views.SetLayoutView = (function(superClass) {
7507 extend(SetLayoutView, superClass);
7509 function SetLayoutView() {
7510 return SetLayoutView.__super__.constructor.apply(this, arguments);
7513 SetLayoutView.prototype.template = 'views/set/set';
7515 SetLayoutView.prototype.tagName = 'div';
7517 SetLayoutView.prototype.className = "set__wrapper";
7519 SetLayoutView.prototype.regions = {
7520 regionCollection: ".set__collection"
7523 SetLayoutView.prototype.events = {
7524 "click .dropdown > i": "populateSetMenu",
7525 "click .selectall": "toggleSelectAll"
7528 SetLayoutView.prototype.initialize = function() {
7529 return this.createModel();
7532 SetLayoutView.prototype.createModel = function() {
7533 return this.model = API.createModel(this.options);
7536 SetLayoutView.prototype.toggleSelectAll = function() {
7537 return API.toggleSelectAll(this.$el);
7540 return SetLayoutView;
7542 })(App.Views.LayoutView);
7545 this.Kodi.module("Views", function(Views, App, Backbone, Marionette, $, _) {
7546 return Views.SongViewPlaceholder = (function(superClass) {
7547 extend(SongViewPlaceholder, superClass);
7549 function SongViewPlaceholder() {
7550 return SongViewPlaceholder.__super__.constructor.apply(this, arguments);
7553 SongViewPlaceholder.prototype.template = "views/song/song_placeholder";
7555 SongViewPlaceholder.prototype.tagName = 'tr';
7557 SongViewPlaceholder.prototype.attributes = function() {
7558 return {
7559 "class": 'song ph'
7563 SongViewPlaceholder.prototype.onRender = function() {
7564 return this.$el.data('model', this.model);
7567 return SongViewPlaceholder;
7569 })(App.Views.ItemView);
7572 this.Kodi.module("Components.Form", function(Form, App, Backbone, Marionette, $, _) {
7573 Form.Controller = (function(superClass) {
7574 extend(Controller, superClass);
7576 function Controller() {
7577 return Controller.__super__.constructor.apply(this, arguments);
7580 Controller.prototype.initialize = function(options) {
7581 var config;
7582 if (options == null) {
7583 options = {};
7585 config = options.config ? options.config : {};
7586 this.formLayout = this.getFormLayout(config);
7587 this.listenTo(this.formLayout, "show", (function(_this) {
7588 return function() {
7589 _this.formBuild(options.form, options.formState, config);
7590 $.material.init();
7591 if (config && typeof config.onShow === 'function') {
7592 return config.onShow(options);
7595 })(this));
7596 this.listenTo(this.formLayout, "form:submit", (function(_this) {
7597 return function() {
7598 return _this.formSubmit(options);
7600 })(this));
7601 return this;
7604 Controller.prototype.formSubmit = function(options) {
7605 var data;
7606 data = Backbone.Syphon.serialize(this.formLayout);
7607 data = App.request("form:value:entities", options.form, data);
7608 return this.processFormSubmit(data, options);
7611 Controller.prototype.processFormSubmit = function(data, options) {
7612 if (options.config && typeof options.config.callback === 'function') {
7613 return options.config.callback(data, this.formLayout);
7617 Controller.prototype.getFormLayout = function(options) {
7618 if (options == null) {
7619 options = {};
7621 return new Form.FormWrapper({
7622 config: options
7626 Controller.prototype.formBuild = function(form, formState, options) {
7627 var buildView, collection;
7628 if (form == null) {
7629 form = [];
7631 if (formState == null) {
7632 formState = {};
7634 if (options == null) {
7635 options = {};
7637 collection = App.request("form:item:entities", form, formState);
7638 buildView = new Form.Groups({
7639 collection: collection
7641 return this.formLayout.formContentRegion.show(buildView);
7644 return Controller;
7646 })(App.Controllers.Base);
7647 App.reqres.setHandler("form:render:items", function(form, formState, options) {
7648 var collection;
7649 if (options == null) {
7650 options = {};
7652 collection = App.request("form:item:entities", form, formState);
7653 return new Form.Groups({
7654 collection: collection
7657 App.reqres.setHandler("form:wrapper", function(options) {
7658 var formController;
7659 if (options == null) {
7660 options = {};
7662 formController = new Form.Controller(options);
7663 return formController.formLayout;
7665 return App.reqres.setHandler("form:popup:wrapper", function(options) {
7666 var formContent, formController, originalCallback, popupStyle, ref, titleHtml;
7667 if (options == null) {
7668 options = {};
7670 originalCallback = options.config.callback;
7671 options.config.callback = function(data, layout) {
7672 App.execute("ui:modal:close");
7673 return originalCallback(data, layout);
7675 formController = new Form.Controller(options);
7676 formContent = formController.formLayout.render().$el;
7677 formController.formLayout.trigger('show');
7678 popupStyle = options.config.editForm ? 'edit-form' : 'form';
7679 titleHtml = (ref = options.titleHtml) != null ? ref : _.escape(options.title);
7680 return App.execute("ui:modal:form:show", titleHtml, formContent, popupStyle);
7684 this.Kodi.module("Components.Form", function(Form, App, Backbone, Marionette, $, _) {
7685 Form.FormWrapper = (function(superClass) {
7686 extend(FormWrapper, superClass);
7688 function FormWrapper() {
7689 return FormWrapper.__super__.constructor.apply(this, arguments);
7692 FormWrapper.prototype.template = "components/form/form";
7694 FormWrapper.prototype.tagName = "form";
7696 FormWrapper.prototype.regions = {
7697 formContentRegion: ".form-content-region",
7698 formResponse: ".response"
7701 FormWrapper.prototype.triggers = {
7702 "click .form-save": "form:submit",
7703 "click [data-form-button='cancel']": "form:cancel"
7706 FormWrapper.prototype.modelEvents = {
7707 "change:_errors": "changeErrors",
7708 "sync:start": "syncStart",
7709 "sync:stop": "syncStop"
7712 FormWrapper.prototype.initialize = function() {
7713 this.config = this.options.config;
7714 return this.on("form:save", (function(_this) {
7715 return function(msg) {
7716 return _this.addSuccessMsg(msg);
7718 })(this));
7721 FormWrapper.prototype.attributes = function() {
7722 var attrs;
7723 attrs = {
7724 "class": 'component-form'
7726 if (this.options.config && this.options.config.attributes) {
7727 attrs = _.extend(attrs, this.options.config.attributes);
7729 return attrs;
7732 FormWrapper.prototype.onRender = function() {
7733 return _.defer((function(_this) {
7734 return function() {
7735 if (_this.config.focusFirstInput) {
7736 _this.focusFirstInput();
7738 $('.btn').ripples({
7739 color: 'rgba(255,255,255,0.1)'
7741 App.vent.trigger("form:onshow", _this.config);
7742 $('.form-item .form-button', _this.$el).on('click', function(e) {
7743 e.preventDefault();
7744 if ($(this).data('trigger')) {
7745 return App.execute($(this).data('trigger'));
7748 if (_this.config.tabs) {
7749 return _this.makeTabs(_this.$el);
7752 })(this));
7755 FormWrapper.prototype.focusFirstInput = function() {
7756 return this.$(":input:visible:enabled:first").focus();
7759 FormWrapper.prototype.changeErrors = function(model, errors, options) {
7760 if (this.config.errors) {
7761 if (_.isEmpty(errors)) {
7762 return this.removeErrors();
7763 } else {
7764 return this.addErrors(errors);
7769 FormWrapper.prototype.removeErrors = function() {
7770 return this.$(".error").removeClass("error").find("small").remove();
7773 FormWrapper.prototype.addErrors = function(errors) {
7774 var array, name, results1;
7775 if (errors == null) {
7776 errors = {};
7778 results1 = [];
7779 for (name in errors) {
7780 array = errors[name];
7781 results1.push(this.addError(name, array[0]));
7783 return results1;
7786 FormWrapper.prototype.addError = function(name, error) {
7787 var el, sm;
7788 el = this.$("[name='" + name + "']");
7789 sm = $("<small>").text(error);
7790 return el.after(sm).closest(".row").addClass("error");
7793 FormWrapper.prototype.addSuccessMsg = function(msg) {
7794 var $el;
7795 $el = $(".response", this.$el);
7796 $el.text(msg).show();
7797 return setTimeout((function() {
7798 return $el.fadeOut();
7799 }), 5000);
7802 FormWrapper.prototype.makeTabs = function($ctx) {
7803 var $tabs;
7804 $tabs = $('<ul>').addClass('form-tabs');
7805 $('.group-title', $ctx).each(function(i, d) {
7806 return $('<li>').html($(d).html()).click(function(e) {
7807 $('.group-parent').hide();
7808 $(d).closest('.group-parent').show();
7809 $(e.target).closest('.form-tabs').find('li').removeClass('active');
7810 return $(e.target).addClass('active');
7811 }).appendTo($tabs);
7813 $('.form-groups', $ctx).before($tabs);
7814 $tabs.find('li').first().trigger('click');
7815 return $ctx.addClass('with-tabs');
7818 return FormWrapper;
7820 })(App.Views.LayoutView);
7821 Form.Item = (function(superClass) {
7822 extend(Item, superClass);
7824 function Item() {
7825 return Item.__super__.constructor.apply(this, arguments);
7828 Item.prototype.template = 'components/form/form_item';
7830 Item.prototype.tagName = 'div';
7832 Item.prototype.initialize = function() {
7833 var attrs, baseAttrs, el, inputType, key, materialBaseAttrs, name, options, ref, textfields, val, value;
7834 name = this.model.get('name') ? this.model.get('name') : this.model.get('id');
7835 baseAttrs = _.extend({
7836 id: 'form-edit-' + this.model.get('id'),
7837 name: name,
7838 "class": ''
7839 }, this.model.get('attributes'));
7840 materialBaseAttrs = _.extend({}, baseAttrs);
7841 materialBaseAttrs["class"] += ' form-control';
7842 if (this.model.get('titleHtml') == null) {
7843 this.model.set('titleHtml', this.model.escape('title'));
7845 switch (this.model.get('type')) {
7846 case 'checkbox':
7847 attrs = {
7848 type: 'checkbox',
7849 value: 1,
7850 "class": 'form-checkbox'
7852 if (this.model.get('defaultValue') === true) {
7853 attrs.checked = 'checked';
7855 el = this.themeTag('input', _.extend(baseAttrs, attrs), '');
7856 break;
7857 case 'textfield':
7858 case 'number':
7859 case 'date':
7860 case 'imageselect':
7861 textfields = ['textfield', 'imageselect'];
7862 inputType = helpers.global.inArray(this.model.get('type'), textfields) ? 'text' : this.model.get('type');
7863 attrs = {
7864 type: inputType,
7865 value: this.model.get('defaultValue')
7867 el = this.themeTag('input', _.extend(materialBaseAttrs, attrs), '');
7868 break;
7869 case 'hidden':
7870 attrs = {
7871 type: 'hidden',
7872 value: this.model.get('defaultValue'),
7873 "class": 'form-hidden'
7875 el = this.themeTag('input', _.extend(baseAttrs, attrs), '');
7876 break;
7877 case 'button':
7878 attrs = {
7879 "class": 'form-button btn btn-secondary'
7881 if (this.model.get('trigger')) {
7882 attrs['data-trigger'] = this.model.get('trigger');
7884 el = this.themeTag('button', _.extend(baseAttrs, attrs), this.model.get('value'));
7885 break;
7886 case 'textarea':
7887 el = this.themeTag('textarea', materialBaseAttrs, this.model.get('defaultValue'));
7888 break;
7889 case 'markup':
7890 attrs = {
7891 "class": 'form-markup'
7893 el = this.themeTag('div', attrs, this.model.get('markup'));
7894 break;
7895 case 'select':
7896 options = '';
7897 ref = this.model.get('options');
7898 for (key in ref) {
7899 val = ref[key];
7900 attrs = {
7901 value: key
7903 value = this.model.get('defaultValue');
7904 if (String(value) === String(key)) {
7905 attrs.selected = 'selected';
7907 options += this.themeTag('option', attrs, val);
7909 el = this.themeTag('select', _.extend(baseAttrs, {
7910 "class": 'form-control'
7911 }), options);
7912 break;
7913 default:
7914 el = null;
7916 if (el) {
7917 return this.model.set({
7918 element: el
7923 Item.prototype.attributes = function() {
7924 var elAttrs, elClasses;
7925 elClasses = [];
7926 elAttrs = this.model.get('attributes');
7927 if (elAttrs["class"]) {
7928 elClasses = _.map(elAttrs["class"].split(' '), function(c) {
7929 return 'form-item-' + c;
7932 return {
7933 "class": 'form-item form-group form-type-' + this.model.get('type') + ' form-edit-' + this.model.get('id') + ' ' + elClasses.join(' ')
7937 Item.prototype.onRender = function() {
7938 return _.defer((function(_this) {
7939 return function() {
7940 if (_this.model.get('prefix')) {
7941 _this.$el.before(_this.model.get('prefix'));
7943 if (_this.model.get('suffix')) {
7944 return _this.$el.after(_this.model.get('suffix'));
7947 })(this));
7950 return Item;
7952 })(App.Views.ItemView);
7953 Form.Group = (function(superClass) {
7954 extend(Group, superClass);
7956 function Group() {
7957 return Group.__super__.constructor.apply(this, arguments);
7960 Group.prototype.template = 'components/form/form_item_group';
7962 Group.prototype.tagName = 'div';
7964 Group.prototype.childViewContainer = '.form-items';
7966 Group.prototype.getChildView = function(item) {
7967 if (item.get('type') === 'imageselect') {
7968 return Form.ItemImageSelect;
7969 } else {
7970 return Form.Item;
7974 Group.prototype.attributes = function() {
7975 return {
7976 "class": 'form-group group-parent ' + this.model.get('class')
7980 Group.prototype.initialize = function() {
7981 var children;
7982 children = this.model.get('children');
7983 if (children.length === 0) {
7984 return this.model.set('title', '');
7985 } else {
7986 return this.collection = children;
7990 return Group;
7992 })(App.Views.CompositeView);
7993 Form.Groups = (function(superClass) {
7994 extend(Groups, superClass);
7996 function Groups() {
7997 return Groups.__super__.constructor.apply(this, arguments);
8000 Groups.prototype.childView = Form.Group;
8002 Groups.prototype.className = 'form-groups';
8004 return Groups;
8006 })(App.Views.CollectionView);
8009 Custom Widgets that extend Form.Item
8011 return Form.ItemImageSelect = (function(superClass) {
8012 extend(ItemImageSelect, superClass);
8014 function ItemImageSelect() {
8015 return ItemImageSelect.__super__.constructor.apply(this, arguments);
8018 ItemImageSelect.prototype.template = 'components/form/form_item_imageselect';
8020 ItemImageSelect.prototype.initialize = function() {
8021 var thumb;
8022 ItemImageSelect.__super__.initialize.apply(this, arguments);
8023 thumb = App.request("images:path:get", this.model.get('defaultValue'), this.model.get('id'));
8024 return this.model.set({
8025 image: {
8026 original: this.model.get('defaultValue'),
8027 thumb: thumb
8032 ItemImageSelect.prototype.onRender = function() {
8033 var $input, $panes, $tabs, $thumbs, $wrapper, field, item, metadataHandler, metadataLookup;
8034 item = this.model.get('formState');
8035 field = this.model.get('id');
8036 metadataHandler = this.model.get('metadataImageHandler');
8037 metadataLookup = this.model.get('metadataLookupField');
8038 $wrapper = $('.form-imageselect', this.$el);
8039 $thumbs = $('.form-imageselect__thumbs', this.$el);
8040 $input = $('.form-imageselect__url input', this.$el);
8041 $tabs = $('.form-imageselect__tabs', this.$el);
8042 $panes = $('.form-imageselect__panes', this.$el);
8043 $tabs.on('click', 'li', function(e) {
8044 $tabs.find('li').removeClass('active');
8045 $(this).addClass('active');
8046 $panes.find('.pane').removeClass('active');
8047 return $panes.find('.pane[rel=' + $(this).data('pane') + ']').addClass('active');
8049 $thumbs.on('click', 'li', function(e) {
8050 $thumbs.find('li').removeClass('selected');
8051 return $input.val($(this).addClass('selected').data('original'));
8053 return _.defer(function() {
8054 if (metadataHandler && metadataLookup && item[metadataLookup]) {
8055 $wrapper.addClass('images-loading');
8056 return App.execute(metadataHandler, item[metadataLookup], function(collection) {
8057 var image, len, n, ref;
8058 ref = collection.where({
8059 type: field
8061 for (n = 0, len = ref.length; n < len; n++) {
8062 image = ref[n];
8063 $('<li>').data('original', image.get('url')).css('background-image', 'url(' + image.get('thumbnail') + ')').attr('title', image.get('title')).appendTo($thumbs);
8065 return $wrapper.removeClass('images-loading');
8071 return ItemImageSelect;
8073 })(Form.Item);
8076 this.Kodi.module("AddonApp", function(AddonApp, App, Backbone, Marionette, $, _) {
8077 var API;
8078 AddonApp.Router = (function(superClass) {
8079 extend(Router, superClass);
8081 function Router() {
8082 return Router.__super__.constructor.apply(this, arguments);
8085 Router.prototype.appRoutes = {
8086 "addons/:type": "list",
8087 "addon/execute/:id": "execute"
8090 return Router;
8092 })(App.Router.Base);
8093 API = {
8094 list: function(type) {
8095 return new AddonApp.List.Controller({
8096 type: type
8099 execute: function(id) {
8100 API.addonController().executeAddon(id, helpers.url.params(), function() {
8101 return Kodi.execute("notification:show", tr('Executed addon'));
8103 return App.navigate("addons/executable", {
8104 trigger: true
8107 addonController: function() {
8108 return App.request("command:kodi:controller", 'auto', 'AddOn');
8110 getEnabledAddons: function(callback) {
8111 var addons;
8112 addons = [];
8113 if (config.getLocal("addOnsLoaded", false) === true) {
8114 addons = config.getLocal("addOnsEnabled", []);
8115 if (callback) {
8116 callback(addons);
8118 } else {
8119 this.addonController().getEnabledAddons(true, function(addons) {
8120 config.setLocal("addOnsEnabled", addons);
8121 config.setLocal("addOnsLoaded", true);
8122 config.set('app', "addOnsSearchSettings", API.getSearchSettings(addons));
8123 if (callback) {
8124 return callback(addons);
8128 return addons;
8130 getSearchSettings: function(addons) {
8131 var addon, i, len, n, searchSetting, searchSettings, set;
8132 searchSettings = [];
8133 for (n = 0, len = addons.length; n < len; n++) {
8134 addon = addons[n];
8135 searchSetting = App.request("addon:search:settings:" + addon.addonid);
8136 if (searchSetting) {
8137 if (!_.isArray(searchSetting)) {
8138 searchSetting = [searchSetting];
8140 for (i in searchSetting) {
8141 set = searchSetting[i];
8142 set.id = addon.addonid + '.' + i;
8143 searchSettings.push(set);
8147 return searchSettings;
8149 isAddOnEnabled: function(filter, callback) {
8150 var addons;
8151 if (filter == null) {
8152 filter = {};
8154 addons = this.getEnabledAddons(callback);
8155 return _.findWhere(addons, filter);
8158 App.on("before:start", function() {
8159 new AddonApp.Router({
8160 controller: API
8162 return API.getEnabledAddons(function(resp) {
8163 return App.vent.trigger("navMain:refresh");
8166 App.reqres.setHandler('addon:isEnabled', function(filter, callback) {
8167 return API.isAddOnEnabled(filter, function(enabled) {
8168 if (callback) {
8169 return callback(enabled);
8173 App.reqres.setHandler('addon:enabled:addons', function(callback) {
8174 return API.getEnabledAddons(function(addons) {
8175 if (callback) {
8176 return callback(addons);
8180 App.reqres.setHandler('addon:excludedPaths', function(addonId) {
8181 var excludedPaths;
8182 if (addonId != null) {
8183 excludedPaths = App.request("addon:excludedPaths:" + addonId);
8185 if (excludedPaths == null) {
8186 excludedPaths = [];
8188 return excludedPaths;
8190 return App.reqres.setHandler('addon:search:enabled', function() {
8191 var settings;
8192 settings = config.get('app', "addOnsSearchSettings", []);
8193 settings = settings.concat(App.request('searchAddons:entities').toJSON());
8194 return settings;
8198 this.Kodi.module("AddonApp.GoogleMusic", function(GoogleMusic, App, Backbone, Marionette, $, _) {
8199 var API;
8200 API = {
8201 addonId: 'plugin.audio.googlemusic.exp',
8202 searchAddon: {
8203 id: this.addonId,
8204 url: 'plugin://plugin.audio.googlemusic.exp/?path=search_result&type=track&query=[QUERY]',
8205 title: 'GoogleMusic',
8206 media: 'music'
8209 return App.reqres.setHandler("addon:search:settings:" + API.addonId, function() {
8210 return API.searchAddon;
8214 this.Kodi.module("AddonApp.List", function(List, App, Backbone, Marionette, $, _) {
8215 return List.Controller = (function(superClass) {
8216 extend(Controller, superClass);
8218 function Controller() {
8219 return Controller.__super__.constructor.apply(this, arguments);
8222 Controller.prototype.initialize = function(options) {
8223 this.type = options.type;
8224 return App.request("addon:entities", this.type, (function(_this) {
8225 return function(collection) {
8226 collection.sortCollection('name');
8227 _this.layout = _this.getLayoutView(collection);
8228 _this.listenTo(_this.layout, "show", function() {
8229 _this.renderList(collection);
8230 return _this.renderSidebar();
8232 return App.regionContent.show(_this.layout);
8234 })(this));
8237 Controller.prototype.renderList = function(collection) {
8238 var view;
8239 view = new List.Addons({
8240 collection: collection
8242 return this.layout.regionContent.show(view);
8245 Controller.prototype.getLayoutView = function(collection) {
8246 return new List.ListLayout({
8247 collection: collection
8251 Controller.prototype.renderSidebar = function() {
8252 var settingsNavView;
8253 settingsNavView = App.request("navMain:children:show", 'addons/all', 'Add-ons');
8254 return this.layout.regionSidebarFirst.show(settingsNavView);
8257 return Controller;
8259 })(App.Controllers.Base);
8262 this.Kodi.module("AddonApp.List", function(List, App, Backbone, Marionette, $, _) {
8263 List.ListLayout = (function(superClass) {
8264 extend(ListLayout, superClass);
8266 function ListLayout() {
8267 return ListLayout.__super__.constructor.apply(this, arguments);
8270 ListLayout.prototype.className = "addon-list";
8272 return ListLayout;
8274 })(App.Views.LayoutWithSidebarFirstView);
8275 List.Teaser = (function(superClass) {
8276 extend(Teaser, superClass);
8278 function Teaser() {
8279 return Teaser.__super__.constructor.apply(this, arguments);
8282 Teaser.prototype.tagName = "li";
8284 return Teaser;
8286 })(App.Views.CardView);
8287 return List.Addons = (function(superClass) {
8288 extend(Addons, superClass);
8290 function Addons() {
8291 return Addons.__super__.constructor.apply(this, arguments);
8294 Addons.prototype.childView = List.Teaser;
8296 Addons.prototype.emptyView = App.Views.EmptyViewResults;
8298 Addons.prototype.tagName = "ul";
8300 Addons.prototype.sort = 'name';
8302 Addons.prototype.className = "card-grid--square";
8304 return Addons;
8306 })(App.Views.CollectionView);
8309 this.Kodi.module("AddonApp.MixCloud", function(MixCloud, App, Backbone, Marionette, $, _) {
8310 var API;
8311 API = {
8312 addonId: 'plugin.audio.mixcloud',
8313 searchAddon: {
8314 id: this.addonId,
8315 url: 'plugin://plugin.audio.mixcloud/?mode=30&key=cloudcast&offset=0&query=[QUERY]',
8316 title: 'MixCloud',
8317 media: 'music'
8320 return App.reqres.setHandler("addon:search:settings:" + API.addonId, function() {
8321 return API.searchAddon;
8325 this.Kodi.module("AddonApp.Pvr", function(Pvr, App, Backbone, Marionette, $, _) {
8326 var API;
8327 API = {
8328 isEnabled: function() {
8329 return App.request("addon:isEnabled", {
8330 type: 'kodi.pvrclient'
8334 return App.reqres.setHandler("addon:pvr:enabled", function() {
8335 return API.isEnabled();
8339 this.Kodi.module("AddonApp.Radio", function(Radio, App, Backbone, Marionette, $, _) {
8340 var API;
8341 API = {
8342 addonId: 'plugin.audio.radio_de',
8343 searchAddon: {
8344 id: this.addonId,
8345 url: 'plugin://plugin.audio.radio_de/stations/search/[QUERY]',
8346 title: 'Radio',
8347 media: 'music'
8350 return App.reqres.setHandler("addon:search:settings:" + API.addonId, function() {
8351 return API.searchAddon;
8355 this.Kodi.module("AddonApp.SoundCloud", function(Soundcloud, App, Backbone, Marionette, $, _) {
8356 var API;
8357 API = {
8358 addonId: 'plugin.audio.soundcloud',
8359 searchAddon: {
8360 id: this.addonId,
8361 url: 'plugin://plugin.audio.soundcloud/search/?query=[QUERY]',
8362 title: 'SoundCloud',
8363 media: 'music'
8366 return App.reqres.setHandler("addon:search:settings:" + API.addonId, function() {
8367 return API.searchAddon;
8371 this.Kodi.module("AddonApp.YouTube", function(Soundcloud, App, Backbone, Marionette, $, _) {
8372 var API;
8373 API = {
8374 addonId: 'plugin.video.youtube',
8375 searchAddon: {
8376 url: 'plugin://plugin.video.youtube/search/?q=[QUERY]',
8377 title: 'YouTube',
8378 media: 'video'
8381 App.reqres.setHandler("addon:search:settings:" + API.addonId, function() {
8382 return API.searchAddon;
8384 return App.reqres.setHandler("addon:excludedPaths:" + API.addonId, function() {
8385 return ['plugin://plugin.video.youtube/special/', 'plugin://plugin.video.youtube/kodion/search/', 'plugin://plugin.video.youtube/kodion/', 'plugin://plugin.video.youtube/channel/'];
8389 this.Kodi.module("AlbumApp", function(AlbumApp, App, Backbone, Marionette, $, _) {
8390 var API;
8391 AlbumApp.Router = (function(superClass) {
8392 extend(Router, superClass);
8394 function Router() {
8395 return Router.__super__.constructor.apply(this, arguments);
8398 Router.prototype.appRoutes = {
8399 "music/albums": "list",
8400 "music/album/:id": "view"
8403 return Router;
8405 })(App.Router.Base);
8406 API = {
8407 list: function() {
8408 return new AlbumApp.List.Controller();
8410 view: function(id) {
8411 return new AlbumApp.Show.Controller({
8412 id: id
8415 action: function(op, view) {
8416 var localPlaylist, model, playlist;
8417 model = view.model;
8418 playlist = App.request("command:kodi:controller", 'audio', 'PlayList');
8419 switch (op) {
8420 case 'play':
8421 return App.execute("command:audio:play", 'albumid', model.get('albumid'));
8422 case 'add':
8423 return playlist.add('albumid', model.get('albumid'));
8424 case 'localadd':
8425 return App.execute("localplaylist:addentity", 'albumid', model.get('albumid'));
8426 case 'localplay':
8427 localPlaylist = App.request("command:local:controller", 'audio', 'PlayList');
8428 return localPlaylist.play('albumid', model.get('albumid'));
8432 App.on("before:start", function() {
8433 return new AlbumApp.Router({
8434 controller: API
8437 App.commands.setHandler('album:action', function(op, model) {
8438 return API.action(op, model);
8440 App.reqres.setHandler('album:action:items', function() {
8441 return {
8442 actions: {
8443 thumbs: 'Thumbs up'
8445 menu: {
8446 add: tr('Queue in Kodi'),
8447 'divider-1': '',
8448 localadd: tr('Add to playlist'),
8449 localplay: tr('Play in browser'),
8450 'divider-2': '',
8451 edit: tr('Edit')
8455 return App.commands.setHandler('album:edit', function(model) {
8456 var loadedModel;
8457 loadedModel = App.request("album:entity", model.get('id'));
8458 return App.execute("when:entity:fetched", loadedModel, (function(_this) {
8459 return function() {
8460 return new AlbumApp.Edit.Controller({
8461 model: loadedModel
8464 })(this));
8468 this.Kodi.module("AlbumApp.Edit", function(Edit, App, Backbone, Marionette, $, _) {
8469 return Edit.Controller = (function(superClass) {
8470 extend(Controller, superClass);
8472 function Controller() {
8473 return Controller.__super__.constructor.apply(this, arguments);
8476 Controller.prototype.initialize = function() {
8477 var form, options;
8478 this.model = this.getOption('model');
8479 options = {
8480 titleHtml: '<span>' + tr('Edit') + '</span>' + this.model.escape('title'),
8481 form: this.getStructure(),
8482 formState: this.model.attributes,
8483 config: {
8484 attributes: {
8485 "class": 'edit-form'
8487 editForm: true,
8488 tabs: true,
8489 callback: (function(_this) {
8490 return function(data, formView) {
8491 return _this.saveCallback(data, formView);
8493 })(this)
8496 return form = App.request("form:popup:wrapper", options);
8499 Controller.prototype.getStructure = function() {
8500 return [
8502 title: 'General',
8503 id: 'general',
8504 children: [
8506 id: 'title',
8507 title: tr('Title'),
8508 type: 'textfield'
8509 }, {
8510 id: 'artist',
8511 title: tr('Artist'),
8512 type: 'textfield',
8513 format: 'array.string'
8514 }, {
8515 id: 'description',
8516 title: tr('Description'),
8517 type: 'textarea'
8518 }, {
8519 id: 'albumlabel',
8520 title: tr('Label'),
8521 type: 'textfield'
8522 }, {
8523 id: 'year',
8524 title: tr('Year'),
8525 type: 'number',
8526 format: 'integer',
8527 attributes: {
8528 "class": 'half-width',
8529 step: 1,
8530 min: 0,
8531 max: 9999
8533 }, {
8534 id: 'rating',
8535 title: tr('Rating'),
8536 type: 'number',
8537 format: 'float',
8538 attributes: {
8539 "class": 'half-width',
8540 step: 0.1,
8541 min: 0,
8542 max: 10
8544 suffix: '<div class="clearfix"></div>'
8547 }, {
8548 title: 'Tags',
8549 id: 'tags',
8550 children: [
8552 id: 'genre',
8553 title: tr('Genres'),
8554 type: 'textfield',
8555 format: 'array.string'
8556 }, {
8557 id: 'style',
8558 title: tr('Styles'),
8559 type: 'textfield',
8560 format: 'array.string'
8561 }, {
8562 id: 'theme',
8563 title: tr('Themes'),
8564 type: 'textarea',
8565 format: 'array.string'
8566 }, {
8567 id: 'mood',
8568 title: tr('Moods'),
8569 type: 'textarea',
8570 format: 'array.string'
8577 Controller.prototype.saveCallback = function(data, formView) {
8578 var controller;
8579 controller = App.request("command:kodi:controller", 'audio', 'AudioLibrary');
8580 return controller.setAlbumDetails(this.model.get('id'), data, (function(_this) {
8581 return function() {
8582 Kodi.vent.trigger('entity:kodi:update', _this.model.get('uid'));
8583 return Kodi.execute("notification:show", t.sprintf(tr("Updated %1$s details"), 'album'));
8585 })(this));
8588 return Controller;
8590 })(App.Controllers.Base);
8593 this.Kodi.module("AlbumApp.List", function(List, App, Backbone, Marionette, $, _) {
8594 var API;
8595 API = {
8596 bindTriggers: function(view) {
8597 App.listenTo(view, 'childview:album:play', function(list, item) {
8598 return App.execute('album:action', 'play', item);
8600 App.listenTo(view, 'childview:album:add', function(list, item) {
8601 return App.execute('album:action', 'add', item);
8603 App.listenTo(view, 'childview:album:localadd', function(list, item) {
8604 return App.execute('album:action', 'localadd', item);
8606 App.listenTo(view, 'childview:album:localplay', function(list, item) {
8607 return App.execute('album:action', 'localplay', item);
8609 return App.listenTo(view, 'childview:album:edit', function(parent, item) {
8610 return App.execute('album:edit', item.model);
8613 getAlbumsList: function(collection) {
8614 var view;
8615 view = new List.Albums({
8616 collection: collection
8618 API.bindTriggers(view);
8619 return view;
8622 List.Controller = (function(superClass) {
8623 extend(Controller, superClass);
8625 function Controller() {
8626 return Controller.__super__.constructor.apply(this, arguments);
8629 Controller.prototype.initialize = function() {
8630 var collection;
8631 collection = App.request("album:entities");
8632 return App.execute("when:entity:fetched", collection, (function(_this) {
8633 return function() {
8634 collection.availableFilters = _this.getAvailableFilters();
8635 collection.sectionId = 'music';
8636 App.request('filter:init', _this.getAvailableFilters());
8637 _this.layout = _this.getLayoutView(collection);
8638 _this.listenTo(_this.layout, "show", function() {
8639 _this.renderList(collection);
8640 return _this.getFiltersView(collection);
8642 return App.regionContent.show(_this.layout);
8644 })(this));
8647 Controller.prototype.getLayoutView = function(collection) {
8648 return new List.ListLayout({
8649 collection: collection
8653 Controller.prototype.getAvailableFilters = function() {
8654 return {
8655 sort: ['label', 'year', 'rating', 'artist', 'dateadded', 'random'],
8656 filter: ['year', 'genre', 'style', 'albumlabel', 'thumbsUp']
8660 Controller.prototype.getFiltersView = function(collection) {
8661 var filters;
8662 filters = App.request('filter:show', collection);
8663 this.layout.regionSidebarFirst.show(filters);
8664 return this.listenTo(filters, "filter:changed", (function(_this) {
8665 return function() {
8666 return _this.renderList(collection);
8668 })(this));
8671 Controller.prototype.renderList = function(collection) {
8672 var filteredCollection, view;
8673 App.execute("loading:show:view", this.layout.regionContent);
8674 filteredCollection = App.request('filter:apply:entities', collection);
8675 view = API.getAlbumsList(filteredCollection);
8676 return this.layout.regionContent.show(view);
8679 return Controller;
8681 })(App.Controllers.Base);
8682 return App.reqres.setHandler("album:list:view", function(collection) {
8683 return API.getAlbumsList(collection);
8687 this.Kodi.module("AlbumApp.List", function(List, App, Backbone, Marionette, $, _) {
8688 List.ListLayout = (function(superClass) {
8689 extend(ListLayout, superClass);
8691 function ListLayout() {
8692 return ListLayout.__super__.constructor.apply(this, arguments);
8695 ListLayout.prototype.className = "album-list with-filters";
8697 return ListLayout;
8699 })(App.Views.LayoutWithSidebarFirstView);
8700 List.AlbumTeaser = (function(superClass) {
8701 extend(AlbumTeaser, superClass);
8703 function AlbumTeaser() {
8704 return AlbumTeaser.__super__.constructor.apply(this, arguments);
8707 AlbumTeaser.prototype.triggers = {
8708 "click .play": "album:play",
8709 "click .dropdown .add": "album:add",
8710 "click .dropdown .localadd": "album:localadd",
8711 "click .dropdown .localplay": "album:localplay",
8712 "click .dropdown .edit": "album:edit"
8715 AlbumTeaser.prototype.initialize = function() {
8716 AlbumTeaser.__super__.initialize.apply(this, arguments);
8717 if (this.model != null) {
8718 this.setMeta();
8719 return this.model.set(App.request('album:action:items'));
8723 AlbumTeaser.prototype.setMeta = function() {
8724 if (this.model) {
8725 return this.model.set({
8726 subtitleHtml: this.themeLink(this.model.get('artist'), helpers.url.get('artist', this.model.get('artistid')))
8731 return AlbumTeaser;
8733 })(App.Views.CardView);
8734 List.Empty = (function(superClass) {
8735 extend(Empty, superClass);
8737 function Empty() {
8738 return Empty.__super__.constructor.apply(this, arguments);
8741 Empty.prototype.tagName = "li";
8743 Empty.prototype.className = "album-empty-result";
8745 return Empty;
8747 })(App.Views.EmptyViewResults);
8748 return List.Albums = (function(superClass) {
8749 extend(Albums, superClass);
8751 function Albums() {
8752 return Albums.__super__.constructor.apply(this, arguments);
8755 Albums.prototype.childView = List.AlbumTeaser;
8757 Albums.prototype.emptyView = List.Empty;
8759 Albums.prototype.tagName = "ul";
8761 Albums.prototype.sort = 'artist';
8763 Albums.prototype.className = "card-grid--square";
8765 return Albums;
8767 })(App.Views.VirtualListView);
8770 this.Kodi.module("AlbumApp.Show", function(Show, App, Backbone, Marionette, $, _) {
8771 var API;
8772 API = {
8773 bindTriggers: function(view) {
8774 App.listenTo(view, 'album:play', function(item) {
8775 return App.execute('album:action', 'play', item);
8777 App.listenTo(view, 'album:add', function(item) {
8778 return App.execute('album:action', 'add', item);
8780 App.listenTo(view, 'album:localadd', function(item) {
8781 return App.execute('album:action', 'localadd', item);
8783 App.listenTo(view, 'album:localplay', function(item) {
8784 return App.execute('album:action', 'localplay', item);
8786 return App.listenTo(view, 'album:edit', function(item) {
8787 return App.execute('album:edit', item.model);
8790 getAlbumsFromSongs: function(songs) {
8791 var album, albumSet, albumsCollectionView, len, n;
8792 albumsCollectionView = new Show.WithSongsCollection();
8793 albumsCollectionView.on("add:child", (function(_this) {
8794 return function(albumView) {
8795 return App.execute("when:entity:fetched", album, function() {
8796 var model, songSet, songView, teaser;
8797 model = albumView.model;
8798 teaser = new Show.AlbumTeaser({
8799 model: model
8801 API.bindTriggers(teaser);
8802 albumView.regionMeta.show(teaser);
8803 songSet = _.findWhere(songs, {
8804 albumid: model.get('albumid')
8806 songView = App.request("song:list:view", songSet.songs);
8807 return albumView.regionSongs.show(songView);
8810 })(this));
8811 for (n = 0, len = songs.length; n < len; n++) {
8812 albumSet = songs[n];
8813 album = App.request("album:entity", albumSet.albumid, {
8814 success: function(album) {
8815 return albumsCollectionView.addChild(album, Show.WithSongsLayout);
8819 return albumsCollectionView;
8822 Show.Controller = (function(superClass) {
8823 extend(Controller, superClass);
8825 function Controller() {
8826 return Controller.__super__.constructor.apply(this, arguments);
8829 Controller.prototype.initialize = function(options) {
8830 var album, id;
8831 id = parseInt(options.id);
8832 album = App.request("album:entity", id);
8833 return App.execute("when:entity:fetched", album, (function(_this) {
8834 return function() {
8835 _this.layout = _this.getLayoutView(album);
8836 _this.listenTo(_this.layout, "destroy", function() {
8837 return App.execute("images:fanart:set", 'none');
8839 _this.listenTo(_this.layout, "show", function() {
8840 _this.getMusic(id);
8841 return _this.getDetailsLayoutView(album);
8843 return App.regionContent.show(_this.layout);
8845 })(this));
8848 Controller.prototype.getLayoutView = function(album) {
8849 return new Show.PageLayout({
8850 model: album
8854 Controller.prototype.getDetailsLayoutView = function(album) {
8855 var headerLayout;
8856 headerLayout = new Show.HeaderLayout({
8857 model: album
8859 this.listenTo(headerLayout, "show", (function(_this) {
8860 return function() {
8861 var detail, teaser;
8862 teaser = new Show.AlbumDetailTeaser({
8863 model: album
8865 API.bindTriggers(teaser);
8866 detail = new Show.Details({
8867 model: album
8869 _this.listenTo(detail, "show", function() {
8870 return API.bindTriggers(detail);
8872 headerLayout.regionSide.show(teaser);
8873 return headerLayout.regionMeta.show(detail);
8875 })(this));
8876 return this.layout.regionHeader.show(headerLayout);
8879 Controller.prototype.getMusic = function(id) {
8880 var options, songs;
8881 options = {
8882 filter: {
8883 albumid: id
8886 songs = App.request("song:entities", options);
8887 return App.execute("when:entity:fetched", songs, (function(_this) {
8888 return function() {
8889 var albumView, songView;
8890 albumView = new Show.WithSongsLayout();
8891 songView = App.request("song:list:view", songs);
8892 _this.listenTo(albumView, "show", function() {
8893 return albumView.regionSongs.show(songView);
8895 return _this.layout.regionContent.show(albumView);
8897 })(this));
8900 return Controller;
8902 })(App.Controllers.Base);
8903 return App.reqres.setHandler("albums:withsongs:view", function(songs) {
8904 return API.getAlbumsFromSongs(songs);
8908 this.Kodi.module("AlbumApp.Show", function(Show, App, Backbone, Marionette, $, _) {
8909 Show.WithSongsLayout = (function(superClass) {
8910 extend(WithSongsLayout, superClass);
8912 function WithSongsLayout() {
8913 return WithSongsLayout.__super__.constructor.apply(this, arguments);
8916 WithSongsLayout.prototype.template = 'apps/album/show/album_with_songs';
8918 WithSongsLayout.prototype.className = 'album-wrapper';
8920 WithSongsLayout.prototype.regions = {
8921 regionMeta: '.region-album-meta',
8922 regionSongs: '.region-album-songs'
8925 return WithSongsLayout;
8927 })(App.Views.LayoutView);
8928 Show.WithSongsCollection = (function(superClass) {
8929 extend(WithSongsCollection, superClass);
8931 function WithSongsCollection() {
8932 return WithSongsCollection.__super__.constructor.apply(this, arguments);
8935 WithSongsCollection.prototype.childView = Show.WithSongsLayout;
8937 WithSongsCollection.prototype.tagName = "div";
8939 WithSongsCollection.prototype.sort = 'year';
8941 WithSongsCollection.prototype.className = "albums-wrapper";
8943 return WithSongsCollection;
8945 })(App.Views.CollectionView);
8946 Show.PageLayout = (function(superClass) {
8947 extend(PageLayout, superClass);
8949 function PageLayout() {
8950 return PageLayout.__super__.constructor.apply(this, arguments);
8953 PageLayout.prototype.className = 'album-show detail-container';
8955 return PageLayout;
8957 })(App.Views.LayoutWithHeaderView);
8958 Show.HeaderLayout = (function(superClass) {
8959 extend(HeaderLayout, superClass);
8961 function HeaderLayout() {
8962 return HeaderLayout.__super__.constructor.apply(this, arguments);
8965 HeaderLayout.prototype.className = 'album-details';
8967 return HeaderLayout;
8969 })(App.Views.LayoutDetailsHeaderView);
8970 Show.Details = (function(superClass) {
8971 extend(Details, superClass);
8973 function Details() {
8974 return Details.__super__.constructor.apply(this, arguments);
8977 Details.prototype.template = 'apps/album/show/details_meta';
8979 Details.prototype.triggers = {
8980 "click .play": "album:play",
8981 "click .add": "album:add",
8982 "click .localadd": "album:localadd",
8983 "click .localplay": "album:localplay",
8984 "click .edit": "album:edit"
8987 return Details;
8989 })(App.Views.DetailsItem);
8990 Show.AlbumTeaser = (function(superClass) {
8991 extend(AlbumTeaser, superClass);
8993 function AlbumTeaser() {
8994 return AlbumTeaser.__super__.constructor.apply(this, arguments);
8997 AlbumTeaser.prototype.tagName = "div";
8999 AlbumTeaser.prototype.initialize = function() {
9000 this.setMeta();
9001 return this.model.set(App.request('album:action:items'));
9004 AlbumTeaser.prototype.setMeta = function() {
9005 return this.model.set({
9006 subtitleHtml: this.themeLink(this.model.get('year'), 'music/albums?year=' + this.model.get('year'))
9010 AlbumTeaser.prototype.attributes = function() {
9011 return this.watchedAttributes('card-minimal');
9014 return AlbumTeaser;
9016 })(App.AlbumApp.List.AlbumTeaser);
9017 return Show.AlbumDetailTeaser = (function(superClass) {
9018 extend(AlbumDetailTeaser, superClass);
9020 function AlbumDetailTeaser() {
9021 return AlbumDetailTeaser.__super__.constructor.apply(this, arguments);
9024 AlbumDetailTeaser.prototype.attributes = function() {
9025 return this.watchedAttributes('card-detail');
9028 return AlbumDetailTeaser;
9030 })(Show.AlbumTeaser);
9033 this.Kodi.module("ArtistApp", function(ArtistApp, App, Backbone, Marionette, $, _) {
9034 var API;
9035 ArtistApp.Router = (function(superClass) {
9036 extend(Router, superClass);
9038 function Router() {
9039 return Router.__super__.constructor.apply(this, arguments);
9042 Router.prototype.appRoutes = {
9043 "music/artists": "list",
9044 "music/artist/:id": "view"
9047 return Router;
9049 })(App.Router.Base);
9050 API = {
9051 list: function() {
9052 return new ArtistApp.List.Controller();
9054 view: function(id) {
9055 return new ArtistApp.Show.Controller({
9056 id: id
9059 action: function(op, view) {
9060 var localPlaylist, model, playlist;
9061 model = view.model;
9062 playlist = App.request("command:kodi:controller", 'audio', 'PlayList');
9063 switch (op) {
9064 case 'play':
9065 return App.execute("command:audio:play", 'artistid', model.get('artistid'));
9066 case 'add':
9067 return playlist.add('artistid', model.get('artistid'));
9068 case 'localadd':
9069 return App.execute("localplaylist:addentity", 'artistid', model.get('artistid'));
9070 case 'localplay':
9071 localPlaylist = App.request("command:local:controller", 'audio', 'PlayList');
9072 return localPlaylist.play('artistid', model.get('artistid'));
9076 App.on("before:start", function() {
9077 return new ArtistApp.Router({
9078 controller: API
9081 App.commands.setHandler('artist:action', function(op, model) {
9082 return API.action(op, model);
9084 App.reqres.setHandler('artist:action:items', function() {
9085 return {
9086 actions: {
9087 thumbs: tr('Thumbs up')
9089 menu: {
9090 add: tr('Queue in Kodi'),
9091 'divider-1': '',
9092 localadd: tr('Add to playlist'),
9093 localplay: tr('Play in browser'),
9094 'divider-1': '',
9095 edit: tr('Edit')
9099 return App.commands.setHandler('artist:edit', function(model) {
9100 var loadedModel;
9101 loadedModel = App.request("artist:entity", model.get('id'));
9102 return App.execute("when:entity:fetched", loadedModel, (function(_this) {
9103 return function() {
9104 return new ArtistApp.Edit.Controller({
9105 model: loadedModel
9108 })(this));
9112 this.Kodi.module("ArtistApp.Edit", function(Edit, App, Backbone, Marionette, $, _) {
9113 return Edit.Controller = (function(superClass) {
9114 extend(Controller, superClass);
9116 function Controller() {
9117 return Controller.__super__.constructor.apply(this, arguments);
9120 Controller.prototype.initialize = function() {
9121 var form, options;
9122 this.model = this.getOption('model');
9123 options = {
9124 titleHtml: '<span>' + tr('Edit') + '</span>' + this.model.escape('artist'),
9125 form: this.getStructure(),
9126 formState: this.model.attributes,
9127 config: {
9128 attributes: {
9129 "class": 'edit-form'
9131 editForm: true,
9132 tabs: true,
9133 callback: (function(_this) {
9134 return function(data, formView) {
9135 return _this.saveCallback(data, formView);
9137 })(this)
9140 return form = App.request("form:popup:wrapper", options);
9143 Controller.prototype.getStructure = function() {
9144 return [
9146 title: 'General',
9147 id: 'general',
9148 children: [
9150 id: 'artist',
9151 title: tr('Title'),
9152 type: 'textfield'
9153 }, {
9154 id: 'description',
9155 title: tr('Description'),
9156 type: 'textarea'
9157 }, {
9158 id: 'formed',
9159 title: tr('Formed'),
9160 type: 'textfield',
9161 attributes: {
9162 "class": 'half-width'
9164 }, {
9165 id: 'disbanded',
9166 title: tr('Disbanded'),
9167 type: 'textfield',
9168 attributes: {
9169 "class": 'half-width'
9171 suffix: '<div class="clearfix"></div>'
9172 }, {
9173 id: 'born',
9174 title: tr('Born'),
9175 type: 'textfield',
9176 attributes: {
9177 "class": 'half-width'
9179 }, {
9180 id: 'died',
9181 title: tr('Died'),
9182 type: 'textfield',
9183 attributes: {
9184 "class": 'half-width'
9186 suffix: '<div class="clearfix"></div>'
9187 }, {
9188 id: 'yearsactive',
9189 title: tr('Years Active'),
9190 type: 'textfield',
9191 format: 'array.string'
9194 }, {
9195 title: 'Tags',
9196 id: 'tags',
9197 children: [
9199 id: 'genre',
9200 title: tr('Genres'),
9201 type: 'textfield',
9202 format: 'array.string'
9203 }, {
9204 id: 'style',
9205 title: tr('Styles'),
9206 type: 'textfield',
9207 format: 'array.string'
9208 }, {
9209 id: 'instrument',
9210 title: tr('Instruments'),
9211 type: 'textarea',
9212 format: 'array.string'
9213 }, {
9214 id: 'mood',
9215 title: tr('Moods'),
9216 type: 'textarea',
9217 format: 'array.string'
9224 Controller.prototype.saveCallback = function(data, formView) {
9225 var controller;
9226 controller = App.request("command:kodi:controller", 'audio', 'AudioLibrary');
9227 return controller.setArtistDetails(this.model.get('id'), data, (function(_this) {
9228 return function() {
9229 Kodi.vent.trigger('entity:kodi:update', _this.model.get('uid'));
9230 return Kodi.execute("notification:show", t.sprintf(tr("Updated %1$s details"), 'album'));
9232 })(this));
9235 return Controller;
9237 })(App.Controllers.Base);
9240 this.Kodi.module("ArtistApp.List", function(List, App, Backbone, Marionette, $, _) {
9241 var API;
9242 API = {
9243 bindTriggers: function(view) {
9244 App.listenTo(view, 'childview:artist:play', function(list, item) {
9245 return App.execute('artist:action', 'play', item);
9247 App.listenTo(view, 'childview:artist:add', function(list, item) {
9248 return App.execute('artist:action', 'add', item);
9250 App.listenTo(view, 'childview:artist:localadd', function(list, item) {
9251 return App.execute('artist:action', 'localadd', item);
9253 App.listenTo(view, 'childview:artist:localplay', function(list, item) {
9254 return App.execute('artist:action', 'localplay', item);
9256 return App.listenTo(view, 'childview:artist:edit', function(parent, item) {
9257 return App.execute('artist:edit', item.model);
9260 getArtistList: function(collection) {
9261 var view;
9262 view = new List.Artists({
9263 collection: collection
9265 API.bindTriggers(view);
9266 return view;
9269 List.Controller = (function(superClass) {
9270 extend(Controller, superClass);
9272 function Controller() {
9273 return Controller.__super__.constructor.apply(this, arguments);
9276 Controller.prototype.initialize = function() {
9277 var collection;
9278 collection = App.request("artist:entities");
9279 return App.execute("when:entity:fetched", collection, (function(_this) {
9280 return function() {
9281 collection.availableFilters = _this.getAvailableFilters();
9282 collection.sectionId = 'music';
9283 App.request('filter:init', _this.getAvailableFilters());
9284 _this.layout = _this.getLayoutView(collection);
9285 _this.listenTo(_this.layout, "show", function() {
9286 _this.renderList(collection);
9287 return _this.getFiltersView(collection);
9289 return App.regionContent.show(_this.layout);
9291 })(this));
9294 Controller.prototype.getLayoutView = function(collection) {
9295 return new List.ListLayout({
9296 collection: collection
9300 Controller.prototype.getAvailableFilters = function() {
9301 return {
9302 sort: ['label', 'random'],
9303 filter: ['mood', 'genre', 'style', 'thumbsUp']
9307 Controller.prototype.getFiltersView = function(collection) {
9308 var filters;
9309 filters = App.request('filter:show', collection);
9310 this.layout.regionSidebarFirst.show(filters);
9311 return this.listenTo(filters, "filter:changed", (function(_this) {
9312 return function() {
9313 return _this.renderList(collection);
9315 })(this));
9318 Controller.prototype.renderList = function(collection) {
9319 var filteredCollection, view;
9320 App.execute("loading:show:view", this.layout.regionContent);
9321 filteredCollection = App.request('filter:apply:entities', collection);
9322 view = API.getArtistList(filteredCollection);
9323 return this.layout.regionContent.show(view);
9326 return Controller;
9328 })(App.Controllers.Base);
9329 return App.reqres.setHandler("artist:list:view", function(collection) {
9330 return API.getArtistList(collection);
9334 this.Kodi.module("ArtistApp.List", function(List, App, Backbone, Marionette, $, _) {
9335 List.ListLayout = (function(superClass) {
9336 extend(ListLayout, superClass);
9338 function ListLayout() {
9339 return ListLayout.__super__.constructor.apply(this, arguments);
9342 ListLayout.prototype.className = "artist-list with-filters";
9344 return ListLayout;
9346 })(App.Views.LayoutWithSidebarFirstView);
9347 List.ArtistTeaser = (function(superClass) {
9348 extend(ArtistTeaser, superClass);
9350 function ArtistTeaser() {
9351 return ArtistTeaser.__super__.constructor.apply(this, arguments);
9354 ArtistTeaser.prototype.triggers = {
9355 "click .play": "artist:play",
9356 "click .dropdown .add": "artist:add",
9357 "click .dropdown .localadd": "artist:localadd",
9358 "click .dropdown .localplay": "artist:localplay",
9359 "click .dropdown .edit": "artist:edit"
9362 ArtistTeaser.prototype.initialize = function() {
9363 ArtistTeaser.__super__.initialize.apply(this, arguments);
9364 if (this.model != null) {
9365 return this.model.set(App.request('album:action:items'));
9369 return ArtistTeaser;
9371 })(App.Views.CardView);
9372 List.Empty = (function(superClass) {
9373 extend(Empty, superClass);
9375 function Empty() {
9376 return Empty.__super__.constructor.apply(this, arguments);
9379 Empty.prototype.tagName = "li";
9381 Empty.prototype.className = "artist-empty-result";
9383 return Empty;
9385 })(App.Views.EmptyViewResults);
9386 return List.Artists = (function(superClass) {
9387 extend(Artists, superClass);
9389 function Artists() {
9390 return Artists.__super__.constructor.apply(this, arguments);
9393 Artists.prototype.childView = List.ArtistTeaser;
9395 Artists.prototype.emptyView = List.Empty;
9397 Artists.prototype.tagName = "ul";
9399 Artists.prototype.className = "card-grid--wide";
9401 return Artists;
9403 })(App.Views.VirtualListView);
9406 this.Kodi.module("ArtistApp.Show", function(Show, App, Backbone, Marionette, $, _) {
9407 var API;
9408 API = {
9409 bindTriggers: function(view) {
9410 App.listenTo(view, 'artist:play', function(item) {
9411 return App.execute('artist:action', 'play', item);
9413 App.listenTo(view, 'artist:add', function(item) {
9414 return App.execute('artist:action', 'add', item);
9416 App.listenTo(view, 'artist:localadd', function(item) {
9417 return App.execute('artist:action', 'localadd', item);
9419 App.listenTo(view, 'artist:localplay', function(item) {
9420 return App.execute('artist:action', 'localplay', item);
9422 return App.listenTo(view, 'artist:edit', function(item) {
9423 return App.execute('artist:edit', item.model);
9427 return Show.Controller = (function(superClass) {
9428 extend(Controller, superClass);
9430 function Controller() {
9431 return Controller.__super__.constructor.apply(this, arguments);
9434 Controller.prototype.initialize = function(options) {
9435 var artist, id;
9436 id = parseInt(options.id);
9437 artist = App.request("artist:entity", id);
9438 return App.execute("when:entity:fetched", artist, (function(_this) {
9439 return function() {
9440 _this.layout = _this.getLayoutView(artist);
9441 _this.listenTo(_this.layout, "destroy", function() {
9442 return App.execute("images:fanart:set", 'none');
9444 _this.listenTo(_this.layout, "show", function() {
9445 _this.getMusic(id);
9446 return _this.getDetailsLayoutView(artist);
9448 return App.regionContent.show(_this.layout);
9450 })(this));
9453 Controller.prototype.getLayoutView = function(artist) {
9454 return new Show.PageLayout({
9455 model: artist
9459 Controller.prototype.getDetailsLayoutView = function(artist) {
9460 var headerLayout;
9461 headerLayout = new Show.HeaderLayout({
9462 model: artist
9464 this.listenTo(headerLayout, "show", (function(_this) {
9465 return function() {
9466 var detail, teaser;
9467 teaser = new Show.ArtistTeaser({
9468 model: artist
9470 API.bindTriggers(teaser);
9471 detail = new Show.Details({
9472 model: artist
9474 _this.listenTo(detail, "show", function() {
9475 return API.bindTriggers(detail);
9477 headerLayout.regionSide.show(teaser);
9478 return headerLayout.regionMeta.show(detail);
9480 })(this));
9481 return this.layout.regionHeader.show(headerLayout);
9484 Controller.prototype.getMusic = function(id) {
9485 var loading, options, songs;
9486 loading = App.request("loading:get:view", tr('Loading albums'));
9487 this.layout.regionContent.show(loading);
9488 options = {
9489 filter: {
9490 artistid: id
9493 songs = App.request("song:entities", options);
9494 return App.execute("when:entity:fetched", songs, (function(_this) {
9495 return function() {
9496 var albumsCollection, songsCollections;
9497 songsCollections = App.request("song:albumparse:entities", songs);
9498 albumsCollection = App.request("albums:withsongs:view", songsCollections);
9499 return _this.layout.regionContent.show(albumsCollection);
9501 })(this));
9504 return Controller;
9506 })(App.Controllers.Base);
9509 this.Kodi.module("ArtistApp.Show", function(Show, App, Backbone, Marionette, $, _) {
9510 Show.PageLayout = (function(superClass) {
9511 extend(PageLayout, superClass);
9513 function PageLayout() {
9514 return PageLayout.__super__.constructor.apply(this, arguments);
9517 PageLayout.prototype.className = 'artist-show detail-container';
9519 return PageLayout;
9521 })(App.Views.LayoutWithHeaderView);
9522 Show.HeaderLayout = (function(superClass) {
9523 extend(HeaderLayout, superClass);
9525 function HeaderLayout() {
9526 return HeaderLayout.__super__.constructor.apply(this, arguments);
9529 HeaderLayout.prototype.className = 'artist-details';
9531 return HeaderLayout;
9533 })(App.Views.LayoutDetailsHeaderView);
9534 Show.Details = (function(superClass) {
9535 extend(Details, superClass);
9537 function Details() {
9538 return Details.__super__.constructor.apply(this, arguments);
9541 Details.prototype.template = 'apps/artist/show/details_meta';
9543 Details.prototype.triggers = {
9544 "click .play": "artist:play",
9545 "click .add": "artist:add",
9546 "click .localadd": "artist:localadd",
9547 "click .localplay": "artist:localplay",
9548 "click .edit": "artist:edit"
9551 return Details;
9553 })(App.Views.DetailsItem);
9554 return Show.ArtistTeaser = (function(superClass) {
9555 extend(ArtistTeaser, superClass);
9557 function ArtistTeaser() {
9558 return ArtistTeaser.__super__.constructor.apply(this, arguments);
9561 ArtistTeaser.prototype.tagName = "div";
9563 ArtistTeaser.prototype.initialize = function() {
9564 return this.model.set({
9565 actions: {
9566 thumbs: tr('Thumbs up')
9571 ArtistTeaser.prototype.attributes = function() {
9572 return this.watchedAttributes('card-detail');
9575 return ArtistTeaser;
9577 })(App.ArtistApp.List.ArtistTeaser);
9580 soundManager.setup({
9581 url: 'lib/soundmanager/swf/',
9582 flashVersion: 9,
9583 preferFlash: true,
9584 useHTML5Audio: true,
9585 useFlashBlock: false,
9586 flashLoadTimeout: 3000,
9587 debugMode: false,
9588 noSWFCache: true,
9589 debugFlash: false,
9590 flashPollingInterval: 1000,
9591 html5PollingInterval: 1000,
9592 onready: function() {
9593 return $(window).trigger('audiostream:ready');
9595 ontimeout: function() {
9596 $(window).trigger('audiostream:timeout');
9597 soundManager.flashLoadTimeout = 0;
9598 soundManager.onerror = {};
9599 return soundManager.reboot();
9603 this.Kodi.module("BrowserApp", function(BrowserApp, App, Backbone, Marionette, $, _) {
9604 var API;
9605 BrowserApp.Router = (function(superClass) {
9606 extend(Router, superClass);
9608 function Router() {
9609 return Router.__super__.constructor.apply(this, arguments);
9612 Router.prototype.appRoutes = {
9613 "browser": "list",
9614 "browser/:media/:id": "view"
9617 return Router;
9619 })(App.Router.Base);
9620 API = {
9621 list: function() {
9622 return new BrowserApp.List.Controller;
9624 view: function(media, id) {
9625 return new BrowserApp.List.Controller({
9626 media: media,
9627 id: id
9631 return App.on("before:start", function() {
9632 return new BrowserApp.Router({
9633 controller: API
9638 this.Kodi.module("BrowserApp.List", function(List, App, Backbone, Marionette, $, _) {
9639 var API;
9640 API = {
9641 bindFileTriggers: function(view) {
9642 App.listenTo(view, 'childview:file:play', (function(_this) {
9643 return function(set, item) {
9644 var playlist;
9645 playlist = App.request("command:kodi:controller", item.model.get('player'), 'PlayList');
9646 return playlist.play('file', item.model.get('file'));
9648 })(this));
9649 App.listenTo(view, 'childview:file:queue', (function(_this) {
9650 return function(set, item) {
9651 var playlist;
9652 playlist = App.request("command:kodi:controller", item.model.get('player'), 'PlayList');
9653 return playlist.add('file', item.model.get('file'));
9655 })(this));
9656 return App.listenTo(view, 'childview:file:download', (function(_this) {
9657 return function(set, item) {
9658 return App.request("command:kodi:controller", 'auto', 'Files').downloadFile(item.model.get('file'));
9660 })(this));
9662 bindFolderTriggers: function(view) {
9663 App.listenTo(view, 'childview:folder:play', (function(_this) {
9664 return function(set, item) {
9665 return App.request("command:kodi:controller", item.model.get('player'), 'PlayList').play('directory', item.model.get('file'));
9667 })(this));
9668 return App.listenTo(view, 'childview:folder:queue', (function(_this) {
9669 return function(set, item) {
9670 return App.request("command:kodi:controller", item.model.get('player'), 'PlayList').add('directory', item.model.get('file'));
9672 })(this));
9674 getFileListView: function(collection) {
9675 var fileView;
9676 fileView = new List.FileList({
9677 collection: collection
9679 API.bindFileTriggers(fileView);
9680 return fileView;
9682 getFolderListView: function(collection) {
9683 var folderView;
9684 folderView = new List.FolderList({
9685 collection: collection
9687 App.listenTo(folderView, 'childview:folder:open', (function(_this) {
9688 return function(set, item) {
9689 return App.navigate(item.model.get('url'), {
9690 trigger: true
9693 })(this));
9694 API.bindFolderTriggers(folderView);
9695 return folderView;
9698 List.Controller = (function(superClass) {
9699 extend(Controller, superClass);
9701 function Controller() {
9702 return Controller.__super__.constructor.apply(this, arguments);
9705 Controller.prototype.sourceCollection = {};
9707 Controller.prototype.backButtonModel = {};
9709 Controller.prototype.initialize = function(options) {
9710 if (options == null) {
9711 options = {};
9713 this.layout = this.getLayout();
9714 this.listenTo(this.layout, "show", (function(_this) {
9715 return function() {
9716 _this.getSources(options);
9717 return _this.getFolderLayout();
9719 })(this));
9720 return App.regionContent.show(this.layout);
9723 Controller.prototype.getLayout = function() {
9724 return new List.ListLayout();
9727 Controller.prototype.getFolderLayout = function() {
9728 var options;
9729 options = {
9730 sortSettings: this.getSort()
9732 this.folderLayout = new List.FolderLayout(options);
9733 this.listenTo(this.folderLayout, 'browser:sort', (function(_this) {
9734 return function(sort, $el) {
9735 return _this.setSort(sort, $el);
9737 })(this));
9738 this.listenTo(this.folderLayout, 'browser:play', (function(_this) {
9739 return function(view) {
9740 if (_this.model) {
9741 return App.request("command:kodi:controller", _this.model.get('player'), 'PlayList').play('directory', _this.model.get('file'));
9744 })(this));
9745 this.listenTo(this.folderLayout, 'browser:queue', (function(_this) {
9746 return function(view) {
9747 if (_this.model) {
9748 return App.request("command:kodi:controller", _this.model.get('player'), 'PlayList').add('directory', _this.model.get('file'));
9751 })(this));
9752 return this.layout.regionContent.show(this.folderLayout);
9755 Controller.prototype.setSort = function(sort, $el) {
9756 var sortSettings;
9757 sortSettings = this.getSort();
9758 if (sortSettings.method === sort) {
9759 sortSettings.order = sortSettings.order === 'ascending' ? 'descending' : 'ascending';
9761 if ($el) {
9762 $el.removeClassStartsWith('order-').addClass('order-' + sortSettings.order).addClass('active');
9764 sortSettings.method = sort;
9765 if (sortSettings.method) {
9766 config.set('app', 'browserSort', sortSettings);
9768 if (this.model) {
9769 return this.getFolder(this.model);
9773 Controller.prototype.getSort = function() {
9774 return config.get('app', 'browserSort', {
9775 method: 'none',
9776 order: 'ascending'
9780 Controller.prototype.getSources = function(options) {
9781 var sources;
9782 sources = App.request("file:source:entities", 'video');
9783 return App.execute("when:entity:fetched", sources, (function(_this) {
9784 return function() {
9785 var setView, sets;
9786 _this.sourceCollection = sources;
9787 sets = App.request("file:source:media:entities", sources);
9788 setView = new List.SourcesSet({
9789 collection: sets
9791 _this.layout.regionSidebarFirst.show(setView);
9792 _this.listenTo(setView, 'childview:childview:source:open', function(set, item) {
9793 return _this.getFolder(item.model);
9795 return _this.loadFromUrl(options);
9797 })(this));
9800 Controller.prototype.loadFromUrl = function(options) {
9801 var model;
9802 if (options.media && options.id) {
9803 model = App.request("file:url:entity", options.media, options.id);
9804 return this.getFolder(model);
9808 Controller.prototype.getFolder = function(model) {
9809 var collection, pathCollection, sortSettings;
9810 this.model = model;
9811 App.navigate(model.get('url'));
9812 sortSettings = this.getSort();
9813 collection = App.request("file:entities", {
9814 file: model.get('file'),
9815 media: model.get('media'),
9816 sort: sortSettings
9818 pathCollection = App.request("file:path:entities", model.get('file'), this.sourceCollection);
9819 this.getPathList(pathCollection);
9820 return App.execute("when:entity:fetched", collection, (function(_this) {
9821 return function() {
9822 var collections;
9823 collections = App.request("file:parsed:entities", collection);
9824 _this.getFolderList(collections.directory);
9825 return _this.getFileList(collections.file);
9827 })(this));
9830 Controller.prototype.getFolderListView = function(collection) {
9831 var folderView;
9832 folderView = new List.FolderList({
9833 collection: collection
9835 this.listenTo(folderView, 'childview:folder:open', (function(_this) {
9836 return function(set, item) {
9837 return _this.getFolder(item.model);
9839 })(this));
9840 API.bindFolderTriggers(folderView);
9841 return folderView;
9844 Controller.prototype.getFolderList = function(collection) {
9845 this.folderLayout.regionFolders.show(this.getFolderListView(collection));
9846 return this.getBackButton();
9849 Controller.prototype.getFileListView = function(collection) {
9850 return API.getFileListView(collection);
9853 Controller.prototype.getFileList = function(collection) {
9854 return this.folderLayout.regionFiles.show(this.getFileListView(collection));
9857 Controller.prototype.getPathList = function(collection) {
9858 var pathView;
9859 pathView = new List.PathList({
9860 collection: collection
9862 this.folderLayout.regionPath.show(pathView);
9863 this.setBackModel(collection);
9864 return this.listenTo(pathView, 'childview:folder:open', (function(_this) {
9865 return function(set, item) {
9866 return _this.getFolder(item.model);
9868 })(this));
9871 Controller.prototype.setBackModel = function(pathCollection) {
9872 if (pathCollection.length >= 2) {
9873 return this.backButtonModel = pathCollection.models[pathCollection.length - 2];
9874 } else {
9875 return this.backButtonModel = {};
9879 Controller.prototype.getBackButton = function() {
9880 var backView;
9881 if (this.backButtonModel.attributes) {
9882 backView = new List.Back({
9883 model: this.backButtonModel
9885 this.folderLayout.regionBack.show(backView);
9886 return this.listenTo(backView, 'folder:open', (function(_this) {
9887 return function(model) {
9888 return _this.getFolder(model.model);
9890 })(this));
9891 } else {
9892 return this.folderLayout.regionBack.empty();
9896 Controller.prototype.getFileViewByPath = function(path, media, callback) {
9897 var collection;
9898 collection = App.request("file:entities", {
9899 file: path,
9900 media: media
9902 return App.execute("when:entity:fetched", collection, (function(_this) {
9903 return function() {
9904 var view;
9905 view = _this.getFileListView(collection);
9906 if (callback) {
9907 return callback(view);
9910 })(this));
9913 return Controller;
9915 })(App.Controllers.Base);
9916 App.reqres.setHandler("browser:file:view", function(collection) {
9917 return API.getFileListView(collection);
9919 return App.reqres.setHandler("browser:directory:view", function(collection) {
9920 return API.getFolderListView(collection);
9924 this.Kodi.module("BrowserApp.List", function(List, App, Backbone, Marionette, $, _) {
9925 List.ListLayout = (function(superClass) {
9926 extend(ListLayout, superClass);
9928 function ListLayout() {
9929 return ListLayout.__super__.constructor.apply(this, arguments);
9932 ListLayout.prototype.className = "browser-page";
9934 return ListLayout;
9936 })(App.Views.LayoutWithSidebarFirstView);
9939 Sources
9941 List.Source = (function(superClass) {
9942 extend(Source, superClass);
9944 function Source() {
9945 return Source.__super__.constructor.apply(this, arguments);
9948 Source.prototype.template = 'apps/browser/list/source';
9950 Source.prototype.tagName = 'li';
9952 Source.prototype.triggers = {
9953 'click .source': 'source:open'
9956 Source.prototype.attributes = function() {
9957 return {
9958 "class": 'type-' + this.model.get('sourcetype')
9962 return Source;
9964 })(App.Views.ItemView);
9965 List.Sources = (function(superClass) {
9966 extend(Sources, superClass);
9968 function Sources() {
9969 return Sources.__super__.constructor.apply(this, arguments);
9972 Sources.prototype.template = 'apps/browser/list/source_set';
9974 Sources.prototype.childView = List.Source;
9976 Sources.prototype.tagName = "div";
9978 Sources.prototype.childViewContainer = 'ul.sources';
9980 Sources.prototype.className = "source-set";
9982 Sources.prototype.initialize = function() {
9983 return this.collection = this.model.get('sources');
9986 return Sources;
9988 })(App.Views.CompositeView);
9989 List.SourcesSet = (function(superClass) {
9990 extend(SourcesSet, superClass);
9992 function SourcesSet() {
9993 return SourcesSet.__super__.constructor.apply(this, arguments);
9996 SourcesSet.prototype.childView = List.Sources;
9998 SourcesSet.prototype.tagName = "div";
10000 SourcesSet.prototype.className = "sources-sets";
10002 return SourcesSet;
10004 })(App.Views.CollectionView);
10007 Folder
10009 List.FolderLayout = (function(superClass) {
10010 extend(FolderLayout, superClass);
10012 function FolderLayout() {
10013 return FolderLayout.__super__.constructor.apply(this, arguments);
10016 FolderLayout.prototype.template = 'apps/browser/list/folder_layout';
10018 FolderLayout.prototype.className = "folder-page-wrapper";
10020 FolderLayout.prototype.regions = {
10021 regionPath: '.path',
10022 regionFolders: '.folders',
10023 regionFiles: '.files',
10024 regionBack: '.back'
10027 FolderLayout.prototype.triggers = {
10028 'click .play': 'browser:play',
10029 'click .queue': 'browser:queue'
10032 FolderLayout.prototype.events = {
10033 'click .sorts li': 'sortList'
10036 FolderLayout.prototype.sortList = function(e) {
10037 $('.sorts li', this.$el).removeClass('active');
10038 return this.trigger('browser:sort', $(e.target).data('sort'), $(e.target));
10041 FolderLayout.prototype.onRender = function() {
10042 $('.sorts li', this.$el).addClass('order-' + this.options.sortSettings.order);
10043 return $('.sorts li[data-sort=' + this.options.sortSettings.method + ']', this.$el).addClass('active');
10046 return FolderLayout;
10048 })(App.Views.LayoutView);
10049 List.Item = (function(superClass) {
10050 extend(Item, superClass);
10052 function Item() {
10053 return Item.__super__.constructor.apply(this, arguments);
10056 Item.prototype.template = 'apps/browser/list/file';
10058 Item.prototype.tagName = 'li';
10060 Item.prototype.initialize = function() {
10061 return this.model.set({
10062 labelHtml: this.formatText(this.model.get('label'))
10066 Item.prototype.onBeforeRender = function() {
10067 if (!this.model.get('labelHtml')) {
10068 return this.model.set({
10069 labelHtml: this.model.escape('label')
10074 return Item;
10076 })(App.Views.ItemView);
10077 List.Folder = (function(superClass) {
10078 extend(Folder, superClass);
10080 function Folder() {
10081 return Folder.__super__.constructor.apply(this, arguments);
10084 Folder.prototype.className = 'folder';
10086 Folder.prototype.triggers = {
10087 'click .title': 'folder:open',
10088 'dblclick .title': 'file:play',
10089 'click .play': 'folder:play',
10090 'click .queue': 'folder:queue'
10093 Folder.prototype.events = {
10094 "click .dropdown > i": "populateModelMenu"
10097 Folder.prototype.initialize = function() {
10098 var menu;
10099 menu = {
10100 queue: tr('Queue in Kodi')
10102 return this.model.set({
10103 menu: menu
10107 return Folder;
10109 })(List.Item);
10110 List.EmptyFiles = (function(superClass) {
10111 extend(EmptyFiles, superClass);
10113 function EmptyFiles() {
10114 return EmptyFiles.__super__.constructor.apply(this, arguments);
10117 EmptyFiles.prototype.tagName = 'li';
10119 EmptyFiles.prototype.initialize = function() {
10120 return this.model.set({
10121 id: 'empty',
10122 content: t.gettext('no media in this folder')
10126 return EmptyFiles;
10128 })(App.Views.EmptyViewPage);
10129 List.File = (function(superClass) {
10130 extend(File, superClass);
10132 function File() {
10133 return File.__super__.constructor.apply(this, arguments);
10136 File.prototype.className = 'file';
10138 File.prototype.triggers = {
10139 'click .play': 'file:play',
10140 'dblclick .title': 'file:play',
10141 'click .queue': 'file:queue',
10142 'click .download': 'file:download'
10145 File.prototype.events = {
10146 "click .dropdown > i": "populateModelMenu"
10149 File.prototype.initialize = function() {
10150 var menu;
10151 menu = {
10152 queue: tr('Queue in Kodi')
10154 if (this.model.get('filetype') === 'file' && this.model.get('file').lastIndexOf('plugin://', 0) !== 0) {
10155 menu.download = tr('Download');
10157 return this.model.set({
10158 menu: menu
10162 return File;
10164 })(List.Item);
10165 List.FolderList = (function(superClass) {
10166 extend(FolderList, superClass);
10168 function FolderList() {
10169 return FolderList.__super__.constructor.apply(this, arguments);
10172 FolderList.prototype.tagName = 'ul';
10174 FolderList.prototype.className = 'browser-folder-list';
10176 FolderList.prototype.childView = List.Folder;
10178 return FolderList;
10180 })(App.Views.CollectionView);
10181 List.FileList = (function(superClass) {
10182 extend(FileList, superClass);
10184 function FileList() {
10185 return FileList.__super__.constructor.apply(this, arguments);
10188 FileList.prototype.tagName = 'ul';
10190 FileList.prototype.className = 'browser-file-list';
10192 FileList.prototype.childView = List.File;
10194 FileList.prototype.emptyView = List.EmptyFiles;
10196 return FileList;
10198 })(App.Views.CollectionView);
10201 Path
10203 List.Path = (function(superClass) {
10204 extend(Path, superClass);
10206 function Path() {
10207 return Path.__super__.constructor.apply(this, arguments);
10210 Path.prototype.template = 'apps/browser/list/path';
10212 Path.prototype.tagName = 'li';
10214 Path.prototype.triggers = {
10215 'click .title': 'folder:open'
10218 return Path;
10220 })(App.Views.ItemView);
10221 List.PathList = (function(superClass) {
10222 extend(PathList, superClass);
10224 function PathList() {
10225 return PathList.__super__.constructor.apply(this, arguments);
10228 PathList.prototype.tagName = 'ul';
10230 PathList.prototype.childView = List.Path;
10232 return PathList;
10234 })(App.Views.CollectionView);
10235 return List.Back = (function(superClass) {
10236 extend(Back, superClass);
10238 function Back() {
10239 return Back.__super__.constructor.apply(this, arguments);
10242 Back.prototype.template = 'apps/browser/list/back_button';
10244 Back.prototype.tagName = 'div';
10246 Back.prototype.className = 'back-button';
10248 Back.prototype.triggers = {
10249 'click .title': 'folder:open',
10250 'click i': 'folder:open'
10253 return Back;
10255 })(App.Views.ItemView);
10258 this.Kodi.module("CastApp", function(CastApp, App, Backbone, Marionette, $, _) {
10259 var API;
10260 API = {
10261 getCastCollection: function(cast, origin) {
10262 return App.request("cast:entities", cast, origin);
10264 getCastView: function(collection) {
10265 var view;
10266 view = new CastApp.List.CastList({
10267 collection: collection
10269 App.listenTo(view, 'childview:cast:google', function(parent, child) {
10270 return window.open('https://www.google.com/webhp?#q=' + encodeURIComponent(child.model.get('name')));
10272 App.listenTo(view, 'childview:cast:imdb', function(parent, child) {
10273 return window.open('http://www.imdb.com/find?s=nm&q=' + encodeURIComponent(child.model.get('name')));
10275 return view;
10278 return App.reqres.setHandler('cast:list:view', function(cast, origin) {
10279 var collection;
10280 collection = API.getCastCollection(cast, origin);
10281 return API.getCastView(collection);
10285 this.Kodi.module("CastApp.List", function(List, App, Backbone, Marionette, $, _) {
10286 List.CastTeaser = (function(superClass) {
10287 extend(CastTeaser, superClass);
10289 function CastTeaser() {
10290 return CastTeaser.__super__.constructor.apply(this, arguments);
10293 CastTeaser.prototype.template = 'apps/cast/list/cast';
10295 CastTeaser.prototype.tagName = "li";
10297 CastTeaser.prototype.triggers = {
10298 "click .imdb": "cast:imdb",
10299 "click .google": "cast:google"
10302 CastTeaser.prototype.onRender = function() {
10303 return _.defer(function() {
10304 var defaultThumb;
10305 defaultThumb = App.request("images:path:get", '');
10306 return $('img', this.$el).on('error', function(e) {
10307 return $(this).attr('src', defaultThumb);
10312 return CastTeaser;
10314 })(App.Views.ItemView);
10315 return List.CastList = (function(superClass) {
10316 extend(CastList, superClass);
10318 function CastList() {
10319 return CastList.__super__.constructor.apply(this, arguments);
10322 CastList.prototype.childView = List.CastTeaser;
10324 CastList.prototype.tagName = "ul";
10326 CastList.prototype.className = "cast-full";
10328 return CastList;
10330 })(App.Views.CollectionView);
10333 this.Kodi.module("CategoryApp", function(CategoryApp, App, Backbone, Marionette, $, _) {
10334 var API;
10335 CategoryApp.Router = (function(superClass) {
10336 extend(Router, superClass);
10338 function Router() {
10339 return Router.__super__.constructor.apply(this, arguments);
10342 Router.prototype.appRoutes = {
10343 "music/genres": "musicGenres"
10346 return Router;
10348 })(App.Router.Base);
10349 API = {
10350 musicGenres: function() {
10351 return new CategoryApp.List.Controller({
10352 entityKey: 'genre:entities',
10353 media: 'audio',
10354 subNavParent: 'music'
10358 return App.on("before:start", function() {
10359 return new CategoryApp.Router({
10360 controller: API
10365 this.Kodi.module("CategoryApp.List", function(List, App, Backbone, Marionette, $, _) {
10366 return List.Controller = (function(superClass) {
10367 extend(Controller, superClass);
10369 function Controller() {
10370 return Controller.__super__.constructor.apply(this, arguments);
10373 Controller.prototype.initialize = function(options) {
10374 var collection;
10375 collection = App.request(this.getOption('entityKey'), this.getOption('media'));
10376 return App.execute("when:entity:fetched", collection, (function(_this) {
10377 return function() {
10378 _this.layout = _this.getLayoutView(collection);
10379 _this.listenTo(_this.layout, "show", function() {
10380 _this.renderList(collection);
10381 return _this.getSubNav();
10383 return App.regionContent.show(_this.layout);
10385 })(this));
10388 Controller.prototype.getLayoutView = function(collection) {
10389 return new List.Layout({
10390 collection: collection
10394 Controller.prototype.renderList = function(collection) {
10395 var view;
10396 view = new List.CategoryList({
10397 collection: collection
10399 return this.layout.regionContent.show(view);
10402 Controller.prototype.getSubNav = function() {
10403 var subNav;
10404 subNav = App.request("navMain:children:show", this.getOption('subNavParent'), 'Sections');
10405 return this.layout.regionSidebarFirst.show(subNav);
10408 return Controller;
10410 })(App.Controllers.Base);
10413 this.Kodi.module("CategoryApp.List", function(List, App, Backbone, Marionette, $, _) {
10414 List.Layout = (function(superClass) {
10415 extend(Layout, superClass);
10417 function Layout() {
10418 return Layout.__super__.constructor.apply(this, arguments);
10421 Layout.prototype.className = "category-list";
10423 return Layout;
10425 })(App.Views.LayoutWithSidebarFirstView);
10426 List.Item = (function(superClass) {
10427 extend(Item, superClass);
10429 function Item() {
10430 return Item.__super__.constructor.apply(this, arguments);
10433 Item.prototype.template = 'apps/category/list/item';
10435 Item.prototype.tagName = "li";
10437 Item.prototype.className = "card category";
10439 return Item;
10441 })(App.Views.CardView);
10442 return List.CategoryList = (function(superClass) {
10443 extend(CategoryList, superClass);
10445 function CategoryList() {
10446 return CategoryList.__super__.constructor.apply(this, arguments);
10449 CategoryList.prototype.childView = List.Item;
10451 CategoryList.prototype.tagName = "ul";
10453 CategoryList.prototype.className = "card-grid--square";
10455 return CategoryList;
10457 })(App.Views.CollectionView);
10460 this.Kodi.module("CommandApp", function(CommandApp, App, Backbone, Marionette, $, _) {
10461 var API;
10462 API = {
10463 currentAudioPlaylistController: function() {
10464 var stateObj;
10465 stateObj = App.request("state:current");
10466 return App.request("command:" + stateObj.getPlayer() + ":controller", 'audio', 'PlayList');
10468 currentVideoPlayerController: function() {
10469 var method, stateObj;
10470 stateObj = App.request("state:current");
10471 method = stateObj.getPlayer() === 'local' ? 'VideoPlayer' : 'PlayList';
10472 return App.request("command:" + stateObj.getPlayer() + ":controller", 'video', method);
10477 Kodi.
10479 App.reqres.setHandler("command:kodi:player", function(method, params, callback) {
10480 var commander;
10481 commander = new CommandApp.Kodi.Player('auto');
10482 return commander.sendCommand(method, params, callback);
10484 App.reqres.setHandler("command:kodi:controller", function(media, controller) {
10485 if (media == null) {
10486 media = 'auto';
10488 return new CommandApp.Kodi[controller](media);
10492 Local.
10494 App.reqres.setHandler("command:local:player", function(method, params, callback) {
10495 var commander;
10496 commander = new CommandApp.Local.Player('audio');
10497 return commander.sendCommand(method, params, callback);
10499 App.reqres.setHandler("command:local:controller", function(media, controller) {
10500 if (media == null) {
10501 media = 'auto';
10503 return new CommandApp.Local[controller](media);
10507 Wrappers single command for playing in kodi and local.
10509 App.commands.setHandler("command:audio:play", function(type, value) {
10510 return API.currentAudioPlaylistController().play(type, value);
10512 App.commands.setHandler("command:audio:add", function(type, value) {
10513 return API.currentAudioPlaylistController().add(type, value);
10515 App.commands.setHandler("command:video:play", function(model, type, resume, callback) {
10516 var value;
10517 if (resume == null) {
10518 resume = 0;
10520 value = model.get(type);
10521 return API.currentVideoPlayerController().play(type, value, model, resume, function(resp) {
10522 var kodiVideo, stateObj;
10523 stateObj = App.request("state:current");
10524 if (stateObj.getPlayer() === 'kodi') {
10525 kodiVideo = App.request("command:kodi:controller", 'video', 'GUI');
10526 return kodiVideo.setFullScreen(true, callback);
10532 Commands that are generally used by settings pages.
10534 App.commands.setHandler("command:kodi:audio:clean", function() {
10535 return App.request("command:kodi:controller", 'auto', 'AudioLibrary').clean();
10537 App.commands.setHandler("command:kodi:video:clean", function() {
10538 return App.request("command:kodi:controller", 'auto', 'VideoLibrary').clean();
10540 return App.addInitializer(function() {});
10543 this.Kodi.module("CommandApp.Kodi", function(Api, App, Backbone, Marionette, $, _) {
10544 return Api.Base = (function(superClass) {
10545 extend(Base, superClass);
10547 function Base() {
10548 return Base.__super__.constructor.apply(this, arguments);
10551 Base.prototype.ajaxOptions = {};
10553 Base.prototype.initialize = function(options) {
10554 if (options == null) {
10555 options = {};
10557 $.jsonrpc.defaultUrl = helpers.url.baseKodiUrl("Base");
10558 return this.setOptions(options);
10561 Base.prototype.setOptions = function(options) {
10562 return this.ajaxOptions = options;
10565 Base.prototype.multipleCommands = function(commands, callback, fail) {
10566 var obj;
10567 obj = $.jsonrpc(commands, this.ajaxOptions);
10568 obj.fail((function(_this) {
10569 return function(error) {
10570 _this.doCallback(fail, error);
10571 return _this.onError(commands, error);
10573 })(this));
10574 obj.done((function(_this) {
10575 return function(response) {
10576 response = _this.parseResponse(commands, response);
10577 _this.triggerMethod("response:ready", response);
10578 if (callback != null) {
10579 return _this.doCallback(callback, response);
10582 })(this));
10583 return obj;
10586 Base.prototype.singleCommand = function(command, params, callback, fail) {
10587 var obj;
10588 command = {
10589 method: command,
10590 url: helpers.url.baseKodiUrl(command)
10592 if ((params != null) && (params.length > 0 || _.isObject(params))) {
10593 command.params = params;
10595 obj = this.multipleCommands([command], callback, fail);
10596 return obj;
10599 Base.prototype.parseResponse = function(commands, response) {
10600 var i, result, results;
10601 results = [];
10602 for (i in response) {
10603 result = response[i];
10604 if (result.result || result.result === false) {
10605 results.push(result.result);
10606 } else {
10607 this.onError(commands[i], result);
10610 if (commands.length === 1 && results.length === 1) {
10611 results = results[0];
10613 return results;
10616 Base.prototype.paramObj = function(key, val) {
10617 return helpers.global.paramObj(key, val);
10620 Base.prototype.doCallback = function(callback, response) {
10621 if (callback != null) {
10622 return callback(response);
10626 Base.prototype.onError = function(commands, error) {
10627 return helpers.debug.rpcError(commands, error);
10630 return Base;
10632 })(Marionette.Object);
10635 this.Kodi.module("CommandApp.Kodi", function(Api, App, Backbone, Marionette, $, _) {
10636 Api.Commander = (function(superClass) {
10637 extend(Commander, superClass);
10639 function Commander() {
10640 return Commander.__super__.constructor.apply(this, arguments);
10643 Commander.prototype.playerActive = 0;
10645 Commander.prototype.playerName = 'music';
10647 Commander.prototype.playerForced = false;
10649 Commander.prototype.playerIds = {
10650 audio: 0,
10651 video: 1
10654 Commander.prototype.setPlayer = function(player) {
10655 if (player === 'audio' || player === 'video') {
10656 this.playerActive = this.playerIds[player];
10657 this.playerName = player;
10658 return this.playerForced = true;
10662 Commander.prototype.getPlayer = function() {
10663 return this.playerActive;
10666 Commander.prototype.getPlayerName = function() {
10667 return this.playerName;
10670 Commander.prototype.playerIdToName = function(playerId) {
10671 playerName;
10672 var id, name, playerName, ref;
10673 ref = this.playerIds;
10674 for (name in ref) {
10675 id = ref[name];
10676 if (id === playerId) {
10677 playerName = name;
10680 return playerName;
10683 Commander.prototype.commandNameSpace = 'JSONRPC';
10685 Commander.prototype.getCommand = function(command, namespace) {
10686 if (namespace == null) {
10687 namespace = this.commandNameSpace;
10689 return namespace + '.' + command;
10692 Commander.prototype.sendCommand = function(command, params, callback, fail) {
10693 return this.singleCommand(this.getCommand(command), params, ((function(_this) {
10694 return function(resp) {
10695 return _this.doCallback(callback, resp);
10697 })(this)), (function(_this) {
10698 return function(err) {
10699 return _this.doCallback(fail, err);
10701 })(this));
10704 return Commander;
10706 })(Api.Base);
10707 return Api.Player = (function(superClass) {
10708 extend(Player, superClass);
10710 function Player() {
10711 return Player.__super__.constructor.apply(this, arguments);
10714 Player.prototype.commandNameSpace = 'Player';
10716 Player.prototype.playlistApi = {};
10718 Player.prototype.initialize = function(media) {
10719 if (media == null) {
10720 media = 'audio';
10722 this.setPlayer(media);
10723 return this.playlistApi = App.request("playlist:kodi:entity:api");
10726 Player.prototype.getParams = function(params, callback) {
10727 var defaultParams;
10728 if (params == null) {
10729 params = [];
10731 if (this.playerForced) {
10732 defaultParams = [this.playerActive];
10733 return this.doCallback(callback, defaultParams.concat(params));
10734 } else {
10735 return this.getActivePlayers((function(_this) {
10736 return function(activeId) {
10737 defaultParams = [activeId];
10738 return _this.doCallback(callback, defaultParams.concat(params));
10740 })(this));
10744 Player.prototype.getActivePlayers = function(callback) {
10745 return this.singleCommand(this.getCommand("GetActivePlayers"), {}, (function(_this) {
10746 return function(resp) {
10747 if (resp.length > 0) {
10748 _this.playerActive = resp[0].playerid;
10749 _this.playerName = _this.playerIdToName(_this.playerActive);
10750 _this.triggerMethod("player:ready", _this.playerActive);
10751 return _this.doCallback(callback, _this.playerActive);
10752 } else {
10753 return _this.doCallback(callback, _this.playerActive);
10756 })(this));
10759 Player.prototype.sendCommand = function(command, params, callback, fail) {
10760 if (params == null) {
10761 params = [];
10763 return this.getParams(params, (function(_this) {
10764 return function(playerParams) {
10765 return _this.singleCommand(_this.getCommand(command), playerParams, (function(resp) {
10766 return _this.doCallback(callback, resp);
10767 }), function(err) {
10768 return _this.doCallback(fail, err);
10771 })(this));
10774 Player.prototype.playEntity = function(type, value, options, callback) {
10775 var params;
10776 if (options == null) {
10777 options = {};
10779 params = {
10780 'item': this.paramObj(type, value),
10781 'options': options
10783 if (type === 'position') {
10784 params.item.playlistid = this.getPlayer();
10786 return this.singleCommand(this.getCommand('Open', 'Player'), params, (function(_this) {
10787 return function(resp) {
10788 if (!App.request('sockets:active')) {
10789 App.request('state:kodi:update');
10791 return _this.doCallback(callback, resp);
10793 })(this));
10796 Player.prototype.setPartyMode = function(op, callback) {
10797 if (op == null) {
10798 op = 'toggle';
10800 return this.sendCommand('SetPartymode', [op], (function(_this) {
10801 return function(resp) {
10802 return _this.doCallback(callback, resp);
10804 })(this));
10807 Player.prototype.getPlaying = function(callback) {
10808 var obj;
10809 obj = {
10810 active: false,
10811 properties: false,
10812 item: false
10814 return this.singleCommand(this.getCommand('GetActivePlayers'), {}, (function(_this) {
10815 return function(resp) {
10816 var commands, itemFields, playerFields;
10817 if (resp.length > 0) {
10818 obj.active = resp[0];
10819 commands = [];
10820 itemFields = helpers.entities.getFields(_this.playlistApi.fields, 'full');
10821 playerFields = ["playlistid", "speed", "position", "totaltime", "time", "percentage", "shuffled", "repeat", "canrepeat", "canshuffle", "canseek", "partymode"];
10822 commands.push({
10823 method: _this.getCommand('GetProperties'),
10824 params: [obj.active.playerid, playerFields]
10826 commands.push({
10827 method: _this.getCommand('GetItem'),
10828 params: [obj.active.playerid, itemFields]
10830 return _this.multipleCommands(commands, function(playing) {
10831 obj.properties = playing[0];
10832 obj.item = playing[1].item;
10833 return _this.doCallback(callback, obj);
10835 } else {
10836 return _this.doCallback(callback, false);
10839 })(this));
10842 return Player;
10844 })(Api.Commander);
10847 this.Kodi.module("CommandApp.Kodi", function(Api, App, Backbone, Marionette, $, _) {
10848 return Api.AddOn = (function(superClass) {
10849 extend(AddOn, superClass);
10851 function AddOn() {
10852 this.getAllAddons = bind(this.getAllAddons, this);
10853 this.getEnabledAddons = bind(this.getEnabledAddons, this);
10854 return AddOn.__super__.constructor.apply(this, arguments);
10857 AddOn.prototype.commandNameSpace = 'Addons';
10859 AddOn.prototype.addonAllFields = ["name", "version", "summary", "description", "path", "author", "thumbnail", "disclaimer", "fanart", "dependencies", "broken", "extrainfo", "rating", "enabled"];
10861 AddOn.prototype.getAddons = function(type, enabled, fields, callback) {
10862 if (type == null) {
10863 type = "unknown";
10865 if (enabled == null) {
10866 enabled = true;
10868 if (fields == null) {
10869 fields = [];
10871 return this.singleCommand(this.getCommand('GetAddons'), [type, "unknown", enabled, fields], (function(_this) {
10872 return function(resp) {
10873 return _this.doCallback(callback, resp.addons);
10875 })(this));
10878 AddOn.prototype.getEnabledAddons = function(load, callback) {
10879 var fields;
10880 if (load == null) {
10881 load = true;
10883 fields = load ? this.addonAllFields : ["name"];
10884 return this.getAddons("unknown", true, fields, (function(_this) {
10885 return function(resp) {
10886 return _this.doCallback(callback, resp);
10888 })(this));
10891 AddOn.prototype.getAllAddons = function(callback) {
10892 return this.getAddons("unknown", "all", this.addonAllFields, (function(_this) {
10893 return function(resp) {
10894 return _this.doCallback(callback, resp);
10896 })(this));
10899 AddOn.prototype.executeAddon = function(addonId, params, callback) {
10900 var opts;
10901 if (params == null) {
10902 params = {};
10904 opts = {
10905 addonid: addonId
10907 if (!_.isEmpty(params)) {
10908 opts.params = params;
10910 return this.singleCommand(this.getCommand('ExecuteAddon'), opts, (function(_this) {
10911 return function(resp) {
10912 return _this.doCallback(callback, resp.addons);
10914 })(this));
10917 return AddOn;
10919 })(Api.Commander);
10922 this.Kodi.module("CommandApp.Kodi", function(Api, App, Backbone, Marionette, $, _) {
10923 return Api.Application = (function(superClass) {
10924 extend(Application, superClass);
10926 function Application() {
10927 return Application.__super__.constructor.apply(this, arguments);
10930 Application.prototype.commandNameSpace = 'Application';
10932 Application.prototype.getProperties = function(callback) {
10933 return this.singleCommand(this.getCommand('GetProperties'), [["volume", "muted", "version"]], (function(_this) {
10934 return function(resp) {
10935 return _this.doCallback(callback, resp);
10937 })(this));
10940 Application.prototype.setVolume = function(volume, callback) {
10941 return this.singleCommand(this.getCommand('SetVolume'), [volume], (function(_this) {
10942 return function(resp) {
10943 return _this.doCallback(callback, resp);
10945 })(this));
10948 Application.prototype.toggleMute = function(callback) {
10949 var stateObj;
10950 stateObj = App.request("state:kodi");
10951 return this.singleCommand(this.getCommand('SetMute'), [!stateObj.getState('muted')], (function(_this) {
10952 return function(resp) {
10953 return _this.doCallback(callback, resp);
10955 })(this));
10958 Application.prototype.quit = function(callback) {
10959 return this.singleCommand(this.getCommand('Quit'), [], (function(_this) {
10960 return function(resp) {
10961 return _this.doCallback(callback, resp);
10963 })(this));
10966 return Application;
10968 })(Api.Commander);
10971 this.Kodi.module("CommandApp.Kodi", function(Api, App, Backbone, Marionette, $, _) {
10972 return Api.AudioLibrary = (function(superClass) {
10973 extend(AudioLibrary, superClass);
10975 function AudioLibrary() {
10976 return AudioLibrary.__super__.constructor.apply(this, arguments);
10979 AudioLibrary.prototype.commandNameSpace = 'AudioLibrary';
10981 AudioLibrary.prototype.setAlbumDetails = function(id, fields, callback) {
10982 var params;
10983 if (fields == null) {
10984 fields = {};
10986 params = {
10987 albumid: id
10989 params = _.extend(params, fields);
10990 return this.singleCommand(this.getCommand('SetAlbumDetails'), params, (function(_this) {
10991 return function(resp) {
10992 return _this.doCallback(callback, resp);
10994 })(this));
10997 AudioLibrary.prototype.setArtistDetails = function(id, fields, callback) {
10998 var params;
10999 if (fields == null) {
11000 fields = {};
11002 params = {
11003 artistid: id
11005 params = _.extend(params, fields);
11006 return this.singleCommand(this.getCommand('SetArtistDetails'), params, (function(_this) {
11007 return function(resp) {
11008 return _this.doCallback(callback, resp);
11010 })(this));
11013 AudioLibrary.prototype.setSongDetails = function(id, fields, callback) {
11014 var params;
11015 if (fields == null) {
11016 fields = {};
11018 params = {
11019 songid: id
11021 params = _.extend(params, fields);
11022 return this.singleCommand(this.getCommand('SetSongDetails'), params, (function(_this) {
11023 return function(resp) {
11024 return _this.doCallback(callback, resp);
11026 })(this));
11029 AudioLibrary.prototype.scan = function(callback) {
11030 return this.singleCommand(this.getCommand('Scan'), (function(_this) {
11031 return function(resp) {
11032 return _this.doCallback(callback, resp);
11034 })(this));
11037 AudioLibrary.prototype.clean = function(callback) {
11038 return this.singleCommand(this.getCommand('Clean'), {
11039 showdialogs: false
11040 }, (function(_this) {
11041 return function(resp) {
11042 return _this.doCallback(callback, resp);
11044 })(this));
11047 return AudioLibrary;
11049 })(Api.Commander);
11052 this.Kodi.module("CommandApp.Kodi", function(Api, App, Backbone, Marionette, $, _) {
11053 return Api.Files = (function(superClass) {
11054 extend(Files, superClass);
11056 function Files() {
11057 return Files.__super__.constructor.apply(this, arguments);
11060 Files.prototype.commandNameSpace = 'Files';
11062 Files.prototype.prepareDownload = function(file, callback) {
11063 return this.singleCommand(this.getCommand('PrepareDownload'), [file], (function(_this) {
11064 return function(resp) {
11065 return _this.doCallback(callback, resp);
11067 })(this));
11070 Files.prototype.downloadPath = function(file, callback) {
11071 return this.prepareDownload(file, (function(_this) {
11072 return function(resp) {
11073 return _this.doCallback(callback, resp.details.path);
11075 })(this));
11078 Files.prototype.downloadFile = function(file) {
11079 var dl;
11080 dl = window.open('about:blank', 'download');
11081 return this.downloadPath(file, function(path) {
11082 return dl.location = path;
11086 Files.prototype.videoStream = function(file, background, player) {
11087 var st;
11088 if (background == null) {
11089 background = '';
11091 if (player == null) {
11092 player = 'html5';
11094 st = helpers.global.localVideoPopup('about:blank');
11095 return this.downloadPath(file, function(path) {
11096 return st.location = "videoPlayer.html?player=" + player + '&src=' + encodeURIComponent(path) + '&bg=' + encodeURIComponent(background);
11100 return Files;
11102 })(Api.Commander);
11105 this.Kodi.module("CommandApp.Kodi", function(Api, App, Backbone, Marionette, $, _) {
11106 return Api.GUI = (function(superClass) {
11107 extend(GUI, superClass);
11109 function GUI() {
11110 return GUI.__super__.constructor.apply(this, arguments);
11113 GUI.prototype.commandNameSpace = 'GUI';
11115 GUI.prototype.setFullScreen = function(fullscreen, callback) {
11116 if (fullscreen == null) {
11117 fullscreen = true;
11119 return this.sendCommand("SetFullscreen", [fullscreen], (function(_this) {
11120 return function(resp) {
11121 return _this.doCallback(callback, resp);
11123 })(this));
11126 GUI.prototype.activateWindow = function(window, params, callback) {
11127 if (params == null) {
11128 params = [];
11130 return this.sendCommand("ActivateWindow", [window, params], (function(_this) {
11131 return function(resp) {
11132 return _this.doCallback(callback, resp);
11134 })(this));
11137 return GUI;
11139 })(Api.Commander);
11142 this.Kodi.module("CommandApp.Kodi", function(Api, App, Backbone, Marionette, $, _) {
11143 return Api.Input = (function(superClass) {
11144 extend(Input, superClass);
11146 function Input() {
11147 return Input.__super__.constructor.apply(this, arguments);
11150 Input.prototype.commandNameSpace = 'Input';
11152 Input.prototype.sendText = function(text, callback) {
11153 return this.singleCommand(this.getCommand('SendText'), [text], (function(_this) {
11154 return function(resp) {
11155 return _this.doCallback(callback, resp);
11157 })(this));
11160 Input.prototype.sendInput = function(type, params, callback) {
11161 if (params == null) {
11162 params = [];
11164 return this.singleCommand(this.getCommand(type), params, (function(_this) {
11165 return function(resp) {
11166 _this.doCallback(callback, resp);
11167 if (!App.request('sockets:active')) {
11168 return App.request('state:kodi:update', callback);
11171 })(this));
11174 return Input;
11176 })(Api.Commander);
11179 this.Kodi.module("CommandApp.Kodi", function(Api, App, Backbone, Marionette, $, _) {
11180 return Api.PlayList = (function(superClass) {
11181 extend(PlayList, superClass);
11183 function PlayList() {
11184 return PlayList.__super__.constructor.apply(this, arguments);
11187 PlayList.prototype.commandNameSpace = 'Playlist';
11189 PlayList.prototype.play = function(type, value, model, resume, callback) {
11190 if (resume == null) {
11191 resume = 0;
11193 return this.getItems((function(_this) {
11194 return function(currentPlaylist) {
11195 var inPlaylist, plItem, pos, stateObj;
11196 plItem = {
11197 type: type.replace('id', ''),
11198 id: value
11200 inPlaylist = currentPlaylist.items ? _.findWhere(currentPlaylist.items, plItem) : false;
11201 if (inPlaylist) {
11202 return _this.playPosition(inPlaylist.position, resume, callback);
11203 } else {
11204 stateObj = App.request("state:kodi");
11205 if (stateObj.isPlaying(_this.getPlayerName())) {
11206 pos = currentPlaylist.items ? stateObj.getPlaying('position') + 1 : 0;
11207 return _this.insertAndPlay(type, value, pos, resume, callback);
11208 } else {
11209 return _this.clear(function() {
11210 return _this.insertAndPlay(type, value, 0, resume, callback);
11215 })(this));
11218 PlayList.prototype.addCollection = function(collection, position, callback) {
11219 var stateObj;
11220 if (position == null) {
11221 position = 0;
11223 stateObj = App.request("state:kodi");
11224 if (stateObj.isPlaying(this.getPlayerName())) {
11225 position = stateObj.getPlaying('position') + 1;
11226 this.addCollectionItems(collection, position, callback);
11227 } else {
11228 this.clear((function(_this) {
11229 return function() {
11230 return _this.addCollectionItems(collection, position, callback);
11232 })(this));
11234 return position;
11237 PlayList.prototype.addCollectionItems = function(collection, position, callback) {
11238 var commands, i, model, models, params, player, pos, type;
11239 if (position == null) {
11240 position = 0;
11242 App.execute("notification:show", t.gettext("Adding items to the queue"));
11243 models = collection.getRawCollection();
11244 player = this.getPlayer();
11245 commands = [];
11246 for (i in models) {
11247 model = models[i];
11248 pos = parseInt(position) + parseInt(i);
11249 type = model.type === 'file' ? 'file' : model.type + 'id';
11250 params = [player, pos, this.paramObj(type, model[type])];
11251 commands.push({
11252 method: this.getCommand('Insert'),
11253 params: params
11256 return this.multipleCommands(commands, (function(_this) {
11257 return function(resp) {
11258 _this.doCallback(callback, resp);
11259 return _this.refreshPlaylistView();
11261 })(this));
11264 PlayList.prototype.playCollection = function(collection, position) {
11265 var pos;
11266 if (position == null) {
11267 position = 0;
11269 return pos = this.addCollection(collection, position, (function(_this) {
11270 return function(resp) {
11271 _this.playEntity('position', parseInt(pos), {}, function() {});
11272 return _this.refreshPlaylistView();
11274 })(this));
11277 PlayList.prototype.add = function(type, value) {
11278 return this.playlistSize((function(_this) {
11279 return function(size) {
11280 return _this.insert(type, value, size);
11282 })(this));
11285 PlayList.prototype.remove = function(position, callback) {
11286 return this.singleCommand(this.getCommand('Remove'), [this.getPlayer(), parseInt(position)], (function(_this) {
11287 return function(resp) {
11288 _this.refreshPlaylistView();
11289 return _this.doCallback(callback, resp);
11291 })(this));
11294 PlayList.prototype.clear = function(callback) {
11295 return this.singleCommand(this.getCommand('Clear'), [this.getPlayer()], (function(_this) {
11296 return function(resp) {
11297 return _this.doCallback(callback, resp);
11299 })(this));
11302 PlayList.prototype.insert = function(type, value, position, callback) {
11303 if (position == null) {
11304 position = 0;
11306 return this.singleCommand(this.getCommand('Insert'), [this.getPlayer(), parseInt(position), this.paramObj(type, value)], (function(_this) {
11307 return function(resp) {
11308 _this.refreshPlaylistView();
11309 return _this.doCallback(callback, resp);
11311 })(this));
11314 PlayList.prototype.getItems = function(callback) {
11315 return this.singleCommand(this.getCommand('GetItems'), [this.getPlayer(), ['title']], (function(_this) {
11316 return function(resp) {
11317 return _this.doCallback(callback, _this.parseItems(resp));
11319 })(this));
11322 PlayList.prototype.parseItems = function(resp) {
11323 if (resp.items) {
11324 resp.items = _.map(resp.items, function(item, idx) {
11325 item.position = parseInt(idx);
11326 return item;
11329 return resp;
11332 PlayList.prototype.insertAndPlay = function(type, value, position, resume, callback) {
11333 if (position == null) {
11334 position = 0;
11336 if (resume == null) {
11337 resume = 0;
11339 return this.insert(type, value, position, (function(_this) {
11340 return function(resp) {
11341 return _this.playPosition(position, resume, callback);
11343 })(this));
11346 PlayList.prototype.playPosition = function(position, resume, callback) {
11347 if (position == null) {
11348 position = 0;
11350 if (resume == null) {
11351 resume = 0;
11353 return this.playEntity('position', parseInt(position), {}, (function(_this) {
11354 return function() {
11355 if (resume > 0) {
11356 App.execute("player:kodi:progress:update", resume);
11358 return _this.doCallback(callback);
11360 })(this));
11363 PlayList.prototype.playlistSize = function(callback) {
11364 return this.getItems((function(_this) {
11365 return function(resp) {
11366 var position;
11367 position = resp.items != null ? resp.items.length : 0;
11368 return _this.doCallback(callback, position);
11370 })(this));
11373 PlayList.prototype.refreshPlaylistView = function() {
11374 var wsActive;
11375 wsActive = App.request("sockets:active");
11376 if (!wsActive) {
11377 return App.execute("playlist:refresh", 'kodi', this.playerName);
11381 PlayList.prototype.moveItem = function(media, id, position1, position2, callback) {
11382 var idProp;
11383 idProp = media === 'file' ? 'file' : media + 'id';
11384 return this.singleCommand(this.getCommand('Remove'), [this.getPlayer(), parseInt(position1)], (function(_this) {
11385 return function(resp) {
11386 return _this.insert(idProp, id, position2, function() {
11387 return _this.doCallback(callback, position2);
11390 })(this));
11393 return PlayList;
11395 })(Api.Player);
11398 this.Kodi.module("CommandApp.Kodi", function(Api, App, Backbone, Marionette, $, _) {
11399 return Api.PVR = (function(superClass) {
11400 extend(PVR, superClass);
11402 function PVR() {
11403 return PVR.__super__.constructor.apply(this, arguments);
11406 PVR.prototype.commandNameSpace = 'PVR';
11408 PVR.prototype.setRecord = function(id, fields, callback) {
11409 var params;
11410 if (fields == null) {
11411 fields = {};
11413 params = {
11414 channel: id,
11415 record: 'toggle'
11417 params = _.extend(params, fields);
11418 return this.singleCommand(this.getCommand('Record'), params, (function(_this) {
11419 return function(resp) {
11420 return _this.doCallback(callback, resp);
11422 })(this));
11425 PVR.prototype.toggleTimer = function(id, timerRule, callback) {
11426 var params;
11427 if (timerRule == null) {
11428 timerRule = false;
11430 params = {
11431 broadcastid: id,
11432 timerrule: timerRule
11434 return this.singleCommand(this.getCommand('ToggleTimer'), params, (function(_this) {
11435 return function(resp) {
11436 return _this.doCallback(callback, resp);
11438 })(this));
11441 PVR.prototype.addTimer = function(id, timerRule, callback) {
11442 var params;
11443 if (timerRule == null) {
11444 timerRule = false;
11446 params = {
11447 broadcastid: id,
11448 timerrule: timerRule
11450 return this.singleCommand(this.getCommand('AddTimer'), params, (function(_this) {
11451 return function(resp) {
11452 return _this.doCallback(callback, resp);
11454 })(this));
11457 PVR.prototype.deleteTimer = function(id, callback) {
11458 var params;
11459 params = {
11460 timerid: id
11462 return this.singleCommand(this.getCommand('DeleteTimer'), params, (function(_this) {
11463 return function(resp) {
11464 return _this.doCallback(callback, resp);
11466 })(this));
11469 return PVR;
11471 })(Api.Commander);
11474 this.Kodi.module("CommandApp.Kodi", function(Api, App, Backbone, Marionette, $, _) {
11475 return Api.Settings = (function(superClass) {
11476 extend(Settings, superClass);
11478 function Settings() {
11479 return Settings.__super__.constructor.apply(this, arguments);
11482 Settings.prototype.commandNameSpace = 'Settings';
11484 Settings.prototype.getSettingValue = function(value, callback) {
11485 return this.sendCommand("getSettingValue", [value], (function(_this) {
11486 return function(resp) {
11487 return _this.doCallback(callback, resp.value);
11489 })(this));
11492 return Settings;
11494 })(Api.Commander);
11497 this.Kodi.module("CommandApp.Kodi", function(Api, App, Backbone, Marionette, $, _) {
11498 return Api.System = (function(superClass) {
11499 extend(System, superClass);
11501 function System() {
11502 return System.__super__.constructor.apply(this, arguments);
11505 System.prototype.commandNameSpace = 'System';
11507 System.prototype.getProperties = function(callback) {
11508 var properties;
11509 properties = ["canshutdown", "cansuspend", "canhibernate", "canreboot"];
11510 return this.singleCommand(this.getCommand('GetProperties'), [properties], (function(_this) {
11511 return function(resp) {
11512 return _this.doCallback(callback, resp);
11514 })(this));
11517 System.prototype.hibernate = function(callback) {
11518 return this.singleCommand(this.getCommand('Hibernate'), [], (function(_this) {
11519 return function(resp) {
11520 return _this.doCallback(callback, resp);
11522 })(this));
11525 System.prototype.reboot = function(callback) {
11526 return this.singleCommand(this.getCommand('Reboot'), [], (function(_this) {
11527 return function(resp) {
11528 return _this.doCallback(callback, resp);
11530 })(this));
11533 System.prototype.shutdown = function(callback) {
11534 return this.singleCommand(this.getCommand('Shutdown'), [], (function(_this) {
11535 return function(resp) {
11536 return _this.doCallback(callback, resp);
11538 })(this));
11541 System.prototype.suspend = function(callback) {
11542 return this.singleCommand(this.getCommand('Suspend'), [], (function(_this) {
11543 return function(resp) {
11544 return _this.doCallback(callback, resp);
11546 })(this));
11549 return System;
11551 })(Api.Commander);
11554 this.Kodi.module("CommandApp.Kodi", function(Api, App, Backbone, Marionette, $, _) {
11555 return Api.VideoLibrary = (function(superClass) {
11556 extend(VideoLibrary, superClass);
11558 function VideoLibrary() {
11559 return VideoLibrary.__super__.constructor.apply(this, arguments);
11562 VideoLibrary.prototype.commandNameSpace = 'VideoLibrary';
11564 VideoLibrary.prototype.setEpisodeDetails = function(id, fields, callback) {
11565 var params;
11566 if (fields == null) {
11567 fields = {};
11569 params = {
11570 episodeid: id
11572 params = _.extend(params, fields);
11573 return this.singleCommand(this.getCommand('SetEpisodeDetails'), params, (function(_this) {
11574 return function(resp) {
11575 return _this.doCallback(callback, resp);
11577 })(this));
11580 VideoLibrary.prototype.setMovieDetails = function(id, fields, callback) {
11581 var params;
11582 if (fields == null) {
11583 fields = {};
11585 params = {
11586 movieid: id
11588 params = _.extend(params, fields);
11589 return this.singleCommand(this.getCommand('SetMovieDetails'), params, (function(_this) {
11590 return function(resp) {
11591 return _this.doCallback(callback, resp);
11593 })(this));
11596 VideoLibrary.prototype.setTVShowDetails = function(id, fields, callback) {
11597 var params;
11598 if (fields == null) {
11599 fields = {};
11601 params = {
11602 tvshowid: id
11604 params = _.extend(params, fields);
11605 return this.singleCommand(this.getCommand('SetTVShowDetails'), params, (function(_this) {
11606 return function(resp) {
11607 return _this.doCallback(callback, resp);
11609 })(this));
11612 VideoLibrary.prototype.setMusicVideoDetails = function(id, fields, callback) {
11613 var params;
11614 if (fields == null) {
11615 fields = {};
11617 params = {
11618 musicvideoid: id
11620 params = _.extend(params, fields);
11621 return this.singleCommand(this.getCommand('SetMusicVideoDetails'), params, (function(_this) {
11622 return function(resp) {
11623 return _this.doCallback(callback, resp);
11625 })(this));
11628 VideoLibrary.prototype.scan = function(callback) {
11629 return this.singleCommand(this.getCommand('Scan'), (function(_this) {
11630 return function(resp) {
11631 return _this.doCallback(callback, resp);
11633 })(this));
11636 VideoLibrary.prototype.clean = function(callback) {
11637 return this.singleCommand(this.getCommand('Clean'), {
11638 showdialogs: false
11639 }, (function(_this) {
11640 return function(resp) {
11641 return _this.doCallback(callback, resp);
11643 })(this));
11646 VideoLibrary.prototype.toggleWatchedCollection = function(collection, op, callback) {
11647 var i, model, ref;
11648 ref = collection.models;
11649 for (i in ref) {
11650 model = ref[i];
11651 this.toggleWatched(model, op);
11653 return this.doCallback(callback, true);
11656 VideoLibrary.prototype.toggleWatched = function(model, op, callback) {
11657 var fields, setPlaycount;
11658 if (op == null) {
11659 op = 'auto';
11661 if (op === 'auto') {
11662 setPlaycount = model.get('playcount') > 0 ? 0 : 1;
11663 } else if (op === 'watched') {
11664 setPlaycount = 1;
11665 } else if (op === 'unwatched') {
11666 setPlaycount = 0;
11668 fields = helpers.global.paramObj('playcount', setPlaycount);
11669 if (model.get('type') === 'movie') {
11670 this.setMovieDetails(model.get('id'), fields, (function(_this) {
11671 return function() {
11672 App.vent.trigger('entity:kodi:update', model.get('uid'));
11673 return _this.doCallback(callback, setPlaycount);
11675 })(this));
11677 if (model.get('type') === 'episode') {
11678 return this.setEpisodeDetails(model.get('id'), fields, (function(_this) {
11679 return function() {
11680 App.vent.trigger('entity:kodi:update', model.get('uid'));
11681 return _this.doCallback(callback, setPlaycount);
11683 })(this));
11687 VideoLibrary.prototype.refreshMovie = function(id, params, callback) {
11688 params = _.extend({
11689 movieid: id,
11690 ignorenfo: false
11691 }, params);
11692 return this.singleCommand(this.getCommand('RefreshMovie'), params, (function(_this) {
11693 return function(resp) {
11694 return _this.doCallback(callback, resp);
11696 })(this));
11699 VideoLibrary.prototype.refreshTVShow = function(id, params, callback) {
11700 params = _.extend({
11701 tvshowid: id,
11702 ignorenfo: false
11703 }, params);
11704 return this.singleCommand(this.getCommand('RefreshTVShow'), params, (function(_this) {
11705 return function(resp) {
11706 return _this.doCallback(callback, resp);
11708 })(this));
11711 VideoLibrary.prototype.refreshEpisode = function(id, params, callback) {
11712 params = _.extend({
11713 episodeid: id,
11714 ignorenfo: false
11715 }, params);
11716 return this.singleCommand(this.getCommand('RefreshEpisode'), params, (function(_this) {
11717 return function(resp) {
11718 return _this.doCallback(callback, resp);
11720 })(this));
11723 return VideoLibrary;
11725 })(Api.Commander);
11728 this.Kodi.module("CommandApp.Local", function(Api, App, Backbone, Marionette, $, _) {
11729 return Api.Base = (function(superClass) {
11730 extend(Base, superClass);
11732 function Base() {
11733 return Base.__super__.constructor.apply(this, arguments);
11736 Base.prototype.localLoad = function(model, callback) {
11737 var files, stateObj;
11738 stateObj = App.request("state:local");
11739 if (model == null) {
11740 stateObj.setPlaying('playing', false);
11741 this.localStateUpdate();
11742 return;
11744 stateObj.setState('currentPlaybackId', 'browser-' + model.get('id'));
11745 files = App.request("command:kodi:controller", 'video', 'Files');
11746 return files.downloadPath(model.get('file'), (function(_this) {
11747 return function(path) {
11748 var sm;
11749 sm = soundManager;
11750 _this.localStop();
11751 stateObj.setState('localPlay', sm.createSound({
11752 id: stateObj.getState('currentPlaybackId'),
11753 url: path,
11754 autoplay: false,
11755 autoLoad: true,
11756 stream: true,
11757 volume: stateObj.getState('volume'),
11758 onerror: function() {
11759 return console.log('SM ERROR!');
11761 onplay: function() {
11762 stateObj.setPlayer('local');
11763 stateObj.setPlaying('playing', true);
11764 stateObj.setPlaying('paused', false);
11765 stateObj.setPlaying('playState', 'playing');
11766 stateObj.setPlaying('position', model.get('position'));
11767 stateObj.setPlaying('itemChanged', true);
11768 stateObj.setPlaying('item', model.attributes);
11769 stateObj.setPlaying('totaltime', helpers.global.secToTime(model.get('duration')));
11770 return _this.localStateUpdate();
11772 onstop: function() {
11773 stateObj.setPlaying('playing', false);
11774 return _this.localStateUpdate();
11776 onpause: function() {
11777 stateObj.setPlaying('paused', true);
11778 stateObj.setPlaying('playState', 'paused');
11779 return _this.localStateUpdate();
11781 onresume: function() {
11782 stateObj.setPlaying('paused', false);
11783 stateObj.setPlaying('playState', 'playing');
11784 return _this.localStateUpdate();
11786 onfinish: function() {
11787 return _this.localFinished();
11789 whileplaying: function() {
11790 var dur, percentage, pos;
11791 pos = parseInt(this.position) / 1000;
11792 dur = parseInt(model.get('duration'));
11793 percentage = Math.round((pos / dur) * 100);
11794 stateObj.setPlaying('time', helpers.global.secToTime(pos));
11795 stateObj.setPlaying('percentage', percentage);
11796 return App.execute('player:local:progress:update', percentage, helpers.global.secToTime(pos));
11798 }));
11799 return _this.doCallback(callback);
11801 })(this));
11804 Base.prototype.localFinished = function() {
11805 return this.localGoTo('next');
11808 Base.prototype.localPlay = function() {
11809 return this.localCommand('play');
11812 Base.prototype.localStop = function() {
11813 return this.localCommand('stop');
11816 Base.prototype.localPause = function() {
11817 return this.localCommand('pause');
11820 Base.prototype.localPlayPause = function() {
11821 var stateObj;
11822 stateObj = App.request("state:local");
11823 if (stateObj.getPlaying('paused')) {
11824 return this.localCommand('play');
11825 } else {
11826 return this.localCommand('pause');
11830 Base.prototype.localSetVolume = function(volume) {
11831 return this.localCommand('setVolume', volume);
11834 Base.prototype.localCommand = function(command, param) {
11835 var currentItem, stateObj;
11836 stateObj = App.request("state:local");
11837 currentItem = stateObj.getState('localPlay');
11838 if (currentItem !== false) {
11839 currentItem[command](param);
11841 return this.localStateUpdate();
11844 Base.prototype.localGoTo = function(param) {
11845 var collection, currentPos, model, posToPlay, stateObj;
11846 collection = App.request("localplayer:get:entities");
11847 stateObj = App.request("state:local");
11848 currentPos = stateObj.getPlaying('position');
11849 posToPlay = false;
11850 if (collection.length > 0) {
11851 if (stateObj.getState('repeat') === 'one') {
11852 posToPlay = currentPos;
11853 } else if (stateObj.getState('shuffled') === true) {
11854 posToPlay = helpers.global.getRandomInt(0, collection.length - 1);
11855 } else {
11856 if (param === 'next') {
11857 if (currentPos === collection.length - 1 && stateObj.getState('repeat') === 'all') {
11858 posToPlay = 0;
11859 } else if (currentPos < collection.length) {
11860 posToPlay = currentPos + 1;
11862 this.localStateNext();
11864 if (param === 'previous') {
11865 if (currentPos === 0 && stateObj.getState('repeat') === 'all') {
11866 posToPlay = collection.length - 1;
11867 } else if (currentPos > 0) {
11868 posToPlay = currentPos - 1;
11873 if (posToPlay !== false) {
11874 model = collection.findWhere({
11875 position: parseInt(posToPlay)
11877 return this.localLoad(model, (function(_this) {
11878 return function() {
11879 _this.localPlay();
11880 return _this.localStateUpdate();
11882 })(this));
11886 Base.prototype.localSeek = function(param) {
11887 var localPlay, newPos, sound, stateObj;
11888 stateObj = App.request("state:local");
11889 localPlay = stateObj.getState('localPlay');
11890 if (localPlay !== false) {
11891 newPos = (param.percentage / 100) * localPlay.duration;
11892 sound = soundManager.getSoundById(stateObj.getState('currentPlaybackId'));
11893 return sound.setPosition(newPos);
11897 Base.prototype.localRepeat = function(param) {
11898 var i, key, newState, state, stateObj, states;
11899 stateObj = App.request("state:local");
11900 if (param !== 'cycle') {
11901 return stateObj.setState('repeat', param);
11902 } else {
11903 newState = false;
11904 states = ['off', 'all', 'one'];
11905 for (i in states) {
11906 state = states[i];
11907 i = parseInt(i);
11908 if (newState !== false) {
11909 continue;
11911 if (stateObj.getState('repeat') === state) {
11912 if (i !== (states.length - 1)) {
11913 key = i + 1;
11914 newState = states[key];
11915 } else {
11916 newState = 'off';
11920 return stateObj.setState('repeat', newState);
11924 Base.prototype.localShuffle = function() {
11925 var currentShuffle, stateObj;
11926 stateObj = App.request("state:local");
11927 currentShuffle = stateObj.getState('shuffled');
11928 return stateObj.setState('shuffled', !currentShuffle);
11931 Base.prototype.localStateUpdate = function() {
11932 return App.vent.trigger("state:local:changed");
11935 Base.prototype.localStateNext = function() {
11936 return App.vent.trigger("state:local:next");
11939 Base.prototype.paramObj = function(key, val) {
11940 return helpers.global.paramObj(key, val);
11943 Base.prototype.doCallback = function(callback, response) {
11944 if (typeof callback === 'function') {
11945 return callback(response);
11949 Base.prototype.onError = function(commands, error) {
11950 return helpers.debug.rpcError(commands, error);
11953 return Base;
11955 })(Marionette.Object);
11958 this.Kodi.module("CommandApp.Local", function(Api, App, Backbone, Marionette, $, _) {
11959 Api.Commander = (function(superClass) {
11960 extend(Commander, superClass);
11962 function Commander() {
11963 return Commander.__super__.constructor.apply(this, arguments);
11966 return Commander;
11968 })(Api.Base);
11969 return Api.Player = (function(superClass) {
11970 extend(Player, superClass);
11972 function Player() {
11973 return Player.__super__.constructor.apply(this, arguments);
11976 Player.prototype.playEntity = function(type, position, callback) {
11977 var collection, model;
11978 if (type == null) {
11979 type = 'position';
11981 collection = App.request("localplayer:get:entities");
11982 model = collection.findWhere({
11983 position: position
11985 return this.localLoad(model, (function(_this) {
11986 return function() {
11987 _this.localPlay();
11988 return _this.doCallback(callback, position);
11990 })(this));
11993 Player.prototype.sendCommand = function(command, param) {
11994 switch (command) {
11995 case 'GoTo':
11996 this.localGoTo(param);
11997 break;
11998 case 'PlayPause':
11999 this.localPlayPause();
12000 break;
12001 case 'Seek':
12002 this.localSeek(param);
12003 break;
12004 case 'SetRepeat':
12005 this.localRepeat(param);
12006 break;
12007 case 'SetShuffle':
12008 this.localShuffle();
12009 break;
12010 case 'Stop':
12011 this.localStop();
12012 break;
12014 return this.localStateUpdate();
12017 Player.prototype.setPartyMode = function(op, callback) {
12018 if (op == null) {
12019 op = 'toggle';
12021 return App.execute('playlist:local:partymode', op, (function(_this) {
12022 return function(resp) {
12023 return _this.doCallback(callback, resp);
12025 })(this));
12028 return Player;
12030 })(Api.Commander);
12033 this.Kodi.module("CommandApp.Local", function(Api, App, Backbone, Marionette, $, _) {
12034 return Api.Application = (function(superClass) {
12035 extend(Application, superClass);
12037 function Application() {
12038 return Application.__super__.constructor.apply(this, arguments);
12041 Application.prototype.getProperties = function(callback) {
12042 var resp, stateObj;
12043 stateObj = App.request("state:local");
12044 resp = {
12045 volume: stateObj.getState('volume'),
12046 muted: stateObj.getState('muted')
12048 return this.doCallback(callback, resp);
12051 Application.prototype.setVolume = function(volume, callback) {
12052 var stateObj;
12053 stateObj = App.request("state:local");
12054 stateObj.setState('volume', volume);
12055 this.localSetVolume(volume);
12056 return this.doCallback(callback, volume);
12059 Application.prototype.toggleMute = function(callback) {
12060 var stateObj, volume;
12061 stateObj = App.request("state:local");
12062 volume = 0;
12063 if (stateObj.getState('muted')) {
12064 volume = stateObj.getState('lastVolume');
12065 stateObj.setState('muted', false);
12066 } else {
12067 stateObj.setState('lastVolume', stateObj.getState('volume'));
12068 stateObj.setState('muted', true);
12069 volume = 0;
12071 this.localSetVolume(volume);
12072 return this.doCallback(callback, volume);
12075 return Application;
12077 })(Api.Commander);
12080 this.Kodi.module("CommandApp.Local", function(Api, App, Backbone, Marionette, $, _) {
12081 return Api.PlayList = (function(superClass) {
12082 extend(PlayList, superClass);
12084 function PlayList() {
12085 return PlayList.__super__.constructor.apply(this, arguments);
12088 PlayList.prototype.play = function(type, value, resume) {
12089 if (resume == null) {
12090 resume = 0;
12092 return this.getSongs(type, value, (function(_this) {
12093 return function(songs) {
12094 return _this.playCollection(songs);
12096 })(this));
12099 PlayList.prototype.add = function(type, value) {
12100 return this.getSongs(type, value, (function(_this) {
12101 return function(songs) {
12102 return _this.addCollection(songs);
12104 })(this));
12107 PlayList.prototype.playCollection = function(models) {
12108 models = this.itemsJson(models);
12109 return this.clear((function(_this) {
12110 return function() {
12111 return _this.insertAndPlay(models, 0);
12113 })(this));
12116 PlayList.prototype.addCollection = function(models) {
12117 models = this.itemsJson(models);
12118 return this.playlistSize((function(_this) {
12119 return function(size) {
12120 return _this.insert(models, size);
12122 })(this));
12125 PlayList.prototype.remove = function(position, callback) {
12126 return this.getItems((function(_this) {
12127 return function(collection) {
12128 var item, pos, raw, ret;
12129 raw = _this.itemsJson(collection);
12130 ret = [];
12131 for (pos in raw) {
12132 item = raw[pos];
12133 if (parseInt(pos) !== parseInt(position)) {
12134 ret.push(item);
12137 return _this.clear(function() {
12138 collection = _this.addItems(ret);
12139 return _this.doCallback(callback, collection);
12142 })(this));
12145 PlayList.prototype.clear = function(callback) {
12146 var collection;
12147 collection = App.execute("localplayer:clear:entities");
12148 this.refreshPlaylistView();
12149 return this.doCallback(callback, collection);
12152 PlayList.prototype.insert = function(models, position, callback) {
12153 if (position == null) {
12154 position = 0;
12156 return this.getItems((function(_this) {
12157 return function(collection) {
12158 var item, len, len1, model, n, o, pos, raw, ref, ref1, ret;
12159 raw = _this.itemsJson(collection);
12160 if (raw.length === 0) {
12161 ret = _.flatten([models]);
12162 } else if (parseInt(position) >= raw.length) {
12163 ret = raw;
12164 ref = _.flatten([models]);
12165 for (n = 0, len = ref.length; n < len; n++) {
12166 model = ref[n];
12167 ret.push(model);
12169 } else {
12170 ret = [];
12171 for (pos in raw) {
12172 item = raw[pos];
12173 if (parseInt(pos) === parseInt(position)) {
12174 ref1 = _.flatten([models]);
12175 for (o = 0, len1 = ref1.length; o < len1; o++) {
12176 model = ref1[o];
12177 ret.push(model);
12180 ret.push(item);
12183 return _this.clear(function() {
12184 collection = _this.addItems(ret);
12185 return _this.doCallback(callback, collection);
12188 })(this));
12191 PlayList.prototype.addItems = function(items) {
12192 App.request("localplayer:item:add:entities", items);
12193 this.updatePlayingPosition(items);
12194 this.refreshPlaylistView();
12195 return items;
12198 PlayList.prototype.getSongs = function(type, value, callback) {
12199 var songs;
12200 if (type === 'songid') {
12201 return App.request("song:byid:entities", [value], (function(_this) {
12202 return function(songs) {
12203 return _this.doCallback(callback, songs.getRawCollection());
12205 })(this));
12206 } else {
12207 songs = App.request("song:entities", {
12208 filter: helpers.global.paramObj(type, value)
12210 return App.execute("when:entity:fetched", songs, (function(_this) {
12211 return function() {
12212 return _this.doCallback(callback, songs.getRawCollection());
12214 })(this));
12218 PlayList.prototype.getItems = function(callback) {
12219 var collection;
12220 collection = App.request("localplayer:get:entities");
12221 return this.doCallback(callback, collection);
12224 PlayList.prototype.itemsJson = function(collection) {
12225 var items;
12226 items = _.isArray(collection) ? collection : collection.toJSON();
12227 return items;
12230 PlayList.prototype.insertAndPlay = function(models, position, callback) {
12231 if (position == null) {
12232 position = 0;
12234 return this.insert(models, position, (function(_this) {
12235 return function(resp) {
12236 return _this.playEntity('position', parseInt(position), {}, function() {
12237 return _this.doCallback(callback, position);
12240 })(this));
12243 PlayList.prototype.playlistSize = function(callback) {
12244 return this.getItems((function(_this) {
12245 return function(resp) {
12246 return _this.doCallback(callback, resp.length);
12248 })(this));
12251 PlayList.prototype.refreshPlaylistView = function() {
12252 return App.execute("playlist:refresh", 'local', 'audio');
12255 PlayList.prototype.moveItem = function(media, id, position1, position2, callback) {
12256 return this.getItems((function(_this) {
12257 return function(collection) {
12258 var item, raw;
12259 raw = collection.getRawCollection();
12260 item = raw[position1];
12261 return _this.remove(position1, function() {
12262 return _this.insert(item, position2, function() {
12263 return _this.doCallback(callback, position2);
12267 })(this));
12270 PlayList.prototype.updatePlayingPosition = function(collection) {
12271 var i, m, model, pos, ref, set, stateObj;
12272 stateObj = App.request("state:local");
12273 if (stateObj.isPlaying()) {
12274 model = stateObj.getPlaying('item');
12275 if (model.uid) {
12276 set = false;
12277 pos = 0;
12278 ref = this.itemsJson(collection);
12279 for (i in ref) {
12280 m = ref[i];
12281 if (set === true) {
12282 continue;
12284 if (m.uid === model.uid) {
12285 pos = parseInt(i);
12286 set = true;
12289 model.position = pos;
12290 stateObj.setPlaying('item', model);
12291 return stateObj.setPlaying('position', pos);
12296 return PlayList;
12298 })(Api.Player);
12301 this.Kodi.module("CommandApp.Local", function(Api, App, Backbone, Marionette, $, _) {
12302 return Api.VideoPlayer = (function(superClass) {
12303 extend(VideoPlayer, superClass);
12305 function VideoPlayer() {
12306 return VideoPlayer.__super__.constructor.apply(this, arguments);
12309 VideoPlayer.prototype.getKodiFilesController = function() {
12310 return new App.CommandApp.Kodi.Files;
12313 VideoPlayer.prototype.play = function(type, value, model) {
12314 return this.videoStream(model.get('file'), model.get('fanart'));
12317 VideoPlayer.prototype.videoStream = function(file, background, player) {
12318 var st;
12319 if (background == null) {
12320 background = '';
12322 if (player == null) {
12323 player = 'html5';
12325 st = helpers.global.localVideoPopup('about:blank');
12326 return this.getKodiFilesController().downloadPath(file, function(path) {
12327 return st.location = "videoPlayer.html?player=" + player + '&src=' + encodeURIComponent(path) + '&bg=' + encodeURIComponent(background);
12331 return VideoPlayer;
12333 })(Api.Player);
12336 this.Kodi.module("EPGApp", function(EPGApp, App, Backbone, Marionette, $, _) {
12337 var API;
12338 EPGApp.Router = (function(superClass) {
12339 extend(Router, superClass);
12341 function Router() {
12342 return Router.__super__.constructor.apply(this, arguments);
12345 Router.prototype.appRoutes = {
12346 "pvr/tv/:channelid": "tv",
12347 "pvr/radio/:channelid": "radio"
12350 return Router;
12352 })(App.Router.Base);
12353 API = {
12354 tv: function(channelid) {
12355 return new EPGApp.List.Controller({
12356 channelid: channelid,
12357 type: "tv"
12360 radio: function(channelid) {
12361 return new EPGApp.List.Controller({
12362 channelid: channelid,
12363 type: "radio"
12366 action: function(op, view) {
12367 var model, player, pvr;
12368 model = view.model;
12369 player = App.request("command:kodi:controller", 'auto', 'Player');
12370 pvr = App.request("command:kodi:controller", 'auto', 'PVR');
12371 switch (op) {
12372 case 'play':
12373 return player.playEntity('channelid', model.get('channelid'));
12374 case 'record':
12375 return pvr.setRecord(model.get('channelid'), {}, function() {
12376 return App.execute("notification:show", tr("Channel recording toggled"));
12378 case 'timer':
12379 return pvr.toggleTimer(model.get('id'));
12383 App.commands.setHandler('broadcast:action', function(op, view) {
12384 return API.action(op, view);
12386 return App.on("before:start", function() {
12387 return new EPGApp.Router({
12388 controller: API
12393 this.Kodi.module("EPGApp.List", function(List, App, Backbone, Marionette, $, _) {
12394 var API;
12395 API = {
12396 bindTriggers: function(view) {
12397 App.listenTo(view, 'childview:broadcast:play', function(parent, child) {
12398 return App.execute('broadcast:action', 'play', child);
12400 App.listenTo(view, 'childview:broadcast:record', function(parent, child) {
12401 return App.execute('broadcast:action', 'record', child);
12403 return App.listenTo(view, 'childview:broadcast:timer', function(parent, child) {
12404 return App.execute('broadcast:action', 'timer', child);
12407 bindChannelTriggers: function(view) {
12408 App.listenTo(view, 'broadcast:play', function(child) {
12409 return App.execute('broadcast:action', 'play', child);
12411 App.listenTo(view, 'broadcast:record', function(child) {
12412 return App.execute('broadcast:action', 'record', child);
12414 return App.listenTo(view, 'broadcast:timer', function(child) {
12415 return App.execute('broadcast:action', 'timer', child);
12419 return List.Controller = (function(superClass) {
12420 extend(Controller, superClass);
12422 function Controller() {
12423 return Controller.__super__.constructor.apply(this, arguments);
12426 Controller.prototype.initialize = function(options) {
12427 var model;
12428 model = App.request('channel:entity', options.channelid);
12429 return App.execute("when:entity:fetched", model, (function(_this) {
12430 return function() {
12431 var collection;
12432 collection = App.request("broadcast:entities", options.channelid);
12433 return App.execute("when:entity:fetched", collection, function() {
12434 _this.layout = _this.getLayoutView(collection);
12435 _this.listenTo(_this.layout, "show", function() {
12436 _this.getSubNav(model);
12437 _this.getChannelActions(model);
12438 return _this.renderProgrammes(collection);
12440 return App.regionContent.show(_this.layout);
12443 })(this));
12446 Controller.prototype.getLayoutView = function(collection) {
12447 return new List.Layout({
12448 collection: collection
12452 Controller.prototype.renderProgrammes = function(collection) {
12453 var view;
12454 view = new List.EPGList({
12455 collection: collection
12457 API.bindTriggers(view);
12458 return this.layout.regionContent.show(view);
12461 Controller.prototype.getSubNav = function(model) {
12462 var subNav;
12463 subNav = App.request("navMain:children:show", 'pvr/tv', 'PVR');
12464 return this.layout.regionSidebarFirst.show(subNav);
12467 Controller.prototype.getChannelActions = function(model) {
12468 var view;
12469 view = new List.ChannelActions({
12470 model: model
12472 API.bindChannelTriggers(view);
12473 return this.layout.appendSidebarView('channel-actions', view);
12476 return Controller;
12478 })(App.Controllers.Base);
12481 this.Kodi.module("EPGApp.List", function(List, App, Backbone, Marionette, $, _) {
12482 List.Layout = (function(superClass) {
12483 extend(Layout, superClass);
12485 function Layout() {
12486 return Layout.__super__.constructor.apply(this, arguments);
12489 Layout.prototype.className = "epg-page";
12491 return Layout;
12493 })(App.Views.LayoutWithSidebarFirstView);
12494 List.ChannelActions = (function(superClass) {
12495 extend(ChannelActions, superClass);
12497 function ChannelActions() {
12498 return ChannelActions.__super__.constructor.apply(this, arguments);
12501 ChannelActions.prototype.template = 'apps/epg/list/channel';
12503 ChannelActions.prototype.className = 'nav-sub';
12505 ChannelActions.prototype.triggers = {
12506 'click .play': 'broadcast:play'
12509 ChannelActions.prototype.events = {
12510 'click .record': 'toggleRecord'
12513 ChannelActions.prototype.toggleRecord = function() {
12514 console.log($('.airing'));
12515 $('.airing').toggleClass('has-timer');
12516 return this.trigger('broadcast:record', this);
12519 return ChannelActions;
12521 })(App.Views.ItemView);
12522 List.ProgrammeList = (function(superClass) {
12523 extend(ProgrammeList, superClass);
12525 function ProgrammeList() {
12526 return ProgrammeList.__super__.constructor.apply(this, arguments);
12529 ProgrammeList.prototype.template = 'apps/epg/list/programme';
12531 ProgrammeList.prototype.tagName = "li";
12533 ProgrammeList.prototype.className = "pvr-card card";
12535 ProgrammeList.prototype.onRender = function() {
12536 if (this.model.attributes.wasactive) {
12537 this.$el.addClass("aired");
12539 if (this.model.attributes.isactive) {
12540 this.$el.addClass("airing");
12542 if (this.model.attributes.hastimer) {
12543 return this.$el.addClass("has-timer");
12547 ProgrammeList.prototype.triggers = {
12548 'click .play': 'broadcast:play'
12551 ProgrammeList.prototype.events = {
12552 'click .record': 'toggleRecord',
12553 'click .toggle-timer': 'toggleTimer'
12556 ProgrammeList.prototype.toggleRecord = function() {
12557 this.$el.toggleClass('has-timer');
12558 return this.trigger('broadcast:record', this);
12561 ProgrammeList.prototype.toggleTimer = function() {
12562 this.$el.toggleClass('has-timer');
12563 return this.trigger('broadcast:timer', this);
12566 return ProgrammeList;
12568 })(App.Views.ItemView);
12569 return List.EPGList = (function(superClass) {
12570 extend(EPGList, superClass);
12572 function EPGList() {
12573 return EPGList.__super__.constructor.apply(this, arguments);
12576 EPGList.prototype.childView = List.ProgrammeList;
12578 EPGList.prototype.tagName = "ul";
12580 EPGList.prototype.className = "programmes";
12582 EPGList.prototype.emptyView = App.Views.EmptyViewResults;
12584 EPGList.prototype.emptyViewOptions = {
12585 emptyKey: 'EPG data'
12588 EPGList.prototype.onShow = function() {
12589 var $airing;
12590 $airing = this.$el.find('.airing');
12591 if ($airing.length) {
12592 return $(window).scrollTop($airing.offset().top - 150);
12596 return EPGList;
12598 })(App.Views.CollectionView);
12601 this.Kodi.module("ExternalApp", function(ExternalApp, App, Backbone, Marionette, $, _) {});
12603 this.Kodi.module("ExternalApp.Youtube", function(Youtube, App, Backbone, Marionette, $, _) {
12604 var API;
12605 API = {
12606 getSearchView: function(query, viewName, title, options, callback) {
12607 if (title == null) {
12608 title = '';
12610 if (options == null) {
12611 options = {};
12613 return App.execute("youtube:search:entities", query, options, function(collection) {
12614 var view;
12615 view = new Youtube[viewName]({
12616 collection: collection,
12617 title: title
12619 App.listenTo(view, 'childview:youtube:play', function(parent, item) {
12620 if (item.model.get('addonEnabled')) {
12621 return API.playKodi(item.model.get('id'));
12622 } else {
12623 return API.playLocal(item.model.get('id'));
12626 App.listenTo(view, 'childview:youtube:localplay', function(parent, item) {
12627 return API.playLocal(item.model.get('id'));
12629 return callback(view);
12632 playLocal: function(id) {
12633 var localPlayer;
12634 localPlayer = "videoPlayer.html?yt=" + id;
12635 return helpers.global.localVideoPopup(localPlayer, 530);
12637 playKodi: function(id) {
12638 var playlist;
12639 playlist = App.request("command:kodi:controller", 'video', 'PlayList');
12640 return playlist.play('file', 'plugin://plugin.video.youtube/play/?video_id=' + id);
12643 App.commands.setHandler("youtube:search:view", function(query, callback) {
12644 return API.getSearchView(query, 'List', '', {}, callback);
12646 App.commands.setHandler("youtube:search:popup", function(query) {
12647 return API.getSearchView(query, 'List', '', {}, function(view) {
12648 var $footer;
12649 $footer = $('<a>', {
12650 "class": 'btn btn-primary',
12651 href: 'https://www.youtube.com/results?search_query=' + query,
12652 target: '_blank'
12654 $footer.html('More videos');
12655 return App.execute("ui:modal:show", _.escape(query), view.render().$el, $footer);
12658 return App.commands.setHandler("youtube:list:view", function(query, title, options, callback) {
12659 if (options == null) {
12660 options = {};
12662 return API.getSearchView(query, 'CardList', title, options, callback);
12666 this.Kodi.module("ExternalApp.Youtube", function(Youtube, App, Backbone, Marionette, $, _) {
12667 Youtube.Item = (function(superClass) {
12668 extend(Item, superClass);
12670 function Item() {
12671 return Item.__super__.constructor.apply(this, arguments);
12674 Item.prototype.template = 'apps/external/youtube/youtube';
12676 Item.prototype.tagName = 'li';
12678 Item.prototype.triggers = {
12679 'click .play': 'youtube:play',
12680 'click .localplay': 'youtube:localplay'
12683 Item.prototype.events = {
12684 'click .action': 'closeModal'
12687 Item.prototype.closeModal = function() {
12688 return App.execute("ui:modal:close");
12691 return Item;
12693 })(App.Views.ItemView);
12694 Youtube.List = (function(superClass) {
12695 extend(List, superClass);
12697 function List() {
12698 return List.__super__.constructor.apply(this, arguments);
12701 List.prototype.childView = Youtube.Item;
12703 List.prototype.tagName = 'ul';
12705 List.prototype.className = 'youtube-list';
12707 return List;
12709 })(App.Views.CollectionView);
12710 Youtube.Card = (function(superClass) {
12711 extend(Card, superClass);
12713 function Card() {
12714 return Card.__super__.constructor.apply(this, arguments);
12717 Card.prototype.triggers = {
12718 'click .play': 'youtube:play',
12719 'click .localplay': 'youtube:localplay'
12722 Card.prototype.initialize = function() {
12723 return this.getMeta();
12726 Card.prototype.getMeta = function() {
12727 if (this.model) {
12728 this.model.set({
12729 subtitleHtml: this.themeLink('YouTube', this.model.get('url'), {
12730 external: true
12733 if (this.model.get('addonEnabled')) {
12734 return this.model.set({
12735 menu: {
12736 localplay: 'Local play'
12743 Card.prototype.onRender = function() {
12744 return this.makeLinksExternal();
12747 return Card;
12749 })(App.Views.CardView);
12750 return Youtube.CardList = (function(superClass) {
12751 extend(CardList, superClass);
12753 function CardList() {
12754 return CardList.__super__.constructor.apply(this, arguments);
12757 CardList.prototype.childView = Youtube.Card;
12759 CardList.prototype.className = "section-content card-grid--musicvideo";
12761 return CardList;
12763 })(App.Views.SetCompositeView);
12766 this.Kodi.module("FilterApp", function(FilterApp, App, Backbone, Marionette, $, _) {
12767 var API;
12768 API = {
12771 Settings/fields
12773 sortFields: [
12775 alias: 'title',
12776 type: 'string',
12777 defaultSort: true,
12778 defaultOrder: 'asc',
12779 key: 'title'
12780 }, {
12781 alias: 'title',
12782 type: 'string',
12783 defaultSort: true,
12784 defaultOrder: 'asc',
12785 key: 'label'
12786 }, {
12787 alias: 'year',
12788 type: 'number',
12789 key: 'year',
12790 defaultOrder: 'desc'
12791 }, {
12792 alias: 'date added',
12793 type: 'string',
12794 key: 'dateadded',
12795 defaultOrder: 'desc'
12796 }, {
12797 alias: 'rating',
12798 type: 'float',
12799 key: 'rating',
12800 defaultOrder: 'desc'
12801 }, {
12802 alias: 'artist',
12803 type: 'string',
12804 key: 'artist',
12805 defaultOrder: 'asc'
12806 }, {
12807 alias: 'random',
12808 type: 'other',
12809 key: 'random',
12810 defaultOrder: 'asc'
12811 }, {
12812 alias: 'album',
12813 type: 'string',
12814 key: 'album',
12815 defaultOrder: 'asc'
12818 filterFields: [
12820 alias: 'year',
12821 type: 'number',
12822 key: 'year',
12823 sortOrder: 'desc',
12824 filterCallback: 'multiple'
12825 }, {
12826 alias: 'genre',
12827 type: 'array',
12828 key: 'genre',
12829 sortOrder: 'asc',
12830 filterCallback: 'multiple'
12831 }, {
12832 alias: 'mood',
12833 type: 'array',
12834 key: 'mood',
12835 sortOrder: 'asc',
12836 filterCallback: 'multiple'
12837 }, {
12838 alias: 'style',
12839 type: 'array',
12840 key: 'style',
12841 sortOrder: 'asc',
12842 filterCallback: 'multiple'
12843 }, {
12844 alias: 'unwatched',
12845 type: "boolean",
12846 key: 'unwatched',
12847 sortOrder: 'asc',
12848 filterCallback: 'unwatched'
12849 }, {
12850 alias: 'watched',
12851 type: "boolean",
12852 key: 'watched',
12853 sortOrder: 'asc',
12854 filterCallback: 'watched'
12855 }, {
12856 alias: 'in progress',
12857 type: "boolean",
12858 key: 'inprogress',
12859 sortOrder: 'asc',
12860 filterCallback: 'inprogress'
12861 }, {
12862 alias: 'writer',
12863 type: 'array',
12864 key: 'writer',
12865 sortOrder: 'asc',
12866 filterCallback: 'multiple'
12867 }, {
12868 alias: 'director',
12869 type: 'array',
12870 key: 'director',
12871 sortOrder: 'asc',
12872 filterCallback: 'multiple'
12873 }, {
12874 alias: 'tag',
12875 type: 'array',
12876 key: 'tag',
12877 sortOrder: 'asc',
12878 filterCallback: 'multiple'
12879 }, {
12880 alias: 'actor',
12881 type: 'object',
12882 property: 'name',
12883 key: 'cast',
12884 sortOrder: 'asc',
12885 filterCallback: 'multiple'
12886 }, {
12887 alias: 'set',
12888 type: 'string',
12889 property: 'set',
12890 key: 'set',
12891 sortOrder: 'asc',
12892 filterCallback: 'multiple'
12893 }, {
12894 alias: 'rated',
12895 type: 'string',
12896 property: 'mpaa',
12897 key: 'mpaa',
12898 sortOrder: 'asc',
12899 filterCallback: 'multiple'
12900 }, {
12901 alias: 'studio',
12902 type: 'array',
12903 property: 'studio',
12904 key: 'studio',
12905 sortOrder: 'asc',
12906 filterCallback: 'multiple'
12907 }, {
12908 alias: 'label',
12909 type: 'string',
12910 property: 'albumlabel',
12911 key: 'albumlabel',
12912 sortOrder: 'asc',
12913 filterCallback: 'multiple'
12914 }, {
12915 alias: 'Thumbs up',
12916 type: "boolean",
12917 key: 'thumbsUp',
12918 sortOrder: 'asc',
12919 filterCallback: 'thumbsup'
12920 }, {
12921 alias: 'album',
12922 type: 'string',
12923 key: 'album',
12924 sortOrder: 'asc',
12925 filterCallback: 'multiple'
12926 }, {
12927 alias: 'artist',
12928 type: 'array',
12929 key: 'artist',
12930 sortOrder: 'asc',
12931 filterCallback: 'multiple'
12934 getFilterFields: function(type) {
12935 var available, availableFilters, field, fields, key, len, n, ret;
12936 key = type + 'Fields';
12937 fields = API[key];
12938 availableFilters = API.getAvailable();
12939 available = availableFilters[type];
12940 ret = [];
12941 for (n = 0, len = fields.length; n < len; n++) {
12942 field = fields[n];
12943 if (helpers.global.inArray(field.key, available)) {
12944 ret.push(field);
12947 return ret;
12951 Storage
12953 storeFiltersNamespace: 'filter:store:',
12954 getStoreNameSpace: function(type) {
12955 return API.storeFiltersNamespace + type;
12957 setStoreFilters: function(filters, type) {
12958 var store;
12959 if (filters == null) {
12960 filters = {};
12962 if (type == null) {
12963 type = 'filters';
12965 store = {};
12966 store[helpers.url.path()] = filters;
12967 helpers.cache.set(API.getStoreNameSpace(type), store);
12968 return App.vent.trigger('filter:changed', filters);
12970 getStoreFilters: function(type) {
12971 var filters, key, path, ret, store, val;
12972 if (type == null) {
12973 type = 'filters';
12975 store = helpers.cache.get(API.getStoreNameSpace(type), {});
12976 path = helpers.url.path();
12977 filters = store[path] ? store[path] : {};
12978 ret = {};
12979 if (!_.isEmpty(filters)) {
12980 for (key in filters) {
12981 val = filters[key];
12982 if (val.length > 0) {
12983 ret[key] = val;
12987 return ret;
12989 updateStoreFiltersKey: function(key, values) {
12990 var filters;
12991 if (values == null) {
12992 values = [];
12994 filters = API.getStoreFilters();
12995 filters[key] = values;
12996 API.setStoreFilters(filters);
12997 return filters;
12999 getStoreFiltersKey: function(key) {
13000 var filter, filters;
13001 filters = API.getStoreFilters();
13002 filter = filters[key] ? filters[key] : [];
13003 return filter;
13005 setStoreSort: function(method, order) {
13006 var sort;
13007 if (order == null) {
13008 order = 'asc';
13010 sort = {
13011 method: method,
13012 order: order
13014 return API.setStoreFilters(sort, 'sort');
13016 getStoreSort: function() {
13017 var defaults, sort;
13018 sort = API.getStoreFilters('sort');
13019 if (!sort.method) {
13020 defaults = _.findWhere(API.getFilterFields('sort'), {
13021 defaultSort: true
13023 sort = {
13024 method: defaults.key,
13025 order: defaults.defaultOrder
13028 return sort;
13030 setAvailable: function(available) {
13031 return API.setStoreFilters(available, 'available');
13033 getAvailable: function() {
13034 return API.getStoreFilters('available');
13038 Parsing
13040 toggleOrder: function(order) {
13041 order = order === 'asc' ? 'desc' : 'asc';
13042 return order;
13044 parseSortable: function(items) {
13045 var i, item, params;
13046 params = API.getStoreSort(false, 'asc');
13047 for (i in items) {
13048 item = items[i];
13049 items[i].active = false;
13050 items[i].order = item.defaultOrder;
13051 if (params.method && item.key === params.method) {
13052 items[i].active = true;
13053 items[i].order = this.toggleOrder(params.order);
13054 } else if (item.defaultSort && params.method === false) {
13055 items[i].active = true;
13058 return items;
13060 parseFilterable: function(items) {
13061 var active, activeItem, i, val;
13062 active = API.getFilterActive();
13063 for (i in items) {
13064 val = items[i];
13065 activeItem = _.findWhere(active, {
13066 key: val.key
13068 items[i].active = activeItem !== void 0;
13070 return items;
13072 getFilterOptions: function(key, collection) {
13073 var collectionItems, data, i, item, items, len, limited, n, s, values;
13074 values = App.request('filter:store:key:get', key);
13075 s = API.getFilterSettings(key);
13076 items = [];
13077 collectionItems = collection.pluck(key);
13078 if (s.filterCallback === 'multiple' && s.type === 'object') {
13079 limited = [];
13080 for (n = 0, len = collectionItems.length; n < len; n++) {
13081 item = collectionItems[n];
13082 for (i in item) {
13083 data = item[i];
13084 if (i < 5) {
13085 limited.push(data[s.property]);
13089 collectionItems = limited;
13091 _.map(_.uniq(_.flatten(collectionItems)), function(val) {
13092 return items.push({
13093 key: key,
13094 value: val,
13095 active: helpers.global.inArray(val, values)
13098 return items;
13102 Apply filters
13104 applyFilters: function(collection) {
13105 var filteredCollection, key, ref, sort, values;
13106 sort = API.getStoreSort();
13107 collection.sortCollection(sort.method, sort.order);
13108 filteredCollection = new App.Entities.Filtered(collection);
13109 ref = API.getStoreFilters();
13110 for (key in ref) {
13111 values = ref[key];
13112 if (values.length > 0) {
13113 filteredCollection = API.applyFilter(filteredCollection, key, values);
13116 return filteredCollection;
13118 applyFilter: function(collection, key, vals) {
13119 var s;
13120 s = API.getFilterSettings(key);
13121 switch (s.filterCallback) {
13122 case 'multiple':
13123 if (s.type === 'array') {
13124 collection.filterByMultipleArray(key, vals);
13125 } else if (s.type === 'object') {
13126 collection.filterByMultipleObject(key, s.property, vals);
13127 } else {
13128 collection.filterByMultiple(key, vals);
13130 break;
13131 case 'unwatched':
13132 collection.filterByUnwatched();
13133 break;
13134 case 'watched':
13135 collection.filterByWatched();
13136 break;
13137 case 'inprogress':
13138 collection.filterByInProgress();
13139 break;
13140 case 'thumbsup':
13141 collection.filterByThumbsUp();
13142 break;
13143 default:
13144 collection;
13146 return collection;
13148 getFilterSettings: function(key, availableOnly) {
13149 var filters;
13150 if (availableOnly == null) {
13151 availableOnly = true;
13153 filters = availableOnly === true ? API.getFilterFields('filter') : API.filterFields;
13154 return _.findWhere(filters, {
13155 key: key
13158 getFilterActive: function() {
13159 var items, key, ref, values;
13160 items = [];
13161 ref = API.getStoreFilters();
13162 for (key in ref) {
13163 values = ref[key];
13164 if (values.length > 0) {
13165 items.push({
13166 key: key,
13167 values: values
13171 return items;
13176 Handlers.
13178 App.reqres.setHandler('filter:show', function(collection) {
13179 var filters, view;
13180 API.setAvailable(collection.availableFilters);
13181 filters = new FilterApp.Show.Controller({
13182 refCollection: collection
13184 view = filters.getFilterView();
13185 return view;
13187 App.reqres.setHandler('filter:options', function(key, collection) {
13188 var filterSettings, options, optionsCollection;
13189 options = API.getFilterOptions(key, collection);
13190 optionsCollection = App.request('filter:filters:options:entities', options);
13191 filterSettings = API.getFilterSettings(key);
13192 optionsCollection.sortCollection('value', filterSettings.sortOrder);
13193 return optionsCollection;
13195 App.reqres.setHandler('filter:active', function() {
13196 return App.request('filter:active:entities', API.getFilterActive());
13198 App.reqres.setHandler('filter:apply:entities', function(collection) {
13199 var newCollection;
13200 API.setAvailable(collection.availableFilters);
13201 newCollection = API.applyFilters(collection);
13202 App.vent.trigger('filter:filtering:stop');
13203 return newCollection;
13205 App.reqres.setHandler('filter:sortable:entities', function() {
13206 return App.request('filter:sort:entities', API.parseSortable(API.getFilterFields('sort')));
13208 App.reqres.setHandler('filter:filterable:entities', function() {
13209 return App.request('filter:filters:entities', API.parseFilterable(API.getFilterFields('filter')));
13211 App.reqres.setHandler('filter:init', function(availableFilters) {
13212 var filterSettings, key, len, n, order, params, ref, results1, values;
13213 params = helpers.url.params();
13214 if (!_.isEmpty(params)) {
13215 API.setStoreFilters({});
13216 if (params.sort) {
13217 order = params.order ? params.order : 'asc';
13218 API.setStoreSort(params.sort, order);
13220 ref = availableFilters.filter;
13221 results1 = [];
13222 for (n = 0, len = ref.length; n < len; n++) {
13223 key = ref[n];
13224 if (params[key]) {
13225 values = API.getStoreFiltersKey(key);
13226 filterSettings = API.getFilterSettings(key, false);
13227 if (!helpers.global.inArray(params[key], values)) {
13228 if (filterSettings.type === 'number') {
13229 values.push(parseInt(params[key]));
13230 } else {
13231 values.push(decodeURIComponent(params[key]));
13233 results1.push(API.updateStoreFiltersKey(key, values));
13234 } else {
13235 results1.push(void 0);
13237 } else {
13238 results1.push(void 0);
13241 return results1;
13244 App.reqres.setHandler('filter:store:set', function(filters) {
13245 API.setStoreFilters(filters);
13246 return filters;
13248 App.reqres.setHandler('filter:store:get', function() {
13249 return API.getStoreFilters();
13251 App.reqres.setHandler('filter:store:key:get', function(key) {
13252 return API.getStoreFiltersKey(key);
13254 App.reqres.setHandler('filter:store:key:update', function(key, values) {
13255 if (values == null) {
13256 values = [];
13258 return API.updateStoreFiltersKey(key, values);
13260 App.reqres.setHandler('filter:store:key:toggle', function(key, value) {
13261 var i, len, n, newValues, ret, values;
13262 values = API.getStoreFiltersKey(key);
13263 ret = [];
13264 if (_.indexOf(values, value) > -1) {
13265 newValues = [];
13266 for (n = 0, len = values.length; n < len; n++) {
13267 i = values[n];
13268 if (i !== value) {
13269 newValues.push(i);
13272 ret = newValues;
13273 } else {
13274 values.push(value);
13275 ret = values;
13277 API.updateStoreFiltersKey(key, ret);
13278 return ret;
13280 App.reqres.setHandler('filter:sort:store:set', function(method, order) {
13281 if (order == null) {
13282 order = 'asc';
13284 return API.setStoreSort(method, order);
13286 return App.reqres.setHandler('filter:sort:store:get', function() {
13287 return API.getStoreSort();
13291 this.Kodi.module("FilterApp.Show", function(Show, App, Backbone, Marionette, $, _) {
13292 return Show.Controller = (function(superClass) {
13293 extend(Controller, superClass);
13295 function Controller() {
13296 return Controller.__super__.constructor.apply(this, arguments);
13299 Controller.prototype.getFilterView = function() {
13300 var collection;
13301 collection = this.getOption('refCollection');
13302 this.layoutFilters = this.getLayoutView(collection);
13303 this.listenTo(this.layoutFilters, "show", (function(_this) {
13304 return function() {
13305 _this.getSort();
13306 _this.getFilters();
13307 _this.getActive();
13308 return _this.getSections();
13310 })(this));
13311 this.listenTo(this.layoutFilters, 'filter:layout:close:filters', (function(_this) {
13312 return function() {
13313 return _this.stateChange('normal');
13315 })(this));
13316 this.listenTo(this.layoutFilters, 'filter:layout:close:options', (function(_this) {
13317 return function() {
13318 return _this.stateChange('filters');
13320 })(this));
13321 this.listenTo(this.layoutFilters, 'filter:layout:open:filters', (function(_this) {
13322 return function() {
13323 return _this.stateChange('filters');
13325 })(this));
13326 this.listenTo(this.layoutFilters, 'filter:layout:open:options', (function(_this) {
13327 return function() {
13328 return _this.stateChange('options');
13330 })(this));
13331 return this.layoutFilters;
13334 Controller.prototype.getLayoutView = function(collection) {
13335 return new Show.FilterLayout({
13336 collection: collection
13340 Controller.prototype.getSort = function() {
13341 var sortCollection, sortView;
13342 sortCollection = App.request('filter:sortable:entities');
13343 sortView = new Show.SortList({
13344 collection: sortCollection
13346 this.layoutFilters.regionSort.show(sortView);
13347 return App.listenTo(sortView, "childview:filter:sortable:select", (function(_this) {
13348 return function(parentview, childview) {
13349 App.request('filter:sort:store:set', childview.model.get('key'), childview.model.get('order'));
13350 _this.layoutFilters.trigger('filter:changed');
13351 return _this.getSort();
13353 })(this));
13356 Controller.prototype.getFilters = function(clearOptions) {
13357 var filterCollection, filtersView;
13358 if (clearOptions == null) {
13359 clearOptions = true;
13361 filterCollection = App.request('filter:filterable:entities');
13362 filtersView = new Show.FilterList({
13363 collection: filterCollection
13365 App.listenTo(filtersView, "childview:filter:filterable:select", (function(_this) {
13366 return function(parentview, childview) {
13367 var key;
13368 key = childview.model.get('key');
13369 if (childview.model.get('type') === 'boolean') {
13370 App.request('filter:store:key:toggle', key, childview.model.get('alias'));
13371 return _this.triggerChange();
13372 } else {
13373 _this.getFilterOptions(key);
13374 return _this.stateChange('options');
13377 })(this));
13378 this.layoutFilters.regionFiltersList.show(filtersView);
13379 if (clearOptions) {
13380 return this.layoutFilters.regionFiltersOptions.empty();
13384 Controller.prototype.getActive = function() {
13385 var activeCollection, optionsView;
13386 activeCollection = App.request('filter:active');
13387 optionsView = new Show.ActiveList({
13388 collection: activeCollection
13390 this.layoutFilters.regionFiltersActive.show(optionsView);
13391 App.listenTo(optionsView, "childview:filter:option:remove", (function(_this) {
13392 return function(parentview, childview) {
13393 var key;
13394 key = childview.model.get('key');
13395 App.request('filter:store:key:update', key, []);
13396 return _this.triggerChange();
13398 })(this));
13399 App.listenTo(optionsView, "childview:filter:add", (function(_this) {
13400 return function(parentview, childview) {
13401 return _this.stateChange('filters');
13403 })(this));
13404 return this.getFilterBar();
13407 Controller.prototype.getFilterOptions = function(key) {
13408 var optionsCollection, optionsView;
13409 optionsCollection = App.request('filter:options', key, this.getOption('refCollection'));
13410 optionsView = new Show.OptionList({
13411 collection: optionsCollection
13413 this.layoutFilters.regionFiltersOptions.show(optionsView);
13414 App.listenTo(optionsView, "childview:filter:option:select", (function(_this) {
13415 return function(parentview, childview) {
13416 var value;
13417 value = childview.model.get('value');
13418 childview.view.$el.find('.option').toggleClass('active');
13419 App.request('filter:store:key:toggle', key, value);
13420 return _this.triggerChange(false);
13422 })(this));
13423 return App.listenTo(optionsView, 'filter:option:deselectall', (function(_this) {
13424 return function(parentview) {
13425 parentview.view.$el.find('.option').removeClass('active');
13426 App.request('filter:store:key:update', key, []);
13427 return _this.triggerChange(false);
13429 })(this));
13432 Controller.prototype.triggerChange = function(clearOptions) {
13433 if (clearOptions == null) {
13434 clearOptions = true;
13436 App.vent.trigger('filter:filtering:start');
13437 this.getFilters(clearOptions);
13438 this.getActive();
13439 App.navigate(helpers.url.path());
13440 return this.layoutFilters.trigger('filter:changed');
13443 Controller.prototype.getFilterBar = function() {
13444 var $list, $wrapper, bar, currentFilters, list;
13445 currentFilters = App.request('filter:store:get');
13446 list = _.flatten(_.values(currentFilters));
13447 $wrapper = $('.layout-container');
13448 $list = $('.region-content-top', $wrapper);
13449 if (list.length > 0) {
13450 bar = new Show.FilterBar({
13451 filters: list
13453 $list.html(bar.render().$el);
13454 $wrapper.addClass('filters-active');
13455 return App.listenTo(bar, 'filter:remove:all', (function(_this) {
13456 return function() {
13457 App.request('filter:store:set', {});
13458 _this.triggerChange();
13459 return _this.stateChange('normal');
13461 })(this));
13462 } else {
13463 return $wrapper.removeClass('filters-active');
13467 Controller.prototype.stateChange = function(state) {
13468 var $wrapper;
13469 if (state == null) {
13470 state = 'normal';
13472 $wrapper = this.layoutFilters.$el.find('.filters-container');
13473 switch (state) {
13474 case 'filters':
13475 return $wrapper.removeClass('show-options').addClass('show-filters');
13476 case 'options':
13477 return $wrapper.addClass('show-options').removeClass('show-filters');
13478 default:
13479 return $wrapper.removeClass('show-options').removeClass('show-filters');
13483 Controller.prototype.getSections = function() {
13484 var collection, nav;
13485 collection = this.getOption('refCollection');
13486 if (collection.sectionId) {
13487 nav = App.request("navMain:children:show", collection.sectionId, 'Sections');
13488 return this.layoutFilters.regionNavSection.show(nav);
13492 return Controller;
13494 })(App.Controllers.Base);
13497 this.Kodi.module("FilterApp.Show", function(Show, App, Backbone, Marionette, $, _) {
13500 Base.
13502 Show.FilterLayout = (function(superClass) {
13503 extend(FilterLayout, superClass);
13505 function FilterLayout() {
13506 return FilterLayout.__super__.constructor.apply(this, arguments);
13509 FilterLayout.prototype.template = 'apps/filter/show/filters_ui';
13511 FilterLayout.prototype.className = "side-bar";
13513 FilterLayout.prototype.regions = {
13514 regionSort: '.sort-options',
13515 regionFiltersActive: '.filters-active',
13516 regionFiltersList: '.filters-list',
13517 regionFiltersOptions: '.filter-options-list',
13518 regionNavSection: '.nav-section'
13521 FilterLayout.prototype.triggers = {
13522 'click .close-filters': 'filter:layout:close:filters',
13523 'click .close-options': 'filter:layout:close:options',
13524 'click .open-filters': 'filter:layout:open:filters'
13527 return FilterLayout;
13529 })(App.Views.LayoutView);
13530 Show.ListItem = (function(superClass) {
13531 extend(ListItem, superClass);
13533 function ListItem() {
13534 return ListItem.__super__.constructor.apply(this, arguments);
13537 ListItem.prototype.template = 'apps/filter/show/list_item';
13539 ListItem.prototype.tagName = 'li';
13541 return ListItem;
13543 })(App.Views.ItemView);
13544 Show.List = (function(superClass) {
13545 extend(List, superClass);
13547 function List() {
13548 return List.__super__.constructor.apply(this, arguments);
13551 List.prototype.childView = Show.ListItem;
13553 List.prototype.tagName = "ul";
13555 List.prototype.className = "selection-list";
13557 return List;
13559 })(App.Views.CollectionView);
13562 Extends.
13564 Show.SortListItem = (function(superClass) {
13565 extend(SortListItem, superClass);
13567 function SortListItem() {
13568 return SortListItem.__super__.constructor.apply(this, arguments);
13571 SortListItem.prototype.triggers = {
13572 "click .sortable": "filter:sortable:select"
13575 SortListItem.prototype.initialize = function() {
13576 var classes, tag;
13577 classes = ['option', 'sortable'];
13578 if (this.model.get('active') === true) {
13579 classes.push('active');
13581 classes.push('order-' + this.model.get('order'));
13582 tag = this.themeTag('span', {
13583 'class': classes.join(' ')
13584 }, t.gettext(this.model.get('alias')));
13585 return this.model.set({
13586 title: tag
13590 return SortListItem;
13592 })(Show.ListItem);
13593 Show.SortList = (function(superClass) {
13594 extend(SortList, superClass);
13596 function SortList() {
13597 return SortList.__super__.constructor.apply(this, arguments);
13600 SortList.prototype.childView = Show.SortListItem;
13602 return SortList;
13604 })(Show.List);
13605 Show.FilterListItem = (function(superClass) {
13606 extend(FilterListItem, superClass);
13608 function FilterListItem() {
13609 return FilterListItem.__super__.constructor.apply(this, arguments);
13612 FilterListItem.prototype.triggers = {
13613 "click .filterable": "filter:filterable:select"
13616 FilterListItem.prototype.initialize = function() {
13617 var classes, tag;
13618 classes = ['option', 'option filterable'];
13619 if (this.model.get('active')) {
13620 classes.push('active');
13622 tag = this.themeTag('span', {
13623 'class': classes.join(' ')
13624 }, t.gettext(this.model.get('alias')));
13625 return this.model.set({
13626 title: tag
13630 return FilterListItem;
13632 })(Show.ListItem);
13633 Show.FilterList = (function(superClass) {
13634 extend(FilterList, superClass);
13636 function FilterList() {
13637 return FilterList.__super__.constructor.apply(this, arguments);
13640 FilterList.prototype.childView = Show.FilterListItem;
13642 return FilterList;
13644 })(Show.List);
13645 Show.OptionListItem = (function(superClass) {
13646 extend(OptionListItem, superClass);
13648 function OptionListItem() {
13649 return OptionListItem.__super__.constructor.apply(this, arguments);
13652 OptionListItem.prototype.triggers = {
13653 "click .filterable-option": "filter:option:select"
13656 OptionListItem.prototype.initialize = function() {
13657 var classes, tag;
13658 classes = ['option', 'option filterable-option'];
13659 if (this.model.get('active')) {
13660 classes.push('active');
13662 tag = this.themeTag('span', {
13663 'class': classes.join(' ')
13664 }, this.model.get('value'));
13665 return this.model.set({
13666 title: tag
13670 return OptionListItem;
13672 })(Show.ListItem);
13673 Show.OptionList = (function(superClass) {
13674 extend(OptionList, superClass);
13676 function OptionList() {
13677 return OptionList.__super__.constructor.apply(this, arguments);
13680 OptionList.prototype.template = 'apps/filter/show/filter_options';
13682 OptionList.prototype.activeValues = [];
13684 OptionList.prototype.childView = Show.OptionListItem;
13686 OptionList.prototype.childViewContainer = 'ul.selection-list';
13688 OptionList.prototype.onRender = function() {
13689 if (this.collection.length <= 10) {
13690 $('.options-search-wrapper', this.$el).addClass('hidden');
13692 return $('.options-search', this.$el).filterList();
13695 OptionList.prototype.triggers = {
13696 'click .deselect-all': 'filter:option:deselectall'
13699 return OptionList;
13701 })(App.Views.CompositeView);
13702 Show.ActiveListItem = (function(superClass) {
13703 extend(ActiveListItem, superClass);
13705 function ActiveListItem() {
13706 return ActiveListItem.__super__.constructor.apply(this, arguments);
13709 ActiveListItem.prototype.triggers = {
13710 "click .filterable-remove": "filter:option:remove"
13713 ActiveListItem.prototype.initialize = function() {
13714 var tag, text, tooltip;
13715 tooltip = t.gettext('Remove') + ' ' + this.model.escape('key') + ' ' + t.gettext('filter');
13716 text = this.themeTag('span', {
13717 'class': 'text'
13718 }, this.model.get('values').join(', '));
13719 tag = this.themeTag('span', {
13720 'class': 'filter-btn filterable-remove',
13721 title: tooltip
13722 }, text);
13723 return this.model.set({
13724 title: tag
13728 return ActiveListItem;
13730 })(Show.ListItem);
13731 Show.ActiveNewListItem = (function(superClass) {
13732 extend(ActiveNewListItem, superClass);
13734 function ActiveNewListItem() {
13735 return ActiveNewListItem.__super__.constructor.apply(this, arguments);
13738 ActiveNewListItem.prototype.triggers = {
13739 "click .filterable-add": "filter:add"
13742 ActiveNewListItem.prototype.initialize = function() {
13743 var tag;
13744 tag = this.themeTag('span', {
13745 'class': 'filter-btn filterable-add'
13746 }, t.gettext('Add filter'));
13747 return this.model.set({
13748 title: tag
13752 return ActiveNewListItem;
13754 })(Show.ListItem);
13755 Show.ActiveList = (function(superClass) {
13756 extend(ActiveList, superClass);
13758 function ActiveList() {
13759 return ActiveList.__super__.constructor.apply(this, arguments);
13762 ActiveList.prototype.childView = Show.ActiveListItem;
13764 ActiveList.prototype.emptyView = Show.ActiveNewListItem;
13766 ActiveList.prototype.className = "active-list";
13768 return ActiveList;
13770 })(Show.List);
13771 return Show.FilterBar = (function(superClass) {
13772 extend(FilterBar, superClass);
13774 function FilterBar() {
13775 return FilterBar.__super__.constructor.apply(this, arguments);
13778 FilterBar.prototype.template = 'apps/filter/show/filters_bar';
13780 FilterBar.prototype.className = "filters-active-bar";
13782 FilterBar.prototype.onRender = function() {
13783 if (this.options.filters) {
13784 return $('.filters-active-all', this.$el).text(this.options.filters.join(', '));
13788 FilterBar.prototype.triggers = {
13789 'click .remove': 'filter:remove:all'
13792 return FilterBar;
13794 })(App.Views.ItemView);
13797 this.Kodi.module("HelpApp", function(HelpApp, App, Backbone, Marionette, $, _) {
13798 var API;
13799 HelpApp.Router = (function(superClass) {
13800 extend(Router, superClass);
13802 function Router() {
13803 return Router.__super__.constructor.apply(this, arguments);
13806 Router.prototype.appRoutes = {
13807 "help": "helpOverview",
13808 "help/overview": "helpOverview",
13809 "help/:id": "helpPage"
13812 return Router;
13814 })(App.Router.Base);
13815 API = {
13816 helpOverview: function() {
13817 return new App.HelpApp.Overview.Controller();
13819 helpPage: function(id) {
13820 return new HelpApp.Show.Controller({
13821 id: id
13824 getPage: function(id, lang, callback) {
13825 var content;
13826 if (lang == null) {
13827 lang = 'en';
13829 content = $.get("lang/" + lang + "/" + id + ".html");
13830 content.fail(function(error) {
13831 if (lang !== 'en') {
13832 return API.getPage(id, 'en', callback);
13835 content.done(function(data) {
13836 return callback(data);
13838 return content;
13840 getSubNav: function() {
13841 var collection;
13842 collection = App.request("navMain:array:entities", this.getSideBarStructure());
13843 return App.request("navMain:collection:show", collection, t.gettext('Help topics'));
13845 getSideBarStructure: function() {
13846 return [
13848 title: t.gettext('About'),
13849 path: 'help'
13850 }, {
13851 title: t.gettext('Readme'),
13852 path: 'help/app-readme'
13853 }, {
13854 title: t.gettext('Changelog'),
13855 path: 'help/app-changelog'
13856 }, {
13857 title: t.gettext('Keyboard'),
13858 path: 'help/keybind-readme'
13859 }, {
13860 title: t.gettext('Add-ons'),
13861 path: 'help/addons'
13862 }, {
13863 title: t.gettext('Developers'),
13864 path: 'help/developers'
13865 }, {
13866 title: t.gettext('Translations'),
13867 path: 'help/lang-readme'
13868 }, {
13869 title: t.gettext('License'),
13870 path: 'help/license'
13875 App.reqres.setHandler('help:subnav', function() {
13876 return API.getSubNav();
13878 App.reqres.setHandler('help:page', function(id, callback) {
13879 var lang;
13880 lang = config.getLocal('lang', 'en');
13881 return API.getPage(id, lang, callback);
13883 return App.on("before:start", function() {
13884 return new HelpApp.Router({
13885 controller: API
13890 this.Kodi.module("HelpApp.Overview", function(Overview, App, Backbone, Marionette, $, _) {
13891 return Overview.Controller = (function(superClass) {
13892 extend(Controller, superClass);
13894 function Controller() {
13895 return Controller.__super__.constructor.apply(this, arguments);
13898 Controller.prototype.initialize = function(options) {
13899 return App.request("help:page", 'help-overview', (function(_this) {
13900 return function(data) {
13901 _this.layout = _this.getLayoutView(data);
13902 _this.listenTo(_this.layout, "show", function() {
13903 _this.getSideBar();
13904 return _this.getPage(data);
13906 return App.regionContent.show(_this.layout);
13908 })(this));
13911 Controller.prototype.getPage = function(data) {
13912 this.pageView = new Overview.Page({
13913 data: data
13915 this.listenTo(this.pageView, "show", (function(_this) {
13916 return function() {
13917 return _this.getReport();
13919 })(this));
13920 return this.layout.regionContent.show(this.pageView);
13923 Controller.prototype.getSideBar = function() {
13924 var subNav;
13925 subNav = App.request("help:subnav");
13926 return this.layout.regionSidebarFirst.show(subNav);
13929 Controller.prototype.getLayoutView = function() {
13930 return new Overview.Layout();
13933 Controller.prototype.getReport = function() {
13934 this.$pageView = this.pageView.$el;
13935 this.getReportChorusVersion();
13936 this.getReportKodiVersion();
13937 this.getReportWebsocketsActive();
13938 this.getReportLocalAudio();
13939 App.vent.on("sockets:available", (function(_this) {
13940 return function() {
13941 return _this.getReportWebsocketsActive();
13943 })(this));
13944 return App.vent.on("state:initialized", (function(_this) {
13945 return function() {
13946 return _this.getReportKodiVersion();
13948 })(this));
13951 Controller.prototype.getReportChorusVersion = function() {
13952 return $.get("addon.xml", (function(_this) {
13953 return function(data) {
13954 return $('.report-chorus-version > span', _this.$pageView).text($('addon', data).attr('version'));
13956 })(this));
13959 Controller.prototype.getReportKodiVersion = function() {
13960 var kodiVersion, state;
13961 state = App.request("state:kodi");
13962 kodiVersion = state.getState('version');
13963 return $('.report-kodi-version > span', this.$pageView).text(kodiVersion.major + '.' + kodiVersion.minor);
13966 Controller.prototype.getReportWebsocketsActive = function() {
13967 var $ws, wsActive;
13968 wsActive = App.request("sockets:active");
13969 $ws = $('.report-websockets', this.$pageView);
13970 if (wsActive) {
13971 $('span', $ws).text(tr("Remote control is set up correctly"));
13972 return $ws.removeClass('warning');
13973 } else {
13974 $('span', $ws).html(tr("You need to 'Allow remote control' for Kodi. You can do that") + ' <a href="#settings/kodi/services">' + tr('here') + '</a>');
13975 return $ws.addClass('warning');
13979 Controller.prototype.getReportLocalAudio = function() {
13980 var localAudio;
13981 localAudio = soundManager.useHTML5Audio ? "HTML 5" : "Flash";
13982 return $('.report-local-audio > span', this.$pageView).text(localAudio);
13985 return Controller;
13987 })(App.Controllers.Base);
13990 this.Kodi.module("HelpApp.Overview", function(Overview, App, Backbone, Marionette, $, _) {
13991 Overview.Page = (function(superClass) {
13992 extend(Page, superClass);
13994 function Page() {
13995 return Page.__super__.constructor.apply(this, arguments);
13998 Page.prototype.className = "help--overview";
14000 Page.prototype.template = 'apps/help/overview/overview';
14002 Page.prototype.tagName = "div";
14004 Page.prototype.onRender = function() {
14005 return $('.help--overview--header', this.$el).html(this.options.data);
14008 return Page;
14010 })(App.Views.CompositeView);
14011 return Overview.Layout = (function(superClass) {
14012 extend(Layout, superClass);
14014 function Layout() {
14015 return Layout.__super__.constructor.apply(this, arguments);
14018 Layout.prototype.className = "help--page help--overview page-wrapper";
14020 return Layout;
14022 })(App.Views.LayoutWithSidebarFirstView);
14025 this.Kodi.module("HelpApp.Show", function(Show, App, Backbone, Marionette, $, _) {
14026 return Show.Controller = (function(superClass) {
14027 extend(Controller, superClass);
14029 function Controller() {
14030 return Controller.__super__.constructor.apply(this, arguments);
14033 Controller.prototype.initialize = function(options) {
14034 return App.request("help:page", options.id, (function(_this) {
14035 return function(data) {
14036 _this.layout = _this.getLayoutView(data);
14037 _this.listenTo(_this.layout, "show", function() {
14038 return _this.getSideBar();
14040 App.regionContent.show(_this.layout);
14041 if (options.pageView) {
14042 return _this.layout.regionContent.show(options.pageView);
14045 })(this));
14048 Controller.prototype.getSideBar = function() {
14049 var subNav;
14050 subNav = App.request("help:subnav");
14051 return this.layout.regionSidebarFirst.show(subNav);
14054 Controller.prototype.getLayoutView = function(data) {
14055 return new Show.Layout({
14056 data: data,
14057 pageView: this.options.pageView
14061 return Controller;
14063 })(App.Controllers.Base);
14066 this.Kodi.module("HelpApp.Show", function(Show, App, Backbone, Marionette, $, _) {
14067 return Show.Layout = (function(superClass) {
14068 extend(Layout, superClass);
14070 function Layout() {
14071 return Layout.__super__.constructor.apply(this, arguments);
14074 Layout.prototype.className = "help--page page-wrapper";
14076 Layout.prototype.onRender = function() {
14077 return $(this.regionContent.el, this.$el).html(this.options.data);
14080 return Layout;
14082 })(App.Views.LayoutWithSidebarFirstView);
14085 this.Kodi.module("Images", function(Images, App, Backbone, Marionette, $, _) {
14086 var API;
14087 API = {
14088 imagesPath: 'images/',
14089 defaultFanartPath: 'fanart_default/',
14090 defaultFanartFiles: ['cans.jpg', 'guitar.jpg', 'speaker.jpg', 'turntable.jpg', 'amp.jpg', 'concert.jpg', 'tweeter.jpg'],
14091 getDefaultThumbnail: function() {
14092 return API.imagesPath + 'thumbnail_default.png';
14094 getRandomFanart: function() {
14095 var file, path, rand;
14096 rand = helpers.global.getRandomInt(0, API.defaultFanartFiles.length - 1);
14097 file = API.defaultFanartFiles[rand];
14098 path = API.imagesPath + API.defaultFanartPath + file;
14099 return path;
14101 parseRawPath: function(rawPath) {
14102 var path;
14103 path = config.getLocal('reverseProxy') ? 'image/' + encodeURIComponent(rawPath) : '/image/' + encodeURIComponent(rawPath);
14104 return path;
14106 setFanartBackground: function(path, region) {
14107 var $body;
14108 $body = App.getRegion(region).$el;
14109 if (path !== 'none') {
14110 if (!path) {
14111 path = this.getRandomFanart();
14113 return $body.css('background-image', 'url(' + path + ')');
14114 } else {
14115 return $body.removeAttr('style');
14118 getImageUrl: function(rawPath, type, useFallback) {
14119 var path;
14120 if (type == null) {
14121 type = 'thumbnail';
14123 if (useFallback == null) {
14124 useFallback = true;
14126 path = '';
14127 if ((rawPath == null) || rawPath === '') {
14128 switch (type) {
14129 case 'fanart':
14130 path = API.getRandomFanart();
14131 break;
14132 default:
14133 path = API.getDefaultThumbnail();
14135 } else if (type === 'trailer') {
14136 path = API.getTrailerUrl(rawPath);
14137 } else {
14138 path = API.parseRawPath(rawPath);
14140 return path;
14142 getTrailerUrl: function(rawpath) {
14143 var trailer;
14144 trailer = helpers.url.parseTrailerUrl(rawpath);
14145 return trailer.img;
14148 App.commands.setHandler("images:fanart:set", function(path, region) {
14149 if (region == null) {
14150 region = 'regionFanart';
14152 return API.setFanartBackground(path, region);
14154 App.reqres.setHandler("images:path:get", function(rawPath, type) {
14155 if (rawPath == null) {
14156 rawPath = '';
14158 if (type == null) {
14159 type = 'thumbnail';
14161 return API.getImageUrl(rawPath, type);
14163 return App.reqres.setHandler("images:path:entity", function(model) {
14164 var i, person, ref;
14165 if (model.thumbnail != null) {
14166 model.thumbnailOriginal = model.thumbnail;
14167 model.thumbnail = API.getImageUrl(model.thumbnail, 'thumbnail');
14169 if (model.fanart != null) {
14170 model.fanartOriginal = model.fanart;
14171 model.fanart = API.getImageUrl(model.fanart, 'fanart');
14173 if ((model.cast != null) && model.cast.length > 0) {
14174 ref = model.cast;
14175 for (i in ref) {
14176 person = ref[i];
14177 model.cast[i].thumbnail = API.getImageUrl(person.thumbnail, 'thumbnail');
14180 return model;
14184 this.Kodi.module("InputApp", function(InputApp, App, Backbone, Marionette, $, _) {
14185 var API;
14186 InputApp.Router = (function(superClass) {
14187 extend(Router, superClass);
14189 function Router() {
14190 return Router.__super__.constructor.apply(this, arguments);
14193 Router.prototype.appRoutes = {
14194 "remote": "remotePage"
14197 return Router;
14199 })(App.Router.Base);
14200 API = {
14201 initKeyBind: function() {
14202 return $(document).keydown((function(_this) {
14203 return function(e) {
14204 return _this.keyBind(e);
14206 })(this));
14208 inputController: function() {
14209 return App.request("command:kodi:controller", 'auto', 'Input');
14211 doInput: function(type) {
14212 return this.inputController().sendInput(type, []);
14214 doAction: function(action) {
14215 return this.inputController().sendInput('ExecuteAction', [action]);
14217 doCommand: function(command, params, callback) {
14218 return App.request('command:kodi:player', command, params, (function(_this) {
14219 return function() {
14220 return _this.pollingUpdate(callback);
14222 })(this));
14224 appController: function() {
14225 return App.request("command:kodi:controller", 'auto', 'Application');
14227 pollingUpdate: function(callback) {
14228 if (!App.request('sockets:active')) {
14229 return App.request('state:kodi:update', callback);
14232 toggleRemote: function(open) {
14233 var $body, rClass;
14234 if (open == null) {
14235 open = 'auto';
14237 $body = $('body');
14238 rClass = 'section-remote';
14239 if (open === 'auto') {
14240 open = $body.hasClass(rClass);
14242 if (open) {
14243 window.history.back();
14244 return helpers.backscroll.scrollToLast();
14245 } else {
14246 helpers.backscroll.setLast();
14247 return App.navigate("remote", {
14248 trigger: true
14252 remotePage: function() {
14253 this.toggleRemote('auto');
14254 return App.regionContent.empty();
14256 keyBind: function(e) {
14257 var kodiControl, remotePage, stateObj, vol, whiteListCommands;
14258 kodiControl = config.getLocal('keyboardControl') === 'kodi';
14259 remotePage = $('body').hasClass('page-remote');
14260 if ($(e.target).is("input, textarea, select")) {
14261 return;
14263 whiteListCommands = [17, 16, 91, 18, 70];
14264 if (helpers.global.inArray(e.which, whiteListCommands)) {
14265 return;
14267 if (!kodiControl && !remotePage) {
14268 return;
14270 if (kodiControl || remotePage) {
14271 e.preventDefault();
14273 stateObj = App.request("state:kodi");
14274 switch (e.which) {
14275 case 37:
14276 case 72:
14277 return this.doInput("Left");
14278 case 38:
14279 case 75:
14280 return this.doInput("Up");
14281 case 39:
14282 case 76:
14283 return this.doInput("Right");
14284 case 40:
14285 case 74:
14286 return this.doInput("Down");
14287 case 8:
14288 return this.doInput("Back");
14289 case 13:
14290 return this.doInput("Select");
14291 case 67:
14292 return this.doInput("ContextMenu");
14293 case 107:
14294 case 187:
14295 case 61:
14296 vol = stateObj.getState('volume') + 5;
14297 return this.appController().setVolume((vol > 100 ? 100 : Math.ceil(vol)));
14298 case 109:
14299 case 189:
14300 case 173:
14301 vol = stateObj.getState('volume') - 5;
14302 return this.appController().setVolume((vol < 0 ? 0 : Math.ceil(vol)));
14303 case 77:
14304 return this.appController().toggleMute();
14305 case 32:
14306 return this.doCommand("PlayPause", "toggle");
14307 case 88:
14308 return this.doCommand("Stop");
14309 case 84:
14310 return this.doAction("showsubtitles");
14311 case 9:
14312 return this.doAction("close");
14313 case 190:
14314 return this.doCommand("GoTo", "next");
14315 case 188:
14316 return this.doCommand("GoTo", "previous");
14317 case 220:
14318 case 160:
14319 return this.doAction("fullscreen");
14320 case 79:
14321 return this.doAction("osd");
14325 App.commands.setHandler("input:textbox", function(msg) {
14326 return App.execute("ui:textinput:show", "Input required", {
14327 msg: msg
14328 }, function(text) {
14329 API.inputController().sendText(text);
14330 return App.execute("notification:show", t.gettext('Sent text') + ' "' + text + '" ' + t.gettext('to Kodi'));
14333 App.commands.setHandler("input:textbox:close", function() {
14334 return App.execute("ui:modal:close");
14336 App.commands.setHandler("input:send", function(action) {
14337 return API.doInput(action);
14339 App.commands.setHandler("input:remote:toggle", function() {
14340 return API.toggleRemote();
14342 App.commands.setHandler("input:action", function(action) {
14343 return API.doAction(action);
14345 App.commands.setHandler("input:resume", function(model, idKey) {
14346 var controller;
14347 controller = new InputApp.Resume.Controller();
14348 return controller.resumePlay(model, idKey);
14350 App.addInitializer(function() {
14351 var controller;
14352 controller = new InputApp.Remote.Controller();
14353 return API.initKeyBind();
14355 return App.on("before:start", function() {
14356 return new InputApp.Router({
14357 controller: API
14362 this.Kodi.module("InputApp.Remote", function(Remote, App, Backbone, Marionette, $, _) {
14363 return Remote.Controller = (function(superClass) {
14364 extend(Controller, superClass);
14366 function Controller() {
14367 return Controller.__super__.constructor.apply(this, arguments);
14370 Controller.prototype.initialize = function() {
14371 return App.vent.on("shell:ready", (function(_this) {
14372 return function(options) {
14373 return _this.getRemote();
14375 })(this));
14378 Controller.prototype.getRemote = function() {
14379 var view;
14380 view = new Remote.Control();
14381 this.listenTo(view, "remote:input", function(type) {
14382 return App.execute("input:send", type);
14384 this.listenTo(view, "remote:player", function(type) {
14385 return App.request('command:kodi:player', type, []);
14387 this.listenTo(view, "remote:info", function() {
14388 if (App.request("state:kodi").isPlaying()) {
14389 return App.execute('input:action', 'osd');
14390 } else {
14391 return App.execute("input:send", 'Info');
14394 this.listenTo(view, "remote:power", (function(_this) {
14395 return function() {
14396 return _this.getShutdownMenu();
14398 })(this));
14399 App.regionRemote.show(view);
14400 return App.vent.on("state:changed", function(state) {
14401 var fanart, playingItem, stateObj;
14402 stateObj = App.request("state:current");
14403 if (stateObj.isPlayingItemChanged()) {
14404 playingItem = stateObj.getPlaying('item');
14405 fanart = App.request("images:path:get", playingItem.fanart, 'fanart');
14406 return $('#remote-background').css('background-image', 'url(' + playingItem.fanart + ')');
14411 Controller.prototype.getShutdownMenu = function() {
14412 var system;
14413 system = App.request("command:kodi:controller", 'auto', 'System');
14414 return system.getProperties(function(props) {
14415 var $content, action, actions, len, model, n, optionalActions, prop, view;
14416 actions = [];
14417 optionalActions = ['shutdown', 'reboot', 'suspend', 'hibernate'];
14418 actions.push({
14419 id: 'quit',
14420 title: 'Quit Kodi'
14422 for (n = 0, len = optionalActions.length; n < len; n++) {
14423 action = optionalActions[n];
14424 prop = 'can' + action;
14425 if (props[prop]) {
14426 actions.push({
14427 id: action,
14428 title: action
14432 model = new Backbone.Model({
14433 id: 1,
14434 actions: actions
14436 view = new Remote.System({
14437 model: model
14439 $content = view.render().$el;
14440 App.execute("ui:modal:show", tr('Shutdown menu'), $content, '', false, 'system');
14441 return App.listenTo(view, 'system:action', (function(_this) {
14442 return function(action) {
14443 switch (action) {
14444 case 'quit':
14445 App.request("command:kodi:controller", 'auto', 'Application').quit();
14446 break;
14447 case 'shutdown':
14448 system.shutdown();
14449 break;
14450 case 'reboot':
14451 system.reboot();
14452 break;
14453 case 'suspend':
14454 system.suspend();
14455 break;
14456 case 'hibernate':
14457 system.hibernate();
14458 break;
14460 return App.execute("ui:modal:close");
14462 })(this));
14466 return Controller;
14468 })(App.Controllers.Base);
14471 this.Kodi.module("InputApp.Remote", function(Remote, App, Backbone, Marionette, $, _) {
14472 Remote.Control = (function(superClass) {
14473 extend(Control, superClass);
14475 function Control() {
14476 return Control.__super__.constructor.apply(this, arguments);
14479 Control.prototype.template = 'apps/input/remote/remote_control';
14481 Control.prototype.events = {
14482 'click .input-button': 'inputClick',
14483 'click .player-button': 'playerClick',
14484 'click .close-remote': 'closeRemote'
14487 Control.prototype.triggers = {
14488 'click .power-button': 'remote:power',
14489 'click .info-button': 'remote:info'
14492 Control.prototype.inputClick = function(e) {
14493 var type;
14494 type = $(e.target).data('type');
14495 return this.trigger('remote:input', type);
14498 Control.prototype.playerClick = function(e) {
14499 var type;
14500 type = $(e.target).data('type');
14501 return this.trigger('remote:player', type);
14504 Control.prototype.closeRemote = function(e) {
14505 return App.execute("input:remote:toggle");
14508 Remote.Landing = (function(superClass1) {
14509 extend(Landing, superClass1);
14511 function Landing() {
14512 return Landing.__super__.constructor.apply(this, arguments);
14515 return Landing;
14517 })(App.Views.ItemView);
14519 return Control;
14521 })(App.Views.ItemView);
14522 return Remote.System = (function(superClass) {
14523 extend(System, superClass);
14525 function System() {
14526 return System.__super__.constructor.apply(this, arguments);
14529 System.prototype.template = 'apps/input/remote/system';
14531 System.prototype.className = 'system-menu';
14533 System.prototype.events = {
14534 'click li': 'doAction'
14537 System.prototype.doAction = function(e) {
14538 var action;
14539 action = $(e.target).data('action');
14540 return this.trigger('system:action', action);
14543 return System;
14545 })(App.Views.ItemView);
14548 this.Kodi.module("InputApp.Resume", function(Resume, App, Backbone, Marionette, $, _) {
14549 return Resume.Controller = (function(superClass) {
14550 extend(Controller, superClass);
14552 function Controller() {
14553 return Controller.__super__.constructor.apply(this, arguments);
14556 Controller.prototype.resumePlay = function(model, idKey) {
14557 var $el, complete_string, item, items, len, n, options, percent, resume, resume_string, start_string, stateObj, time_string, title;
14558 stateObj = App.request("state:current");
14559 title = t.gettext('Resume playback');
14560 resume = model.get('resume');
14561 percent = 0;
14562 options = [];
14563 if (parseInt(resume.position) > 0 && stateObj.getPlayer() === 'kodi') {
14564 percent = helpers.global.getPercent(resume.position, resume.total);
14565 time_string = helpers.global.formatTime(helpers.global.secToTime(resume.position));
14566 complete_string = helpers.global.round(percent, 0) + '% ' + t.gettext('complete');
14567 resume_string = t.gettext('Resume from') + ' <strong>' + time_string + '</strong> <small>' + complete_string + '</small>';
14568 start_string = t.gettext('Start from the beginning');
14569 items = [
14571 title: resume_string,
14572 percent: percent
14573 }, {
14574 title: start_string,
14575 percent: 0
14578 for (n = 0, len = items.length; n < len; n++) {
14579 item = items[n];
14580 $el = $('<span>').attr('data-percent', item.percent).html(item.title).click(function(e) {
14581 return App.execute("command:video:play", model, idKey, $(this).data('percent'));
14583 options.push($el);
14585 return App.execute("ui:modal:options", title, options);
14586 } else {
14587 return App.execute("command:video:play", model, idKey, 0);
14591 Controller.prototype.initialize = function() {};
14593 return Controller;
14595 })(App.Controllers.Base);
14598 this.Kodi.module("LabApp.apiBrowser", function(apiBrowser, App, Backbone, Marionette, $, _) {
14599 return apiBrowser.Controller = (function(superClass) {
14600 extend(Controller, superClass);
14602 function Controller() {
14603 return Controller.__super__.constructor.apply(this, arguments);
14606 Controller.prototype.initialize = function() {
14607 var collection;
14608 collection = App.request("introspect:entities");
14609 return App.execute("when:entity:fetched", collection, (function(_this) {
14610 return function() {
14611 collection.dictionary = App.request("introspect:dictionary");
14612 _this.layout = _this.getLayoutView(collection);
14613 _this.listenTo(_this.layout, "show", function() {
14614 _this.renderList(collection);
14615 if (_this.options.method) {
14616 return _this.renderPage(_this.options.method, collection);
14617 } else {
14618 return _this.renderLanding();
14621 return App.regionContent.show(_this.layout);
14623 })(this));
14626 Controller.prototype.getLayoutView = function(collection) {
14627 return new apiBrowser.Layout({
14628 collection: collection
14632 Controller.prototype.renderList = function(collection) {
14633 var view;
14634 view = new apiBrowser.apiMethods({
14635 collection: collection
14637 this.listenTo(view, 'childview:lab:apibrowser:method:view', (function(_this) {
14638 return function(item) {
14639 return _this.renderPage(item.model.get('id'), collection);
14641 })(this));
14642 return this.layout.regionSidebarFirst.show(view);
14645 Controller.prototype.renderPage = function(id, collection) {
14646 var model, pageView;
14647 model = App.request("introspect:entity", id, collection);
14648 pageView = new apiBrowser.apiMethodPage({
14649 model: model
14651 helpers.debug.msg("Params/Returns for " + (model.get('method')) + ":", 'info', [model.get('params'), model.get('returns')]);
14652 this.listenTo(pageView, 'lab:apibrowser:execute', (function(_this) {
14653 return function(item) {
14654 var api, input, method, params;
14655 input = $('.api-method--params').val();
14656 params = JSON.parse(input);
14657 method = item.model.get('method');
14658 helpers.debug.msg("Parameters for: " + method, 'info', params);
14659 api = App.request("command:kodi:controller", "auto", "Commander");
14660 return api.singleCommand(method, params, function(response) {
14661 var output;
14662 helpers.debug.msg("Response for: " + method, 'info', response);
14663 output = prettyPrint(response);
14664 return $('#api-result').html(output).prepend($('<h3>Response (check the console for more)</h3>'));
14667 })(this));
14668 App.navigate("lab/api-browser/" + (model.get('method')));
14669 return this.layout.regionContent.show(pageView);
14672 Controller.prototype.renderLanding = function() {
14673 var view;
14674 view = new apiBrowser.apiBrowserLanding();
14675 return this.layout.regionContent.show(view);
14678 return Controller;
14680 })(App.Controllers.Base);
14683 this.Kodi.module("LabApp.apiBrowser", function(apiBrowser, App, Backbone, Marionette, $, _) {
14684 apiBrowser.Layout = (function(superClass) {
14685 extend(Layout, superClass);
14687 function Layout() {
14688 return Layout.__super__.constructor.apply(this, arguments);
14691 Layout.prototype.className = "api-browser--page page-wrapper";
14693 return Layout;
14695 })(App.Views.LayoutWithSidebarFirstView);
14696 apiBrowser.apiMethodItem = (function(superClass) {
14697 extend(apiMethodItem, superClass);
14699 function apiMethodItem() {
14700 return apiMethodItem.__super__.constructor.apply(this, arguments);
14703 apiMethodItem.prototype.className = "api-browser--method";
14705 apiMethodItem.prototype.template = 'apps/lab/apiBrowser/api_method_item';
14707 apiMethodItem.prototype.tagName = "li";
14709 apiMethodItem.prototype.triggers = {
14710 'click .api-method--item': 'lab:apibrowser:method:view'
14713 return apiMethodItem;
14715 })(App.Views.ItemView);
14716 apiBrowser.apiMethods = (function(superClass) {
14717 extend(apiMethods, superClass);
14719 function apiMethods() {
14720 return apiMethods.__super__.constructor.apply(this, arguments);
14723 apiMethods.prototype.template = 'apps/lab/apiBrowser/api_method_list';
14725 apiMethods.prototype.childView = apiBrowser.apiMethodItem;
14727 apiMethods.prototype.childViewContainer = 'ul.items';
14729 apiMethods.prototype.tagName = "div";
14731 apiMethods.prototype.className = "api-browser--methods";
14733 apiMethods.prototype.onRender = function() {
14734 return $('#api-search', this.$el).filterList({
14735 items: '.api-browser--methods .api-browser--method',
14736 textSelector: '.method'
14740 return apiMethods;
14742 })(App.Views.CompositeView);
14743 apiBrowser.apiMethodPage = (function(superClass) {
14744 extend(apiMethodPage, superClass);
14746 function apiMethodPage() {
14747 return apiMethodPage.__super__.constructor.apply(this, arguments);
14750 apiMethodPage.prototype.className = "api-browser--page";
14752 apiMethodPage.prototype.template = 'apps/lab/apiBrowser/api_method_page';
14754 apiMethodPage.prototype.tagName = "div";
14756 apiMethodPage.prototype.triggers = {
14757 'click #send-command': 'lab:apibrowser:execute'
14760 apiMethodPage.prototype.regions = {
14761 'apiResult': '#api-result'
14764 apiMethodPage.prototype.onShow = function() {
14765 $('.api-method--params', this.$el).html(prettyPrint(this.model.get('params')));
14766 if (this.model.get('type') === 'method') {
14767 return $('.api-method--return', this.$el).html(prettyPrint(this.model.get('returns')));
14771 return apiMethodPage;
14773 })(App.Views.ItemView);
14774 return apiBrowser.apiBrowserLanding = (function(superClass) {
14775 extend(apiBrowserLanding, superClass);
14777 function apiBrowserLanding() {
14778 return apiBrowserLanding.__super__.constructor.apply(this, arguments);
14781 apiBrowserLanding.prototype.className = "api-browser--landing";
14783 apiBrowserLanding.prototype.template = 'apps/lab/apiBrowser/api_browser_landing';
14785 apiBrowserLanding.prototype.tagName = "div";
14787 return apiBrowserLanding;
14789 })(App.Views.ItemView);
14792 this.Kodi.module("LabApp.IconBrowser", function(lab, App, Backbone, Marionette, $, _) {
14793 return lab.IconsPage = (function(superClass) {
14794 extend(IconsPage, superClass);
14796 function IconsPage() {
14797 return IconsPage.__super__.constructor.apply(this, arguments);
14800 IconsPage.prototype.template = 'apps/lab/iconBrowser/icon_browser_page';
14802 IconsPage.prototype.tagName = "div";
14804 IconsPage.prototype.className = "icon-browser page";
14806 IconsPage.prototype.onRender = function() {
14807 var $ctx, $ico, icoClass, len, n, name, ref, results1, set, type;
14808 ref = ['material', 'custom'];
14809 results1 = [];
14810 for (n = 0, len = ref.length; n < len; n++) {
14811 type = ref[n];
14812 $ctx = $('#icons-' + type, this.$el);
14813 set = type + 'Icons';
14814 results1.push((function() {
14815 var ref1, results2;
14816 ref1 = this.options[set];
14817 results2 = [];
14818 for (icoClass in ref1) {
14819 name = ref1[icoClass];
14820 $ico = $('<li><i class="' + icoClass + '"></i><span>' + name + '</span><small>' + icoClass + '</small></li>');
14821 results2.push($ctx.append($ico));
14823 return results2;
14824 }).call(this));
14826 return results1;
14829 return IconsPage;
14831 })(App.Views.LayoutView);
14834 this.Kodi.module("LabApp.lab", function(lab, App, Backbone, Marionette, $, _) {
14835 lab.labItem = (function(superClass) {
14836 extend(labItem, superClass);
14838 function labItem() {
14839 return labItem.__super__.constructor.apply(this, arguments);
14842 labItem.prototype.className = "lab--item";
14844 labItem.prototype.template = 'apps/lab/lab/lab_item';
14846 labItem.prototype.tagName = "div";
14848 return labItem;
14850 })(App.Views.ItemView);
14851 return lab.labItems = (function(superClass) {
14852 extend(labItems, superClass);
14854 function labItems() {
14855 return labItems.__super__.constructor.apply(this, arguments);
14858 labItems.prototype.tagName = "div";
14860 labItems.prototype.className = "lab--items page";
14862 labItems.prototype.childView = lab.labItem;
14864 labItems.prototype.onRender = function() {
14865 this.$el.prepend($('<h3>').text(t.gettext('Experimental code, use at own risk')));
14866 this.$el.prepend($('<h2>').text(t.gettext('The lab')));
14867 return this.$el.addClass('page-secondary');
14870 return labItems;
14872 })(App.Views.CollectionView);
14875 this.Kodi.module("LabApp", function(LabApp, App, Backbone, Marionette, $, _) {
14876 var API;
14877 LabApp.Router = (function(superClass) {
14878 extend(Router, superClass);
14880 function Router() {
14881 return Router.__super__.constructor.apply(this, arguments);
14884 Router.prototype.appRoutes = {
14885 "lab": "labLanding",
14886 "lab/api-browser": "apiBrowser",
14887 "lab/api-browser/:method": "apiBrowser",
14888 "lab/screenshot": "screenShot",
14889 "lab/icon-browser": "iconBrowser"
14892 return Router;
14894 })(App.Router.Base);
14895 API = {
14896 labLanding: function() {
14897 var view;
14898 view = new LabApp.lab.labItems({
14899 collection: new App.Entities.NavMainCollection(this.labItems())
14901 return App.regionContent.show(view);
14903 labItems: function() {
14904 return [
14906 title: 'API browser',
14907 description: 'Execute any API command.',
14908 path: 'lab/api-browser'
14909 }, {
14910 title: 'Screenshot',
14911 description: 'Take a screenshot of Kodi right now.',
14912 path: 'lab/screenshot'
14913 }, {
14914 title: 'Icon browser',
14915 description: 'View all the icons available to Chorus.',
14916 path: 'lab/icon-browser'
14920 apiBrowser: function(method) {
14921 if (method == null) {
14922 method = false;
14924 return new LabApp.apiBrowser.Controller({
14925 method: method
14928 screenShot: function() {
14929 App.execute("input:action", 'screenshot');
14930 App.execute("notification:show", t.gettext("Screenshot saved to your screenshots folder"));
14931 return App.navigate("#lab", {
14932 trigger: true
14935 iconBrowser: function() {
14936 return $.getJSON('lib/icons/mdi.json', (function(_this) {
14937 return function(mdiIcons) {
14938 return $.getJSON('lib/icons/icomoon.json', function(customIcons) {
14939 var view;
14940 console.log(mdiIcons, customIcons);
14941 view = new LabApp.IconBrowser.IconsPage({
14942 materialIcons: mdiIcons,
14943 customIcons: customIcons
14945 return App.regionContent.show(view);
14948 })(this));
14951 return App.on("before:start", function() {
14952 return new LabApp.Router({
14953 controller: API
14958 this.Kodi.module("LandingApp", function(LandingApp, App, Backbone, Marionette, $, _) {
14959 var API;
14960 LandingApp.Router = (function(superClass) {
14961 extend(Router, superClass);
14963 function Router() {
14964 return Router.__super__.constructor.apply(this, arguments);
14967 Router.prototype.appRoutes = {
14968 "music": "landingPage",
14969 "music/top": "landingPage",
14970 "movies/recent": "landingPage",
14971 "tvshows/recent": "landingPage",
14972 "music/genre/:filter": "filteredPage"
14975 return Router;
14977 })(App.Router.Base);
14978 API = {
14979 landingSettings: {
14980 music: {
14981 subnavId: 'music',
14982 sections: [
14984 title: 'Recently added albums',
14985 entity: 'album',
14986 sort: 'dateadded',
14987 order: 'descending',
14988 limit: 14,
14989 moreLink: 'music/albums?sort=dateadded&order=desc'
14990 }, {
14991 title: 'Recently played albums',
14992 entity: 'album',
14993 sort: 'lastplayed',
14994 order: 'descending',
14995 limit: 14
14996 }, {
14997 title: 'Random albums',
14998 entity: 'album',
14999 sort: 'random',
15000 order: 'descending',
15001 limit: 14,
15002 moreLink: 'music/albums?sort=random'
15006 musictop: {
15007 subnavId: 'music',
15008 sections: [
15010 title: 'Top Albums',
15011 entity: 'album',
15012 sort: 'playcount',
15013 order: 'descending',
15014 limit: 56,
15015 filter: {
15016 'operator': 'greaterthan',
15017 'field': 'playcount',
15018 'value': '0'
15020 }, {
15021 title: 'Top Songs',
15022 entity: 'song',
15023 sort: 'playcount',
15024 order: 'descending',
15025 limit: 100,
15026 filter: {
15027 'operator': 'greaterthan',
15028 'field': 'playcount',
15029 'value': '0'
15034 moviesrecent: {
15035 subnavId: 'movies/recent',
15036 sections: [
15038 title: 'Continue watching',
15039 entity: 'movie',
15040 sort: 'lastplayed',
15041 order: 'descending',
15042 limit: 14,
15043 filter: {
15044 'operator': 'true',
15045 'field': 'inprogress',
15046 'value': ''
15048 moreLink: 'movies?sort=dateadded&order=desc&inprogress=in progress'
15049 }, {
15050 title: 'Recently added',
15051 entity: 'movie',
15052 sort: 'dateadded',
15053 order: 'descending',
15054 limit: 14,
15055 filter: {
15056 'operator': 'is',
15057 'field': 'playcount',
15058 'value': '0'
15060 moreLink: 'movies?sort=dateadded&order=desc&unwatched=unwatched'
15061 }, {
15062 title: 'Random movies',
15063 entity: 'movie',
15064 sort: 'random',
15065 order: 'descending',
15066 limit: 14,
15067 moreLink: 'movies?sort=random'
15071 tvshowsrecent: {
15072 subnavId: 'tvshows/recent',
15073 sections: [
15075 title: 'Continue watching',
15076 entity: 'tvshow',
15077 sort: 'lastplayed',
15078 order: 'descending',
15079 limit: 14,
15080 filter: {
15081 'operator': 'true',
15082 'field': 'inprogress',
15083 'value': ''
15085 moreLink: 'tvshows?sort=dateadded&order=desc&inprogress=in progress',
15086 preventSelect: true
15087 }, {
15088 title: 'Recently added',
15089 entity: 'episode',
15090 sort: 'dateadded',
15091 order: 'descending',
15092 limit: 12,
15093 filter: {
15094 'operator': 'is',
15095 'field': 'playcount',
15096 'value': '0'
15102 filteredSettings: {
15103 musicgenre: {
15104 subnavId: 'music',
15105 sections: [
15107 title: '%1$s Artists',
15108 entity: 'artist',
15109 sort: 'title',
15110 order: 'ascending',
15111 limit: 500,
15112 filter: {
15113 'operator': 'is',
15114 'field': 'genre',
15115 'value': '[FILTER]'
15117 }, {
15118 title: '%1$s Albums',
15119 entity: 'album',
15120 sort: 'title',
15121 order: 'ascending',
15122 limit: 500,
15123 filter: {
15124 'operator': 'is',
15125 'field': 'genre',
15126 'value': '[FILTER]'
15128 }, {
15129 title: '%1$s Songs',
15130 entity: 'song',
15131 sort: 'title',
15132 order: 'ascending',
15133 limit: 1000,
15134 filter: {
15135 'operator': 'is',
15136 'field': 'genre',
15137 'value': '[FILTER]'
15143 landingPage: function() {
15144 var settings, type;
15145 type = helpers.url.arg(0) + helpers.url.arg(1);
15146 settings = API.landingSettings[type];
15147 return new LandingApp.Show.Controller({
15148 settings: settings,
15149 filter: false
15152 filteredPage: function(filter) {
15153 var settings, type;
15154 type = helpers.url.arg(0) + helpers.url.arg(1);
15155 settings = API.filteredSettings[type];
15156 return new LandingApp.Show.Controller({
15157 settings: settings,
15158 filter: decodeURIComponent(filter)
15162 return App.on("before:start", function() {
15163 return new LandingApp.Router({
15164 controller: API
15169 this.Kodi.module("LandingApp.Show", function(Show, App, Backbone, Marionette, $, _) {
15170 return Show.Controller = (function(superClass) {
15171 extend(Controller, superClass);
15173 function Controller() {
15174 this.renderSection = bind(this.renderSection, this);
15175 this.getSections = bind(this.getSections, this);
15176 return Controller.__super__.constructor.apply(this, arguments);
15179 Controller.prototype.initialize = function(options) {
15180 this.fanarts = [];
15181 this.rendered = 0;
15182 this.settings = options.settings;
15183 this.layout = this.getLayoutView();
15184 $('body').addClass('landing-loading');
15185 this.listenTo(this.layout, "show", (function(_this) {
15186 return function() {
15187 _this.content = _this.getContentView();
15188 _this.listenTo(_this.content, "show", function() {
15189 window.scroll(0, 350);
15190 _this.getSections(_this.settings.sections);
15191 return _this.getSubNav(_this.settings.subnavId);
15193 return _this.layout.regionContent.show(_this.content);
15195 })(this));
15196 return App.regionContent.show(this.layout);
15199 Controller.prototype.getLayoutView = function() {
15200 return new Show.Layout();
15203 Controller.prototype.getContentView = function() {
15204 return new Show.Page();
15207 Controller.prototype.getSubNav = function(subnavId) {
15208 var subNav;
15209 subNav = App.request("navMain:children:show", subnavId, 'Sections');
15210 return this.layout.regionSidebarFirst.show(subNav);
15213 Controller.prototype.getSections = function(sections) {
15214 var i, results1, section;
15215 results1 = [];
15216 for (i in sections) {
15217 section = sections[i];
15218 section.idx = parseInt(i) + 1;
15219 results1.push(this.getSection(section));
15221 return results1;
15224 Controller.prototype.getSection = function(section) {
15225 var opts;
15226 section = this.addFilterValue(section);
15227 opts = {
15228 sort: {
15229 method: section.sort,
15230 order: section.order
15232 limit: {
15233 start: 0,
15234 end: section.limit
15236 addFields: ['fanart'],
15237 cache: false,
15238 success: (function(_this) {
15239 return function(collection) {
15240 _this.rendered++;
15241 if (collection.length > 0) {
15242 _this.renderSection(section, collection);
15243 return _this.getFanArts(collection);
15246 })(this)
15248 if (section.filter) {
15249 opts.filter = section.filter;
15251 return App.request(section.entity + ":entities", opts);
15254 Controller.prototype.renderSection = function(section, collection) {
15255 var setView, view;
15256 view = App.request(section.entity + ":list:view", collection, true);
15257 setView = new Show.ListSet({
15258 section: section,
15259 filter: this.getOption('filter')
15261 App.listenTo(setView, "show", (function(_this) {
15262 return function() {
15263 return setView.regionCollection.show(view);
15265 })(this));
15266 App.listenTo(setView, 'landing:set:more', function(viewItem) {
15267 return App.navigate(viewItem.model.get('section').moreLink, {
15268 trigger: true
15271 if (this.content["regionSection" + section.idx]) {
15272 return this.content["regionSection" + section.idx].show(setView);
15276 Controller.prototype.addFilterValue = function(section) {
15277 var filterVal;
15278 filterVal = this.getOption('filter');
15279 if (filterVal !== false) {
15280 if (section.filter && section.filter.value) {
15281 section.filter.value = filterVal;
15284 return section;
15287 Controller.prototype.getFanArts = function(collection) {
15288 var $hero, item, len, n, randomModel, ref;
15289 $hero = $("#landing-hero");
15290 ref = collection.toJSON();
15291 for (n = 0, len = ref.length; n < len; n++) {
15292 item = ref[n];
15293 if (item.fanart && item.fanart !== '') {
15294 this.fanarts.push(item);
15297 if ($hero.is(':visible') && this.rendered === this.settings.sections.length && this.fanarts.length > 0) {
15298 randomModel = this.fanarts[Math.floor(Math.random() * this.fanarts.length)];
15299 $hero.css('background-image', 'url(' + randomModel.fanart + ')').attr('href', '#' + randomModel.url).attr('title', randomModel.title);
15300 return $('body').removeClass('landing-loading');
15304 return Controller;
15306 })(App.Controllers.Base);
15309 this.Kodi.module("LandingApp.Show", function(Show, App, Backbone, Marionette, $, _) {
15310 Show.Layout = (function(superClass) {
15311 extend(Layout, superClass);
15313 function Layout() {
15314 return Layout.__super__.constructor.apply(this, arguments);
15317 Layout.prototype.className = "landing-page";
15319 return Layout;
15321 })(App.Views.LayoutWithSidebarFirstView);
15322 return Show.Page = (function(superClass) {
15323 extend(Page, superClass);
15325 function Page() {
15326 return Page.__super__.constructor.apply(this, arguments);
15329 Page.prototype.template = "apps/landing/show/landing_page";
15331 Page.prototype.className = "landing-content";
15333 Page.prototype.regions = {
15334 regionHero: '#landing-hero',
15335 regionSection1: '#landing-section-1',
15336 regionSection2: '#landing-section-2',
15337 regionSection3: '#landing-section-3',
15338 regionSection4: '#landing-section-4',
15339 regionSection5: '#landing-section-5',
15340 regionSection6: '#landing-section-6'
15343 Show.ListSet = (function(superClass1) {
15344 extend(ListSet, superClass1);
15346 function ListSet() {
15347 return ListSet.__super__.constructor.apply(this, arguments);
15350 ListSet.prototype.className = 'landing-set';
15352 ListSet.prototype.triggers = {
15353 'click .more': 'landing:set:more'
15356 ListSet.prototype.initialize = function() {
15357 this.setOptions();
15358 return this.createModel();
15361 ListSet.prototype.setOptions = function() {
15362 this.options.menu = {};
15363 if (this.options.filter !== false && this.options.section.title) {
15364 this.options.title = t.sprintf(tr(this.options.section.title), this.options.filter);
15365 } else if (this.options.section.title) {
15366 this.options.title = tr(this.options.section.title);
15368 if (this.options.section.moreLink) {
15369 this.options.menu.more = tr('More like this');
15371 if (this.options.section.preventSelect) {
15372 return this.options.noMenuDefault = true;
15376 return ListSet;
15378 })(App.Views.SetLayoutView);
15380 return Page;
15382 })(App.Views.LayoutView);
15385 this.Kodi.module("LoadingApp", function(LoadingApp, App, Backbone, Marionette, $, _) {
15386 var API;
15387 API = {
15388 getLoaderView: function(msgTextHtml, inline) {
15389 if (msgTextHtml == null) {
15390 msgTextHtml = 'Just a sec...';
15392 if (inline == null) {
15393 inline = false;
15395 return new LoadingApp.Show.Page({
15396 textHtml: msgTextHtml,
15397 inline: inline
15401 App.commands.setHandler("loading:show:view", function(region, msgTextHtml) {
15402 var view;
15403 view = API.getLoaderView(msgTextHtml);
15404 return region.show(view);
15406 App.commands.setHandler("loading:show:page", function() {
15407 return App.execute("loading:show:view", App.regionContent);
15409 return App.reqres.setHandler("loading:get:view", function(msgText, inline) {
15410 if (inline == null) {
15411 inline = true;
15413 return API.getLoaderView(msgText, inline);
15417 this.Kodi.module("LoadingApp.Show", function(Show, App, Backbone, Marionette, $, _) {
15418 return Show.Page = (function(superClass) {
15419 extend(Page, superClass);
15421 function Page() {
15422 return Page.__super__.constructor.apply(this, arguments);
15425 Page.prototype.template = "apps/loading/show/loading_page";
15427 Page.prototype.onRender = function() {
15428 return this.$el.find('h2').html(this.options.textHtml);
15431 Page.prototype.attributes = function() {
15432 if (this.options.inline) {
15433 return {
15434 "class": 'loader-inline'
15439 return Page;
15441 })(Backbone.Marionette.ItemView);
15444 this.Kodi.module("localPlaylistApp.List", function(List, App, Backbone, Marionette, $, _) {
15445 return List.Controller = (function(superClass) {
15446 extend(Controller, superClass);
15448 function Controller() {
15449 return Controller.__super__.constructor.apply(this, arguments);
15452 Controller.prototype.initialize = function(options) {
15453 var id, playlists;
15454 id = options.id;
15455 playlists = App.request("localplaylist:entities");
15456 this.layout = this.getLayoutView(playlists);
15457 this.listenTo(this.layout, "show", (function(_this) {
15458 return function() {
15459 _this.getListsView(playlists);
15460 return _this.getItems(id);
15462 })(this));
15463 return App.regionContent.show(this.layout);
15466 Controller.prototype.getLayoutView = function(collection) {
15467 return new List.ListLayout({
15468 collection: collection
15472 Controller.prototype.getListsView = function(playlists) {
15473 var view;
15474 this.sideLayout = new List.SideLayout();
15475 view = new List.Lists({
15476 collection: playlists
15478 App.listenTo(this.sideLayout, "show", (function(_this) {
15479 return function() {
15480 if (playlists.length > 0) {
15481 return _this.sideLayout.regionLists.show(view);
15484 })(this));
15485 App.listenTo(this.sideLayout, 'lists:new', function() {
15486 return App.execute("localplaylist:newlist");
15488 return this.layout.regionSidebarFirst.show(this.sideLayout);
15491 Controller.prototype.getItems = function(id) {
15492 var collection, playlist;
15493 playlist = App.request("localplaylist:entity", id);
15494 collection = App.request("localplaylist:item:entities", id);
15495 this.itemLayout = new List.Layout({
15496 list: playlist
15498 App.listenTo(this.itemLayout, "show", (function(_this) {
15499 return function() {
15500 var media, view;
15501 if (collection.length > 0) {
15502 media = playlist.get('media');
15503 view = App.request(media + ":list:view", collection, true);
15504 _this.itemLayout.regionListItems.show(view);
15505 _this.bindRemove(id, view);
15506 return _this.initSortable(id, view);
15509 })(this));
15510 this.bindLayout(id);
15511 return this.layout.regionContent.show(this.itemLayout);
15514 Controller.prototype.bindLayout = function(id) {
15515 var collection;
15516 collection = App.request("localplaylist:item:entities", id);
15517 App.listenTo(this.itemLayout, 'list:clear', function() {
15518 App.execute("localplaylist:clear:entities", id);
15519 return App.execute("localplaylist:reload", id);
15521 App.listenTo(this.itemLayout, 'list:delete', function() {
15522 App.execute("localplaylist:clear:entities", id);
15523 App.execute("localplaylist:remove:entity", id);
15524 return App.navigate("playlists", {
15525 trigger: true
15528 App.listenTo(this.itemLayout, 'list:rename', function() {
15529 return App.execute("localplaylist:rename", id);
15531 App.listenTo(this.itemLayout, 'list:play', function() {
15532 var kodiPlaylist;
15533 kodiPlaylist = App.request("command:kodi:controller", 'audio', 'PlayList');
15534 return kodiPlaylist.playCollection(collection);
15536 App.listenTo(this.itemLayout, 'list:localplay', function() {
15537 var localPlaylist;
15538 localPlaylist = App.request("command:local:controller", 'audio', 'PlayList');
15539 return localPlaylist.playCollection(collection);
15541 return App.listenTo(this.itemLayout, 'list:export', function() {
15542 return App.execute("playlist:export", collection);
15546 Controller.prototype.bindRemove = function(id, view) {
15547 return App.listenTo(view, 'childview:song:remove', (function(_this) {
15548 return function(parent, viewItem) {
15549 return _this.updateOrder(id, view.$el, [parent.$el.data('id')]);
15551 })(this));
15554 Controller.prototype.initSortable = function(id, view) {
15555 var self;
15556 self = this;
15557 return $('tbody', view.$el).sortable({
15558 onEnd: (function(_this) {
15559 return function(e) {
15560 return self.updateOrder(id, _this.el);
15562 })(this)
15566 Controller.prototype.updateOrder = function(playlistId, $ctx, exclude) {
15567 var order, pos;
15568 if (exclude == null) {
15569 exclude = [];
15571 order = [];
15572 pos = 0;
15573 $('tr', $ctx).each(function(i, d) {
15574 var id;
15575 id = $(d).data('id');
15576 if (helpers.global.inArray(id, exclude)) {
15577 return $(d).remove();
15578 } else {
15579 order.push(id);
15580 $(d).data('id', pos);
15581 return pos++;
15584 return App.request("localplaylist:item:updateorder", playlistId, order);
15587 return Controller;
15589 })(App.Controllers.Base);
15592 this.Kodi.module("localPlaylistApp.List", function(List, App, Backbone, Marionette, $, _) {
15593 List.ListLayout = (function(superClass) {
15594 extend(ListLayout, superClass);
15596 function ListLayout() {
15597 return ListLayout.__super__.constructor.apply(this, arguments);
15600 ListLayout.prototype.className = "local-playlist-list";
15602 return ListLayout;
15604 })(App.Views.LayoutWithSidebarFirstView);
15605 List.SideLayout = (function(superClass) {
15606 extend(SideLayout, superClass);
15608 function SideLayout() {
15609 return SideLayout.__super__.constructor.apply(this, arguments);
15612 SideLayout.prototype.template = 'apps/localPlaylist/list/playlist_sidebar_layout';
15614 SideLayout.prototype.tagName = 'div';
15616 SideLayout.prototype.className = 'side-inner';
15618 SideLayout.prototype.regions = {
15619 regionLists: '.current-lists'
15622 SideLayout.prototype.triggers = {
15623 'click .new-list': 'lists:new'
15626 return SideLayout;
15628 })(App.Views.LayoutView);
15629 List.List = (function(superClass) {
15630 extend(List, superClass);
15632 function List() {
15633 return List.__super__.constructor.apply(this, arguments);
15636 List.prototype.template = 'apps/localPlaylist/list/playlist';
15638 List.prototype.tagName = "li";
15640 List.prototype.initialize = function() {
15641 var path;
15642 path = helpers.url.get('playlist', this.model.get('id'));
15643 this.model.set({
15644 title: this.model.get('name'),
15645 path: path
15647 if (path === helpers.url.path()) {
15648 return this.model.set({
15649 active: true
15654 return List;
15656 })(App.Views.ItemView);
15657 List.Lists = (function(superClass) {
15658 extend(Lists, superClass);
15660 function Lists() {
15661 return Lists.__super__.constructor.apply(this, arguments);
15664 Lists.prototype.template = 'apps/localPlaylist/list/playlist_list';
15666 Lists.prototype.childView = List.List;
15668 Lists.prototype.tagName = "div";
15670 Lists.prototype.childViewContainer = 'ul.lists';
15672 Lists.prototype.onRender = function() {
15673 return $('h3', this.$el).text(t.gettext('Playlists'));
15676 return Lists;
15678 })(App.Views.CompositeView);
15679 List.Selection = (function(superClass) {
15680 extend(Selection, superClass);
15682 function Selection() {
15683 return Selection.__super__.constructor.apply(this, arguments);
15686 Selection.prototype.template = 'apps/localPlaylist/list/playlist';
15688 Selection.prototype.tagName = "li";
15690 Selection.prototype.initialize = function() {
15691 return this.model.set({
15692 title: this.model.get('name')
15696 Selection.prototype.triggers = {
15697 'click .item': 'item:selected'
15700 return Selection;
15702 })(App.Views.ItemView);
15703 List.SelectionList = (function(superClass) {
15704 extend(SelectionList, superClass);
15706 function SelectionList() {
15707 return SelectionList.__super__.constructor.apply(this, arguments);
15710 SelectionList.prototype.template = 'apps/localPlaylist/list/playlist_list';
15712 SelectionList.prototype.childView = List.Selection;
15714 SelectionList.prototype.tagName = "div";
15716 SelectionList.prototype.className = 'playlist-selection-list';
15718 SelectionList.prototype.childViewContainer = 'ul.lists';
15720 SelectionList.prototype.onRender = function() {
15721 return $('h3', this.$el).text(t.gettext('Existing playlists'));
15724 return SelectionList;
15726 })(App.Views.CompositeView);
15727 return List.Layout = (function(superClass) {
15728 extend(Layout, superClass);
15730 function Layout() {
15731 return Layout.__super__.constructor.apply(this, arguments);
15734 Layout.prototype.template = 'apps/localPlaylist/list/playlist_layout';
15736 Layout.prototype.tagName = 'div';
15738 Layout.prototype.className = 'local-playlist';
15740 Layout.prototype.regions = {
15741 regionListItems: '.item-container'
15744 Layout.prototype.triggers = {
15745 'click .local-playlist-header .rename': 'list:rename',
15746 'click .local-playlist-header .clear': 'list:clear',
15747 'click .local-playlist-header .delete': 'list:delete',
15748 'click .local-playlist-header .play': 'list:play',
15749 'click .local-playlist-header .localplay': 'list:localplay',
15750 'click .local-playlist-header .export': 'list:export'
15753 Layout.prototype.onRender = function() {
15754 if (this.options && this.options.list) {
15755 return $('h2', this.$el).text(this.options.list.get('name'));
15759 return Layout;
15761 })(App.Views.LayoutView);
15764 this.Kodi.module("localPlaylistApp", function(localPlaylistApp, App, Backbone, Marionette, $, _) {
15765 var API;
15766 localPlaylistApp.Router = (function(superClass) {
15767 extend(Router, superClass);
15769 function Router() {
15770 return Router.__super__.constructor.apply(this, arguments);
15773 Router.prototype.appRoutes = {
15774 "playlists": "list",
15775 "playlist/:id": "list"
15778 return Router;
15780 })(App.Router.Base);
15783 Main functionality.
15785 API = {
15786 playlistNameMsg: 'Give your playlist a name',
15787 list: function(id) {
15788 var item, items, lists;
15789 if (id === null) {
15790 lists = App.request("localplaylist:entities");
15791 items = lists.getRawCollection();
15792 if (_.isEmpty(lists)) {
15793 id = 0;
15794 } else {
15795 item = _.min(items, function(list) {
15796 return list.id;
15798 id = item.id;
15799 App.navigate(helpers.url.get('playlist', id));
15802 return new localPlaylistApp.List.Controller({
15803 id: id
15806 addToList: function(entityType, id) {
15807 var $content, $new, playlists, view;
15808 playlists = App.request("localplaylist:entities");
15809 if (!playlists || playlists.length === 0) {
15810 return this.createNewList(entityType, id);
15811 } else {
15812 view = new localPlaylistApp.List.SelectionList({
15813 collection: playlists
15815 $content = view.render().$el;
15816 $new = $('<button>').text(tr('Create a new list')).addClass('btn btn-primary');
15817 $new.on('click', (function(_this) {
15818 return function() {
15819 return _.defer(function() {
15820 return API.createNewList(entityType, id);
15823 })(this));
15824 App.execute("ui:modal:show", tr('Add to playlist'), $content, $new);
15825 return App.listenTo(view, 'childview:item:selected', (function(_this) {
15826 return function(list, item) {
15827 return _this.addToExistingList(item.model.get('id'), entityType, id);
15829 })(this));
15832 addToExistingList: function(playlistId, entityType, ids) {
15833 var collection;
15834 if (!_.isArray(ids)) {
15835 ids = [ids];
15837 if (helpers.global.inArray(entityType, ['albumid', 'artistid', 'songid'])) {
15838 return App.request("song:custom:entities", entityType, ids, (function(_this) {
15839 return function(collection) {
15840 return _this.addCollectionToList(collection, playlistId);
15842 })(this));
15843 } else if (entityType === 'playlist') {
15844 collection = App.request("playlist:kodi:entities", 'audio');
15845 return App.execute("when:entity:fetched", collection, (function(_this) {
15846 return function() {
15847 return _this.addCollectionToList(collection, playlistId);
15849 })(this));
15850 } else {
15854 addCollectionToList: function(collection, playlistId, notify) {
15855 if (notify == null) {
15856 notify = true;
15858 App.request("localplaylist:item:add:entities", playlistId, collection);
15859 App.execute("ui:modal:close");
15860 if (notify === true) {
15861 return App.execute("notification:show", tr("Added to your playlist"));
15864 createNewList: function(entityType, id) {
15865 return App.execute("ui:textinput:show", tr('Add a new playlist'), {
15866 msg: tr(API.playlistNameMsg)
15867 }, (function(_this) {
15868 return function(text) {
15869 var playlistId;
15870 if (text !== '') {
15871 playlistId = App.request("localplaylist:add:entity", text, 'song');
15872 return _this.addToExistingList(playlistId, entityType, id);
15875 })(this), false);
15877 createEmptyList: function() {
15878 return App.execute("ui:textinput:show", tr('Add a new playlist'), {
15879 msg: tr(API.playlistNameMsg)
15880 }, (function(_this) {
15881 return function(text) {
15882 var playlistId;
15883 if (text !== '') {
15884 playlistId = App.request("localplaylist:add:entity", text, 'song');
15885 return App.navigate("playlist/" + playlistId, {
15886 trigger: true
15890 })(this));
15892 rename: function(id) {
15893 var listModel;
15894 listModel = App.request("localplaylist:entity", id);
15895 return App.execute("ui:textinput:show", tr('Rename playlist'), {
15896 msg: tr(API.playlistNameMsg),
15897 defaultVal: listModel.get('name')
15898 }, (function(_this) {
15899 return function(text) {
15900 listModel.set({
15901 name: text
15902 }).save();
15903 return API.list(id);
15905 })(this));
15910 Listeners.
15912 App.commands.setHandler("localplaylist:addentity", function(entityType, id) {
15913 return API.addToList(entityType, id);
15915 App.commands.setHandler("localplaylist:newlist", function() {
15916 return API.createEmptyList();
15918 App.commands.setHandler("localplaylist:reload", function(id) {
15919 return API.list(id);
15921 App.commands.setHandler("localplaylist:rename", function(id) {
15922 return API.rename(id);
15926 Init the router
15928 return App.on("before:start", function() {
15929 return new localPlaylistApp.Router({
15930 controller: API
15935 this.Kodi.module("MovieApp.Edit", function(Edit, App, Backbone, Marionette, $, _) {
15936 return Edit.Controller = (function(superClass) {
15937 extend(Controller, superClass);
15939 function Controller() {
15940 return Controller.__super__.constructor.apply(this, arguments);
15943 Controller.prototype.initialize = function() {
15944 var form, options;
15945 this.model = this.getOption('model');
15946 options = {
15947 titleHtml: '<span>' + tr('Edit') + '</span>' + this.model.escape('title'),
15948 form: this.getStructure(),
15949 formState: this.model.attributes,
15950 config: {
15951 attributes: {
15952 "class": 'edit-form'
15954 editForm: true,
15955 tabs: true,
15956 callback: (function(_this) {
15957 return function(data, formView) {
15958 _this.setArt(data);
15959 return _this.saveCallback(data, formView);
15961 })(this)
15964 return form = App.request("form:popup:wrapper", options);
15967 Controller.prototype.getStructure = function() {
15968 return [
15970 title: 'General',
15971 id: 'general',
15972 children: [
15974 id: 'title',
15975 title: tr('Title'),
15976 type: 'textfield'
15977 }, {
15978 id: 'plotoutline',
15979 title: tr('Tagline'),
15980 type: 'textfield'
15981 }, {
15982 id: 'plot',
15983 title: tr('Plot'),
15984 type: 'textarea'
15985 }, {
15986 id: 'studio',
15987 title: tr('Studio'),
15988 type: 'textfield',
15989 format: 'array.string'
15990 }, {
15991 id: 'year',
15992 title: tr('Year'),
15993 type: 'number',
15994 format: 'integer',
15995 attributes: {
15996 "class": 'half-width',
15997 step: 1,
15998 min: 1000,
15999 max: 9999
16001 }, {
16002 id: 'mpaa',
16003 title: tr('Content rating'),
16004 type: 'textfield',
16005 attributes: {
16006 "class": 'half-width'
16008 }, {
16009 id: 'rating',
16010 title: tr('Rating'),
16011 type: 'number',
16012 format: 'float',
16013 attributes: {
16014 "class": 'half-width',
16015 step: 0.1,
16016 min: 0,
16017 max: 10
16019 }, {
16020 id: 'imdbnumber',
16021 title: tr('IMDb'),
16022 type: 'textfield',
16023 attributes: {
16024 "class": 'half-width'
16026 suffix: '<div class="clearfix"></div>'
16027 }, {
16028 id: 'sorttitle',
16029 title: tr('Sort title'),
16030 type: 'textfield'
16031 }, {
16032 id: 'originaltitle',
16033 title: tr('Original title'),
16034 type: 'textfield'
16037 }, {
16038 title: 'Tags',
16039 id: 'tags',
16040 children: [
16042 id: 'director',
16043 title: tr('Directors'),
16044 type: 'textfield',
16045 format: 'array.string'
16046 }, {
16047 id: 'writer',
16048 title: tr('Writers'),
16049 type: 'textfield',
16050 format: 'array.string'
16051 }, {
16052 id: 'genre',
16053 title: tr('Genres'),
16054 type: 'textfield',
16055 format: 'array.string'
16056 }, {
16057 id: 'country',
16058 title: tr('Country'),
16059 type: 'textfield',
16060 format: 'array.string'
16061 }, {
16062 id: 'set',
16063 title: tr('Set'),
16064 type: 'textfield'
16065 }, {
16066 id: 'tag',
16067 title: tr('Tags'),
16068 type: 'textarea',
16069 format: 'array.string'
16072 }, {
16073 title: 'Trailer',
16074 id: 'trailers',
16075 children: [
16077 id: 'trailer',
16078 title: tr('URL'),
16079 type: 'imageselect',
16080 attributes: {
16081 "class": 'fanart-size'
16083 description: t.sprintf(tr('This should be the play path for the trailer. Eg. %1$s'), 'plugin://plugin.video.youtube/?action=play_video&videoid=[YOUTUBE_ID]'),
16084 metadataImageHandler: 'youtube:trailer:entities',
16085 metadataLookupField: 'title'
16088 }, {
16089 title: 'Poster',
16090 id: 'poster',
16091 children: [
16093 id: 'thumbnail',
16094 title: tr('URL'),
16095 type: 'imageselect',
16096 valueProperty: 'thumbnailOriginal',
16097 description: tr('Add an image via an external URL'),
16098 metadataImageHandler: 'themoviedb:movie:image:entities',
16099 metadataLookupField: 'imdbnumber'
16102 }, {
16103 title: 'Background',
16104 id: 'background',
16105 children: [
16107 id: 'fanart',
16108 title: tr('URL'),
16109 type: 'imageselect',
16110 valueProperty: 'fanartOriginal',
16111 description: tr('Add an image via an external URL'),
16112 metadataImageHandler: 'themoviedb:movie:image:entities',
16113 metadataLookupField: 'imdbnumber'
16116 }, {
16117 title: 'Information',
16118 id: 'info',
16119 children: [
16121 id: 'file',
16122 title: tr('File path'),
16123 type: 'textarea',
16124 attributes: {
16125 disabled: 'disabled',
16126 cols: 5
16128 format: 'prevent.submit'
16135 Controller.prototype.setArt = function(data) {
16136 var art;
16137 art = {};
16138 if ('fanart' in data) {
16139 art["fanart"] = data.fanart;
16141 if ('thumbnail' in data) {
16142 art["poster"] = data.thumbnail;
16143 delete data.thumbnail;
16145 return data["art"] = art;
16148 Controller.prototype.saveCallback = function(data, formView) {
16149 var controller;
16150 controller = App.request("command:kodi:controller", 'video', 'VideoLibrary');
16151 return controller.setMovieDetails(this.model.get('id'), data, (function(_this) {
16152 return function() {
16153 Kodi.vent.trigger('entity:kodi:update', _this.model.get('uid'));
16154 return App.execute("notification:show", t.sprintf(tr("Updated %1$s details"), 'movie'));
16156 })(this));
16159 return Controller;
16161 })(App.Controllers.Base);
16164 this.Kodi.module("MovieApp.List", function(List, App, Backbone, Marionette, $, _) {
16165 var API;
16166 API = {
16167 getMoviesView: function(collection, set) {
16168 var view, viewName;
16169 if (set == null) {
16170 set = false;
16172 viewName = set ? 'MoviesSet' : 'Movies';
16173 view = new List[viewName]({
16174 collection: collection
16176 API.bindTriggers(view);
16177 return view;
16179 bindTriggers: function(view) {
16180 App.listenTo(view, 'childview:movie:play', function(parent, viewItem) {
16181 return App.execute('movie:action', 'play', viewItem);
16183 App.listenTo(view, 'childview:movie:add', function(parent, viewItem) {
16184 return App.execute('movie:action', 'add', viewItem);
16186 App.listenTo(view, 'childview:movie:localplay', function(parent, viewItem) {
16187 return App.execute('movie:action', 'localplay', viewItem);
16189 App.listenTo(view, 'childview:movie:download', function(parent, viewItem) {
16190 return App.execute('movie:action', 'download', viewItem);
16192 App.listenTo(view, 'childview:movie:watched', function(parent, viewItem) {
16193 return App.execute('movie:action:watched', parent, viewItem);
16195 return App.listenTo(view, 'childview:movie:edit', function(parent, viewItem) {
16196 return App.execute('movie:action', 'edit', viewItem);
16200 List.Controller = (function(superClass) {
16201 extend(Controller, superClass);
16203 function Controller() {
16204 return Controller.__super__.constructor.apply(this, arguments);
16207 Controller.prototype.initialize = function() {
16208 var collection;
16209 collection = App.request("movie:entities");
16210 return App.execute("when:entity:fetched", collection, (function(_this) {
16211 return function() {
16212 collection.availableFilters = _this.getAvailableFilters();
16213 collection.sectionId = 'movies/recent';
16214 App.request('filter:init', _this.getAvailableFilters());
16215 _this.layout = _this.getLayoutView(collection);
16216 _this.listenTo(_this.layout, "show", function() {
16217 _this.renderList(collection);
16218 return _this.getFiltersView(collection);
16220 return App.regionContent.show(_this.layout);
16222 })(this));
16225 Controller.prototype.getLayoutView = function(collection) {
16226 return new List.ListLayout({
16227 collection: collection
16231 Controller.prototype.getAvailableFilters = function() {
16232 return {
16233 sort: ['title', 'year', 'dateadded', 'rating', 'random'],
16234 filter: ['year', 'genre', 'writer', 'director', 'cast', 'set', 'unwatched', 'watched', 'inprogress', 'mpaa', 'studio', 'thumbsUp', 'tag']
16238 Controller.prototype.getFiltersView = function(collection) {
16239 var filters;
16240 filters = App.request('filter:show', collection);
16241 this.layout.regionSidebarFirst.show(filters);
16242 return this.listenTo(filters, "filter:changed", (function(_this) {
16243 return function() {
16244 return _this.renderList(collection);
16246 })(this));
16249 Controller.prototype.renderList = function(collection) {
16250 var filteredCollection, view;
16251 App.execute("loading:show:view", this.layout.regionContent);
16252 filteredCollection = App.request('filter:apply:entities', collection);
16253 view = API.getMoviesView(filteredCollection);
16254 return this.layout.regionContent.show(view);
16257 return Controller;
16259 })(App.Controllers.Base);
16260 return App.reqres.setHandler("movie:list:view", function(collection) {
16261 return API.getMoviesView(collection, true);
16265 this.Kodi.module("MovieApp.List", function(List, App, Backbone, Marionette, $, _) {
16266 List.ListLayout = (function(superClass) {
16267 extend(ListLayout, superClass);
16269 function ListLayout() {
16270 return ListLayout.__super__.constructor.apply(this, arguments);
16273 ListLayout.prototype.className = "movie-list with-filters";
16275 return ListLayout;
16277 })(App.Views.LayoutWithSidebarFirstView);
16278 List.MovieTeaser = (function(superClass) {
16279 extend(MovieTeaser, superClass);
16281 function MovieTeaser() {
16282 return MovieTeaser.__super__.constructor.apply(this, arguments);
16285 MovieTeaser.prototype.triggers = {
16286 "click .play": "movie:play",
16287 "click .watched": "movie:watched",
16288 "click .add": "movie:add",
16289 "click .localplay": "movie:localplay",
16290 "click .download": "movie:download",
16291 "click .edit": "movie:edit"
16294 MovieTeaser.prototype.initialize = function() {
16295 MovieTeaser.__super__.initialize.apply(this, arguments);
16296 this.setMeta();
16297 if (this.model != null) {
16298 return this.model.set(App.request('movie:action:items'));
16302 MovieTeaser.prototype.attributes = function() {
16303 return this.watchedAttributes('card');
16306 MovieTeaser.prototype.setMeta = function() {
16307 if (this.model) {
16308 return this.model.set({
16309 labelHtml: this.model.get('label'),
16310 subtitleHtml: this.themeLink(this.model.get('year'), 'movies?year=' + this.model.get('year'))
16315 return MovieTeaser;
16317 })(App.Views.CardView);
16318 List.Empty = (function(superClass) {
16319 extend(Empty, superClass);
16321 function Empty() {
16322 return Empty.__super__.constructor.apply(this, arguments);
16325 Empty.prototype.tagName = "li";
16327 Empty.prototype.className = "movie-empty-result";
16329 return Empty;
16331 })(App.Views.EmptyViewResults);
16332 List.Movies = (function(superClass) {
16333 extend(Movies, superClass);
16335 function Movies() {
16336 return Movies.__super__.constructor.apply(this, arguments);
16339 Movies.prototype.childView = List.MovieTeaser;
16341 Movies.prototype.emptyView = List.Empty;
16343 Movies.prototype.tagName = "ul";
16345 Movies.prototype.className = "card-grid--tall";
16347 return Movies;
16349 })(App.Views.VirtualListView);
16350 return List.MoviesSet = (function(superClass) {
16351 extend(MoviesSet, superClass);
16353 function MoviesSet() {
16354 return MoviesSet.__super__.constructor.apply(this, arguments);
16357 MoviesSet.prototype.childView = List.MovieTeaser;
16359 MoviesSet.prototype.emptyView = List.Empty;
16361 MoviesSet.prototype.tagName = "ul";
16363 MoviesSet.prototype.className = "card-grid--tall";
16365 return MoviesSet;
16367 })(App.Views.CollectionView);
16370 this.Kodi.module("MovieApp", function(MovieApp, App, Backbone, Marionette, $, _) {
16371 var API;
16372 MovieApp.Router = (function(superClass) {
16373 extend(Router, superClass);
16375 function Router() {
16376 return Router.__super__.constructor.apply(this, arguments);
16379 Router.prototype.appRoutes = {
16380 "movies": "list",
16381 "movie/:id": "view"
16384 return Router;
16386 })(App.Router.Base);
16387 API = {
16388 list: function() {
16389 return new MovieApp.List.Controller();
16391 view: function(id) {
16392 return new MovieApp.Show.Controller({
16393 id: id
16396 action: function(op, view) {
16397 var files, model, playlist, videoLib;
16398 model = view.model;
16399 playlist = App.request("command:kodi:controller", 'video', 'PlayList');
16400 files = App.request("command:kodi:controller", 'video', 'Files');
16401 videoLib = App.request("command:kodi:controller", 'video', 'VideoLibrary');
16402 switch (op) {
16403 case 'play':
16404 return App.execute("input:resume", model, 'movieid');
16405 case 'add':
16406 return playlist.add('movieid', model.get('movieid'));
16407 case 'localplay':
16408 return files.videoStream(model.get('file'), model.get('fanart'));
16409 case 'download':
16410 return files.downloadFile(model.get('file'));
16411 case 'toggleWatched':
16412 return videoLib.toggleWatched(model, 'auto');
16413 case 'edit':
16414 return App.execute('movie:edit', model);
16415 case 'refresh':
16416 return helpers.entities.refreshEntity(model, videoLib, 'refreshMovie');
16420 App.reqres.setHandler('movie:action:items', function() {
16421 return {
16422 actions: {
16423 watched: tr('Watched'),
16424 thumbs: tr('Thumbs up')
16426 menu: {
16427 add: tr('Queue in Kodi'),
16428 'divider-1': '',
16429 download: tr('Download'),
16430 localplay: tr('Play in browser'),
16431 'divider-2': '',
16432 edit: tr('Edit')
16436 App.commands.setHandler('movie:action', function(op, view) {
16437 return API.action(op, view);
16439 App.commands.setHandler('movie:action:watched', function(parent, viewItem) {
16440 var op, progress;
16441 op = parent.$el.hasClass('is-watched') ? 'unwatched' : 'watched';
16442 progress = op === 'watched' ? 100 : 0;
16443 parent.$el.toggleClass('is-watched');
16444 helpers.entities.setProgress(parent.$el, progress);
16445 helpers.entities.setProgress(parent.$el.closest('.movie-show').find('.region-content-wrapper'), progress);
16446 return API.action('toggleWatched', viewItem);
16448 App.commands.setHandler('movie:edit', function(model) {
16449 var loadedModel;
16450 loadedModel = App.request("movie:entity", model.get('id'));
16451 return App.execute("when:entity:fetched", loadedModel, (function(_this) {
16452 return function() {
16453 return new MovieApp.Edit.Controller({
16454 model: loadedModel
16457 })(this));
16459 return App.on("before:start", function() {
16460 return new MovieApp.Router({
16461 controller: API
16466 this.Kodi.module("MovieApp.Show", function(Show, App, Backbone, Marionette, $, _) {
16467 var API;
16468 API = {
16469 bindTriggers: function(view) {
16470 App.listenTo(view, 'movie:play', function(viewItem) {
16471 return App.execute('movie:action', 'play', viewItem);
16473 App.listenTo(view, 'movie:add', function(viewItem) {
16474 return App.execute('movie:action', 'add', viewItem);
16476 App.listenTo(view, 'movie:localplay', function(viewItem) {
16477 return App.execute('movie:action', 'localplay', viewItem);
16479 App.listenTo(view, 'movie:download', function(viewItem) {
16480 return App.execute('movie:action', 'download', viewItem);
16482 App.listenTo(view, 'toggle:watched', function(viewItem) {
16483 return App.execute('movie:action:watched', viewItem.view, viewItem.view);
16485 App.listenTo(view, 'movie:refresh', function(viewItem) {
16486 return App.execute('movie:action', 'refresh', viewItem);
16488 return App.listenTo(view, 'movie:edit', function(viewItem) {
16489 return App.execute('movie:edit', viewItem.model);
16492 moreContent: [
16494 title: 'More from %1$s',
16495 filter: 'set',
16496 key: 'set',
16497 type: 'string',
16498 pluck: false
16499 }, {
16500 title: 'More %1$s movies',
16501 filter: 'genre',
16502 key: 'genre',
16503 type: 'array',
16504 pluck: false
16505 }, {
16506 title: 'More movies starring %1$s',
16507 filter: 'actor',
16508 key: 'cast',
16509 type: 'array',
16510 pluck: 'name'
16511 }, {
16512 title: 'Other movies released in %1$s',
16513 filter: 'year',
16514 key: 'year',
16515 type: 'string',
16516 pluck: false
16520 return Show.Controller = (function(superClass) {
16521 extend(Controller, superClass);
16523 function Controller() {
16524 return Controller.__super__.constructor.apply(this, arguments);
16527 Controller.prototype.initialize = function(options) {
16528 var id, movie;
16529 id = parseInt(options.id);
16530 movie = App.request("movie:entity", id);
16531 return App.execute("when:entity:fetched", movie, (function(_this) {
16532 return function() {
16533 _this.layout = _this.getLayoutView(movie);
16534 _this.listenTo(_this.layout, "show", function() {
16535 _this.getDetailsLayoutView(movie);
16536 return _this.getContentView(movie);
16538 return App.regionContent.show(_this.layout);
16540 })(this));
16543 Controller.prototype.getLayoutView = function(movie) {
16544 return new Show.PageLayout({
16545 model: movie
16549 Controller.prototype.getContentView = function(movie) {
16550 this.contentLayout = new Show.Content({
16551 model: movie
16553 this.listenTo(this.contentLayout, "movie:youtube", function(view) {
16554 var trailer;
16555 trailer = movie.get('mediaTrailer');
16556 return App.execute("ui:modal:youtube", movie.escape('title') + ' Trailer', trailer.id);
16558 this.listenTo(this.contentLayout, 'show', (function(_this) {
16559 return function() {
16560 if (movie.get('cast').length > 0) {
16561 _this.contentLayout.regionCast.show(_this.getCast(movie));
16563 return _this.getMoreContent(movie);
16565 })(this));
16566 return this.layout.regionContent.show(this.contentLayout);
16569 Controller.prototype.getCast = function(movie) {
16570 return App.request('cast:list:view', movie.get('cast'), 'movies');
16573 Controller.prototype.getDetailsLayoutView = function(movie) {
16574 var headerLayout;
16575 headerLayout = new Show.HeaderLayout({
16576 model: movie
16578 this.listenTo(headerLayout, "show", (function(_this) {
16579 return function() {
16580 var detail, teaser;
16581 teaser = new Show.MovieTeaser({
16582 model: movie
16584 API.bindTriggers(teaser);
16585 detail = new Show.Details({
16586 model: movie
16588 API.bindTriggers(detail);
16589 headerLayout.regionSide.show(teaser);
16590 return headerLayout.regionMeta.show(detail);
16592 })(this));
16593 return this.layout.regionHeader.show(headerLayout);
16596 Controller.prototype.getMoreContent = function(movie) {
16597 var filterVal, filterVals, i, idx, more, opts, ref, results1;
16598 idx = 0;
16599 ref = API.moreContent;
16600 results1 = [];
16601 for (i in ref) {
16602 more = ref[i];
16603 filterVal = false;
16604 if (more.type === 'array') {
16605 filterVals = more.pluck ? _.pluck(movie.get(more.key), more.pluck) : movie.get(more.key);
16606 filterVals = _.shuffle(filterVals.slice(0, 4));
16607 filterVal = _.first(filterVals);
16608 } else {
16609 filterVal = movie.get(more.key);
16611 if (filterVal && filterVal !== '') {
16612 idx++;
16613 opts = {
16614 limit: {
16615 start: 0,
16616 end: 6
16618 cache: false,
16619 sort: {
16620 method: 'random',
16621 order: 'ascending'
16623 filter: {},
16624 title: t.sprintf(tr(more.title), '<a href="#movies?' + more.key + '=' + _.escape(filterVal) + '">' + _.escape(filterVal) + '</a>'),
16625 idx: idx
16627 opts.filter[more.filter] = filterVal;
16628 opts.success = (function(_this) {
16629 return function(collection) {
16630 var view;
16631 collection.remove(movie);
16632 if (collection.length > 0) {
16633 view = new Show.Set({
16634 set: collection.options.title
16636 App.listenTo(view, "show", function() {
16637 var listview;
16638 listview = App.request("movie:list:view", collection);
16639 return view.regionCollection.show(listview);
16641 return _this.contentLayout["regionMore" + collection.options.idx].show(view);
16644 })(this);
16645 results1.push(App.request("movie:entities", opts));
16646 } else {
16647 results1.push(void 0);
16650 return results1;
16653 return Controller;
16655 })(App.Controllers.Base);
16658 this.Kodi.module("MovieApp.Show", function(Show, App, Backbone, Marionette, $, _) {
16659 Show.PageLayout = (function(superClass) {
16660 extend(PageLayout, superClass);
16662 function PageLayout() {
16663 return PageLayout.__super__.constructor.apply(this, arguments);
16666 PageLayout.prototype.className = 'movie-show detail-container';
16668 return PageLayout;
16670 })(App.Views.LayoutWithHeaderView);
16671 Show.HeaderLayout = (function(superClass) {
16672 extend(HeaderLayout, superClass);
16674 function HeaderLayout() {
16675 return HeaderLayout.__super__.constructor.apply(this, arguments);
16678 HeaderLayout.prototype.className = 'movie-details';
16680 return HeaderLayout;
16682 })(App.Views.LayoutDetailsHeaderView);
16683 Show.Details = (function(superClass) {
16684 extend(Details, superClass);
16686 function Details() {
16687 return Details.__super__.constructor.apply(this, arguments);
16690 Details.prototype.template = 'apps/movie/show/details_meta';
16692 Details.prototype.triggers = {
16693 'click .play': 'movie:play',
16694 'click .add': 'movie:add',
16695 'click .stream': 'movie:localplay',
16696 'click .download': 'movie:download',
16697 'click .edit': 'movie:edit',
16698 'click .refresh': 'movie:refresh'
16701 Details.prototype.attributes = function() {
16702 return this.watchedAttributes();
16705 return Details;
16707 })(App.Views.DetailsItem);
16708 Show.MovieTeaser = (function(superClass) {
16709 extend(MovieTeaser, superClass);
16711 function MovieTeaser() {
16712 return MovieTeaser.__super__.constructor.apply(this, arguments);
16715 MovieTeaser.prototype.tagName = "div";
16717 MovieTeaser.prototype.triggers = {
16718 'click .play': 'movie:play'
16721 MovieTeaser.prototype.initialize = function() {
16722 return this.model.set({
16723 actions: {
16724 thumbs: tr('Thumbs up')
16729 MovieTeaser.prototype.attributes = function() {
16730 return this.watchedAttributes('card-detail');
16733 return MovieTeaser;
16735 })(App.Views.CardView);
16736 Show.Content = (function(superClass) {
16737 extend(Content, superClass);
16739 function Content() {
16740 return Content.__super__.constructor.apply(this, arguments);
16743 Content.prototype.template = 'apps/movie/show/content';
16745 Content.prototype.className = "movie-content content-sections";
16747 Content.prototype.triggers = {
16748 'click .youtube': 'movie:youtube'
16751 Content.prototype.regions = {
16752 regionCast: '.region-cast',
16753 regionMore1: '.region-more-1',
16754 regionMore2: '.region-more-2',
16755 regionMore3: '.region-more-3',
16756 regionMore4: '.region-more-4',
16757 regionMore5: '.region-more-5'
16760 Content.prototype.modelEvents = {
16761 'change': 'modelChange'
16764 Content.prototype.modelChange = function() {
16765 this.render();
16766 return this.trigger('show');
16769 return Content;
16771 })(App.Views.LayoutView);
16772 return Show.Set = (function(superClass) {
16773 extend(Set, superClass);
16775 function Set() {
16776 return Set.__super__.constructor.apply(this, arguments);
16779 Set.prototype.template = 'apps/movie/show/set';
16781 Set.prototype.className = 'movie-set';
16783 Set.prototype.onRender = function() {
16784 if (this.options && this.options.set) {
16785 return $('h2.set-name', this.$el).html(this.options.set);
16789 Set.prototype.regions = function() {
16790 return {
16791 regionCollection: '.collection-items'
16795 return Set;
16797 })(App.Views.LayoutView);
16800 this.Kodi.module("MusicVideoApp.Edit", function(Edit, App, Backbone, Marionette, $, _) {
16801 return Edit.Controller = (function(superClass) {
16802 extend(Controller, superClass);
16804 function Controller() {
16805 return Controller.__super__.constructor.apply(this, arguments);
16808 Controller.prototype.initialize = function() {
16809 var form, options;
16810 this.model = this.getOption('model');
16811 options = {
16812 titleHtml: '<span>' + tr('Edit') + '</span>' + this.model.escape('title'),
16813 form: this.getStructure(),
16814 formState: this.model.attributes,
16815 config: {
16816 attributes: {
16817 "class": 'edit-form'
16819 editForm: true,
16820 tabs: true,
16821 callback: (function(_this) {
16822 return function(data, formView) {
16823 return _this.saveCallback(data, formView);
16825 })(this)
16828 return form = App.request("form:popup:wrapper", options);
16831 Controller.prototype.getStructure = function() {
16832 return [
16834 title: 'General',
16835 id: 'general',
16836 children: [
16838 id: 'title',
16839 title: tr('Title'),
16840 type: 'textfield'
16841 }, {
16842 id: 'artist',
16843 title: tr('Artist'),
16844 type: 'textfield',
16845 format: 'array.string'
16846 }, {
16847 id: 'album',
16848 title: tr('Album'),
16849 type: 'textfield'
16850 }, {
16851 id: 'plot',
16852 title: tr('Plot'),
16853 type: 'textarea'
16854 }, {
16855 id: 'studio',
16856 title: tr('Studio'),
16857 type: 'textfield',
16858 format: 'array.string'
16859 }, {
16860 id: 'year',
16861 title: tr('Year'),
16862 type: 'number',
16863 format: 'integer',
16864 attributes: {
16865 "class": 'half-width',
16866 step: 1,
16867 min: 0,
16868 max: 9999
16870 }, {
16871 id: 'rating',
16872 title: tr('Rating'),
16873 type: 'number',
16874 format: 'float',
16875 attributes: {
16876 "class": 'half-width',
16877 step: 0.1,
16878 min: 0,
16879 max: 10
16881 suffix: '<div class="clearfix"></div>'
16884 }, {
16885 title: 'Tags',
16886 id: 'tags',
16887 children: [
16889 id: 'director',
16890 title: tr('Directors'),
16891 type: 'textfield',
16892 format: 'array.string'
16893 }, {
16894 id: 'genre',
16895 title: tr('Genres'),
16896 type: 'textfield',
16897 format: 'array.string'
16898 }, {
16899 id: 'tag',
16900 title: tr('Tags'),
16901 type: 'textfield',
16902 format: 'array.string'
16905 }, {
16906 title: 'Poster',
16907 id: 'poster',
16908 children: [
16910 id: 'thumbnail',
16911 title: tr('URL'),
16912 type: 'imageselect',
16913 valueProperty: 'thumbnailOriginal',
16914 description: tr('Add an image via an external URL'),
16915 attributes: {
16916 "class": 'fanart-size'
16918 metadataImageHandler: 'fanarttv:artist:image:entities',
16919 metadataLookupField: 'artist'
16922 }, {
16923 title: 'Background',
16924 id: 'background',
16925 children: [
16927 id: 'fanart',
16928 title: tr('URL'),
16929 type: 'imageselect',
16930 valueProperty: 'fanartOriginal',
16931 description: tr('Add an image via an external URL'),
16932 metadataImageHandler: 'fanarttv:artist:image:entities',
16933 metadataLookupField: 'artist'
16936 }, {
16937 title: 'Information',
16938 id: 'info',
16939 children: [
16941 id: 'file',
16942 title: tr('File path'),
16943 type: 'textarea',
16944 attributes: {
16945 disabled: 'disabled',
16946 cols: 5
16948 format: 'prevent.submit'
16955 Controller.prototype.saveCallback = function(data, formView) {
16956 var controller;
16957 controller = App.request("command:kodi:controller", 'audio', 'VideoLibrary');
16958 return controller.setMusicVideoDetails(this.model.get('id'), data, (function(_this) {
16959 return function() {
16960 Kodi.vent.trigger('entity:kodi:update', _this.model.get('uid'));
16961 return Kodi.execute("notification:show", t.sprintf(tr("Updated %1$s details"), 'album'));
16963 })(this));
16966 return Controller;
16968 })(App.Controllers.Base);
16971 this.Kodi.module("MusicVideoApp.List", function(List, App, Backbone, Marionette, $, _) {
16972 var API;
16973 API = {
16974 bindTriggers: function(view) {
16975 App.listenTo(view, 'childview:musicvideo:play', function(parent, viewItem) {
16976 return App.execute('musicvideo:action', 'play', viewItem);
16978 App.listenTo(view, 'childview:musicvideo:add', function(parent, viewItem) {
16979 return App.execute('musicvideo:action', 'add', viewItem);
16981 App.listenTo(view, 'childview:musicvideo:localplay', function(parent, viewItem) {
16982 return App.execute('musicvideo:action', 'localplay', viewItem);
16984 App.listenTo(view, 'childview:musicvideo:download', function(parent, viewItem) {
16985 return App.execute('musicvideo:action', 'download', viewItem);
16987 App.listenTo(view, 'childview:musicvideo:edit', function(parent, viewItem) {
16988 return App.execute('musicvideo:edit', viewItem.model);
16990 return view;
16992 getVideoList: function(collection) {
16993 var view;
16994 view = new List.Videos({
16995 collection: collection
16997 API.bindTriggers(view);
16998 return view;
17001 List.Controller = (function(superClass) {
17002 extend(Controller, superClass);
17004 function Controller() {
17005 return Controller.__super__.constructor.apply(this, arguments);
17008 Controller.prototype.initialize = function() {
17009 var collection;
17010 collection = App.request("musicvideo:entities");
17011 return App.execute("when:entity:fetched", collection, (function(_this) {
17012 return function() {
17013 collection.availableFilters = _this.getAvailableFilters();
17014 collection.sectionId = 'music';
17015 App.request('filter:init', _this.getAvailableFilters());
17016 _this.layout = _this.getLayoutView(collection);
17017 _this.listenTo(_this.layout, "show", function() {
17018 _this.renderList(collection);
17019 return _this.getFiltersView(collection);
17021 return App.regionContent.show(_this.layout);
17023 })(this));
17026 Controller.prototype.getLayoutView = function(collection) {
17027 return new List.ListLayout({
17028 collection: collection
17032 Controller.prototype.getAvailableFilters = function() {
17033 return {
17034 sort: ['label', 'year', 'artist', 'album'],
17035 filter: ['studio', 'director', 'artist', 'album', 'year']
17039 Controller.prototype.getFiltersView = function(collection) {
17040 var filters;
17041 filters = App.request('filter:show', collection);
17042 this.layout.regionSidebarFirst.show(filters);
17043 return this.listenTo(filters, "filter:changed", (function(_this) {
17044 return function() {
17045 return _this.renderList(collection);
17047 })(this));
17050 Controller.prototype.renderList = function(collection) {
17051 var filteredCollection, view;
17052 App.execute("loading:show:view", this.layout.regionContent);
17053 filteredCollection = App.request('filter:apply:entities', collection);
17054 view = API.getVideoList(filteredCollection);
17055 return this.layout.regionContent.show(view);
17058 return Controller;
17060 })(App.Controllers.Base);
17061 return App.reqres.setHandler("musicvideo:list:view", function(collection) {
17062 return API.getVideoList(collection);
17066 this.Kodi.module("MusicVideoApp.List", function(List, App, Backbone, Marionette, $, _) {
17067 List.ListLayout = (function(superClass) {
17068 extend(ListLayout, superClass);
17070 function ListLayout() {
17071 return ListLayout.__super__.constructor.apply(this, arguments);
17074 ListLayout.prototype.className = "musicvideo-list with-filters";
17076 return ListLayout;
17078 })(App.Views.LayoutWithSidebarFirstView);
17079 List.Teaser = (function(superClass) {
17080 extend(Teaser, superClass);
17082 function Teaser() {
17083 return Teaser.__super__.constructor.apply(this, arguments);
17086 Teaser.prototype.triggers = {
17087 "click .play": "musicvideo:play",
17088 "click .add": "musicvideo:add",
17089 'click .stream': 'musicvideo:localplay',
17090 'click .download': 'musicvideo:download',
17091 'click .edit': 'musicvideo:edit',
17092 'click .refresh': 'musicvideo:refresh'
17095 Teaser.prototype.initialize = function() {
17096 Teaser.__super__.initialize.apply(this, arguments);
17097 if (this.model != null) {
17098 this.setMeta();
17099 return this.model.set(App.request('musicvideo:action:items'));
17103 Teaser.prototype.setMeta = function() {
17104 var artist;
17105 if (this.model) {
17106 artist = this.model.get('artist') !== '' ? this.model.get('artist') : '&nbsp;';
17107 return this.model.set({
17108 subtitleHtml: this.themeLink(this.model.get('artist'), 'search/artist/' + artist)
17113 return Teaser;
17115 })(App.Views.CardView);
17116 List.Empty = (function(superClass) {
17117 extend(Empty, superClass);
17119 function Empty() {
17120 return Empty.__super__.constructor.apply(this, arguments);
17123 Empty.prototype.tagName = "li";
17125 Empty.prototype.className = "musicvideo-empty-result";
17127 return Empty;
17129 })(App.Views.EmptyViewResults);
17130 return List.Videos = (function(superClass) {
17131 extend(Videos, superClass);
17133 function Videos() {
17134 return Videos.__super__.constructor.apply(this, arguments);
17137 Videos.prototype.childView = List.Teaser;
17139 Videos.prototype.emptyView = List.Empty;
17141 Videos.prototype.tagName = "ul";
17143 Videos.prototype.className = "card-grid--musicvideo";
17145 return Videos;
17147 })(App.Views.VirtualListView);
17150 this.Kodi.module("MusicVideoApp", function(MusicVideoApp, App, Backbone, Marionette, $, _) {
17151 var API;
17152 MusicVideoApp.Router = (function(superClass) {
17153 extend(Router, superClass);
17155 function Router() {
17156 return Router.__super__.constructor.apply(this, arguments);
17159 Router.prototype.appRoutes = {
17160 "music/videos": "list",
17161 "music/video/:id": "view"
17164 return Router;
17166 })(App.Router.Base);
17167 API = {
17168 list: function() {
17169 return new MusicVideoApp.List.Controller();
17171 view: function(id) {
17172 return new MusicVideoApp.Show.Controller({
17173 id: id
17176 action: function(op, view) {
17177 var files, model, playlist, videoLib;
17178 model = view.model;
17179 playlist = App.request("command:kodi:controller", 'video', 'PlayList');
17180 files = App.request("command:kodi:controller", 'video', 'Files');
17181 videoLib = App.request("command:kodi:controller", 'video', 'VideoLibrary');
17182 switch (op) {
17183 case 'play':
17184 return App.execute("input:resume", model, 'musicvideoid');
17185 case 'add':
17186 return playlist.add('musicvideoid', model.get('musicvideoid'));
17187 case 'localplay':
17188 return files.videoStream(model.get('file'), model.get('fanart'));
17189 case 'download':
17190 return files.downloadFile(model.get('file'));
17191 case 'refresh':
17192 return helpers.entities.refreshEntity(model, videoLib, 'refreshMusicVideo');
17196 App.on("before:start", function() {
17197 return new MusicVideoApp.Router({
17198 controller: API
17201 App.commands.setHandler('musicvideo:action', function(op, model) {
17202 return API.action(op, model);
17204 App.reqres.setHandler('musicvideo:action:items', function() {
17205 return {
17206 actions: {
17207 thumbs: tr('Thumbs up')
17209 menu: {
17210 'add': tr('Queue in Kodi'),
17211 'divider-1': '',
17212 'download': tr('Download'),
17213 'stream': tr('Play in browser'),
17214 'divider-2': '',
17215 'edit': tr('Edit')
17219 return App.commands.setHandler('musicvideo:edit', function(model) {
17220 var loadedModel;
17221 loadedModel = App.request("musicvideo:entity", model.get('id'));
17222 return App.execute("when:entity:fetched", loadedModel, (function(_this) {
17223 return function() {
17224 return new MusicVideoApp.Edit.Controller({
17225 model: loadedModel
17228 })(this));
17232 this.Kodi.module("MusicVideoApp.Show", function(Show, App, Backbone, Marionette, $, _) {
17233 var API;
17234 API = {
17235 bindTriggers: function(view) {
17236 App.listenTo(view, 'musicvideo:play', function(viewItem) {
17237 return App.execute('musicvideo:action', 'play', viewItem);
17239 App.listenTo(view, 'musicvideo:add', function(viewItem) {
17240 return App.execute('musicvideo:action', 'add', viewItem);
17242 App.listenTo(view, 'musicvideo:localplay', function(viewItem) {
17243 return App.execute('musicvideo:action', 'localplay', viewItem);
17245 App.listenTo(view, 'musicvideo:download', function(viewItem) {
17246 return App.execute('musicvideo:action', 'download', viewItem);
17248 App.listenTo(view, 'musicvideo:edit', function(viewItem) {
17249 return App.execute('musicvideo:edit', viewItem.model);
17251 return view;
17254 return Show.Controller = (function(superClass) {
17255 extend(Controller, superClass);
17257 function Controller() {
17258 return Controller.__super__.constructor.apply(this, arguments);
17261 Controller.prototype.initialize = function(options) {
17262 var id, musicvideo;
17263 id = parseInt(options.id);
17264 musicvideo = App.request("musicvideo:entity", id);
17265 return App.execute("when:entity:fetched", musicvideo, (function(_this) {
17266 return function() {
17267 _this.layout = _this.getLayoutView(musicvideo);
17268 _this.listenTo(_this.layout, "show", function() {
17269 _this.getDetailsLayoutView(musicvideo);
17270 return _this.getRelatedVideos(musicvideo);
17272 return App.regionContent.show(_this.layout);
17274 })(this));
17277 Controller.prototype.getLayoutView = function(musicvideo) {
17278 return new Show.PageLayout({
17279 model: musicvideo
17283 Controller.prototype.getDetailsLayoutView = function(musicvideo) {
17284 var headerLayout;
17285 headerLayout = new Show.HeaderLayout({
17286 model: musicvideo
17288 this.listenTo(headerLayout, "show", (function(_this) {
17289 return function() {
17290 var detail, teaser;
17291 teaser = new Show.DetailTeaser({
17292 model: musicvideo
17294 API.bindTriggers(teaser);
17295 detail = new Show.Details({
17296 model: musicvideo
17298 _this.listenTo(detail, "show", function() {
17299 return API.bindTriggers(detail);
17301 headerLayout.regionSide.show(teaser);
17302 return headerLayout.regionMeta.show(detail);
17304 })(this));
17305 return this.layout.regionHeader.show(headerLayout);
17308 Controller.prototype.getRelatedVideos = function(musicvideo) {
17309 var opts, title;
17310 title = tr('Related music videos from YouTube');
17311 opts = {
17312 maxResults: 8
17314 return App.execute('youtube:list:view', musicvideo.get('title') + ' ' + musicvideo.get('artist'), title, opts, (function(_this) {
17315 return function(view) {
17316 return _this.layout.regionContent.show(view);
17318 })(this));
17321 return Controller;
17323 })(App.Controllers.Base);
17326 this.Kodi.module("MusicVideoApp.Show", function(Show, App, Backbone, Marionette, $, _) {
17327 Show.PageLayout = (function(superClass) {
17328 extend(PageLayout, superClass);
17330 function PageLayout() {
17331 return PageLayout.__super__.constructor.apply(this, arguments);
17334 PageLayout.prototype.className = 'musicvideo-show detail-container';
17336 return PageLayout;
17338 })(App.Views.LayoutWithHeaderView);
17339 Show.HeaderLayout = (function(superClass) {
17340 extend(HeaderLayout, superClass);
17342 function HeaderLayout() {
17343 return HeaderLayout.__super__.constructor.apply(this, arguments);
17346 HeaderLayout.prototype.className = 'musicvideo-details';
17348 return HeaderLayout;
17350 })(App.Views.LayoutDetailsHeaderView);
17351 Show.Details = (function(superClass) {
17352 extend(Details, superClass);
17354 function Details() {
17355 return Details.__super__.constructor.apply(this, arguments);
17358 Details.prototype.template = 'apps/musicvideo/show/details_meta';
17360 Details.prototype.triggers = {
17361 "click .play": "musicvideo:play",
17362 "click .add": "musicvideo:add",
17363 "click .download": "musicvideo:download",
17364 "click .localplay": "musicvideo:localplay",
17365 "click .edit": "musicvideo:edit"
17368 return Details;
17370 })(App.Views.DetailsItem);
17371 return Show.DetailTeaser = (function(superClass) {
17372 extend(DetailTeaser, superClass);
17374 function DetailTeaser() {
17375 return DetailTeaser.__super__.constructor.apply(this, arguments);
17378 DetailTeaser.prototype.attributes = function() {
17379 return this.watchedAttributes('card-detail');
17382 return DetailTeaser;
17384 })(App.MusicVideoApp.List.Teaser);
17387 this.Kodi.module("NavMain", function(NavMain, App, Backbone, Marionette, $, _) {
17388 var API;
17389 API = {
17390 getNav: function() {
17391 var navStructure;
17392 navStructure = App.request('navMain:entities');
17393 return new NavMain.List({
17394 collection: navStructure
17397 getNavChildren: function(path, title) {
17398 var navStructure;
17399 if (title == null) {
17400 title = 'default';
17402 navStructure = App.request('navMain:entities', path);
17403 if (title !== 'default') {
17404 navStructure.set({
17405 title: tr(title)
17408 return new NavMain.ItemList({
17409 model: navStructure
17412 getNavCollection: function(collection, title) {
17413 var navStructure;
17414 navStructure = new App.Entities.NavMain({
17415 title: title,
17416 items: collection
17418 return new NavMain.ItemList({
17419 model: navStructure
17423 this.onStart = function(options) {
17424 return App.vent.on("shell:ready", (function(_this) {
17425 return function(options) {
17426 var nav;
17427 nav = API.getNav();
17428 return App.regionNav.show(nav);
17430 })(this));
17432 App.reqres.setHandler("navMain:children:show", function(path, title) {
17433 if (title == null) {
17434 title = 'default';
17436 return API.getNavChildren(path, title);
17438 App.reqres.setHandler("navMain:collection:show", function(collection, title) {
17439 if (title == null) {
17440 title = '';
17442 return API.getNavCollection(collection, title);
17444 return App.vent.on("navMain:refresh", function() {
17445 var nav;
17446 nav = API.getNav();
17447 return App.regionNav.show(nav);
17451 this.Kodi.module("NavMain", function(NavMain, App, Backbone, Marionette, $, _) {
17452 NavMain.List = (function(superClass) {
17453 extend(List, superClass);
17455 function List() {
17456 return List.__super__.constructor.apply(this, arguments);
17459 List.prototype.template = "apps/navMain/show/navMain";
17461 return List;
17463 })(Backbone.Marionette.ItemView);
17464 NavMain.Item = (function(superClass) {
17465 extend(Item, superClass);
17467 function Item() {
17468 return Item.__super__.constructor.apply(this, arguments);
17471 Item.prototype.template = "apps/navMain/show/nav_item";
17473 Item.prototype.tagName = "li";
17475 Item.prototype.initialize = function() {
17476 var classes, tag;
17477 classes = [];
17478 if (this.model.get('path') === helpers.url.path()) {
17479 classes.push('active');
17481 tag = this.themeLink(this.model.get('title'), this.model.get('path'), {
17482 'className': classes.join(' ')
17484 return this.model.set({
17485 link: tag
17489 return Item;
17491 })(Backbone.Marionette.ItemView);
17492 return NavMain.ItemList = (function(superClass) {
17493 extend(ItemList, superClass);
17495 function ItemList() {
17496 return ItemList.__super__.constructor.apply(this, arguments);
17499 ItemList.prototype.template = 'apps/navMain/show/nav_sub';
17501 ItemList.prototype.childView = NavMain.Item;
17503 ItemList.prototype.tagName = "div";
17505 ItemList.prototype.childViewContainer = 'ul.items';
17507 ItemList.prototype.className = "nav-sub";
17509 ItemList.prototype.initialize = function() {
17510 return this.collection = this.model.get('items');
17513 return ItemList;
17515 })(App.Views.CompositeView);
17518 this.Kodi.module("NotificationsApp", function(NotificationApp, App, Backbone, Marionette, $, _) {
17519 var API;
17520 API = {
17521 notificationMinTimeOut: 5000
17523 return App.commands.setHandler("notification:show", function(msg, severity) {
17524 var timeout;
17525 if (severity == null) {
17526 severity = 'normal';
17528 timeout = msg.length < 50 ? API.notificationMinTimeOut : msg.length * 100;
17529 return $.snackbar({
17530 content: msg,
17531 style: 'type-' + severity,
17532 timeout: timeout
17537 this.Kodi.module("PlayerApp", function(PlayerApp, App, Backbone, Marionette, $, _) {
17538 var API;
17539 API = {
17540 getPlayer: function(player) {
17541 return new PlayerApp.Show.Player({
17542 player: player
17545 doCommand: function(player, command, params, callback) {
17546 return App.request("command:" + player + ":player", command, params, (function(_this) {
17547 return function() {
17548 return _this.pollingUpdate(callback);
17550 })(this));
17552 getAppController: function(player) {
17553 return App.request("command:" + player + ":controller", 'auto', 'Application');
17555 pollingUpdate: function(callback) {
17556 var stateObj;
17557 stateObj = App.request("state:current");
17558 if (stateObj.getPlayer() === 'kodi') {
17559 if (!App.request('sockets:active')) {
17560 return App.request('state:kodi:update', callback);
17562 } else {
17566 initPlayer: function(player, playerView) {
17567 var $playerCtx, $progress, $volume, appController;
17568 this.initProgress(player);
17569 this.initVolume(player);
17570 App.vent.trigger("state:player:updated", player);
17571 appController = this.getAppController(player);
17572 App.vent.on("state:initialized", (function(_this) {
17573 return function() {
17574 var stateObj;
17575 stateObj = App.request("state:kodi");
17576 if (stateObj.isPlaying()) {
17577 _this.timerStop();
17578 return _this.timerStart();
17581 })(this));
17582 App.listenTo(playerView, "control:play", (function(_this) {
17583 return function() {
17584 return _this.doCommand(player, 'PlayPause', 'toggle');
17586 })(this));
17587 App.listenTo(playerView, "control:prev", (function(_this) {
17588 return function() {
17589 return _this.doCommand(player, 'GoTo', 'previous');
17591 })(this));
17592 App.listenTo(playerView, "control:next", (function(_this) {
17593 return function() {
17594 return _this.doCommand(player, 'GoTo', 'next');
17596 })(this));
17597 App.listenTo(playerView, "control:repeat", (function(_this) {
17598 return function() {
17599 return _this.doCommand(player, 'SetRepeat', 'cycle');
17601 })(this));
17602 App.listenTo(playerView, "control:shuffle", (function(_this) {
17603 return function() {
17604 return _this.doCommand(player, 'SetShuffle', 'toggle');
17606 })(this));
17607 App.listenTo(playerView, "control:mute", (function(_this) {
17608 return function() {
17609 return appController.toggleMute(function() {
17610 return _this.pollingUpdate();
17613 })(this));
17614 App.listenTo(playerView, 'control:menu', function() {
17615 return App.execute("ui:playermenu", 'toggle');
17617 if (player === 'kodi') {
17618 App.listenTo(playerView, "remote:toggle", (function(_this) {
17619 return function() {
17620 return App.execute("input:remote:toggle");
17622 })(this));
17624 $playerCtx = $('#player-' + player);
17625 $progress = $('.playing-progress', $playerCtx);
17626 if (player === 'kodi') {
17627 $progress.on('change', function() {
17628 API.timerStop();
17629 return API.doCommand(player, 'Seek', {
17630 percentage: Math.round(this.vGet())
17631 }, function() {
17632 return API.timerStart();
17635 $progress.on('slide', function() {
17636 return API.timerStop();
17638 } else {
17639 $progress.on('change', function() {
17640 return API.doCommand(player, 'Seek', {
17641 percentage: Math.round(this.vGet())
17645 $volume = $('.volume', $playerCtx);
17646 return $volume.on('change', function() {
17647 return appController.setVolume(Math.round(this.vGet()), function() {
17648 return API.pollingUpdate();
17652 timerStart: function() {
17653 return App.playingTimerInterval = setTimeout(((function(_this) {
17654 return function() {
17655 return _this.timerUpdate();
17657 })(this)), 1000);
17659 timerStop: function() {
17660 return clearTimeout(App.playingTimerInterval);
17662 timerUpdate: function() {
17663 var cur, curTimeObj, dur, percent, stateObj;
17664 stateObj = App.request("state:kodi");
17665 this.timerStop();
17666 if (stateObj.isPlaying() && (stateObj.getPlaying('time') != null)) {
17667 cur = helpers.global.timeToSec(stateObj.getPlaying('time')) + 1;
17668 dur = helpers.global.timeToSec(stateObj.getPlaying('totaltime'));
17669 percent = Math.ceil(cur / dur * 100);
17670 curTimeObj = helpers.global.secToTime(cur);
17671 stateObj.setPlaying('time', curTimeObj);
17672 this.setProgress('kodi', percent, curTimeObj);
17673 return this.timerStart();
17676 setProgress: function(player, percent, currentTime) {
17677 var $cur, $playerCtx;
17678 if (percent == null) {
17679 percent = 0;
17681 if (currentTime == null) {
17682 currentTime = 0;
17684 $playerCtx = $('#player-' + player);
17685 $('.playing-progress', $playerCtx).val(percent);
17686 $cur = $('.playing-time-current', $playerCtx);
17687 return $cur.text(helpers.global.formatTime(currentTime));
17689 initProgress: function(player, percent) {
17690 var $playerCtx;
17691 if (percent == null) {
17692 percent = 0;
17694 $playerCtx = $('#player-' + player);
17695 return $('.playing-progress', $playerCtx).noUiSlider({
17696 start: percent,
17697 connect: 'upper',
17698 step: 1,
17699 range: {
17700 min: 0,
17701 max: 100
17705 initVolume: function(player, percent) {
17706 var $playerCtx;
17707 if (percent == null) {
17708 percent = 50;
17710 $playerCtx = $('#player-' + player);
17711 return $('.volume', $playerCtx).noUiSlider({
17712 start: percent,
17713 connect: 'upper',
17714 step: 1,
17715 range: {
17716 min: 0,
17717 max: 100
17722 return this.onStart = function(options) {
17723 App.vent.on("shell:ready", (function(_this) {
17724 return function(options) {
17725 App.kodiPlayer = API.getPlayer('kodi');
17726 App.listenTo(App.kodiPlayer, "show", function() {
17727 API.initPlayer('kodi', App.kodiPlayer);
17728 return App.execute("player:kodi:timer", 'start');
17730 App.regionPlayerKodi.show(App.kodiPlayer);
17731 App.localPlayer = API.getPlayer('local');
17732 App.listenTo(App.localPlayer, "show", function() {
17733 return API.initPlayer('local', App.localPlayer);
17735 return App.regionPlayerLocal.show(App.localPlayer);
17737 })(this));
17738 App.commands.setHandler('player:kodi:timer', function(state) {
17739 if (state == null) {
17740 state = 'start';
17742 if (state === 'start') {
17743 return API.timerStart();
17744 } else if (state === 'stop') {
17745 return API.timerStop();
17746 } else if (state === 'update') {
17747 return API.timerUpdate();
17750 App.commands.setHandler('player:local:progress:update', function(percent, currentTime) {
17751 return API.setProgress('local', percent, currentTime);
17753 return App.commands.setHandler('player:kodi:progress:update', function(percent, callback) {
17754 return API.doCommand('kodi', 'Seek', {
17755 percentage: percent
17756 }, callback);
17761 this.Kodi.module("PlayerApp.Show", function(Show, App, Backbone, Marionette, $, _) {
17762 return Show.Player = (function(superClass) {
17763 extend(Player, superClass);
17765 function Player() {
17766 return Player.__super__.constructor.apply(this, arguments);
17769 Player.prototype.template = "apps/player/show/player";
17771 Player.prototype.regions = {
17772 regionProgress: '.playing-progress',
17773 regionVolume: '.volume',
17774 regionThumbnail: '.playing-thumb',
17775 regionTitle: '.playing-title',
17776 regionSubtitle: '.playing-subtitle',
17777 regionTimeCur: '.playing-time-current',
17778 regionTimeDur: '.playing-time-duration'
17781 Player.prototype.triggers = {
17782 'click .remote-toggle': 'remote:toggle',
17783 'click .control-prev': 'control:prev',
17784 'click .control-play': 'control:play',
17785 'click .control-next': 'control:next',
17786 'click .control-stop': 'control:stop',
17787 'click .control-mute': 'control:mute',
17788 'click .control-shuffle': 'control:shuffle',
17789 'click .control-repeat': 'control:repeat',
17790 'click .control-menu': 'control:menu'
17793 return Player;
17795 })(App.Views.ItemView);
17798 this.Kodi.module("PlaylistApp.List", function(List, App, Backbone, Marionette, $, _) {
17799 return List.Controller = (function(superClass) {
17800 extend(Controller, superClass);
17802 function Controller() {
17803 return Controller.__super__.constructor.apply(this, arguments);
17806 Controller.prototype.initialize = function() {
17807 return App.vent.on("shell:ready", (function(_this) {
17808 return function(options) {
17809 return _this.getPlaylistBar();
17811 })(this));
17814 Controller.prototype.playlistController = function(player, media) {
17815 return App.request("command:" + player + ":controller", media, 'PlayList');
17818 Controller.prototype.playerController = function(player, media) {
17819 return App.request("command:" + player + ":controller", media, 'Player');
17822 Controller.prototype.playerCommand = function(player, command, params) {
17823 if (params == null) {
17824 params = [];
17826 return App.request("command:" + player + ":player", command, params, function() {
17827 return App.request("state:kodi:update");
17831 Controller.prototype.stateObj = function() {
17832 return App.request("state:current");
17835 Controller.prototype.getPlaylistBar = function() {
17836 this.layout = this.getLayout();
17837 this.listenTo(this.layout, "show", (function(_this) {
17838 return function() {
17839 _this.renderList('kodi', 'audio');
17840 _this.renderList('local', 'audio');
17841 return App.vent.on("state:initialized", function() {
17842 return _this.changePlaylist(_this.stateObj().getState('player'), _this.stateObj().getState('media'));
17845 })(this));
17846 this.listenTo(this.layout, 'playlist:kodi:audio', (function(_this) {
17847 return function() {
17848 return _this.changePlaylist('kodi', 'audio');
17850 })(this));
17851 this.listenTo(this.layout, 'playlist:kodi:video', (function(_this) {
17852 return function() {
17853 return _this.changePlaylist('kodi', 'video');
17855 })(this));
17856 this.listenTo(this.layout, 'playlist:kodi', (function(_this) {
17857 return function() {
17858 _this.stateObj().setPlayer('kodi');
17859 return _this.renderList('kodi', 'audio');
17861 })(this));
17862 this.listenTo(this.layout, 'playlist:local', (function(_this) {
17863 return function() {
17864 _this.stateObj().setPlayer('local');
17865 return _this.renderList('local', 'audio');
17867 })(this));
17868 this.listenTo(this.layout, 'playlist:clear', (function(_this) {
17869 return function() {
17870 return _this.playlistController(_this.stateObj().getPlayer(), _this.stateObj().getState('media')).clear();
17872 })(this));
17873 this.listenTo(this.layout, 'playlist:refresh', (function(_this) {
17874 return function() {
17875 _this.renderList(_this.stateObj().getPlayer(), _this.stateObj().getState('media'));
17876 return App.execute("notification:show", tr('Playlist refreshed'));
17878 })(this));
17879 this.listenTo(this.layout, 'playlist:party', (function(_this) {
17880 return function() {
17881 return _this.playerController(_this.stateObj().getPlayer(), _this.stateObj().getState('media')).setPartyMode('toggle', function(resp) {
17882 App.request("state:" + _this.stateObj().getPlayer() + ":update");
17883 return App.execute("notification:show", t.sprintf(tr('%1$s party mode toggled'), _this.stateObj().getPlayer()));
17886 })(this));
17887 this.listenTo(this.layout, 'playlist:save', (function(_this) {
17888 return function() {
17889 return App.execute("localplaylist:addentity", 'playlist');
17891 })(this));
17892 return App.regionPlaylist.show(this.layout);
17895 Controller.prototype.getLayout = function() {
17896 return new List.Layout();
17899 Controller.prototype.getList = function(collection) {
17900 return new List.Items({
17901 collection: collection
17905 Controller.prototype.renderList = function(type, media) {
17906 var collection, listView;
17907 this.layout.$el.removeClassStartsWith('media-').addClass('media-' + media);
17908 if (type === 'kodi') {
17909 collection = App.request("playlist:list", type, media);
17910 return App.execute("when:entity:fetched", collection, (function(_this) {
17911 return function() {
17912 var listView;
17913 listView = _this.getList(collection);
17914 App.listenTo(listView, "show", function() {
17915 _this.bindActions(listView, type, media);
17916 return App.vent.trigger("state:content:updated", type, media);
17918 return _this.layout.kodiPlayList.show(listView);
17920 })(this));
17921 } else {
17922 collection = App.request("localplayer:get:entities");
17923 listView = this.getList(collection);
17924 App.listenTo(listView, "show", (function(_this) {
17925 return function() {
17926 _this.bindActions(listView, type, media);
17927 return App.vent.trigger("state:content:updated", type, media);
17929 })(this));
17930 return this.layout.localPlayList.show(listView);
17934 Controller.prototype.bindActions = function(listView, type, media) {
17935 var playlist;
17936 playlist = this.playlistController(type, media);
17937 this.listenTo(listView, "childview:playlist:item:remove", function(playlistView, item) {
17938 return playlist.remove(item.model.get('position'));
17940 this.listenTo(listView, "childview:playlist:item:play", function(playlistView, item) {
17941 return playlist.playEntity('position', parseInt(item.model.get('position')));
17943 return this.initSortable(type, media);
17946 Controller.prototype.changePlaylist = function(player, media) {
17947 return this.renderList(player, media);
17950 Controller.prototype.initSortable = function(type, media) {
17951 var $ctx, playlist;
17952 $ctx = $('.' + type + '-playlist');
17953 playlist = this.playlistController(type, media);
17954 return $('ul.playlist-items', $ctx).sortable({
17955 filter: '.row-playing,.row-paused',
17956 onEnd: function(e) {
17957 return playlist.moveItem($(e.item).data('type'), $(e.item).data('id'), e.oldIndex, e.newIndex);
17962 Controller.prototype.focusPlaying = function(type, media) {
17963 var $playing;
17964 if (config.getLocal('playlistFocusPlaying', true)) {
17965 $playing = $('.' + type + '-playlist .row-playing');
17966 if ($playing.length > 0) {
17967 return $playing.get(0).scrollIntoView();
17972 return Controller;
17974 })(App.Controllers.Base);
17977 this.Kodi.module("PlaylistApp.List", function(List, App, Backbone, Marionette, $, _) {
17978 List.Layout = (function(superClass) {
17979 extend(Layout, superClass);
17981 function Layout() {
17982 return Layout.__super__.constructor.apply(this, arguments);
17985 Layout.prototype.template = "apps/playlist/list/playlist_bar";
17987 Layout.prototype.tagName = "div";
17989 Layout.prototype.className = "playlist-bar";
17991 Layout.prototype.regions = {
17992 kodiPlayList: '.kodi-playlist',
17993 localPlayList: '.local-playlist'
17996 Layout.prototype.triggers = {
17997 'click .kodi-playlists .media-toggle .video': 'playlist:kodi:video',
17998 'click .kodi-playlists .media-toggle .audio': 'playlist:kodi:audio',
17999 'click .player-toggle .kodi': 'playlist:kodi',
18000 'click .player-toggle .local': 'playlist:local',
18001 'click .clear-playlist': 'playlist:clear',
18002 'click .refresh-playlist': 'playlist:refresh',
18003 'click .party-mode': 'playlist:party',
18004 'click .save-playlist': 'playlist:save'
18007 Layout.prototype.events = {
18008 'click .playlist-menu a': 'menuClick'
18011 Layout.prototype.menuClick = function(e) {
18012 return e.preventDefault();
18015 return Layout;
18017 })(App.Views.LayoutView);
18018 List.Item = (function(superClass) {
18019 extend(Item, superClass);
18021 function Item() {
18022 return Item.__super__.constructor.apply(this, arguments);
18025 Item.prototype.template = "apps/playlist/list/playlist_item";
18027 Item.prototype.tagName = "li";
18029 Item.prototype.initialize = function() {
18030 var subtitle;
18031 subtitle = '';
18032 switch (this.model.get('type')) {
18033 case 'song':
18034 subtitle = this.model.get('artist') ? this.model.get('artist').join(', ') : '';
18035 break;
18036 default:
18037 subtitle = '';
18039 return this.model.set({
18040 subtitle: subtitle
18044 Item.prototype.triggers = {
18045 "click .remove": "playlist:item:remove",
18046 "click .play": "playlist:item:play"
18049 Item.prototype.events = {
18050 "click .thumbs": "toggleThumbs"
18053 Item.prototype.attributes = function() {
18054 var classes;
18055 classes = ['item', 'pos-' + this.model.get('position'), 'plitem-' + this.model.get('type') + '-' + this.model.get('id')];
18056 if (this.model.get('canThumbsUp') && App.request('thumbsup:check', this.model)) {
18057 classes.push('thumbs-up');
18059 return {
18060 "class": classes.join(' '),
18061 'data-type': this.model.get('type'),
18062 'data-id': this.model.get('id'),
18063 'data-pos': this.model.get('position')
18067 Item.prototype.toggleThumbs = function() {
18068 App.request("thumbsup:toggle:entity", this.model);
18069 this.$el.toggleClass('thumbs-up');
18070 return $('.item-' + this.model.get('type') + '-' + this.model.get('id')).toggleClass('thumbs-up');
18073 return Item;
18075 })(App.Views.ItemView);
18076 return List.Items = (function(superClass) {
18077 extend(Items, superClass);
18079 function Items() {
18080 return Items.__super__.constructor.apply(this, arguments);
18083 Items.prototype.childView = List.Item;
18085 Items.prototype.tagName = "ul";
18087 Items.prototype.className = "playlist-items";
18089 return Items;
18091 })(App.Views.CollectionView);
18094 this.Kodi.module("PlaylistApp.LocalParty", function(LocalParty, App, Backbone, Marionette, $, _) {
18095 var API;
18096 API = {
18097 getController: function() {
18098 return new LocalParty.Manager();
18101 LocalParty.Manager = (function(superClass) {
18102 extend(Manager, superClass);
18104 function Manager() {
18105 return Manager.__super__.constructor.apply(this, arguments);
18108 Manager.prototype.initialize = function(options) {
18109 this.stateObj = App.request("state:local");
18110 return this.localPlaylist = App.request("command:local:controller", 'audio', 'PlayList');
18113 Manager.prototype.fillGlasses = function(callback) {
18114 this.stateObj.setPlaying('partymode', true);
18115 return this.getSongs(10, (function(_this) {
18116 return function(collection) {
18117 return _this.localPlaylist.clear(function() {
18118 _this.localPlaylist.playCollection(collection);
18119 if (callback) {
18120 return callback(true);
18124 })(this));
18127 Manager.prototype.topUpGlasses = function() {
18128 return this.getSongs(1, (function(_this) {
18129 return function(collection) {
18130 return _this.localPlaylist.remove(0, function() {
18131 return _this.localPlaylist.addCollection(collection);
18134 })(this));
18137 Manager.prototype.getSongs = function(limit, callback) {
18138 var options;
18139 options = {
18140 sort: {
18141 method: 'random',
18142 order: 'ascending'
18144 limit: {
18145 start: 0,
18146 end: limit
18148 cache: false,
18149 success: function(result) {
18150 return callback(result);
18153 return App.request("song:entities", options);
18156 Manager.prototype.leaveParty = function(callback) {
18157 this.stateObj.setPlaying('partymode', false);
18158 if (callback) {
18159 return callback(true);
18163 Manager.prototype.isPartyMode = function() {
18164 return this.stateObj.getPlaying('partymode', false);
18167 return Manager;
18169 })(App.Controllers.Base);
18170 App.commands.setHandler('playlist:local:partymode', function(op, callback) {
18171 var manager;
18172 if (op == null) {
18173 op = 'toggle';
18175 manager = API.getController();
18176 if (op === 'toggle') {
18177 op = !manager.isPartyMode();
18179 if (op === true) {
18180 manager.fillGlasses(callback);
18181 } else {
18182 manager.leaveParty(callback);
18184 return App.vent.trigger("state:local:changed");
18186 return App.vent.on("state:local:next", function() {
18187 var manager;
18188 manager = API.getController();
18189 if (manager.isPartyMode()) {
18190 return manager.topUpGlasses();
18195 this.Kodi.module("PlaylistApp.M3u", function(M3u, App, Backbone, Marionette, $, _) {
18196 return M3u.Controller = (function(superClass) {
18197 extend(Controller, superClass);
18199 function Controller() {
18200 return Controller.__super__.constructor.apply(this, arguments);
18203 Controller.prototype.initialize = function(options) {
18204 var List;
18205 List = this.getList(options.collection);
18206 return App.regionOffscreen.show(List);
18209 Controller.prototype.getList = function(collection) {
18210 return new M3u.List({
18211 collection: collection
18215 return Controller;
18217 })(App.Controllers.Base);
18220 this.Kodi.module("PlaylistApp.M3u", function(M3u, App, Backbone, Marionette, $, _) {
18221 return M3u.List = (function(superClass) {
18222 extend(List, superClass);
18224 function List() {
18225 return List.__super__.constructor.apply(this, arguments);
18228 List.prototype.template = 'apps/playlist/m3u/list';
18230 List.prototype.tagName = "pre";
18232 List.prototype.className = "m3u-export";
18234 List.prototype.onRender = function() {
18235 var content, filename;
18236 content = this.$el.text();
18237 filename = $('.local-playlist-header h2').html() + ".m3u";
18238 return helpers.global.saveFileText(content, filename);
18241 return List;
18243 })(App.Views.LayoutView);
18246 this.Kodi.module("PlaylistApp", function(PlaylistApp, App, Backbone, Marionette, $, _) {
18247 var API;
18248 PlaylistApp.Router = (function(superClass) {
18249 extend(Router, superClass);
18251 function Router() {
18252 return Router.__super__.constructor.apply(this, arguments);
18255 Router.prototype.appRoutes = {
18256 "playlist": "list"
18259 return Router;
18261 })(App.Router.Base);
18262 API = {
18263 list: function() {
18264 return new PlaylistApp.Show.Controller();
18266 "export": function(collection) {
18267 return new PlaylistApp.M3u.Controller({
18268 collection: collection
18271 type: 'kodi',
18272 media: 'audio',
18273 setContext: function(type, media) {
18274 if (type == null) {
18275 type = 'kodi';
18277 if (media == null) {
18278 media = 'audio';
18280 this.type = type;
18281 return this.media = media;
18283 getController: function() {
18284 return App.request("command:" + this.type + ":controller", this.media, 'PlayList');
18286 getPlaylistItems: function() {
18287 return App.request("playlist:" + this.type + ":entities", this.media);
18290 App.reqres.setHandler("playlist:list", function(type, media) {
18291 API.setContext(type, media);
18292 return API.getPlaylistItems();
18294 App.commands.setHandler("playlist:export", function(collection) {
18295 return API["export"](collection);
18297 App.on("before:start", function() {
18298 return new PlaylistApp.Router({
18299 controller: API
18302 return App.addInitializer(function() {
18303 var controller;
18304 controller = new PlaylistApp.List.Controller();
18305 App.commands.setHandler("playlist:refresh", function(type, media) {
18306 return controller.renderList(type, media);
18308 return App.vent.on("state:kodi:playing:updated", function(stateObj) {
18309 return controller.focusPlaying(stateObj.getState('player'), stateObj.getPlaying());
18314 this.Kodi.module("PlaylistApp.Show", function(Show, App, Backbone, Marionette, $, _) {
18315 return Show.Controller = (function(superClass) {
18316 extend(Controller, superClass);
18318 function Controller() {
18319 return Controller.__super__.constructor.apply(this, arguments);
18322 Controller.prototype.initialize = function(options) {
18323 this.landing = this.getLanding();
18324 return App.regionContent.show(this.landing);
18327 Controller.prototype.getLanding = function() {
18328 return new Show.Landing();
18331 return Controller;
18333 })(App.Controllers.Base);
18336 this.Kodi.module("PlaylistApp.Show", function(Show, App, Backbone, Marionette, $, _) {
18337 return Show.Landing = (function(superClass) {
18338 extend(Landing, superClass);
18340 function Landing() {
18341 return Landing.__super__.constructor.apply(this, arguments);
18344 Landing.prototype.template = 'apps/playlist/show/landing';
18346 return Landing;
18348 })(App.Views.ItemView);
18351 this.Kodi.module("PVR.ChannelList", function(List, App, Backbone, Marionette, $, _) {
18352 return List.Controller = (function(superClass) {
18353 extend(Controller, superClass);
18355 function Controller() {
18356 return Controller.__super__.constructor.apply(this, arguments);
18359 Controller.prototype.initialize = function(options) {
18360 var collection;
18361 collection = App.request("channel:entities", options.group);
18362 return App.execute("when:entity:fetched", collection, (function(_this) {
18363 return function() {
18364 _this.layout = _this.getLayoutView(collection);
18365 _this.listenTo(_this.layout, "show", function() {
18366 _this.renderChannels(collection);
18367 return _this.getSubNav();
18369 return App.regionContent.show(_this.layout);
18371 })(this));
18374 Controller.prototype.getLayoutView = function(collection) {
18375 return new List.Layout({
18376 collection: collection
18380 Controller.prototype.renderChannels = function(collection) {
18381 var view;
18382 view = new List.ChannelList({
18383 collection: collection
18385 this.listenTo(view, 'childview:channel:play', function(parent, child) {
18386 var player;
18387 player = App.request("command:kodi:controller", 'auto', 'Player');
18388 return player.playEntity('channelid', child.model.get('id'), {}, (function(_this) {
18389 return function() {};
18390 })(this));
18392 this.listenTo(view, 'childview:channel:record', function(parent, child) {
18393 var pvr;
18394 pvr = App.request("command:kodi:controller", 'auto', 'PVR');
18395 return pvr.setRecord(child.model.get('id'), {}, function() {
18396 return App.execute("notification:show", tr("Channel recording toggled"));
18399 return this.layout.regionContent.show(view);
18402 Controller.prototype.getSubNav = function() {
18403 var subNav;
18404 subNav = App.request("navMain:children:show", 'pvr/tv', 'PVR');
18405 return this.layout.regionSidebarFirst.show(subNav);
18408 return Controller;
18410 })(App.Controllers.Base);
18413 this.Kodi.module("PVR.ChannelList", function(List, App, Backbone, Marionette, $, _) {
18414 List.Layout = (function(superClass) {
18415 extend(Layout, superClass);
18417 function Layout() {
18418 return Layout.__super__.constructor.apply(this, arguments);
18421 Layout.prototype.className = "pvr-page";
18423 return Layout;
18425 })(App.Views.LayoutWithSidebarFirstView);
18426 List.ChannelTeaser = (function(superClass) {
18427 extend(ChannelTeaser, superClass);
18429 function ChannelTeaser() {
18430 return ChannelTeaser.__super__.constructor.apply(this, arguments);
18433 ChannelTeaser.prototype.tagName = "li";
18435 ChannelTeaser.prototype.triggers = {
18436 "click .play": "channel:play",
18437 "click .record": "channel:record"
18440 ChannelTeaser.prototype.initialize = function() {
18441 ChannelTeaser.__super__.initialize.apply(this, arguments);
18442 if (this.model != null) {
18443 return this.model.set({
18444 subtitle: this.model.get('broadcastnow').title
18449 return ChannelTeaser;
18451 })(App.Views.CardView);
18452 return List.ChannelList = (function(superClass) {
18453 extend(ChannelList, superClass);
18455 function ChannelList() {
18456 return ChannelList.__super__.constructor.apply(this, arguments);
18459 ChannelList.prototype.childView = List.ChannelTeaser;
18461 ChannelList.prototype.tagName = "ul";
18463 ChannelList.prototype.className = "card-grid--square";
18465 return ChannelList;
18467 })(App.Views.CollectionView);
18470 this.Kodi.module("PVR", function(PVR, App, Backbone, Marionette, $, _) {
18471 var API;
18472 PVR.Router = (function(superClass) {
18473 extend(Router, superClass);
18475 function Router() {
18476 return Router.__super__.constructor.apply(this, arguments);
18479 Router.prototype.appRoutes = {
18480 "pvr/tv": "tv",
18481 "pvr/radio": "radio",
18482 "pvr/recordings": "recordings"
18485 return Router;
18487 })(App.Router.Base);
18488 API = {
18489 tv: function() {
18490 return new PVR.ChannelList.Controller({
18491 group: 'alltv'
18494 radio: function() {
18495 return new PVR.ChannelList.Controller({
18496 group: 'allradio'
18499 recordings: function() {
18500 return new PVR.RecordingList.Controller();
18503 return App.on("before:start", function() {
18504 return new PVR.Router({
18505 controller: API
18510 this.Kodi.module("PVR.RecordingList", function(List, App, Backbone, Marionette, $, _) {
18511 return List.Controller = (function(superClass) {
18512 extend(Controller, superClass);
18514 function Controller() {
18515 return Controller.__super__.constructor.apply(this, arguments);
18518 Controller.prototype.initialize = function(options) {
18519 var collection;
18520 collection = App.request("recording:entities", options.group);
18521 return App.execute("when:entity:fetched", collection, (function(_this) {
18522 return function() {
18523 collection.sortCollection('starttime', 'desc');
18524 _this.layout = _this.getLayoutView(collection);
18525 _this.listenTo(_this.layout, "show", function() {
18526 _this.renderChannels(collection);
18527 return _this.getSubNav();
18529 return App.regionContent.show(_this.layout);
18531 })(this));
18534 Controller.prototype.getLayoutView = function(collection) {
18535 return new List.Layout({
18536 collection: collection
18540 Controller.prototype.renderChannels = function(collection) {
18541 var view;
18542 view = new List.RecordingList({
18543 collection: collection
18545 this.listenTo(view, 'childview:recording:play', function(parent, child) {
18546 var playlist;
18547 if (child.model.get('player') === 'video') {
18548 return App.execute("input:resume", child.model, 'file');
18549 } else {
18550 playlist = App.request("command:kodi:controller", child.model.get('player'), 'PlayList');
18551 return playlist.play('file', child.model.get('file'));
18554 return this.layout.regionContent.show(view);
18557 Controller.prototype.getSubNav = function() {
18558 var subNav;
18559 subNav = App.request("navMain:children:show", 'pvr/tv', 'PVR');
18560 return this.layout.regionSidebarFirst.show(subNav);
18563 return Controller;
18565 })(App.Controllers.Base);
18568 this.Kodi.module("PVR.RecordingList", function(List, App, Backbone, Marionette, $, _) {
18569 List.Layout = (function(superClass) {
18570 extend(Layout, superClass);
18572 function Layout() {
18573 return Layout.__super__.constructor.apply(this, arguments);
18576 Layout.prototype.className = "pvr-page";
18578 return Layout;
18580 })(App.Views.LayoutWithSidebarFirstView);
18581 List.RecordingTeaser = (function(superClass) {
18582 extend(RecordingTeaser, superClass);
18584 function RecordingTeaser() {
18585 return RecordingTeaser.__super__.constructor.apply(this, arguments);
18588 RecordingTeaser.prototype.template = 'apps/pvr/recordingList/recording';
18590 RecordingTeaser.prototype.tagName = "li";
18592 RecordingTeaser.prototype.className = 'pvr-card card';
18594 RecordingTeaser.prototype.triggers = {
18595 "click .play": "recording:play"
18598 return RecordingTeaser;
18600 })(App.Views.ItemView);
18601 return List.RecordingList = (function(superClass) {
18602 extend(RecordingList, superClass);
18604 function RecordingList() {
18605 return RecordingList.__super__.constructor.apply(this, arguments);
18608 RecordingList.prototype.childView = List.RecordingTeaser;
18610 RecordingList.prototype.tagName = "ul";
18612 RecordingList.prototype.className = "recordings";
18614 return RecordingList;
18616 })(App.Views.CollectionView);
18619 this.Kodi.module("SearchApp.List", function(List, App, Backbone, Marionette, $, _) {
18620 return List.Controller = (function(superClass) {
18621 extend(Controller, superClass);
18623 function Controller() {
18624 this.updateProgress = bind(this.updateProgress, this);
18625 this.getLoader = bind(this.getLoader, this);
18626 return Controller.__super__.constructor.apply(this, arguments);
18629 Controller.prototype.maxItemsCombinedSearch = 21;
18631 Controller.prototype.allEntities = ['movie', 'tvshow', 'artist', 'album', 'song', 'musicvideo'];
18633 Controller.prototype.searchFieldMap = {
18634 artist: 'artist',
18635 album: 'album',
18636 song: 'title',
18637 movie: 'title',
18638 tvshow: 'title',
18639 musicvideo: 'title'
18642 Controller.prototype.entityTitles = {
18643 musicvideo: 'music video'
18646 Controller.prototype.entityPreventSelect = ['tvshow'];
18648 Controller.prototype.initialize = function() {
18649 var media;
18650 this.pageLayout = this.getPageLayout();
18651 this.layout = this.getLayout();
18652 this.processed = [];
18653 this.processedItems = 0;
18654 this.addonSearches = App.request("addon:search:enabled");
18655 App.execute("selected:clear:items");
18656 media = this.getOption('media');
18657 if (media === 'all') {
18658 this.entities = this.allEntities;
18659 } else {
18660 this.entities = [media];
18662 this.listenTo(this.layout, "show", (function(_this) {
18663 return function() {
18664 var entity, len, n, ref, results1;
18665 _this.getLoader();
18666 ref = _this.entities;
18667 results1 = [];
18668 for (n = 0, len = ref.length; n < len; n++) {
18669 entity = ref[n];
18670 if (helpers.global.inArray(entity, _this.allEntities)) {
18671 results1.push(_this.getResultMedia(entity));
18672 } else {
18673 results1.push(_this.getResultAddon(entity));
18676 return results1;
18678 })(this));
18679 this.listenTo(this.pageLayout, "show", (function(_this) {
18680 return function() {
18681 _this.pageLayout.regionContent.show(_this.layout);
18682 return _this.pageLayout.regionSidebarFirst.show(_this.getSidebar());
18684 })(this));
18685 return App.regionContent.show(this.pageLayout);
18688 Controller.prototype.getPageLayout = function() {
18689 return new List.PageLayout();
18692 Controller.prototype.getLayout = function() {
18693 return new List.ListLayout();
18696 Controller.prototype.getSidebar = function() {
18697 var len, media, medias, n, opts, ref;
18698 medias = [
18700 id: 'all',
18701 title: 'all media'
18704 ref = this.allEntities;
18705 for (n = 0, len = ref.length; n < len; n++) {
18706 media = ref[n];
18707 medias.push({
18708 id: media,
18709 title: this.getTitle(media) + 's'
18712 opts = {
18713 links: {
18714 media: medias,
18715 addon: this.addonSearches
18717 query: this.getOption('query')
18719 return new List.Sidebar(opts);
18722 Controller.prototype.getLoader = function() {
18723 var addon, i, name, query, searchNames, text, toProcess;
18724 toProcess = _.difference(this.entities, this.processed);
18725 for (i in toProcess) {
18726 name = toProcess[i];
18727 addon = _.findWhere(this.addonSearches, {
18728 id: name
18730 toProcess[parseInt(i)] = addon ? addon.title : name + 's';
18732 searchNames = helpers.global.arrayToSentence(toProcess, false);
18733 query = helpers.global.arrayToSentence([this.getOption('query')], false);
18734 text = t.gettext('Searching for') + ' ' + query + ' ' + t.gettext('in') + ' ' + searchNames;
18735 return App.execute("loading:show:view", this.layout.loadingSet, text);
18738 Controller.prototype.getResultMedia = function(entity) {
18739 var limit, opts, query;
18740 query = this.getOption('query');
18741 limit = {
18742 start: 0
18744 if (this.getOption('media') === 'all') {
18745 limit.end = this.maxItemsCombinedSearch;
18747 opts = {
18748 limits: limit,
18749 filter: {
18750 'operator': 'contains',
18751 'field': this.searchFieldMap[entity],
18752 'value': query
18754 success: (function(_this) {
18755 return function(loaded) {
18756 var more, setView, view;
18757 if (loaded.length > 0) {
18758 _this.processedItems = _this.processedItems + loaded.length;
18759 more = false;
18760 if (loaded.length === _this.maxItemsCombinedSearch) {
18761 more = true;
18762 loaded.first(20);
18764 view = App.request(entity + ":list:view", loaded, true);
18765 setView = new List.ListSet({
18766 entity: entity,
18767 more: more,
18768 query: query,
18769 title: _this.getTitle(entity) + 's',
18770 noMenuDefault: helpers.global.inArray(entity, _this.entityPreventSelect)
18772 App.listenTo(setView, "show", function() {
18773 return setView.regionCollection.show(view);
18775 _this.layout[entity + "Set"].show(setView);
18777 return _this.updateProgress(entity);
18779 })(this)
18781 return App.request(entity + ":entities", opts);
18784 Controller.prototype.getResultAddon = function(addonId) {
18785 var addonSearch, opts;
18786 addonSearch = _.findWhere(this.addonSearches, {
18787 id: addonId
18789 opts = {
18790 file: addonSearch.url.replace('[QUERY]', this.getOption('query')),
18791 media: addonSearch.media,
18792 addonId: addonSearch.id,
18793 success: (function(_this) {
18794 return function(fullCollection) {
18795 var collection, filesView, i, len, n, ref, setView, type, typeCollection;
18796 i = 0;
18797 typeCollection = App.request("file:parsed:entities", fullCollection);
18798 ref = ['file', 'directory'];
18799 for (n = 0, len = ref.length; n < len; n++) {
18800 type = ref[n];
18801 collection = typeCollection[type];
18802 if (collection.length > 0) {
18803 i++;
18804 _this.processedItems = _this.processedItems + collection.length;
18805 filesView = App.request("browser:" + type + ":view", collection);
18806 setView = new List.ListSet({
18807 entity: addonSearch.title,
18808 title: i === 1 ? addonSearch.title : '',
18809 more: false,
18810 query: _this.getOption('query'),
18811 noMenuDefault: true
18813 App.listenTo(setView, "show", function() {
18814 return setView.regionResult.show(filesView);
18816 _this.layout.appendAddonView(addonId + type, setView);
18819 return _this.updateProgress(addonId);
18821 })(this)
18823 return App.request("file:entities", opts);
18826 Controller.prototype.getTitle = function(entity) {
18827 var title;
18828 title = this.entityTitles[entity] ? this.entityTitles[entity] : entity;
18829 return title;
18832 Controller.prototype.updateProgress = function(done) {
18833 if (done != null) {
18834 this.processed.push(done);
18836 this.getLoader();
18837 if (this.processed.length === this.entities.length) {
18838 this.layout.loadingSet.$el.empty();
18839 if (this.processedItems === 0) {
18840 return this.pageLayout.regionContent.$el.html('<h2 class="search-no-result">' + tr('No results found') + '</h2>');
18845 return Controller;
18847 })(App.Controllers.Base);
18850 this.Kodi.module("SearchApp.List", function(List, App, Backbone, Marionette, $, _) {
18851 List.PageLayout = (function(superClass) {
18852 extend(PageLayout, superClass);
18854 function PageLayout() {
18855 return PageLayout.__super__.constructor.apply(this, arguments);
18858 PageLayout.prototype.className = "search-page-layout";
18860 return PageLayout;
18862 })(App.Views.LayoutWithSidebarFirstView);
18863 List.ListLayout = (function(superClass) {
18864 extend(ListLayout, superClass);
18866 function ListLayout() {
18867 return ListLayout.__super__.constructor.apply(this, arguments);
18870 ListLayout.prototype.template = 'apps/search/list/search_layout';
18872 ListLayout.prototype.className = "search-page";
18874 ListLayout.prototype.regions = {
18875 artistSet: '.entity-set-artist',
18876 albumSet: '.entity-set-album',
18877 songSet: '.entity-set-song',
18878 movieSet: '.entity-set-movie',
18879 tvshowSet: '.entity-set-tvshow',
18880 musicvideoSet: '.entity-set-musicvideo',
18881 loadingSet: '.entity-set-loading'
18884 ListLayout.prototype.appendAddonView = function(addonId, addonView) {
18885 var addonViewId;
18886 addonViewId = 'addonSet_' + addonId.split('.').join('_');
18887 $('.entity-set-addons', this.$el).append('<div id="' + addonViewId + '">');
18888 this.regionManager.addRegion(addonViewId, '#' + addonViewId);
18889 return this[addonViewId].show(addonView);
18892 List.ListSet = (function(superClass1) {
18893 extend(ListSet, superClass1);
18895 function ListSet() {
18896 return ListSet.__super__.constructor.apply(this, arguments);
18899 ListSet.prototype.className = "search-set landing-set";
18901 ListSet.prototype.initialize = function() {
18902 this.setOptions();
18903 return this.createModel();
18906 ListSet.prototype.setOptions = function() {
18907 if (this.options.more && this.options.query) {
18908 return this.options.more = this.themeLink(t.gettext('Show more'), 'search/' + this.options.entity + '/' + this.options.query);
18912 return ListSet;
18914 })(App.Views.SetLayoutView);
18916 return ListLayout;
18918 })(App.Views.LayoutView);
18919 return List.Sidebar = (function(superClass) {
18920 extend(Sidebar, superClass);
18922 function Sidebar() {
18923 return Sidebar.__super__.constructor.apply(this, arguments);
18926 Sidebar.prototype.template = 'apps/search/list/search_sidebar';
18928 Sidebar.prototype.className = "search-sidebar";
18930 Sidebar.prototype.onRender = function() {
18931 var $list, active, item, link, links, query, ref, results1, type;
18932 query = encodeURIComponent(this.options.query);
18933 ref = this.options.links;
18934 results1 = [];
18935 for (type in ref) {
18936 links = ref[type];
18937 if (links.length === 0) {
18938 results1.push($('.sidebar-section-' + type, this.$el).remove());
18939 } else {
18940 $list = $('.search-' + type + '-links', this.$el);
18941 results1.push((function() {
18942 var len, n, results2;
18943 results2 = [];
18944 for (n = 0, len = links.length; n < len; n++) {
18945 item = links[n];
18946 active = helpers.url.arg(1) === item.id ? 'active' : '';
18947 link = this.themeLink(t.gettext(item.title), 'search/' + item.id + '/' + query, {
18948 className: active
18950 results2.push($list.append(this.themeTag('li', {}, link)));
18952 return results2;
18953 }).call(this));
18956 return results1;
18959 return Sidebar;
18961 })(App.Views.LayoutView);
18964 this.Kodi.module("SearchApp", function(SearchApp, App, Backbone, Marionette, $, _) {
18965 var API;
18966 SearchApp.Router = (function(superClass) {
18967 extend(Router, superClass);
18969 function Router() {
18970 return Router.__super__.constructor.apply(this, arguments);
18973 Router.prototype.appRoutes = {
18974 "search": "view",
18975 "search/:media/:query": "list"
18978 return Router;
18980 })(App.Router.Base);
18981 API = {
18982 keyUpTimeout: 2000,
18983 externalSearchUrls: {
18984 google: 'https://www.google.com/webhp?#q=[QUERY]',
18985 imdb: 'http://www.imdb.com/find?s=all&q=[QUERY]',
18986 tmdb: 'https://www.themoviedb.org/search?query=[QUERY]',
18987 tvdb: 'http://thetvdb.com/?searchseriesid=&tab=listseries&function=Search&string=[QUERY]',
18988 soundcloud: 'https://soundcloud.com/search?q=[QUERY]',
18989 youtube: 'https://www.youtube.com/results?search_query=[QUERY]'
18991 list: function(media, query) {
18992 App.navigate("search/" + media + "/" + query);
18993 $('#search').val(query);
18994 return new SearchApp.List.Controller({
18995 query: query,
18996 media: media
18999 view: function() {
19000 return new SearchApp.Show.Controller();
19002 searchBind: function() {
19003 return $('#search').on('keyup', function(e) {
19004 var media, val;
19005 $('#search-region').removeClass('pre-search');
19006 val = $('#search').val();
19007 media = helpers.url.arg(0) === 'search' ? helpers.url.arg(1) : 'all';
19008 clearTimeout(App.searchAllTimeout);
19009 if (e.which === 13) {
19010 return API.list(media, val);
19011 } else {
19012 $('#search-region').addClass('pre-search');
19013 return App.searchAllTimeout = setTimeout((function() {
19014 $('#search-region').removeClass('pre-search');
19015 return API.list(media, val);
19016 }), API.keyUpTimeout);
19021 App.commands.setHandler('search:go', function(type, query, provider) {
19022 var url;
19023 if (provider == null) {
19024 provider = 'all';
19026 if (type === 'internal') {
19027 return App.navigate("search/" + provider + "/" + query, {
19028 trigger: true
19030 } else if (API.externalSearchUrls[provider]) {
19031 url = API.externalSearchUrls[provider].replace('[QUERY]', encodeURIComponent(query));
19032 return window.open(url);
19035 App.on("before:start", function() {
19036 return new SearchApp.Router({
19037 controller: API
19040 return App.addInitializer(function() {
19041 return App.vent.on("shell:ready", function() {
19042 return API.searchBind();
19047 this.Kodi.module("SearchApp.Show", function(Show, App, Backbone, Marionette, $, _) {
19048 return Show.Controller = (function(superClass) {
19049 extend(Controller, superClass);
19051 function Controller() {
19052 return Controller.__super__.constructor.apply(this, arguments);
19055 Controller.prototype.initialize = function(options) {
19056 this.landing = this.getLanding();
19057 this.listenTo(this.landing, "show", (function(_this) {
19058 return function() {
19059 return $('#search').focus();
19061 })(this));
19062 return App.regionContent.show(this.landing);
19065 Controller.prototype.getLanding = function() {
19066 return new Show.Landing();
19069 return Controller;
19071 })(App.Controllers.Base);
19074 this.Kodi.module("SearchApp.Show", function(Show, App, Backbone, Marionette, $, _) {
19075 return Show.Landing = (function(superClass) {
19076 extend(Landing, superClass);
19078 function Landing() {
19079 return Landing.__super__.constructor.apply(this, arguments);
19082 Landing.prototype.template = 'apps/search/show/landing';
19084 return Landing;
19086 })(App.Views.ItemView);
19089 this.Kodi.module("Selected", function(Selected, App, Backbone, Marionette, $, _) {
19090 Selected.List = (function(superClass) {
19091 extend(List, superClass);
19093 function List() {
19094 return List.__super__.constructor.apply(this, arguments);
19097 List.prototype.items = [];
19099 List.prototype.media = '';
19101 List.prototype.type = '';
19103 List.prototype.getItems = function() {
19104 return this.items;
19107 List.prototype.getCollection = function(callback) {
19108 var collection, idProp, ids;
19109 if (helpers.global.inArray(this.type, ['song', 'artist', 'album'])) {
19110 ids = _.pluck(this.items, 'id');
19111 idProp = this.type + 'id';
19112 return App.request("song:custom:entities", idProp, ids, function(collection) {
19113 return callback(collection);
19115 } else {
19116 collection = App.request(this.type + ":build:collection", this.items);
19117 return callback(collection);
19121 List.prototype.updateItems = function(op, model) {
19122 this.items = _.filter(this.items, function(item) {
19123 return item.uid !== model.uid;
19125 if (op === 'add') {
19126 this.items.push(model);
19127 this.type = model.type;
19128 this.media = helpers.global.inArray(this.type, ['song', 'album', 'artist']) ? 'audio' : 'video';
19130 this.updateUi();
19131 return this;
19134 List.prototype.clearItems = function() {
19135 this.items = [];
19136 this.updateUi();
19137 return this;
19140 List.prototype.setMedia = function(media) {
19141 this.media = media;
19142 return this;
19145 List.prototype.getType = function() {
19146 return this.type;
19149 List.prototype.getMedia = function() {
19150 return this.media;
19153 List.prototype.updateUi = function() {
19154 var $selectedRegion, selectedText;
19155 selectedText = this.items.length + ' ' + t.ngettext("item selected", "items selected", this.items.length);
19156 $('#selected-count').text(selectedText);
19157 $selectedRegion = $('#selected-region');
19158 $selectedRegion.removeClassStartsWith('media-');
19159 $selectedRegion.addClass('media-' + this.media);
19160 if (this.items.length === 0) {
19161 $selectedRegion.hide();
19162 return $('.selected').removeClass('selected');
19163 } else {
19164 return $selectedRegion.show();
19168 return List;
19170 })(Marionette.Object);
19171 App.addInitializer(function() {
19172 return App.selected = new Selected.List;
19174 App.reqres.setHandler("selected:get:items", function() {
19175 return App.selected.getItems();
19177 App.reqres.setHandler("selected:get:media", function() {
19178 return App.selected.getMedia();
19180 App.commands.setHandler("selected:update:items", function(op, model) {
19181 return App.selected.updateItems(op, model);
19183 App.commands.setHandler("selected:clear:items", function() {
19184 return App.selected.clearItems();
19186 App.commands.setHandler("selected:set:media", function(media) {
19187 return App.selected.setMedia(media);
19189 App.commands.setHandler("selected:action:play", function() {
19190 return App.selected.getCollection(function(collection) {
19191 var kodiPlaylist;
19192 kodiPlaylist = App.request("command:kodi:controller", App.selected.getMedia(), 'PlayList');
19193 kodiPlaylist.playCollection(collection);
19194 return App.selected.clearItems();
19197 App.commands.setHandler("selected:action:add", function() {
19198 return App.selected.getCollection(function(collection) {
19199 var kodiPlaylist;
19200 kodiPlaylist = App.request("command:kodi:controller", App.selected.getMedia(), 'PlayList');
19201 kodiPlaylist.addCollection(collection);
19202 return App.selected.clearItems();
19205 return App.commands.setHandler("selected:action:localadd", function() {
19206 var idProp, ids, items;
19207 items = App.selected.getItems();
19208 ids = _.pluck(items, 'id');
19209 idProp = App.selected.getType() + 'id';
19210 App.execute("localplaylist:addentity", idProp, ids);
19211 return App.selected.clearItems();
19215 this.Kodi.module("SettingsApp", function(SettingsApp, App, Backbone, Marionette, $, _) {
19216 var API;
19217 SettingsApp.Router = (function(superClass) {
19218 extend(Router, superClass);
19220 function Router() {
19221 return Router.__super__.constructor.apply(this, arguments);
19224 Router.prototype.appRoutes = {
19225 "settings/web": "local",
19226 "settings/kodi": "kodi",
19227 "settings/kodi/:section": "kodi",
19228 "settings/addons": "addons",
19229 "settings/nav": "navMain",
19230 "settings/search": "search"
19233 return Router;
19235 })(App.Router.Base);
19236 API = {
19237 subNavId: 'settings/web',
19238 local: function() {
19239 return new SettingsApp.Show.Local.Controller();
19241 addons: function() {
19242 return new SettingsApp.Show.Addons.Controller();
19244 navMain: function() {
19245 return new SettingsApp.Show.navMain.Controller();
19247 search: function() {
19248 return new SettingsApp.Show.Search.Controller();
19250 kodi: function(section, category) {
19251 return new SettingsApp.Show.Kodi.Controller({
19252 section: section,
19253 category: category
19256 getSubNav: function() {
19257 var collection, sidebarView;
19258 collection = App.request("settings:kodi:entities", {
19259 type: 'sections'
19261 sidebarView = new SettingsApp.Show.Sidebar();
19262 App.listenTo(sidebarView, "show", (function(_this) {
19263 return function() {
19264 var settingsNavView;
19265 App.execute("when:entity:fetched", collection, function() {
19266 var kodiSettingsView;
19267 kodiSettingsView = App.request("navMain:collection:show", collection, t.gettext('Kodi settings'));
19268 return sidebarView.regionKodiNav.show(kodiSettingsView);
19270 settingsNavView = App.request("navMain:children:show", API.subNavId, 'General');
19271 return sidebarView.regionLocalNav.show(settingsNavView);
19273 })(this));
19274 return sidebarView;
19277 App.on("before:start", function() {
19278 return new SettingsApp.Router({
19279 controller: API
19282 return App.reqres.setHandler('settings:subnav', function() {
19283 return API.getSubNav();
19287 this.Kodi.module("SettingsApp.Show.Base", function(SettingsBase, App, Backbone, Marionette, $, _) {
19288 return SettingsBase.Controller = (function(superClass) {
19289 extend(Controller, superClass);
19291 function Controller() {
19292 return Controller.__super__.constructor.apply(this, arguments);
19295 Controller.prototype.initialize = function() {
19296 this.layout = this.getLayoutView();
19297 this.listenTo(this.layout, "show", (function(_this) {
19298 return function() {
19299 _this.getSubNav();
19300 return _this.getForm();
19302 })(this));
19303 return App.regionContent.show(this.layout);
19306 Controller.prototype.getLayoutView = function() {
19307 return new App.SettingsApp.Show.Layout();
19310 Controller.prototype.getSubNav = function() {
19311 var subNav;
19312 subNav = App.request('settings:subnav');
19313 return this.layout.regionSidebarFirst.show(subNav);
19316 Controller.prototype.getForm = function() {
19317 return this.getCollection((function(_this) {
19318 return function(collection) {
19319 var form, options;
19320 options = {
19321 form: _this.getStructure(collection),
19322 formState: [],
19323 config: {
19324 attributes: {
19325 "class": 'settings-form'
19327 callback: function(formState, formView) {
19328 return _this.saveCallback(formState, formView);
19330 onShow: function() {
19331 return _this.onReady();
19335 form = App.request("form:wrapper", options);
19336 return _this.layout.regionContent.show(form);
19338 })(this));
19341 Controller.prototype.getCollection = function(callback) {
19342 var res;
19343 res = {};
19344 return callback(res);
19347 Controller.prototype.getStructure = function(collection) {
19348 return [];
19351 Controller.prototype.saveCallback = function(formState, formView) {};
19353 Controller.prototype.onReady = function() {
19354 return this.layout;
19357 return Controller;
19359 })(App.Controllers.Base);
19362 this.Kodi.module("SettingsApp.Show.Addons", function(Addons, App, Backbone, Marionette, $, _) {
19363 return Addons.Controller = (function(superClass) {
19364 extend(Controller, superClass);
19366 function Controller() {
19367 return Controller.__super__.constructor.apply(this, arguments);
19370 Controller.prototype.initialize = function() {
19371 this.layout = this.getLayoutView();
19372 this.listenTo(this.layout, "show", (function(_this) {
19373 return function() {
19374 _this.getSubNav();
19375 return _this.getForm();
19377 })(this));
19378 return App.regionContent.show(this.layout);
19381 Controller.prototype.getLayoutView = function() {
19382 return new App.SettingsApp.Show.Layout();
19385 Controller.prototype.getSubNav = function() {
19386 var subNav;
19387 subNav = App.request('settings:subnav');
19388 return this.layout.regionSidebarFirst.show(subNav);
19391 Controller.prototype.addonController = function() {
19392 return App.request("command:kodi:controller", 'auto', 'AddOn');
19395 Controller.prototype.getAllAddons = function(callback) {
19396 return this.addonController().getAllAddons(callback);
19399 Controller.prototype.getForm = function() {
19400 return this.getAllAddons((function(_this) {
19401 return function(addons) {
19402 var form, options;
19403 options = {
19404 form: _this.getStructure(addons),
19405 formState: [],
19406 config: {
19407 attributes: {
19408 "class": 'settings-form'
19410 callback: function(data, formView) {
19411 return _this.saveCallback(data, formView);
19415 form = App.request("form:wrapper", options);
19416 return _this.layout.regionContent.show(form);
19418 })(this));
19421 Controller.prototype.getStructure = function(addons) {
19422 var addon, el, elements, enabled, form, i, type, types;
19423 form = [];
19424 types = [];
19425 for (i in addons) {
19426 addon = addons[i];
19427 types[addon.type] = true;
19429 for (type in types) {
19430 enabled = types[type];
19431 elements = _.where(addons, {
19432 type: type
19434 for (i in elements) {
19435 el = elements[i];
19436 elements[i] = $.extend(el, {
19437 id: el.addonid,
19438 name: el.addonid,
19439 type: 'checkbox',
19440 defaultValue: el.enabled,
19441 title: el.name
19444 form.push({
19445 title: type,
19446 id: type,
19447 children: elements
19450 return form;
19453 Controller.prototype.saveCallback = function(data, formView) {
19454 var updating;
19455 updating = [];
19456 return this.getAllAddons(function(addons) {
19457 var addon, addonid, commander, commands, key, val;
19458 for (key in addons) {
19459 addon = addons[key];
19460 addonid = addon.addonid;
19461 if (addon.enabled === !data[addonid]) {
19462 updating[addonid] = data[addonid];
19465 commander = App.request("command:kodi:controller", 'auto', 'Commander');
19466 commands = [];
19467 for (key in updating) {
19468 val = updating[key];
19469 commands.push({
19470 method: 'Addons.SetAddonEnabled',
19471 params: [key, val]
19474 return commander.multipleCommands(commands, (function(_this) {
19475 return function(resp) {
19476 return Kodi.execute("notification:show", 'Toggled ' + commands.length + ' addons');
19478 })(this));
19482 return Controller;
19484 })(App.Controllers.Base);
19487 this.Kodi.module("SettingsApp.Show.Kodi", function(Kodi, App, Backbone, Marionette, $, _) {
19488 return Kodi.Controller = (function(superClass) {
19489 var API;
19491 extend(Controller, superClass);
19493 function Controller() {
19494 return Controller.__super__.constructor.apply(this, arguments);
19497 API = {
19498 optionLookups: {
19499 'lookandfeel.skin': 'xbmc.gui.skin',
19500 'locale.language': 'kodi.resource.language',
19501 'screensaver.mode': 'xbmc.ui.screensaver',
19502 'musiclibrary.albumsscraper': 'xbmc.metadata.scraper.albums',
19503 'musiclibrary.artistsscraper': 'xbmc.metadata.scraper.artists',
19504 'musicplayer.visualisation': 'xbmc.player.musicviz',
19505 'services.webskin': 'xbmc.webinterface',
19506 'subtitles.tv': 'xbmc.subtitle.module',
19507 'subtitles.movie': 'xbmc.subtitle.module',
19508 'audiocds.encoder': 'xbmc.audioencoder'
19510 actionLookups: {
19511 "musiclibrary.cleanup": "command:kodi:audio:clean",
19512 "videolibrary.cleanup": "command:kodi:video:clean"
19514 parseOptions: function(options) {
19515 var out;
19516 out = {};
19517 $(options).each(function(i, option) {
19518 return out[option.value] = option.label;
19520 return out;
19522 labelRewrites: function(item) {
19523 if (item.id.lastIndexOf('videolibrary', 0) === 0) {
19524 item.title += ' (video)';
19526 if (item.id.lastIndexOf('musiclibrary', 0) === 0) {
19527 item.title += ' (music)';
19529 return item;
19533 Controller.prototype.initialize = function(options) {
19534 this.layout = this.getLayoutView();
19535 this.listenTo(this.layout, "show", (function(_this) {
19536 return function() {
19537 _this.getSubNav();
19538 if (options.section) {
19539 return _this.getSettingsForm(options.section);
19542 })(this));
19543 return App.regionContent.show(this.layout);
19546 Controller.prototype.getLayoutView = function() {
19547 return new App.SettingsApp.Show.Layout();
19550 Controller.prototype.getSubNav = function() {
19551 var subNav;
19552 subNav = App.request('settings:subnav');
19553 return this.layout.regionSidebarFirst.show(subNav);
19556 Controller.prototype.getSettingsForm = function(section) {
19557 var categoryCollection, formStructure;
19558 formStructure = [];
19559 categoryCollection = App.request("settings:kodi:entities", {
19560 type: 'categories',
19561 section: section
19563 return App.execute("when:entity:fetched", categoryCollection, (function(_this) {
19564 return function() {
19565 var categories, categoryNames;
19566 categoryNames = categoryCollection.pluck("id");
19567 categories = categoryCollection.toJSON();
19568 return App.request("settings:kodi:filtered:entities", {
19569 type: 'settings',
19570 section: section,
19571 categories: categoryNames,
19572 callback: function(categorySettings) {
19573 $(categories).each(function(i, category) {
19574 var items;
19575 items = _this.mapSettingsToElements(categorySettings[category.id]);
19576 if (items.length > 0) {
19577 return formStructure.push({
19578 title: category.title,
19579 id: category.id,
19580 children: items
19584 return _this.getForm(section, formStructure);
19588 })(this));
19591 Controller.prototype.getForm = function(section, formStructure) {
19592 var form, options;
19593 options = {
19594 form: formStructure,
19595 config: {
19596 attributes: {
19597 "class": 'settings-form'
19599 callback: (function(_this) {
19600 return function(data, formView) {
19601 return _this.saveCallback(data, formView);
19603 })(this)
19606 form = App.request("form:wrapper", options);
19607 return this.layout.regionContent.show(form);
19610 Controller.prototype.getAddonOptions = function(elId, value) {
19611 var addon, addons, filteredAddons, i, lookup, mappedType, options;
19612 mappedType = API.optionLookups[elId];
19613 options = [];
19614 lookup = {};
19615 if (mappedType) {
19616 addons = App.request('addon:enabled:addons');
19617 filteredAddons = _.where(addons, {
19618 type: mappedType
19620 for (i in filteredAddons) {
19621 addon = filteredAddons[i];
19622 options.push({
19623 value: addon.addonid,
19624 label: addon.name
19626 lookup[addon.addonid] = true;
19628 if (!lookup[value]) {
19629 options.push({
19630 value: value,
19631 label: value
19634 return options;
19636 return false;
19639 Controller.prototype.mapSettingsToElements = function(items) {
19640 var elements;
19641 elements = [];
19642 $(items).each((function(_this) {
19643 return function(i, item) {
19644 var options, type;
19645 type = null;
19646 switch (item.type) {
19647 case 'boolean':
19648 type = 'checkbox';
19649 break;
19650 case 'path':
19651 type = 'textfield';
19652 break;
19653 case 'addon':
19654 options = _this.getAddonOptions(item.id, item.value);
19655 if (options) {
19656 item.options = options;
19657 } else {
19658 type = 'textfield';
19660 break;
19661 case 'integer':
19662 type = 'textfield';
19663 break;
19664 case 'string':
19665 type = 'textfield';
19666 break;
19667 case 'action':
19668 if (API.actionLookups[item.id]) {
19669 type = 'button';
19670 item.value = item.label;
19671 item.trigger = API.actionLookups[item.id];
19672 } else {
19673 type = 'hide';
19675 break;
19676 default:
19677 type = 'hide';
19679 if (item.options) {
19680 type = 'select';
19681 item.options = API.parseOptions(item.options);
19683 item = API.labelRewrites(item);
19684 if (type === 'hide') {
19685 return console.log('no setting to field mapping for: ' + item.type + ' -> ' + item.id);
19686 } else {
19687 item.type = type;
19688 item.defaultValue = item.value;
19689 return elements.push(item);
19692 })(this));
19693 return elements;
19696 Controller.prototype.saveCallback = function(data, formView) {
19697 return App.execute("settings:kodi:save:entities", data, (function(_this) {
19698 return function(resp) {
19699 App.execute("notification:show", t.gettext("Saved Kodi settings"));
19700 App.vent.trigger("config:local:updated", {});
19701 return App.vent.trigger("config:kodi:updated", data);
19703 })(this));
19706 return Controller;
19708 })(App.Controllers.Base);
19711 this.Kodi.module("SettingsApp.Show.Kodi", function(Kodi, App, Backbone, Marionette, $, _) {});
19713 this.Kodi.module("SettingsApp.Show.Local", function(Local, App, Backbone, Marionette, $, _) {
19714 return Local.Controller = (function(superClass) {
19715 extend(Controller, superClass);
19717 function Controller() {
19718 return Controller.__super__.constructor.apply(this, arguments);
19721 Controller.prototype.initialize = function() {
19722 this.layout = this.getLayoutView();
19723 this.listenTo(this.layout, "show", (function(_this) {
19724 return function() {
19725 _this.getSubNav();
19726 return _this.getForm();
19728 })(this));
19729 return App.regionContent.show(this.layout);
19732 Controller.prototype.getLayoutView = function() {
19733 return new App.SettingsApp.Show.Layout();
19736 Controller.prototype.getSubNav = function() {
19737 var subNav;
19738 subNav = App.request('settings:subnav');
19739 return this.layout.regionSidebarFirst.show(subNav);
19742 Controller.prototype.getForm = function() {
19743 var form, options;
19744 options = {
19745 form: this.getStructure(),
19746 formState: this.getState(),
19747 config: {
19748 attributes: {
19749 "class": 'settings-form'
19751 callback: (function(_this) {
19752 return function(data, formView) {
19753 return _this.saveCallback(data, formView);
19755 })(this)
19758 form = App.request("form:wrapper", options);
19759 return this.layout.regionContent.show(form);
19762 Controller.prototype.getStructure = function() {
19763 return [
19765 title: 'General options',
19766 id: 'general',
19767 children: [
19769 id: 'lang',
19770 title: tr("Language"),
19771 type: 'select',
19772 options: helpers.translate.getLanguages(),
19773 defaultValue: 'en',
19774 description: tr('Preferred language, need to refresh browser to take effect')
19775 }, {
19776 id: 'defaultPlayer',
19777 title: tr("Default player"),
19778 type: 'select',
19779 options: {
19780 auto: 'Auto',
19781 kodi: 'Kodi',
19782 local: 'Local'
19784 defaultValue: 'auto',
19785 description: tr('Which player to start with')
19786 }, {
19787 id: 'keyboardControl',
19788 title: tr("Keyboard controls"),
19789 type: 'select',
19790 options: {
19791 kodi: 'Kodi',
19792 local: 'Browser',
19793 both: 'Both'
19795 defaultValue: 'kodi',
19796 description: tr('In Chorus, will you keyboard control Kodi, the browser or both') + '. <a href="#help/keybind-readme">' + tr('Learn more') + '</a>'
19799 }, {
19800 title: 'List options',
19801 id: 'list',
19802 children: [
19804 id: 'ignoreArticle',
19805 title: tr("Ignore article"),
19806 type: 'checkbox',
19807 defaultValue: true,
19808 description: tr("Ignore articles (terms such as 'The' and 'A') when sorting lists")
19809 }, {
19810 id: 'albumArtistsOnly',
19811 title: tr("Album artists only"),
19812 type: 'checkbox',
19813 defaultValue: true,
19814 description: tr('When listing artists should we only see artists with albums or all artists found. Warning: turning this off can impact performance with large libraries')
19815 }, {
19816 id: 'playlistFocusPlaying',
19817 title: tr("Focus playlist on playing"),
19818 type: 'checkbox',
19819 defaultValue: true,
19820 description: tr('Automatically scroll the playlist to the current playing item. This happens whenever the playing item is changed')
19823 }, {
19824 title: 'Appearance',
19825 id: 'appearance',
19826 children: [
19828 id: 'vibrantHeaders',
19829 title: tr("Vibrant headers"),
19830 type: 'checkbox',
19831 defaultValue: true,
19832 description: tr("Use colourful headers for media pages")
19833 }, {
19834 id: 'disableThumbs',
19835 title: tr("Disable Thumbs Up"),
19836 type: 'checkbox',
19837 defaultValue: false,
19838 description: t.sprintf(tr("Remove the thumbs up button from media. Note: you may also want to remove the menu item from the %1$s"), '<a href="#settings/nav">' + tr('Main Nav') + '</a>')
19839 }, {
19840 id: 'showDeviceName',
19841 title: tr("Show device name"),
19842 type: 'checkbox',
19843 defaultValue: false,
19844 description: tr("Show the Kodi device name in the header of Chorus")
19847 }, {
19848 title: 'Advanced options',
19849 id: 'advanced',
19850 children: [
19852 id: 'socketsPort',
19853 title: tr("Websockets port"),
19854 type: 'textfield',
19855 defaultValue: '9090',
19856 description: "9090 " + tr("is the default")
19857 }, {
19858 id: 'socketsHost',
19859 title: tr("Websockets host"),
19860 type: 'textfield',
19861 defaultValue: 'auto',
19862 description: tr("The hostname used for websockets connection. Set to 'auto' to use the current hostname.")
19863 }, {
19864 id: 'pollInterval',
19865 title: tr("Poll interval"),
19866 type: 'select',
19867 defaultValue: '10000',
19868 options: {
19869 '5000': "5 " + tr('sec'),
19870 '10000': "10 " + tr('sec'),
19871 '30000': "30 " + tr('sec'),
19872 '60000': "60 " + tr('sec')
19874 description: tr("How often do I poll for updates from Kodi (Only applies when websockets inactive)")
19875 }, {
19876 id: 'kodiSettingsLevel',
19877 title: tr("Kodi settings level"),
19878 type: 'select',
19879 defaultValue: 'standard',
19880 options: {
19881 'standard': 'Standard',
19882 'advanced': 'Advanced',
19883 'expert': 'Expert'
19885 description: tr('Advanced setting level is recommended for those who know what they are doing.')
19886 }, {
19887 id: 'reverseProxy',
19888 title: tr("Reverse proxy support"),
19889 type: 'checkbox',
19890 defaultValue: false,
19891 description: tr('Enable support for reverse proxying.')
19892 }, {
19893 id: 'refreshIgnoreNFO',
19894 title: tr("Refresh Ignore NFO"),
19895 type: 'checkbox',
19896 defaultValue: true,
19897 description: tr('Ignore local NFO files when manually refreshing media.')
19900 }, {
19901 title: 'API Keys',
19902 id: 'apikeys',
19903 children: [
19905 id: 'apiKeyTMDB',
19906 title: tr("The Movie DB"),
19907 type: 'textfield',
19908 defaultValue: '',
19909 description: tr("Set your personal API key")
19910 }, {
19911 id: 'apiKeyFanartTv',
19912 title: tr("FanartTV"),
19913 type: 'textfield',
19914 defaultValue: '',
19915 description: tr("Set your personal API key")
19916 }, {
19917 id: 'apiKeyYouTube',
19918 title: tr("YouTube"),
19919 type: 'textfield',
19920 defaultValue: '',
19921 description: tr("Set your personal API key")
19928 Controller.prototype.getState = function() {
19929 return config.get('app', 'config:local', config["static"]);
19932 Controller.prototype.saveCallback = function(data, formView) {
19933 config.set('app', 'config:local', data);
19934 config["static"] = _.extend(config["static"], config.get('app', 'config:local', config["static"]));
19935 Kodi.vent.trigger("config:local:updated", config["static"]);
19936 return Kodi.execute("notification:show", tr("Web Settings saved."));
19939 return Controller;
19941 })(App.Controllers.Base);
19944 this.Kodi.module("SettingsApp.Show.navMain", function(NavMain, App, Backbone, Marionette, $, _) {
19945 return NavMain.Controller = (function(superClass) {
19946 extend(Controller, superClass);
19948 function Controller() {
19949 this.onReady = bind(this.onReady, this);
19950 return Controller.__super__.constructor.apply(this, arguments);
19953 Controller.prototype.getCollection = function(callback) {
19954 var collection;
19955 collection = App.request('navMain:entities');
19956 return $.getJSON('lib/icons/mdi.json', function(iconList) {
19957 return callback({
19958 collection: collection,
19959 icons: iconList
19964 Controller.prototype.getStructure = function(data) {
19965 var defaults, form, i, iconLink, item, ref, row;
19966 this.data = data;
19967 defaults = ' <a class="nav-restore-defaults">' + t.gettext('Click here restore defaults') + '</a>';
19968 iconLink = '<a href="#lab/icon-browser">icons</a>';
19969 form = [
19971 title: t.gettext('Main Menu Structure'),
19972 id: 'intro',
19973 children: [
19975 id: 'intro-text',
19976 type: 'markup',
19977 markup: t.sprintf(tr('Here you can change the title, url and %1$s for menu items. You can also remove, re-order and add new items.'), iconLink) + defaults
19982 ref = data.collection.getRawCollection();
19983 for (i in ref) {
19984 item = ref[i];
19985 item.weight = i;
19986 row = this.getRow(item, data.icons);
19987 form.push(row);
19989 form.push({
19990 id: 'add-another',
19991 "class": 'add-another-wrapper',
19992 children: [
19994 type: 'button',
19995 value: 'Add another',
19996 id: 'add-another'
20000 return form;
20003 Controller.prototype.saveCallback = function(formState, formView) {
20004 var i, item, items, ref;
20005 items = [];
20006 ref = formState.title;
20007 for (i in ref) {
20008 item = ref[i];
20009 items.push({
20010 title: formState.title[i],
20011 path: formState.path[i],
20012 icon: formState.icon[i],
20013 weight: formState.weight[i],
20014 id: formState.id[i],
20015 parent: 0
20018 App.request("navMain:update:entities", items);
20019 App.vent.trigger("navMain:refresh");
20020 return Kodi.execute("notification:show", t.gettext('Menu updated'));
20023 Controller.prototype.onReady = function() {
20024 var $ctx, self;
20025 self = this;
20026 $ctx = this.layout.regionContent.$el;
20027 $('.settings-form').addClass('settings-form-draggable');
20028 this.binds();
20029 $('#form-edit-add-another', $ctx).click(function(e) {
20030 var blank, formView, row;
20031 e.preventDefault();
20032 blank = {
20033 weight: $(".nav-item-row").length + 1,
20034 title: '',
20035 path: '',
20036 icon: 'mdi-action-extension'
20038 row = self.getRow(blank);
20039 formView = App.request("form:render:items", [row]);
20040 $(this).closest('.add-another-wrapper').before(formView.render().$el);
20041 return self.binds();
20043 if ($(window).width() > config.getLocal('largeBreakpoint')) {
20044 $('.form-groups', $ctx).sortable({
20045 draggable: ".draggable-row",
20046 onEnd: function(e) {
20047 return $('input[id^="form-edit-weight-"]', e.target).each(function(i, d) {
20048 return $(d).attr('value', i);
20053 return $('.nav-restore-defaults', $ctx).on("click", (function(_this) {
20054 return function(e) {
20055 e.preventDefault();
20056 App.request("navMain:update:defaults");
20057 _this.initialize();
20058 return App.vent.trigger("navMain:refresh");
20060 })(this));
20063 Controller.prototype.binds = function() {
20064 var $ctx;
20065 $ctx = $('.settings-form');
20066 $('select[id^="form-edit-icon"]', $ctx).once('icon-changer').on("change", function(e) {
20067 return $(this).closest('.group-parent', $ctx).find('i').first().attr('class', $(this).val());
20069 $('.remove-item', $ctx).on("click", function(e) {
20070 return $(this).closest('.group-parent', $ctx).remove();
20072 return $.material.init();
20075 Controller.prototype.getRow = function(item) {
20076 var i, icon, icons;
20077 icons = this.data.icons;
20078 i = item.weight;
20079 icon = '<i class="' + item.icon + '"></i>';
20080 return {
20081 id: 'item-' + item.weight,
20082 "class": 'nav-item-row draggable-row',
20083 children: [
20085 id: 'title-' + i,
20086 name: 'title[]',
20087 type: 'textfield',
20088 title: 'Title',
20089 defaultValue: item.title
20090 }, {
20091 id: 'path-' + i,
20092 name: 'path[]',
20093 type: 'textfield',
20094 title: 'Url',
20095 defaultValue: item.path
20096 }, {
20097 id: 'icon-' + i,
20098 name: 'icon[]',
20099 type: 'select',
20100 titleHtml: 'Icon' + icon,
20101 defaultValue: item.icon,
20102 options: icons
20103 }, {
20104 id: 'weight-' + i,
20105 name: 'weight[]',
20106 type: 'hidden',
20107 title: '',
20108 defaultValue: i
20109 }, {
20110 id: 'id-' + i,
20111 name: 'id[]',
20112 type: 'hidden',
20113 title: '',
20114 defaultValue: 1000 + i
20115 }, {
20116 id: 'remove-' + i,
20117 type: 'markup',
20118 markup: '<span class="remove-item">&times;</span>'
20124 return Controller;
20126 })(App.SettingsApp.Show.Base.Controller);
20129 this.Kodi.module("SettingsApp.Show.Search", function(Search, App, Backbone, Marionette, $, _) {
20130 return Search.Controller = (function(superClass) {
20131 extend(Controller, superClass);
20133 function Controller() {
20134 this.onReady = bind(this.onReady, this);
20135 return Controller.__super__.constructor.apply(this, arguments);
20138 Controller.prototype.getCollection = function(callback) {
20139 this.collection = App.request('searchAddons:entities');
20140 return callback(this.collection);
20143 Controller.prototype.getStructure = function(data) {
20144 var form, i, item, items, row;
20145 this.data = data;
20146 form = [
20148 title: t.gettext('Custom Add-on search'),
20149 id: 'intro',
20150 children: [
20152 id: 'intro-text',
20153 type: 'markup',
20154 markup: t.sprintf(tr("Add custom add-on searches"), '<a href="#help/addons">' + tr('Add-ons help page') + '</a>')
20159 items = this.collection.toJSON();
20160 if (items.length === 0) {
20161 items.push(this.getBlank(items.length));
20163 for (i in items) {
20164 item = items[i];
20165 item.weight = i;
20166 row = this.getRow(item);
20167 form.push(row);
20169 form.push({
20170 id: 'add-another',
20171 "class": 'add-another-wrapper',
20172 children: [
20174 type: 'button',
20175 value: 'Add another',
20176 id: 'add-another'
20180 return form;
20183 Controller.prototype.saveCallback = function(formState, formView) {
20184 var i, item, items, ref;
20185 items = [];
20186 ref = formState.title;
20187 for (i in ref) {
20188 item = ref[i];
20189 items.push({
20190 title: formState.title[i],
20191 url: formState.url[i],
20192 media: formState.media[i],
20193 weight: formState.weight[i],
20194 id: formState.id[i]
20197 App.request("searchAddons:update:entities", items);
20198 return Kodi.execute("notification:show", t.gettext('Custom Addon search updated'));
20201 Controller.prototype.onReady = function() {
20202 var $ctx, self;
20203 self = this;
20204 $ctx = this.layout.regionContent.$el;
20205 $('.settings-form').addClass('settings-form-draggable');
20206 this.binds();
20207 if ($(window).width() > config.getLocal('largeBreakpoint')) {
20208 $('.form-groups', $ctx).sortable({
20209 draggable: ".draggable-row",
20210 onEnd: function(e) {
20211 return $('input[id^="form-edit-weight-"]', e.target).each(function(i, d) {
20212 return $(d).attr('value', i);
20217 $('#form-edit-add-another', $ctx).click(function(e) {
20218 var blank, formView, row;
20219 e.preventDefault();
20220 blank = self.getBlank($(".item-row").length);
20221 row = self.getRow(blank);
20222 formView = App.request("form:render:items", [row]);
20223 $(this).closest('.add-another-wrapper').before(formView.render().$el);
20224 return self.binds();
20226 return $('.restore-defaults', $ctx).on("click", (function(_this) {
20227 return function(e) {};
20228 })(this));
20231 Controller.prototype.getBlank = function(weight) {
20232 return {
20233 weight: weight,
20234 title: '',
20235 url: '',
20236 media: 'music'
20240 Controller.prototype.binds = function() {
20241 var $ctx;
20242 $ctx = $('.settings-form');
20243 $('.remove-item', $ctx).on("click", function(e) {
20244 return $(this).closest('.group-parent', $ctx).remove();
20246 return $.material.init();
20249 Controller.prototype.getRow = function(item) {
20250 var i;
20251 i = item.weight;
20252 return {
20253 id: 'item-' + item.weight,
20254 "class": 'item-row draggable-row',
20255 children: [
20257 id: 'title-' + i,
20258 name: 'title[]',
20259 type: 'textfield',
20260 title: 'Title',
20261 defaultValue: item.title
20262 }, {
20263 id: 'url-' + i,
20264 name: 'url[]',
20265 type: 'textfield',
20266 title: 'Url',
20267 defaultValue: item.url
20268 }, {
20269 id: 'media-' + i,
20270 name: 'media[]',
20271 type: 'select',
20272 title: 'Media',
20273 defaultValue: item.media,
20274 options: {
20275 music: 'Music',
20276 video: 'Video'
20278 }, {
20279 id: 'weight-' + i,
20280 name: 'weight[]',
20281 type: 'hidden',
20282 title: '',
20283 defaultValue: i
20284 }, {
20285 id: 'id-' + i,
20286 name: 'id[]',
20287 type: 'hidden',
20288 title: '',
20289 defaultValue: 'custom.addon.' + i
20290 }, {
20291 id: 'remove-' + i,
20292 type: 'markup',
20293 markup: '<span class="remove-item">&times;</span>'
20299 return Controller;
20301 })(App.SettingsApp.Show.Base.Controller);
20304 this.Kodi.module("SettingsApp.Show", function(Show, App, Backbone, Marionette, $, _) {
20305 Show.Layout = (function(superClass) {
20306 extend(Layout, superClass);
20308 function Layout() {
20309 return Layout.__super__.constructor.apply(this, arguments);
20312 Layout.prototype.className = "settings-page";
20314 return Layout;
20316 })(App.Views.LayoutWithSidebarFirstView);
20317 return Show.Sidebar = (function(superClass) {
20318 extend(Sidebar, superClass);
20320 function Sidebar() {
20321 return Sidebar.__super__.constructor.apply(this, arguments);
20324 Sidebar.prototype.className = "settings-sidebar";
20326 Sidebar.prototype.template = "apps/settings/show/settings_sidebar";
20328 Sidebar.prototype.tagName = "div";
20330 Sidebar.prototype.regions = {
20331 regionKodiNav: '.kodi-nav',
20332 regionLocalNav: '.local-nav'
20335 return Sidebar;
20337 })(App.Views.LayoutView);
20340 this.Kodi.module("Shell", function(Shell, App, Backbone, Marionette, $, _) {
20341 var API;
20342 Shell.Router = (function(superClass) {
20343 extend(Router, superClass);
20345 function Router() {
20346 return Router.__super__.constructor.apply(this, arguments);
20349 Router.prototype.appRoutes = {
20350 "": "homePage",
20351 "home": "homePage"
20354 return Router;
20356 })(App.Router.Base);
20357 API = {
20358 homePage: function() {
20359 var home;
20360 home = new Shell.HomepageLayout();
20361 App.regionContent.show(home);
20362 this.setFanart();
20363 App.vent.on("state:changed", (function(_this) {
20364 return function(state) {
20365 var stateObj;
20366 stateObj = App.request("state:current");
20367 if (stateObj.isPlayingItemChanged() && helpers.url.arg(0) === '') {
20368 return _this.setFanart();
20371 })(this));
20372 return App.listenTo(home, "destroy", (function(_this) {
20373 return function() {
20374 return App.execute("images:fanart:set", 'none');
20376 })(this));
20378 setFanart: function() {
20379 var playingItem, stateObj;
20380 stateObj = App.request("state:current");
20381 if (stateObj != null) {
20382 playingItem = stateObj.getPlaying('item');
20383 return App.execute("images:fanart:set", playingItem.fanart);
20384 } else {
20385 return App.execute("images:fanart:set");
20388 renderLayout: function() {
20389 var playlistState, shellLayout;
20390 shellLayout = new Shell.Layout();
20391 App.root.show(shellLayout);
20392 App.addRegions(shellLayout.regions);
20393 App.execute("loading:show:page");
20394 this.setAppTitle();
20395 playlistState = config.get('app', 'shell:playlist:state', 'open');
20396 if (playlistState === 'closed') {
20397 this.alterRegionClasses('add', "shell-playlist-closed");
20399 this.configUpdated();
20400 App.vent.on("config:local:updated", (function(_this) {
20401 return function(data) {
20402 return _this.configUpdated();
20404 })(this));
20405 App.vent.on("filter:filtering:start", (function(_this) {
20406 return function() {
20407 return _this.alterRegionClasses('add', "filters-loading");
20409 })(this));
20410 App.vent.on("filter:filtering:stop", (function(_this) {
20411 return function() {
20412 return _this.alterRegionClasses('remove', "filters-loading");
20414 })(this));
20415 App.listenTo(shellLayout, "shell:playlist:toggle", (function(_this) {
20416 return function(child, args) {
20417 var state;
20418 playlistState = config.get('app', 'shell:playlist:state', 'open');
20419 state = playlistState === 'open' ? 'closed' : 'open';
20420 config.set('app', 'shell:playlist:state', state);
20421 return _this.alterRegionClasses('toggle', "shell-playlist-closed");
20423 })(this));
20424 App.listenTo(shellLayout, "shell:reconnect", (function(_this) {
20425 return function() {
20426 return App.execute('shell:reconnect');
20428 })(this));
20429 this.bindListenersContextMenu(shellLayout);
20430 return this.bindListenersSelectedMenu(shellLayout);
20432 alterRegionClasses: function(op, classes, region) {
20433 var $body, action;
20434 if (region == null) {
20435 region = 'root';
20437 $body = App.getRegion(region).$el;
20438 action = op + "Class";
20439 return $body[action](classes);
20441 configUpdated: function() {
20442 var disableThumbs, disableThumbsClassOp;
20443 disableThumbs = config.getLocal('disableThumbs', false);
20444 disableThumbsClassOp = disableThumbs === true ? 'add' : 'remove';
20445 this.alterRegionClasses(disableThumbsClassOp, 'disable-thumbs');
20446 return this.setAppTitle();
20448 setAppTitle: function() {
20449 var settingsController;
20450 App.getRegion('regionTitle').$el.text('');
20451 if (config.getLocal('showDeviceName', false) === true) {
20452 settingsController = App.request("command:kodi:controller", 'auto', 'Settings');
20453 return settingsController.getSettingValue('services.devicename', function(title) {
20454 return App.getRegion('regionTitle').$el.text(title);
20458 bindListenersContextMenu: function(shellLayout) {
20459 App.listenTo(shellLayout, "shell:audio:scan", function() {
20460 return App.request("command:kodi:controller", 'auto', 'AudioLibrary').scan();
20462 App.listenTo(shellLayout, "shell:video:scan", function() {
20463 return App.request("command:kodi:controller", 'auto', 'VideoLibrary').scan();
20465 App.listenTo(shellLayout, "shell:goto:lab", function() {
20466 return App.navigate("#lab", {
20467 trigger: true
20470 App.listenTo(shellLayout, "shell:about", function() {
20471 return App.navigate("#help", {
20472 trigger: true
20475 return App.listenTo(shellLayout, "shell:send:input", function() {
20476 return App.execute("input:textbox", '');
20479 bindListenersSelectedMenu: function(shellLayout) {
20480 App.listenTo(shellLayout, "shell:selected:play", function() {
20481 return App.execute("selected:action:play");
20483 App.listenTo(shellLayout, "shell:selected:add", function() {
20484 return App.execute("selected:action:add");
20486 return App.listenTo(shellLayout, "shell:selected:localadd", function() {
20487 return App.execute("selected:action:localadd");
20491 App.addInitializer(function() {
20492 return App.commands.setHandler("shell:view:ready", function() {
20493 API.renderLayout();
20494 new Shell.Router({
20495 controller: API
20497 App.vent.trigger("shell:ready");
20498 return App.commands.setHandler("body:state", function(op, state) {
20499 return API.alterRegionClasses(op, state);
20503 App.commands.setHandler('shell:reconnect', function() {
20504 API.alterRegionClasses('add', 'reconnecting');
20505 return helpers.connection.reconnect(function() {
20506 API.alterRegionClasses('remove', 'lost-connection');
20507 return API.alterRegionClasses('remove', 'reconnecting');
20510 return App.commands.setHandler('shell:disconnect', function() {
20511 API.alterRegionClasses('add', 'lost-connection');
20512 API.alterRegionClasses('remove', 'reconnecting');
20513 return helpers.connection.disconnect(function() {});
20517 this.Kodi.module("Shell", function(Shell, App, Backbone, Marionette, $, _) {
20518 Shell.Layout = (function(superClass) {
20519 extend(Layout, superClass);
20521 function Layout() {
20522 return Layout.__super__.constructor.apply(this, arguments);
20525 Layout.prototype.template = "apps/shell/show/shell";
20527 Layout.prototype.regions = {
20528 regionNav: '#nav-bar',
20529 regionContent: '#content',
20530 regionSidebarFirst: '#sidebar-first',
20531 regionPlaylist: '#playlist-bar',
20532 regionTitle: '#page-title .title',
20533 regionTitleContext: '#page-title .context',
20534 regionFanart: '#fanart',
20535 regionPlayerKodi: '#player-kodi',
20536 regionPlayerLocal: '#player-local',
20537 regionModal: '#modal-window',
20538 regionModalTitle: '.modal-title',
20539 regionModalBody: '.modal-body',
20540 regionModalFooter: '.modal-footer',
20541 regionRemote: '#remote',
20542 regionSearch: '#search-region',
20543 regionTitle: '#page-title .title',
20544 regionOffscreen: '#offscreen'
20547 Layout.prototype.triggers = {
20548 "click .playlist-toggle-open": "shell:playlist:toggle",
20549 "click .audio-scan": "shell:audio:scan",
20550 "click .video-scan": "shell:video:scan",
20551 "click .goto-lab": "shell:goto:lab",
20552 "click .send-input": "shell:send:input",
20553 "click .about": "shell:about",
20554 "click .selected-play": "shell:selected:play",
20555 "click .selected-add": "shell:selected:add",
20556 "click .selected-localadd": "shell:selected:localadd",
20557 "click .reconnect": "shell:reconnect"
20560 Layout.prototype.events = {
20561 "click .player-menu > li": "closePlayerMenu"
20564 Layout.prototype.closePlayerMenu = function() {
20565 return App.execute("ui:playermenu", 'close');
20568 return Layout;
20570 })(App.Views.LayoutView);
20571 Shell.HomepageLayout = (function(superClass) {
20572 extend(HomepageLayout, superClass);
20574 function HomepageLayout() {
20575 return HomepageLayout.__super__.constructor.apply(this, arguments);
20578 HomepageLayout.prototype.template = "apps/shell/show/homepage";
20580 return HomepageLayout;
20582 })(Backbone.Marionette.LayoutView);
20583 return App.execute("shell:view:ready");
20586 this.Kodi.module("SongApp.Edit", function(Edit, App, Backbone, Marionette, $, _) {
20587 return Edit.Controller = (function(superClass) {
20588 extend(Controller, superClass);
20590 function Controller() {
20591 return Controller.__super__.constructor.apply(this, arguments);
20594 Controller.prototype.initialize = function() {
20595 var form, options;
20596 this.model = this.getOption('model');
20597 options = {
20598 titleHtml: '<span>' + tr('Edit') + '</span>' + this.model.escape('title'),
20599 form: this.getStructure(),
20600 formState: this.model.attributes,
20601 config: {
20602 attributes: {
20603 "class": 'edit-form'
20605 editForm: true,
20606 tabs: true,
20607 callback: (function(_this) {
20608 return function(data, formView) {
20609 return _this.saveCallback(data, formView);
20611 })(this)
20614 return form = App.request("form:popup:wrapper", options);
20617 Controller.prototype.getStructure = function() {
20618 return [
20620 title: 'General',
20621 id: 'general',
20622 children: [
20624 id: 'title',
20625 title: tr('Title'),
20626 type: 'textfield'
20627 }, {
20628 id: 'artist',
20629 title: tr('Artist'),
20630 type: 'textfield',
20631 format: 'array.string'
20632 }, {
20633 id: 'albumartist',
20634 title: tr('Album artist'),
20635 type: 'textfield',
20636 format: 'array.string'
20637 }, {
20638 id: 'album',
20639 title: tr('Album'),
20640 type: 'textfield'
20641 }, {
20642 id: 'year',
20643 title: tr('Year'),
20644 type: 'number',
20645 format: 'integer',
20646 attributes: {
20647 "class": 'half-width',
20648 step: 1,
20649 min: 1000,
20650 max: 9999
20652 }, {
20653 id: 'rating',
20654 title: tr('Rating'),
20655 type: 'number',
20656 format: 'float',
20657 attributes: {
20658 "class": 'half-width',
20659 step: 0.1,
20660 min: 0,
20661 max: 10
20663 suffix: '<div class="clearfix"></div>'
20664 }, {
20665 id: 'track',
20666 title: tr('Track'),
20667 type: 'textfield',
20668 format: 'integer',
20669 attributes: {
20670 "class": 'half-width'
20672 }, {
20673 id: 'disc',
20674 title: tr('Disc'),
20675 type: 'textfield',
20676 format: 'integer',
20677 attributes: {
20678 "class": 'half-width'
20680 suffix: '<div class="clearfix"></div>'
20683 }, {
20684 title: 'Tags',
20685 id: 'tags',
20686 children: [
20688 id: 'genre',
20689 title: tr('Genres'),
20690 type: 'textfield',
20691 format: 'array.string'
20694 }, {
20695 title: 'Information',
20696 id: 'info',
20697 children: [
20699 id: 'file',
20700 title: tr('File path'),
20701 type: 'textarea',
20702 attributes: {
20703 disabled: 'disabled',
20704 cols: 5
20706 format: 'prevent.submit'
20713 Controller.prototype.saveCallback = function(data, formView) {
20714 var controller;
20715 controller = App.request("command:kodi:controller", 'audio', 'AudioLibrary');
20716 return controller.setSongDetails(this.model.get('id'), data, (function(_this) {
20717 return function() {
20718 Kodi.vent.trigger('entity:kodi:update', _this.model.get('uid'));
20719 return Kodi.execute("notification:show", t.sprintf(tr("Updated %1$s details"), 'song'));
20721 })(this));
20724 return Controller;
20726 })(App.Controllers.Base);
20729 this.Kodi.module("SongApp.List", function(List, App, Backbone, Marionette, $, _) {
20730 var API;
20731 API = {
20732 getSongsView: function(songs, verbose) {
20733 if (verbose == null) {
20734 verbose = false;
20736 this.songsView = new List.Songs({
20737 collection: songs,
20738 verbose: verbose
20740 App.listenTo(this.songsView, 'childview:song:play', (function(_this) {
20741 return function(list, item) {
20742 return _this.playSong(item.model.get('songid'));
20744 })(this));
20745 App.listenTo(this.songsView, 'childview:song:add', (function(_this) {
20746 return function(list, item) {
20747 return _this.addSong(item.model.get('songid'));
20749 })(this));
20750 App.listenTo(this.songsView, 'childview:song:localadd', (function(_this) {
20751 return function(list, item) {
20752 return _this.localAddSong(item.model.get('songid'));
20754 })(this));
20755 App.listenTo(this.songsView, 'childview:song:localplay', (function(_this) {
20756 return function(list, item) {
20757 return _this.localPlaySong(item.model.get('songid'));
20759 })(this));
20760 App.listenTo(this.songsView, 'childview:song:download', (function(_this) {
20761 return function(list, item) {
20762 return _this.downloadSong(item.model);
20764 })(this));
20765 App.listenTo(this.songsView, 'childview:song:musicvideo', (function(_this) {
20766 return function(list, item) {
20767 return App.execute("youtube:search:popup", item.model.get('label') + ' ' + item.model.get('artist'));
20769 })(this));
20770 App.listenTo(this.songsView, 'childview:song:edit', function(parent, item) {
20771 return App.execute('song:edit', item.model);
20773 App.listenTo(this.songsView, "show", function() {
20774 return App.vent.trigger("state:content:updated");
20776 return this.songsView;
20778 playSong: function(songId) {
20779 return App.execute("command:audio:play", 'songid', songId);
20781 addSong: function(songId) {
20782 return App.execute("command:audio:add", 'songid', songId);
20784 localAddSong: function(songId) {
20785 return App.execute("localplaylist:addentity", 'songid', songId);
20787 localPlaySong: function(songId) {
20788 var localPlaylist;
20789 localPlaylist = App.request("command:local:controller", 'audio', 'PlayList');
20790 return localPlaylist.play('songid', songId);
20792 downloadSong: function(model) {
20793 var files;
20794 files = App.request("command:kodi:controller", 'video', 'Files');
20795 return files.downloadFile(model.get('file'));
20798 return App.reqres.setHandler("song:list:view", function(songs, verbose) {
20799 if (verbose == null) {
20800 verbose = false;
20802 return API.getSongsView(songs, verbose);
20806 this.Kodi.module("SongApp.List", function(List, App, Backbone, Marionette, $, _) {
20807 List.Song = (function(superClass) {
20808 extend(Song, superClass);
20810 function Song() {
20811 return Song.__super__.constructor.apply(this, arguments);
20814 Song.prototype.template = 'apps/song/list/song';
20816 Song.prototype.tagName = "tr";
20818 Song.prototype.initialize = function() {
20819 var duration, menu;
20820 Song.__super__.initialize.apply(this, arguments);
20821 if (this.model) {
20822 duration = helpers.global.secToTime(this.model.get('duration'));
20823 menu = {
20824 'song-localadd': 'Add to playlist',
20825 'song-download': 'Download song',
20826 'song-localplay': 'Play in browser',
20827 'song-musicvideo': 'Music video',
20828 'divider': '',
20829 'song-edit': 'Edit'
20831 return this.model.set({
20832 displayDuration: helpers.global.formatTime(duration),
20833 menu: menu
20838 Song.prototype.triggers = {
20839 "click .play": "song:play",
20840 "dblclick .song-title": "song:play",
20841 "click .add": "song:add",
20842 "click .song-localadd": "song:localadd",
20843 "click .song-download": "song:download",
20844 "click .song-localplay": "song:localplay",
20845 "click .song-musicvideo": "song:musicvideo",
20846 "click .song-remove": "song:remove",
20847 "click .song-edit": "song:edit"
20850 Song.prototype.events = {
20851 "click .dropdown > i": "populateModelMenu",
20852 "click .thumbs": "toggleThumbs",
20853 "click": "toggleSelect"
20856 Song.prototype.modelEvents = {
20857 'change': 'render'
20860 Song.prototype.toggleThumbs = function() {
20861 App.request("thumbsup:toggle:entity", this.model);
20862 this.$el.toggleClass('thumbs-up');
20863 return $('.plitem-' + this.model.get('type') + '-' + this.model.get('id')).toggleClass('thumbs-up');
20866 Song.prototype.attributes = function() {
20867 var classes;
20868 if (this.model) {
20869 classes = ['song', 'table-row', 'can-play', 'item-' + this.model.get('uid')];
20870 if (App.request("thumbsup:check", this.model)) {
20871 classes.push('thumbs-up');
20873 return {
20874 'class': classes.join(' '),
20875 'data-id': this.model.id
20880 Song.prototype.onShow = function() {
20881 return this.menuBlur();
20884 Song.prototype.onRender = function() {
20885 return this.$el.data('model', this.model);
20888 return Song;
20890 })(App.Views.ItemView);
20891 return List.Songs = (function(superClass) {
20892 extend(Songs, superClass);
20894 function Songs() {
20895 return Songs.__super__.constructor.apply(this, arguments);
20898 Songs.prototype.childView = List.Song;
20900 Songs.prototype.placeHolderViewName = 'SongViewPlaceholder';
20902 Songs.prototype.cardSelector = '.song';
20904 Songs.prototype.preload = 40;
20906 Songs.prototype.tagName = "table";
20908 Songs.prototype.attributes = function() {
20909 var verbose;
20910 verbose = this.options.verbose ? 'verbose' : 'basic';
20911 return {
20912 "class": 'songs-table table table-hover ' + verbose
20916 return Songs;
20918 })(App.Views.VirtualListView);
20921 this.Kodi.module("SongApp", function(SongApp, App, Backbone, Marionette, $, _) {
20922 return App.commands.setHandler('song:edit', function(model) {
20923 var loadedModel;
20924 loadedModel = App.request("song:entity", model.get('songid'));
20925 return App.execute("when:entity:fetched", loadedModel, (function(_this) {
20926 return function() {
20927 return new SongApp.Edit.Controller({
20928 model: loadedModel
20931 })(this));
20935 this.Kodi.module("StateApp", function(StateApp, App, Backbone, Marionette, $, _) {
20936 return StateApp.Base = (function(superClass) {
20937 extend(Base, superClass);
20939 function Base() {
20940 return Base.__super__.constructor.apply(this, arguments);
20943 Base.prototype.instanceSettings = {};
20945 Base.prototype.state = {
20946 player: 'kodi',
20947 media: 'audio',
20948 volume: 50,
20949 lastVolume: 50,
20950 muted: false,
20951 shuffled: false,
20952 repeat: 'off',
20953 version: {
20954 major: 15,
20955 minor: 0
20959 Base.prototype.playing = {
20960 playing: false,
20961 paused: false,
20962 playState: '',
20963 item: {},
20964 media: 'audio',
20965 itemChanged: false,
20966 latPlaying: '',
20967 canrepeat: true,
20968 canseek: true,
20969 canshuffle: true,
20970 partymode: false,
20971 percentage: 0,
20972 playlistid: 0,
20973 position: 0,
20974 speed: 0,
20975 time: {
20976 hours: 0,
20977 milliseconds: 0,
20978 minutes: 0,
20979 seconds: 0
20981 totaltime: {
20982 hours: 0,
20983 milliseconds: 0,
20984 minutes: 0,
20985 seconds: 0
20989 Base.prototype.defaultPlayingItem = {
20990 thumbnail: '',
20991 fanart: '',
20992 id: 0,
20993 songid: 0,
20994 episodeid: 0,
20995 album: '',
20996 albumid: '',
20997 duration: 0,
20998 type: 'song'
21001 Base.prototype.getState = function(key) {
21002 if (key == null) {
21003 key = 'all';
21005 if (key === 'all') {
21006 return this.state;
21007 } else {
21008 return this.state[key];
21012 Base.prototype.setState = function(key, value) {
21013 return this.state[key] = value;
21016 Base.prototype.getPlaying = function(key) {
21017 var ret;
21018 if (key == null) {
21019 key = 'all';
21021 ret = this.playing;
21022 if (ret.item.length === 0) {
21023 ret.item = this.defaultPlayingItem;
21025 if (key === 'all') {
21026 return this.playing;
21027 } else {
21028 return this.playing[key];
21032 Base.prototype.setPlaying = function(key, value) {
21033 return this.playing[key] = value;
21036 Base.prototype.isPlaying = function(media) {
21037 if (media == null) {
21038 media = 'auto';
21040 if (media === 'auto') {
21041 return this.getPlaying('playing');
21042 } else {
21043 return media === this.getState('media') && this.getPlaying('playing');
21047 Base.prototype.isPlayingItemChanged = function() {
21048 return this.getPlaying('itemChanged');
21051 Base.prototype.doCallback = function(callback, resp) {
21052 if (typeof callback === 'function') {
21053 return callback(resp);
21057 Base.prototype.getCurrentState = function(callback) {};
21059 Base.prototype.getCachedState = function() {
21060 return {
21061 state: this.state,
21062 playing: this.playing
21066 Base.prototype.setPlayer = function(player) {
21067 var $body;
21068 if (player == null) {
21069 player = 'kodi';
21071 $body = App.getRegion('root').$el;
21072 $body.removeClassStartsWith('active-player-').addClass('active-player-' + player);
21073 config.set('state', 'lastplayer', player);
21074 return config.set('app', 'state:lastplayer', player);
21077 Base.prototype.getPlayer = function() {
21078 var $body, player;
21079 player = 'kodi';
21080 $body = App.getRegion('root').$el;
21081 if ($body.hasClass('active-player-local')) {
21082 player = 'local';
21084 return player;
21087 return Base;
21089 })(Marionette.Object);
21092 this.Kodi.module("StateApp.Kodi", function(StateApp, App, Backbone, Marionette, $, _) {
21093 return StateApp.State = (function(superClass) {
21094 extend(State, superClass);
21096 function State() {
21097 return State.__super__.constructor.apply(this, arguments);
21100 State.prototype.playerController = {};
21102 State.prototype.applicationController = {};
21104 State.prototype.playlistApi = {};
21106 State.prototype.initialize = function() {
21107 this.state = _.extend({}, this.state);
21108 this.playing = _.extend({}, this.playing);
21109 this.setState('player', 'kodi');
21110 this.playerController = App.request("command:kodi:controller", 'auto', 'Player');
21111 this.applicationController = App.request("command:kodi:controller", 'auto', 'Application');
21112 this.playlistApi = App.request("playlist:kodi:entity:api");
21113 App.reqres.setHandler("state:kodi:update", (function(_this) {
21114 return function(callback) {
21115 return _this.getCurrentState(callback);
21117 })(this));
21118 return App.reqres.setHandler("state:kodi:get", (function(_this) {
21119 return function() {
21120 return _this.getCachedState();
21122 })(this));
21125 State.prototype.getCurrentState = function(callback) {
21126 return this.applicationController.getProperties((function(_this) {
21127 return function(properties) {
21128 _this.setState('volume', properties.volume);
21129 _this.setState('muted', properties.muted);
21130 _this.setState('version', properties.version);
21131 App.reqres.setHandler('player:kodi:timer', 'stop');
21132 return _this.playerController.getPlaying(function(playing) {
21133 var autoMap, key, len, media, n;
21134 if (playing) {
21135 _this.setPlaying('playing', true);
21136 _this.setPlaying('paused', playing.properties.speed === 0);
21137 _this.setPlaying('playState', (playing.properties.speed === 0 ? 'paused' : 'playing'));
21138 autoMap = ['canrepeat', 'canseek', 'canshuffle', 'partymode', 'percentage', 'playlistid', 'position', 'speed', 'time', 'totaltime'];
21139 for (n = 0, len = autoMap.length; n < len; n++) {
21140 key = autoMap[n];
21141 if (playing.properties[key] != null) {
21142 _this.setPlaying(key, playing.properties[key]);
21145 _this.setState('shuffled', playing.properties.shuffled);
21146 _this.setState('repeat', playing.properties.repeat);
21147 media = _this.playerController.playerIdToName(playing.properties.playlistid);
21148 if (media) {
21149 _this.setState('media', media);
21151 if (playing.item.file !== _this.getPlaying('lastPlaying')) {
21152 _this.setPlaying('itemChanged', true);
21153 App.vent.trigger("state:kodi:itemchanged", _this.getCachedState());
21154 } else {
21155 _this.setPlaying('itemChanged', false);
21157 _this.setPlaying('lastPlaying', playing.item.file);
21158 _this.setPlaying('item', _this.parseItem(playing.item, {
21159 media: media,
21160 playlistid: playing.properties.playlistid
21161 }));
21162 App.reqres.setHandler('player:kodi:timer', 'start');
21163 } else {
21164 _this.setPlaying('playing', false);
21165 _this.setPlaying('paused', false);
21166 _this.setPlaying('item', _this.defaultPlayingItem);
21167 _this.setPlaying('lstPlaying', '');
21169 App.vent.trigger("state:kodi:changed", _this.getCachedState());
21170 App.vent.trigger("state:changed");
21171 return _this.doCallback(callback, _this.getCachedState());
21174 })(this));
21177 State.prototype.parseItem = function(model, options) {
21178 model = this.playlistApi.parseItem(model, options);
21179 model = App.request("images:path:entity", model);
21180 model.url = helpers.url.get(model.type, model.id);
21181 model.url = helpers.url.playlistUrl(model);
21182 return model;
21185 return State;
21187 })(App.StateApp.Base);
21190 this.Kodi.module("StateApp.Kodi", function(StateApp, App, Backbone, Marionette, $, _) {
21191 return StateApp.Notifications = (function(superClass) {
21192 extend(Notifications, superClass);
21194 function Notifications() {
21195 return Notifications.__super__.constructor.apply(this, arguments);
21198 Notifications.prototype.wsActive = false;
21200 Notifications.prototype.wsObj = {};
21202 Notifications.prototype.getConnection = function() {
21203 var host, protocol, socketHost, socketPath, socketPort;
21204 host = config.getLocal('socketsHost');
21205 socketPath = config.getLocal('jsonRpcEndpoint');
21206 socketPort = config.getLocal('socketsPort');
21207 socketHost = host === 'auto' ? location.hostname : host;
21208 protocol = helpers.url.isSecureProtocol() ? "wss" : "ws";
21209 return protocol + "://" + socketHost + ":" + socketPort + "/" + socketPath + "?kodi";
21212 Notifications.prototype.initialize = function() {
21213 var msg, ws;
21214 if (window.WebSocket) {
21215 ws = new WebSocket(this.getConnection());
21216 ws.onopen = (function(_this) {
21217 return function(e) {
21218 helpers.debug.msg("Websockets Active");
21219 _this.wsActive = true;
21220 return App.vent.trigger("sockets:available");
21222 })(this);
21223 ws.onerror = (function(_this) {
21224 return function(resp) {
21225 helpers.debug.msg(_this.socketConnectionErrorMsg(), "warning", resp);
21226 _this.wsActive = false;
21227 return App.vent.trigger("sockets:unavailable");
21229 })(this);
21230 ws.onmessage = (function(_this) {
21231 return function(resp) {
21232 return _this.messageReceived(resp);
21234 })(this);
21235 ws.onclose = (function(_this) {
21236 return function(resp) {
21237 helpers.debug.msg("Websockets Closed", "warning", resp);
21238 _this.wsActive = false;
21239 App.execute("notification:show", tr("Lost websocket connection"));
21240 return setTimeout(function() {
21241 App.execute("notification:show", tr("Attempting websockets reconnect"));
21242 return App.execute('state:ws:init');
21243 }, 60000);
21245 })(this);
21246 } else {
21247 msg = "Your browser doesn't support websockets! Get with the times and update your browser.";
21248 helpers.debug.msg(t.gettext(msg), "warning", resp);
21249 App.vent.trigger("sockets:unavailable");
21251 return App.reqres.setHandler("sockets:active", (function(_this) {
21252 return function() {
21253 return _this.wsActive;
21255 })(this));
21258 Notifications.prototype.parseResponse = function(resp) {
21259 return jQuery.parseJSON(resp.data);
21262 Notifications.prototype.messageReceived = function(resp) {
21263 var data;
21264 data = this.parseResponse(resp);
21265 return this.onMessage(data);
21268 Notifications.prototype.socketConnectionErrorMsg = function() {
21269 var msg;
21270 msg = "Failed to connect to websockets";
21271 return t.gettext(msg);
21274 Notifications.prototype.refreshStateNow = function(callback) {
21275 App.vent.trigger("state:kodi:changed", this.getCachedState());
21276 return setTimeout(((function(_this) {
21277 return function() {
21278 return App.request("state:kodi:update", function(state) {
21279 if (callback) {
21280 return callback(state);
21284 })(this)), 1000);
21287 Notifications.prototype.onLibraryUpdate = function(data) {
21288 var model;
21289 model = data.params.data.item ? data.params.data.item : data.params.data;
21290 model.uid = helpers.entities.createUid(model, model.type);
21291 App.vent.trigger('entity:kodi:update', model.uid);
21292 if (model.type === 'episode') {
21293 clearTimeout(App.episodeRecheckTimeout);
21294 return App.episodeRecheckTimeout = setTimeout(function() {
21295 return App.request('episode:entity', model.id, {
21296 success: function(epModel) {
21297 return App.vent.trigger('entity:kodi:update', 'tvshow-' + epModel.get('tvshowid'));
21300 }, 2000);
21304 Notifications.prototype.onMessage = function(data) {
21305 var wait;
21306 switch (data.method) {
21307 case 'Player.OnPlay':
21308 this.setPlaying('paused', false);
21309 this.setPlaying('playState', 'playing');
21310 App.execute("player:kodi:timer", 'start');
21311 this.refreshStateNow();
21312 break;
21313 case 'Player.OnResume':
21314 this.setPlaying('paused', false);
21315 this.setPlaying('playState', 'playing');
21316 App.execute("player:kodi:timer", 'start');
21317 this.refreshStateNow();
21318 break;
21319 case 'Player.OnStop':
21320 this.setPlaying('playing', false);
21321 App.execute("player:kodi:timer", 'stop');
21322 this.refreshStateNow();
21323 break;
21324 case 'Player.OnPropertyChanged':
21325 this.refreshStateNow();
21326 break;
21327 case 'Player.OnPause':
21328 this.setPlaying('paused', true);
21329 this.setPlaying('playState', 'paused');
21330 App.execute("player:kodi:timer", 'stop');
21331 this.refreshStateNow();
21332 break;
21333 case 'Player.OnSeek':
21334 App.execute("player:kodi:timer", 'stop');
21335 this.refreshStateNow(function() {
21336 return App.execute("player:kodi:timer", 'start');
21338 break;
21339 case 'Playlist.OnClear':
21340 case 'Playlist.OnAdd':
21341 case 'Playlist.OnRemove':
21342 clearTimeout(App.playlistUpdateTimeout);
21343 App.playlistUpdateTimeout = setTimeout((function(_this) {
21344 return function(e) {
21345 var playerController;
21346 playerController = App.request("command:kodi:controller", 'auto', 'Player');
21347 App.execute("playlist:refresh", 'kodi', playerController.playerIdToName(data.params.data.playlistid));
21348 return _this.refreshStateNow();
21350 })(this), 500);
21351 break;
21352 case 'Application.OnVolumeChanged':
21353 App.request("state:kodi").getCurrentState();
21354 break;
21355 case 'VideoLibrary.OnScanStarted':
21356 App.execute("notification:show", t.gettext("Video library scan started"));
21357 break;
21358 case 'VideoLibrary.OnScanFinished':
21359 App.execute("notification:show", t.gettext("Video library scan complete"));
21360 Backbone.fetchCache.clearItem('MovieCollection');
21361 Backbone.fetchCache.clearItem('TVShowCollection');
21362 break;
21363 case 'AudioLibrary.OnScanStarted':
21364 App.execute("notification:show", t.gettext("Audio library scan started"));
21365 break;
21366 case 'AudioLibrary.OnScanFinished':
21367 App.execute("notification:show", t.gettext("Audio library scan complete"));
21368 Backbone.fetchCache.clearItem('AlbumCollection');
21369 Backbone.fetchCache.clearItem('ArtistCollection');
21370 break;
21371 case 'AudioLibrary.OnCleanStarted':
21372 App.execute("notification:show", t.gettext("Audio library clean started"));
21373 break;
21374 case 'AudioLibrary.OnCleanFinished':
21375 App.execute("notification:show", t.gettext("Audio library clean finished"));
21376 break;
21377 case 'VideoLibrary.OnCleanStarted':
21378 App.execute("notification:show", t.gettext("Video library clean started"));
21379 break;
21380 case 'VideoLibrary.OnCleanFinished':
21381 App.execute("notification:show", t.gettext("Video library clean finished"));
21382 break;
21383 case 'AudioLibrary.OnUpdate':
21384 case 'VideoLibrary.OnUpdate':
21385 this.onLibraryUpdate(data);
21386 break;
21387 case 'Input.OnInputRequested':
21388 App.execute("input:textbox", '');
21389 wait = 60;
21390 App.inputTimeout = setTimeout((function() {
21391 var msg, wotd;
21392 wotd = '<a href="http://goo.gl/PGE7wg" target="_blank">word of the day</a>';
21393 msg = t.sprintf(tr("%1$d seconds ago, an input dialog opened in Kodi and it is still open! To prevent " + "a mainframe implosion, you should probably give me some text. I don't really care what it " + "is at this point, why not be creative? Do you have a %2$s? I won't tell..."), wait, wotd);
21394 App.execute("input:textbox", msg);
21395 }), 1000 * wait);
21396 break;
21397 case 'Input.OnInputFinished':
21398 clearTimeout(App.inputTimeout);
21399 App.execute("input:textbox:close");
21400 break;
21401 case 'System.OnQuit':
21402 App.execute("notification:show", t.gettext("Kodi has quit"));
21403 App.execute("shell:disconnect");
21404 break;
21405 case 'System.OnWake':
21406 case 'System.OnRestart':
21407 App.execute("shell:reconnect");
21408 break;
21412 return Notifications;
21414 })(App.StateApp.Base);
21417 this.Kodi.module("StateApp.Kodi", function(StateApp, App, Backbone, Marionette, $, _) {
21418 return StateApp.Polling = (function(superClass) {
21419 extend(Polling, superClass);
21421 function Polling() {
21422 return Polling.__super__.constructor.apply(this, arguments);
21425 Polling.prototype.commander = {};
21427 Polling.prototype.checkInterval = 10000;
21429 Polling.prototype.currentInterval = '';
21431 Polling.prototype.timeoutObj = {};
21433 Polling.prototype.failures = 0;
21435 Polling.prototype.maxFailures = 100;
21437 Polling.prototype.initialize = function() {
21438 var interval;
21439 interval = config.getLocal('pollInterval');
21440 this.checkInterval = parseInt(interval);
21441 return this.currentInterval = this.checkInterval;
21444 Polling.prototype.startPolling = function() {
21445 return this.update();
21448 Polling.prototype.updateState = function() {
21449 var stateObj;
21450 stateObj = App.request("state:kodi");
21451 return stateObj.getCurrentState();
21454 Polling.prototype.update = function() {
21455 if (config.getLocal('connected', true) === false) {
21456 return;
21458 if (App.kodiPolling.failures < App.kodiPolling.maxFailures) {
21459 App.kodiPolling.updateState();
21460 return App.kodiPolling.timeout = setTimeout(App.kodiPolling.ping, App.kodiPolling.currentInterval);
21461 } else {
21462 App.execute("notification:show", t.gettext("Unable to communicate with Kodi in a long time. I think it's dead Jim!"));
21463 return App.execute("shell:disconnect");
21467 Polling.prototype.ping = function() {
21468 var commander;
21469 commander = App.request("command:kodi:controller", 'auto', 'Commander');
21470 commander.setOptions({
21471 timeout: 5000,
21472 error: function() {
21473 return App.kodiPolling.failure();
21476 commander.onError = function() {};
21477 return commander.sendCommand('Ping', [], function() {
21478 return App.kodiPolling.alive();
21482 Polling.prototype.alive = function() {
21483 App.kodiPolling.failures = 0;
21484 App.kodiPolling.currentInterval = App.kodiPolling.checkInterval;
21485 return App.kodiPolling.update();
21488 Polling.prototype.failure = function() {
21489 App.kodiPolling.failures++;
21490 if (App.kodiPolling.failures > 10) {
21491 App.kodiPolling.currentInterval = App.kodiPolling.checkInterval * 5;
21493 if (App.kodiPolling.failures > 20) {
21494 App.kodiPolling.currentInterval = App.kodiPolling.checkInterval * 10;
21496 if (App.kodiPolling.failures > 30) {
21497 App.kodiPolling.currentInterval = App.kodiPolling.checkInterval * 30;
21499 return App.kodiPolling.update();
21502 return Polling;
21504 })(App.StateApp.Base);
21507 this.Kodi.module("StateApp.Local", function(StateApp, App, Backbone, Marionette, $, _) {
21508 return StateApp.State = (function(superClass) {
21509 extend(State, superClass);
21511 function State() {
21512 return State.__super__.constructor.apply(this, arguments);
21515 State.prototype.initialize = function() {
21516 this.state = _.extend({}, this.state);
21517 this.playing = _.extend({}, this.playing);
21518 this.setState('player', 'local');
21519 this.setState('currentPlaybackId', 'browser-none');
21520 this.setState('localPlay', false);
21521 App.reqres.setHandler("state:local:update", (function(_this) {
21522 return function(callback) {
21523 return _this.getCurrentState(callback);
21525 })(this));
21526 return App.reqres.setHandler("state:local:get", (function(_this) {
21527 return function() {
21528 return _this.getCachedState();
21530 })(this));
21533 State.prototype.getCurrentState = function(callback) {
21534 return this.doCallback(callback, this.getCachedState());
21537 return State;
21539 })(App.StateApp.Base);
21542 this.Kodi.module("StateApp", function(StateApp, App, Backbone, Marionette, $, _) {
21543 var API;
21544 API = {
21545 setState: function(player) {
21546 if (player == null) {
21547 player = 'kodi';
21549 this.setBodyClasses(player);
21550 this.setPlayingContent(player);
21551 this.setPlayerPlaying(player);
21552 this.setAppProperties(player);
21553 return this.setTitle(player);
21555 playerClass: function(className, player) {
21556 return player + '-' + className;
21558 setBodyClasses: function(player) {
21559 var $body, c, len, n, newClasses, results1, stateObj;
21560 stateObj = App.request("state:" + player);
21561 $body = App.getRegion('root').$el;
21562 $body.removeClassStartsWith(player + '-');
21563 newClasses = [];
21564 newClasses.push('shuffled-' + (stateObj.getState('shuffled') ? 'on' : 'off'));
21565 newClasses.push('partymode-' + (stateObj.getPlaying('partymode') ? 'on' : 'off'));
21566 newClasses.push('mute-' + (stateObj.getState('muted') ? 'on' : 'off'));
21567 newClasses.push('repeat-' + stateObj.getState('repeat'));
21568 newClasses.push('media-' + stateObj.getState('media'));
21569 if (stateObj.isPlaying()) {
21570 newClasses.push(stateObj.getPlaying('playState'));
21571 } else {
21572 newClasses.push('not-playing');
21574 results1 = [];
21575 for (n = 0, len = newClasses.length; n < len; n++) {
21576 c = newClasses[n];
21577 results1.push($body.addClass(this.playerClass(c, player)));
21579 return results1;
21581 setPlayingContent: function(player) {
21582 var $plItem, $playlistCtx, className, item, playState, stateObj;
21583 stateObj = App.request("state:" + player);
21584 $playlistCtx = $('.media-' + stateObj.getState('media') + ' .' + player + '-playlist');
21585 $('.can-play').removeClassStartsWith(player + '-row-');
21586 $('.item', $playlistCtx).removeClassStartsWith('row-');
21587 if (stateObj.isPlaying()) {
21588 item = stateObj.getPlaying('item');
21589 playState = stateObj.getPlaying('playState');
21590 className = '.item-' + item.uid;
21591 $(className).addClass(this.playerClass('row-' + playState, player));
21592 $plItem = $('.pos-' + stateObj.getPlaying('position'), $playlistCtx).addClass('row-' + playState);
21593 if ($plItem.data('type') === 'file') {
21594 $('.thumb', $plItem).css("background-image", "url('" + item.thumbnail + "')");
21595 $('.title', $plItem).html(helpers.entities.playingLink(item));
21597 return App.vent.trigger("state:" + player + ":playing:updated", stateObj);
21600 setPlayerPlaying: function(player) {
21601 var $dur, $img, $playerCtx, $subtitle, $title, item, stateObj;
21602 stateObj = App.request("state:" + player);
21603 $playerCtx = $('#player-' + player);
21604 $title = $('.playing-title', $playerCtx);
21605 $subtitle = $('.playing-subtitle', $playerCtx);
21606 $dur = $('.playing-time-duration', $playerCtx);
21607 $img = $('.playing-thumb', $playerCtx);
21608 if (stateObj.isPlaying()) {
21609 item = stateObj.getPlaying('item');
21610 $title.html(helpers.entities.playingLink(item));
21611 $subtitle.html(helpers.entities.getSubtitle(item));
21612 $dur.text(helpers.global.formatTime(stateObj.getPlaying('totaltime')));
21613 return $img.css("background-image", "url('" + item.thumbnail + "')");
21614 } else {
21615 $title.html(t.gettext('Nothing playing'));
21616 $subtitle.html('');
21617 $dur.text('0');
21618 return $img.attr('src', App.request("images:path:get"));
21621 setAppProperties: function(player) {
21622 var $playerCtx, stateObj;
21623 stateObj = App.request("state:" + player);
21624 $playerCtx = $('#player-' + player);
21625 return $('.volume', $playerCtx).val(stateObj.getState('volume'));
21627 setTitle: function(player) {
21628 var stateObj;
21629 if (player === 'kodi') {
21630 stateObj = App.request("state:" + player);
21631 if (stateObj.isPlaying() && stateObj.getPlaying('playState') === 'playing') {
21632 return helpers.global.appTitle(stateObj.getPlaying('item'));
21633 } else {
21634 return helpers.global.appTitle();
21638 getDefaultPlayer: function() {
21639 var player;
21640 player = config.getLocal('defaultPlayer', 'auto');
21641 if (player === 'auto') {
21642 player = config.get('app', 'state:lastplayer', 'kodi');
21644 return player;
21646 initKodiState: function() {
21647 App.kodiState = new StateApp.Kodi.State();
21648 App.localState = new StateApp.Local.State();
21649 App.kodiState.setPlayer(this.getDefaultPlayer());
21650 App.kodiState.getCurrentState(function(state) {
21651 API.setState('kodi');
21652 App.kodiSockets = new StateApp.Kodi.Notifications();
21653 App.kodiPolling = new StateApp.Kodi.Polling();
21654 App.vent.on("sockets:unavailable", function() {
21655 return App.kodiPolling.startPolling();
21657 App.vent.on("playlist:rendered", function() {
21658 return App.request("playlist:refresh", App.kodiState.getState('player'), App.kodiState.getState('media'));
21660 App.vent.on("state:content:updated", function() {
21661 API.setPlayingContent('kodi');
21662 return API.setPlayingContent('local');
21664 App.vent.on("state:kodi:changed", function(state) {
21665 return API.setState('kodi');
21667 App.vent.on("state:local:changed", function(state) {
21668 return API.setState('local');
21670 App.vent.on("state:player:updated", function(player) {
21671 return API.setPlayerPlaying(player);
21673 return App.vent.trigger("state:initialized");
21675 App.reqres.setHandler("state:kodi", function() {
21676 return App.kodiState;
21678 App.reqres.setHandler("state:local", function() {
21679 return App.localState;
21681 App.reqres.setHandler("state:current", function() {
21682 var stateObj;
21683 stateObj = App.kodiState.getPlayer() === 'kodi' ? App.kodiState : App.localState;
21684 return stateObj;
21686 App.commands.setHandler('state:ws:init', function() {
21687 return App.kodiSockets = new StateApp.Kodi.Notifications();
21689 return App.vent.trigger("state:changed");
21692 return App.addInitializer(function() {
21693 return API.initKodiState();
21697 this.Kodi.module("ThumbsApp.List", function(List, App, Backbone, Marionette, $, _) {
21698 return List.Controller = (function(superClass) {
21699 extend(Controller, superClass);
21701 function Controller() {
21702 return Controller.__super__.constructor.apply(this, arguments);
21705 Controller.prototype.entityTitles = {
21706 musicvideo: 'music video'
21709 Controller.prototype.initialize = function() {
21710 var entities;
21711 this.layout = this.getLayout();
21712 entities = ['song', 'artist', 'album', 'tvshow', 'movie', 'episode', 'musicvideo'];
21713 this.listenTo(this.layout, "show", (function(_this) {
21714 return function() {
21715 var entity, len, n, results1;
21716 results1 = [];
21717 for (n = 0, len = entities.length; n < len; n++) {
21718 entity = entities[n];
21719 results1.push(_this.getResult(entity));
21721 return results1;
21723 })(this));
21724 return App.regionContent.show(this.layout);
21727 Controller.prototype.getLayout = function() {
21728 return new List.ListLayout();
21731 Controller.prototype.getResult = function(entity) {
21732 var limit, loaded, query, setView, view;
21733 query = this.getOption('query');
21734 limit = this.getOption('media') === 'all' ? 'limit' : 'all';
21735 loaded = App.request("thumbsup:get:entities", entity);
21736 if (loaded.length > 0) {
21737 view = App.request(entity + ":list:view", loaded, true);
21738 setView = new List.ListSet({
21739 entity: this.getTitle(entity)
21741 App.listenTo(setView, "show", (function(_this) {
21742 return function() {
21743 return setView.regionResult.show(view);
21745 })(this));
21746 return this.layout[entity + "Set"].show(setView);
21750 Controller.prototype.getTitle = function(entity) {
21751 var title;
21752 title = this.entityTitles[entity] ? this.entityTitles[entity] : entity;
21753 return title;
21756 return Controller;
21758 })(App.Controllers.Base);
21761 this.Kodi.module("ThumbsApp.List", function(List, App, Backbone, Marionette, $, _) {
21762 List.ListLayout = (function(superClass) {
21763 extend(ListLayout, superClass);
21765 function ListLayout() {
21766 return ListLayout.__super__.constructor.apply(this, arguments);
21769 ListLayout.prototype.template = 'apps/thumbs/list/thumbs_layout';
21771 ListLayout.prototype.className = "thumbs-page set-page";
21773 ListLayout.prototype.regions = {
21774 artistSet: '.entity-set-artist',
21775 albumSet: '.entity-set-album',
21776 songSet: '.entity-set-song',
21777 movieSet: '.entity-set-movie',
21778 tvshowSet: '.entity-set-tvshow',
21779 episodeSet: '.entity-set-episode',
21780 musicvideoSet: '.entity-set-musicvideo'
21783 return ListLayout;
21785 })(App.Views.LayoutView);
21786 return List.ListSet = (function(superClass) {
21787 extend(ListSet, superClass);
21789 function ListSet() {
21790 return ListSet.__super__.constructor.apply(this, arguments);
21793 ListSet.prototype.template = 'apps/thumbs/list/thumbs_set';
21795 ListSet.prototype.className = "thumbs-set";
21797 ListSet.prototype.onRender = function() {
21798 if (this.options) {
21799 if (this.options.entity) {
21800 return $('h2.set-header', this.$el).text(t.gettext(this.options.entity + 's'));
21805 ListSet.prototype.regions = {
21806 regionResult: '.set-results'
21809 return ListSet;
21811 })(App.Views.LayoutView);
21814 this.Kodi.module("ThumbsApp", function(ThumbsApp, App, Backbone, Marionette, $, _) {
21815 var API;
21816 ThumbsApp.Router = (function(superClass) {
21817 extend(Router, superClass);
21819 function Router() {
21820 return Router.__super__.constructor.apply(this, arguments);
21823 Router.prototype.appRoutes = {
21824 "thumbsup": "list"
21827 return Router;
21829 })(App.Router.Base);
21830 API = {
21831 list: function() {
21832 return new ThumbsApp.List.Controller();
21835 return App.on("before:start", function() {
21836 return new ThumbsApp.Router({
21837 controller: API
21842 this.Kodi.module("TVShowApp.EditEpisode", function(Edit, App, Backbone, Marionette, $, _) {
21843 return Edit.Controller = (function(superClass) {
21844 extend(Controller, superClass);
21846 function Controller() {
21847 return Controller.__super__.constructor.apply(this, arguments);
21850 Controller.prototype.initialize = function() {
21851 var form, options;
21852 this.model = this.getOption('model');
21853 options = {
21854 titleHtml: '<span>' + tr('Edit') + '</span>' + this.model.escape('showtitle') + ' - ' + this.model.escape('title') + ' (S' + this.model.escape('season') + ' E' + this.model.escape('episode') + ')',
21855 form: this.getStructure(),
21856 formState: this.model.attributes,
21857 config: {
21858 attributes: {
21859 "class": 'edit-form'
21861 editForm: true,
21862 tabs: true,
21863 callback: (function(_this) {
21864 return function(data, formView) {
21865 return _this.saveCallback(data, formView);
21867 })(this)
21870 return form = App.request("form:popup:wrapper", options);
21873 Controller.prototype.getStructure = function() {
21874 return [
21876 title: 'General',
21877 id: 'general',
21878 children: [
21880 id: 'title',
21881 title: tr('Title'),
21882 type: 'textfield'
21883 }, {
21884 id: 'plot',
21885 title: tr('Plot'),
21886 type: 'textarea'
21887 }, {
21888 id: 'rating',
21889 title: tr('Rating'),
21890 type: 'number',
21891 format: 'float',
21892 attributes: {
21893 "class": 'half-width',
21894 step: 0.1,
21895 min: 0,
21896 max: 10
21898 }, {
21899 id: 'firstaired',
21900 title: tr('First aired'),
21901 type: 'date',
21902 attributes: {
21903 "class": 'half-width'
21905 suffix: '<div class="clearfix"></div>'
21906 }, {
21907 id: 'originaltitle',
21908 title: tr('Original title'),
21909 type: 'textfield'
21912 }, {
21913 title: 'Tags',
21914 id: 'tags',
21915 children: [
21917 id: 'director',
21918 title: tr('Directors'),
21919 type: 'textfield',
21920 format: 'array.string'
21921 }, {
21922 id: 'writer',
21923 title: tr('Writers'),
21924 type: 'textfield',
21925 format: 'array.string'
21928 }, {
21929 title: 'Information',
21930 id: 'info',
21931 children: [
21933 id: 'file',
21934 title: tr('File path'),
21935 type: 'textarea',
21936 attributes: {
21937 disabled: 'disabled',
21938 cols: 5
21940 format: 'prevent.submit'
21947 Controller.prototype.saveCallback = function(data, formView) {
21948 var controller;
21949 controller = App.request("command:kodi:controller", 'video', 'VideoLibrary');
21950 return controller.setEpisodeDetails(this.model.get('id'), data, (function(_this) {
21951 return function() {
21952 Kodi.vent.trigger('entity:kodi:update', _this.model.get('uid'));
21953 return Kodi.execute("notification:show", t.sprintf(tr("Updated %1$s details"), 'episode'));
21955 })(this));
21958 return Controller;
21960 })(App.Controllers.Base);
21963 this.Kodi.module("TVShowApp.EditShow", function(Edit, App, Backbone, Marionette, $, _) {
21964 return Edit.Controller = (function(superClass) {
21965 extend(Controller, superClass);
21967 function Controller() {
21968 return Controller.__super__.constructor.apply(this, arguments);
21971 Controller.prototype.initialize = function() {
21972 var form, options;
21973 this.model = this.getOption('model');
21974 options = {
21975 titleHtml: '<span>' + tr('Edit') + '</span>' + this.model.escape('title'),
21976 form: this.getStructure(),
21977 formState: this.model.attributes,
21978 config: {
21979 attributes: {
21980 "class": 'edit-form'
21982 editForm: true,
21983 tabs: true,
21984 callback: (function(_this) {
21985 return function(data, formView) {
21986 _this.setArt(data);
21987 return _this.saveCallback(data, formView);
21989 })(this)
21992 return form = App.request("form:popup:wrapper", options);
21995 Controller.prototype.getStructure = function() {
21996 return [
21998 title: 'General',
21999 id: 'general',
22000 children: [
22002 id: 'title',
22003 title: tr('Title'),
22004 type: 'textfield'
22005 }, {
22006 id: 'plot',
22007 title: tr('Plot'),
22008 type: 'textarea'
22009 }, {
22010 id: 'studio',
22011 title: tr('Studio'),
22012 type: 'textfield',
22013 format: 'array.string'
22014 }, {
22015 id: 'mpaa',
22016 title: tr('Content rating'),
22017 type: 'textfield',
22018 attributes: {
22019 "class": 'half-width'
22021 }, {
22022 id: 'premiered',
22023 title: tr('Premiered'),
22024 type: 'date',
22025 attributes: {
22026 "class": 'half-width'
22028 suffix: '<div class="clearfix"></div>'
22029 }, {
22030 id: 'rating',
22031 title: tr('Rating'),
22032 type: 'number',
22033 format: 'float',
22034 attributes: {
22035 "class": 'half-width',
22036 step: 0.1,
22037 min: 0,
22038 max: 10
22040 }, {
22041 id: 'imdbnumber',
22042 title: tr('IMDb'),
22043 type: 'textfield',
22044 attributes: {
22045 "class": 'half-width'
22047 suffix: '<div class="clearfix"></div>'
22048 }, {
22049 id: 'sorttitle',
22050 title: tr('Sort title'),
22051 type: 'textfield'
22052 }, {
22053 id: 'originaltitle',
22054 title: tr('Original title'),
22055 type: 'textfield'
22058 }, {
22059 title: 'Tags',
22060 id: 'tags',
22061 children: [
22063 id: 'genre',
22064 title: tr('Genres'),
22065 type: 'textfield',
22066 format: 'array.string'
22067 }, {
22068 id: 'tag',
22069 title: tr('Tags'),
22070 type: 'textarea',
22071 format: 'array.string'
22074 }, {
22075 title: 'Poster',
22076 id: 'poster',
22077 children: [
22079 id: 'thumbnail',
22080 title: tr('URL'),
22081 type: 'imageselect',
22082 valueProperty: 'thumbnailOriginal',
22083 description: tr('Add an image via an external URL'),
22084 metadataImageHandler: 'themoviedb:tv:image:entities',
22085 metadataLookupField: 'imdbnumber'
22088 }, {
22089 title: 'Background',
22090 id: 'background',
22091 children: [
22093 id: 'fanart',
22094 title: tr('URL'),
22095 type: 'imageselect',
22096 valueProperty: 'fanartOriginal',
22097 description: tr('Add an image via an external URL'),
22098 metadataImageHandler: 'themoviedb:tv:image:entities',
22099 metadataLookupField: 'imdbnumber'
22106 Controller.prototype.setArt = function(data) {
22107 var art;
22108 art = {};
22109 if ('fanart' in data) {
22110 art["fanart"] = data.fanart;
22112 if ('thumbnail' in data) {
22113 art["poster"] = data.thumbnail;
22114 delete data.thumbnail;
22116 return data["art"] = art;
22119 Controller.prototype.saveCallback = function(data, formView) {
22120 var controller;
22121 controller = App.request("command:kodi:controller", 'video', 'VideoLibrary');
22122 return controller.setTVShowDetails(this.model.get('id'), data, (function(_this) {
22123 return function() {
22124 Kodi.vent.trigger('entity:kodi:update', _this.model.get('uid'));
22125 return Kodi.execute("notification:show", t.sprintf(tr("Updated %1$s details"), 'tvshow'));
22127 })(this));
22130 return Controller;
22132 })(App.Controllers.Base);
22135 this.Kodi.module("TVShowApp.Episode", function(Episode, App, Backbone, Marionette, $, _) {
22136 var API;
22137 API = {
22138 getEpisodeList: function(collection) {
22139 var view;
22140 view = new Episode.Episodes({
22141 collection: collection
22143 App.listenTo(view, 'childview:episode:play', function(parent, viewItem) {
22144 return App.execute('episode:action', 'play', viewItem);
22146 App.listenTo(view, 'childview:episode:add', function(parent, viewItem) {
22147 return App.execute('episode:action', 'add', viewItem);
22149 App.listenTo(view, 'childview:episode:localplay', function(parent, viewItem) {
22150 return App.execute('episode:action', 'localplay', viewItem);
22152 App.listenTo(view, 'childview:episode:download', function(parent, viewItem) {
22153 return App.execute('episode:action', 'download', viewItem);
22155 App.listenTo(view, 'childview:episode:watched', function(parent, viewItem) {
22156 return App.execute('episode:action:watched', parent, viewItem);
22158 App.listenTo(view, 'childview:episode:goto:season', function(parent, viewItem) {
22159 return App.execute('episode:action', 'gotoSeason', viewItem);
22161 App.listenTo(view, 'childview:episode:edit', function(parent, viewItem) {
22162 return App.execute('episode:edit', viewItem.model);
22164 return view;
22166 bindTriggers: function(view) {
22167 App.listenTo(view, 'episode:play', function(viewItem) {
22168 return App.execute('episode:action', 'play', viewItem);
22170 App.listenTo(view, 'episode:add', function(viewItem) {
22171 return App.execute('episode:action', 'add', viewItem);
22173 App.listenTo(view, 'episode:localplay', function(viewItem) {
22174 return App.execute('episode:action', 'localplay', viewItem);
22176 App.listenTo(view, 'episode:download', function(viewItem) {
22177 return App.execute('episode:action', 'download', viewItem);
22179 App.listenTo(view, 'toggle:watched', function(viewItem) {
22180 return App.execute('episode:action:watched', viewItem.view, viewItem.view);
22182 App.listenTo(view, 'episode:refresh', function(viewItem) {
22183 return App.execute('episode:action', 'refresh', viewItem);
22185 return App.listenTo(view, 'episode:edit', function(viewItem) {
22186 return App.execute('episode:edit', viewItem.model);
22190 Episode.Controller = (function(superClass) {
22191 extend(Controller, superClass);
22193 function Controller() {
22194 return Controller.__super__.constructor.apply(this, arguments);
22197 Controller.prototype.initialize = function(options) {
22198 var episode, episodeId, id, seasonId;
22199 id = parseInt(options.id);
22200 seasonId = parseInt(options.season);
22201 episodeId = parseInt(options.episodeid);
22202 episode = App.request("episode:entity", episodeId);
22203 return App.execute("when:entity:fetched", episode, (function(_this) {
22204 return function() {
22205 _this.layout = _this.getLayoutView(episode);
22206 _this.listenTo(_this.layout, "show", function() {
22207 _this.getDetailsLayoutView(episode);
22208 return _this.getContentView(episode);
22210 return App.regionContent.show(_this.layout);
22212 })(this));
22215 Controller.prototype.getLayoutView = function(episode) {
22216 return new Episode.PageLayout({
22217 model: episode
22221 Controller.prototype.getDetailsLayoutView = function(episode) {
22222 var headerLayout;
22223 headerLayout = new Episode.HeaderLayout({
22224 model: episode
22226 this.listenTo(headerLayout, "show", (function(_this) {
22227 return function() {
22228 var detail, teaser;
22229 teaser = new Episode.EpisodeDetailTeaser({
22230 model: episode
22232 detail = new Episode.Details({
22233 model: episode
22235 API.bindTriggers(detail);
22236 headerLayout.regionSide.show(teaser);
22237 return headerLayout.regionMeta.show(detail);
22239 })(this));
22240 return this.layout.regionHeader.show(headerLayout);
22243 Controller.prototype.getContentView = function(episode) {
22244 this.contentLayout = new Episode.Content({
22245 model: episode
22247 App.listenTo(this.contentLayout, 'show', (function(_this) {
22248 return function() {
22249 if (episode.get('cast').length > 0) {
22250 _this.contentLayout.regionCast.show(_this.getCast(episode));
22252 return _this.getSeason(episode);
22254 })(this));
22255 return this.layout.regionContent.show(this.contentLayout);
22258 Controller.prototype.getCast = function(episode) {
22259 return App.request('cast:list:view', episode.get('cast'), 'tvshows');
22262 Controller.prototype.getSeason = function(episode) {
22263 var collection;
22264 collection = App.request("episode:tvshow:entities", episode.get('tvshowid'), episode.get('season'));
22265 return App.execute("when:entity:fetched", collection, (function(_this) {
22266 return function() {
22267 var view;
22268 collection.sortCollection('episode', 'asc');
22269 view = App.request("episode:list:view", collection);
22270 return _this.contentLayout.regionSeason.show(view);
22272 })(this));
22275 return Controller;
22277 })(App.Controllers.Base);
22278 return App.reqres.setHandler("episode:list:view", function(collection) {
22279 return API.getEpisodeList(collection);
22283 this.Kodi.module("TVShowApp.Episode", function(Episode, App, Backbone, Marionette, $, _) {
22284 Episode.EpisodeTeaser = (function(superClass) {
22285 extend(EpisodeTeaser, superClass);
22287 function EpisodeTeaser() {
22288 return EpisodeTeaser.__super__.constructor.apply(this, arguments);
22291 EpisodeTeaser.prototype.triggers = {
22292 "click .play": "episode:play",
22293 "click .watched": "episode:watched",
22294 "click .add": "episode:add",
22295 "click .localplay": "episode:localplay",
22296 "click .download": "episode:download",
22297 "click .goto-season": "episode:goto:season",
22298 "click .edit": "episode:edit"
22301 EpisodeTeaser.prototype.initialize = function() {
22302 EpisodeTeaser.__super__.initialize.apply(this, arguments);
22303 if (this.model != null) {
22304 this.setMeta();
22305 return this.model.set(App.request('episode:action:items'));
22309 EpisodeTeaser.prototype.attributes = function() {
22310 return this.watchedAttributes('card');
22313 EpisodeTeaser.prototype.setMeta = function() {
22314 var epNum, epNumFull, showLink, subTitleTip;
22315 epNum = this.themeTag('span', {
22316 "class": 'ep-num'
22317 }, this.model.escape('season') + 'x' + this.model.escape('episode') + ' ');
22318 epNumFull = this.themeTag('span', {
22319 "class": 'ep-num-full'
22320 }, t.gettext('Episode') + ' ' + this.model.escape('episode'));
22321 showLink = this.themeLink(this.model.escape('showtitle') + ' ', 'tvshow/' + this.model.escape('tvshowid'), {
22322 className: 'show-name'
22324 subTitleTip = this.model.escape('firstaired') ? {
22325 title: tr('First aired') + ': ' + this.model.escape('firstaired')
22326 } : {};
22327 return this.model.set({
22328 labelHtml: epNum + this.model.get('title'),
22329 subtitleHtml: this.themeTag('div', subTitleTip, showLink + epNumFull)
22333 return EpisodeTeaser;
22335 })(App.Views.CardView);
22336 Episode.Empty = (function(superClass) {
22337 extend(Empty, superClass);
22339 function Empty() {
22340 return Empty.__super__.constructor.apply(this, arguments);
22343 Empty.prototype.tagName = "li";
22345 Empty.prototype.className = "episode-empty-result";
22347 return Empty;
22349 })(App.Views.EmptyViewResults);
22350 Episode.Episodes = (function(superClass) {
22351 extend(Episodes, superClass);
22353 function Episodes() {
22354 return Episodes.__super__.constructor.apply(this, arguments);
22357 Episodes.prototype.childView = Episode.EpisodeTeaser;
22359 Episodes.prototype.emptyView = Episode.Empty;
22361 Episodes.prototype.tagName = "ul";
22363 Episodes.prototype.className = "card-grid--episode";
22365 return Episodes;
22367 })(App.Views.CollectionView);
22368 Episode.PageLayout = (function(superClass) {
22369 extend(PageLayout, superClass);
22371 function PageLayout() {
22372 return PageLayout.__super__.constructor.apply(this, arguments);
22375 PageLayout.prototype.className = 'episode-show detail-container';
22377 return PageLayout;
22379 })(App.Views.LayoutWithHeaderView);
22380 Episode.HeaderLayout = (function(superClass) {
22381 extend(HeaderLayout, superClass);
22383 function HeaderLayout() {
22384 return HeaderLayout.__super__.constructor.apply(this, arguments);
22387 HeaderLayout.prototype.className = 'episode-details';
22389 return HeaderLayout;
22391 })(App.Views.LayoutDetailsHeaderView);
22392 Episode.Details = (function(superClass) {
22393 extend(Details, superClass);
22395 function Details() {
22396 return Details.__super__.constructor.apply(this, arguments);
22399 Details.prototype.template = 'apps/tvshow/episode/details_meta';
22401 Details.prototype.triggers = {
22402 'click .play': 'episode:play',
22403 'click .add': 'episode:add',
22404 'click .stream': 'episode:localplay',
22405 'click .download': 'episode:download',
22406 'click .edit': 'episode:edit',
22407 'click .refresh': 'episode:refresh'
22410 Details.prototype.attributes = function() {
22411 return this.watchedAttributes();
22414 return Details;
22416 })(App.Views.DetailsItem);
22417 Episode.EpisodeDetailTeaser = (function(superClass) {
22418 extend(EpisodeDetailTeaser, superClass);
22420 function EpisodeDetailTeaser() {
22421 return EpisodeDetailTeaser.__super__.constructor.apply(this, arguments);
22424 EpisodeDetailTeaser.prototype.tagName = "div";
22426 EpisodeDetailTeaser.prototype.triggers = {
22427 "click .menu": "episode-menu:clicked"
22430 EpisodeDetailTeaser.prototype.initialize = function() {
22431 return this.model.set({
22432 actions: {
22433 thumbs: tr('Thumbs up')
22438 EpisodeDetailTeaser.prototype.attributes = function() {
22439 return this.watchedAttributes('card-detail');
22442 return EpisodeDetailTeaser;
22444 })(App.Views.CardView);
22445 return Episode.Content = (function(superClass) {
22446 extend(Content, superClass);
22448 function Content() {
22449 return Content.__super__.constructor.apply(this, arguments);
22452 Content.prototype.template = 'apps/tvshow/episode/content';
22454 Content.prototype.className = "episode-content content-sections";
22456 Content.prototype.regions = {
22457 regionCast: '.region-cast',
22458 regionSeason: '.region-season'
22461 Content.prototype.modelEvents = {
22462 'change': 'modelChange'
22465 Content.prototype.modelChange = function() {
22466 this.render();
22467 return this.trigger('show');
22470 return Content;
22472 })(App.Views.LayoutView);
22475 this.Kodi.module("TVShowApp.List", function(List, App, Backbone, Marionette, $, _) {
22476 var API;
22477 API = {
22478 getTVShowsList: function(tvshows, set) {
22479 var view, viewName;
22480 if (set == null) {
22481 set = false;
22483 viewName = set ? 'TVShowsSet' : 'TVShows';
22484 view = new List[viewName]({
22485 collection: tvshows
22487 API.bindTriggers(view);
22488 return view;
22490 bindTriggers: function(view) {
22491 App.listenTo(view, 'childview:tvshow:play', function(parent, viewItem) {
22492 return App.execute('tvshow:action', 'play', viewItem);
22494 App.listenTo(view, 'childview:tvshow:add', function(parent, viewItem) {
22495 return App.execute('tvshow:action', 'add', viewItem);
22497 App.listenTo(view, 'childview:tvshow:watched', function(parent, viewItem) {
22498 return App.execute('tvshow:action:watched', parent, viewItem);
22500 return App.listenTo(view, 'childview:tvshow:edit', function(parent, viewItem) {
22501 return App.execute('tvshow:edit', viewItem.model);
22505 List.Controller = (function(superClass) {
22506 extend(Controller, superClass);
22508 function Controller() {
22509 return Controller.__super__.constructor.apply(this, arguments);
22512 Controller.prototype.initialize = function() {
22513 var collection;
22514 collection = App.request("tvshow:entities");
22515 collection.availableFilters = this.getAvailableFilters();
22516 collection.sectionId = 'tvshows/recent';
22517 App.request('filter:init', this.getAvailableFilters());
22518 return App.execute("when:entity:fetched", collection, (function(_this) {
22519 return function() {
22520 _this.layout = _this.getLayoutView(collection);
22521 _this.listenTo(_this.layout, "show", function() {
22522 _this.getFiltersView(collection);
22523 return _this.renderList(collection);
22525 return App.regionContent.show(_this.layout);
22527 })(this));
22530 Controller.prototype.getLayoutView = function(tvshows) {
22531 return new List.ListLayout({
22532 collection: tvshows
22536 Controller.prototype.getAvailableFilters = function() {
22537 return {
22538 sort: ['title', 'year', 'dateadded', 'rating', 'random'],
22539 filter: ['year', 'genre', 'unwatched', 'inprogress', 'cast', 'mpaa', 'studio', 'thumbsUp', 'tag']
22543 Controller.prototype.getFiltersView = function(collection) {
22544 var filters;
22545 filters = App.request('filter:show', collection);
22546 this.layout.regionSidebarFirst.show(filters);
22547 return this.listenTo(filters, "filter:changed", (function(_this) {
22548 return function() {
22549 return _this.renderList(collection);
22551 })(this));
22554 Controller.prototype.renderList = function(collection) {
22555 var filteredCollection, view;
22556 App.execute("loading:show:view", this.layout.regionContent);
22557 filteredCollection = App.request('filter:apply:entities', collection);
22558 view = API.getTVShowsList(filteredCollection);
22559 return this.layout.regionContent.show(view);
22562 return Controller;
22564 })(App.Controllers.Base);
22565 return App.reqres.setHandler("tvshow:list:view", function(collection) {
22566 return API.getTVShowsList(collection, true);
22570 this.Kodi.module("TVShowApp.List", function(List, App, Backbone, Marionette, $, _) {
22571 List.ListLayout = (function(superClass) {
22572 extend(ListLayout, superClass);
22574 function ListLayout() {
22575 return ListLayout.__super__.constructor.apply(this, arguments);
22578 ListLayout.prototype.className = "tvshow-list with-filters";
22580 return ListLayout;
22582 })(App.Views.LayoutWithSidebarFirstView);
22583 List.TVShowTeaser = (function(superClass) {
22584 extend(TVShowTeaser, superClass);
22586 function TVShowTeaser() {
22587 return TVShowTeaser.__super__.constructor.apply(this, arguments);
22590 TVShowTeaser.prototype.triggers = {
22591 "click .play": "tvshow:play",
22592 "click .watched": "tvshow:watched",
22593 "click .add": "tvshow:add",
22594 "click .edit": "tvshow:edit"
22597 TVShowTeaser.prototype.initialize = function() {
22598 TVShowTeaser.__super__.initialize.apply(this, arguments);
22599 this.setMeta();
22600 return this.model.set(App.request('tvshow:action:items'));
22603 TVShowTeaser.prototype.attributes = function() {
22604 return this.watchedAttributes('card tv-show prevent-select');
22607 TVShowTeaser.prototype.setMeta = function() {
22608 if (this.model) {
22609 return this.model.set({
22610 subtitle: this.model.get('rating')
22615 return TVShowTeaser;
22617 })(App.Views.CardView);
22618 List.Empty = (function(superClass) {
22619 extend(Empty, superClass);
22621 function Empty() {
22622 return Empty.__super__.constructor.apply(this, arguments);
22625 Empty.prototype.tagName = "li";
22627 Empty.prototype.className = "tvshow-empty-result";
22629 return Empty;
22631 })(App.Views.EmptyViewResults);
22632 List.TVShows = (function(superClass) {
22633 extend(TVShows, superClass);
22635 function TVShows() {
22636 return TVShows.__super__.constructor.apply(this, arguments);
22639 TVShows.prototype.childView = List.TVShowTeaser;
22641 TVShows.prototype.emptyView = List.Empty;
22643 TVShows.prototype.tagName = "ul";
22645 TVShows.prototype.className = "card-grid--tall";
22647 return TVShows;
22649 })(App.Views.VirtualListView);
22650 return List.TVShowsSet = (function(superClass) {
22651 extend(TVShowsSet, superClass);
22653 function TVShowsSet() {
22654 return TVShowsSet.__super__.constructor.apply(this, arguments);
22657 TVShowsSet.prototype.childView = List.TVShowTeaser;
22659 TVShowsSet.prototype.emptyView = List.Empty;
22661 TVShowsSet.prototype.tagName = "ul";
22663 TVShowsSet.prototype.className = "card-grid--tall";
22665 return TVShowsSet;
22667 })(App.Views.CollectionView);
22670 this.Kodi.module("TVShowApp.Season", function(Season, App, Backbone, Marionette, $, _) {
22671 var API;
22672 API = {
22673 getSeasonList: function(collection) {
22674 var view;
22675 view = new Season.Seasons({
22676 collection: collection
22678 return view;
22680 bindTriggers: function(view) {
22681 App.listenTo(view, 'season:play', function(view) {
22682 return App.execute('tvshow:action', 'play', view);
22684 App.listenTo(view, 'season:add', function(view) {
22685 return App.execute('tvshow:action', 'add', view);
22687 return App.listenTo(view, 'toggle:watched', function(view) {
22688 return App.execute('tvshow:action:watched', view.view, view.view, true);
22691 mergeSeasonDetails: function(tvshow, season, seasons) {
22692 var attributes, len, mergeAttributes, n, prop;
22693 mergeAttributes = ['season', 'thumbnail', 'episode', 'unwatched', 'playcount', 'progress', 'watchedepisodes'];
22694 attributes = {
22695 seasons: seasons,
22696 type: 'season'
22698 for (n = 0, len = mergeAttributes.length; n < len; n++) {
22699 prop = mergeAttributes[n];
22700 attributes[prop] = season.get(prop);
22702 tvshow.set(attributes);
22703 return tvshow;
22706 Season.Controller = (function(superClass) {
22707 extend(Controller, superClass);
22709 function Controller() {
22710 return Controller.__super__.constructor.apply(this, arguments);
22713 Controller.prototype.initialize = function(options) {
22714 var id, seasonId, tvshow;
22715 id = parseInt(options.id);
22716 seasonId = parseInt(options.season);
22717 tvshow = App.request("tvshow:entity", id);
22718 return App.execute("when:entity:fetched", tvshow, (function(_this) {
22719 return function() {
22720 var seasons;
22721 seasons = App.request("season:entities", tvshow.get('id'));
22722 return App.execute("when:entity:fetched", seasons, function() {
22723 var season;
22724 season = seasons.findWhere({
22725 season: seasonId
22727 tvshow = API.mergeSeasonDetails(tvshow, season, seasons);
22728 _this.layout = _this.getLayoutView(tvshow);
22729 _this.listenTo(_this.layout, "show", function() {
22730 _this.getDetailsLayoutView(tvshow);
22731 return _this.getEpisodes(tvshow, seasonId);
22733 return App.regionContent.show(_this.layout);
22736 })(this));
22739 Controller.prototype.getLayoutView = function(tvshow) {
22740 return new Season.PageLayout({
22741 model: tvshow
22745 Controller.prototype.getDetailsLayoutView = function(tvshow) {
22746 var headerLayout;
22747 headerLayout = new Season.HeaderLayout({
22748 model: tvshow
22750 this.listenTo(headerLayout, "show", (function(_this) {
22751 return function() {
22752 var detail, teaser;
22753 teaser = new Season.SeasonDetailTeaser({
22754 model: tvshow
22756 detail = new Season.Details({
22757 model: tvshow
22759 API.bindTriggers(detail);
22760 headerLayout.regionSide.show(teaser);
22761 return headerLayout.regionMeta.show(detail);
22763 })(this));
22764 return this.layout.regionHeader.show(headerLayout);
22767 Controller.prototype.getEpisodes = function(tvshow, seasonId) {
22768 var collection;
22769 collection = App.request("episode:tvshow:entities", tvshow.get('tvshowid'), seasonId);
22770 return App.execute("when:entity:fetched", collection, (function(_this) {
22771 return function() {
22772 var view;
22773 collection.sortCollection('episode', 'asc');
22774 view = App.request("episode:list:view", collection);
22775 return _this.layout.regionContent.show(view);
22777 })(this));
22780 return Controller;
22782 })(App.Controllers.Base);
22783 return App.reqres.setHandler("season:list:view", function(collection) {
22784 return API.getSeasonList(collection);
22788 this.Kodi.module("TVShowApp.Season", function(Season, App, Backbone, Marionette, $, _) {
22789 Season.SeasonTeaser = (function(superClass) {
22790 extend(SeasonTeaser, superClass);
22792 function SeasonTeaser() {
22793 return SeasonTeaser.__super__.constructor.apply(this, arguments);
22796 SeasonTeaser.prototype.triggers = {
22797 "click .play": "season:play",
22798 "click .watched": "season:watched",
22799 "click .add": "season:add"
22802 SeasonTeaser.prototype.initialize = function() {
22803 var subtitle;
22804 SeasonTeaser.__super__.initialize.apply(this, arguments);
22805 subtitle = this.model.get('episode') + ' ' + tr('episodes');
22806 this.model.set({
22807 subtitle: subtitle
22809 this.model.set(App.request('tvshow:action:items'));
22810 return this.model.set({
22811 label: tr('Season') + ' ' + this.model.get('season')
22815 SeasonTeaser.prototype.attributes = function() {
22816 return this.watchedAttributes('card tv-season prevent-select');
22819 return SeasonTeaser;
22821 })(App.Views.CardView);
22822 Season.Empty = (function(superClass) {
22823 extend(Empty, superClass);
22825 function Empty() {
22826 return Empty.__super__.constructor.apply(this, arguments);
22829 Empty.prototype.tagName = "li";
22831 Empty.prototype.className = "season-empty-result";
22833 return Empty;
22835 })(App.Views.EmptyViewResults);
22836 Season.Seasons = (function(superClass) {
22837 extend(Seasons, superClass);
22839 function Seasons() {
22840 return Seasons.__super__.constructor.apply(this, arguments);
22843 Seasons.prototype.childView = Season.SeasonTeaser;
22845 Seasons.prototype.emptyView = Season.Empty;
22847 Seasons.prototype.tagName = "ul";
22849 Seasons.prototype.className = "card-grid--tall";
22851 return Seasons;
22853 })(App.Views.CollectionView);
22854 Season.PageLayout = (function(superClass) {
22855 extend(PageLayout, superClass);
22857 function PageLayout() {
22858 return PageLayout.__super__.constructor.apply(this, arguments);
22861 PageLayout.prototype.className = 'season-show tv-collection detail-container';
22863 return PageLayout;
22865 })(App.Views.LayoutWithHeaderView);
22866 Season.HeaderLayout = (function(superClass) {
22867 extend(HeaderLayout, superClass);
22869 function HeaderLayout() {
22870 return HeaderLayout.__super__.constructor.apply(this, arguments);
22873 HeaderLayout.prototype.className = 'season-details';
22875 return HeaderLayout;
22877 })(App.Views.LayoutDetailsHeaderView);
22878 Season.Details = (function(superClass) {
22879 extend(Details, superClass);
22881 function Details() {
22882 return Details.__super__.constructor.apply(this, arguments);
22885 Details.prototype.template = 'apps/tvshow/season/details_meta';
22887 Details.prototype.triggers = {
22888 "click .play": "season:play",
22889 "click .add": "season:add"
22892 Details.prototype.attributes = function() {
22893 return this.watchedAttributes('details-meta');
22896 return Details;
22898 })(App.Views.DetailsItem);
22899 return Season.SeasonDetailTeaser = (function(superClass) {
22900 extend(SeasonDetailTeaser, superClass);
22902 function SeasonDetailTeaser() {
22903 return SeasonDetailTeaser.__super__.constructor.apply(this, arguments);
22906 SeasonDetailTeaser.prototype.tagName = "div";
22908 SeasonDetailTeaser.prototype.className = "card-detail";
22910 return SeasonDetailTeaser;
22912 })(App.Views.CardView);
22915 this.Kodi.module("TVShowApp.Show", function(Show, App, Backbone, Marionette, $, _) {
22916 var API;
22917 API = {
22918 bindTriggersTVShow: function(view) {
22919 App.listenTo(view, 'tvshow:play', function(view) {
22920 return App.execute('tvshow:action', 'play', view);
22922 App.listenTo(view, 'tvshow:add', function(view) {
22923 return App.execute('tvshow:action', 'add', view);
22925 App.listenTo(view, 'toggle:watched', function(view) {
22926 return App.execute('tvshow:action:watched', view.view, view.view, true);
22928 App.listenTo(view, 'tvshow:refresh', function(view) {
22929 return App.execute('tvshow:action', 'refresh', view);
22931 App.listenTo(view, 'tvshow:refresh:episodes', function(view) {
22932 return App.execute('tvshow:action', 'refreshEpisodes', view);
22934 return App.listenTo(view, 'tvshow:edit', function(view) {
22935 return App.execute('tvshow:edit', view.model);
22938 bindTriggersTVSeason: function(view) {
22939 App.listenTo(view, 'childview:season:play', function(parent, viewItem) {
22940 return App.execute('tvshow:action', 'play', viewItem);
22942 App.listenTo(view, 'childview:season:add', function(parent, viewItem) {
22943 return App.execute('tvshow:action', 'add', viewItem);
22945 return App.listenTo(view, 'childview:season:watched', function(parent, viewItem) {
22946 return App.execute('tvshow:action:watched', parent, viewItem, false);
22950 return Show.Controller = (function(superClass) {
22951 extend(Controller, superClass);
22953 function Controller() {
22954 return Controller.__super__.constructor.apply(this, arguments);
22957 Controller.prototype.initialize = function(options) {
22958 var id, tvshow;
22959 id = parseInt(options.id);
22960 tvshow = App.request("tvshow:entity", id);
22961 return App.execute("when:entity:fetched", tvshow, (function(_this) {
22962 return function() {
22963 _this.layout = _this.getLayoutView(tvshow);
22964 _this.listenTo(_this.layout, "destroy", function() {
22965 return App.execute("images:fanart:set", 'none');
22967 _this.listenTo(_this.layout, "show", function() {
22968 _this.getDetailsLayoutView(tvshow);
22969 return _this.getSeasons(tvshow);
22971 return App.regionContent.show(_this.layout);
22973 })(this));
22976 Controller.prototype.getLayoutView = function(tvshow) {
22977 return new Show.PageLayout({
22978 model: tvshow
22982 Controller.prototype.getDetailsLayoutView = function(tvshow) {
22983 var headerLayout;
22984 headerLayout = new Show.HeaderLayout({
22985 model: tvshow
22987 this.listenTo(headerLayout, "show", (function(_this) {
22988 return function() {
22989 var detail, teaser;
22990 teaser = new Show.TVShowTeaser({
22991 model: tvshow
22993 detail = new Show.Details({
22994 model: tvshow
22996 API.bindTriggersTVShow(detail);
22997 API.bindTriggersTVShow(teaser);
22998 headerLayout.regionSide.show(teaser);
22999 return headerLayout.regionMeta.show(detail);
23001 })(this));
23002 return this.layout.regionHeader.show(headerLayout);
23005 Controller.prototype.getSeasons = function(tvshow) {
23006 var collection;
23007 collection = App.request("season:entities", tvshow.get('tvshowid'));
23008 return App.execute("when:entity:fetched", collection, (function(_this) {
23009 return function() {
23010 var view;
23011 view = App.request("season:list:view", collection);
23012 API.bindTriggersTVSeason(view);
23013 if (_this.layout.regionContent) {
23014 _this.layout.regionContent.show(view);
23015 return App.vent.on('entity:kodi:update', function(uid) {
23016 if (tvshow.get('uid') === uid) {
23017 return _this.getSeasons(tvshow);
23022 })(this));
23025 return Controller;
23027 })(App.Controllers.Base);
23030 this.Kodi.module("TVShowApp.Show", function(Show, App, Backbone, Marionette, $, _) {
23031 Show.PageLayout = (function(superClass) {
23032 extend(PageLayout, superClass);
23034 function PageLayout() {
23035 return PageLayout.__super__.constructor.apply(this, arguments);
23038 PageLayout.prototype.className = 'tvshow-show tv-collection detail-container';
23040 return PageLayout;
23042 })(App.Views.LayoutWithHeaderView);
23043 Show.HeaderLayout = (function(superClass) {
23044 extend(HeaderLayout, superClass);
23046 function HeaderLayout() {
23047 return HeaderLayout.__super__.constructor.apply(this, arguments);
23050 HeaderLayout.prototype.className = 'tvshow-details';
23052 return HeaderLayout;
23054 })(App.Views.LayoutDetailsHeaderView);
23055 Show.Details = (function(superClass) {
23056 extend(Details, superClass);
23058 function Details() {
23059 return Details.__super__.constructor.apply(this, arguments);
23062 Details.prototype.template = 'apps/tvshow/show/details_meta';
23064 Details.prototype.triggers = {
23065 "click .play": "tvshow:play",
23066 "click .add": "tvshow:add",
23067 "click .edit": "tvshow:edit",
23068 "click .refresh": "tvshow:refresh",
23069 "click .refresh-episodes": "tvshow:refresh:episodes"
23072 Details.prototype.attributes = function() {
23073 return this.watchedAttributes('details-meta');
23076 return Details;
23078 })(App.Views.DetailsItem);
23079 return Show.TVShowTeaser = (function(superClass) {
23080 extend(TVShowTeaser, superClass);
23082 function TVShowTeaser() {
23083 return TVShowTeaser.__super__.constructor.apply(this, arguments);
23086 TVShowTeaser.prototype.tagName = "div";
23088 TVShowTeaser.prototype.triggers = {
23089 "click .play": "tvshow:play"
23092 TVShowTeaser.prototype.initialize = function() {
23093 return this.model.set({
23094 actions: {
23095 thumbs: tr('Thumbs up')
23100 TVShowTeaser.prototype.attributes = function() {
23101 return this.watchedAttributes('card-detail');
23104 return TVShowTeaser;
23106 })(App.Views.CardView);
23109 this.Kodi.module("TVShowApp", function(TVShowApp, App, Backbone, Marionette, $, _) {
23110 var API;
23111 TVShowApp.Router = (function(superClass) {
23112 extend(Router, superClass);
23114 function Router() {
23115 return Router.__super__.constructor.apply(this, arguments);
23118 Router.prototype.appRoutes = {
23119 "tvshows": "list",
23120 "tvshow/:tvshowid": "view",
23121 "tvshow/:tvshowid/:season": "season",
23122 "tvshow/:tvshowid/:season/:episodeid": "episode"
23125 return Router;
23127 })(App.Router.Base);
23128 API = {
23129 list: function() {
23130 return new TVShowApp.List.Controller();
23132 view: function(tvshowid) {
23133 return new TVShowApp.Show.Controller({
23134 id: tvshowid
23137 season: function(tvshowid, season) {
23138 return new TVShowApp.Season.Controller({
23139 id: tvshowid,
23140 season: season
23143 episode: function(tvshowid, season, episodeid) {
23144 return new TVShowApp.Episode.Controller({
23145 id: tvshowid,
23146 season: season,
23147 episodeid: episodeid
23150 toggleWatched: function(model, season, op) {
23151 if (season == null) {
23152 season = 'all';
23154 return API.getAllEpisodesCollection(model.get('tvshowid'), season, function(collection) {
23155 var videoLib;
23156 videoLib = App.request("command:kodi:controller", 'video', 'VideoLibrary');
23157 return videoLib.toggleWatchedCollection(collection, op);
23160 toggleWatchedUiState: function($el, setChildren) {
23161 var $layout, classOp, op, progress, unwatched;
23162 if (setChildren == null) {
23163 setChildren = true;
23165 op = $el.hasClass('is-watched') ? 'unwatched' : 'watched';
23166 classOp = op === 'watched' ? 'addClass' : 'removeClass';
23167 progress = op === 'watched' ? 100 : 0;
23168 $el[classOp]('is-watched');
23169 helpers.entities.setProgress($el, progress);
23170 $layout = $el.closest('.tv-collection');
23171 if (setChildren) {
23172 $layout.find('.region-content .card')[classOp]('is-watched');
23173 helpers.entities.setProgress($layout, progress);
23175 unwatched = parseInt($layout.find('.episode-total').text()) - $layout.find('.region-content .is-watched').length;
23176 $layout.find('.episode-unwatched').text(unwatched);
23177 return $layout;
23179 getAllEpisodesCollection: function(tvshowid, season, callback) {
23180 var collectionAll;
23181 collectionAll = App.request("episode:tvshow:entities", tvshowid, season);
23182 return App.execute("when:entity:fetched", collectionAll, (function(_this) {
23183 return function() {
23184 return callback(collectionAll);
23186 })(this));
23188 episodeAction: function(op, view) {
23189 var files, model, playlist, videoLib;
23190 model = view.model;
23191 playlist = App.request("command:kodi:controller", 'video', 'PlayList');
23192 files = App.request("command:kodi:controller", 'video', 'Files');
23193 videoLib = App.request("command:kodi:controller", 'video', 'VideoLibrary');
23194 switch (op) {
23195 case 'play':
23196 return App.execute("input:resume", model, 'episodeid');
23197 case 'add':
23198 return playlist.add('episodeid', model.get('episodeid'));
23199 case 'localplay':
23200 return files.videoStream(model.get('file'), model.get('fanart'));
23201 case 'download':
23202 return files.downloadFile(model.get('file'));
23203 case 'toggleWatched':
23204 return videoLib.toggleWatched(model, 'auto');
23205 case 'gotoSeason':
23206 return App.navigate("#tvshow/" + model.get('tvshowid') + '/' + model.get('season'), {
23207 trigger: true
23209 case 'refresh':
23210 return helpers.entities.refreshEntity(model, videoLib, 'refreshEpisode');
23213 tvShowAction: function(op, view) {
23214 var model, playlist, season, videoLib;
23215 model = view.model;
23216 playlist = App.request("command:kodi:controller", 'video', 'PlayList');
23217 season = model.get('type') === 'season' ? model.get('season') : 'all';
23218 videoLib = App.request("command:kodi:controller", 'video', 'VideoLibrary');
23219 switch (op) {
23220 case 'play':
23221 return API.getAllEpisodesCollection(model.get('tvshowid'), season, function(collection) {
23222 return playlist.playCollection(collection);
23224 case 'add':
23225 return API.getAllEpisodesCollection(model.get('tvshowid'), season, function(collection) {
23226 return playlist.addCollection(collection);
23228 case 'watched':
23229 return API.toggleWatched(model, season, op);
23230 case 'unwatched':
23231 return API.toggleWatched(model, season, op);
23232 case 'edit':
23233 return App.execute('tvshow:edit', model);
23234 case 'refresh':
23235 return helpers.entities.refreshEntity(model, videoLib, 'refreshTVShow');
23236 case 'refreshEpisodes':
23237 return helpers.entities.refreshEntity(model, videoLib, 'refreshTVShow', {
23238 refreshepisodes: true
23243 App.commands.setHandler('episode:action', function(op, view) {
23244 return API.episodeAction(op, view);
23246 App.commands.setHandler('tvshow:action', function(op, view) {
23247 return API.tvShowAction(op, view);
23249 App.reqres.setHandler('episode:action:items', function() {
23250 return {
23251 actions: {
23252 watched: tr('Watched'),
23253 thumbs: tr('Thumbs up')
23255 menu: {
23256 'add': tr('Queue in Kodi'),
23257 'divider-1': '',
23258 'download': tr('Download'),
23259 'localplay': tr('Play in browser'),
23260 'divider-2': '',
23261 'goto-season': tr('Go to season'),
23262 'divider-3': '',
23263 'edit': tr('Edit')
23267 App.reqres.setHandler('tvshow:action:items', function() {
23268 return {
23269 actions: {
23270 watched: tr('Watched'),
23271 thumbs: tr('Thumbs up')
23273 menu: {
23274 add: tr('Queue in Kodi'),
23275 'divider-': '',
23276 'edit': tr('Edit')
23280 App.commands.setHandler('tvshow:action:watched', function(parent, viewItem, setChildren) {
23281 var msg, op;
23282 if (setChildren == null) {
23283 setChildren = false;
23285 op = parent.$el.hasClass('is-watched') ? 'unwatched' : 'watched';
23286 if (viewItem.model.get('type') === 'season') {
23287 msg = tr('Set all episodes for this season as') + ' ' + tr(op);
23288 } else {
23289 msg = tr('Set all episodes for this TV show as') + ' ' + tr(op);
23291 return App.execute("ui:modal:confirm", tr('Are you sure?'), msg, function() {
23292 API.toggleWatchedUiState(parent.$el, setChildren);
23293 return API.tvShowAction(op, viewItem);
23296 App.commands.setHandler('episode:action:watched', function(parent, viewItem) {
23297 API.toggleWatchedUiState(parent.$el, false);
23298 return API.episodeAction('toggleWatched', viewItem);
23300 App.commands.setHandler('tvshow:edit', function(model) {
23301 var loadedModel;
23302 loadedModel = App.request("tvshow:entity", model.get('id'));
23303 return App.execute("when:entity:fetched", loadedModel, (function(_this) {
23304 return function() {
23305 return new TVShowApp.EditShow.Controller({
23306 model: loadedModel
23309 })(this));
23311 App.commands.setHandler('episode:edit', function(model) {
23312 var loadedModel;
23313 loadedModel = App.request("episode:entity", model.get('id'));
23314 return App.execute("when:entity:fetched", loadedModel, (function(_this) {
23315 return function() {
23316 return new TVShowApp.EditEpisode.Controller({
23317 model: loadedModel
23320 })(this));
23322 return App.on("before:start", function() {
23323 return new TVShowApp.Router({
23324 controller: API
23329 this.Kodi.module("UiApp", function(UiApp, App, Backbone, Marionette, $, _) {
23330 var API;
23331 API = {
23332 openModal: function(titleHtml, msgHtml, open, style) {
23333 var $body, $modal, $title;
23334 if (open == null) {
23335 open = true;
23337 if (style == null) {
23338 style = '';
23340 $title = App.getRegion('regionModalTitle').$el;
23341 $body = App.getRegion('regionModalBody').$el;
23342 $modal = App.getRegion('regionModal').$el;
23343 $modal.removeClassStartsWith('style-');
23344 $modal.addClass('style-' + style);
23345 $title.html(titleHtml);
23346 $body.html(msgHtml);
23347 if (open) {
23348 $modal.modal();
23350 $modal.on('hidden.bs.modal', function(e) {
23351 return $body.html('');
23353 return $modal;
23355 closeModal: function() {
23356 App.getRegion('regionModal').$el.modal('hide');
23357 return $('.modal-body').html('');
23359 closeModalButton: function(text) {
23360 if (text == null) {
23361 text = 'close';
23363 return API.getButton(t.gettext(text), 'default').on('click', function() {
23364 return API.closeModal();
23367 getModalButtonContainer: function() {
23368 return App.getRegion('regionModalFooter').$el.empty();
23370 getButton: function(text, type) {
23371 if (type == null) {
23372 type = 'primary';
23374 return $('<button>').addClass('btn btn-' + type).text(text);
23376 defaultButtons: function(callback) {
23377 var $ok;
23378 $ok = API.getButton(t.gettext('ok'), 'primary').on('click', function() {
23379 if (callback) {
23380 callback();
23382 return API.closeModal();
23384 return API.getModalButtonContainer().append(API.closeModalButton()).append($ok);
23386 confirmButtons: function(callback) {
23387 var $ok;
23388 $ok = API.getButton(t.gettext('yes'), 'primary').on('click', function() {
23389 if (callback) {
23390 callback();
23392 return API.closeModal();
23394 return API.getModalButtonContainer().append(API.closeModalButton('no')).append($ok);
23396 playerMenu: function(op) {
23397 var $el, openClass;
23398 if (op == null) {
23399 op = 'toggle';
23401 $el = $('.player-menu-wrapper');
23402 openClass = 'opened';
23403 switch (op) {
23404 case 'open':
23405 return $el.addClass(openClass);
23406 case 'close':
23407 return $el.removeClass(openClass);
23408 default:
23409 return $el.toggleClass(openClass);
23412 buildOptions: function(options) {
23413 var $newOption, $option, $wrap, len, n, option;
23414 if (options.length === 0) {
23415 return;
23417 $wrap = $('<ul>').addClass('modal-options options-list');
23418 $option = $('<li>');
23419 for (n = 0, len = options.length; n < len; n++) {
23420 option = options[n];
23421 $newOption = $option.clone();
23422 $newOption.html(option);
23423 $newOption.click(function(e) {
23424 API.closeModal();
23425 return $(this).closest('ul').find('li, span').unbind('click');
23427 $wrap.append($newOption);
23429 return $wrap;
23432 App.commands.setHandler("ui:textinput:show", function(title, options, callback) {
23433 var $input, $msg, el, msg, open, val;
23434 if (options == null) {
23435 options = {};
23437 msg = options.msg ? options.msg : '';
23438 open = options.open ? true : false;
23439 val = options.defaultVal ? options.defaultVal : '';
23440 $input = $('<input>', {
23441 id: 'text-input',
23442 "class": 'form-control',
23443 type: 'text',
23444 value: val
23445 }).on('keyup', function(e) {
23446 if (e.keyCode === 13 && callback) {
23447 callback($('#text-input').val());
23448 return API.closeModal();
23451 $msg = $('<p>').text(msg);
23452 API.defaultButtons(function() {
23453 return callback($('#text-input').val());
23455 API.openModal(title, $msg, callback, open);
23456 el = App.getRegion('regionModalBody').$el.append($input.wrap('<div class="form-control-wrapper"></div>'));
23457 setTimeout(function() {
23458 return el.find('input').first().focus();
23459 }, 200);
23460 return $.material.init();
23462 App.commands.setHandler("ui:modal:close", function() {
23463 return API.closeModal();
23465 App.commands.setHandler("ui:modal:confirm", function(titleHtml, msgHtml, callback) {
23466 if (msgHtml == null) {
23467 msgHtml = '';
23469 API.confirmButtons(function() {
23470 return callback(true);
23472 return API.openModal(titleHtml, msgHtml, true, 'confirm');
23474 App.commands.setHandler("ui:modal:show", function(titleHtml, msgHtml, footerHtml, closeButton, style) {
23475 if (msgHtml == null) {
23476 msgHtml = '';
23478 if (footerHtml == null) {
23479 footerHtml = '';
23481 if (closeButton == null) {
23482 closeButton = false;
23484 if (style == null) {
23485 style = '';
23487 API.getModalButtonContainer().html(footerHtml);
23488 if (closeButton) {
23489 API.getModalButtonContainer().prepend(API.closeModalButton());
23491 return API.openModal(titleHtml, msgHtml, true, style);
23493 App.commands.setHandler("ui:modal:form:show", function(titleHtml, msgHtml, style) {
23494 if (msgHtml == null) {
23495 msgHtml = '';
23497 if (style == null) {
23498 style = 'form';
23500 return API.openModal(titleHtml, msgHtml, true, style);
23502 App.commands.setHandler("ui:modal:close", function() {
23503 return API.closeModal();
23505 App.commands.setHandler("ui:modal:youtube", function(titleHtml, videoid) {
23506 var msgHtml;
23507 API.getModalButtonContainer().html('');
23508 msgHtml = '<iframe width="560" height="315" src="https://www.youtube.com/embed/' + videoid + '?rel=0&amp;showinfo=0&amp;autoplay=1" frameborder="0" allowfullscreen></iframe>';
23509 return API.openModal(titleHtml, msgHtml, true, 'video');
23511 App.commands.setHandler("ui:modal:options", function(titleHtml, items) {
23512 var $options;
23513 $options = API.buildOptions(items);
23514 return API.openModal(titleHtml, $options, true, 'options');
23516 App.commands.setHandler("ui:playermenu", function(op) {
23517 return API.playerMenu(op);
23519 App.commands.setHandler("ui:dropdown:bind:close", function($el) {
23520 return $el.on("click", '.dropdown-menu li, .dropdown-menu a', function(e) {
23521 return $(e.target).closest('.dropdown-menu').parent().removeClass('open').trigger('hide.bs.dropdown');
23524 return App.vent.on("shell:ready", (function(_this) {
23525 return function(options) {
23526 return $('html').on('click', function() {
23527 return API.playerMenu('close');
23530 })(this));