").append(x.parseHTML(e)).find(i):e)}).complete(r&&function(e,t){s.each(r,o||[e.responseText,t,e])}),this},x.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){x.fn[t]=function(e){return this.on(t,e)}}),x.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:yn,type:"GET",isLocal:Cn.test(mn[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Dn,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":x.parseJSON,"text xml":x.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?_n(_n(e,x.ajaxSettings),t):_n(x.ajaxSettings,e)},ajaxPrefilter:Hn(An),ajaxTransport:Hn(jn),ajax:function(e,n){"object"==typeof e&&(n=e,e=t),n=n||{};var r,i,o,a,s,l,u,c,p=x.ajaxSetup({},n),f=p.context||p,d=p.context&&(f.nodeType||f.jquery)?x(f):x.event,h=x.Deferred(),g=x.Callbacks("once memory"),m=p.statusCode||{},y={},v={},b=0,w="canceled",C={readyState:0,getResponseHeader:function(e){var t;if(2===b){if(!c){c={};while(t=Tn.exec(a))c[t[1].toLowerCase()]=t[2]}t=c[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===b?a:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return b||(e=v[n]=v[n]||e,y[e]=t),this},overrideMimeType:function(e){return b||(p.mimeType=e),this},statusCode:function(e){var t;if(e)if(2>b)for(t in e)m[t]=[m[t],e[t]];else C.always(e[C.status]);return this},abort:function(e){var t=e||w;return u&&u.abort(t),k(0,t),this}};if(h.promise(C).complete=g.add,C.success=C.done,C.error=C.fail,p.url=((e||p.url||yn)+"").replace(xn,"").replace(kn,mn[1]+"//"),p.type=n.method||n.type||p.method||p.type,p.dataTypes=x.trim(p.dataType||"*").toLowerCase().match(T)||[""],null==p.crossDomain&&(r=En.exec(p.url.toLowerCase()),p.crossDomain=!(!r||r[1]===mn[1]&&r[2]===mn[2]&&(r[3]||("http:"===r[1]?"80":"443"))===(mn[3]||("http:"===mn[1]?"80":"443")))),p.data&&p.processData&&"string"!=typeof p.data&&(p.data=x.param(p.data,p.traditional)),qn(An,p,n,C),2===b)return C;l=p.global,l&&0===x.active++&&x.event.trigger("ajaxStart"),p.type=p.type.toUpperCase(),p.hasContent=!Nn.test(p.type),o=p.url,p.hasContent||(p.data&&(o=p.url+=(bn.test(o)?"&":"?")+p.data,delete p.data),p.cache===!1&&(p.url=wn.test(o)?o.replace(wn,"$1_="+vn++):o+(bn.test(o)?"&":"?")+"_="+vn++)),p.ifModified&&(x.lastModified[o]&&C.setRequestHeader("If-Modified-Since",x.lastModified[o]),x.etag[o]&&C.setRequestHeader("If-None-Match",x.etag[o])),(p.data&&p.hasContent&&p.contentType!==!1||n.contentType)&&C.setRequestHeader("Content-Type",p.contentType),C.setRequestHeader("Accept",p.dataTypes[0]&&p.accepts[p.dataTypes[0]]?p.accepts[p.dataTypes[0]]+("*"!==p.dataTypes[0]?", "+Dn+"; q=0.01":""):p.accepts["*"]);for(i in p.headers)C.setRequestHeader(i,p.headers[i]);if(p.beforeSend&&(p.beforeSend.call(f,C,p)===!1||2===b))return C.abort();w="abort";for(i in{success:1,error:1,complete:1})C[i](p[i]);if(u=qn(jn,p,n,C)){C.readyState=1,l&&d.trigger("ajaxSend",[C,p]),p.async&&p.timeout>0&&(s=setTimeout(function(){C.abort("timeout")},p.timeout));try{b=1,u.send(y,k)}catch(N){if(!(2>b))throw N;k(-1,N)}}else k(-1,"No Transport");function k(e,n,r,i){var c,y,v,w,T,N=n;2!==b&&(b=2,s&&clearTimeout(s),u=t,a=i||"",C.readyState=e>0?4:0,c=e>=200&&300>e||304===e,r&&(w=Mn(p,C,r)),w=On(p,w,C,c),c?(p.ifModified&&(T=C.getResponseHeader("Last-Modified"),T&&(x.lastModified[o]=T),T=C.getResponseHeader("etag"),T&&(x.etag[o]=T)),204===e||"HEAD"===p.type?N="nocontent":304===e?N="notmodified":(N=w.state,y=w.data,v=w.error,c=!v)):(v=N,(e||!N)&&(N="error",0>e&&(e=0))),C.status=e,C.statusText=(n||N)+"",c?h.resolveWith(f,[y,N,C]):h.rejectWith(f,[C,N,v]),C.statusCode(m),m=t,l&&d.trigger(c?"ajaxSuccess":"ajaxError",[C,p,c?y:v]),g.fireWith(f,[C,N]),l&&(d.trigger("ajaxComplete",[C,p]),--x.active||x.event.trigger("ajaxStop")))}return C},getJSON:function(e,t,n){return x.get(e,t,n,"json")},getScript:function(e,n){return x.get(e,t,n,"script")}}),x.each(["get","post"],function(e,n){x[n]=function(e,r,i,o){return x.isFunction(r)&&(o=o||i,i=r,r=t),x.ajax({url:e,type:n,dataType:o,data:r,success:i})}});function Mn(e,n,r){var i,o,a,s,l=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),o===t&&(o=e.mimeType||n.getResponseHeader("Content-Type"));if(o)for(s in l)if(l[s]&&l[s].test(o)){u.unshift(s);break}if(u[0]in r)a=u[0];else{for(s in r){if(!u[0]||e.converters[s+" "+u[0]]){a=s;break}i||(i=s)}a=a||i}return a?(a!==u[0]&&u.unshift(a),r[a]):t}function On(e,t,n,r){var i,o,a,s,l,u={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)u[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!l&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),l=o,o=c.shift())if("*"===o)o=l;else if("*"!==l&&l!==o){if(a=u[l+" "+o]||u["* "+o],!a)for(i in u)if(s=i.split(" "),s[1]===o&&(a=u[l+" "+s[0]]||u["* "+s[0]])){a===!0?a=u[i]:u[i]!==!0&&(o=s[0],c.unshift(s[1]));break}if(a!==!0)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(p){return{state:"parsererror",error:a?p:"No conversion from "+l+" to "+o}}}return{state:"success",data:t}}x.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(e){return x.globalEval(e),e}}}),x.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),x.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=a.head||x("head")[0]||a.documentElement;return{send:function(t,i){n=a.createElement("script"),n.async=!0,e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,t){(t||!n.readyState||/loaded|complete/.test(n.readyState))&&(n.onload=n.onreadystatechange=null,n.parentNode&&n.parentNode.removeChild(n),n=null,t||i(200,"success"))},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(t,!0)}}}});var Fn=[],Bn=/(=)\?(?=&|$)|\?\?/;x.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Fn.pop()||x.expando+"_"+vn++;return this[e]=!0,e}}),x.ajaxPrefilter("json jsonp",function(n,r,i){var o,a,s,l=n.jsonp!==!1&&(Bn.test(n.url)?"url":"string"==typeof n.data&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Bn.test(n.data)&&"data");return l||"jsonp"===n.dataTypes[0]?(o=n.jsonpCallback=x.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,l?n[l]=n[l].replace(Bn,"$1"+o):n.jsonp!==!1&&(n.url+=(bn.test(n.url)?"&":"?")+n.jsonp+"="+o),n.converters["script json"]=function(){return s||x.error(o+" was not called"),s[0]},n.dataTypes[0]="json",a=e[o],e[o]=function(){s=arguments},i.always(function(){e[o]=a,n[o]&&(n.jsonpCallback=r.jsonpCallback,Fn.push(o)),s&&x.isFunction(a)&&a(s[0]),s=a=t}),"script"):t});var Pn,Rn,Wn=0,$n=e.ActiveXObject&&function(){var e;for(e in Pn)Pn[e](t,!0)};function In(){try{return new e.XMLHttpRequest}catch(t){}}function zn(){try{return new e.ActiveXObject("Microsoft.XMLHTTP")}catch(t){}}x.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&In()||zn()}:In,Rn=x.ajaxSettings.xhr(),x.support.cors=!!Rn&&"withCredentials"in Rn,Rn=x.support.ajax=!!Rn,Rn&&x.ajaxTransport(function(n){if(!n.crossDomain||x.support.cors){var r;return{send:function(i,o){var a,s,l=n.xhr();if(n.username?l.open(n.type,n.url,n.async,n.username,n.password):l.open(n.type,n.url,n.async),n.xhrFields)for(s in n.xhrFields)l[s]=n.xhrFields[s];n.mimeType&&l.overrideMimeType&&l.overrideMimeType(n.mimeType),n.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");try{for(s in i)l.setRequestHeader(s,i[s])}catch(u){}l.send(n.hasContent&&n.data||null),r=function(e,i){var s,u,c,p;try{if(r&&(i||4===l.readyState))if(r=t,a&&(l.onreadystatechange=x.noop,$n&&delete Pn[a]),i)4!==l.readyState&&l.abort();else{p={},s=l.status,u=l.getAllResponseHeaders(),"string"==typeof l.responseText&&(p.text=l.responseText);try{c=l.statusText}catch(f){c=""}s||!n.isLocal||n.crossDomain?1223===s&&(s=204):s=p.text?200:404}}catch(d){i||o(-1,d)}p&&o(s,c,p,u)},n.async?4===l.readyState?setTimeout(r):(a=++Wn,$n&&(Pn||(Pn={},x(e).unload($n)),Pn[a]=r),l.onreadystatechange=r):r()},abort:function(){r&&r(t,!0)}}}});var Xn,Un,Vn=/^(?:toggle|show|hide)$/,Yn=RegExp("^(?:([+-])=|)("+w+")([a-z%]*)$","i"),Jn=/queueHooks$/,Gn=[nr],Qn={"*":[function(e,t){var n=this.createTween(e,t),r=n.cur(),i=Yn.exec(t),o=i&&i[3]||(x.cssNumber[e]?"":"px"),a=(x.cssNumber[e]||"px"!==o&&+r)&&Yn.exec(x.css(n.elem,e)),s=1,l=20;if(a&&a[3]!==o){o=o||a[3],i=i||[],a=+r||1;do s=s||".5",a/=s,x.style(n.elem,e,a+o);while(s!==(s=n.cur()/r)&&1!==s&&--l)}return i&&(a=n.start=+a||+r||0,n.unit=o,n.end=i[1]?a+(i[1]+1)*i[2]:+i[2]),n}]};function Kn(){return setTimeout(function(){Xn=t}),Xn=x.now()}function Zn(e,t,n){var r,i=(Qn[t]||[]).concat(Qn["*"]),o=0,a=i.length;for(;a>o;o++)if(r=i[o].call(n,t,e))return r}function er(e,t,n){var r,i,o=0,a=Gn.length,s=x.Deferred().always(function(){delete l.elem}),l=function(){if(i)return!1;var t=Xn||Kn(),n=Math.max(0,u.startTime+u.duration-t),r=n/u.duration||0,o=1-r,a=0,l=u.tweens.length;for(;l>a;a++)u.tweens[a].run(o);return s.notifyWith(e,[u,o,n]),1>o&&l?n:(s.resolveWith(e,[u]),!1)},u=s.promise({elem:e,props:x.extend({},t),opts:x.extend(!0,{specialEasing:{}},n),originalProperties:t,originalOptions:n,startTime:Xn||Kn(),duration:n.duration,tweens:[],createTween:function(t,n){var r=x.Tween(e,u.opts,t,n,u.opts.specialEasing[t]||u.opts.easing);return u.tweens.push(r),r},stop:function(t){var n=0,r=t?u.tweens.length:0;if(i)return this;for(i=!0;r>n;n++)u.tweens[n].run(1);return t?s.resolveWith(e,[u,t]):s.rejectWith(e,[u,t]),this}}),c=u.props;for(tr(c,u.opts.specialEasing);a>o;o++)if(r=Gn[o].call(u,e,c,u.opts))return r;return x.map(c,Zn,u),x.isFunction(u.opts.start)&&u.opts.start.call(e,u),x.fx.timer(x.extend(l,{elem:e,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)}function tr(e,t){var n,r,i,o,a;for(n in e)if(r=x.camelCase(n),i=t[r],o=e[n],x.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),a=x.cssHooks[r],a&&"expand"in a){o=a.expand(o),delete e[r];for(n in o)n in e||(e[n]=o[n],t[n]=i)}else t[r]=i}x.Animation=x.extend(er,{tweener:function(e,t){x.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");var n,r=0,i=e.length;for(;i>r;r++)n=e[r],Qn[n]=Qn[n]||[],Qn[n].unshift(t)},prefilter:function(e,t){t?Gn.unshift(e):Gn.push(e)}});function nr(e,t,n){var r,i,o,a,s,l,u=this,c={},p=e.style,f=e.nodeType&&nn(e),d=x._data(e,"fxshow");n.queue||(s=x._queueHooks(e,"fx"),null==s.unqueued&&(s.unqueued=0,l=s.empty.fire,s.empty.fire=function(){s.unqueued||l()}),s.unqueued++,u.always(function(){u.always(function(){s.unqueued--,x.queue(e,"fx").length||s.empty.fire()})})),1===e.nodeType&&("height"in t||"width"in t)&&(n.overflow=[p.overflow,p.overflowX,p.overflowY],"inline"===x.css(e,"display")&&"none"===x.css(e,"float")&&(x.support.inlineBlockNeedsLayout&&"inline"!==ln(e.nodeName)?p.zoom=1:p.display="inline-block")),n.overflow&&(p.overflow="hidden",x.support.shrinkWrapBlocks||u.always(function(){p.overflow=n.overflow[0],p.overflowX=n.overflow[1],p.overflowY=n.overflow[2]}));for(r in t)if(i=t[r],Vn.exec(i)){if(delete t[r],o=o||"toggle"===i,i===(f?"hide":"show"))continue;c[r]=d&&d[r]||x.style(e,r)}if(!x.isEmptyObject(c)){d?"hidden"in d&&(f=d.hidden):d=x._data(e,"fxshow",{}),o&&(d.hidden=!f),f?x(e).show():u.done(function(){x(e).hide()}),u.done(function(){var t;x._removeData(e,"fxshow");for(t in c)x.style(e,t,c[t])});for(r in c)a=Zn(f?d[r]:0,r,u),r in d||(d[r]=a.start,f&&(a.end=a.start,a.start="width"===r||"height"===r?1:0))}}function rr(e,t,n,r,i){return new rr.prototype.init(e,t,n,r,i)}x.Tween=rr,rr.prototype={constructor:rr,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||"swing",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(x.cssNumber[n]?"":"px")},cur:function(){var e=rr.propHooks[this.prop];return e&&e.get?e.get(this):rr.propHooks._default.get(this)},run:function(e){var t,n=rr.propHooks[this.prop];return this.pos=t=this.options.duration?x.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):rr.propHooks._default.set(this),this}},rr.prototype.init.prototype=rr.prototype,rr.propHooks={_default:{get:function(e){var t;return null==e.elem[e.prop]||e.elem.style&&null!=e.elem.style[e.prop]?(t=x.css(e.elem,e.prop,""),t&&"auto"!==t?t:0):e.elem[e.prop]},set:function(e){x.fx.step[e.prop]?x.fx.step[e.prop](e):e.elem.style&&(null!=e.elem.style[x.cssProps[e.prop]]||x.cssHooks[e.prop])?x.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},rr.propHooks.scrollTop=rr.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},x.each(["toggle","show","hide"],function(e,t){var n=x.fn[t];x.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate(ir(t,!0),e,r,i)}}),x.fn.extend({fadeTo:function(e,t,n,r){return this.filter(nn).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=x.isEmptyObject(e),o=x.speed(t,n,r),a=function(){var t=er(this,x.extend({},e),o);(i||x._data(this,"finish"))&&t.stop(!0)};return a.finish=a,i||o.queue===!1?this.each(a):this.queue(o.queue,a)},stop:function(e,n,r){var i=function(e){var t=e.stop;delete e.stop,t(r)};return"string"!=typeof e&&(r=n,n=e,e=t),n&&e!==!1&&this.queue(e||"fx",[]),this.each(function(){var t=!0,n=null!=e&&e+"queueHooks",o=x.timers,a=x._data(this);if(n)a[n]&&a[n].stop&&i(a[n]);else for(n in a)a[n]&&a[n].stop&&Jn.test(n)&&i(a[n]);for(n=o.length;n--;)o[n].elem!==this||null!=e&&o[n].queue!==e||(o[n].anim.stop(r),t=!1,o.splice(n,1));(t||!r)&&x.dequeue(this,e)})},finish:function(e){return e!==!1&&(e=e||"fx"),this.each(function(){var t,n=x._data(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=x.timers,a=r?r.length:0;for(n.finish=!0,x.queue(this,e,[]),i&&i.stop&&i.stop.call(this,!0),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;a>t;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}});function ir(e,t){var n,r={height:e},i=0;for(t=t?1:0;4>i;i+=2-t)n=Zt[i],r["margin"+n]=r["padding"+n]=e;return t&&(r.opacity=r.width=e),r}x.each({slideDown:ir("show"),slideUp:ir("hide"),slideToggle:ir("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){x.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),x.speed=function(e,t,n){var r=e&&"object"==typeof e?x.extend({},e):{complete:n||!n&&t||x.isFunction(e)&&e,duration:e,easing:n&&t||t&&!x.isFunction(t)&&t};return r.duration=x.fx.off?0:"number"==typeof r.duration?r.duration:r.duration in x.fx.speeds?x.fx.speeds[r.duration]:x.fx.speeds._default,(null==r.queue||r.queue===!0)&&(r.queue="fx"),r.old=r.complete,r.complete=function(){x.isFunction(r.old)&&r.old.call(this),r.queue&&x.dequeue(this,r.queue)},r},x.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},x.timers=[],x.fx=rr.prototype.init,x.fx.tick=function(){var e,n=x.timers,r=0;for(Xn=x.now();n.length>r;r++)e=n[r],e()||n[r]!==e||n.splice(r--,1);n.length||x.fx.stop(),Xn=t},x.fx.timer=function(e){e()&&x.timers.push(e)&&x.fx.start()},x.fx.interval=13,x.fx.start=function(){Un||(Un=setInterval(x.fx.tick,x.fx.interval))},x.fx.stop=function(){clearInterval(Un),Un=null},x.fx.speeds={slow:600,fast:200,_default:400},x.fx.step={},x.expr&&x.expr.filters&&(x.expr.filters.animated=function(e){return x.grep(x.timers,function(t){return e===t.elem}).length}),x.fn.offset=function(e){if(arguments.length)return e===t?this:this.each(function(t){x.offset.setOffset(this,e,t)});var n,r,o={top:0,left:0},a=this[0],s=a&&a.ownerDocument;if(s)return n=s.documentElement,x.contains(n,a)?(typeof a.getBoundingClientRect!==i&&(o=a.getBoundingClientRect()),r=or(s),{top:o.top+(r.pageYOffset||n.scrollTop)-(n.clientTop||0),left:o.left+(r.pageXOffset||n.scrollLeft)-(n.clientLeft||0)}):o},x.offset={setOffset:function(e,t,n){var r=x.css(e,"position");"static"===r&&(e.style.position="relative");var i=x(e),o=i.offset(),a=x.css(e,"top"),s=x.css(e,"left"),l=("absolute"===r||"fixed"===r)&&x.inArray("auto",[a,s])>-1,u={},c={},p,f;l?(c=i.position(),p=c.top,f=c.left):(p=parseFloat(a)||0,f=parseFloat(s)||0),x.isFunction(t)&&(t=t.call(e,n,o)),null!=t.top&&(u.top=t.top-o.top+p),null!=t.left&&(u.left=t.left-o.left+f),"using"in t?t.using.call(e,u):i.css(u)}},x.fn.extend({position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return"fixed"===x.css(r,"position")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),x.nodeName(e[0],"html")||(n=e.offset()),n.top+=x.css(e[0],"borderTopWidth",!0),n.left+=x.css(e[0],"borderLeftWidth",!0)),{top:t.top-n.top-x.css(r,"marginTop",!0),left:t.left-n.left-x.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||s;while(e&&!x.nodeName(e,"html")&&"static"===x.css(e,"position"))e=e.offsetParent;return e||s})}}),x.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);x.fn[e]=function(i){return x.access(this,function(e,i,o){var a=or(e);return o===t?a?n in a?a[n]:a.document.documentElement[i]:e[i]:(a?a.scrollTo(r?x(a).scrollLeft():o,r?o:x(a).scrollTop()):e[i]=o,t)},e,i,arguments.length,null)}});function or(e){return x.isWindow(e)?e:9===e.nodeType?e.defaultView||e.parentWindow:!1}x.each({Height:"height",Width:"width"},function(e,n){x.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,i){x.fn[i]=function(i,o){var a=arguments.length&&(r||"boolean"!=typeof i),s=r||(i===!0||o===!0?"margin":"border");return x.access(this,function(n,r,i){var o;return x.isWindow(n)?n.document.documentElement["client"+e]:9===n.nodeType?(o=n.documentElement,Math.max(n.body["scroll"+e],o["scroll"+e],n.body["offset"+e],o["offset"+e],o["client"+e])):i===t?x.css(n,r,s):x.style(n,r,i,s)},n,a?i:t,a,null)}})}),x.fn.size=function(){return this.length},x.fn.andSelf=x.fn.addBack,"object"==typeof module&&module&&"object"==typeof module.exports?module.exports=x:(e.jQuery=e.$=x,"function"==typeof define&&define.amd&&define("jquery",[],function(){return x}))})(window);
-
-/*!
- * jQuery Cookie Plugin v1.4.1
- * https://github.com/carhartl/jquery-cookie
- *
- * Copyright 2006, 2014 Klaus Hartl
- * Released under the MIT license
- */
-(function (factory) {
- if (typeof define === 'function' && define.amd) {
- // AMD
- define(['jquery'], factory);
- } else if (typeof exports === 'object') {
- // CommonJS
- factory(require('jquery'));
- } else {
- // Browser globals
- factory(jQuery);
- }
-}(function ($) {
-
- var pluses = /\+/g;
-
- function encode(s) {
- return config.raw ? s : encodeURIComponent(s);
- }
-
- function decode(s) {
- return config.raw ? s : decodeURIComponent(s);
- }
-
- function stringifyCookieValue(value) {
- return encode(config.json ? JSON.stringify(value) : String(value));
- }
-
- function parseCookieValue(s) {
- if (s.indexOf('"') === 0) {
- // This is a quoted cookie as according to RFC2068, unescape...
- s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
- }
-
- try {
- // Replace server-side written pluses with spaces.
- // If we can't decode the cookie, ignore it, it's unusable.
- // If we can't parse the cookie, ignore it, it's unusable.
- s = decodeURIComponent(s.replace(pluses, ' '));
- return config.json ? JSON.parse(s) : s;
- } catch(e) {}
- }
-
- function read(s, converter) {
- var value = config.raw ? s : parseCookieValue(s);
- return $.isFunction(converter) ? converter(value) : value;
- }
-
- var config = $.cookie = function (key, value, options) {
-
- // Write
-
- if (arguments.length > 1 && !$.isFunction(value)) {
- options = $.extend({}, config.defaults, options);
-
- if (typeof options.expires === 'number') {
- var days = options.expires, t = options.expires = new Date();
- t.setTime(+t + days * 864e+5);
- }
-
- return (document.cookie = [
- encode(key), '=', stringifyCookieValue(value),
- options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
- options.path ? '; path=' + options.path : '',
- options.domain ? '; domain=' + options.domain : '',
- options.secure ? '; secure' : ''
- ].join(''));
- }
-
- // Read
-
- var result = key ? undefined : {};
-
- // To prevent the for loop in the first place assign an empty array
- // in case there are no cookies at all. Also prevents odd result when
- // calling $.cookie().
- var cookies = document.cookie ? document.cookie.split('; ') : [];
-
- for (var i = 0, l = cookies.length; i < l; i++) {
- var parts = cookies[i].split('=');
- var name = decode(parts.shift());
- var cookie = parts.join('=');
-
- if (key && key === name) {
- // If second argument (value) is a function it's a converter...
- result = read(cookie, value);
- break;
- }
-
- // Prevent storing a cookie that we couldn't decode.
- if (!key && (cookie = read(cookie)) !== undefined) {
- result[name] = cookie;
- }
- }
-
- return result;
- };
-
- config.defaults = {};
-
- $.removeCookie = function (key, options) {
- if ($.cookie(key) === undefined) {
- return false;
- }
-
- // Must not alter options, thus extending a fresh object...
- $.cookie(key, '', $.extend({}, options, { expires: -1 }));
- return !$.cookie(key);
- };
-
-}));
-
-/*!
-
- handlebars v1.3.0
-
-Copyright (C) 2011 by Yehuda Katz
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-
-@license
-*/
-/* exported Handlebars */
-var Handlebars = (function() {
-// handlebars/safe-string.js
-var __module4__ = (function() {
- "use strict";
- var __exports__;
- // Build out our basic SafeString type
- function SafeString(string) {
- this.string = string;
- }
-
- SafeString.prototype.toString = function() {
- return "" + this.string;
- };
-
- __exports__ = SafeString;
- return __exports__;
-})();
-
-// handlebars/utils.js
-var __module3__ = (function(__dependency1__) {
- "use strict";
- var __exports__ = {};
- /*jshint -W004 */
- var SafeString = __dependency1__;
-
- var escape = {
- "&": "&",
- "<": "<",
- ">": ">",
- '"': """,
- "'": "'",
- "`": "`"
- };
-
- var badChars = /[&<>"'`]/g;
- var possible = /[&<>"'`]/;
-
- function escapeChar(chr) {
- return escape[chr] || "&";
- }
-
- function extend(obj, value) {
- for(var key in value) {
- if(Object.prototype.hasOwnProperty.call(value, key)) {
- obj[key] = value[key];
- }
- }
- }
-
- __exports__.extend = extend;var toString = Object.prototype.toString;
- __exports__.toString = toString;
- // Sourced from lodash
- // https://github.com/bestiejs/lodash/blob/master/LICENSE.txt
- var isFunction = function(value) {
- return typeof value === 'function';
- };
- // fallback for older versions of Chrome and Safari
- if (isFunction(/x/)) {
- isFunction = function(value) {
- return typeof value === 'function' && toString.call(value) === '[object Function]';
- };
- }
- var isFunction;
- __exports__.isFunction = isFunction;
- var isArray = Array.isArray || function(value) {
- return (value && typeof value === 'object') ? toString.call(value) === '[object Array]' : false;
- };
- __exports__.isArray = isArray;
-
- function escapeExpression(string) {
- // don't escape SafeStrings, since they're already safe
- if (string instanceof SafeString) {
- return string.toString();
- } else if (!string && string !== 0) {
- return "";
- }
-
- // Force a string conversion as this will be done by the append regardless and
- // the regex test will do this transparently behind the scenes, causing issues if
- // an object's to string has escaped characters in it.
- string = "" + string;
-
- if(!possible.test(string)) { return string; }
- return string.replace(badChars, escapeChar);
- }
-
- __exports__.escapeExpression = escapeExpression;function isEmpty(value) {
- if (!value && value !== 0) {
- return true;
- } else if (isArray(value) && value.length === 0) {
- return true;
- } else {
- return false;
- }
- }
-
- __exports__.isEmpty = isEmpty;
- return __exports__;
-})(__module4__);
-
-// handlebars/exception.js
-var __module5__ = (function() {
- "use strict";
- var __exports__;
-
- var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
-
- function Exception(message, node) {
- var line;
- if (node && node.firstLine) {
- line = node.firstLine;
-
- message += ' - ' + line + ':' + node.firstColumn;
- }
-
- var tmp = Error.prototype.constructor.call(this, message);
-
- // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
- for (var idx = 0; idx < errorProps.length; idx++) {
- this[errorProps[idx]] = tmp[errorProps[idx]];
- }
-
- if (line) {
- this.lineNumber = line;
- this.column = node.firstColumn;
- }
- }
-
- Exception.prototype = new Error();
-
- __exports__ = Exception;
- return __exports__;
-})();
-
-// handlebars/base.js
-var __module2__ = (function(__dependency1__, __dependency2__) {
- "use strict";
- var __exports__ = {};
- var Utils = __dependency1__;
- var Exception = __dependency2__;
-
- var VERSION = "1.3.0";
- __exports__.VERSION = VERSION;var COMPILER_REVISION = 4;
- __exports__.COMPILER_REVISION = COMPILER_REVISION;
- var REVISION_CHANGES = {
- 1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it
- 2: '== 1.0.0-rc.3',
- 3: '== 1.0.0-rc.4',
- 4: '>= 1.0.0'
- };
- __exports__.REVISION_CHANGES = REVISION_CHANGES;
- var isArray = Utils.isArray,
- isFunction = Utils.isFunction,
- toString = Utils.toString,
- objectType = '[object Object]';
-
- function HandlebarsEnvironment(helpers, partials) {
- this.helpers = helpers || {};
- this.partials = partials || {};
-
- registerDefaultHelpers(this);
- }
-
- __exports__.HandlebarsEnvironment = HandlebarsEnvironment;HandlebarsEnvironment.prototype = {
- constructor: HandlebarsEnvironment,
-
- logger: logger,
- log: log,
-
- registerHelper: function(name, fn, inverse) {
- if (toString.call(name) === objectType) {
- if (inverse || fn) { throw new Exception('Arg not supported with multiple helpers'); }
- Utils.extend(this.helpers, name);
- } else {
- if (inverse) { fn.not = inverse; }
- this.helpers[name] = fn;
- }
- },
-
- registerPartial: function(name, str) {
- if (toString.call(name) === objectType) {
- Utils.extend(this.partials, name);
- } else {
- this.partials[name] = str;
- }
- }
- };
-
- function registerDefaultHelpers(instance) {
- instance.registerHelper('helperMissing', function(arg) {
- if(arguments.length === 2) {
- return undefined;
- } else {
- throw new Exception("Missing helper: '" + arg + "'");
- }
- });
-
- instance.registerHelper('blockHelperMissing', function(context, options) {
- var inverse = options.inverse || function() {}, fn = options.fn;
-
- if (isFunction(context)) { context = context.call(this); }
-
- if(context === true) {
- return fn(this);
- } else if(context === false || context == null) {
- return inverse(this);
- } else if (isArray(context)) {
- if(context.length > 0) {
- return instance.helpers.each(context, options);
- } else {
- return inverse(this);
- }
- } else {
- return fn(context);
- }
- });
-
- instance.registerHelper('each', function(context, options) {
- var fn = options.fn, inverse = options.inverse;
- var i = 0, ret = "", data;
-
- if (isFunction(context)) { context = context.call(this); }
-
- if (options.data) {
- data = createFrame(options.data);
- }
-
- if(context && typeof context === 'object') {
- if (isArray(context)) {
- for(var j = context.length; i
0) {
- throw new Exception("Invalid path: " + original, this);
- } else if (part === "..") {
- depth++;
- } else {
- this.isScoped = true;
- }
- } else {
- dig.push(part);
- }
- }
-
- this.original = original;
- this.parts = dig;
- this.string = dig.join('.');
- this.depth = depth;
-
- // an ID is simple if it only has one part, and that part is not
- // `..` or `this`.
- this.isSimple = parts.length === 1 && !this.isScoped && depth === 0;
-
- this.stringModeValue = this.string;
- },
-
- PartialNameNode: function(name, locInfo) {
- LocationInfo.call(this, locInfo);
- this.type = "PARTIAL_NAME";
- this.name = name.original;
- },
-
- DataNode: function(id, locInfo) {
- LocationInfo.call(this, locInfo);
- this.type = "DATA";
- this.id = id;
- },
-
- StringNode: function(string, locInfo) {
- LocationInfo.call(this, locInfo);
- this.type = "STRING";
- this.original =
- this.string =
- this.stringModeValue = string;
- },
-
- IntegerNode: function(integer, locInfo) {
- LocationInfo.call(this, locInfo);
- this.type = "INTEGER";
- this.original =
- this.integer = integer;
- this.stringModeValue = Number(integer);
- },
-
- BooleanNode: function(bool, locInfo) {
- LocationInfo.call(this, locInfo);
- this.type = "BOOLEAN";
- this.bool = bool;
- this.stringModeValue = bool === "true";
- },
-
- CommentNode: function(comment, locInfo) {
- LocationInfo.call(this, locInfo);
- this.type = "comment";
- this.comment = comment;
- }
- };
-
- // Must be exported as an object rather than the root of the module as the jison lexer
- // most modify the object to operate properly.
- __exports__ = AST;
- return __exports__;
-})(__module5__);
-
-// handlebars/compiler/parser.js
-var __module9__ = (function() {
- "use strict";
- var __exports__;
- /* jshint ignore:start */
- /* Jison generated parser */
- var handlebars = (function(){
- var parser = {trace: function trace() { },
- yy: {},
- symbols_: {"error":2,"root":3,"statements":4,"EOF":5,"program":6,"simpleInverse":7,"statement":8,"openInverse":9,"closeBlock":10,"openBlock":11,"mustache":12,"partial":13,"CONTENT":14,"COMMENT":15,"OPEN_BLOCK":16,"sexpr":17,"CLOSE":18,"OPEN_INVERSE":19,"OPEN_ENDBLOCK":20,"path":21,"OPEN":22,"OPEN_UNESCAPED":23,"CLOSE_UNESCAPED":24,"OPEN_PARTIAL":25,"partialName":26,"partial_option0":27,"sexpr_repetition0":28,"sexpr_option0":29,"dataName":30,"param":31,"STRING":32,"INTEGER":33,"BOOLEAN":34,"OPEN_SEXPR":35,"CLOSE_SEXPR":36,"hash":37,"hash_repetition_plus0":38,"hashSegment":39,"ID":40,"EQUALS":41,"DATA":42,"pathSegments":43,"SEP":44,"$accept":0,"$end":1},
- terminals_: {2:"error",5:"EOF",14:"CONTENT",15:"COMMENT",16:"OPEN_BLOCK",18:"CLOSE",19:"OPEN_INVERSE",20:"OPEN_ENDBLOCK",22:"OPEN",23:"OPEN_UNESCAPED",24:"CLOSE_UNESCAPED",25:"OPEN_PARTIAL",32:"STRING",33:"INTEGER",34:"BOOLEAN",35:"OPEN_SEXPR",36:"CLOSE_SEXPR",40:"ID",41:"EQUALS",42:"DATA",44:"SEP"},
- productions_: [0,[3,2],[3,1],[6,2],[6,3],[6,2],[6,1],[6,1],[6,0],[4,1],[4,2],[8,3],[8,3],[8,1],[8,1],[8,1],[8,1],[11,3],[9,3],[10,3],[12,3],[12,3],[13,4],[7,2],[17,3],[17,1],[31,1],[31,1],[31,1],[31,1],[31,1],[31,3],[37,1],[39,3],[26,1],[26,1],[26,1],[30,2],[21,1],[43,3],[43,1],[27,0],[27,1],[28,0],[28,2],[29,0],[29,1],[38,1],[38,2]],
- performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {
-
- var $0 = $$.length - 1;
- switch (yystate) {
- case 1: return new yy.ProgramNode($$[$0-1], this._$);
- break;
- case 2: return new yy.ProgramNode([], this._$);
- break;
- case 3:this.$ = new yy.ProgramNode([], $$[$0-1], $$[$0], this._$);
- break;
- case 4:this.$ = new yy.ProgramNode($$[$0-2], $$[$0-1], $$[$0], this._$);
- break;
- case 5:this.$ = new yy.ProgramNode($$[$0-1], $$[$0], [], this._$);
- break;
- case 6:this.$ = new yy.ProgramNode($$[$0], this._$);
- break;
- case 7:this.$ = new yy.ProgramNode([], this._$);
- break;
- case 8:this.$ = new yy.ProgramNode([], this._$);
- break;
- case 9:this.$ = [$$[$0]];
- break;
- case 10: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
- break;
- case 11:this.$ = new yy.BlockNode($$[$0-2], $$[$0-1].inverse, $$[$0-1], $$[$0], this._$);
- break;
- case 12:this.$ = new yy.BlockNode($$[$0-2], $$[$0-1], $$[$0-1].inverse, $$[$0], this._$);
- break;
- case 13:this.$ = $$[$0];
- break;
- case 14:this.$ = $$[$0];
- break;
- case 15:this.$ = new yy.ContentNode($$[$0], this._$);
- break;
- case 16:this.$ = new yy.CommentNode($$[$0], this._$);
- break;
- case 17:this.$ = new yy.MustacheNode($$[$0-1], null, $$[$0-2], stripFlags($$[$0-2], $$[$0]), this._$);
- break;
- case 18:this.$ = new yy.MustacheNode($$[$0-1], null, $$[$0-2], stripFlags($$[$0-2], $$[$0]), this._$);
- break;
- case 19:this.$ = {path: $$[$0-1], strip: stripFlags($$[$0-2], $$[$0])};
- break;
- case 20:this.$ = new yy.MustacheNode($$[$0-1], null, $$[$0-2], stripFlags($$[$0-2], $$[$0]), this._$);
- break;
- case 21:this.$ = new yy.MustacheNode($$[$0-1], null, $$[$0-2], stripFlags($$[$0-2], $$[$0]), this._$);
- break;
- case 22:this.$ = new yy.PartialNode($$[$0-2], $$[$0-1], stripFlags($$[$0-3], $$[$0]), this._$);
- break;
- case 23:this.$ = stripFlags($$[$0-1], $$[$0]);
- break;
- case 24:this.$ = new yy.SexprNode([$$[$0-2]].concat($$[$0-1]), $$[$0], this._$);
- break;
- case 25:this.$ = new yy.SexprNode([$$[$0]], null, this._$);
- break;
- case 26:this.$ = $$[$0];
- break;
- case 27:this.$ = new yy.StringNode($$[$0], this._$);
- break;
- case 28:this.$ = new yy.IntegerNode($$[$0], this._$);
- break;
- case 29:this.$ = new yy.BooleanNode($$[$0], this._$);
- break;
- case 30:this.$ = $$[$0];
- break;
- case 31:$$[$0-1].isHelper = true; this.$ = $$[$0-1];
- break;
- case 32:this.$ = new yy.HashNode($$[$0], this._$);
- break;
- case 33:this.$ = [$$[$0-2], $$[$0]];
- break;
- case 34:this.$ = new yy.PartialNameNode($$[$0], this._$);
- break;
- case 35:this.$ = new yy.PartialNameNode(new yy.StringNode($$[$0], this._$), this._$);
- break;
- case 36:this.$ = new yy.PartialNameNode(new yy.IntegerNode($$[$0], this._$));
- break;
- case 37:this.$ = new yy.DataNode($$[$0], this._$);
- break;
- case 38:this.$ = new yy.IdNode($$[$0], this._$);
- break;
- case 39: $$[$0-2].push({part: $$[$0], separator: $$[$0-1]}); this.$ = $$[$0-2];
- break;
- case 40:this.$ = [{part: $$[$0]}];
- break;
- case 43:this.$ = [];
- break;
- case 44:$$[$0-1].push($$[$0]);
- break;
- case 47:this.$ = [$$[$0]];
- break;
- case 48:$$[$0-1].push($$[$0]);
- break;
- }
- },
- table: [{3:1,4:2,5:[1,3],8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],22:[1,13],23:[1,14],25:[1,15]},{1:[3]},{5:[1,16],8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],22:[1,13],23:[1,14],25:[1,15]},{1:[2,2]},{5:[2,9],14:[2,9],15:[2,9],16:[2,9],19:[2,9],20:[2,9],22:[2,9],23:[2,9],25:[2,9]},{4:20,6:18,7:19,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,21],20:[2,8],22:[1,13],23:[1,14],25:[1,15]},{4:20,6:22,7:19,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,21],20:[2,8],22:[1,13],23:[1,14],25:[1,15]},{5:[2,13],14:[2,13],15:[2,13],16:[2,13],19:[2,13],20:[2,13],22:[2,13],23:[2,13],25:[2,13]},{5:[2,14],14:[2,14],15:[2,14],16:[2,14],19:[2,14],20:[2,14],22:[2,14],23:[2,14],25:[2,14]},{5:[2,15],14:[2,15],15:[2,15],16:[2,15],19:[2,15],20:[2,15],22:[2,15],23:[2,15],25:[2,15]},{5:[2,16],14:[2,16],15:[2,16],16:[2,16],19:[2,16],20:[2,16],22:[2,16],23:[2,16],25:[2,16]},{17:23,21:24,30:25,40:[1,28],42:[1,27],43:26},{17:29,21:24,30:25,40:[1,28],42:[1,27],43:26},{17:30,21:24,30:25,40:[1,28],42:[1,27],43:26},{17:31,21:24,30:25,40:[1,28],42:[1,27],43:26},{21:33,26:32,32:[1,34],33:[1,35],40:[1,28],43:26},{1:[2,1]},{5:[2,10],14:[2,10],15:[2,10],16:[2,10],19:[2,10],20:[2,10],22:[2,10],23:[2,10],25:[2,10]},{10:36,20:[1,37]},{4:38,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,7],22:[1,13],23:[1,14],25:[1,15]},{7:39,8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,21],20:[2,6],22:[1,13],23:[1,14],25:[1,15]},{17:23,18:[1,40],21:24,30:25,40:[1,28],42:[1,27],43:26},{10:41,20:[1,37]},{18:[1,42]},{18:[2,43],24:[2,43],28:43,32:[2,43],33:[2,43],34:[2,43],35:[2,43],36:[2,43],40:[2,43],42:[2,43]},{18:[2,25],24:[2,25],36:[2,25]},{18:[2,38],24:[2,38],32:[2,38],33:[2,38],34:[2,38],35:[2,38],36:[2,38],40:[2,38],42:[2,38],44:[1,44]},{21:45,40:[1,28],43:26},{18:[2,40],24:[2,40],32:[2,40],33:[2,40],34:[2,40],35:[2,40],36:[2,40],40:[2,40],42:[2,40],44:[2,40]},{18:[1,46]},{18:[1,47]},{24:[1,48]},{18:[2,41],21:50,27:49,40:[1,28],43:26},{18:[2,34],40:[2,34]},{18:[2,35],40:[2,35]},{18:[2,36],40:[2,36]},{5:[2,11],14:[2,11],15:[2,11],16:[2,11],19:[2,11],20:[2,11],22:[2,11],23:[2,11],25:[2,11]},{21:51,40:[1,28],43:26},{8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,3],22:[1,13],23:[1,14],25:[1,15]},{4:52,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,5],22:[1,13],23:[1,14],25:[1,15]},{14:[2,23],15:[2,23],16:[2,23],19:[2,23],20:[2,23],22:[2,23],23:[2,23],25:[2,23]},{5:[2,12],14:[2,12],15:[2,12],16:[2,12],19:[2,12],20:[2,12],22:[2,12],23:[2,12],25:[2,12]},{14:[2,18],15:[2,18],16:[2,18],19:[2,18],20:[2,18],22:[2,18],23:[2,18],25:[2,18]},{18:[2,45],21:56,24:[2,45],29:53,30:60,31:54,32:[1,57],33:[1,58],34:[1,59],35:[1,61],36:[2,45],37:55,38:62,39:63,40:[1,64],42:[1,27],43:26},{40:[1,65]},{18:[2,37],24:[2,37],32:[2,37],33:[2,37],34:[2,37],35:[2,37],36:[2,37],40:[2,37],42:[2,37]},{14:[2,17],15:[2,17],16:[2,17],19:[2,17],20:[2,17],22:[2,17],23:[2,17],25:[2,17]},{5:[2,20],14:[2,20],15:[2,20],16:[2,20],19:[2,20],20:[2,20],22:[2,20],23:[2,20],25:[2,20]},{5:[2,21],14:[2,21],15:[2,21],16:[2,21],19:[2,21],20:[2,21],22:[2,21],23:[2,21],25:[2,21]},{18:[1,66]},{18:[2,42]},{18:[1,67]},{8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,4],22:[1,13],23:[1,14],25:[1,15]},{18:[2,24],24:[2,24],36:[2,24]},{18:[2,44],24:[2,44],32:[2,44],33:[2,44],34:[2,44],35:[2,44],36:[2,44],40:[2,44],42:[2,44]},{18:[2,46],24:[2,46],36:[2,46]},{18:[2,26],24:[2,26],32:[2,26],33:[2,26],34:[2,26],35:[2,26],36:[2,26],40:[2,26],42:[2,26]},{18:[2,27],24:[2,27],32:[2,27],33:[2,27],34:[2,27],35:[2,27],36:[2,27],40:[2,27],42:[2,27]},{18:[2,28],24:[2,28],32:[2,28],33:[2,28],34:[2,28],35:[2,28],36:[2,28],40:[2,28],42:[2,28]},{18:[2,29],24:[2,29],32:[2,29],33:[2,29],34:[2,29],35:[2,29],36:[2,29],40:[2,29],42:[2,29]},{18:[2,30],24:[2,30],32:[2,30],33:[2,30],34:[2,30],35:[2,30],36:[2,30],40:[2,30],42:[2,30]},{17:68,21:24,30:25,40:[1,28],42:[1,27],43:26},{18:[2,32],24:[2,32],36:[2,32],39:69,40:[1,70]},{18:[2,47],24:[2,47],36:[2,47],40:[2,47]},{18:[2,40],24:[2,40],32:[2,40],33:[2,40],34:[2,40],35:[2,40],36:[2,40],40:[2,40],41:[1,71],42:[2,40],44:[2,40]},{18:[2,39],24:[2,39],32:[2,39],33:[2,39],34:[2,39],35:[2,39],36:[2,39],40:[2,39],42:[2,39],44:[2,39]},{5:[2,22],14:[2,22],15:[2,22],16:[2,22],19:[2,22],20:[2,22],22:[2,22],23:[2,22],25:[2,22]},{5:[2,19],14:[2,19],15:[2,19],16:[2,19],19:[2,19],20:[2,19],22:[2,19],23:[2,19],25:[2,19]},{36:[1,72]},{18:[2,48],24:[2,48],36:[2,48],40:[2,48]},{41:[1,71]},{21:56,30:60,31:73,32:[1,57],33:[1,58],34:[1,59],35:[1,61],40:[1,28],42:[1,27],43:26},{18:[2,31],24:[2,31],32:[2,31],33:[2,31],34:[2,31],35:[2,31],36:[2,31],40:[2,31],42:[2,31]},{18:[2,33],24:[2,33],36:[2,33],40:[2,33]}],
- defaultActions: {3:[2,2],16:[2,1],50:[2,42]},
- parseError: function parseError(str, hash) {
- throw new Error(str);
- },
- parse: function parse(input) {
- var self = this, stack = [0], vstack = [null], lstack = [], table = this.table, yytext = "", yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1;
- this.lexer.setInput(input);
- this.lexer.yy = this.yy;
- this.yy.lexer = this.lexer;
- this.yy.parser = this;
- if (typeof this.lexer.yylloc == "undefined")
- this.lexer.yylloc = {};
- var yyloc = this.lexer.yylloc;
- lstack.push(yyloc);
- var ranges = this.lexer.options && this.lexer.options.ranges;
- if (typeof this.yy.parseError === "function")
- this.parseError = this.yy.parseError;
- function popStack(n) {
- stack.length = stack.length - 2 * n;
- vstack.length = vstack.length - n;
- lstack.length = lstack.length - n;
- }
- function lex() {
- var token;
- token = self.lexer.lex() || 1;
- if (typeof token !== "number") {
- token = self.symbols_[token] || token;
- }
- return token;
- }
- var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected;
- while (true) {
- state = stack[stack.length - 1];
- if (this.defaultActions[state]) {
- action = this.defaultActions[state];
- } else {
- if (symbol === null || typeof symbol == "undefined") {
- symbol = lex();
- }
- action = table[state] && table[state][symbol];
- }
- if (typeof action === "undefined" || !action.length || !action[0]) {
- var errStr = "";
- if (!recovering) {
- expected = [];
- for (p in table[state])
- if (this.terminals_[p] && p > 2) {
- expected.push("'" + this.terminals_[p] + "'");
- }
- if (this.lexer.showPosition) {
- errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'";
- } else {
- errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1?"end of input":"'" + (this.terminals_[symbol] || symbol) + "'");
- }
- this.parseError(errStr, {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected});
- }
- }
- if (action[0] instanceof Array && action.length > 1) {
- throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol);
- }
- switch (action[0]) {
- case 1:
- stack.push(symbol);
- vstack.push(this.lexer.yytext);
- lstack.push(this.lexer.yylloc);
- stack.push(action[1]);
- symbol = null;
- if (!preErrorSymbol) {
- yyleng = this.lexer.yyleng;
- yytext = this.lexer.yytext;
- yylineno = this.lexer.yylineno;
- yyloc = this.lexer.yylloc;
- if (recovering > 0)
- recovering--;
- } else {
- symbol = preErrorSymbol;
- preErrorSymbol = null;
- }
- break;
- case 2:
- len = this.productions_[action[1]][1];
- yyval.$ = vstack[vstack.length - len];
- yyval._$ = {first_line: lstack[lstack.length - (len || 1)].first_line, last_line: lstack[lstack.length - 1].last_line, first_column: lstack[lstack.length - (len || 1)].first_column, last_column: lstack[lstack.length - 1].last_column};
- if (ranges) {
- yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]];
- }
- r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack);
- if (typeof r !== "undefined") {
- return r;
- }
- if (len) {
- stack = stack.slice(0, -1 * len * 2);
- vstack = vstack.slice(0, -1 * len);
- lstack = lstack.slice(0, -1 * len);
- }
- stack.push(this.productions_[action[1]][0]);
- vstack.push(yyval.$);
- lstack.push(yyval._$);
- newState = table[stack[stack.length - 2]][stack[stack.length - 1]];
- stack.push(newState);
- break;
- case 3:
- return true;
- }
- }
- return true;
- }
- };
-
-
- function stripFlags(open, close) {
- return {
- left: open.charAt(2) === '~',
- right: close.charAt(0) === '~' || close.charAt(1) === '~'
- };
- }
-
- /* Jison generated lexer */
- var lexer = (function(){
- var lexer = ({EOF:1,
- parseError:function parseError(str, hash) {
- if (this.yy.parser) {
- this.yy.parser.parseError(str, hash);
- } else {
- throw new Error(str);
- }
- },
- setInput:function (input) {
- this._input = input;
- this._more = this._less = this.done = false;
- 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};
- if (this.options.ranges) this.yylloc.range = [0,0];
- this.offset = 0;
- return this;
- },
- input:function () {
- var ch = this._input[0];
- this.yytext += ch;
- this.yyleng++;
- this.offset++;
- this.match += ch;
- this.matched += ch;
- var lines = ch.match(/(?:\r\n?|\n).*/g);
- if (lines) {
- this.yylineno++;
- this.yylloc.last_line++;
- } else {
- this.yylloc.last_column++;
- }
- if (this.options.ranges) this.yylloc.range[1]++;
-
- this._input = this._input.slice(1);
- return ch;
- },
- unput:function (ch) {
- var len = ch.length;
- var lines = ch.split(/(?:\r\n?|\n)/g);
-
- this._input = ch + this._input;
- this.yytext = this.yytext.substr(0, this.yytext.length-len-1);
- //this.yyleng -= len;
- this.offset -= len;
- var oldLines = this.match.split(/(?:\r\n?|\n)/g);
- this.match = this.match.substr(0, this.match.length-1);
- this.matched = this.matched.substr(0, this.matched.length-1);
-
- if (lines.length-1) this.yylineno -= lines.length-1;
- var r = this.yylloc.range;
-
- this.yylloc = {first_line: this.yylloc.first_line,
- last_line: this.yylineno+1,
- first_column: this.yylloc.first_column,
- last_column: lines ?
- (lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length:
- this.yylloc.first_column - len
- };
-
- if (this.options.ranges) {
- this.yylloc.range = [r[0], r[0] + this.yyleng - len];
- }
- return this;
- },
- more:function () {
- this._more = true;
- return this;
- },
- less:function (n) {
- this.unput(this.match.slice(n));
- },
- pastInput:function () {
- var past = this.matched.substr(0, this.matched.length - this.match.length);
- return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
- },
- upcomingInput:function () {
- var next = this.match;
- if (next.length < 20) {
- next += this._input.substr(0, 20-next.length);
- }
- return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, "");
- },
- showPosition:function () {
- var pre = this.pastInput();
- var c = new Array(pre.length + 1).join("-");
- return pre + this.upcomingInput() + "\n" + c+"^";
- },
- next:function () {
- if (this.done) {
- return this.EOF;
- }
- if (!this._input) this.done = true;
-
- var token,
- match,
- tempMatch,
- index,
- col,
- lines;
- if (!this._more) {
- this.yytext = '';
- this.match = '';
- }
- var rules = this._currentRules();
- for (var i=0;i < rules.length; i++) {
- tempMatch = this._input.match(this.rules[rules[i]]);
- if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
- match = tempMatch;
- index = i;
- if (!this.options.flex) break;
- }
- }
- if (match) {
- lines = match[0].match(/(?:\r\n?|\n).*/g);
- if (lines) this.yylineno += lines.length;
- this.yylloc = {first_line: this.yylloc.last_line,
- last_line: this.yylineno+1,
- first_column: this.yylloc.last_column,
- last_column: lines ? lines[lines.length-1].length-lines[lines.length-1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length};
- this.yytext += match[0];
- this.match += match[0];
- this.matches = match;
- this.yyleng = this.yytext.length;
- if (this.options.ranges) {
- this.yylloc.range = [this.offset, this.offset += this.yyleng];
- }
- this._more = false;
- this._input = this._input.slice(match[0].length);
- this.matched += match[0];
- token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]);
- if (this.done && this._input) this.done = false;
- if (token) return token;
- else return;
- }
- if (this._input === "") {
- return this.EOF;
- } else {
- return this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(),
- {text: "", token: null, line: this.yylineno});
- }
- },
- lex:function lex() {
- var r = this.next();
- if (typeof r !== 'undefined') {
- return r;
- } else {
- return this.lex();
- }
- },
- begin:function begin(condition) {
- this.conditionStack.push(condition);
- },
- popState:function popState() {
- return this.conditionStack.pop();
- },
- _currentRules:function _currentRules() {
- return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules;
- },
- topState:function () {
- return this.conditionStack[this.conditionStack.length-2];
- },
- pushState:function begin(condition) {
- this.begin(condition);
- }});
- lexer.options = {};
- lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
-
-
- function strip(start, end) {
- return yy_.yytext = yy_.yytext.substr(start, yy_.yyleng-end);
- }
-
-
- var YYSTATE=YY_START
- switch($avoiding_name_collisions) {
- case 0:
- if(yy_.yytext.slice(-2) === "\\\\") {
- strip(0,1);
- this.begin("mu");
- } else if(yy_.yytext.slice(-1) === "\\") {
- strip(0,1);
- this.begin("emu");
- } else {
- this.begin("mu");
- }
- if(yy_.yytext) return 14;
-
- break;
- case 1:return 14;
- break;
- case 2:
- this.popState();
- return 14;
-
- break;
- case 3:strip(0,4); this.popState(); return 15;
- break;
- case 4:return 35;
- break;
- case 5:return 36;
- break;
- case 6:return 25;
- break;
- case 7:return 16;
- break;
- case 8:return 20;
- break;
- case 9:return 19;
- break;
- case 10:return 19;
- break;
- case 11:return 23;
- break;
- case 12:return 22;
- break;
- case 13:this.popState(); this.begin('com');
- break;
- case 14:strip(3,5); this.popState(); return 15;
- break;
- case 15:return 22;
- break;
- case 16:return 41;
- break;
- case 17:return 40;
- break;
- case 18:return 40;
- break;
- case 19:return 44;
- break;
- case 20:// ignore whitespace
- break;
- case 21:this.popState(); return 24;
- break;
- case 22:this.popState(); return 18;
- break;
- case 23:yy_.yytext = strip(1,2).replace(/\\"/g,'"'); return 32;
- break;
- case 24:yy_.yytext = strip(1,2).replace(/\\'/g,"'"); return 32;
- break;
- case 25:return 42;
- break;
- case 26:return 34;
- break;
- case 27:return 34;
- break;
- case 28:return 33;
- break;
- case 29:return 40;
- break;
- case 30:yy_.yytext = strip(1,2); return 40;
- break;
- case 31:return 'INVALID';
- break;
- case 32:return 5;
- break;
- }
- };
- lexer.rules = [/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|\\\{\{|\\\\\{\{|$)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\()/,/^(?:\))/,/^(?:\{\{(~)?>)/,/^(?:\{\{(~)?#)/,/^(?:\{\{(~)?\/)/,/^(?:\{\{(~)?\^)/,/^(?:\{\{(~)?\s*else\b)/,/^(?:\{\{(~)?\{)/,/^(?:\{\{(~)?&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{(~)?)/,/^(?:=)/,/^(?:\.\.)/,/^(?:\.(?=([=~}\s\/.)])))/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}(~)?\}\})/,/^(?:(~)?\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=([~}\s)])))/,/^(?:false(?=([~}\s)])))/,/^(?:-?[0-9]+(?=([~}\s)])))/,/^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.)]))))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:$)/];
- lexer.conditions = {"mu":{"rules":[4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32],"inclusive":false},"emu":{"rules":[2],"inclusive":false},"com":{"rules":[3],"inclusive":false},"INITIAL":{"rules":[0,1,32],"inclusive":true}};
- return lexer;})()
- parser.lexer = lexer;
- function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser;
- return new Parser;
- })();__exports__ = handlebars;
- /* jshint ignore:end */
- return __exports__;
-})();
-
-// handlebars/compiler/base.js
-var __module8__ = (function(__dependency1__, __dependency2__) {
- "use strict";
- var __exports__ = {};
- var parser = __dependency1__;
- var AST = __dependency2__;
-
- __exports__.parser = parser;
-
- function parse(input) {
- // Just return if an already-compile AST was passed in.
- if(input.constructor === AST.ProgramNode) { return input; }
-
- parser.yy = AST;
- return parser.parse(input);
- }
-
- __exports__.parse = parse;
- return __exports__;
-})(__module9__, __module7__);
-
-// handlebars/compiler/compiler.js
-var __module10__ = (function(__dependency1__) {
- "use strict";
- var __exports__ = {};
- var Exception = __dependency1__;
-
- function Compiler() {}
-
- __exports__.Compiler = Compiler;// the foundHelper register will disambiguate helper lookup from finding a
- // function in a context. This is necessary for mustache compatibility, which
- // requires that context functions in blocks are evaluated by blockHelperMissing,
- // and then proceed as if the resulting value was provided to blockHelperMissing.
-
- Compiler.prototype = {
- compiler: Compiler,
-
- disassemble: function() {
- var opcodes = this.opcodes, opcode, out = [], params, param;
-
- for (var i=0, l=opcodes.length; i 0) {
- this.source[1] = this.source[1] + ", " + locals.join(", ");
- }
-
- // Generate minimizer alias mappings
- if (!this.isChild) {
- for (var alias in this.context.aliases) {
- if (this.context.aliases.hasOwnProperty(alias)) {
- this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias];
- }
- }
- }
-
- if (this.source[1]) {
- this.source[1] = "var " + this.source[1].substring(2) + ";";
- }
-
- // Merge children
- if (!this.isChild) {
- this.source[1] += '\n' + this.context.programs.join('\n') + '\n';
- }
-
- if (!this.environment.isSimple) {
- this.pushSource("return buffer;");
- }
-
- var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"];
-
- for(var i=0, l=this.environment.depths.list.length; i this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); }
- return this.topStackName();
- },
- topStackName: function() {
- return "stack" + this.stackSlot;
- },
- flushInline: function() {
- var inlineStack = this.inlineStack;
- if (inlineStack.length) {
- this.inlineStack = [];
- for (var i = 0, len = inlineStack.length; i < len; i++) {
- var entry = inlineStack[i];
- if (entry instanceof Literal) {
- this.compileStack.push(entry);
- } else {
- this.pushStack(entry);
- }
- }
- }
- },
- isInline: function() {
- return this.inlineStack.length;
- },
-
- popStack: function(wrapped) {
- var inline = this.isInline(),
- item = (inline ? this.inlineStack : this.compileStack).pop();
-
- if (!wrapped && (item instanceof Literal)) {
- return item.value;
- } else {
- if (!inline) {
- if (!this.stackSlot) {
- throw new Exception('Invalid stack pop');
- }
- this.stackSlot--;
- }
- return item;
- }
- },
-
- topStack: function(wrapped) {
- var stack = (this.isInline() ? this.inlineStack : this.compileStack),
- item = stack[stack.length - 1];
-
- if (!wrapped && (item instanceof Literal)) {
- return item.value;
- } else {
- return item;
- }
- },
-
- quotedString: function(str) {
- return '"' + str
- .replace(/\\/g, '\\\\')
- .replace(/"/g, '\\"')
- .replace(/\n/g, '\\n')
- .replace(/\r/g, '\\r')
- .replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4
- .replace(/\u2029/g, '\\u2029') + '"';
- },
-
- setupHelper: function(paramSize, name, missingParams) {
- var params = [],
- paramsInit = this.setupParams(paramSize, params, missingParams);
- var foundHelper = this.nameLookup('helpers', name, 'helper');
-
- return {
- params: params,
- paramsInit: paramsInit,
- name: foundHelper,
- callParams: ["depth0"].concat(params).join(", "),
- helperMissingParams: missingParams && ["depth0", this.quotedString(name)].concat(params).join(", ")
- };
- },
-
- setupOptions: function(paramSize, params) {
- var options = [], contexts = [], types = [], param, inverse, program;
-
- options.push("hash:" + this.popStack());
-
- if (this.options.stringParams) {
- options.push("hashTypes:" + this.popStack());
- options.push("hashContexts:" + this.popStack());
- }
-
- inverse = this.popStack();
- program = this.popStack();
-
- // Avoid setting fn and inverse if neither are set. This allows
- // helpers to do a check for `if (options.fn)`
- if (program || inverse) {
- if (!program) {
- this.context.aliases.self = "this";
- program = "self.noop";
- }
-
- if (!inverse) {
- this.context.aliases.self = "this";
- inverse = "self.noop";
- }
-
- options.push("inverse:" + inverse);
- options.push("fn:" + program);
- }
-
- for(var i=0; i tag) - put directly in global namespace
- $.sammy = window.Sammy = factory($);
- }
-})(function($){
-
- var Sammy,
- PATH_REPLACER = "([^\/]+)",
- PATH_NAME_MATCHER = /:([\w\d]+)/g,
- QUERY_STRING_MATCHER = /\?([^#]*)?$/,
- // mainly for making `arguments` an Array
- _makeArray = function(nonarray) { return Array.prototype.slice.call(nonarray); },
- // borrowed from jQuery
- _isFunction = function( obj ) { return Object.prototype.toString.call(obj) === "[object Function]"; },
- _isArray = function( obj ) { return Object.prototype.toString.call(obj) === "[object Array]"; },
- _isRegExp = function( obj ) { return Object.prototype.toString.call(obj) === "[object RegExp]"; },
- _decode = function( str ) { return decodeURIComponent((str || '').replace(/\+/g, ' ')); },
- _encode = encodeURIComponent,
- _escapeHTML = function(s) {
- return String(s).replace(/&(?!\w+;)/g, '&').replace(//g, '>').replace(/"/g, '"');
- },
- _routeWrapper = function(verb) {
- return function() {
- return this.route.apply(this, [verb].concat(Array.prototype.slice.call(arguments)));
- };
- },
- _template_cache = {},
- _has_history = !!(window.history && history.pushState),
- loggers = [];
-
-
- // `Sammy` (also aliased as $.sammy) is not only the namespace for a
- // number of prototypes, its also a top level method that allows for easy
- // creation/management of `Sammy.Application` instances. There are a
- // number of different forms for `Sammy()` but each returns an instance
- // of `Sammy.Application`. When a new instance is created using
- // `Sammy` it is added to an Object called `Sammy.apps`. This
- // provides for an easy way to get at existing Sammy applications. Only one
- // instance is allowed per `element_selector` so when calling
- // `Sammy('selector')` multiple times, the first time will create
- // the application and the following times will extend the application
- // already added to that selector.
- //
- // ### Example
- //
- // // returns the app at #main or a new app
- // Sammy('#main')
- //
- // // equivalent to "new Sammy.Application", except appends to apps
- // Sammy();
- // Sammy(function() { ... });
- //
- // // extends the app at '#main' with function.
- // Sammy('#main', function() { ... });
- //
- Sammy = function() {
- var args = _makeArray(arguments),
- app, selector;
- Sammy.apps = Sammy.apps || {};
- if (args.length === 0 || args[0] && _isFunction(args[0])) { // Sammy()
- return Sammy.apply(Sammy, ['body'].concat(args));
- } else if (typeof (selector = args.shift()) == 'string') { // Sammy('#main')
- app = Sammy.apps[selector] || new Sammy.Application();
- app.element_selector = selector;
- if (args.length > 0) {
- $.each(args, function(i, plugin) {
- app.use(plugin);
- });
- }
- // if the selector changes make sure the reference in Sammy.apps changes
- if (app.element_selector != selector) {
- delete Sammy.apps[selector];
- }
- Sammy.apps[app.element_selector] = app;
- return app;
- }
- };
-
- Sammy.VERSION = '0.7.4';
-
- // Add to the global logger pool. Takes a function that accepts an
- // unknown number of arguments and should print them or send them somewhere
- // The first argument is always a timestamp.
- Sammy.addLogger = function(logger) {
- loggers.push(logger);
- };
-
- // Sends a log message to each logger listed in the global
- // loggers pool. Can take any number of arguments.
- // Also prefixes the arguments with a timestamp.
- Sammy.log = function() {
- var args = _makeArray(arguments);
- args.unshift("[" + Date() + "]");
- $.each(loggers, function(i, logger) {
- logger.apply(Sammy, args);
- });
- };
-
- if (typeof window.console != 'undefined') {
- if (_isFunction(window.console.log.apply)) {
- Sammy.addLogger(function() {
- window.console.log.apply(window.console, arguments);
- });
- } else {
- Sammy.addLogger(function() {
- window.console.log(arguments);
- });
- }
- } else if (typeof console != 'undefined') {
- Sammy.addLogger(function() {
- console.log.apply(console, arguments);
- });
- }
-
- $.extend(Sammy, {
- makeArray: _makeArray,
- isFunction: _isFunction,
- isArray: _isArray
- });
-
- // Sammy.Object is the base for all other Sammy classes. It provides some useful
- // functionality, including cloning, iterating, etc.
- Sammy.Object = function(obj) { // constructor
- return $.extend(this, obj || {});
- };
-
- $.extend(Sammy.Object.prototype, {
-
- // Escape HTML in string, use in templates to prevent script injection.
- // Also aliased as `h()`
- escapeHTML: _escapeHTML,
- h: _escapeHTML,
-
- // Returns a copy of the object with Functions removed.
- toHash: function() {
- var json = {};
- $.each(this, function(k,v) {
- if (!_isFunction(v)) {
- json[k] = v;
- }
- });
- return json;
- },
-
- // Renders a simple HTML version of this Objects attributes.
- // Does not render functions.
- // For example. Given this Sammy.Object:
- //
- // var s = new Sammy.Object({first_name: 'Sammy', last_name: 'Davis Jr.'});
- // s.toHTML()
- // //=> 'first_name Sammy
last_name Davis Jr.
'
- //
- toHTML: function() {
- var display = "";
- $.each(this, function(k, v) {
- if (!_isFunction(v)) {
- display += "" + k + " " + v + "
";
- }
- });
- return display;
- },
-
- // Returns an array of keys for this object. If `attributes_only`
- // is true will not return keys that map to a `function()`
- keys: function(attributes_only) {
- var keys = [];
- for (var property in this) {
- if (!_isFunction(this[property]) || !attributes_only) {
- keys.push(property);
- }
- }
- return keys;
- },
-
- // Checks if the object has a value at `key` and that the value is not empty
- has: function(key) {
- return this[key] && $.trim(this[key].toString()) !== '';
- },
-
- // convenience method to join as many arguments as you want
- // by the first argument - useful for making paths
- join: function() {
- var args = _makeArray(arguments);
- var delimiter = args.shift();
- return args.join(delimiter);
- },
-
- // Shortcut to Sammy.log
- log: function() {
- Sammy.log.apply(Sammy, arguments);
- },
-
- // Returns a string representation of this object.
- // if `include_functions` is true, it will also toString() the
- // methods of this object. By default only prints the attributes.
- toString: function(include_functions) {
- var s = [];
- $.each(this, function(k, v) {
- if (!_isFunction(v) || include_functions) {
- s.push('"' + k + '": ' + v.toString());
- }
- });
- return "Sammy.Object: {" + s.join(',') + "}";
- }
- });
-
-
- // Return whether the event targets this window.
- Sammy.targetIsThisWindow = function targetIsThisWindow(event) {
- var targetWindow = $(event.target).attr('target');
- if ( !targetWindow || targetWindow === window.name || targetWindow === '_self' ) { return true; }
- if ( targetWindow === '_blank' ) { return false; }
- if ( targetWindow === 'top' && window === window.top ) { return true; }
- return false;
- };
-
-
- // The DefaultLocationProxy is the default location proxy for all Sammy applications.
- // A location proxy is a prototype that conforms to a simple interface. The purpose
- // of a location proxy is to notify the Sammy.Application its bound to when the location
- // or 'external state' changes.
- //
- // The `DefaultLocationProxy` watches for changes to the path of the current window and
- // is also able to set the path based on changes in the application. It does this by
- // using different methods depending on what is available in the current browser. In
- // the latest and greatest browsers it used the HTML5 History API and the `pushState`
- // `popState` events/methods. This allows you to use Sammy to serve a site behind normal
- // URI paths as opposed to the older default of hash (#) based routing. Because the server
- // can interpret the changed path on a refresh or re-entry, though, it requires additional
- // support on the server side. If you'd like to force disable HTML5 history support, please
- // use the `disable_push_state` setting on `Sammy.Application`. If pushState support
- // is enabled, `DefaultLocationProxy` also binds to all links on the page. If a link is clicked
- // that matches the current set of routes, the URL is changed using pushState instead of
- // fully setting the location and the app is notified of the change.
- //
- // If the browser does not have support for HTML5 History, `DefaultLocationProxy` automatically
- // falls back to the older hash based routing. The newest browsers (IE, Safari > 4, FF >= 3.6)
- // support a 'onhashchange' DOM event, thats fired whenever the location.hash changes.
- // In this situation the DefaultLocationProxy just binds to this event and delegates it to
- // the application. In the case of older browsers a poller is set up to track changes to the
- // hash.
- Sammy.DefaultLocationProxy = function(app, run_interval_every) {
- this.app = app;
- // set is native to false and start the poller immediately
- this.is_native = false;
- this.has_history = _has_history;
- this._startPolling(run_interval_every);
- };
-
- Sammy.DefaultLocationProxy.fullPath = function(location_obj) {
- // Bypass the `window.location.hash` attribute. If a question mark
- // appears in the hash IE6 will strip it and all of the following
- // characters from `window.location.hash`.
- var matches = location_obj.toString().match(/^[^#]*(#.+)$/);
- var hash = matches ? matches[1] : '';
- return [location_obj.pathname, location_obj.search, hash].join('');
- };
-$.extend(Sammy.DefaultLocationProxy.prototype , {
- // bind the proxy events to the current app.
- bind: function() {
- var proxy = this, app = this.app, lp = Sammy.DefaultLocationProxy;
- $(window).bind('hashchange.' + this.app.eventNamespace(), function(e, non_native) {
- // if we receive a native hash change event, set the proxy accordingly
- // and stop polling
- if (proxy.is_native === false && !non_native) {
- proxy.is_native = true;
- window.clearInterval(lp._interval);
- lp._interval = null;
- }
- app.trigger('location-changed');
- });
- if (_has_history && !app.disable_push_state) {
- // bind to popstate
- $(window).bind('popstate.' + this.app.eventNamespace(), function(e) {
- app.trigger('location-changed');
- });
- // bind to link clicks that have routes
- $(document).delegate('a', 'click.history-' + this.app.eventNamespace(), function (e) {
- if (e.isDefaultPrevented() || e.metaKey || e.ctrlKey) {
- return;
- }
- var full_path = lp.fullPath(this),
- // Get anchor's host name in a cross browser compatible way.
- // IE looses hostname property when setting href in JS
- // with a relative URL, e.g. a.setAttribute('href',"/whatever").
- // Circumvent this problem by creating a new link with given URL and
- // querying that for a hostname.
- hostname = this.hostname ? this.hostname : function (a) {
- var l = document.createElement("a");
- l.href = a.href;
- return l.hostname;
- }(this);
-
- if (hostname == window.location.hostname &&
- app.lookupRoute('get', full_path) &&
- Sammy.targetIsThisWindow(e)) {
- e.preventDefault();
- proxy.setLocation(full_path);
- return false;
- }
- });
- }
- if (!lp._bindings) {
- lp._bindings = 0;
- }
- lp._bindings++;
- },
-
- // unbind the proxy events from the current app
- unbind: function() {
- $(window).unbind('hashchange.' + this.app.eventNamespace());
- $(window).unbind('popstate.' + this.app.eventNamespace());
- $(document).undelegate('a', 'click.history-' + this.app.eventNamespace());
- Sammy.DefaultLocationProxy._bindings--;
- if (Sammy.DefaultLocationProxy._bindings <= 0) {
- window.clearInterval(Sammy.DefaultLocationProxy._interval);
- Sammy.DefaultLocationProxy._interval = null;
- }
- },
-
- // get the current location from the hash.
- getLocation: function() {
- return Sammy.DefaultLocationProxy.fullPath(window.location);
- },
-
- // set the current location to `new_location`
- setLocation: function(new_location) {
- if (/^([^#\/]|$)/.test(new_location)) { // non-prefixed url
- if (_has_history && !this.app.disable_push_state) {
- new_location = '/' + new_location;
- } else {
- new_location = '#!/' + new_location;
- }
- }
- if (new_location != this.getLocation()) {
- // HTML5 History exists and new_location is a full path
- if (_has_history && !this.app.disable_push_state && /^\//.test(new_location)) {
- history.pushState({ path: new_location }, window.title, new_location);
- this.app.trigger('location-changed');
- } else {
- return (window.location = new_location);
- }
- }
- },
-
- _startPolling: function(every) {
- // set up interval
- var proxy = this;
- if (!Sammy.DefaultLocationProxy._interval) {
- if (!every) { every = 10; }
- var hashCheck = function() {
- var current_location = proxy.getLocation();
- if (typeof Sammy.DefaultLocationProxy._last_location == 'undefined' ||
- current_location != Sammy.DefaultLocationProxy._last_location) {
- window.setTimeout(function() {
- $(window).trigger('hashchange', [true]);
- }, 0);
- }
- Sammy.DefaultLocationProxy._last_location = current_location;
- };
- hashCheck();
- Sammy.DefaultLocationProxy._interval = window.setInterval(hashCheck, every);
- }
- }
- });
-
-
- // Sammy.Application is the Base prototype for defining 'applications'.
- // An 'application' is a collection of 'routes' and bound events that is
- // attached to an element when `run()` is called.
- // The only argument an 'app_function' is evaluated within the context of the application.
- Sammy.Application = function(app_function) {
- var app = this;
- this.routes = {};
- this.listeners = new Sammy.Object({});
- this.arounds = [];
- this.befores = [];
- // generate a unique namespace
- this.namespace = (new Date()).getTime() + '-' + parseInt(Math.random() * 1000, 10);
- this.context_prototype = function() { Sammy.EventContext.apply(this, arguments); };
- this.context_prototype.prototype = new Sammy.EventContext();
-
- if (_isFunction(app_function)) {
- app_function.apply(this, [this]);
- }
- // set the location proxy if not defined to the default (DefaultLocationProxy)
- if (!this._location_proxy) {
- this.setLocationProxy(new Sammy.DefaultLocationProxy(this, this.run_interval_every));
- }
- if (this.debug) {
- this.bindToAllEvents(function(e, data) {
- app.log(app.toString(), e.cleaned_type, data || {});
- });
- }
- };
-
- Sammy.Application.prototype = $.extend({}, Sammy.Object.prototype, {
-
- // the four route verbs
- ROUTE_VERBS: ['get','post','put','delete'],
-
- // An array of the default events triggered by the
- // application during its lifecycle
- APP_EVENTS: ['run', 'unload', 'lookup-route', 'run-route', 'route-found', 'event-context-before', 'event-context-after', 'changed', 'error', 'check-form-submission', 'redirect', 'location-changed'],
-
- _last_route: null,
- _location_proxy: null,
- _running: false,
-
- // Defines what element the application is bound to. Provide a selector
- // (parseable by `jQuery()`) and this will be used by `$element()`
- element_selector: 'body',
-
- // When set to true, logs all of the default events using `log()`
- debug: false,
-
- // When set to true, and the error() handler is not overridden, will actually
- // raise JS errors in routes (500) and when routes can't be found (404)
- raise_errors: false,
-
- // The time in milliseconds that the URL is queried for changes
- run_interval_every: 50,
-
- // if using the `DefaultLocationProxy` setting this to true will force the app to use
- // traditional hash based routing as opposed to the new HTML5 PushState support
- disable_push_state: false,
-
- // The default template engine to use when using `partial()` in an
- // `EventContext`. `template_engine` can either be a string that
- // corresponds to the name of a method/helper on EventContext or it can be a function
- // that takes two arguments, the content of the unrendered partial and an optional
- // JS object that contains interpolation data. Template engine is only called/referred
- // to if the extension of the partial is null or unknown. See `partial()`
- // for more information
- template_engine: null,
-
- // //=> Sammy.Application: body
- toString: function() {
- return 'Sammy.Application:' + this.element_selector;
- },
-
- // returns a jQuery object of the Applications bound element.
- $element: function(selector) {
- return selector ? $(this.element_selector).find(selector) : $(this.element_selector);
- },
-
- // `use()` is the entry point for including Sammy plugins.
- // The first argument to use should be a function() that is evaluated
- // in the context of the current application, just like the `app_function`
- // argument to the `Sammy.Application` constructor.
- //
- // Any additional arguments are passed to the app function sequentially.
- //
- // For much more detail about plugins, check out:
- // [http://sammyjs.org/docs/plugins](http://sammyjs.org/docs/plugins)
- //
- // ### Example
- //
- // var MyPlugin = function(app, prepend) {
- //
- // this.helpers({
- // myhelper: function(text) {
- // alert(prepend + " " + text);
- // }
- // });
- //
- // };
- //
- // var app = $.sammy(function() {
- //
- // this.use(MyPlugin, 'This is my plugin');
- //
- // this.get('#/', function() {
- // this.myhelper('and dont you forget it!');
- // //=> Alerts: This is my plugin and dont you forget it!
- // });
- //
- // });
- //
- // If plugin is passed as a string it assumes your are trying to load
- // Sammy."Plugin". This is the preferred way of loading core Sammy plugins
- // as it allows for better error-messaging.
- //
- // ### Example
- //
- // $.sammy(function() {
- // this.use('Mustache'); //=> Sammy.Mustache
- // this.use('Storage'); //=> Sammy.Storage
- // });
- //
- use: function() {
- // flatten the arguments
- var args = _makeArray(arguments),
- plugin = args.shift(),
- plugin_name = plugin || '';
- try {
- args.unshift(this);
- if (typeof plugin == 'string') {
- plugin_name = 'Sammy.' + plugin;
- plugin = Sammy[plugin];
- }
- plugin.apply(this, args);
- } catch(e) {
- if (typeof plugin === 'undefined') {
- this.error("Plugin Error: called use() but plugin (" + plugin_name.toString() + ") is not defined", e);
- } else if (!_isFunction(plugin)) {
- this.error("Plugin Error: called use() but '" + plugin_name.toString() + "' is not a function", e);
- } else {
- this.error("Plugin Error", e);
- }
- }
- return this;
- },
-
- // Sets the location proxy for the current app. By default this is set to
- // a new `Sammy.DefaultLocationProxy` on initialization. However, you can set
- // the location_proxy inside you're app function to give your app a custom
- // location mechanism. See `Sammy.DefaultLocationProxy` and `Sammy.DataLocationProxy`
- // for examples.
- //
- // `setLocationProxy()` takes an initialized location proxy.
- //
- // ### Example
- //
- // // to bind to data instead of the default hash;
- // var app = $.sammy(function() {
- // this.setLocationProxy(new Sammy.DataLocationProxy(this));
- // });
- //
- setLocationProxy: function(new_proxy) {
- var original_proxy = this._location_proxy;
- this._location_proxy = new_proxy;
- if (this.isRunning()) {
- if (original_proxy) {
- // if there is already a location proxy, unbind it.
- original_proxy.unbind();
- }
- this._location_proxy.bind();
- }
- },
-
- // provide log() override for inside an app that includes the relevant application element_selector
- log: function() {
- Sammy.log.apply(Sammy, Array.prototype.concat.apply([this.element_selector],arguments));
- },
-
-
- // `route()` is the main method for defining routes within an application.
- // For great detail on routes, check out:
- // [http://sammyjs.org/docs/routes](http://sammyjs.org/docs/routes)
- //
- // This method also has aliases for each of the different verbs (eg. `get()`, `post()`, etc.)
- //
- // ### Arguments
- //
- // * `verb` A String in the set of ROUTE_VERBS or 'any'. 'any' will add routes for each
- // of the ROUTE_VERBS. If only two arguments are passed,
- // the first argument is the path, the second is the callback and the verb
- // is assumed to be 'any'.
- // * `path` A Regexp or a String representing the path to match to invoke this verb.
- // * `callback` A Function that is called/evaluated when the route is run see: `runRoute()`.
- // It is also possible to pass a string as the callback, which is looked up as the name
- // of a method on the application.
- //
- route: function(verb, path) {
- var app = this, param_names = [], add_route, path_match, callback = Array.prototype.slice.call(arguments,2);
-
- // if the method signature is just (path, callback)
- // assume the verb is 'any'
- if (callback.length === 0 && _isFunction(path)) {
- path = verb;
- callback = [path];
- verb = 'any';
- }
-
- verb = verb.toLowerCase(); // ensure verb is lower case
-
- // if path is a string turn it into a regex
- if (path.constructor == String) {
-
- // Needs to be explicitly set because IE will maintain the index unless NULL is returned,
- // which means that with two consecutive routes that contain params, the second set of params will not be found and end up in splat instead of params
- // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/RegExp/lastIndex
- PATH_NAME_MATCHER.lastIndex = 0;
-
- // find the names
- while ((path_match = PATH_NAME_MATCHER.exec(path)) !== null) {
- param_names.push(path_match[1]);
- }
- // replace with the path replacement
- path = new RegExp(path.replace(PATH_NAME_MATCHER, PATH_REPLACER) + "$");
- }
- // lookup callbacks
- $.each(callback,function(i,cb){
- if (typeof(cb) === 'string') {
- callback[i] = app[cb];
- }
- });
-
- add_route = function(with_verb) {
- var r = {verb: with_verb, path: path, callback: callback, param_names: param_names};
- // add route to routes array
- app.routes[with_verb] = app.routes[with_verb] || [];
- // place routes in order of definition
- app.routes[with_verb].push(r);
- };
-
- if (verb === 'any') {
- $.each(this.ROUTE_VERBS, function(i, v) { add_route(v); });
- } else {
- add_route(verb);
- }
-
- // return the app
- return this;
- },
-
- // Alias for route('get', ...)
- get: _routeWrapper('get'),
-
- // Alias for route('post', ...)
- post: _routeWrapper('post'),
-
- // Alias for route('put', ...)
- put: _routeWrapper('put'),
-
- // Alias for route('delete', ...)
- del: _routeWrapper('delete'),
-
- // Alias for route('any', ...)
- any: _routeWrapper('any'),
-
- // `mapRoutes` takes an array of arrays, each array being passed to route()
- // as arguments, this allows for mass definition of routes. Another benefit is
- // this makes it possible/easier to load routes via remote JSON.
- //
- // ### Example
- //
- // var app = $.sammy(function() {
- //
- // this.mapRoutes([
- // ['get', '#/', function() { this.log('index'); }],
- // // strings in callbacks are looked up as methods on the app
- // ['post', '#/create', 'addUser'],
- // // No verb assumes 'any' as the verb
- // [/dowhatever/, function() { this.log(this.verb, this.path)}];
- // ]);
- // });
- //
- mapRoutes: function(route_array) {
- var app = this;
- $.each(route_array, function(i, route_args) {
- app.route.apply(app, route_args);
- });
- return this;
- },
-
- // A unique event namespace defined per application.
- // All events bound with `bind()` are automatically bound within this space.
- eventNamespace: function() {
- return ['sammy-app', this.namespace].join('-');
- },
-
- // Works just like `jQuery.fn.bind()` with a couple notable differences.
- //
- // * It binds all events to the application element
- // * All events are bound within the `eventNamespace()`
- // * Events are not actually bound until the application is started with `run()`
- // * callbacks are evaluated within the context of a Sammy.EventContext
- //
- bind: function(name, data, callback) {
- var app = this;
- // build the callback
- // if the arity is 2, callback is the second argument
- if (typeof callback == 'undefined') { callback = data; }
- var listener_callback = function() {
- // pull off the context from the arguments to the callback
- var e, context, data;
- e = arguments[0];
- data = arguments[1];
- if (data && data.context) {
- context = data.context;
- delete data.context;
- } else {
- context = new app.context_prototype(app, 'bind', e.type, data, e.target);
- }
- e.cleaned_type = e.type.replace(app.eventNamespace(), '');
- callback.apply(context, [e, data]);
- };
-
- // it could be that the app element doesnt exist yet
- // so attach to the listeners array and then run()
- // will actually bind the event.
- if (!this.listeners[name]) { this.listeners[name] = []; }
- this.listeners[name].push(listener_callback);
- if (this.isRunning()) {
- // if the app is running
- // *actually* bind the event to the app element
- this._listen(name, listener_callback);
- }
- return this;
- },
-
- // Triggers custom events defined with `bind()`
- //
- // ### Arguments
- //
- // * `name` The name of the event. Automatically prefixed with the `eventNamespace()`
- // * `data` An optional Object that can be passed to the bound callback.
- // * `context` An optional context/Object in which to execute the bound callback.
- // If no context is supplied a the context is a new `Sammy.EventContext`
- //
- trigger: function(name, data) {
- this.$element().trigger([name, this.eventNamespace()].join('.'), [data]);
- return this;
- },
-
- // Reruns the current route
- refresh: function() {
- this.last_location = null;
- this.trigger('location-changed');
- return this;
- },
-
- // Takes a single callback that is pushed on to a stack.
- // Before any route is run, the callbacks are evaluated in order within
- // the current `Sammy.EventContext`
- //
- // If any of the callbacks explicitly return false, execution of any
- // further callbacks and the route itself is halted.
- //
- // You can also provide a set of options that will define when to run this
- // before based on the route it proceeds.
- //
- // ### Example
- //
- // var app = $.sammy(function() {
- //
- // // will run at #/route but not at #/
- // this.before('#/route', function() {
- // //...
- // });
- //
- // // will run at #/ but not at #/route
- // this.before({except: {path: '#/route'}}, function() {
- // this.log('not before #/route');
- // });
- //
- // this.get('#/', function() {});
- //
- // this.get('#/route', function() {});
- //
- // });
- //
- // See `contextMatchesOptions()` for a full list of supported options
- //
- before: function(options, callback) {
- if (_isFunction(options)) {
- callback = options;
- options = {};
- }
- this.befores.push([options, callback]);
- return this;
- },
-
- // A shortcut for binding a callback to be run after a route is executed.
- // After callbacks have no guarunteed order.
- after: function(callback) {
- return this.bind('event-context-after', callback);
- },
-
-
- // Adds an around filter to the application. around filters are functions
- // that take a single argument `callback` which is the entire route
- // execution path wrapped up in a closure. This means you can decide whether
- // or not to proceed with execution by not invoking `callback` or,
- // more usefully wrapping callback inside the result of an asynchronous execution.
- //
- // ### Example
- //
- // The most common use case for around() is calling a _possibly_ async function
- // and executing the route within the functions callback:
- //
- // var app = $.sammy(function() {
- //
- // var current_user = false;
- //
- // function checkLoggedIn(callback) {
- // // /session returns a JSON representation of the logged in user
- // // or an empty object
- // if (!current_user) {
- // $.getJSON('/session', function(json) {
- // if (json.login) {
- // // show the user as logged in
- // current_user = json;
- // // execute the route path
- // callback();
- // } else {
- // // show the user as not logged in
- // current_user = false;
- // // the context of aroundFilters is an EventContext
- // this.redirect('#/login');
- // }
- // });
- // } else {
- // // execute the route path
- // callback();
- // }
- // };
- //
- // this.around(checkLoggedIn);
- //
- // });
- //
- around: function(callback) {
- this.arounds.push(callback);
- return this;
- },
-
- // Adds a onComplete function to the application. onComplete functions are executed
- // at the end of a chain of route callbacks, if they call next(). Unlike after,
- // which is called as soon as the route is complete, onComplete is like a final next()
- // for all routes, and is thus run asynchronously
- //
- // ### Example
- //
- // app.get('/chain',function(context,next) {
- // console.log('chain1');
- // next();
- // },function(context,next) {
- // console.log('chain2');
- // next();
- // });
- //
- // app.get('/link',function(context,next) {
- // console.log('link1');
- // next();
- // },function(context,next) {
- // console.log('link2');
- // next();
- // });
- //
- // app.onComplete(function() {
- // console.log("Running finally");
- // });
- //
- // If you go to '/chain', you will get the following messages:
- //
- // chain1
- // chain2
- // Running onComplete
- //
- //
- // If you go to /link, you will get the following messages:
- //
- // link1
- // link2
- // Running onComplete
- //
- //
- // It really comes to play when doing asynchronous:
- //
- // app.get('/chain',function(context,next) {
- // $.get('/my/url',function() {
- // console.log('chain1');
- // next();
- // });
- // },function(context,next) {
- // console.log('chain2');
- // next();
- // });
- //
- onComplete: function(callback) {
- this._onComplete = callback;
- return this;
- },
-
- // Returns `true` if the current application is running.
- isRunning: function() {
- return this._running;
- },
-
- // Helpers extends the EventContext prototype specific to this app.
- // This allows you to define app specific helper functions that can be used
- // whenever you're inside of an event context (templates, routes, bind).
- //
- // ### Example
- //
- // var app = $.sammy(function() {
- //
- // helpers({
- // upcase: function(text) {
- // return text.toString().toUpperCase();
- // }
- // });
- //
- // get('#/', function() { with(this) {
- // // inside of this context I can use the helpers
- // $('#main').html(upcase($('#main').text());
- // }});
- //
- // });
- //
- //
- // ### Arguments
- //
- // * `extensions` An object collection of functions to extend the context.
- //
- helpers: function(extensions) {
- $.extend(this.context_prototype.prototype, extensions);
- return this;
- },
-
- // Helper extends the event context just like `helpers()` but does it
- // a single method at a time. This is especially useful for dynamically named
- // helpers
- //
- // ### Example
- //
- // // Trivial example that adds 3 helper methods to the context dynamically
- // var app = $.sammy(function(app) {
- //
- // $.each([1,2,3], function(i, num) {
- // app.helper('helper' + num, function() {
- // this.log("I'm helper number " + num);
- // });
- // });
- //
- // this.get('#/', function() {
- // this.helper2(); //=> I'm helper number 2
- // });
- // });
- //
- // ### Arguments
- //
- // * `name` The name of the method
- // * `method` The function to be added to the prototype at `name`
- //
- helper: function(name, method) {
- this.context_prototype.prototype[name] = method;
- return this;
- },
-
- // Actually starts the application's lifecycle. `run()` should be invoked
- // within a document.ready block to ensure the DOM exists before binding events, etc.
- //
- // ### Example
- //
- // var app = $.sammy(function() { ... }); // your application
- // $(function() { // document.ready
- // app.run();
- // });
- //
- // ### Arguments
- //
- // * `start_url` Optionally, a String can be passed which the App will redirect to
- // after the events/routes have been bound.
- run: function(start_url) {
- if (this.isRunning()) { return false; }
- var app = this;
-
- // actually bind all the listeners
- $.each(this.listeners.toHash(), function(name, callbacks) {
- $.each(callbacks, function(i, listener_callback) {
- app._listen(name, listener_callback);
- });
- });
-
- this.trigger('run', {start_url: start_url});
- this._running = true;
- // set last location
- this.last_location = null;
- if (!(/\#(.+)/.test(this.getLocation())) && typeof start_url != 'undefined') {
- this.setLocation(start_url);
- }
- // check url
- this._checkLocation();
- this._location_proxy.bind();
- this.bind('location-changed', function() {
- app._checkLocation();
- });
-
- // bind to submit to capture post/put/delete routes
- this.bind('submit', function(e) {
- if ( !Sammy.targetIsThisWindow(e) ) { return true; }
- var returned = app._checkFormSubmission($(e.target).closest('form'));
- return (returned === false) ? e.preventDefault() : false;
- });
-
- // bind unload to body unload
- $(window).bind('unload', function() {
- app.unload();
- });
-
- // trigger html changed
- return this.trigger('changed');
- },
-
- // The opposite of `run()`, un-binds all event listeners and intervals
- // `run()` Automatically binds a `onunload` event to run this when
- // the document is closed.
- unload: function() {
- if (!this.isRunning()) { return false; }
- var app = this;
- this.trigger('unload');
- // clear interval
- this._location_proxy.unbind();
- // unbind form submits
- this.$element().unbind('submit').removeClass(app.eventNamespace());
- // unbind all events
- $.each(this.listeners.toHash() , function(name, listeners) {
- $.each(listeners, function(i, listener_callback) {
- app._unlisten(name, listener_callback);
- });
- });
- this._running = false;
- return this;
- },
-
- // Not only runs `unbind` but also destroys the app reference.
- destroy: function() {
- this.unload();
- delete Sammy.apps[this.element_selector];
- return this;
- },
-
- // Will bind a single callback function to every event that is already
- // being listened to in the app. This includes all the `APP_EVENTS`
- // as well as any custom events defined with `bind()`.
- //
- // Used internally for debug logging.
- bindToAllEvents: function(callback) {
- var app = this;
- // bind to the APP_EVENTS first
- $.each(this.APP_EVENTS, function(i, e) {
- app.bind(e, callback);
- });
- // next, bind to listener names (only if they dont exist in APP_EVENTS)
- $.each(this.listeners.keys(true), function(i, name) {
- if ($.inArray(name, app.APP_EVENTS) == -1) {
- app.bind(name, callback);
- }
- });
- return this;
- },
-
- // Returns a copy of the given path with any query string after the hash
- // removed.
- routablePath: function(path) {
- return path.replace(QUERY_STRING_MATCHER, '');
- },
-
- // Given a verb and a String path, will return either a route object or false
- // if a matching route can be found within the current defined set.
- lookupRoute: function(verb, path) {
- var app = this, routed = false, i = 0, l, route;
- if (typeof this.routes[verb] != 'undefined') {
- l = this.routes[verb].length;
- for (; i < l; i++) {
- route = this.routes[verb][i];
- if (app.routablePath(path).match(route.path)) {
- routed = route;
- break;
- }
- }
- }
- return routed;
- },
-
- // First, invokes `lookupRoute()` and if a route is found, parses the
- // possible URL params and then invokes the route's callback within a new
- // `Sammy.EventContext`. If the route can not be found, it calls
- // `notFound()`. If `raise_errors` is set to `true` and
- // the `error()` has not been overridden, it will throw an actual JS
- // error.
- //
- // You probably will never have to call this directly.
- //
- // ### Arguments
- //
- // * `verb` A String for the verb.
- // * `path` A String path to lookup.
- // * `params` An Object of Params pulled from the URI or passed directly.
- //
- // ### Returns
- //
- // Either returns the value returned by the route callback or raises a 404 Not Found error.
- //
- runRoute: function(verb, path, params, target) {
- var app = this,
- route = this.lookupRoute(verb, path),
- context,
- wrapped_route,
- arounds,
- around,
- befores,
- before,
- callback_args,
- path_params,
- final_returned;
-
- if (this.debug) {
- this.log('runRoute', [verb, path].join(' '));
- }
-
- this.trigger('run-route', {verb: verb, path: path, params: params});
- if (typeof params == 'undefined') { params = {}; }
-
- $.extend(params, this._parseQueryString(path));
-
- if (route) {
- this.trigger('route-found', {route: route});
- // pull out the params from the path
- if ((path_params = route.path.exec(this.routablePath(path))) !== null) {
- // first match is the full path
- path_params.shift();
- // for each of the matches
- $.each(path_params, function(i, param) {
- // if theres a matching param name
- if (route.param_names[i]) {
- // set the name to the match
- params[route.param_names[i]] = _decode(param);
- } else {
- // initialize 'splat'
- if (!params.splat) { params.splat = []; }
- params.splat.push(_decode(param));
- }
- });
- }
-
- // set event context
- context = new this.context_prototype(this, verb, path, params, target);
- // ensure arrays
- arounds = this.arounds.slice(0);
- befores = this.befores.slice(0);
- // set the callback args to the context + contents of the splat
- callback_args = [context];
- if (params.splat) {
- callback_args = callback_args.concat(params.splat);
- }
- // wrap the route up with the before filters
- wrapped_route = function() {
- var returned, i, nextRoute;
- while (befores.length > 0) {
- before = befores.shift();
- // check the options
- if (app.contextMatchesOptions(context, before[0])) {
- returned = before[1].apply(context, [context]);
- if (returned === false) { return false; }
- }
- }
- app.last_route = route;
- context.trigger('event-context-before', {context: context});
- // run multiple callbacks
- if (typeof(route.callback) === "function") {
- route.callback = [route.callback];
- }
- if (route.callback && route.callback.length) {
- i = -1;
- nextRoute = function() {
- i++;
- if (route.callback[i]) {
- returned = route.callback[i].apply(context,callback_args);
- } else if (app._onComplete && typeof(app._onComplete === "function")) {
- app._onComplete(context);
- }
- };
- callback_args.push(nextRoute);
- nextRoute();
- }
- context.trigger('event-context-after', {context: context});
- return returned;
- };
- $.each(arounds.reverse(), function(i, around) {
- var last_wrapped_route = wrapped_route;
- wrapped_route = function() { return around.apply(context, [last_wrapped_route]); };
- });
- try {
- final_returned = wrapped_route();
- } catch(e) {
- this.error(['500 Error', verb, path].join(' '), e);
- }
- return final_returned;
- } else {
- return this.notFound(verb, path);
- }
- },
-
- // Matches an object of options against an `EventContext` like object that
- // contains `path` and `verb` attributes. Internally Sammy uses this
- // for matching `before()` filters against specific options. You can set the
- // object to _only_ match certain paths or verbs, or match all paths or verbs _except_
- // those that match the options.
- //
- // ### Example
- //
- // var app = $.sammy(),
- // context = {verb: 'get', path: '#/mypath'};
- //
- // // match against a path string
- // app.contextMatchesOptions(context, '#/mypath'); //=> true
- // app.contextMatchesOptions(context, '#/otherpath'); //=> false
- // // equivalent to
- // app.contextMatchesOptions(context, {only: {path:'#/mypath'}}); //=> true
- // app.contextMatchesOptions(context, {only: {path:'#/otherpath'}}); //=> false
- // // match against a path regexp
- // app.contextMatchesOptions(context, /path/); //=> true
- // app.contextMatchesOptions(context, /^path/); //=> false
- // // match only a verb
- // app.contextMatchesOptions(context, {only: {verb:'get'}}); //=> true
- // app.contextMatchesOptions(context, {only: {verb:'post'}}); //=> false
- // // match all except a verb
- // app.contextMatchesOptions(context, {except: {verb:'post'}}); //=> true
- // app.contextMatchesOptions(context, {except: {verb:'get'}}); //=> false
- // // match all except a path
- // app.contextMatchesOptions(context, {except: {path:'#/otherpath'}}); //=> true
- // app.contextMatchesOptions(context, {except: {path:'#/mypath'}}); //=> false
- // // match multiple paths
- // app.contextMatchesOptions(context, {path: ['#/mypath', '#/otherpath']}); //=> true
- // app.contextMatchesOptions(context, {path: ['#/otherpath', '#/thirdpath']}); //=> false
- // // equivalent to
- // app.contextMatchesOptions(context, {only: {path: ['#/mypath', '#/otherpath']}}); //=> true
- // app.contextMatchesOptions(context, {only: {path: ['#/otherpath', '#/thirdpath']}}); //=> false
- // // match all except multiple paths
- // app.contextMatchesOptions(context, {except: {path: ['#/mypath', '#/otherpath']}}); //=> false
- // app.contextMatchesOptions(context, {except: {path: ['#/otherpath', '#/thirdpath']}}); //=> true
- //
- contextMatchesOptions: function(context, match_options, positive) {
- var options = match_options;
- // normalize options
- if (typeof options === 'string' || _isRegExp(options)) {
- options = {path: options};
- }
- if (typeof positive === 'undefined') {
- positive = true;
- }
- // empty options always match
- if ($.isEmptyObject(options)) {
- return true;
- }
- // Do we have to match against multiple paths?
- if (_isArray(options.path)){
- var results, numopt, opts, len;
- results = [];
- for (numopt = 0, len = options.path.length; numopt < len; numopt += 1) {
- opts = $.extend({}, options, {path: options.path[numopt]});
- results.push(this.contextMatchesOptions(context, opts));
- }
- var matched = $.inArray(true, results) > -1 ? true : false;
- return positive ? matched : !matched;
- }
- if (options.only) {
- return this.contextMatchesOptions(context, options.only, true);
- } else if (options.except) {
- return this.contextMatchesOptions(context, options.except, false);
- }
- var path_matched = true, verb_matched = true;
- if (options.path) {
- if (!_isRegExp(options.path)) {
- options.path = new RegExp(options.path.toString() + '$');
- }
- path_matched = options.path.test(context.path);
- }
- if (options.verb) {
- if(typeof options.verb === 'string') {
- verb_matched = options.verb === context.verb;
- } else {
- verb_matched = options.verb.indexOf(context.verb) > -1;
- }
- }
- return positive ? (verb_matched && path_matched) : !(verb_matched && path_matched);
- },
-
-
- // Delegates to the `location_proxy` to get the current location.
- // See `Sammy.DefaultLocationProxy` for more info on location proxies.
- getLocation: function() {
- return this._location_proxy.getLocation();
- },
-
- // Delegates to the `location_proxy` to set the current location.
- // See `Sammy.DefaultLocationProxy` for more info on location proxies.
- //
- // ### Arguments
- //
- // * `new_location` A new location string (e.g. '#/')
- //
- setLocation: function(new_location) {
- return this._location_proxy.setLocation(new_location);
- },
-
- // Swaps the content of `$element()` with `content`
- // You can override this method to provide an alternate swap behavior
- // for `EventContext.partial()`.
- //
- // ### Example
- //
- // var app = $.sammy(function() {
- //
- // // implements a 'fade out'/'fade in'
- // this.swap = function(content, callback) {
- // var context = this;
- // context.$element().fadeOut('slow', function() {
- // context.$element().html(content);
- // context.$element().fadeIn('slow', function() {
- // if (callback) {
- // callback.apply();
- // }
- // });
- // });
- // };
- //
- // });
- //
- swap: function(content, callback) {
- var $el = this.$element().html(content);
- if (_isFunction(callback)) { callback(content); }
- return $el;
- },
-
- // a simple global cache for templates. Uses the same semantics as
- // `Sammy.Cache` and `Sammy.Storage` so can easily be replaced with
- // a persistent storage that lasts beyond the current request.
- templateCache: function(key, value) {
- if (typeof value != 'undefined') {
- return _template_cache[key] = value;
- } else {
- return _template_cache[key];
- }
- },
-
- // clear the templateCache
- clearTemplateCache: function() {
- return (_template_cache = {});
- },
-
- // This throws a '404 Not Found' error by invoking `error()`.
- // Override this method or `error()` to provide custom
- // 404 behavior (i.e redirecting to / or showing a warning)
- notFound: function(verb, path) {
- var ret = this.error(['404 Not Found', verb, path].join(' '));
- return (verb === 'get') ? ret : true;
- },
-
- // The base error handler takes a string `message` and an `Error`
- // object. If `raise_errors` is set to `true` on the app level,
- // this will re-throw the error to the browser. Otherwise it will send the error
- // to `log()`. Override this method to provide custom error handling
- // e.g logging to a server side component or displaying some feedback to the
- // user.
- error: function(message, original_error) {
- if (!original_error) { original_error = new Error(); }
- original_error.message = [message, original_error.message].join(' ');
- this.trigger('error', {message: original_error.message, error: original_error});
- if (this.raise_errors) {
- throw(original_error);
- } else {
- this.log(original_error.message, original_error);
- }
- },
-
- _checkLocation: function() {
- var location, returned;
- // get current location
- location = this.getLocation();
- // compare to see if hash has changed
- if (!this.last_location || this.last_location[0] != 'get' || this.last_location[1] != location) {
- // reset last location
- this.last_location = ['get', location];
- // lookup route for current hash
- returned = this.runRoute('get', location);
- }
- return returned;
- },
-
- _getFormVerb: function(form) {
- var $form = $(form), verb, $_method;
- $_method = $form.find('input[name="_method"]');
- if ($_method.length > 0) { verb = $_method.val(); }
- if (!verb) { verb = $form[0].getAttribute('method'); }
- if (!verb || verb === '') { verb = 'get'; }
- return $.trim(verb.toString().toLowerCase());
- },
-
- _checkFormSubmission: function(form) {
- var $form, path, verb, params, returned;
- this.trigger('check-form-submission', {form: form});
- $form = $(form);
- path = $form.attr('action') || '';
- verb = this._getFormVerb($form);
-
- if (this.debug) {
- this.log('_checkFormSubmission', $form, path, verb);
- }
-
- if (verb === 'get') {
- params = this._serializeFormParams($form);
- if (params !== '') { path += '?' + params; }
- this.setLocation(path);
- returned = false;
- } else {
- params = $.extend({}, this._parseFormParams($form));
- returned = this.runRoute(verb, path, params, form.get(0));
- }
- return (typeof returned == 'undefined') ? false : returned;
- },
-
- _serializeFormParams: function($form) {
- var queryString = "",
- fields = $form.serializeArray(),
- i;
- if (fields.length > 0) {
- queryString = this._encodeFormPair(fields[0].name, fields[0].value);
- for (i = 1; i < fields.length; i++) {
- queryString = queryString + "&" + this._encodeFormPair(fields[i].name, fields[i].value);
- }
- }
- return queryString;
- },
-
- _encodeFormPair: function(name, value){
- return _encode(name) + "=" + _encode(value);
- },
-
- _parseFormParams: function($form) {
- var params = {},
- form_fields = $form.serializeArray(),
- i;
- for (i = 0; i < form_fields.length; i++) {
- params = this._parseParamPair(params, form_fields[i].name, form_fields[i].value);
- }
- return params;
- },
-
- _parseQueryString: function(path) {
- var params = {}, parts, pairs, pair, i;
-
- parts = path.match(QUERY_STRING_MATCHER);
- if (parts && parts[1]) {
- pairs = parts[1].split('&');
- for (i = 0; i < pairs.length; i++) {
- pair = pairs[i].split('=');
- params = this._parseParamPair(params, _decode(pair[0]), _decode(pair[1] || ""));
- }
- }
- return params;
- },
-
- _parseParamPair: function(params, key, value) {
- if (typeof params[key] !== 'undefined') {
- if (_isArray(params[key])) {
- params[key].push(value);
- } else {
- params[key] = [params[key], value];
- }
- } else {
- params[key] = value;
- }
- return params;
- },
-
- _listen: function(name, callback) {
- return this.$element().bind([name, this.eventNamespace()].join('.'), callback);
- },
-
- _unlisten: function(name, callback) {
- return this.$element().unbind([name, this.eventNamespace()].join('.'), callback);
- }
-
- });
-
- // `Sammy.RenderContext` is an object that makes sequential template loading,
- // rendering and interpolation seamless even when dealing with asynchronous
- // operations.
- //
- // `RenderContext` objects are not usually created directly, rather they are
- // instantiated from an `Sammy.EventContext` by using `render()`, `load()` or
- // `partial()` which all return `RenderContext` objects.
- //
- // `RenderContext` methods always returns a modified `RenderContext`
- // for chaining (like jQuery itself).
- //
- // The core magic is in the `then()` method which puts the callback passed as
- // an argument into a queue to be executed once the previous callback is complete.
- // All the methods of `RenderContext` are wrapped in `then()` which allows you
- // to queue up methods by chaining, but maintaining a guaranteed execution order
- // even with remote calls to fetch templates.
- //
- Sammy.RenderContext = function(event_context) {
- this.event_context = event_context;
- this.callbacks = [];
- this.previous_content = null;
- this.content = null;
- this.next_engine = false;
- this.waiting = false;
- };
-
- Sammy.RenderContext.prototype = $.extend({}, Sammy.Object.prototype, {
-
- // The "core" of the `RenderContext` object, adds the `callback` to the
- // queue. If the context is `waiting` (meaning an async operation is happening)
- // then the callback will be executed in order, once the other operations are
- // complete. If there is no currently executing operation, the `callback`
- // is executed immediately.
- //
- // The value returned from the callback is stored in `content` for the
- // subsequent operation. If you return `false`, the queue will pause, and
- // the next callback in the queue will not be executed until `next()` is
- // called. This allows for the guaranteed order of execution while working
- // with async operations.
- //
- // If then() is passed a string instead of a function, the string is looked
- // up as a helper method on the event context.
- //
- // ### Example
- //
- // this.get('#/', function() {
- // // initialize the RenderContext
- // // Even though `load()` executes async, the next `then()`
- // // wont execute until the load finishes
- // this.load('myfile.txt')
- // .then(function(content) {
- // // the first argument to then is the content of the
- // // prev operation
- // $('#main').html(content);
- // });
- // });
- //
- then: function(callback) {
- if (!_isFunction(callback)) {
- // if a string is passed to then, assume we want to call
- // a helper on the event context in its context
- if (typeof callback === 'string' && callback in this.event_context) {
- var helper = this.event_context[callback];
- callback = function(content) {
- return helper.apply(this.event_context, [content]);
- };
- } else {
- return this;
- }
- }
- var context = this;
- if (this.waiting) {
- this.callbacks.push(callback);
- } else {
- this.wait();
- window.setTimeout(function() {
- var returned = callback.apply(context, [context.content, context.previous_content]);
- if (returned !== false) {
- context.next(returned);
- }
- }, 0);
- }
- return this;
- },
-
- // Pause the `RenderContext` queue. Combined with `next()` allows for async
- // operations.
- //
- // ### Example
- //
- // this.get('#/', function() {
- // this.load('mytext.json')
- // .then(function(content) {
- // var context = this,
- // data = JSON.parse(content);
- // // pause execution
- // context.wait();
- // // post to a url
- // $.post(data.url, {}, function(response) {
- // context.next(JSON.parse(response));
- // });
- // })
- // .then(function(data) {
- // // data is json from the previous post
- // $('#message').text(data.status);
- // });
- // });
- wait: function() {
- this.waiting = true;
- },
-
- // Resume the queue, setting `content` to be used in the next operation.
- // See `wait()` for an example.
- next: function(content) {
- this.waiting = false;
- if (typeof content !== 'undefined') {
- this.previous_content = this.content;
- this.content = content;
- }
- if (this.callbacks.length > 0) {
- this.then(this.callbacks.shift());
- }
- },
-
- // Load a template into the context.
- // The `location` can either be a string specifying the remote path to the
- // file, a jQuery object, or a DOM element.
- //
- // No interpolation happens by default, the content is stored in
- // `content`.
- //
- // In the case of a path, unless the option `{cache: false}` is passed the
- // data is stored in the app's `templateCache()`.
- //
- // If a jQuery or DOM object is passed the `innerHTML` of the node is pulled in.
- // This is useful for nesting templates as part of the initial page load wrapped
- // in invisible elements or `