a";cd=b3.getElementsByTagName("*")||[];cb=b3.getElementsByTagName("a")[0];if(!cb||!cb.style||!cd.length){return ce}cc=l.createElement("select");b5=cc.appendChild(l.createElement("option"));ca=b3.getElementsByTagName("input")[0];cb.style.cssText="top:1px;float:left;opacity:.5";ce.getSetAttribute=b3.className!=="t";ce.leadingWhitespace=b3.firstChild.nodeType===3;ce.tbody=!b3.getElementsByTagName("tbody").length;ce.htmlSerialize=!!b3.getElementsByTagName("link").length;ce.style=/top/.test(cb.getAttribute("style"));ce.hrefNormalized=cb.getAttribute("href")==="/a";ce.opacity=/^0.5/.test(cb.style.opacity);ce.cssFloat=!!cb.style.cssFloat;ce.checkOn=!!ca.value;ce.optSelected=b5.selected;ce.enctype=!!l.createElement("form").enctype;ce.html5Clone=l.createElement("nav").cloneNode(true).outerHTML!=="<:nav>";ce.inlineBlockNeedsLayout=false;ce.shrinkWrapBlocks=false;ce.pixelPosition=false;ce.deleteExpando=true;ce.noCloneEvent=true;ce.reliableMarginRight=true;ce.boxSizingReliable=true;ca.checked=true;ce.noCloneChecked=ca.cloneNode(true).checked;cc.disabled=true;ce.optDisabled=!b5.disabled;try{delete b3.test}catch(b8){ce.deleteExpando=false}ca=l.createElement("input");ca.setAttribute("value","");ce.input=ca.getAttribute("value")==="";ca.value="t";ca.setAttribute("type","radio");ce.radioValue=ca.value==="t";ca.setAttribute("checked","t");ca.setAttribute("name","t");b9=l.createDocumentFragment();b9.appendChild(ca);ce.appendChecked=ca.checked;ce.checkClone=b9.cloneNode(true).cloneNode(true).lastChild.checked;if(b3.attachEvent){b3.attachEvent("onclick",function(){ce.noCloneEvent=false});b3.cloneNode(true).click()}for(b6 in {submit:true,change:true,focusin:true}){b3.setAttribute(b7="on"+b6,"t");ce[b6+"Bubbles"]=b7 in a1||b3.attributes[b7].expando===false}b3.style.backgroundClip="content-box";b3.cloneNode(true).style.backgroundClip="";ce.clearCloneStyle=b3.style.backgroundClip==="content-box";for(b6 in bI(ce)){break}ce.ownLast=b6!=="0";bI(function(){var cf,ci,ch,cg="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",e=l.getElementsByTagName("body")[0];if(!e){return}cf=l.createElement("div");cf.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px";e.appendChild(cf).appendChild(b3);b3.innerHTML="
").append(bI.parseHTML(ca)).find(e):ca)}).complete(b9&&function(cb,ca){b3.each(b9,b4||[cb.responseText,ca,cb])})}return this};bI.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,b3){bI.fn[b3]=function(b4){return this.on(b3,b4)}});bI.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:W,type:"GET",isLocal:A.test(b1[1]),global:true,processData:true,async:true,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":aU,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":true,"text json":bI.parseJSON,"text xml":bI.parseXML},flatOptions:{url:true,context:true}},ajaxSetup:function(b3,e){return e?r(r(b3,bI.ajaxSettings),e):r(bI.ajaxSettings,b3)},ajaxPrefilter:bK(v),ajaxTransport:bK(a6),ajax:function(b7,b4){if(typeof b7==="object"){b4=b7;b7=aE}b4=b4||{};var cg,ci,b8,cn,cc,b3,cj,b5,cb=bI.ajaxSetup({},b4),cp=cb.context||cb,ce=cb.context&&(cp.nodeType||cp.jquery)?bI(cp):bI.event,co=bI.Deferred(),cl=bI.Callbacks("once memory"),b9=cb.statusCode||{},cf={},cm={},b6=0,ca="canceled",ch={readyState:0,getResponseHeader:function(cq){var e;if(b6===2){if(!b5){b5={};while((e=ae.exec(cn))){b5[e[1].toLowerCase()]=e[2]}}e=b5[cq.toLowerCase()]}return e==null?null:e},getAllResponseHeaders:function(){return b6===2?cn:null},setRequestHeader:function(cq,cr){var e=cq.toLowerCase();if(!b6){cq=cm[e]=cm[e]||cq;cf[cq]=cr}return this},overrideMimeType:function(e){if(!b6){cb.mimeType=e}return this},statusCode:function(cq){var e;if(cq){if(b6<2){for(e in cq){b9[e]=[b9[e],cq[e]]}}else{ch.always(cq[ch.status])}}return this},abort:function(cq){var e=cq||ca;if(cj){cj.abort(e)}cd(0,e);return this}};co.promise(ch).complete=cl.add;ch.success=ch.done;ch.error=ch.fail;cb.url=((b7||cb.url||W)+"").replace(an,"").replace(aF,b1[1]+"//");cb.type=b4.method||b4.type||cb.method||cb.type;cb.dataTypes=bI.trim(cb.dataType||"*").toLowerCase().match(aa)||[""];if(cb.crossDomain==null){cg=aS.exec(cb.url.toLowerCase());cb.crossDomain=!!(cg&&(cg[1]!==b1[1]||cg[2]!==b1[2]||(cg[3]||(cg[1]==="http:"?"80":"443"))!==(b1[3]||(b1[1]==="http:"?"80":"443"))))}if(cb.data&&cb.processData&&typeof cb.data!=="string"){cb.data=bI.param(cb.data,cb.traditional)}n(v,cb,b4,ch);if(b6===2){return ch}b3=cb.global;if(b3&&bI.active++===0){bI.event.trigger("ajaxStart")}cb.type=cb.type.toUpperCase();cb.hasContent=!o.test(cb.type);b8=cb.url;if(!cb.hasContent){if(cb.data){b8=(cb.url+=(ax.test(b8)?"&":"?")+cb.data);delete cb.data}if(cb.cache===false){cb.url=M.test(b8)?b8.replace(M,"$1_="+bN++):b8+(ax.test(b8)?"&":"?")+"_="+bN++}}if(cb.ifModified){if(bI.lastModified[b8]){ch.setRequestHeader("If-Modified-Since",bI.lastModified[b8])}if(bI.etag[b8]){ch.setRequestHeader("If-None-Match",bI.etag[b8])}}if(cb.data&&cb.hasContent&&cb.contentType!==false||b4.contentType){ch.setRequestHeader("Content-Type",cb.contentType)}ch.setRequestHeader("Accept",cb.dataTypes[0]&&cb.accepts[cb.dataTypes[0]]?cb.accepts[cb.dataTypes[0]]+(cb.dataTypes[0]!=="*"?", "+aU+"; q=0.01":""):cb.accepts["*"]);for(ci in cb.headers){ch.setRequestHeader(ci,cb.headers[ci])}if(cb.beforeSend&&(cb.beforeSend.call(cp,ch,cb)===false||b6===2)){return ch.abort()}ca="abort";for(ci in {success:1,error:1,complete:1}){ch[ci](cb[ci])}cj=n(a6,cb,b4,ch);if(!cj){cd(-1,"No Transport")}else{ch.readyState=1;if(b3){ce.trigger("ajaxSend",[ch,cb])}if(cb.async&&cb.timeout>0){cc=setTimeout(function(){ch.abort("timeout")},cb.timeout)}try{b6=1;cj.send(cf,cd)}catch(ck){if(b6<2){cd(-1,ck)}else{throw ck}}}function cd(cu,cq,cv,cs){var e,cy,cw,ct,cx,cr=cq;if(b6===2){return}b6=2;if(cc){clearTimeout(cc)}cj=aE;cn=cs||"";ch.readyState=cu>0?4:0;e=cu>=200&&cu<300||cu===304;if(cv){ct=g(cb,ch,cv)}ct=ad(cb,ct,ch,e);if(e){if(cb.ifModified){cx=ch.getResponseHeader("Last-Modified");if(cx){bI.lastModified[b8]=cx}cx=ch.getResponseHeader("etag");if(cx){bI.etag[b8]=cx}}if(cu===204||cb.type==="HEAD"){cr="nocontent"}else{if(cu===304){cr="notmodified"}else{cr=ct.state;cy=ct.data;cw=ct.error;e=!cw}}}else{cw=cr;if(cu||!cr){cr="error";if(cu<0){cu=0}}}ch.status=cu;ch.statusText=(cq||cr)+"";if(e){co.resolveWith(cp,[cy,cr,ch])}else{co.rejectWith(cp,[ch,cr,cw])}ch.statusCode(b9);b9=aE;if(b3){ce.trigger(e?"ajaxSuccess":"ajaxError",[ch,cb,e?cy:cw])}cl.fireWith(cp,[ch,cr]);if(b3){ce.trigger("ajaxComplete",[ch,cb]);if(!(--bI.active)){bI.event.trigger("ajaxStop")}}}return ch},getJSON:function(e,b3,b4){return bI.get(e,b3,b4,"json")},getScript:function(e,b3){return bI.get(e,aE,b3,"script")}});bI.each(["get","post"],function(e,b3){bI[b3]=function(b4,b6,b7,b5){if(bI.isFunction(b6)){b5=b5||b7;b7=b6;b6=aE}return bI.ajax({url:b4,type:b3,dataType:b5,data:b6,success:b7})}});function g(ca,b9,b6){var e,b5,b4,b7,b3=ca.contents,b8=ca.dataTypes;while(b8[0]==="*"){b8.shift();if(b5===aE){b5=ca.mimeType||b9.getResponseHeader("Content-Type")}}if(b5){for(b7 in b3){if(b3[b7]&&b3[b7].test(b5)){b8.unshift(b7);break}}}if(b8[0] in b6){b4=b8[0]}else{for(b7 in b6){if(!b8[0]||ca.converters[b7+" "+b8[0]]){b4=b7;break}if(!e){e=b7}}b4=b4||e}if(b4){if(b4!==b8[0]){b8.unshift(b4)}return b6[b4]}}function ad(ce,b6,cb,b4){var b3,b9,cc,b7,b5,cd={},ca=ce.dataTypes.slice();if(ca[1]){for(cc in ce.converters){cd[cc.toLowerCase()]=ce.converters[cc]}}b9=ca.shift();while(b9){if(ce.responseFields[b9]){cb[ce.responseFields[b9]]=b6}if(!b5&&b4&&ce.dataFilter){b6=ce.dataFilter(b6,ce.dataType)}b5=b9;b9=ca.shift();if(b9){if(b9==="*"){b9=b5}else{if(b5!=="*"&&b5!==b9){cc=cd[b5+" "+b9]||cd["* "+b9];if(!cc){for(b3 in cd){b7=b3.split(" ");if(b7[1]===b9){cc=cd[b5+" "+b7[0]]||cd["* "+b7[0]];if(cc){if(cc===true){cc=cd[b3]}else{if(cd[b3]!==true){b9=b7[0];ca.unshift(b7[1])}}break}}}}if(cc!==true){if(cc&&ce["throws"]){b6=cc(b6)}else{try{b6=cc(b6)}catch(b8){return{state:"parsererror",error:cc?b8:"No conversion from "+b5+" to "+b9}}}}}}}}return{state:"success",data:b6}}bI.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(e){bI.globalEval(e);return e}}});bI.ajaxPrefilter("script",function(e){if(e.cache===aE){e.cache=false}if(e.crossDomain){e.type="GET";e.global=false}});bI.ajaxTransport("script",function(b4){if(b4.crossDomain){var e,b3=l.head||bI("head")[0]||l.documentElement;return{send:function(b5,b6){e=l.createElement("script");e.async=true;if(b4.scriptCharset){e.charset=b4.scriptCharset}e.src=b4.url;e.onload=e.onreadystatechange=function(b8,b7){if(b7||!e.readyState||/loaded|complete/.test(e.readyState)){e.onload=e.onreadystatechange=null;if(e.parentNode){e.parentNode.removeChild(e)}e=null;if(!b7){b6(200,"success")}}};b3.insertBefore(e,b3.firstChild)},abort:function(){if(e){e.onload(aE,true)}}}}});var bp=[],a4=/(=)\?(?=&|$)|\?\?/;bI.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=bp.pop()||(bI.expando+"_"+(bN++));this[e]=true;return e}});bI.ajaxPrefilter("json jsonp",function(b5,e,b6){var b8,b3,b4,b7=b5.jsonp!==false&&(a4.test(b5.url)?"url":typeof b5.data==="string"&&!(b5.contentType||"").indexOf("application/x-www-form-urlencoded")&&a4.test(b5.data)&&"data");if(b7||b5.dataTypes[0]==="jsonp"){b8=b5.jsonpCallback=bI.isFunction(b5.jsonpCallback)?b5.jsonpCallback():b5.jsonpCallback;if(b7){b5[b7]=b5[b7].replace(a4,"$1"+b8)}else{if(b5.jsonp!==false){b5.url+=(ax.test(b5.url)?"&":"?")+b5.jsonp+"="+b8}}b5.converters["script json"]=function(){if(!b4){bI.error(b8+" was not called")}return b4[0]};b5.dataTypes[0]="json";b3=a1[b8];a1[b8]=function(){b4=arguments};b6.always(function(){a1[b8]=b3;if(b5[b8]){b5.jsonpCallback=e.jsonpCallback;bp.push(b8)}if(b4&&bI.isFunction(b3)){b3(b4[0])}b4=b3=aE});return"script"}});var af,av,aw=0,aN=a1.ActiveXObject&&function(){var e;for(e in af){af[e](aE,true)}};function bC(){try{return new a1.XMLHttpRequest()}catch(b3){}}function bd(){try{return new a1.ActiveXObject("Microsoft.XMLHTTP")}catch(b3){}}bI.ajaxSettings.xhr=a1.ActiveXObject?function(){return !this.isLocal&&bC()||bd()}:bC;av=bI.ajaxSettings.xhr();bI.support.cors=!!av&&("withCredentials" in av);av=bI.support.ajax=!!av;if(av){bI.ajaxTransport(function(e){if(!e.crossDomain||bI.support.cors){var b3;return{send:function(b9,b4){var b7,b5,b8=e.xhr();if(e.username){b8.open(e.type,e.url,e.async,e.username,e.password)}else{b8.open(e.type,e.url,e.async)}if(e.xhrFields){for(b5 in e.xhrFields){b8[b5]=e.xhrFields[b5]}}if(e.mimeType&&b8.overrideMimeType){b8.overrideMimeType(e.mimeType)}if(!e.crossDomain&&!b9["X-Requested-With"]){b9["X-Requested-With"]="XMLHttpRequest"}try{for(b5 in b9){b8.setRequestHeader(b5,b9[b5])}}catch(b6){}b8.send((e.hasContent&&e.data)||null);b3=function(cc,cb){var ca,cd,cg,ce;try{if(b3&&(cb||b8.readyState===4)){b3=aE;if(b7){b8.onreadystatechange=bI.noop;if(aN){delete af[b7]}}if(cb){if(b8.readyState!==4){b8.abort()}}else{ce={};ca=b8.status;cd=b8.getAllResponseHeaders();if(typeof b8.responseText==="string"){ce.text=b8.responseText}try{cg=b8.statusText}catch(cf){cg=""}if(!ca&&e.isLocal&&!e.crossDomain){ca=ce.text?200:404}else{if(ca===1223){ca=204}}}}}catch(ch){if(!cb){b4(-1,ch)}}if(ce){b4(ca,cg,ce,cd)}};if(!e.async){b3()}else{if(b8.readyState===4){setTimeout(b3)}else{b7=++aw;if(aN){if(!af){af={};bI(a1).unload(aN)}af[b7]=b3}b8.onreadystatechange=b3}}},abort:function(){if(b3){b3(aE,true)}}}}})}var J,ab,bQ=/^(?:toggle|show|hide)$/,bJ=new RegExp("^(?:([+-])=|)("+bz+")([a-z%]*)$","i"),bP=/queueHooks$/,az=[h],aY={"*":[function(e,b8){var ca=this.createTween(e,b8),b6=ca.cur(),b5=bJ.exec(b8),b9=b5&&b5[3]||(bI.cssNumber[e]?"":"px"),b3=(bI.cssNumber[e]||b9!=="px"&&+b6)&&bJ.exec(bI.css(ca.elem,e)),b4=1,b7=20;if(b3&&b3[3]!==b9){b9=b9||b3[3];b5=b5||[];b3=+b6||1;do{b4=b4||".5";b3=b3/b4;bI.style(ca.elem,e,b3+b9)}while(b4!==(b4=ca.cur()/b6)&&b4!==1&&--b7)}if(b5){b3=ca.start=+b3||+b6||0;ca.unit=b9;ca.end=b5[1]?b3+(b5[1]+1)*b5[2]:+b5[2]}return ca}]};function bl(){setTimeout(function(){J=aE});return(J=bI.now())}function ba(b6,b8,b5){var b3,b7=(aY[b8]||[]).concat(aY["*"]),e=0,b4=b7.length;for(;e-1,cb={},ca={},b4,b6;if(cd){ca=b7.position();b4=ca.top;b6=ca.left}else{b4=parseFloat(e)||0;b6=parseFloat(cc)||0}if(bI.isFunction(ce)){ce=ce.call(b5,b8,b3)}if(ce.top!=null){cb.top=(ce.top-b3.top)+b4}if(ce.left!=null){cb.left=(ce.left-b3.left)+b6}if("using" in ce){ce.using.call(b5,cb)}else{b7.css(cb)}}};bI.fn.extend({position:function(){if(!this[0]){return}var b4,b5,e={top:0,left:0},b3=this[0];if(bI.css(b3,"position")==="fixed"){b5=b3.getBoundingClientRect()}else{b4=this.offsetParent();b5=this.offset();if(!bI.nodeName(b4[0],"html")){e=b4.offset()}e.top+=bI.css(b4[0],"borderTopWidth",true);e.left+=bI.css(b4[0],"borderLeftWidth",true)}return{top:b5.top-e.top-bI.css(b3,"marginTop",true),left:b5.left-e.left-bI.css(b3,"marginLeft",true)}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||bV;while(e&&(!bI.nodeName(e,"html")&&bI.css(e,"position")==="static")){e=e.offsetParent}return e||bV})}});bI.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(b4,b3){var e=/Y/.test(b3);bI.fn[b4]=function(b5){return bI.access(this,function(b6,b9,b8){var b7=bo(b6);if(b8===aE){return b7?(b3 in b7)?b7[b3]:b7.document.documentElement[b9]:b6[b9]}if(b7){b7.scrollTo(!e?b8:bI(b7).scrollLeft(),e?b8:bI(b7).scrollTop())}else{b6[b9]=b8}},b4,b5,arguments.length,null)}});function bo(e){return bI.isWindow(e)?e:e.nodeType===9?e.defaultView||e.parentWindow:false}bI.each({Height:"height",Width:"width"},function(e,b3){bI.each({padding:"inner"+e,content:b3,"":"outer"+e},function(b4,b5){bI.fn[b5]=function(b9,b8){var b7=arguments.length&&(b4||typeof b9!=="boolean"),b6=b4||(b9===true||b8===true?"margin":"border");return bI.access(this,function(cb,ca,cc){var cd;if(bI.isWindow(cb)){return cb.document.documentElement["client"+e]}if(cb.nodeType===9){cd=cb.documentElement;return Math.max(cb.body["scroll"+e],cd["scroll"+e],cb.body["offset"+e],cd["offset"+e],cd["client"+e])}return cc===aE?bI.css(cb,ca,b6):bI.style(cb,ca,cc,b6)},b3,b7?b9:aE,b7,null)}})});bI.fn.size=function(){return this.length};bI.fn.andSelf=bI.fn.addBack;if(typeof module==="object"&&module&&typeof module.exports==="object"){module.exports=bI}else{a1.jQuery=a1.$=bI;if(typeof define==="function"&&define.amd){define("jquery",[],function(){return bI})}}})(window);
+/*! JsRender v1.0.0-beta: http://github.com/BorisMoore/jsrender and http://jsviews.com/jsviews */
+(function(a,w,p){if(w&&w.views||a.jsviews){return}var af="v1.0.0-beta",L,ab,s,x,n="{",m="{",g="}",f="}",X="^",y=/^(?:null|true|false|\d[\d.]*|([\w$]+|\.|~([\w$]+)|#(view|([\w$]+))?)([\w$.^]*?)(?:[.[^]([\w$]+)\]?)?)$/g,Y=/(\()(?=\s*\()|(?:([([])\s*)?(?:(\^?)([#~]?[\w$.^]+)?\s*((\+\+|--)|\+|-|&&|\|\||===|!==|==|!=|<=|>=|[<>%*!:?\/]|(=))\s*|([#~]?[\w$.^]+)([([])?)|(,\s*)|(\(?)\\?(?:(')|("))|(?:\s*(([)\]])(?=\s*\.|\s*\^)|[)\]])([([]?))|(\s+)/g,S=/[ \t]*(\r\n|\n|\r)/g,e=/\\(['"])/g,ad=/['"\\]/g,o=/\x08(~)?([^\x08]+)\x08/g,j=/^if\s/,r=/<(\w+)[>\s]/,N=/[\x00`><"'&]/g,I=N,ag=0,ae=0,K={"&":"&","<":"<",">":">","\x00":"","'":"'",'"':""","`":"`"},V="data-jsv-tmpl",O={},R={template:{compile:b},tag:{compile:q},helper:{},converter:{}},M={jsviews:af,render:O,settings:{delimiters:u,debugMode:true,tryCatch:true},sub:{View:ac,Error:D,tmplFn:B,parse:v,extend:t,error:F,syntaxError:Q},_cnvt:G,_tag:c,_err:function(ah){return T.debugMode?("Error: "+(ah.message||ah))+". ":""}};function D(ai,ah){if(ah&&ah.onError){if(ah.onError(ai)===false){return}}this.name="JsRender Error";this.message=ai||"JsRender error"}function t(aj,ai){var ah;aj=aj||{};for(ah in ai){aj[ah]=ai[ah]}return aj}(D.prototype=new Error()).constructor=D;function u(ah,aj,ai){if(!U.rTag||arguments.length){n=ah?ah.charAt(0):n;m=ah?ah.charAt(1):m;g=aj?aj.charAt(0):g;f=aj?aj.charAt(1):f;X=ai||X;ah="\\"+n+"(\\"+X+")?\\"+m;aj="\\"+g+"\\"+f;s="(?:(?:(\\w+(?=[\\/\\s\\"+g+"]))|(?:(\\w+)?(:)|(>)|!--((?:[^-]|-(?!-))*)--|(\\*)))\\s*((?:[^\\"+g+"]|\\"+g+"(?!\\"+f+"))*?)";U.rTag=s+")";s=new RegExp(ah+s+"(\\/)?|(?:\\/(\\w+)))"+aj,"g");x=new RegExp("<.*>|([^\\\\]|^)[{}]|"+ah+".*"+aj)}return[n,m,g,f,X]}function J(al,an){if(!an){an=al;al=p}var ak,am,aj,ao,ai=this,ah=!an||an==="root";if(al){ao=ai.type===an?ai:p;if(!ao){ak=ai.views;if(ai._.useKey){for(am in ak){if(ao=ak[am].get(al,an)){break}}}else{for(am=0,aj=ak.length;!ao&&am0){try{aj=aq.nodeType>0?aq:!x.test(aq)&&w&&w(a.document).find(aq)[0]}catch(ar){}if(aj){aq=aj.getAttribute(V);ah=ah||aq;aq=E[aq];if(!aq){ah=ah||"_"+ag++;aj.setAttribute(V,ah);aq=E[ah]=b(ah,aj.innerHTML,ao,am,ai,ap)}}return aq}}var al,aj;an=an||"";al=ak(an);ap=ap||(an.markup?an:{});ap.tmplName=ah;if(ao){ap._parentTmpl=ao}if(!al&&an.markup&&(al=ak(an.markup))){if(al.fn&&(al.debug!==an.debug||al.allowCode!==an.allowCode)){al=al.markup}}if(al!==p){if(ah&&!ao){O[ah]=function(){return an.render.apply(an,arguments)}}if(al.fn||an.fn){if(al.fn){if(ah&&ah!==al.tmplName){an=aa(ap,al)}else{an=al}}}else{an=h(al,ap);B(al,an)}k(ap);return an}}function h(ak,al){var ah,ai=T.wrapMap||{},aj=t({markup:ak,tmpls:[],links:{},tags:{},bnds:[],_is:"template",render:l},al);if(!al.htmlTag){ah=r.exec(ak);aj.htmlTag=ah?ah[1].toLowerCase():""}ah=ai[aj.htmlTag];if(ah&&ah!==ai.div){aj.markup=L.trim(aj.markup);aj._elCnt=true}return aj}function d(aj,ai){function ak(al,an,ar){var am,aq,ap,ao;if(al&&""+al!==al&&!al.nodeType&&!al.markup){for(ap in al){ak(ap,al[ap],an)}return M}if(an===p){an=al;al=p}if(al&&""+al!==al){ar=an;an=al;al=p}ao=ar?ar[ah]=ar[ah]||{}:ak;aq=ai.compile;if(am=U.onBeforeStoreItem){aq=am(ao,al,an,aq)||aq}if(!al){an=aq(p,an)}else{if(an===null){delete ao[al]}else{ao[al]=aq?(an=aq(al,an,ar,aj,ai)):an}}if(aq&&an){an._is=aj}if(am=U.onStoreItem){am(ao,al,an,aq)}return an}var ah=aj+"s";M[ah]=ak;R[aj]=ai}function l(aC,aj,ak,aD,am,al){var ax,aw,ao,az,aB,au,at,ap,ai,aA,ay,ah,an,av=this,ar=!av.attr||av.attr==="html",aq="";if(aD===true){at=true;aD=0}if(av.tag){ap=av;av=av.tag;aA=av._;ah=av.tagName;an=ap.tmpl;aj=aa(aj,av.ctx);ai=ap.content;if(ap.props.link===false){aj=aj||{};aj.link=false}ak=ak||ap.view;aC=aC===p?ak:aC}else{an=av.jquery&&(av[0]||F('Unknown template: "'+av.selector+'"'))||av}if(an){if(!ak&&aC&&aC._is==="view"){ak=aC}if(ak){ai=ai||ak.content;al=al||ak._.onRender;if(aC===ak){aC=ak.data;am=true}aj=aa(aj,ak.ctx)}if(!ak||ak.data===p){(aj=aj||{}).root=aC}if(!an.fn){an=E[an]||E(an)}if(an){al=(aj&&aj.link)!==false&&ar&&al;ay=al;if(al===true){ay=p;al=ak._.onRender}if(L.isArray(aC)&&!am){az=at?ak:(aD!==p&&ak)||ac(aj,"array",ak,aC,an,aD,ai,al);for(ax=0,aw=aC.length;ax":aM+ap}if(ah){ay="prm"+aG;ah="try{var "+ay+"=["+aL+"][0];}catch(e){"+ay+'="";}\n';aL=ay}}else{if(aC){ar=h(aI,aw);ar.tmplName=ai+"/"+ap;A(aC,ar);aq.push(ar)}if(!aH){aE=ap;al=ak;ak=""}at=av[aG+1];at=at&&at[0]==="else"}an+=",args:["+aL+"]}";if(aO&&aN||aM&&ap!==">"){aF=new Function("data,view,j,u"," // "+ai+" "+aK+" "+ap+"\n"+ah+"return {"+an+";");aF.paths=aN;aF._ctxs=ap;if(au){return aF}az=1}ak+=(aO?"\n"+(aN?"":ah)+(au?"return ":"ret+=")+(az?(az=0,ao=true,'c("'+aM+'",view,'+(aN?((aj[aK-1]=aF),aK):"{"+an)+");"):ap===">"?(am=true,"h("+aL+");"):(aD=true,"(v="+aL+")!="+(au?"=":"")+'u?v:"";')):(aP=true,"{tmpl:"+(aC?aq.length:"0")+","+an+","));if(aE&&!at){ak="["+ak.slice(0,-1)+"]";if(au||aN){ak=new Function("data,view,j,u"," // "+ai+" "+aK+" "+aE+"\nreturn "+ak+";");if(aN){(aj[aK-1]=ak).paths=aN}ak._ctxs=ap;if(au){return ak}}ak=al+'\nret+=t("'+aE+'",view,this,'+(aK||ak)+");";aN=0;aE=0}}}}ak="// "+ai+"\nvar j=j||"+(w?"jQuery.":"js")+"views"+(aD?",v":"")+(aP?",t=j._tag":"")+(ao?",c=j._cnvt":"")+(am?",h=j.converters.html":"")+(au?";\n":',ret="";\n')+(T.tryCatch?"try{\n":"")+(aw.debug?"debugger;":"")+ak+(au?"\n":"\nreturn ret;\n")+(T.tryCatch?"\n}catch(e){return j._err(e);}":"");try{ak=new Function("data,view,j,u",ak)}catch(aJ){Q("Compiled template code:\n\n"+ak,aJ)}if(ax){ax.fn=ak}return ak}function v(am,ai,ar){function at(ay,aP,aA,av,aH,aF,aB,aQ,aw,aG,aO,aN,aJ,aM,aD,aK,az,aL,aC,aE){var ax;aF=aF||"";aA=aA||aP||aN;aH=aH||aw;aG=aG||az||"";function aI(aU,aT,aW,aR,aY,aX,aV){if(aT){if(ai){if(an==="linkTo"){au=ai.to=ai.to||[];au.push(aH)}if(!an||ao){ai.push(aH)}}if(aT!=="."){var aS=(aW?'view.hlp("'+aW+'")':aR?"view":"data")+(aV?(aY?"."+aY:aW?"":(aR?"":"."+aT))+(aX||""):(aV=aW?"":aR?aY||"":aT,""));aS=aS+(aV?"."+aV:"");return aS.slice(0,9)==="view.data"?aS.slice(5):aS}}return aU}if(aB){Q(am)}else{if(ai&&aK&&!ap&&!al){if(!an||ao||au){ax=aq[aj];if(aE.length-2>aC-ax){ax=aE.slice(ax,aC+1);aK=m+":"+ax+g;aK=ak[aK]=ak[aK]||B(n+aK+f,ar,true);if(!aK.paths){v(ax,aK.paths=[],ar)}(au||ai).push({_jsvOb:aK})}}}return(ap?(ap=!aJ,(ap?ay:'"')):al?(al=!aM,(al?ay:'"')):((aA?(aj++,aq[aj]=aC++,aA):"")+(aL?(aj?"":an?(an=ao=au=false,"\b"):","):aQ?(aj&&Q(am),an=aH,ao=av,"\b"+aH+":"):aH?(aH.split("^").join(".").replace(y,aI)+(aG?(ah[++aj]=true,aH.charAt(0)!=="."&&(aq[aj]=aC),aG):aF)):aF?aF:aD?((ah[aj--]=false,aD)+(aG?(ah[++aj]=true,aG):"")):aO?(ah[aj]||Q(am),","):aP?"":(ap=aJ,al=aM,'"'))))}}var an,au,ao,ak=ar.links,ah={},aq={0:-1},aj=0,al=false,ap=false;return(am+" ").replace(Y,at)}function aa(ai,ah){return ai&&ai!==ah?(ah?t(t({},ah),ai):ai):ah&&t({},ah)}function i(ah){return K[ah]||(K[ah]=""+ah.charCodeAt(0)+";")}for(ab in R){d(ab,R[ab])}var E=M.templates,Z=M.converters,C=M.helpers,P=M.tags,U=M.sub,T=M.settings;if(w){L=w;L.fn.render=l}else{L=a.jsviews={};L.isArray=Array&&Array.isArray||function(ah){return Object.prototype.toString.call(ah)==="[object Array]"}}L.render=O;L.views=M;L.templates=E=M.templates;P({"else":function(){},"if":{render:function(aj){var ah=this,ai=(ah.rendering.done||!aj&&(arguments.length||!ah.tagCtx.index))?"":(ah.rendering.done=true,ah.selected=ah.tagCtx.index,ah.tagCtx.render());return ai},onUpdate:function(al,ak,aj){var ai,ah,am;for(ai=0;(ah=this.tagCtxs[ai])&&ah.args.length;ai++){ah=ah.args[0];am=!ah!==!aj[ai].args[0];if(!!ah||am){return am}}return false},flow:true},"for":{render:function(am){var ak=this,aj=ak.tagCtx,al=!arguments.length,ah="",ai=al||0;if(!ak.rendering.done){if(al){ah=p}else{if(am!==p){ah+=aj.render(am);ai+=L.isArray(am)?am.length:1}}if(ak.rendering.done=ai){ak.selected=aj.index}}return ah},onArrayChange:function(ak,ai){var aj,ah=this,al=ai.change;if(this.tagCtxs[1]&&(al==="insert"&&ak.target.length===ai.items.length||al==="remove"&&!ak.target.length||al==="refresh"&&!ai.oldItems.length!==!ak.target.length)){this.refresh()}else{for(aj in ah._.arrVws){aj=ah._.arrVws[aj];if(aj.data===ak.target){aj._.onArrayChange.apply(aj,arguments)}}}ak.done=true},flow:true},include:{flow:true},"*":{render:function(ah){return ah},flow:true}});Z({html:function(ah){return ah!=p?String(ah).replace(I,i):""},attr:function(ah){return ah!=p?String(ah).replace(N,i):ah===null?null:""},url:function(ah){return ah!=p?encodeURI(String(ah)):ah===null?null:""}});u()})(this,this.jQuery);
+/*! JsObservable v1.0.0-alpha: http://github.com/BorisMoore/jsviews and http://jsviews.com/jsviews */
+(function(y,f,k){if(!f){throw"requires jQuery or JsRender"}if(f.observable){return}var c="v1.0.0-alpha",w,n,H,t,A=f.event.special,s=f.views?f.views.sub:{},l=1,G=[].splice,j=[].concat,p=f.isArray,C=f.expando,q="object",E=s.propChng=s.propChng||"propertyChange",e=s.arrChng=s.arrChng||"arrayChange",x=s._cbBnds=s._cbBnds||{},g=E+".observe",u=f.isFunction,i=1,r=1,h=f.hasData;function v(I){return p(I)?new a(I):new b(I)}function b(I){this._data=I;return this}function a(I){this._data=I;return this}function B(I){return p(I)?[I]:I}function m(I){if(typeof I!=="number"){throw"Invalid index."}}function d(P,J){P=p(P)?P:[P];var N,O,L=J,M=L,I=P.length,K=[];for(N=0;N-1){ah=ah[L];continue}}if(L==="*"){if(u(ah)){if(O=ah.depends){o(O,M,I||P)}}else{S(ai,L)}break}else{if(L&&!(u(O=ah[L])&&O.depends)){S(ai+"."+L,T.join("."))}}}L=L?ah[L]:ah}if(u(L)){if(O=L.depends){o(ah,d(O,ah),M,N,I||B(P))}break}ah=L}}aa(ah,I);if(V){F(ae,V)}return{cbId:V,bnd:ae,leaf:ah}}function D(){[].push.call(arguments,true);return o.apply(this,arguments)}f.observable=v;v.Object=b;v.Array=a;v.observe=o;v.unobserve=D;b.prototype={_data:null,data:function(){return this._data},observe:function(I,J){return o(this._data,I,J)},unobserve:function(I,J){return D(this._data,I,J)},setProperty:function(Q,N,L){var M,O,J,K,P=this,I=P._data;Q=Q||"";if(I){if(p(Q)){O=Q.length;while(O--){J=Q[O];P.setProperty(J.name,J.value,L===k||L)}}else{if(""+Q!==Q){for(O in Q){P.setProperty(O,Q[O],N)}}else{if(Q.indexOf(C)<0){K=Q.split(".");while(I&&K.length>1){I=I[K.shift()]}P._setProperty(I,K.join("."),N,L)}}}}return P},_setProperty:function(J,N,M,L){var O,I,K=N?J[N]:J;if(u(K)){if(K.set){I=K;O=K.set===true?K:K.set;K=K.call(J)}}if(K!==M||L&&K!=M){if(!(K instanceof Date)||K>M||K1){J=p(J)?J:[J];if(J.length){this._insert(I,J)}}return this},_insert:function(I,J){t=this._data;H=t.length;G.apply(t,[I,0].concat(J));this._trigger({change:"insert",index:I,items:J})},remove:function(J,K){m(J);K=(K===k||K===null)?1:K;if(K&&J>-1){var I=this._data.slice(J,J+K);K=I.length;if(K){this._remove(J,K,I)}}return this},_remove:function(J,K,I){t=this._data;H=t.length;t.splice(J,K);this._trigger({change:"remove",index:J,items:I})},move:function(L,K,I){m(L);m(K);I=(I===k||I===null)?1:I;if(I){var J=this._data.slice(L,L+I);this._move(L,K,I,J)}return this},_move:function(L,K,I,J){t=this._data;H=t.length;t.splice(L,I);t.splice.apply(t,[K,0].concat(J));this._trigger({change:"move",oldIndex:L,index:K,items:J})},refresh:function(I){var J=this._data.slice(0);this._refresh(J,I);return this},_refresh:function(J,I){t=this._data;H=t.length;G.apply(t,[0,t.length].concat(I));this._trigger({change:"refresh",oldItems:J})},_trigger:function(I){var K=t.length,J=f([t]);J.triggerHandler(e,I);if(K!==H){J.triggerHandler(E,{path:"length",value:K,oldValue:H})}}};A[E]=A[e]={remove:function(I){if((I=I.data)&&(I.off=1,I=I.cb)){w=x[n=I._bnd]}},teardown:function(I){if(w){delete w[f.data(this,"obId")];F(w,n)}}}})(this,this.jQuery||this.jsviews);
+/*! JsViews v1.0.0-alpha: http://github.com/BorisMoore/jsviews and http://jsviews.com/jsviews */
+(function(r,az,u){if(!az){throw"requires jQuery"}if(!az.views){throw"requires JsRender"}if(!az.observable){throw"requires jquery.observable"}if(az.link){return}var g="v1.0.0-alpha",V,aG,aP,e,au,at,M,L,ax,b,Q=r.document,h=az.views,K=h.sub,m=h.settings,n=K.extend,O=K.View(u,"top"),d=az.isFunction,l=h.templates,S=az.observable,aO=S.observe,af="data-jsv",ab=m.linkAttr||"data-link",q=m.propChng=m.propChng||"propertyChange",Z=K.arrChng=K.arrChng||"arrayChange",aH=K._cbBnds=K._cbBnds||{},P="change.jsv",y="onBeforeChange",x="onAfterChange",aB="onAfterCreate",c='"><\/script>',aE='',
+ openScript = ' - data-linked tag, close marker
+ preceding = id
+ ? (preceding + endOfElCnt + spaceBefore + openScript + id + closeScript + spaceAfter + tag)
+ : endOfElCnt || all;
+ }
+ if (tag) {
+ // If there are ids (markers since the last tag), move them to the defer string
+ tagStack.unshift(parentTag);
+ parentTag = tag.slice(1);
+ if (tagStack[0] === badParent[parentTag]) {
+ // TODO: move this to design-time validation check
+ error('"' + parentTag + '" has incorrect parent tag');
+ }
+ if ((elCnt = elContent[parentTag]) && !prevElCnt) {
+ deferStack.unshift(defer);
+ defer = "";
+ }
+ prevElCnt = elCnt;
+//TODO Consider providing validation which throws if you place as child of
, etc. - since if not caught,
+//this can cause errors subsequently which are difficult to debug.
+// if (elContent[tagStack[0]]>2 && !elCnt) {
+// error(parentTag + " in " + tagStack[0]);
+// }
+ if (defer && elCnt) {
+ defer += "+"; // Will be used for stepping back through deferred tokens
+ }
+ }
+ return preceding;
+ }
+
+ function processViewInfos(vwInfos, targetParent) {
+ // If targetParent, we are processing viewInfos (which may include navigation through '+-' paths) and hooking up to the right parentElem etc.
+ // (and elem may also be defined - the next node)
+ // If no targetParent, then we are processing viewInfos on newly inserted content
+ var deferPath, deferChar, bindChar, parentElem, id, onAftCr, deep,
+ addedBindEls = [];
+
+ // In elCnt context (element-only content model), prevNode is the first node after the open, nextNode is the first node after the close.
+ // If both are null/undefined, then open and close are at end of parent content, so the view is empty, and its placeholder is the
+ // 'lastChild' of the parentNode. If there is a prevNode, then it is either the first node in the view, or the view is empty and
+ // its placeholder is the 'previousSibling' of the prevNode, which is also the nextNode.
+ if (vwInfos) {
+ //targetParent = targetParent || targetElem && targetElem.previousSibling;
+ //targetParent = targetElem ? targetElem.previousSibling : targetParent;
+ len = vwInfos.length;
+ if (vwInfos.tokens.charAt(0) === "@") {
+ // This is a special script element that was created in convertMarkers() to process deferred bindings, and inserted following the
+ // target parent element - because no element tags were encountered to carry those binding tokens.
+ targetParent = elem.previousSibling;
+ elem.parentNode.removeChild(elem);
+ elem = null;
+ }
+ len = vwInfos.length;
+ while (len--) {
+ vwInfo = vwInfos[len];
+ //if (prevIds.indexOf(vwInfo.token) < 0) { // This token is a newly created view or tag binding
+ bindChar = vwInfo.ch;
+ if (deferPath = vwInfo.path) {
+ // We have a 'deferred path'
+ j = deferPath.length - 1;
+ while (deferChar = deferPath.charAt(j--)) {
+ // Use the "+" and"-" characters to navigate the path back to the original parent node where the deferred bindings ocurred
+ if (deferChar === "+") {
+ if (deferPath.charAt(j) === "-") {
+ j--;
+ targetParent = targetParent.previousSibling;
+ } else {
+ targetParent = targetParent.parentNode;
+ }
+ } else {
+ targetParent = targetParent.lastChild;
+ }
+ // Note: Can use previousSibling and lastChild, not previousElementSibling and lastElementChild,
+ // since we have removed white space within elCnt. Hence support IE < 9
+ }
+ }
+ if (bindChar === "^") {
+ if (tag = bindingStore[id = vwInfo.id]) {
+ // The binding may have been deleted, for example in a different handler to an array collectionChange event
+ // This is a tag binding
+ deep = targetParent && (!elem || elem.parentNode !== targetParent);
+ if (!elem || deep) {
+ tag.parentElem = targetParent;
+ }
+ if (vwInfo.elCnt) {
+ if (vwInfo.open) {
+ if (targetParent) {
+ // This is an 'open view' node (preceding script marker node,
+ // or if elCnt, the first element in the view, with a data-jsv annotation) for binding
+ targetParent._dfr = "#" + id + bindChar + (targetParent._dfr || "");
+ }
+ } else if (deep) {
+ // There is no ._nxt so add token to _dfr. It is deferred.
+ targetParent._dfr = "/" + id + bindChar + (targetParent._dfr || "");
+ }
+ }
+ // This is an open or close marker for a data-linked tag {^{...}}. Add it to bindEls.
+ addedBindEls.push([deep ? null : elem, vwInfo]);
+ }
+ } else if (view = viewStore[id = vwInfo.id]) {
+ // The view may have been deleted, for example in a different handler to an array collectionChange event
+ if (!view.link) {
+ // If view is not already extended for JsViews, extend and initialize the view object created in JsRender, as a JsViews view
+ view.parentElem = targetParent || elem && elem.parentNode || parentNode;
+ $extend(view, LinkedView);
+ view._.onRender = addBindingMarkers;
+ view._.onArrayChange = arrayChangeHandler;
+ setArrayChangeLink(view);
+ }
+ parentElem = view.parentElem;
+ if (vwInfo.open) {
+ // This is an 'open view' node (preceding script marker node,
+ // or if elCnt, the first element in the view, with a data-jsv annotation) for binding
+ view._elCnt = vwInfo.elCnt;
+ if (targetParent) {
+ targetParent._dfr = "#" + id + bindChar + (targetParent._dfr || "");
+ } else {
+ // No targetParent, so there is a ._nxt elem (and this is processing tokens on the elem)
+ if (!view._prv) {
+ parentElem._dfr = removeSubStr(parentElem._dfr, "#" + id + bindChar);
+ }
+ view._prv = elem;
+ }
+ } else {
+ // This is a 'close view' marker node for binding
+ if (targetParent && (!elem || elem.parentNode !== targetParent)) {
+ // There is no ._nxt so add token to _dfr. It is deferred.
+ targetParent._dfr = "/" + id + bindChar + (targetParent._dfr || "");
+ view._nxt = undefined;
+ } else if (elem) {
+ // This view did not have a ._nxt, but has one now, so token may be in _dfr, and must be removed. (No longer deferred)
+ if (!view._nxt) {
+ parentElem._dfr = removeSubStr(parentElem._dfr, "/" + id + bindChar);
+ }
+ view._nxt = elem;
+ }
+ linkCtx = view.linkCtx;
+ if (onAftCr = onAfterCreate || (view.ctx && view.ctx.onAfterCreate)) {
+ onAftCr.call(linkCtx, view);
+ }
+ }
+ //}
+ }
+ }
+ len = addedBindEls.length;
+ while (len--) {
+ // These were added in reverse order to addedBindEls. We push them in BindEls in the correct order.
+ bindEls.push(addedBindEls[len]);
+ }
+ }
+ return !vwInfos || vwInfos.elCnt;
+ }
+
+ function getViewInfos(vwInfos) {
+ // Used by view.childTags() and tag.childTags()
+ // Similar to processViewInfos in how it steps through bindings to find tags. Only finds data-linked tags.
+ var level, parentTag;
+
+ if (len = vwInfos && vwInfos.length) {
+ for (j = 0; j < len; j++) {
+ vwInfo = vwInfos[j];
+ if (get.id) {
+ get.id = get.id !== vwInfo.id && get.id;
+ } else {
+ // This is an open marker for a data-linked tag {^{...}}, within the content of the tag whose id is get.id. Add it to bindEls.
+ parentTag = tag = bindingStore[vwInfo.id].linkCtx.tag;
+ if (!tag.flow) {
+ if (!deep) {
+ level = 1;
+ while (parentTag = parentTag.parent) {
+ level++;
+ }
+ tagDepth = tagDepth || level; // The level of the first tag encountered.
+ }
+ if ((deep || level === tagDepth) && (!tagName || tag.tagName === tagName)) {
+ // Filter on top-level or tagName as appropriate
+ tags.push(tag);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ function dataLink() {
+ //================ Data-link and fixup of data-jsv annotations ================
+ elems = qsa ? parentNode.querySelectorAll(linkViewsSel) : $(linkViewsSel, parentNode).get();
+ l = elems.length;
+
+ // The prevNode will be in the returned query, since we called markPrevOrNextNode() on it.
+ // But it may have contained nodes that satisfy the selector also.
+ if (prevNode) {
+ // Find the last contained node of prevNode, to use as the prevNode - so we only link subsequent elems in the query
+ prevNodes = qsa ? prevNode.querySelectorAll(linkViewsSel) : $(linkViewsSel, prevNode).get();
+ prevNode = prevNodes.length ? prevNodes[prevNodes.length - 1] : prevNode;
+ }
+ tagDepth = 0;
+ for (i = 0; i < l; i++) {
+ elem = elems[i];
+ if (prevNode && !found) {
+ // If prevNode is set, not false, skip linking. If this element is the prevNode, set to false so subsequent elements will link.
+ found = (elem === prevNode);
+ } else if (nextNode && elem === nextNode) {
+ // If nextNode is set then break when we get to nextNode
+ break;
+ } else if (elem.parentNode
+ // elem has not been removed from DOM
+ && processInfos(viewInfos(elem, undefined, tags && rOpenTagMarkers))
+ // If a link() call, processViewInfos() adds bindings to bindEls, and returns true for non-script nodes, for adding data-link bindings
+ // If a childTags() call getViewInfos adds tag bindings to tags array.
+ && elem.getAttribute($viewsLinkAttr)) {
+ bindEls.push([elem]);
+ }
+ }
+
+ // Remove temporary marker script nodes they were added by markPrevOrNextNode
+ unmarkPrevOrNextNode(prevNode, elCnt);
+ unmarkPrevOrNextNode(nextNode, elCnt);
+
+ if (get) {
+ lazyLink && lazyLink.resolve();
+ return;
+ }
+
+ if (elCnt && defer + ids) {
+ // There are some views with elCnt, for which the open or close did not precede any HTML tag - so they have not been processed yet
+ elem = nextNode;
+ if (defer) {
+ if (nextNode) {
+ processViewInfos(viewInfos(defer + "+", true), nextNode);
+ } else {
+ processViewInfos(viewInfos(defer, true), parentNode);
+ }
+ }
+ processViewInfos(viewInfos(ids, true), parentNode);
+ // If there were any tokens on nextNode which have now been associated with inserted HTML tags, remove them from nextNode
+ if (nextNode) {
+ tokens = nextNode.getAttribute(jsvAttrStr);
+ if (l = tokens.indexOf(prevIds) + 1) {
+ tokens = tokens.slice(l + prevIds.length - 1);
+ }
+ nextNode.setAttribute(jsvAttrStr, ids + tokens);
+ }
+ }
+
+ //================ Bind the data-linked elements and tags ================
+ l = bindEls.length;
+ for (i = 0; i < l; i++) {
+ elem = bindEls[i];
+ linkInfo = elem[1];
+ elem = elem[0];
+ if (linkInfo) {
+ tag = bindingStore[linkInfo.id];
+ linkCtx = tag.linkCtx;
+ tag = linkCtx ? linkCtx.tag : tag;
+ // The tag may have been stored temporarily on the bindingStore - or may have already been replaced by the actual binding
+ if (linkInfo.open) {
+ // This is an 'open linked tag' binding annotation for a data-linked tag {^{...}}
+ if (elem) {
+ tag.parentElem = elem.parentNode;
+ tag._prv = elem;
+ }
+ tag._elCnt = linkInfo.elCnt;
+ if (tag && (!tag.onBeforeLink || tag.onBeforeLink() !== false) && !tag._.bound) {
+ // By default we data-link depth-first ("on the way in"), which is better for perf. But if a tag needs nested tags to be linked (refreshed)
+ // first, before linking its content, then make onBeforeLink() return false. In that case we data-link depth-first, so nested tags will have already refreshed.
+ tag._.bound = true;
+ view = tag.tagCtx.view;
+ addDataBinding(undefined, tag._prv, view, view.data||outerData, linkInfo.id);
+ }
+
+ tag._.linking = true;
+ } else {
+ tag._nxt = elem;
+ if (tag._.linking) {
+ // This is a 'close linked tag' binding annotation
+ // Add data binding
+ tagCtx = tag.tagCtx;
+ view = tagCtx.view;
+ tag.contents = getContents;
+ tag.nodes = getNodes;
+ tag.childTags = getChildTags;
+
+ delete tag._.linking;
+ callAfterLink(tag, tagCtx);
+ if (!tag._.bound) {
+ tag._.bound = true;
+ addDataBinding(undefined, tag._prv, view, view.data||outerData, linkInfo.id);
+ }
+ }
+ }
+ } else {
+ view = $view(elem);
+ // Add data binding for a data-linked element (with data-link attribute)
+ addDataBinding(elem.getAttribute($viewsLinkAttr), elem, view, view.data||outerData);
+ }
+ }
+ lazyLink && lazyLink.resolve();
+ }
+ //==== /end of nested functions ====
+
+ var linkCtx, tag, i, l, j, len, elems, elem, view, vwInfos, vwInfo, linkInfo, prevNodes, token, prevView, nextView, node, tags, deep, tagName, tagCtx, cvt,
+ tagDepth, get, depth, fragment, copiedNode, firstTag, parentTag, wrapper, div, tokens, elCnt, prevElCnt, htmlTag, ids, prevIds, found, lazyLink, linkedElem,
+ noDomLevel0 = $viewsSettings.noDomLevel0,
+ self = this,
+ thisId = self._.id + "_",
+ defer = "",
+ // The marker ids for which no tag was encountered (empty views or final closing markers) which we carry over to container tag
+ bindEls = [],
+ tagStack = [],
+ deferStack = [],
+ onAfterCreate = self.hlp(onAfterCreateStr),
+ processInfos = processViewInfos;
+
+ if (refresh) {
+ lazyLink = refresh.lazyLink && $.Deferred();
+ if (refresh.tmpl) {
+ // refresh is the prevView, passed in from addViews()
+ prevView = "/" + refresh._.id + "_";
+ } else {
+ get = refresh.get;
+ if (refresh.tag) {
+ thisId = refresh.tag + "^";
+ refresh = true;
+ }
+ }
+ refresh = refresh === true;
+ }
+
+ if (get) {
+ processInfos = getViewInfos;
+ tags = get.tags;
+ deep = get.deep;
+ tagName = get.name;
+ }
+
+ parentNode = parentNode
+ ? ("" + parentNode === parentNode
+ ? $(parentNode)[0] // It is a string, so treat as selector
+ : parentNode.jquery
+ ? parentNode[0] // A jQuery object - take first element.
+ : parentNode)
+ : (self.parentElem // view.link()
+ || document.body); // link(null, data) to link the whole document
+
+ parentTag = parentNode.tagName.toLowerCase();
+ elCnt = !!elContent[parentTag];
+
+ prevNode = prevNode && markPrevOrNextNode(prevNode, elCnt);
+ nextNode = nextNode && markPrevOrNextNode(nextNode, elCnt) || null;
+
+ if (html !== undefined) {
+ //================ Insert html into DOM using documentFragments (and wrapping HTML appropriately). ================
+ // Also convert markers to DOM annotations, based on content model.
+ // Corresponds to nextNode ? $(nextNode).before(html) : $(parentNode).html(html);
+ // but allows insertion to wrap correctly even with inserted script nodes. jQuery version will fail e.g. under tbody or select.
+ // This version should also be slightly faster
+ div = document.createElement("div");
+ wrapper = div;
+ prevIds = ids = "";
+ htmlTag = parentNode.namespaceURI === "http://www.w3.org/2000/svg" ? "svg" : (firstTag = rFirstElem.exec(html)) && firstTag[1] || "";
+ if (noDomLevel0 && firstTag && firstTag[2]) {
+ error("Unsupported: " + firstTag[2]); // For security reasons, don't allow insertion of elements with onFoo attributes.
+ }
+ if (elCnt) {
+ // Now look for following view, and find its tokens, or if not found, get the parentNode._dfr tokens
+ node = nextNode;
+ while (node && !(nextView = viewInfos(node))) {
+ node = node.nextSibling;
+ }
+ if (tokens = nextView ? nextView.tokens : parentNode._dfr) {
+ token = prevView || "";
+ if (refresh || !prevView) {
+ token += "#" + thisId;
+ }
+ j = tokens.indexOf(token);
+ if (j + 1) {
+ j += token.length;
+ // Transfer the initial tokens to inserted nodes, by setting them as the ids variable, picked up in convertMarkers
+ prevIds = ids = tokens.slice(0, j);
+ tokens = tokens.slice(j);
+ if (nextView) {
+ node.setAttribute(jsvAttrStr, tokens);
+ } else {
+ parentNode._dfr = tokens;
+ }
+ }
+ }
+ }
+
+ //================ Convert the markers to DOM annotations, based on content model. ================
+// oldElCnt = elCnt;
+ html = ("" + html).replace(rConvertMarkers, convertMarkers);
+// if (!!oldElCnt !== !!elCnt) {
+// error("Parse: " + html); // Parse error. Content not well-formed?
+// }
+ // Append wrapper element to doc fragment
+ safeFragment.appendChild(div);
+
+ // Go to html and back, then peel off extra wrappers
+ // Corresponds to jQuery $(nextNode).before(html) or $(parentNode).html(html);
+ // but supports svg elements, and other features missing from jQuery version (and this version should also be slightly faster)
+ htmlTag = wrapMap[htmlTag] || wrapMap.div;
+ depth = htmlTag[0];
+ wrapper.innerHTML = htmlTag[1] + html + htmlTag[2];
+ while (depth--) {
+ wrapper = wrapper.lastChild;
+ }
+ safeFragment.removeChild(div);
+ fragment = document.createDocumentFragment();
+ while (copiedNode = wrapper.firstChild) {
+ fragment.appendChild(copiedNode);
+ }
+ // Insert into the DOM
+ parentNode.insertBefore(fragment, nextNode);
+ }
+
+ if (lazyLink) {
+ setTimeout(dataLink, 0);
+ } else {
+ dataLink();
+ }
+
+ return lazyLink && lazyLink.promise();
+ }
+
+ function addDataBinding(linkMarkup, node, currentView, data, boundTagId) {
+ // Add data binding for data-linked elements or {^{...}} data-linked tags
+ var tmpl, tokens, attr, convertBack, params, trimLen, tagExpr, linkFn, linkCtx, tag, rTagIndex;
+
+ if (boundTagId) {
+ // {^{...}} data-linked tag. So only one linkTag in linkMarkup
+ tag = bindingStore[boundTagId];
+ tag = tag.linkCtx ? tag.linkCtx.tag : tag;
+
+ linkCtx = tag.linkCtx || {
+ data: data, // source
+ elem: tag._elCnt ? tag.parentElem : node, // target
+ view: currentView,
+ ctx: currentView.ctx,
+ attr: "html", // Script marker nodes are associated with {^{ and always target HTML.
+ fn: tag._.bnd,
+ tag: tag,
+ // Pass the boundTagId in the linkCtx, so that it can be picked up in observeAndBind
+ _bndId: boundTagId
+ };
+ bindDataLinkTarget(linkCtx, linkCtx.fn);
+ } else if (linkMarkup && node) {
+ // Compiled linkFn expressions could be stored in the tmpl.links array of the template
+ // TODO - consider also caching globally so that if {{:foo}} or data-link="foo" occurs in different places,
+ // the compiled template for this is cached and only compiled once...
+ //links = currentView.links || currentView.tmpl.links;
+
+ tmpl = currentView.tmpl;
+
+// if (!(linkTags = links[linkMarkup])) {
+ // This is the first time this view template has been linked, so we compile the data-link expressions, and store them on the template.
+
+ linkMarkup = normalizeLinkTag(linkMarkup, node);
+ rTag.lastIndex = 0;
+ while (tokens = rTag.exec(linkMarkup)) { // TODO require } to be followed by whitespace or $, and remove the \}(!\}) option.
+ // Iterate over the data-link expressions, for different target attrs,
+ // (only one if there is a boundTagId - the case of data-linked tag {^{...}})
+ // e.g. )|!--((?:[^-]|-(?!-))*)--|(\*)))\s*((?:[^}]|}(?!}))*?))})
+ return this;
+ })();
+
+ //===============
+ // Public helpers
+ //===============
+
+ $viewsSub.viewInfos = viewInfos;
+ // Expose viewInfos() as public helper method
+
+ //====================================
+ // Additional members for linked views
+ //====================================
+
+ function transferViewTokens(prevNode, nextNode, parentElem, id, viewOrTagChar, refresh) {
+ // Transfer tokens on prevNode of viewToRemove/viewToRefresh to nextNode or parentElem._dfr
+ var i, l, vwInfos, vwInfo, viewOrTag, viewId, tokens,
+ precedingLength = 0,
+ emptyView = prevNode === nextNode;
+
+ if (prevNode) {
+ // prevNode is either the first node in the viewOrTag, or has been replaced by the vwInfos tokens string
+ vwInfos = viewInfos(prevNode) || [];
+ for (i = 0, l = vwInfos.length; i < l; i++) {
+ // Step through views or tags on the prevNode
+ vwInfo = vwInfos[i];
+ viewId = vwInfo.id;
+ if (viewId === id && vwInfo.ch === viewOrTagChar) {
+ if (refresh) {
+ // This is viewOrTagToRefresh, this is the last viewOrTag to process...
+ l = 0;
+ } else {
+ // This is viewOrTagToRemove, so we are done...
+ break;
+ }
+ }
+ if (!emptyView) {
+ viewOrTag = vwInfo.ch === "_"
+ ? viewStore[viewId]
+ : bindingStore[viewId].linkCtx.tag;
+ if (vwInfo.open) {
+ // A "#m" token
+ viewOrTag._prv = nextNode;
+ } else if (vwInfo.close) {
+ // A "/m" token
+ viewOrTag._nxt = nextNode;
+ }
+ }
+ precedingLength += viewId.length + 2;
+ }
+
+ if (precedingLength) {
+ prevNode.setAttribute(jsvAttrStr, prevNode.getAttribute(jsvAttrStr).slice(precedingLength));
+ }
+ tokens = nextNode ? nextNode.getAttribute(jsvAttrStr) : parentElem._dfr;
+ if (l = tokens.indexOf("/" + id + viewOrTagChar) + 1) {
+ tokens = vwInfos.tokens.slice(0, precedingLength) + tokens.slice(l + (refresh ? -1 : id.length + 1));
+ }
+ if (tokens) {
+ if (nextNode) {
+ // If viewOrTagToRemove was an empty viewOrTag, we will remove both #n and /n
+ // (and any intervening tokens) from the nextNode (=== prevNode)
+ // If viewOrTagToRemove was not empty, we will take tokens preceding #n from prevNode,
+ // and concatenate with tokens following /n on nextNode
+ nextNode.setAttribute(jsvAttrStr, tokens);
+ } else {
+ parentElem._dfr = tokens;
+ }
+ }
+ } else {
+ // !prevNode, so there may be a deferred nodes token on the parentElem. Remove it.
+ parentElem._dfr = removeSubStr(parentElem._dfr, "#" + id + viewOrTagChar);
+ if (!refresh && !nextNode) {
+ // If this viewOrTag is being removed, and there was no .nxt, remove closing token from deferred tokens
+ parentElem._dfr = removeSubStr(parentElem._dfr, "/" + id + viewOrTagChar);
+ }
+ }
+ }
+
+ LinkedView = {
+ // Note: a linked view will also, after linking have nodes[], _prv (prevNode), _nxt (nextNode) ...
+ addViews: function(index, dataItems, tmpl) {
+ // if view is not an array view, do nothing
+ var i, viewsCount,
+ self = this,
+ itemsCount = dataItems.length,
+ views = self.views;
+
+ if (!self._.useKey && itemsCount && (tmpl = self.tmpl)) {
+ // view is of type "array"
+ // Use passed-in template if provided, since self added view may use a different template than the original one used to render the array.
+ viewsCount = views.length + itemsCount;
+
+ if (renderAndLink(self, index, tmpl, views, dataItems, self.ctx) !== false) {
+ for (i = index + itemsCount; i < viewsCount; i++) {
+ $observable(views[i]).setProperty("index", i);
+ //This is fixing up index, but not key, and not index on child views. From child views, use view.get("item").index.
+ }
+ }
+ }
+ return self;
+ },
+
+ removeViews: function(index, itemsCount, keepNodes) {
+ // view.removeViews() removes all the child views
+ // view.removeViews( index ) removes the child view with specified index or key
+ // view.removeViews( index, count ) removes the specified nummber of child views, starting with the specified index
+ function removeView(index) {
+ var id, bindId, parentElem, prevNode, nextNode, nodesToRemove,
+ viewToRemove = views[index];
+
+ if (viewToRemove && viewToRemove.link) {
+ id = viewToRemove._.id;
+ if (!keepNodes) {
+ // Remove the HTML nodes from the DOM, unless they have already been removed, including nodes of child views
+ nodesToRemove = viewToRemove.nodes();
+ }
+
+ // Remove child views, without removing nodes
+ viewToRemove.removeViews(undefined, undefined, true);
+
+ viewToRemove.data = undefined; // Set data to undefined: used as a flag that this view is being removed
+ prevNode = viewToRemove._prv;
+ nextNode = viewToRemove._nxt;
+ parentElem = viewToRemove.parentElem;
+ // If prevNode and nextNode are the same, the view is empty
+ if (!keepNodes) {
+ // Remove the HTML nodes from the DOM, unless they have already been removed, including nodes of child views
+ if (viewToRemove._elCnt) {
+ // if keepNodes is false (and transferring of tokens has not already been done at a higher level)
+ // then transfer tokens from prevNode which is being removed, to nextNode.
+ transferViewTokens(prevNode, nextNode, parentElem, id, "_");
+ }
+ $(nodesToRemove).remove();
+ }
+ if (!viewToRemove._elCnt) {
+ prevNode.parentNode && parentElem.removeChild(prevNode);
+ nextNode.parentNode && parentElem.removeChild(nextNode);
+ }
+ setArrayChangeLink(viewToRemove);
+ for (bindId in viewToRemove._.bnds) {
+ removeViewBinding(bindId);
+ }
+ delete viewStore[id];
+ }
+ }
+
+ var current, view, viewsCount,
+ self = this,
+ isArray = !self._.useKey,
+ views = self.views;
+
+ if (isArray) {
+ viewsCount = views.length;
+ }
+ if (index === undefined) {
+ // Remove all child views
+ if (isArray) {
+ // views and data are arrays
+ current = viewsCount;
+ while (current--) {
+ removeView(current);
+ }
+ self.views = [];
+ } else {
+ // views and data are objects
+ for (view in views) {
+ // Remove by key
+ removeView(view);
+ }
+ self.views = {};
+ }
+ } else {
+ if (itemsCount === undefined) {
+ if (isArray) {
+ // The parentView is data array view.
+ // Set itemsCount to 1, to remove this item
+ itemsCount = 1;
+ } else {
+ // Remove child view with key 'index'
+ removeView(index);
+ delete views[index];
+ }
+ }
+ if (isArray && itemsCount) {
+ current = index + itemsCount;
+ // Remove indexed items (parentView is data array view);
+ while (current-- > index) {
+ removeView(current);
+ }
+ views.splice(index, itemsCount);
+ if (viewsCount = views.length) {
+ // Fixup index on following view items...
+ while (index < viewsCount) {
+ $observable(views[index]).setProperty("index", index++);
+ }
+ }
+ }
+ }
+ return this;
+ },
+
+ refresh: function(context) {
+ var self = this,
+ parent = self.parent;
+
+ if (parent) {
+ renderAndLink(self, self.index, self.tmpl, parent.views, self.data, context, true);
+ setArrayChangeLink(self);
+ }
+ return self;
+ },
+
+ nodes: getNodes,
+ contents: getContents,
+ childTags: getChildTags,
+ link: viewLink
+ };
+
+ //=========================
+ // Extend $.views.settings
+ //=========================
+
+ $viewsSettings.merge = {
+ input: {
+ from: { fromAttr: inputAttrib }, to: { toAttr: "value" }
+ },
+ textarea: valueBinding,
+ select: valueBinding,
+ optgroup: {
+ from: { fromAttr: "label" }, to: { toAttr: "label" }
+ }
+ };
+
+ if ($viewsSettings.debugMode) {
+ // In debug mode create global for accessing views, etc
+ validate = !$viewsSettings.noValidate;
+ global._jsv = {
+ views: viewStore,
+ bindings: bindingStore
+ };
+ }
+
+ //========================
+ // Extend jQuery namespace
+ //========================
+
+ $extend($, {
+
+ //=======================
+ // jQuery $.view() plugin
+ //=======================
+
+ view: $views.view = $view = function(node, inner, type) {
+ // $.view() returns top view
+ // $.view(node) returns view that contains node
+ // $.view(selector) returns view that contains first selected element
+ // $.view(nodeOrSelector, type) returns nearest containing view of given type
+ // $.view(nodeOrSelector, "root") returns root containing view (child of top view)
+ // $.view(nodeOrSelector, true, type) returns nearest inner (contained) view of given type
+
+ if (inner !== !!inner) {
+ // inner not boolean, so this is view(nodeOrSelector, type)
+ type = inner;
+ inner = undefined;
+ }
+ var view, vwInfos, i, j, k, l, elem, elems,
+ level = 0,
+ body = document.body;
+
+ if (node && node !== body && topView._.useKey > 1) {
+ // Perf optimization for common cases
+
+ node = "" + node === node
+ ? $(node)[0]
+ : node.jquery
+ ? node[0]
+ : node;
+
+ if (node) {
+ if (inner) {
+ // Treat supplied node as a container element and return the first view encountered.
+ elems = qsa ? node.querySelectorAll(bindElsSel) : $(bindElsSel, node).get();
+ l = elems.length;
+ for (i = 0; i < l; i++) {
+ elem = elems[i];
+ vwInfos = viewInfos(elem, undefined, rOpenViewMarkers);
+
+ for (j = 0, k = vwInfos.length; j < k; j++) {
+ view = vwInfos[j];
+ if (view = viewStore[view.id]) {
+ view = view && type ? view.get(true, type) : view;
+ if (view) {
+ return view;
+ }
+ }
+ }
+ }
+ } else {
+ while (node) {
+ // Move back through siblings and up through parents to find preceding node which is a _prv (prevNode)
+ // script marker node for a non-element-content view, or a _prv (first node) for an elCnt view
+ if (vwInfos = viewInfos(node, undefined, rViewMarkers)) {
+ l = vwInfos.length;
+ while (l--) {
+ view = vwInfos[l];
+ if (view.open) {
+ if (level < 1) {
+ view = viewStore[view.id];
+ return view && type ? view.get(type) : view || topView;
+ }
+ level--;
+ } else {
+ // level starts at zero. If we hit a view.close, then we move level to 1, and we don't return a view until
+ // we are back at level zero (or a parent view with level < 0)
+ level++;
+ }
+ }
+ }
+ node = node.previousSibling || node.parentNode;
+ }
+ }
+ }
+ }
+ return inner ? undefined : topView;
+ },
+
+ link: $views.link = $link,
+ unlink: $views.unlink = $unlink,
+
+ //=====================
+ // override $.cleanData
+ //=====================
+ cleanData: function(elems) {
+ if (elems.length) {
+ // Remove JsViews bindings. Also, remove from the DOM any corresponding script marker nodes
+ clean(elems);
+ // (elems HTMLCollection no longer includes removed script marker nodes)
+ oldCleanData.call($, elems);
+ }
+ }
+ });
+
+ //===============================
+ // Extend jQuery instance plugins
+ //===============================
+
+ $extend($.fn, {
+ link: function(expr, from, context, parentView, prevNode, nextNode) {
+ return $link(expr, this, from, context, parentView, prevNode, nextNode);
+ },
+ unlink: function(expr) {
+ return $unlink(expr, this);
+ },
+ view: function(type) {
+ return $view(this[0], type);
+ }
+ });
+
+ //===============
+ // Extend topView
+ //===============
+
+ $extend(topView, { tmpl: { links: {}, tags: {} }});
+ $extend(topView, LinkedView);
+ topView._.onRender = addBindingMarkers;
+
+})(this, this.jQuery);
diff --git a/nano/js/libraries/3-jquery.timers.js b/nano/js/libraries/3-jquery.timers.js
new file mode 100644
index 00000000000..7001e503108
--- /dev/null
+++ b/nano/js/libraries/3-jquery.timers.js
@@ -0,0 +1,4 @@
+jQuery.fn.extend({everyTime:function(c,a,d,b){return this.each(function(){jQuery.timer.add(this,c,a,d,b)})},oneTime:function(c,a,d){return this.each(function(){jQuery.timer.add(this,c,a,d,1)})},stopTime:function(c,a){return this.each(function(){jQuery.timer.remove(this,c,a)})}});
+jQuery.extend({timer:{global:[],guid:1,dataKey:"jQuery.timer",regex:/^([0-9]+(?:\.[0-9]*)?)\s*(.*s)?$/,powers:{ms:1,cs:10,ds:100,s:1E3,das:1E4,hs:1E5,ks:1E6},timeParse:function(c){if(c==undefined||c==null)return null;var a=this.regex.exec(jQuery.trim(c.toString()));return a[2]?parseFloat(a[1])*(this.powers[a[2]]||1):c},add:function(c,a,d,b,e){var g=0;if(jQuery.isFunction(d)){e||(e=b);b=d;d=a}a=jQuery.timer.timeParse(a);if(!(typeof a!="number"||isNaN(a)||a<0)){if(typeof e!="number"||isNaN(e)||e<0)e=
+0;e=e||0;var f=jQuery.data(c,this.dataKey)||jQuery.data(c,this.dataKey,{});f[d]||(f[d]={});b.timerID=b.timerID||this.guid++;var h=function(){if(++g>e&&e!==0||b.call(c,g)===false)jQuery.timer.remove(c,d,b)};h.timerID=b.timerID;f[d][b.timerID]||(f[d][b.timerID]=window.setInterval(h,a));this.global.push(c)}},remove:function(c,a,d){var b=jQuery.data(c,this.dataKey),e;if(b){if(a){if(b[a]){if(d){if(d.timerID){window.clearInterval(b[a][d.timerID]);delete b[a][d.timerID]}}else for(d in b[a]){window.clearInterval(b[a][d]);
+delete b[a][d]}for(e in b[a])break;if(!e){e=null;delete b[a]}}}else for(a in b)this.remove(c,a,d);for(e in b)break;e||jQuery.removeData(c,this.dataKey)}}}});jQuery(window).bind("unload",function(){jQuery.each(jQuery.timer.global,function(c,a){jQuery.timer.remove(a)})});
\ No newline at end of file
diff --git a/nano/js/nano_base_helpers.js b/nano/js/nano_base_helpers.js
new file mode 100644
index 00000000000..61ca5267b9c
--- /dev/null
+++ b/nano/js/nano_base_helpers.js
@@ -0,0 +1,116 @@
+NanoBaseHelpers = function ()
+{
+ var _urlParameters = {}; // This is populated with the base url parameters, which is probaby just the "src" parameter
+
+ var init = function ()
+ {
+ var body = $('body'); // We store data in the body tag, it's as good a place as any
+
+ _urlParameters = body.data('urlParameters');
+
+ initHelpers();
+ };
+
+ var initHelpers = function ()
+ {
+ $.views.helpers({
+ // Generate a Byond link
+ link: function( text, icon, parameters, status ) {
+
+ var iconHtml = '';
+ if (typeof icon != 'undefined' && icon != null)
+ {
+ iconHtml = '';
+ }
+
+ if (typeof status != 'undefined' && status != null)
+ {
+ return '
' + iconHtml + text + '
';
+ }
+
+ return '
' + iconHtml + text + '
';
+ },
+ // Round a number to the nearest integer
+ round: function(number) {
+ return Math.round(number);
+ },
+ // Round a number down to integer
+ floor: function(number) {
+ return Math.floor(number);
+ },
+ // Round a number up to integer
+ ceil: function(number) {
+ return Math.ceil(number);
+ },
+ // Format a string (~string("Hello {0}, how are {1}?", 'Martin', 'you') becomes "Hello Martin, how are you?")
+ string: function() {
+ if (arguments.length == 0)
+ {
+ return "";
+ }
+ else if (arguments.length == 1)
+ {
+ return arguments[0];
+ }
+ else if (arguments.length > 1)
+ {
+ stringArgs = [];
+ for (var i = 1; i < arguments.length; i++)
+ {
+ stringArgs.push(arguments[i]);
+ }
+ return arguments[0].format(stringArgs);
+ }
+ return
+ }
+ });
+ }
+
+ var generateHref = function (parameters)
+ {
+ var queryString = '?';
+
+ for (var key in _urlParameters)
+ {
+ if (_urlParameters.hasOwnProperty(key))
+ {
+ if (queryString !== '?')
+ {
+ queryString += ';';
+ }
+ queryString += key + '=' + _urlParameters[key];
+ }
+ }
+
+ for (var key in parameters)
+ {
+ if (parameters.hasOwnProperty(key))
+ {
+ if (queryString !== '?')
+ {
+ queryString += ';';
+ }
+ queryString += key + '=' + parameters[key];
+ }
+ }
+ return queryString;
+ }
+
+ return {
+ init: function ()
+ {
+ init();
+ }
+ };
+} ();
+
+$(document).ready(function()
+{
+ NanoBaseHelpers.init();
+});
+
+
+
+
+
+
diff --git a/nano/js/nano_config.js b/nano/js/nano_config.js
new file mode 100644
index 00000000000..2fe30a9d72c
--- /dev/null
+++ b/nano/js/nano_config.js
@@ -0,0 +1,94 @@
+var NanoConfig = function ()
+{
+ return {
+ init: function ()
+ {
+
+ }
+ }
+} ();
+
+NanoConfig.init();
+
+if (!Array.prototype.indexOf)
+{
+ Array.prototype.indexOf = function(elt /*, from*/)
+ {
+ var len = this.length;
+
+ var from = Number(arguments[1]) || 0;
+ from = (from < 0)
+ ? Math.ceil(from)
+ : Math.floor(from);
+ if (from < 0)
+ from += len;
+
+ for (; from < len; from++)
+ {
+ if (from in this &&
+ this[from] === elt)
+ return from;
+ }
+ return -1;
+ };
+}
+
+if (!String.prototype.format)
+{
+ String.prototype.format = function (args) {
+ var str = this;
+ return str.replace(String.prototype.format.regex, function(item) {
+ var intVal = parseInt(item.substring(1, item.length - 1));
+ var replace;
+ if (intVal >= 0) {
+ replace = args[intVal];
+ } else if (intVal === -1) {
+ replace = "{";
+ } else if (intVal === -2) {
+ replace = "}";
+ } else {
+ replace = "";
+ }
+ return replace;
+ });
+ };
+ String.prototype.format.regex = new RegExp("{-?[0-9]+}", "g");
+}
+
+Object.size = function(obj) {
+ var size = 0, key;
+ for (var key in obj) {
+ if (obj.hasOwnProperty(key)) size++;
+ }
+ return size;
+};
+
+if(!window.console) {
+ window.console = {
+ log : function(str) {
+ return false;
+ }
+ };
+}
+
+String.prototype.toTitleCase = function () {
+ var smallWords = /^(a|an|and|as|at|but|by|en|for|if|in|of|on|or|the|to|vs?\.?|via)$/i;
+
+ return this.replace(/([^\W_]+[^\s-]*) */g, function (match, p1, index, title) {
+ if (index > 0 && index + p1.length !== title.length &&
+ p1.search(smallWords) > -1 && title.charAt(index - 2) !== ":" &&
+ title.charAt(index - 1).search(/[^\s-]/) < 0) {
+ return match.toLowerCase();
+ }
+
+ if (p1.substr(1).search(/[A-Z]|\../) > -1) {
+ return match;
+ }
+
+ return match.charAt(0).toUpperCase() + match.substr(1);
+ });
+};
+
+$.ajaxSetup({
+ cache: false
+});
\ No newline at end of file
diff --git a/nano/js/nano_update.js b/nano/js/nano_update.js
new file mode 100644
index 00000000000..1034ac89795
--- /dev/null
+++ b/nano/js/nano_update.js
@@ -0,0 +1,196 @@
+NanoUpdate = function ()
+{
+ var _isInitialised = false;
+
+ var _templates = null;
+ var _data = null;
+ var _earlyUpdateData = null; // This is for newer data which has arrived before the template has been rendered
+
+ var _beforeUpdateCallbacks = [];
+ var _afterUpdateCallbacks = [];
+
+ var _canClick = true;
+
+ var init = function ()
+ {
+ var body = $('body'); // We store data in the body tag, it's as good a place as any
+
+ _data = body.data('initialData');
+
+ var templateData = body.data('templateData');
+
+ var templateCount = 0;
+ for (var key in templateData)
+ {
+ if (templateData.hasOwnProperty(key))
+ {
+ templateCount++;
+ }
+ }
+
+ for (var key in templateData)
+ {
+ if (templateData.hasOwnProperty(key))
+ {
+ $.when($.get(templateData[key]))
+ .done(function(templateData) {
+ if (_templates == null)
+ {
+ _templates = {};
+ }
+
+ templateData += ''
+
+ try
+ {
+ _templates[key] = $.templates(templateData);
+ _templates[key].link( "#mainTemplate", _data ); // initial data gets applied first, before any updates
+
+ templateCount--;
+
+ if (templateCount <= 0)
+ {
+ _isInitialised = true;
+ }
+
+ if (_earlyUpdateData !== null) // Newer data has already arrived, so update
+ {
+ observedDataUpdateRecursive(_earlyUpdateData, _data);
+ }
+
+ //alert($("#mainTemplate").html());
+ }
+ catch(error)
+ {
+ alert('An error occurred while loading the UI: ' + error.message);
+ return;
+ }
+ });
+ }
+ }
+
+ NanoUpdate.addAfterUpdateCallback(function (updateData) {
+ var uiStatusClass;
+ if (updateData['ui']['status'] == 2)
+ {
+ uiStatusClass = 'icon24 uiStatusGood';
+ $('.linkActive').removeClass('inactive');
+ }
+ else if (updateData['ui']['status'] == 1)
+ {
+ uiStatusClass = 'icon24 uiStatusAverage';
+ $('.linkActive').addClass('inactive');
+ }
+ else
+ {
+ uiStatusClass = 'icon24 uiStatusBad'
+ $('.linkActive').addClass('inactive');
+ }
+ $('#uiStatusIcon').attr('class', uiStatusClass);
+
+ $('.linkActive').stopTime('linkPending');
+ $('.linkActive').removeClass('linkPending');
+
+ $('.linkActive').off('click');
+ $('.linkActive').on('click', function (event) {
+ event.preventDefault();
+ var href = $(this).data('href');
+ if (href != null && _canClick)
+ {
+ _canClick = false;
+ $('body').oneTime(300, 'enableClick', function () {
+ _canClick = true;
+ });
+ $(this).oneTime(300, 'linkPending', function () {
+ $(this).addClass('linkPending');
+ });
+ window.location.href = href;
+ }
+ });
+ });
+ };
+
+ // Receive update data from the server
+ var receiveUpdateData = function (jsonString)
+ {
+ var updateData = jQuery.parseJSON(jsonString);
+
+ if (_isInitialised) // templates have been loaded and are observing the data. We need to update it recursively
+ {
+ executeCallbacks(_beforeUpdateCallbacks, updateData);
+
+ observedDataUpdateRecursive(updateData, _data);
+
+ executeCallbacks(_afterUpdateCallbacks, updateData);
+ }
+ else
+ {
+ _earlyUpdateData = updateData; // templates have not been loaded, therefor they are not observing the data. We set _earlyUpdateData which will be applied after the template is loaded with the initial data
+ }
+ }
+
+ // This function updates the observed data recursively
+ // It has to be done recursively as each piece of data is observed individually and needs to be updated individually
+ var observedDataUpdateRecursive = function (updateData, data, path)
+ {
+ if (path === null || typeof path === 'undefined')
+ {
+ path = '';
+ }
+ else
+ {
+ path += '.';
+ }
+ for (var key in updateData)
+ {
+ if (updateData.hasOwnProperty(key))
+ {
+ var currentPath = path + key;
+ if (updateData[key] != null && typeof updateData[key] === 'object' && !$.isArray(updateData[key]))
+ {
+ observedDataUpdateRecursive(updateData[key], data, currentPath)
+ }
+ else
+ {
+ $.observable(data).setProperty(currentPath, updateData[key]);
+ }
+ }
+ }
+ }
+
+ var executeCallbacks = function (callbacks, updateData)
+ {
+ for (var index in callbacks)
+ {
+ callbacks[index].call(this, updateData);
+ }
+ }
+
+ return {
+ init: function ()
+ {
+ init();
+ },
+ isInitialised: function ()
+ {
+ return _isInitialised;
+ },
+ receiveUpdateData: function (jsonString)
+ {
+ receiveUpdateData(jsonString);
+ },
+ addBeforeUpdateCallback: function (callbackFunction)
+ {
+ _beforeUpdateCallbacks.push(callbackFunction);
+ },
+ addAfterUpdateCallback: function (callbackFunction)
+ {
+ _afterUpdateCallbacks.push(callbackFunction);
+ }
+ };
+} ();
+
+$(document).ready(function()
+{
+ NanoUpdate.init();
+});
\ No newline at end of file
diff --git a/nano/templates/apc.tmpl b/nano/templates/apc.tmpl
new file mode 100644
index 00000000000..446ba31a145
--- /dev/null
+++ b/nano/templates/apc.tmpl
@@ -0,0 +1,141 @@
+
+ {^{if locked}}
+ Swipe ID card to unlock interface
+ {{else}}
+ Swipe ID card to lock interface
+ {{/if}}
+
\ No newline at end of file
diff --git a/nano/templates/cryo.tmpl b/nano/templates/cryo.tmpl
new file mode 100644
index 00000000000..3399876ac0d
--- /dev/null
+++ b/nano/templates/cryo.tmpl
@@ -0,0 +1,58 @@
+
\ No newline at end of file
diff --git a/nano/uiBackground.fla b/nano/uiBackground.fla
new file mode 100644
index 00000000000..60f9f1ee941
Binary files /dev/null and b/nano/uiBackground.fla differ