").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);
diff --git a/examples/jquery-migrate-1.2.1.min.js b/examples/jquery-migrate-1.2.1.min.js
new file mode 100644
index 0000000..62149c2
--- /dev/null
+++ b/examples/jquery-migrate-1.2.1.min.js
@@ -0,0 +1,2 @@
+/*! jQuery Migrate v1.2.1 | (c) 2005, 2013 jQuery Foundation, Inc. and other contributors | jquery.org/license */
+jQuery.migrateMute===void 0&&(jQuery.migrateMute=!0),function(e,t,n){function r(n){var r=t.console;i[n]||(i[n]=!0,e.migrateWarnings.push(n),r&&r.warn&&!e.migrateMute&&(r.warn("JQMIGRATE: "+n),e.migrateTrace&&r.trace&&r.trace()))}function a(t,a,i,o){if(Object.defineProperty)try{return Object.defineProperty(t,a,{configurable:!0,enumerable:!0,get:function(){return r(o),i},set:function(e){r(o),i=e}}),n}catch(s){}e._definePropertyBroken=!0,t[a]=i}var i={};e.migrateWarnings=[],!e.migrateMute&&t.console&&t.console.log&&t.console.log("JQMIGRATE: Logging is active"),e.migrateTrace===n&&(e.migrateTrace=!0),e.migrateReset=function(){i={},e.migrateWarnings.length=0},"BackCompat"===document.compatMode&&r("jQuery is not compatible with Quirks Mode");var o=e("
",{size:1}).attr("size")&&e.attrFn,s=e.attr,u=e.attrHooks.value&&e.attrHooks.value.get||function(){return null},c=e.attrHooks.value&&e.attrHooks.value.set||function(){return n},l=/^(?:input|button)$/i,d=/^[238]$/,p=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,f=/^(?:checked|selected)$/i;a(e,"attrFn",o||{},"jQuery.attrFn is deprecated"),e.attr=function(t,a,i,u){var c=a.toLowerCase(),g=t&&t.nodeType;return u&&(4>s.length&&r("jQuery.fn.attr( props, pass ) is deprecated"),t&&!d.test(g)&&(o?a in o:e.isFunction(e.fn[a])))?e(t)[a](i):("type"===a&&i!==n&&l.test(t.nodeName)&&t.parentNode&&r("Can't change the 'type' of an input or button in IE 6/7/8"),!e.attrHooks[c]&&p.test(c)&&(e.attrHooks[c]={get:function(t,r){var a,i=e.prop(t,r);return i===!0||"boolean"!=typeof i&&(a=t.getAttributeNode(r))&&a.nodeValue!==!1?r.toLowerCase():n},set:function(t,n,r){var a;return n===!1?e.removeAttr(t,r):(a=e.propFix[r]||r,a in t&&(t[a]=!0),t.setAttribute(r,r.toLowerCase())),r}},f.test(c)&&r("jQuery.fn.attr('"+c+"') may use property instead of attribute")),s.call(e,t,a,i))},e.attrHooks.value={get:function(e,t){var n=(e.nodeName||"").toLowerCase();return"button"===n?u.apply(this,arguments):("input"!==n&&"option"!==n&&r("jQuery.fn.attr('value') no longer gets properties"),t in e?e.value:null)},set:function(e,t){var a=(e.nodeName||"").toLowerCase();return"button"===a?c.apply(this,arguments):("input"!==a&&"option"!==a&&r("jQuery.fn.attr('value', val) no longer sets properties"),e.value=t,n)}};var g,h,v=e.fn.init,m=e.parseJSON,y=/^([^<]*)(<[\w\W]+>)([^>]*)$/;e.fn.init=function(t,n,a){var i;return t&&"string"==typeof t&&!e.isPlainObject(n)&&(i=y.exec(e.trim(t)))&&i[0]&&("<"!==t.charAt(0)&&r("$(html) HTML strings must start with '<' character"),i[3]&&r("$(html) HTML text after last tag is ignored"),"#"===i[0].charAt(0)&&(r("HTML string cannot start with a '#' character"),e.error("JQMIGRATE: Invalid selector string (XSS)")),n&&n.context&&(n=n.context),e.parseHTML)?v.call(this,e.parseHTML(i[2],n,!0),n,a):v.apply(this,arguments)},e.fn.init.prototype=e.fn,e.parseJSON=function(e){return e||null===e?m.apply(this,arguments):(r("jQuery.parseJSON requires a valid JSON string"),null)},e.uaMatch=function(e){e=e.toLowerCase();var t=/(chrome)[ \/]([\w.]+)/.exec(e)||/(webkit)[ \/]([\w.]+)/.exec(e)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(e)||/(msie) ([\w.]+)/.exec(e)||0>e.indexOf("compatible")&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(e)||[];return{browser:t[1]||"",version:t[2]||"0"}},e.browser||(g=e.uaMatch(navigator.userAgent),h={},g.browser&&(h[g.browser]=!0,h.version=g.version),h.chrome?h.webkit=!0:h.webkit&&(h.safari=!0),e.browser=h),a(e,"browser",e.browser,"jQuery.browser is deprecated"),e.sub=function(){function t(e,n){return new t.fn.init(e,n)}e.extend(!0,t,this),t.superclass=this,t.fn=t.prototype=this(),t.fn.constructor=t,t.sub=this.sub,t.fn.init=function(r,a){return a&&a instanceof e&&!(a instanceof t)&&(a=t(a)),e.fn.init.call(this,r,a,n)},t.fn.init.prototype=t.fn;var n=t(document);return r("jQuery.sub() is deprecated"),t},e.ajaxSetup({converters:{"text json":e.parseJSON}});var b=e.fn.data;e.fn.data=function(t){var a,i,o=this[0];return!o||"events"!==t||1!==arguments.length||(a=e.data(o,t),i=e._data(o,t),a!==n&&a!==i||i===n)?b.apply(this,arguments):(r("Use of jQuery.fn.data('events') is deprecated"),i)};var j=/\/(java|ecma)script/i,w=e.fn.andSelf||e.fn.addBack;e.fn.andSelf=function(){return r("jQuery.fn.andSelf() replaced by jQuery.fn.addBack()"),w.apply(this,arguments)},e.clean||(e.clean=function(t,a,i,o){a=a||document,a=!a.nodeType&&a[0]||a,a=a.ownerDocument||a,r("jQuery.clean() is deprecated");var s,u,c,l,d=[];if(e.merge(d,e.buildFragment(t,a).childNodes),i)for(c=function(e){return!e.type||j.test(e.type)?o?o.push(e.parentNode?e.parentNode.removeChild(e):e):i.appendChild(e):n},s=0;null!=(u=d[s]);s++)e.nodeName(u,"script")&&c(u)||(i.appendChild(u),u.getElementsByTagName!==n&&(l=e.grep(e.merge([],u.getElementsByTagName("script")),c),d.splice.apply(d,[s+1,0].concat(l)),s+=l.length));return d});var Q=e.event.add,x=e.event.remove,k=e.event.trigger,N=e.fn.toggle,T=e.fn.live,M=e.fn.die,S="ajaxStart|ajaxStop|ajaxSend|ajaxComplete|ajaxError|ajaxSuccess",C=RegExp("\\b(?:"+S+")\\b"),H=/(?:^|\s)hover(\.\S+|)\b/,A=function(t){return"string"!=typeof t||e.event.special.hover?t:(H.test(t)&&r("'hover' pseudo-event is deprecated, use 'mouseenter mouseleave'"),t&&t.replace(H,"mouseenter$1 mouseleave$1"))};e.event.props&&"attrChange"!==e.event.props[0]&&e.event.props.unshift("attrChange","attrName","relatedNode","srcElement"),e.event.dispatch&&a(e.event,"handle",e.event.dispatch,"jQuery.event.handle is undocumented and deprecated"),e.event.add=function(e,t,n,a,i){e!==document&&C.test(t)&&r("AJAX events should be attached to document: "+t),Q.call(this,e,A(t||""),n,a,i)},e.event.remove=function(e,t,n,r,a){x.call(this,e,A(t)||"",n,r,a)},e.fn.error=function(){var e=Array.prototype.slice.call(arguments,0);return r("jQuery.fn.error() is deprecated"),e.splice(0,0,"error"),arguments.length?this.bind.apply(this,e):(this.triggerHandler.apply(this,e),this)},e.fn.toggle=function(t,n){if(!e.isFunction(t)||!e.isFunction(n))return N.apply(this,arguments);r("jQuery.fn.toggle(handler, handler...) is deprecated");var a=arguments,i=t.guid||e.guid++,o=0,s=function(n){var r=(e._data(this,"lastToggle"+t.guid)||0)%o;return e._data(this,"lastToggle"+t.guid,r+1),n.preventDefault(),a[r].apply(this,arguments)||!1};for(s.guid=i;a.length>o;)a[o++].guid=i;return this.click(s)},e.fn.live=function(t,n,a){return r("jQuery.fn.live() is deprecated"),T?T.apply(this,arguments):(e(this.context).on(t,this.selector,n,a),this)},e.fn.die=function(t,n){return r("jQuery.fn.die() is deprecated"),M?M.apply(this,arguments):(e(this.context).off(t,this.selector||"**",n),this)},e.event.trigger=function(e,t,n,a){return n||C.test(e)||r("Global events are undocumented and deprecated"),k.call(this,e,t,n||document,a)},e.each(S.split("|"),function(t,n){e.event.special[n]={setup:function(){var t=this;return t!==document&&(e.event.add(document,n+"."+e.guid,function(){e.event.trigger(n,null,t,!0)}),e._data(this,n,e.guid++)),!1},teardown:function(){return this!==document&&e.event.remove(document,n+"."+e._data(this,n)),!1}}})}(jQuery,window);
\ No newline at end of file
diff --git a/examples/maze_growth.html b/examples/maze_growth.html
new file mode 100644
index 0000000..77ccee9
--- /dev/null
+++ b/examples/maze_growth.html
@@ -0,0 +1,261 @@
+
+
+
+
+
Lores: Maze Generator - Ondrovo.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Click to set maze start point(s). Press SPACE to run. Adjust speed with number keys.
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/serpienski.html b/examples/serpienski.html
new file mode 100644
index 0000000..512499c
--- /dev/null
+++ b/examples/serpienski.html
@@ -0,0 +1,116 @@
+
+
+
+
+
Lores: Maze Generator - Ondrovo.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/sprites.html b/examples/sprites.html
new file mode 100644
index 0000000..7af6b6e
--- /dev/null
+++ b/examples/sprites.html
@@ -0,0 +1,155 @@
+
+
+
+
+
Lores Test
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/tiles.html b/examples/tiles.html
new file mode 100644
index 0000000..f99c7f7
--- /dev/null
+++ b/examples/tiles.html
@@ -0,0 +1,128 @@
+
+
+
+
+
+
Lores: Tiles - Ondrovo.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/wall.xpm b/examples/wall.xpm
new file mode 100644
index 0000000..dbfac9c
--- /dev/null
+++ b/examples/wall.xpm
@@ -0,0 +1,28 @@
+/* XPM */
+static char * wall_xpm[] = {
+"16 16 9 1",
+" c None",
+". c #60625F",
+"+ c #757774",
+"@ c #7D7E7B",
+"# c #848583",
+"$ c #898B88",
+"% c #A5A09F",
+"& c #AEA9A8",
+"* c #BDB8B6",
+"%%%%%&&&****&&&.",
+"%+@@@@@##$$$$$#.",
+"%+++@@@@##$$##$.",
+"&++@@@@@@#$$$#$.",
+"&@+@+@@@#######.",
+"&@#@@++@@@#####.",
+"##@@+@@@#@###.",
+".++.............",
+"***&%%%.%&%%%&&&",
+"$##++++.%@####@#",
+"#@@++++.%@####@#",
+"@@@@@++.%@#$#@@#",
+"@@@@@@+.%@#$#@@@",
+"@#@@@@+.%####@+#",
+"@@@@@##.%@###@+@",
+"......+........."};
diff --git a/js/lores-animator.js b/js/lores-animator.js
deleted file mode 100644
index 18328ad..0000000
--- a/js/lores-animator.js
+++ /dev/null
@@ -1,119 +0,0 @@
-// Lores.js - animator module
-// (c) Ondrej Hruska 2013
-// www.ondrovo.com
-
-// MIT license
-
-
-/* Animation manager */
-function LoresAnimator(opts) {
- this.initAnim = opts.init || function(){};
- this.animFrame = opts.frame || function(){};
- this.fps = opts.fps || 40;
-
- this.started = false;
- this.paused = false;
- this.halted = false;
-};
-
-
-LoresAnimator.prototype.start = function() {
- this.initAnim();
-
- this.started = true;
- this.paused = false;
- this.halted = false;
-
- this._requestFrame();
-};
-
-
-LoresAnimator.prototype.stop = function() {
- var self = this;
-
- this.started = false;
- this.halted = true;
-};
-
-
-LoresAnimator.prototype.isPaused = function() {
- return this.started && this.paused && !this.halted;
-};
-
-
-LoresAnimator.prototype.isRunning = function() {
- return this.started && !this.paused && !this.halted;
-};
-
-
-LoresAnimator.prototype.toggle = function() {
- var self = this;
-
- if(!this.started || this.halted) {
- throw "Invalid state for toggle()";
- }
-
- if(this.paused) {
- this.resume();
- } else {
- this.pause();
- }
-};
-
-
-LoresAnimator.prototype.pause = function() {
- var self = this;
-
- if(!this.started || this.halted) {
- throw "Invalid state for pause()";
- }
-
- this.paused = true;
- this.halted = false;
-};
-
-
-LoresAnimator.prototype.setFps = function(fps) {
-
- this.fps = fps;
-};
-
-
-LoresAnimator.prototype.resume = function() {
- var self = this;
-
- if(!this.started || !this.paused || this.halted) {
- throw "Invalid state for resume()";
- }
-
- this.paused = false;
- this.halted = false;
-
- this._requestFrame();
-};
-
-
-LoresAnimator.prototype.step = function(timestamp) {
- var self = this;
-
- if(!this.started) {
- throw "Invalid state for step()";
- }
-
- if(this.halted || this.paused) return;
-
- setTimeout(function() {
- self._requestFrame();
- self.animFrame(timestamp);
- }, 1000 / self.fps);
-};
-
-
-LoresAnimator.prototype._requestFrame = function() {
-
- if(this.halted || this.paused) return;
-
- var self = this;
- window.requestAnimationFrame(function(time){self.step(time)});
-};
-
diff --git a/js/lores-base.js b/js/lores-base.js
deleted file mode 100644
index f9f0b94..0000000
--- a/js/lores-base.js
+++ /dev/null
@@ -1,847 +0,0 @@
-// Lores.js - base module
-// (c) Ondrej Hruska 2013
-// www.ondrovo.com
-
-// MIT license
-
-
-/* Display module with mouse input */
-function LoresDisplay(element, options) {
-
- var self = this;
-
- this.wrapper = $(element);
- options = options || {};
- options.width = options.width || $(element).width();
- options.height = options.height || $(element).height();
- options.cols = options.cols || 16;
- options.rows = options.rows || 16;
- options.bg = options.bg || 0;
- options.fg = options.fg || 1;
-
- options.colors = new LoresPalette(options.colors) || new LoresPalette();
-
- options.filter = options.filter || new LoresColorFilter();
-
- this.colors = options.colors;
- this.filter = options.filter;
-
- this.width = options.width;
- this.height = options.height;
-
- this.rows = options.rows;
- this.cols = options.cols;
-
- this.pixelWidth = (this.width/this.cols);
- this.pixelHeight = (this.height/this.rows);
-
- this.bg = options.bg;
- this.fg = options.fg;
-
-
- // build a canvas
- //this.wrapper.empty();
-
- this.canvas = $('
')
- .css({position: "absolute", left: 0, top: 0})
- .attr({"width": this.width, "height": this.height})
- .appendTo(this.wrapper);
-
- this.context = this.canvas[0].getContext('2d');
-
- this.erase(true);
-
-
- // mouse input
-
- // undefined if none
- this.moveHandler = options.moveHandler;
- this.rawMoveHandler = options.rawMoveHandler;
-
- this.clickHandler = options.clickHandler;
- this.rawClickHandler = options.rawClickHandler;
-
- this.mouseDown = false;
-
- this.lastMousePos = {x:-1,y:-1,outside:true};
- this.lastMousePosRaw = {x:-1,y:-1,outside:true};
-
-
- // add click handler
- $(this.canvas).on('click', function(evt) {
- var pos = self._getMousePos(self.canvas, evt);
-
- if(self.rawClickHandler) {
- var pixel = {
- x: pos.x,
- y: pos.y,
- outside: false,
- };
-
- self.rawClickHandler(pixel, self);
- }
-
- if(self.clickHandler) {
-
- var pixel = {
- x: Math.floor(pos.x / self.pixelWidth),
- y: Math.floor(pos.y / self.pixelHeight),
- };
-
- self.clickHandler(pixel, self);
- }
- });
-
-
- // add move handler
- $(window).on('mousemove', function(evt) {
- var pos = self._getMousePos(self.canvas, evt);
-
-
- if(self.rawMoveHandler) {
- var pixel = {
- x: pos.x,
- y: pos.y,
- outside: false,
- };
-
- if(pixel.x < 0 || pixel.x >= self.width || pixel.y < 0 || pixel.y >= self.height) {
- pixel.outside = true;
- }
-
- self.rawMoveHandler(pixel, self);
- self.lastMousePosRaw = pixel;
- }
-
- if(self.moveHandler) {
- var pixel = {
- x: Math.floor(pos.x / self.pixelWidth),
- y: Math.floor(pos.y / self.pixelHeight),
- outside: false,
- };
-
- if(pixel.x < 0 || pixel.x >= self.cols || pixel.y < 0 || pixel.y >= self.rows) {
- pixel.outside = true;
- }
-
- if(self.lastMousePos.x != pixel.x || self.lastMousePos.y != pixel.y) {
- self.moveHandler(pixel, self.lastMousePos, self);
- self.lastMousePos = pixel;
- }
- }
- });
-
-
- $(window).on('mousedown', function(evt) {
- self.mouseDown = true;
- });
-
-
- $(window).on('mouseup', function(evt) {
- self.mouseDown = false;
- });
-};
-
-
-LoresDisplay.prototype.erase = function(fillWithBg) {
-
- if(fillWithBg) {
- this.fill(this.bg);
- } else {
- this.context.clearRect(0,0,this.width,this.height);
- }
-};
-
-
-LoresDisplay.prototype.fill = function(color) {
-
- color = this.resolveColor(color);
-
- if(color == -1) {
- color = this.resolveColor(this.bg);
- }
-
- if(color == -1) {
- this.erase(false);
- } else {
- this.context.fillStyle = color;
- this.context.fillRect(0,0,this.width,this.height);
- }
-};
-
-
-LoresDisplay.prototype.getCanvas = function() {
- return this.canvas;
-};
-
-
-LoresDisplay.prototype.getContext = function() {
- return this.context;
-};
-
-
-LoresDisplay.prototype.getPalette = function() {
- return this.colors;
-};
-
-
-LoresDisplay.prototype.setPalette = function(newPalette) {
- this.colors = newPalette;
-};
-
-
-LoresDisplay.prototype.getColorFilter = function() {
- return this.filter;
-};
-
-
-LoresDisplay.prototype.setColorFilter = function(newFilter) {
- this.filter = newFilter;
-};
-
-
-LoresDisplay.prototype.addFilter = function(from, to) {
- this.filter.addFilter(from, to);
-};
-
-
-LoresDisplay.prototype.removeFilter = function(color) {
- this.filter.removeFilter(color);
-};
-
-
-LoresDisplay.prototype.getPixelSize = function() {
- return {
- x: this.pixelWidth,
- w: this.pixelWidth,
- y: this.pixelHeight,
- h: this.pixelHeight,
- };
-};
-
-
-LoresDisplay.prototype.resolveColor = function(color) {
-
- if(color === undefined || color === null || color === "") {
- throw "Null color";
- } else if(typeof color == "boolean") {
- color = color ? this.fg : this.bg;
- } else if(color == "transparent") {
- color = -1;
- }
-
- color = this.filter.process(color);
-
-
- if(typeof color == "number") {
-
- if(color == -1) return -1; // alpha = bg
-
- var color = this.getColor(color);
-
- if(color === undefined) {
- throw "Undefined color id '" + JSON.stringify(color) + "'";
- }
- }
-
-
- return color;
-};
-
-
-// alias
-LoresDisplay.prototype.setColor = function(index, color) {
- this.colors.add(index, color);
-};
-
-
-LoresDisplay.prototype.addColor = function(index, color) {
- this.colors.add(index, color);
-};
-
-
-LoresDisplay.prototype.removeColor = function(index) {
- this.colors.remove(index, color);
-};
-
-
-LoresDisplay.prototype.hasColor = function(index) {
- return index == -1 || this.colors.has(index);
-};
-
-
-LoresDisplay.prototype.getColor = function(index) {
- return this.colors.get(index);
-};
-
-
-LoresDisplay.prototype.setBg = function(color) {
- this.bg = color;
-};
-
-
-LoresDisplay.prototype.erasePixel = function(x, y) {
- this.setPixel(x, y, -1);
-};
-
-
-LoresDisplay.prototype.setPixel = function(x, y, color) {
-
- color = this.resolveColor(color);
-
- if(color == -1) {
- color = this.resolveColor(this.bg);
- }
-
- x = Math.floor(x);
- y = Math.floor(y);
-
- var x1 = x * this.pixelWidth;
- var y1 = y * this.pixelHeight;
-
- if(color == -1) {
- this.context.clearRect(x1, y1, this.pixelWidth, this.pixelHeight);
- } else {
- this.context.fillStyle = color;
- this.context.fillRect(x1, y1, this.pixelWidth, this.pixelHeight);
- }
-};
-
-
-/* moveHandler(display, pos, lastPos) */
-LoresDisplay.prototype.setMoveHandler = function(handler) {
- this.moveHandler = handler;
-};
-
-
-/* rawMoveHandler(display, pos, lastPos) */
-LoresDisplay.prototype.setRawMoveHandler = function(handler) {
- this.rawMoveHandler = handler;
-};
-
-
-/* clickHandler(display, pos) */
-LoresDisplay.prototype.setClickHandler = function(handler) {
- this.clickHandler = handler;
-};
-
-
-LoresDisplay.prototype.isMouseDown = function() {
- return this.mouseDown;
-};
-
-
-LoresDisplay.prototype.resetMouse = function() {
- this.mouseDown = false;
-};
-
-
-LoresDisplay.prototype._getMousePos = function(canvas, event) {
- var rect = canvas[0].getBoundingClientRect();
- return {
- x: event.clientX - rect.left,
- y: event.clientY - rect.top
- };
-};
-
-
-LoresDisplay.prototype.getWidth = function() {
- return this.cols;
-};
-
-
-LoresDisplay.prototype.getHeight = function() {
- return this.rows;
-};
-
-
-LoresDisplay.prototype.drawMap = function(map, x, y) {
-
- for(var yy = 0; yy
= this.rows) break;
- if(y+yy < 0) continue;
-
- for(var xx = 0; xx= this.cols) break;
- if(x+xx < 0) continue;
-
- this.setPixel(x+xx, y+yy, color);
- }
- }
-
-};
-
-
-LoresDisplay.prototype.getMap = function() {
- var map = new LoresPixmap(this.cols, this.rows, this.bg);
- map.connectedDisplay = this;
- return map;
-};
-
-
-
-
-/* Color table */
-function LoresPalette(values) {
- this.table = {
- 0: "#000000",
- 1: "#00ff00",
- };
-
- this.table = $.extend( this.table, values );
-};
-
-
-// alias
-LoresPalette.prototype.set = function(index, color) {
- this.add(index, color);
-};
-
-
-LoresPalette.prototype.add = function(index, color) {
- this.table[index] = color;
-};
-
-
-LoresPalette.prototype.remove = function(index) {
- delete this.table[index];
-};
-
-
-LoresPalette.prototype.has = function(index) {
- return this.table[index] !== undefined;
-};
-
-
-LoresPalette.prototype.get = function(index) {
- return this.table[index];
-};
-
-
-
-
-/* Pixel map */
-function LoresPixmap(width, height, fill) {
-
- this.doAutoFlush = true;
- this.connectedDisplay = null;
- if(fill == undefined) fill = -1;
- this.bg = fill;
-
- if(width === undefined || height === undefined) return;
-
- this.width = width;
- this.height = height;
-
- this.map = [];
-
- for(var y=0; y= this.width || x < 0 || y >= this.height || y < 0 ) return;
-
- if(this.map[y][x] == color) return; // no need to overwrite it
-
- this.map[y][x] = color;
-
- if(color != -1 && this.doAutoFlush && this.connectedDisplay !== null) {
- this.connectedDisplay.setPixel(x,y,color);
- }
-};
-
-
-LoresPixmap.prototype.getPixel = function(x,y) {
- if(x >= this.width || x < 0 || y >= this.height || y < 0 ) return -1;
-
- return this.map[y][x];
-};
-
-
-LoresPixmap.prototype.getArray = function() {
- return this.map;
-};
-
-
-LoresPixmap.prototype.setArray = function(dataArray) {
- this.width = dataArray[0].length;
- this.height = dataArray.length;
-
- this.map = dataArray;
-};
-
-
-LoresPixmap.prototype.getWidth = function() {
- return this.width;
-};
-
-
-LoresPixmap.prototype.getHeight = function() {
- return this.height;
-};
-
-
-LoresPixmap.prototype.drawMap = function(otherMap, x, y) {
-
- for(var yy = 0; yy < otherMap.getHeight(); yy++) {
- for(var xx = 0; xx < otherMap.getWidth(); xx++) {
-
- var color = otherMap.getPixel(xx,yy);
- if(color == -1) continue; // transparent = no draw
- this.setPixel(x+xx, y+yy, color);
-
- }
- }
-};
-
-
-LoresPixmap.prototype.eraseMap = function(otherMap, x, y) {
-
- for(var yy = 0; yy < otherMap.getHeight(); yy++) {
- for(var xx = 0; xx < otherMap.getWidth(); xx++) {
-
- var color = otherMap.getPixel(xx,yy);
- if(color == -1) continue; // transparent = no draw
- this.setPixel(x+xx, y+yy, this.bg);
-
- }
- }
-};
-
-
-LoresPixmap.prototype.eraseRect = function(x, y, w, h) {
-
- for(var yy = 0; yy < h; yy++) {
- for(var xx = 0; xx < w; xx++) {
-
- this.setPixel(x+xx, y+yy, this.bg);
- }
- }
-};
-
-
-LoresPixmap.prototype.fillRect = function(x, y, w, h, color) {
-
- for(var yy = 0; yy < h; yy++) {
- for(var xx = 0; xx < w; xx++) {
-
- this.setPixel(x+xx, y+yy, color);
- }
- }
-};
-
-
-LoresPixmap.prototype.flush = function() {
- if(this.connectedDisplay == null) {
- throw "Cannot flush map without a connected display.";
- }
-
- this.connectedDisplay.drawMap(this, 0, 0);
-};
-
-
-LoresPixmap.prototype.autoFlush = function(state) {
- this.doAutoFlush = state;
-};
-
-
-
-
-/* Color filter
- *
- * Used to translate color codes when
- * writing from a pixmap to display
- */
-function LoresColorFilter(translations) {
- this.table = translations || {};
-}
-
-
-LoresColorFilter.prototype.process = function(color) {
- color = (this.table[color] !== undefined) ? this.table[color] : color;
- return color;
-};
-
-
-LoresColorFilter.prototype.addFilter = function(from, to) {
- this.table[from] = to;
-};
-
-
-LoresColorFilter.prototype.removeFilter = function(color) {
- delete this.table[from];
-};
-
-
-
-
-/* Keyboard input handler */
-function LoresKeyboard() {
-
- this.states = {};
-
- this.pressHandlers = {};
- this.downHandlers = {};
- this.upHandlers = {};
-
- var self = this;
-
- $(window).on("keydown", function(event) {
- self.states[event.which] = true;
- self.downHandlers[event.which] && self.downHandlers[event.which](event.which);
- });
-
- $(window).on("keyup", function(event) {
- self.states[event.which] = false;
- self.upHandlers[event.which] && self.upHandlers[event.which](event.which);
- });
-
- $(window).on("keypress", function(event) {
- self.pressHandlers[event.which] && self.pressHandlers[event.which](event.which);
- });
-
-};
-
-LoresKeyboard.DELETE = 46;
-LoresKeyboard.BACKSPACE = 8;
-LoresKeyboard.SPACE = 32;
-LoresKeyboard.ENTER = 13;
-LoresKeyboard.ESC = 27;
-
-LoresKeyboard.LEFT = 37;
-LoresKeyboard.RIGHT = 39;
-LoresKeyboard.UP = 38;
-LoresKeyboard.DOWN = 40;
-
-LoresKeyboard.CTRL = 17;
-LoresKeyboard.SHIFT = 16;
-LoresKeyboard.META = 91;
-LoresKeyboard.INSERT = 45;
-LoresKeyboard.PAGEUP = 33;
-LoresKeyboard.PAGEDOWN = 34;
-LoresKeyboard.HOME = 36;
-LoresKeyboard.END = 35;
-
-LoresKeyboard.A = 65;
-LoresKeyboard.B = 66;
-LoresKeyboard.C = 67;
-LoresKeyboard.D = 68;
-LoresKeyboard.E = 69;
-LoresKeyboard.F = 70;
-LoresKeyboard.G = 71;
-LoresKeyboard.H = 72;
-LoresKeyboard.I = 73;
-LoresKeyboard.J = 74;
-LoresKeyboard.K = 75;
-LoresKeyboard.L = 76;
-LoresKeyboard.M = 77;
-LoresKeyboard.N = 78;
-LoresKeyboard.O = 79;
-LoresKeyboard.P = 80;
-LoresKeyboard.Q = 81;
-LoresKeyboard.R = 82;
-LoresKeyboard.S = 83;
-LoresKeyboard.T = 84;
-LoresKeyboard.U = 85;
-LoresKeyboard.V = 86;
-LoresKeyboard.W = 87;
-LoresKeyboard.X = 88;
-LoresKeyboard.Y = 89;
-LoresKeyboard.Z = 90;
-
-LoresKeyboard.NUM_0 = 96;
-LoresKeyboard.NUM_1 = 97;
-LoresKeyboard.NUM_2 = 98;
-LoresKeyboard.NUM_3 = 99;
-LoresKeyboard.NUM_4 = 100;
-LoresKeyboard.NUM_5 = 101;
-LoresKeyboard.NUM_6 = 102;
-LoresKeyboard.NUM_7 = 103;
-LoresKeyboard.NUM_8 = 104;
-LoresKeyboard.NUM_9 = 105;
-
-LoresKeyboard.LETTERS = [65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90];
-LoresKeyboard.ASDW = [65,83,68,87];
-LoresKeyboard.ARROWS = [37,38,39,40];
-LoresKeyboard.NUMBERS = [96,97,98,99,100,101,102,103,104,105];
-
-
-
-LoresKeyboard.prototype.isDown = function(keycode) {
- var val = this.states[keycode];
- if(val == undefined) val = false;
- return val;
-};
-
-
-LoresKeyboard.prototype.resetKeys = function() {
- this.states = {};
-};
-
-
-LoresKeyboard.prototype.setPressHandler = function(keycode, handler) {
-
- if(keycode.constructor == Array) {
-
- for(var i=0; i= 0; i--) {
- if (this.sprites[i] == sprite) this.sprites.splice(i, 1);
- }
-};
-
-
-LoresSpritePool.prototype.getSprites = function() {
- return this.sprites;
-};
-
-
-LoresSpritePool.prototype.getColliding = function(sprite, pixelPerfect) {
- var results = [];
- for(var i in this.sprites) {
- var spr = this.sprites[i];
- if(spr === sprite) continue;
- if(spr.collidesWith(sprite, pixelPerfect)) {
- results.push(spr);
- }
- }
- return results;
-};
-
-
-LoresSpritePool.prototype.eraseMap = function() {
- if(this.drawingMap == undefined) throw "LoresSpritePool has no map.";
-
- this.drawingMap.erase();
-};
-
-
-LoresSpritePool.prototype.drawOnMap = function() {
- if(this.drawingMap == undefined) throw "LoresSpritePool has no map.";
-
- for(var i in this.sprites) {
- this.sprites[i].drawOnMap(this.drawingMap);
- }
-};
-
-
-LoresSpritePool.prototype.eraseOnMap = function(perPixel) {
- if(this.drawingMap == undefined) throw "LoresSpritePool has no map.";
-
- for(var i in this.sprites) {
- this.sprites[i].eraseOnMap(this.drawingMap, perPixel);
- }
-};
-
-
-LoresSpritePool.prototype.sortSprites = function() {
- this.sprites.sort(function(a,b) {
- return (a.z > b.z) ? -1 : ((b.z > a.z) ? 1 : 0);
- });
-};
-
-
-LoresSpritePool.prototype.moveSprites = function() {
- for(var i in this.sprites) {
- this.sprites[i].doMove();
- }
-};
-
-
-
-
-function LoresSprite(opts) {
- this.map = opts.map;
-
- this.x = opts.x;
- this.y = opts.y;
-
- this.newx = opts.x;
- this.newy = opts.y;
-
- this.z = (opts.z === undefined ? 0 : opts.z); // z-index
-}
-
-
-LoresSprite.prototype.setMap = function(map) {
- this.map = map;
-};
-
-
-LoresSprite.prototype.setPosition = function(xpos, ypos) {
- this.newx = xpos;
- this.newy = ypos;
-};
-
-
-LoresSprite.prototype.scheduleMove = function(xoffset, yoffset) {
- this.newx = this.x + xoffset;
- this.newy = this.y + yoffset;
-};
-
-
-LoresSprite.prototype.doMove = function() {
- this.x = this.newx;
- this.y = this.newy;
-};
-
-
-LoresSprite.prototype.getWidth = function() {
- return this.map.getWidth();
-};
-
-
-LoresSprite.prototype.getHeight = function() {
- return this.map.getHeight();
-};
-
-
-LoresSprite.prototype.collidesWith = function(other, pixelPerfect) {
- var x1L = this.x;
- var x1R = this.x + this.getWidth() - 1;
- var y1U = this.y;
- var y1D = this.y + this.getHeight() - 1;
-
- var x2L = other.x;
- var x2R = other.x + other.getWidth() - 1;
- var y2U = other.y;
- var y2D = other.y + other.getHeight() - 1;
-
- var horizontal = x1L >= x2L && x1L <= x2R;
- horizontal |= x1R <= x2R && x1R >= x2L;
-
- var vertical = y1U >= y2U && y1U <= y2D;
- vertical |= y1D <= y2D && y1D >= y2U;
-
- var rectCollides = (horizontal && vertical);
-
- if(!rectCollides) return false; // surely
-
- if(!pixelPerfect) {
- return true; // rect collision suffices
-
- } else {
-
- for(var yy = Math.max(y1U, y2U); yy <= Math.min(y1D, y2D); yy++) {
- for(var xx = Math.max(x1L, x2L); xx <= Math.min(x1R, x2R); xx++) {
-
- var c1 = this.map.getPixel( (xx - x1L), (yy - y1U) );
- var c2 = other.map.getPixel( (xx - x2L), (yy - y2U) );
-
- if(c1 != -1 && c2 != -1) return true; // collision detected
- }
- }
-
- return false; // nope
- }
-};
-
-
-LoresSprite.prototype.drawOnMap = function(map) {
- map.drawMap(this.map, this.x, this.y);
-};
-
-
-LoresSprite.prototype.eraseOnMap = function(map, perPixel) {
- if(perPixel) {
- map.eraseMap(this.map, this.x, this.y);
- } else {
- map.eraseRect(this.x, this.y, this.map.getWidth(), this.map.getHeight());
- }
-};
\ No newline at end of file
diff --git a/js/lores.js b/js/lores.js
new file mode 100644
index 0000000..c9e0c78
--- /dev/null
+++ b/js/lores.js
@@ -0,0 +1,1594 @@
+// Lores.js
+// (c) Ondrej Hruska 2013
+// www.ondrovo.com | @MightyPork | ondra@ondrovo.com
+
+// MIT license
+
+
+/* ====== CORE ====== */
+
+Lores = {
+ version: "0.1", // library version
+ verbose: true, // enable debug output to console
+};
+
+
+/* ====== DISPLAY ====== */
+function LoresDisplay(element, options) {
+
+ var self = this;
+
+ this.wrapper = $(element);
+ options = options || {};
+ options.width = options.width || $(element).width();
+ options.height = options.height || $(element).height();
+ options.cols = options.cols || 16;
+ options.rows = options.rows || 16;
+ options.bg = options.bg || 0;
+ options.fg = options.fg || 1;
+
+ options.colors = new LoresPalette(options.colors) || new LoresPalette();
+
+ options.colorTranslator = options.colorTranslator || new LoresColorTranslator();
+
+ this.colors = options.colors;
+ this.colorTranslator = options.colorTranslator;
+
+ this.width = options.width;
+ this.height = options.height;
+
+ this.rows = options.rows;
+ this.cols = options.cols;
+
+ this.pixelWidth = (this.width/this.cols);
+ this.pixelHeight = (this.height/this.rows);
+
+ this.bg = options.bg;
+ this.fg = options.fg;
+
+
+ // build a canvas
+
+ this.canvas = $(' ')
+ .css({position: "absolute", left: 0, top: 0})
+ .attr({"width": this.width, "height": this.height})
+ .appendTo(this.wrapper);
+
+ this.context = this.canvas[0].getContext('2d');
+
+ this.erase(true);
+
+
+ // mouse input
+
+ this.moveHandler = options.moveHandler;
+ this.rawMoveHandler = options.rawMoveHandler;
+
+ this.clickHandler = options.clickHandler;
+ this.rawClickHandler = options.rawClickHandler;
+
+ this.mouseDown = false;
+
+ this.lastMousePos = {x:-1,y:-1,outside:true};
+ this.lastMousePosRaw = {x:-1,y:-1,outside:true};
+
+
+ // add click handler
+ $(this.canvas).on('click', function(evt) {
+ var pos = self._getMousePos(self.canvas, evt);
+
+ if(self.rawClickHandler) {
+ var pixel = {
+ x: pos.x,
+ y: pos.y,
+ outside: false,
+ };
+
+ self.rawClickHandler(pixel, self);
+ }
+
+ if(self.clickHandler) {
+
+ var pixel = {
+ x: Math.floor(pos.x / self.pixelWidth),
+ y: Math.floor(pos.y / self.pixelHeight),
+ };
+
+ self.clickHandler(pixel, self);
+ }
+ });
+
+
+ // add move handler
+ $(window).on('mousemove', function(evt) {
+ var pos = self._getMousePos(self.canvas, evt);
+
+
+ if(self.rawMoveHandler) {
+ var pixel = {
+ x: pos.x,
+ y: pos.y,
+ outside: false,
+ };
+
+ if(pixel.x < 0 || pixel.x >= self.width || pixel.y < 0 || pixel.y >= self.height) {
+ pixel.outside = true;
+ }
+
+ self.rawMoveHandler(pixel, self.lastMousePosRaw, self);
+ self.lastMousePosRaw = pixel;
+ }
+
+ if(self.moveHandler) {
+ var pixel = {
+ x: Math.floor(pos.x / self.pixelWidth),
+ y: Math.floor(pos.y / self.pixelHeight),
+ outside: false,
+ };
+
+ if(pixel.x < 0 || pixel.x >= self.cols || pixel.y < 0 || pixel.y >= self.rows) {
+ pixel.outside = true;
+ }
+
+ if(self.lastMousePos.x != pixel.x || self.lastMousePos.y != pixel.y) {
+ self.moveHandler(pixel, self.lastMousePos, self);
+ self.lastMousePos = pixel;
+ }
+ }
+ });
+
+
+ $(window).on('mousedown', function(evt) {
+ self.mouseDown = true;
+ });
+
+
+ $(window).on('mouseup', function(evt) {
+ self.mouseDown = false;
+ });
+};
+
+
+LoresDisplay.prototype.erase = function(fillWithBg) {
+
+ if(fillWithBg) {
+ this.fill(this.bg);
+ } else {
+ this.context.clearRect(0,0,this.width,this.height);
+ }
+};
+
+
+LoresDisplay.prototype.fill = function(color) {
+
+ color = this.resolveColor(color);
+
+ if(color == -1) {
+ color = this.resolveColor(this.bg);
+ }
+
+ if(color == -1) {
+ this.erase(false);
+ } else {
+ this.context.fillStyle = color;
+ this.context.fillRect(0,0,this.width,this.height);
+ }
+};
+
+
+LoresDisplay.prototype.eraseRect = function(x1,y1,w,h,fillWithBg) {
+
+ if(fillWithBg) {
+ this.fillRect(x1,y1,x2,y2,this.bg);
+ } else {
+ this.context.clearRect(
+ x1 * this.pixelWidth,
+ y1 * this.pixelHeight,
+ w * this.pixelWidth,
+ h * this.pixelHeight
+ );
+ }
+};
+
+
+LoresDisplay.prototype.fillRect = function(x1,y1,w,h,color) {
+
+ color = this.resolveColor(color);
+
+ if(color == -1) {
+ color = this.resolveColor(this.bg);
+ }
+
+ if(color == -1) {
+ this.eraseRect(x1,y1,w,h,false);
+ } else {
+ this.context.fillStyle = color;
+ this.context.fillRect(
+ x1 * this.pixelWidth,
+ y1 * this.pixelHeight,
+ w * this.pixelWidth,
+ h * this.pixelHeight
+ );
+ }
+};
+
+
+LoresDisplay.prototype.getCanvas = function() {
+ return this.canvas[0];
+};
+
+
+LoresDisplay.prototype.getContext = function() {
+ return this.context;
+};
+
+
+LoresDisplay.prototype.getPalette = function() {
+ return this.colors;
+};
+
+
+LoresDisplay.prototype.setPalette = function(newPalette) {
+ this.colors = newPalette;
+};
+
+
+LoresDisplay.prototype.getColorTranslator = function() {
+ return this.colorTranslator;
+};
+
+
+LoresDisplay.prototype.setColorTranslator = function(newTranslator) {
+ this.colorTranslator = newTranslator;
+};
+
+
+LoresDisplay.prototype.setColorReplacementTable = function(newTable) {
+ this.colorTranslator.setRules(newTable);
+};
+
+
+LoresDisplay.prototype.addColorRule = function(from, to) {
+ this.colorTranslator.addRule(from, to);
+};
+
+
+LoresDisplay.prototype.removeColorRule = function(color) {
+ this.colorTranslator.removeRule(color);
+};
+
+
+LoresDisplay.prototype.setColorFilter = function(filterFunction) {
+ this.colorTranslator.setFilter(filterFunction);
+};
+
+
+LoresDisplay.prototype.getPixelSize = function() {
+ return {
+ x: this.pixelWidth,
+ w: this.pixelWidth,
+ y: this.pixelHeight,
+ h: this.pixelHeight,
+ };
+};
+
+
+LoresDisplay.prototype.resolveColor = function(color) {
+
+ if(color === undefined || color === null || color === "") {
+ throw "Null color";
+ } else if(typeof color == "boolean") {
+ color = color ? this.fg : this.bg;
+ } else if(color == "transparent") {
+ color = -1;
+ }
+
+ color = this.colorTranslator.process(color);
+
+
+ if(typeof color == "number") {
+
+ if(color == -1) return -1; // alpha = bg
+
+ var color = this.getColor(color);
+
+ if(color === undefined) {
+ throw "Undefined color id '" + JSON.stringify(color) + "'";
+ }
+ }
+
+
+ return color;
+};
+
+
+// alias
+LoresDisplay.prototype.setColor = function(index, color) {
+ this.colors.add(index, color);
+};
+
+
+LoresDisplay.prototype.addColor = function(index, color) {
+ this.colors.add(index, color);
+};
+
+
+LoresDisplay.prototype.removeColor = function(index) {
+ this.colors.remove(index, color);
+};
+
+
+LoresDisplay.prototype.hasColor = function(index) {
+ return index == -1 || this.colors.has(index);
+};
+
+
+LoresDisplay.prototype.getColor = function(index) {
+ return this.colors.get(index);
+};
+
+
+LoresDisplay.prototype.setBg = function(color) {
+ this.bg = color;
+};
+
+
+LoresDisplay.prototype.erasePixel = function(x, y) {
+ this.setPixel(x, y, -1);
+};
+
+
+LoresDisplay.prototype.setPixel = function(x, y, color) {
+
+ if(x<0||x>=this.cols||y<0||y>=this.rows) return; // out of bounds
+
+
+ color = this.resolveColor(color);
+
+ if(color == -1 && this.bg != -1) {
+ color = this.resolveColor(this.bg);
+ }
+
+ x = Math.floor(x);
+ y = Math.floor(y);
+
+ var x1 = x * this.pixelWidth;
+ var y1 = y * this.pixelHeight;
+
+ if(color == -1) {
+ this.context.clearRect(x1, y1, this.pixelWidth, this.pixelHeight);
+ } else {
+ this.context.fillStyle = color;
+ this.context.fillRect(x1, y1, this.pixelWidth, this.pixelHeight);
+ }
+};
+
+
+/* moveHandler(display, pos, lastPos) */
+LoresDisplay.prototype.setMoveHandler = function(handler) {
+ this.moveHandler = handler;
+};
+
+
+/* rawMoveHandler(display, pos, lastPos) */
+LoresDisplay.prototype.setRawMoveHandler = function(handler) {
+ this.rawMoveHandler = handler;
+};
+
+
+/* clickHandler(display, pos) */
+LoresDisplay.prototype.setClickHandler = function(handler) {
+ this.clickHandler = handler;
+};
+
+
+LoresDisplay.prototype.isMouseDown = function() {
+ return this.mouseDown;
+};
+
+
+LoresDisplay.prototype.resetMouse = function() {
+ this.mouseDown = false;
+};
+
+
+LoresDisplay.prototype._getMousePos = function(canvas, event) {
+ var rect = canvas[0].getBoundingClientRect();
+ return {
+ x: event.clientX - rect.left,
+ y: event.clientY - rect.top
+ };
+};
+
+
+LoresDisplay.prototype.getWidth = function() {
+ return this.cols;
+};
+
+
+LoresDisplay.prototype.getHeight = function() {
+ return this.rows;
+};
+
+
+LoresDisplay.prototype.drawMap = function(map, x, y) {
+
+ for(var yy = 0; yy= this.rows) break;
+ if(y+yy < 0) continue;
+
+ for(var xx = 0; xx= this.cols) break;
+ if(x+xx < 0) continue;
+
+ this.setPixel(x+xx, y+yy, color);
+ }
+ }
+
+};
+
+
+LoresDisplay.prototype.getMap = function(fill) {
+ if(fill === undefined) fill = this.bg;
+ var map = new LoresPixmap(this.cols, this.rows, fill);
+ map.connectedDisplay = this;
+ return map;
+};
+
+
+
+/* ====== PALETTE ====== */
+
+function LoresPalette(values) {
+ this.table = {
+ 0: "#000000",
+ 1: "#00ff00",
+ };
+
+ this.table = $.extend( this.table, values );
+};
+
+
+// alias
+LoresPalette.prototype.set = function(index, color) {
+ this.add(index, color);
+};
+
+
+LoresPalette.prototype.add = function(index, color) {
+ this.table[index] = color;
+};
+
+
+LoresPalette.prototype.remove = function(index) {
+ delete this.table[index];
+};
+
+
+LoresPalette.prototype.has = function(index) {
+ return this.table[index] !== undefined;
+};
+
+
+LoresPalette.prototype.get = function(index) {
+ return this.table[index];
+};
+
+
+
+/* ====== PIXMAP ====== */
+
+function LoresPixmap(width, height, fill) {
+
+ this.doAutoFlush = true;
+ this.connectedDisplay = null;
+ if(fill == undefined) fill = -1;
+ this.bg = fill;
+ this.ready = false;
+
+ if(width === undefined || height === undefined) return;
+
+ this.width = width;
+ this.height = height;
+
+ this.map = [];
+
+ for(var y=0; y= 1 && validLineIndex <= cc) {
+ // key c color
+ var key = line.substring(0, cpp);
+
+ var parts = line.substring(cpp).trim().split(/\s/g);
+ if(parts[0]=='c') {
+ var color = parts[1];
+
+ colors[key] = color;
+ } else {
+ throw "Invalid color definition: " + line;
+ }
+
+
+ valid = true;
+
+ } else if(line.length == w * cpp) {
+
+ // an actual line
+ valid = true;
+
+ var row = [];
+
+ for(var pi = 0; pi < w; pi++) {
+ var c = line.substring(pi*cpp, (pi+1)*cpp);
+
+ var color = colors[c];
+
+ if(color === undefined) color = -1;
+
+ row.push(color);
+ }
+
+ array.push(row);
+
+ } else {
+ throw "Invalid img data: " + line;
+ }
+
+ if(valid) validLineIndex++;
+ }
+ }
+
+ return array;
+};
+
+
+LoresPixmap.prototype.setReady = function() {
+ this.ready = true;
+};
+
+
+LoresPixmap.prototype.isReady = function() {
+ return this.ready;
+};
+
+
+LoresPixmap.prototype.setBg = function(color) {
+ this.bg = color;
+};
+
+
+LoresPixmap.prototype.erase = function() {
+ this.fill(this.bg);
+};
+
+
+LoresPixmap.prototype.fill = function(color) {
+ for(var y=0; y= this.width || x < 0 || y >= this.height || y < 0 ) return;
+
+ if(this.map[y][x] == color) return; // no need to overwrite it
+
+ this.map[y][x] = color;
+
+ if(color != -1 && this.doAutoFlush && this.connectedDisplay !== null) {
+ this.connectedDisplay.setPixel(x,y,color);
+ }
+};
+
+
+LoresPixmap.prototype.getPixel = function(x,y) {
+ if(x >= this.width || x < 0 || y >= this.height || y < 0 ) return -1;
+
+ return this.map[y][x];
+};
+
+
+LoresPixmap.prototype.getArray = function() {
+ return this.map;
+};
+
+
+LoresPixmap.prototype.setArray = function(dataArray) {
+ this.width = dataArray[0].length;
+ this.height = dataArray.length;
+
+ this.setReady(true);
+
+ this.map = dataArray;
+};
+
+
+LoresPixmap.prototype.getWidth = function() {
+ return this.width;
+};
+
+
+LoresPixmap.prototype.getHeight = function() {
+ return this.height;
+};
+
+
+LoresPixmap.prototype.drawMap = function(otherMap, x, y) {
+
+ for(var yy = 0; yy < otherMap.getHeight(); yy++) {
+ for(var xx = 0; xx < otherMap.getWidth(); xx++) {
+
+ var color = otherMap.getPixel(xx,yy);
+ if(color == -1) continue; // transparent = no draw
+ this.setPixel(x+xx, y+yy, color);
+
+ }
+ }
+};
+
+
+LoresPixmap.prototype.eraseMap = function(otherMap, x, y) {
+
+ for(var yy = 0; yy < otherMap.getHeight(); yy++) {
+ for(var xx = 0; xx < otherMap.getWidth(); xx++) {
+
+ var color = otherMap.getPixel(xx,yy);
+ if(color == -1) continue; // transparent = no draw
+ this.setPixel(x+xx, y+yy, this.bg);
+
+ }
+ }
+};
+
+
+LoresPixmap.prototype.eraseRect = function(x, y, w, h) {
+
+ for(var yy = 0; yy < h; yy++) {
+ for(var xx = 0; xx < w; xx++) {
+
+ this.setPixel(x+xx, y+yy, this.bg);
+ }
+ }
+};
+
+
+LoresPixmap.prototype.fillRect = function(x, y, w, h, color) {
+
+ for(var yy = 0; yy < h; yy++) {
+ for(var xx = 0; xx < w; xx++) {
+
+ this.setPixel(x+xx, y+yy, color);
+ }
+ }
+};
+
+
+LoresPixmap.prototype.flush = function() {
+ if(this.connectedDisplay == null) {
+ throw "Cannot flush map without a connected display.";
+ }
+
+ this.connectedDisplay.drawMap(this, 0, 0);
+};
+
+
+LoresPixmap.prototype.autoFlush = function(state) {
+ this.doAutoFlush = state;
+};
+
+
+LoresPixmap.prototype.checker = function(base, color) {
+
+ for(var y = 0; y < this.height; y++) {
+ for(var x = 0; x < this.width; x++) {
+ this.setPixel(x, y, ((x+y)%2==0 ? color : base));
+ }
+ }
+};
+
+
+LoresPixmap.prototype.walk = function(walker) {
+
+ for(var y = 0; y < this.height; y++) {
+ for(var x = 0; x < this.width; x++) {
+ walker(x, y, this);
+ }
+ }
+};
+
+
+
+/* ====== COLOR TRANSLATOR ====== */
+
+// Used to translate color codes when writing from a pixmap to display
+function LoresColorTranslator(rules) {
+ this.table = rules || {};
+ this.filter = function(color) {return color};
+}
+
+
+LoresColorTranslator.prototype.process = function(color) {
+ color = (this.table[color] !== undefined) ? this.table[color] : color;
+
+ return this.filter(color);
+};
+
+
+LoresColorTranslator.prototype.addRule = function(from, to) {
+ this.table[from] = to;
+};
+
+
+LoresColorTranslator.prototype.removeRule = function(color) {
+ delete this.table[from];
+};
+
+
+LoresColorTranslator.prototype.setRules = function(rules) {
+ this.table = rules;
+};
+
+
+LoresColorTranslator.prototype.setFilter = function(filterFunction) {
+ this.filter = filterFunction;
+};
+
+
+
+/* ====== KEYBOARD ====== */
+
+function LoresKeyboard() {
+
+ this.states = {};
+
+ this.pressHandlers = {};
+ this.downHandlers = {};
+ this.upHandlers = {};
+
+ var self = this;
+
+ $(window).on("keydown", function(event) {
+ self.states[event.which] = true;
+ self.downHandlers[event.which] && self.downHandlers[event.which](event.which);
+ self.downHandlers[ -1 ] && self.downHandlers[ -1 ](event.which);
+ });
+
+ $(window).on("keyup", function(event) {
+ self.states[event.which] = false;
+ self.upHandlers[event.which] && self.upHandlers[event.which](event.which);
+ self.upHandlers[ -1 ] && self.upHandlers[ -1 ](event.which);
+ });
+
+ $(window).on("keypress", function(event) {
+ self.pressHandlers[event.which] && self.pressHandlers[event.which](event.which);
+ self.pressHandlers[ -1 ] && self.pressHandlers[ -1 ](event.which);
+ });
+
+};
+
+LoresKeyboard.DELETE = 46;
+LoresKeyboard.BACKSPACE = 8;
+LoresKeyboard.SPACE = 32;
+LoresKeyboard.ENTER = 13;
+LoresKeyboard.ESC = 27;
+
+LoresKeyboard.LEFT = 37;
+LoresKeyboard.RIGHT = 39;
+LoresKeyboard.UP = 38;
+LoresKeyboard.DOWN = 40;
+
+LoresKeyboard.CTRL = 17;
+LoresKeyboard.SHIFT = 16;
+LoresKeyboard.META = 91;
+LoresKeyboard.INSERT = 45;
+LoresKeyboard.PAGEUP = 33;
+LoresKeyboard.PAGEDOWN = 34;
+LoresKeyboard.HOME = 36;
+LoresKeyboard.END = 35;
+
+LoresKeyboard.A = 65;
+LoresKeyboard.B = 66;
+LoresKeyboard.C = 67;
+LoresKeyboard.D = 68;
+LoresKeyboard.E = 69;
+LoresKeyboard.F = 70;
+LoresKeyboard.G = 71;
+LoresKeyboard.H = 72;
+LoresKeyboard.I = 73;
+LoresKeyboard.J = 74;
+LoresKeyboard.K = 75;
+LoresKeyboard.L = 76;
+LoresKeyboard.M = 77;
+LoresKeyboard.N = 78;
+LoresKeyboard.O = 79;
+LoresKeyboard.P = 80;
+LoresKeyboard.Q = 81;
+LoresKeyboard.R = 82;
+LoresKeyboard.S = 83;
+LoresKeyboard.T = 84;
+LoresKeyboard.U = 85;
+LoresKeyboard.V = 86;
+LoresKeyboard.W = 87;
+LoresKeyboard.X = 88;
+LoresKeyboard.Y = 89;
+LoresKeyboard.Z = 90;
+
+LoresKeyboard.NUM_0 = 96;
+LoresKeyboard.NUM_1 = 97;
+LoresKeyboard.NUM_2 = 98;
+LoresKeyboard.NUM_3 = 99;
+LoresKeyboard.NUM_4 = 100;
+LoresKeyboard.NUM_5 = 101;
+LoresKeyboard.NUM_6 = 102;
+LoresKeyboard.NUM_7 = 103;
+LoresKeyboard.NUM_8 = 104;
+LoresKeyboard.NUM_9 = 105;
+
+LoresKeyboard.LETTERS = [65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90];
+LoresKeyboard.ASDW = [65,83,68,87];
+LoresKeyboard.ARROWS = [37,38,39,40];
+LoresKeyboard.NUMBERS = [96,97,98,99,100,101,102,103,104,105];
+
+// for the lazy bums
+LKey = LoresKeyboard;
+
+
+LoresKeyboard.prototype.isDown = function(keycode) {
+ var val = this.states[keycode];
+ if(val == undefined) val = false;
+ return val;
+};
+
+
+LoresKeyboard.prototype.resetKeys = function() {
+ this.states = {};
+};
+
+
+LoresKeyboard.prototype.setPressHandler = function(keycode, handler) {
+
+ if(keycode.constructor == Array) {
+
+ for(var i=0; i= 0; i--) {
+ if (this.sprites[i] == sprite) this.sprites.splice(i, 1);
+ }
+};
+
+
+LoresSpritePool.prototype.getSprites = function() {
+ return this.sprites;
+};
+
+
+LoresSpritePool.prototype.getColliding = function(sprite, pixelPerfect) {
+ var results = [];
+ for(var i in this.sprites) {
+ var spr = this.sprites[i];
+ if(spr === sprite) continue;
+ if(spr.collidesWith(sprite, pixelPerfect)) {
+ results.push(spr);
+ }
+ }
+ return results;
+};
+
+
+LoresSpritePool.prototype.eraseMap = function() {
+ if(this.drawingMap == undefined) throw "LoresSpritePool has no map.";
+
+ this.drawingMap.erase();
+};
+
+
+LoresSpritePool.prototype.drawOnMap = function() {
+ if(this.drawingMap == undefined) throw "LoresSpritePool has no map.";
+
+ for(var i in this.sprites) {
+ this.sprites[i].drawOnMap(this.drawingMap);
+ }
+};
+
+
+LoresSpritePool.prototype.eraseOnMap = function(perPixel) {
+ if(this.drawingMap == undefined) throw "LoresSpritePool has no map.";
+
+ for(var i in this.sprites) {
+ this.sprites[i].eraseOnMap(this.drawingMap, perPixel);
+ }
+};
+
+
+LoresSpritePool.prototype.sortSprites = function() {
+ this.sprites.sort(function(a,b) {
+ return (a.z > b.z) ? -1 : ((b.z > a.z) ? 1 : 0);
+ });
+};
+
+
+LoresSpritePool.prototype.moveSprites = function() {
+ for(var i in this.sprites) {
+ this.sprites[i].doMove();
+ }
+};
+
+
+
+/* ====== SPRITE ====== */
+
+function LoresSprite(opts) {
+ this.map = opts.map;
+
+ this.x = opts.x;
+ this.y = opts.y;
+
+ this.newx = opts.x;
+ this.newy = opts.y;
+
+ this.z = (opts.z === undefined ? 0 : opts.z); // z-index
+}
+
+
+LoresSprite.prototype.setMap = function(map) {
+ this.map = map;
+};
+
+
+LoresSprite.prototype.setPosition = function(xpos, ypos) {
+ this.newx = xpos;
+ this.newy = ypos;
+};
+
+
+LoresSprite.prototype.scheduleMove = function(xoffset, yoffset) {
+ this.newx = this.x + xoffset;
+ this.newy = this.y + yoffset;
+};
+
+
+LoresSprite.prototype.doMove = function() {
+ this.x = this.newx;
+ this.y = this.newy;
+};
+
+
+LoresSprite.prototype.getWidth = function() {
+ return this.map.getWidth();
+};
+
+
+LoresSprite.prototype.getHeight = function() {
+ return this.map.getHeight();
+};
+
+
+LoresSprite.prototype.collidesWith = function(other, pixelPerfect) {
+ var x1L = this.x;
+ var x1R = this.x + this.getWidth() - 1;
+ var y1U = this.y;
+ var y1D = this.y + this.getHeight() - 1;
+
+ var x2L = other.x;
+ var x2R = other.x + other.getWidth() - 1;
+ var y2U = other.y;
+ var y2D = other.y + other.getHeight() - 1;
+
+ var horizontal = x1L >= x2L && x1L <= x2R;
+ horizontal |= x1R <= x2R && x1R >= x2L;
+
+ var vertical = y1U >= y2U && y1U <= y2D;
+ vertical |= y1D <= y2D && y1D >= y2U;
+
+ var rectCollides = (horizontal && vertical);
+
+ if(!rectCollides) return false; // surely
+
+ if(!pixelPerfect) {
+ return true; // rect collision suffices
+
+ } else {
+
+ for(var yy = Math.max(y1U, y2U); yy <= Math.min(y1D, y2D); yy++) {
+ for(var xx = Math.max(x1L, x2L); xx <= Math.min(x1R, x2R); xx++) {
+
+ var c1 = this.map.getPixel( (xx - x1L), (yy - y1U) );
+ var c2 = other.map.getPixel( (xx - x2L), (yy - y2U) );
+
+ if(c1 != -1 && c2 != -1) return true; // collision detected
+ }
+ }
+
+ return false; // nope
+ }
+};
+
+
+LoresSprite.prototype.drawOnMap = function(map) {
+ map.drawMap(this.map, this.x, this.y);
+};
+
+
+LoresSprite.prototype.eraseOnMap = function(map, perPixel) {
+ if(perPixel) {
+ map.eraseMap(this.map, this.x, this.y);
+ } else {
+ map.eraseRect(this.x, this.y, this.map.getWidth(), this.map.getHeight());
+ }
+};
+
+
+
+/* ====== TILE SET ====== */
+
+function LoresTileset(helperDisplay, tileWidth, tileHeight) {
+ this.display = helperDisplay || null;
+ this.tiles = {};
+ this.w = tileWidth;
+ this.h = tileHeight;
+ this.lastX = -this.w;
+ this.lastY = 0;
+}
+
+
+LoresTileset.prototype.erase = function() {
+ this.display.erase(true);
+};
+
+
+LoresTileset.prototype.addTile = function(name, pixmap) {
+
+ if(pixmap.getWidth() != this.w && pixmap.getHeight() != this.h) {
+ throw "Tile size not compatible with tileset.";
+ }
+
+ var x = this.lastX + this.w;
+ var y = this.lastY;
+
+ var maxX = x + this.w;
+ var maxY = y + this.h;
+
+ if(maxX >= this.display.getWidth()) {
+ x = 0;
+ y += this.h;
+ }
+
+ if(maxY >= this.display.getHeight()) {
+ throw "Tileset is full, can't add another tile.";
+ }
+
+ this.tiles[name] = {x:x,y:y};
+
+ this.lastX = x;
+ this.lastY = y;
+
+ Lores.verbose && console.log("new tile at "+x+","+y);
+
+ this.display.drawMap(pixmap, x, y);
+};
+
+
+LoresTileset.prototype.renderTile = function(dest, name, x, y) {
+ var source = this.display.getCanvas();
+ var ctx = dest.getContext();
+
+ var tile = this.tiles[name];
+
+ if(tile===undefined) throw "Tile not found: " + name;
+
+ var px = dest.getPixelSize();
+ var px2 = this.display.getPixelSize();
+
+ if(px.x != px2.x || px.y != px2.y) throw "Tileset's pixel size doesn't match target pixel size. Can't draw.";
+
+ ctx.drawImage(
+ source,
+
+ tile.x*px.x,
+ tile.y*px.y,
+ this.w*px.x,
+ this.h*px.y,
+
+ x*this.w*px.x,
+ y*this.w*px.y,
+ this.w*px.x,
+ this.h*px.y
+ );
+};
+
+
+
+/* ====== UTILS ====== */
+
+Lores.buildCheckerBackground = function(element, bgCols, bgRows, bg, fg) {
+ var scr = new LoresDisplay(
+ $(element),
+ {
+ cols: bgCols,
+ rows: bgRows,
+ colors: {
+ 0: bg || "#000",
+ 1: fg || "#111",
+ },
+ bg: 0,
+ }
+ );
+
+ var map = scr.getMap();
+ map.autoFlush(false);
+ map.checker(0,1);
+ map.flush();
+};
+
+
+Lores.buildHelperCanvas = function(element, cols, rows, palette, bg) {
+ var scr = new LoresDisplay(
+ $(element),
+ {
+ cols: cols,
+ rows: rows,
+ colors: palette,
+ bg: (bg===undefined ? -1 : bg),
+ }
+ );
+
+ $(scr.getCanvas()).css("display","none");
+
+ return scr;
+};
+
+
+Lores.shuffleArray = function(array) {
+ for (var i = array.length - 1; i > 0; i--) {
+ var j = Math.floor(Math.random() * (i + 1));
+ var temp = array[i];
+ array[i] = array[j];
+ array[j] = temp;
+ }
+ return array;
+};
+
+
+
+/* ====== THIRD PARTY ====== */
+
+// Shim for requestAnimationFrame
+// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
+// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
+// requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel
+// MIT license
+(function() {
+ var lastTime = 0;
+ var vendors = ['ms', 'moz', 'webkit', 'o'];
+ for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
+ window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
+ window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame']
+ || window[vendors[x]+'CancelRequestAnimationFrame'];
+ }
+
+ if (!window.requestAnimationFrame)
+ window.requestAnimationFrame = function(callback, element) {
+ var currTime = new Date().getTime();
+ var timeToCall = Math.max(0, 16 - (currTime - lastTime));
+ var id = window.setTimeout(function() { callback(currTime + timeToCall); },
+ timeToCall);
+ lastTime = currTime + timeToCall;
+ return id;
+ };
+
+ if (!window.cancelAnimationFrame)
+ window.cancelAnimationFrame = function(id) {
+ clearTimeout(id);
+ };
+}());