From 19eceac3f9f772678334ca7e49ab02b864293c83 Mon Sep 17 00:00:00 2001 From: Frederic Date: Tue, 20 Dec 2016 12:18:38 +0100 Subject: [PATCH] Initial commit. --- .gitignore | 2 + blayer-webif/blayerwebif.js | 152 ++++++ blayer-webif/index.html | 53 ++ blayer-webif/jquery-1.8.2.min.js | 2 + blayer-webif/jquery.js | 2 + blayer-webif/style.css | 87 +++ blayer.cfg | 17 + blayer.py | 428 +++++++++++++++ blayer.py.old | 379 ++++++++++++++ blmplay.py | 68 +++ blphub.py | 167 ++++++ blphub.sh | 4 + blpserver.py | 88 ++++ blup/BLP.py | 174 ++++++ blup/__init__.py | 3 + blup/animation.py | 344 ++++++++++++ blup/font.py | 97 ++++ blup/frame.py | 152 ++++++ blup/output.py | 444 ++++++++++++++++ colormarq.py | 24 + colortest.py | 44 ++ colorwischer.py | 34 ++ eq.py | 104 ++++ fire,py | 2 + fire.py | 63 +++ http2telnet.py | 84 +++ loadbbm.tmp | 43 ++ lugsaar | 23 + matrix.py | 13 + miniplayer.py | 183 +++++++ mrmcd13.bml | 873 +++++++++++++++++++++++++++++++ playlist.txt | 147 ++++++ playlist2.txt | 143 +++++ pong.py | 456 ++++++++++++++++ r0ketpong.py | 178 +++++++ randomPlayer.py | 4 + robot.bml | 28 + smileys.bml | 39 ++ smileys2.bml | 50 ++ staticSinePlasma.py | 135 +++++ staticSinePlasma_neopixel.py | 152 ++++++ test.txt | 1 + ttrs.py | 311 +++++++++++ writebml.py | 49 ++ 44 files changed, 5846 insertions(+) create mode 100644 .gitignore create mode 100644 blayer-webif/blayerwebif.js create mode 100644 blayer-webif/index.html create mode 100644 blayer-webif/jquery-1.8.2.min.js create mode 100644 blayer-webif/jquery.js create mode 100644 blayer-webif/style.css create mode 100644 blayer.cfg create mode 100644 blayer.py create mode 100644 blayer.py.old create mode 100644 blmplay.py create mode 100755 blphub.py create mode 100755 blphub.sh create mode 100644 blpserver.py create mode 100644 blup/BLP.py create mode 100644 blup/__init__.py create mode 100644 blup/animation.py create mode 100644 blup/font.py create mode 100644 blup/frame.py create mode 100644 blup/output.py create mode 100644 colormarq.py create mode 100644 colortest.py create mode 100644 colorwischer.py create mode 100644 eq.py create mode 100644 fire,py create mode 100644 fire.py create mode 100644 http2telnet.py create mode 100644 loadbbm.tmp create mode 100644 lugsaar create mode 100644 matrix.py create mode 100644 miniplayer.py create mode 100644 mrmcd13.bml create mode 100644 playlist.txt create mode 100644 playlist2.txt create mode 100644 pong.py create mode 100644 r0ketpong.py create mode 100644 randomPlayer.py create mode 100644 robot.bml create mode 100644 smileys.bml create mode 100644 smileys2.bml create mode 100644 staticSinePlasma.py create mode 100644 staticSinePlasma_neopixel.py create mode 100644 test.txt create mode 100755 ttrs.py create mode 100644 writebml.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a2a4827 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +blm/ + diff --git a/blayer-webif/blayerwebif.js b/blayer-webif/blayerwebif.js new file mode 100644 index 0000000..a888063 --- /dev/null +++ b/blayer-webif/blayerwebif.js @@ -0,0 +1,152 @@ + +SERVER_URL="http://volta:8080/do_request"; +UPLOAD_URL="http://volta:8080/do_upload"; + +function refreshStatus() { + + $.ajax({ + url: SERVER_URL, + dataType: "json", + type: "POST", + data: JSON.stringify({"cmd": "getcurrentfilename"}), + success: function(data) { + $.blayerwebif.newFilename = data.currentfilename; + if($.blayerwebif.oldFilename == $.blayerwebif.newFilename) + return; + $.blayerwebif.oldFilename = $.blayerwebif.newFilename; + + $("#tagtable").empty(); + $("#tagtable").append("filename"+$.blayerwebif.newFilename+""+key+""+value+""+value+""); + $("#filenames").append("
  • "+value+"
  • "); + + }); + $("#animationcount").text(data.allfilenames.length); + $(".playNextLink").click(function() { + linkNode = this; + $.ajax({ + url: SERVER_URL, + dataType: "json", + type: "POST", + data: JSON.stringify({"cmd": "playnext", "filename": $(this).text()}), + success: function(data) { + if(data.playnext == "ok") + item = $("queued..."); + else + item = $("queue full :("); + + $(linkNode).after(item); + $(item).delay(1000).fadeOut("slow"); + } + }); + }); + }, + }); +} + +function refreshStatusPeriodic() { + refreshStatus(); + setTimeout(refreshStatusPeriodic, 1000); +} + +$(document).ready(function() { + $.blayerwebif = {}; + $.blayerwebif.oldFilename = ""; + + setTimeout(refreshStatusPeriodic, 1000); + refreshFilenames(); + refreshStatus(); + + $("#uploadbutton").click(function() { + $.ajax({ + url: UPLOAD_URL, + type: "POST", + beforeSend: function() { + $("#uploadform").after("uploading..."); + }, + success: function() { + $("#uploadingnotification").text("done! :)").delay(1000).fadeOut("slow"); + refreshFilenames(); + }, + error: function() { + $("#uploadingnotification").text("failed! :(").delay(1000).fadeOut("slow"); + }, + contentType: false, + cache: false, + processData: false, + data: new FormData($("#uploadform")[0]) + }); + }); + + $("#next").click(function() { + $.ajax({ + url: SERVER_URL, + dataType: "json", + type: "POST", + data: JSON.stringify({"cmd": "next"}), + success: function() { + refreshStatus(); + } + }); + }); + + $("#pause").click(function() { + button = this; + $.ajax({ + url: SERVER_URL, + dataType: "json", + type: "POST", + data: JSON.stringify({"cmd": "togglepaused"}), + success: function(data) { + refreshStatus(); + + if(data.paused) + $(button).text("play"); + else + $(button).text("pause"); + } + }); + }); + + $("#filtertext").keyup(function() { + keyword = $("#filtertext").val().toLowerCase(); + $("#filenames li").each(function(key, value) { + + if($(value).children("a").text().toLowerCase().indexOf(keyword) >= 0) + $(value).show(); + else + $(value).hide(); + }); + }); +}); + + diff --git a/blayer-webif/index.html b/blayer-webif/index.html new file mode 100644 index 0000000..f32825f --- /dev/null +++ b/blayer-webif/index.html @@ -0,0 +1,53 @@ + + + blayer web interface + + + + + + + +
    +
    +

    control

    + +

    + + play / pause +

    +
    + +
    +

    upload

    +
    + + +
    +
    + +
    +

    current animation

    + + + + +
    + +
    +
    + +
    +
    +

    animation database

    +

    ### animations in total

    + +

    filter:

    + +
      +
    + +
    +
    + + diff --git a/blayer-webif/jquery-1.8.2.min.js b/blayer-webif/jquery-1.8.2.min.js new file mode 100644 index 0000000..f65cf1d --- /dev/null +++ b/blayer-webif/jquery-1.8.2.min.js @@ -0,0 +1,2 @@ +/*! jQuery v1.8.2 jquery.com | jquery.org/license */ +(function(a,b){function G(a){var b=F[a]={};return p.each(a.split(s),function(a,c){b[c]=!0}),b}function J(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(I,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:+d+""===d?+d:H.test(d)?p.parseJSON(d):d}catch(f){}p.data(a,c,d)}else d=b}return d}function K(a){var b;for(b in a){if(b==="data"&&p.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function ba(){return!1}function bb(){return!0}function bh(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function bi(a,b){do a=a[b];while(a&&a.nodeType!==1);return a}function bj(a,b,c){b=b||0;if(p.isFunction(b))return p.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return p.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=p.grep(a,function(a){return a.nodeType===1});if(be.test(b))return p.filter(b,d,!c);b=p.filter(b,d)}return p.grep(a,function(a,d){return p.inArray(a,b)>=0===c})}function bk(a){var b=bl.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function bC(a,b){return a.getElementsByTagName(b)[0]||a.appendChild(a.ownerDocument.createElement(b))}function bD(a,b){if(b.nodeType!==1||!p.hasData(a))return;var c,d,e,f=p._data(a),g=p._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;d").appendTo(e.body),c=b.css("display");b.remove();if(c==="none"||c===""){bI=e.body.appendChild(bI||p.extend(e.createElement("iframe"),{frameBorder:0,width:0,height:0}));if(!bJ||!bI.createElement)bJ=(bI.contentWindow||bI.contentDocument).document,bJ.write(""),bJ.close();b=bJ.body.appendChild(bJ.createElement(a)),c=bH(b,"display"),e.body.removeChild(bI)}return bS[a]=c,c}function ci(a,b,c,d){var e;if(p.isArray(b))p.each(b,function(b,e){c||ce.test(a)?d(a,e):ci(a+"["+(typeof e=="object"?b:"")+"]",e,c,d)});else if(!c&&p.type(b)==="object")for(e in b)ci(a+"["+e+"]",b[e],c,d);else d(a,b)}function cz(a){return function(b,c){typeof b!="string"&&(c=b,b="*");var d,e,f,g=b.toLowerCase().split(s),h=0,i=g.length;if(p.isFunction(c))for(;h)[^>]*$|#([\w\-]*)$)/,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^[\],:{}\s]*$/,x=/(?:^|:|,)(?:\s*\[)+/g,y=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,z=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,A=/^-ms-/,B=/-([\da-z])/gi,C=function(a,b){return(b+"").toUpperCase()},D=function(){e.addEventListener?(e.removeEventListener("DOMContentLoaded",D,!1),p.ready()):e.readyState==="complete"&&(e.detachEvent("onreadystatechange",D),p.ready())},E={};p.fn=p.prototype={constructor:p,init:function(a,c,d){var f,g,h,i;if(!a)return this;if(a.nodeType)return this.context=this[0]=a,this.length=1,this;if(typeof a=="string"){a.charAt(0)==="<"&&a.charAt(a.length-1)===">"&&a.length>=3?f=[null,a,null]:f=u.exec(a);if(f&&(f[1]||!c)){if(f[1])return c=c instanceof p?c[0]:c,i=c&&c.nodeType?c.ownerDocument||c:e,a=p.parseHTML(f[1],i,!0),v.test(f[1])&&p.isPlainObject(c)&&this.attr.call(a,c,!0),p.merge(this,a);g=e.getElementById(f[2]);if(g&&g.parentNode){if(g.id!==f[2])return d.find(a);this.length=1,this[0]=g}return this.context=e,this.selector=a,this}return!c||c.jquery?(c||d).find(a):this.constructor(c).find(a)}return p.isFunction(a)?d.ready(a):(a.selector!==b&&(this.selector=a.selector,this.context=a.context),p.makeArray(a,this))},selector:"",jquery:"1.8.2",length:0,size:function(){return this.length},toArray:function(){return k.call(this)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=p.merge(this.constructor(),a);return d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")"),d},each:function(a,b){return p.each(this,a,b)},ready:function(a){return p.ready.promise().done(a),this},eq:function(a){return a=+a,a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(k.apply(this,arguments),"slice",k.call(arguments).join(","))},map:function(a){return this.pushStack(p.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:j,sort:[].sort,splice:[].splice},p.fn.init.prototype=p.fn,p.extend=p.fn.extend=function(){var a,c,d,e,f,g,h=arguments[0]||{},i=1,j=arguments.length,k=!1;typeof h=="boolean"&&(k=h,h=arguments[1]||{},i=2),typeof h!="object"&&!p.isFunction(h)&&(h={}),j===i&&(h=this,--i);for(;i0)return;d.resolveWith(e,[p]),p.fn.trigger&&p(e).trigger("ready").off("ready")},isFunction:function(a){return p.type(a)==="function"},isArray:Array.isArray||function(a){return p.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):E[m.call(a)]||"object"},isPlainObject:function(a){if(!a||p.type(a)!=="object"||a.nodeType||p.isWindow(a))return!1;try{if(a.constructor&&!n.call(a,"constructor")&&!n.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||n.call(a,d)},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},error:function(a){throw new Error(a)},parseHTML:function(a,b,c){var d;return!a||typeof a!="string"?null:(typeof b=="boolean"&&(c=b,b=0),b=b||e,(d=v.exec(a))?[b.createElement(d[1])]:(d=p.buildFragment([a],b,c?null:[]),p.merge([],(d.cacheable?p.clone(d.fragment):d.fragment).childNodes)))},parseJSON:function(b){if(!b||typeof b!="string")return null;b=p.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(w.test(b.replace(y,"@").replace(z,"]").replace(x,"")))return(new Function("return "+b))();p.error("Invalid JSON: "+b)},parseXML:function(c){var d,e;if(!c||typeof c!="string")return null;try{a.DOMParser?(e=new DOMParser,d=e.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(f){d=b}return(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&p.error("Invalid XML: "+c),d},noop:function(){},globalEval:function(b){b&&r.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(A,"ms-").replace(B,C)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,c,d){var e,f=0,g=a.length,h=g===b||p.isFunction(a);if(d){if(h){for(e in a)if(c.apply(a[e],d)===!1)break}else for(;f0&&a[0]&&a[i-1]||i===0||p.isArray(a));if(j)for(;h-1)i.splice(c,1),e&&(c<=g&&g--,c<=h&&h--)}),this},has:function(a){return p.inArray(a,i)>-1},empty:function(){return i=[],this},disable:function(){return i=j=c=b,this},disabled:function(){return!i},lock:function(){return j=b,c||l.disable(),this},locked:function(){return!j},fireWith:function(a,b){return b=b||[],b=[a,b.slice?b.slice():b],i&&(!d||j)&&(e?j.push(b):k(b)),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!d}};return l},p.extend({Deferred:function(a){var b=[["resolve","done",p.Callbacks("once memory"),"resolved"],["reject","fail",p.Callbacks("once memory"),"rejected"],["notify","progress",p.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return p.Deferred(function(c){p.each(b,function(b,d){var f=d[0],g=a[b];e[d[1]](p.isFunction(g)?function(){var a=g.apply(this,arguments);a&&p.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f+"With"](this===e?c:this,[a])}:c[f])}),a=null}).promise()},promise:function(a){return a!=null?p.extend(a,d):d}},e={};return d.pipe=d.then,p.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[a^1][2].disable,b[2][2].lock),e[f[0]]=g.fire,e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=k.call(arguments),d=c.length,e=d!==1||a&&p.isFunction(a.promise)?d:0,f=e===1?a:p.Deferred(),g=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?k.call(arguments):d,c===h?f.notifyWith(b,c):--e||f.resolveWith(b,c)}},h,i,j;if(d>1){h=new Array(d),i=new Array(d),j=new Array(d);for(;b
    a",c=n.getElementsByTagName("*"),d=n.getElementsByTagName("a")[0],d.style.cssText="top:1px;float:left;opacity:.5";if(!c||!c.length)return{};f=e.createElement("select"),g=f.appendChild(e.createElement("option")),h=n.getElementsByTagName("input")[0],b={leadingWhitespace:n.firstChild.nodeType===3,tbody:!n.getElementsByTagName("tbody").length,htmlSerialize:!!n.getElementsByTagName("link").length,style:/top/.test(d.getAttribute("style")),hrefNormalized:d.getAttribute("href")==="/a",opacity:/^0.5/.test(d.style.opacity),cssFloat:!!d.style.cssFloat,checkOn:h.value==="on",optSelected:g.selected,getSetAttribute:n.className!=="t",enctype:!!e.createElement("form").enctype,html5Clone:e.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",boxModel:e.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},h.checked=!0,b.noCloneChecked=h.cloneNode(!0).checked,f.disabled=!0,b.optDisabled=!g.disabled;try{delete n.test}catch(o){b.deleteExpando=!1}!n.addEventListener&&n.attachEvent&&n.fireEvent&&(n.attachEvent("onclick",m=function(){b.noCloneEvent=!1}),n.cloneNode(!0).fireEvent("onclick"),n.detachEvent("onclick",m)),h=e.createElement("input"),h.value="t",h.setAttribute("type","radio"),b.radioValue=h.value==="t",h.setAttribute("checked","checked"),h.setAttribute("name","t"),n.appendChild(h),i=e.createDocumentFragment(),i.appendChild(n.lastChild),b.checkClone=i.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=h.checked,i.removeChild(h),i.appendChild(n);if(n.attachEvent)for(k in{submit:!0,change:!0,focusin:!0})j="on"+k,l=j in n,l||(n.setAttribute(j,"return;"),l=typeof n[j]=="function"),b[k+"Bubbles"]=l;return p(function(){var c,d,f,g,h="padding:0;margin:0;border:0;display:block;overflow:hidden;",i=e.getElementsByTagName("body")[0];if(!i)return;c=e.createElement("div"),c.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",i.insertBefore(c,i.firstChild),d=e.createElement("div"),c.appendChild(d),d.innerHTML="
    t
    ",f=d.getElementsByTagName("td"),f[0].style.cssText="padding:0;margin:0;border:0;display:none",l=f[0].offsetHeight===0,f[0].style.display="",f[1].style.display="none",b.reliableHiddenOffsets=l&&f[0].offsetHeight===0,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",b.boxSizing=d.offsetWidth===4,b.doesNotIncludeMarginInBodyOffset=i.offsetTop!==1,a.getComputedStyle&&(b.pixelPosition=(a.getComputedStyle(d,null)||{}).top!=="1%",b.boxSizingReliable=(a.getComputedStyle(d,null)||{width:"4px"}).width==="4px",g=e.createElement("div"),g.style.cssText=d.style.cssText=h,g.style.marginRight=g.style.width="0",d.style.width="1px",d.appendChild(g),b.reliableMarginRight=!parseFloat((a.getComputedStyle(g,null)||{}).marginRight)),typeof d.style.zoom!="undefined"&&(d.innerHTML="",d.style.cssText=h+"width:1px;padding:1px;display:inline;zoom:1",b.inlineBlockNeedsLayout=d.offsetWidth===3,d.style.display="block",d.style.overflow="visible",d.innerHTML="
    ",d.firstChild.style.width="5px",b.shrinkWrapBlocks=d.offsetWidth!==3,c.style.zoom=1),i.removeChild(c),c=d=f=g=null}),i.removeChild(n),c=d=f=g=h=i=n=null,b}();var H=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,I=/([A-Z])/g;p.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(p.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){return a=a.nodeType?p.cache[a[p.expando]]:a[p.expando],!!a&&!K(a)},data:function(a,c,d,e){if(!p.acceptData(a))return;var f,g,h=p.expando,i=typeof c=="string",j=a.nodeType,k=j?p.cache:a,l=j?a[h]:a[h]&&h;if((!l||!k[l]||!e&&!k[l].data)&&i&&d===b)return;l||(j?a[h]=l=p.deletedIds.pop()||p.guid++:l=h),k[l]||(k[l]={},j||(k[l].toJSON=p.noop));if(typeof c=="object"||typeof c=="function")e?k[l]=p.extend(k[l],c):k[l].data=p.extend(k[l].data,c);return f=k[l],e||(f.data||(f.data={}),f=f.data),d!==b&&(f[p.camelCase(c)]=d),i?(g=f[c],g==null&&(g=f[p.camelCase(c)])):g=f,g},removeData:function(a,b,c){if(!p.acceptData(a))return;var d,e,f,g=a.nodeType,h=g?p.cache:a,i=g?a[p.expando]:p.expando;if(!h[i])return;if(b){d=c?h[i]:h[i].data;if(d){p.isArray(b)||(b in d?b=[b]:(b=p.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,f=b.length;e1,null,!1))},removeData:function(a){return this.each(function(){p.removeData(this,a)})}}),p.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=p._data(a,b),c&&(!d||p.isArray(c)?d=p._data(a,b,p.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=p.queue(a,b),d=c.length,e=c.shift(),f=p._queueHooks(a,b),g=function(){p.dequeue(a,b)};e==="inprogress"&&(e=c.shift(),d--),e&&(b==="fx"&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return p._data(a,c)||p._data(a,c,{empty:p.Callbacks("once memory").add(function(){p.removeData(a,b+"queue",!0),p.removeData(a,c,!0)})})}}),p.fn.extend({queue:function(a,c){var d=2;return typeof a!="string"&&(c=a,a="fx",d--),arguments.length1)},removeAttr:function(a){return this.each(function(){p.removeAttr(this,a)})},prop:function(a,b){return p.access(this,p.prop,a,b,arguments.length>1)},removeProp:function(a){return a=p.propFix[a]||a,this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,f,g,h;if(p.isFunction(a))return this.each(function(b){p(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(s);for(c=0,d=this.length;c=0)d=d.replace(" "+c[f]+" "," ");e.className=a?p.trim(d):""}}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";return p.isFunction(a)?this.each(function(c){p(this).toggleClass(a.call(this,c,this.className,b),b)}):this.each(function(){if(c==="string"){var e,f=0,g=p(this),h=b,i=a.split(s);while(e=i[f++])h=d?h:!g.hasClass(e),g[h?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&p._data(this,"__className__",this.className),this.className=this.className||a===!1?"":p._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c=0)return!0;return!1},val:function(a){var c,d,e,f=this[0];if(!arguments.length){if(f)return c=p.valHooks[f.type]||p.valHooks[f.nodeName.toLowerCase()],c&&"get"in c&&(d=c.get(f,"value"))!==b?d:(d=f.value,typeof d=="string"?d.replace(P,""):d==null?"":d);return}return e=p.isFunction(a),this.each(function(d){var f,g=p(this);if(this.nodeType!==1)return;e?f=a.call(this,d,g.val()):f=a,f==null?f="":typeof f=="number"?f+="":p.isArray(f)&&(f=p.map(f,function(a){return a==null?"":a+""})),c=p.valHooks[this.type]||p.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,f,"value")===b)this.value=f})}}),p.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,f=a.selectedIndex,g=[],h=a.options,i=a.type==="select-one";if(f<0)return null;c=i?f:0,d=i?f+1:h.length;for(;c=0}),c.length||(a.selectedIndex=-1),c}}},attrFn:{},attr:function(a,c,d,e){var f,g,h,i=a.nodeType;if(!a||i===3||i===8||i===2)return;if(e&&p.isFunction(p.fn[c]))return p(a)[c](d);if(typeof a.getAttribute=="undefined")return p.prop(a,c,d);h=i!==1||!p.isXMLDoc(a),h&&(c=c.toLowerCase(),g=p.attrHooks[c]||(T.test(c)?M:L));if(d!==b){if(d===null){p.removeAttr(a,c);return}return g&&"set"in g&&h&&(f=g.set(a,d,c))!==b?f:(a.setAttribute(c,d+""),d)}return g&&"get"in g&&h&&(f=g.get(a,c))!==null?f:(f=a.getAttribute(c),f===null?b:f)},removeAttr:function(a,b){var c,d,e,f,g=0;if(b&&a.nodeType===1){d=b.split(s);for(;g=0}})});var V=/^(?:textarea|input|select)$/i,W=/^([^\.]*|)(?:\.(.+)|)$/,X=/(?:^|\s)hover(\.\S+|)\b/,Y=/^key/,Z=/^(?:mouse|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=function(a){return p.event.special.hover?a:a.replace(X,"mouseenter$1 mouseleave$1")};p.event={add:function(a,c,d,e,f){var g,h,i,j,k,l,m,n,o,q,r;if(a.nodeType===3||a.nodeType===8||!c||!d||!(g=p._data(a)))return;d.handler&&(o=d,d=o.handler,f=o.selector),d.guid||(d.guid=p.guid++),i=g.events,i||(g.events=i={}),h=g.handle,h||(g.handle=h=function(a){return typeof p!="undefined"&&(!a||p.event.triggered!==a.type)?p.event.dispatch.apply(h.elem,arguments):b},h.elem=a),c=p.trim(_(c)).split(" ");for(j=0;j=0&&(s=s.slice(0,-1),i=!0),s.indexOf(".")>=0&&(t=s.split("."),s=t.shift(),t.sort());if((!f||p.event.customEvent[s])&&!p.event.global[s])return;c=typeof c=="object"?c[p.expando]?c:new p.Event(s,c):new p.Event(s),c.type=s,c.isTrigger=!0,c.exclusive=i,c.namespace=t.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+t.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,m=s.indexOf(":")<0?"on"+s:"";if(!f){h=p.cache;for(j in h)h[j].events&&h[j].events[s]&&p.event.trigger(c,d,h[j].handle.elem,!0);return}c.result=b,c.target||(c.target=f),d=d!=null?p.makeArray(d):[],d.unshift(c),n=p.event.special[s]||{};if(n.trigger&&n.trigger.apply(f,d)===!1)return;q=[[f,n.bindType||s]];if(!g&&!n.noBubble&&!p.isWindow(f)){r=n.delegateType||s,k=$.test(r+s)?f:f.parentNode;for(l=f;k;k=k.parentNode)q.push([k,r]),l=k;l===(f.ownerDocument||e)&&q.push([l.defaultView||l.parentWindow||a,r])}for(j=0;j=0:p.find(m,this,null,[f]).length),h[m]&&j.push(l);j.length&&u.push({elem:f,matches:j})}o.length>q&&u.push({elem:this,matches:o.slice(q)});for(d=0;d0?this.on(b,null,a,c):this.trigger(b)},Y.test(b)&&(p.event.fixHooks[b]=p.event.keyHooks),Z.test(b)&&(p.event.fixHooks[b]=p.event.mouseHooks)}),function(a,b){function bc(a,b,c,d){c=c||[],b=b||r;var e,f,i,j,k=b.nodeType;if(!a||typeof a!="string")return c;if(k!==1&&k!==9)return[];i=g(b);if(!i&&!d)if(e=P.exec(a))if(j=e[1]){if(k===9){f=b.getElementById(j);if(!f||!f.parentNode)return c;if(f.id===j)return c.push(f),c}else if(b.ownerDocument&&(f=b.ownerDocument.getElementById(j))&&h(b,f)&&f.id===j)return c.push(f),c}else{if(e[2])return w.apply(c,x.call(b.getElementsByTagName(a),0)),c;if((j=e[3])&&_&&b.getElementsByClassName)return w.apply(c,x.call(b.getElementsByClassName(j),0)),c}return bp(a.replace(L,"$1"),b,c,d,i)}function bd(a){return function(b){var c=b.nodeName.toLowerCase();return c==="input"&&b.type===a}}function be(a){return function(b){var c=b.nodeName.toLowerCase();return(c==="input"||c==="button")&&b.type===a}}function bf(a){return z(function(b){return b=+b,z(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function bg(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}function bh(a,b){var c,d,f,g,h,i,j,k=C[o][a];if(k)return b?0:k.slice(0);h=a,i=[],j=e.preFilter;while(h){if(!c||(d=M.exec(h)))d&&(h=h.slice(d[0].length)),i.push(f=[]);c=!1;if(d=N.exec(h))f.push(c=new q(d.shift())),h=h.slice(c.length),c.type=d[0].replace(L," ");for(g in e.filter)(d=W[g].exec(h))&&(!j[g]||(d=j[g](d,r,!0)))&&(f.push(c=new q(d.shift())),h=h.slice(c.length),c.type=g,c.matches=d);if(!c)break}return b?h.length:h?bc.error(a):C(a,i).slice(0)}function bi(a,b,d){var e=b.dir,f=d&&b.dir==="parentNode",g=u++;return b.first?function(b,c,d){while(b=b[e])if(f||b.nodeType===1)return a(b,c,d)}:function(b,d,h){if(!h){var i,j=t+" "+g+" ",k=j+c;while(b=b[e])if(f||b.nodeType===1){if((i=b[o])===k)return b.sizset;if(typeof i=="string"&&i.indexOf(j)===0){if(b.sizset)return b}else{b[o]=k;if(a(b,d,h))return b.sizset=!0,b;b.sizset=!1}}}else while(b=b[e])if(f||b.nodeType===1)if(a(b,d,h))return b}}function bj(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function bk(a,b,c,d,e){var f,g=[],h=0,i=a.length,j=b!=null;for(;h-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==l)||((b=c).nodeType?j(a,c,d):k(a,c,d))}];for(;i1&&bj(m),i>1&&a.slice(0,i-1).join("").replace(L,"$1"),c,i0,f=a.length>0,g=function(h,i,j,k,m){var n,o,p,q=[],s=0,u="0",x=h&&[],y=m!=null,z=l,A=h||f&&e.find.TAG("*",m&&i.parentNode||i),B=t+=z==null?1:Math.E;y&&(l=i!==r&&i,c=g.el);for(;(n=A[u])!=null;u++){if(f&&n){for(o=0;p=a[o];o++)if(p(n,i,j)){k.push(n);break}y&&(t=B,c=++g.el)}d&&((n=!p&&n)&&s--,h&&x.push(n))}s+=u;if(d&&u!==s){for(o=0;p=b[o];o++)p(x,q,i,j);if(h){if(s>0)while(u--)!x[u]&&!q[u]&&(q[u]=v.call(k));q=bk(q)}w.apply(k,q),y&&!h&&q.length>0&&s+b.length>1&&bc.uniqueSort(k)}return y&&(t=B,l=z),x};return g.el=0,d?z(g):g}function bo(a,b,c,d){var e=0,f=b.length;for(;e2&&(j=h[0]).type==="ID"&&b.nodeType===9&&!f&&e.relative[h[1].type]){b=e.find.ID(j.matches[0].replace(V,""),b,f)[0];if(!b)return c;a=a.slice(h.shift().length)}for(g=W.POS.test(a)?-1:h.length-1;g>=0;g--){j=h[g];if(e.relative[k=j.type])break;if(l=e.find[k])if(d=l(j.matches[0].replace(V,""),R.test(h[0].type)&&b.parentNode||b,f)){h.splice(g,1),a=d.length&&h.join("");if(!a)return w.apply(c,x.call(d,0)),c;break}}}return i(a,m)(d,b,f,c,R.test(a)),c}function bq(){}var c,d,e,f,g,h,i,j,k,l,m=!0,n="undefined",o=("sizcache"+Math.random()).replace(".",""),q=String,r=a.document,s=r.documentElement,t=0,u=0,v=[].pop,w=[].push,x=[].slice,y=[].indexOf||function(a){var b=0,c=this.length;for(;be.cacheLength&&delete a[b.shift()],a[c]=d},a)},B=A(),C=A(),D=A(),E="[\\x20\\t\\r\\n\\f]",F="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",G=F.replace("w","w#"),H="([*^$|!~]?=)",I="\\["+E+"*("+F+")"+E+"*(?:"+H+E+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+G+")|)|)"+E+"*\\]",J=":("+F+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:"+I+")|[^:]|\\\\.)*|.*))\\)|)",K=":(even|odd|eq|gt|lt|nth|first|last)(?:\\("+E+"*((?:-\\d)?\\d*)"+E+"*\\)|)(?=[^-]|$)",L=new RegExp("^"+E+"+|((?:^|[^\\\\])(?:\\\\.)*)"+E+"+$","g"),M=new RegExp("^"+E+"*,"+E+"*"),N=new RegExp("^"+E+"*([\\x20\\t\\r\\n\\f>+~])"+E+"*"),O=new RegExp(J),P=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,Q=/^:not/,R=/[\x20\t\r\n\f]*[+~]/,S=/:not\($/,T=/h\d/i,U=/input|select|textarea|button/i,V=/\\(?!\\)/g,W={ID:new RegExp("^#("+F+")"),CLASS:new RegExp("^\\.("+F+")"),NAME:new RegExp("^\\[name=['\"]?("+F+")['\"]?\\]"),TAG:new RegExp("^("+F.replace("w","w*")+")"),ATTR:new RegExp("^"+I),PSEUDO:new RegExp("^"+J),POS:new RegExp(K,"i"),CHILD:new RegExp("^:(only|nth|first|last)-child(?:\\("+E+"*(even|odd|(([+-]|)(\\d*)n|)"+E+"*(?:([+-]|)"+E+"*(\\d+)|))"+E+"*\\)|)","i"),needsContext:new RegExp("^"+E+"*[>+~]|"+K,"i")},X=function(a){var b=r.createElement("div");try{return a(b)}catch(c){return!1}finally{b=null}},Y=X(function(a){return a.appendChild(r.createComment("")),!a.getElementsByTagName("*").length}),Z=X(function(a){return a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!==n&&a.firstChild.getAttribute("href")==="#"}),$=X(function(a){a.innerHTML="";var b=typeof a.lastChild.getAttribute("multiple");return b!=="boolean"&&b!=="string"}),_=X(function(a){return a.innerHTML="",!a.getElementsByClassName||!a.getElementsByClassName("e").length?!1:(a.lastChild.className="e",a.getElementsByClassName("e").length===2)}),ba=X(function(a){a.id=o+0,a.innerHTML="
    ",s.insertBefore(a,s.firstChild);var b=r.getElementsByName&&r.getElementsByName(o).length===2+r.getElementsByName(o+0).length;return d=!r.getElementById(o),s.removeChild(a),b});try{x.call(s.childNodes,0)[0].nodeType}catch(bb){x=function(a){var b,c=[];for(;b=this[a];a++)c.push(b);return c}}bc.matches=function(a,b){return bc(a,null,null,b)},bc.matchesSelector=function(a,b){return bc(b,null,null,[a]).length>0},f=bc.getText=function(a){var b,c="",d=0,e=a.nodeType;if(e){if(e===1||e===9||e===11){if(typeof a.textContent=="string")return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=f(a)}else if(e===3||e===4)return a.nodeValue}else for(;b=a[d];d++)c+=f(b);return c},g=bc.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?b.nodeName!=="HTML":!1},h=bc.contains=s.contains?function(a,b){var c=a.nodeType===9?a.documentElement:a,d=b&&b.parentNode;return a===d||!!(d&&d.nodeType===1&&c.contains&&c.contains(d))}:s.compareDocumentPosition?function(a,b){return b&&!!(a.compareDocumentPosition(b)&16)}:function(a,b){while(b=b.parentNode)if(b===a)return!0;return!1},bc.attr=function(a,b){var c,d=g(a);return d||(b=b.toLowerCase()),(c=e.attrHandle[b])?c(a):d||$?a.getAttribute(b):(c=a.getAttributeNode(b),c?typeof a[b]=="boolean"?a[b]?b:null:c.specified?c.value:null:null)},e=bc.selectors={cacheLength:50,createPseudo:z,match:W,attrHandle:Z?{}:{href:function(a){return a.getAttribute("href",2)},type:function(a){return a.getAttribute("type")}},find:{ID:d?function(a,b,c){if(typeof b.getElementById!==n&&!c){var d=b.getElementById(a);return d&&d.parentNode?[d]:[]}}:function(a,c,d){if(typeof c.getElementById!==n&&!d){var e=c.getElementById(a);return e?e.id===a||typeof e.getAttributeNode!==n&&e.getAttributeNode("id").value===a?[e]:b:[]}},TAG:Y?function(a,b){if(typeof b.getElementsByTagName!==n)return b.getElementsByTagName(a)}:function(a,b){var c=b.getElementsByTagName(a);if(a==="*"){var d,e=[],f=0;for(;d=c[f];f++)d.nodeType===1&&e.push(d);return e}return c},NAME:ba&&function(a,b){if(typeof b.getElementsByName!==n)return b.getElementsByName(name)},CLASS:_&&function(a,b,c){if(typeof b.getElementsByClassName!==n&&!c)return b.getElementsByClassName(a)}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(V,""),a[3]=(a[4]||a[5]||"").replace(V,""),a[2]==="~="&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),a[1]==="nth"?(a[2]||bc.error(a[0]),a[3]=+(a[3]?a[4]+(a[5]||1):2*(a[2]==="even"||a[2]==="odd")),a[4]=+(a[6]+a[7]||a[2]==="odd")):a[2]&&bc.error(a[0]),a},PSEUDO:function(a){var b,c;if(W.CHILD.test(a[0]))return null;if(a[3])a[2]=a[3];else if(b=a[4])O.test(b)&&(c=bh(b,!0))&&(c=b.indexOf(")",b.length-c)-b.length)&&(b=b.slice(0,c),a[0]=a[0].slice(0,c)),a[2]=b;return a.slice(0,3)}},filter:{ID:d?function(a){return a=a.replace(V,""),function(b){return b.getAttribute("id")===a}}:function(a){return a=a.replace(V,""),function(b){var c=typeof b.getAttributeNode!==n&&b.getAttributeNode("id");return c&&c.value===a}},TAG:function(a){return a==="*"?function(){return!0}:(a=a.replace(V,"").toLowerCase(),function(b){return b.nodeName&&b.nodeName.toLowerCase()===a})},CLASS:function(a){var b=B[o][a];return b||(b=B(a,new RegExp("(^|"+E+")"+a+"("+E+"|$)"))),function(a){return b.test(a.className||typeof a.getAttribute!==n&&a.getAttribute("class")||"")}},ATTR:function(a,b,c){return function(d,e){var f=bc.attr(d,a);return f==null?b==="!=":b?(f+="",b==="="?f===c:b==="!="?f!==c:b==="^="?c&&f.indexOf(c)===0:b==="*="?c&&f.indexOf(c)>-1:b==="$="?c&&f.substr(f.length-c.length)===c:b==="~="?(" "+f+" ").indexOf(c)>-1:b==="|="?f===c||f.substr(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d){return a==="nth"?function(a){var b,e,f=a.parentNode;if(c===1&&d===0)return!0;if(f){e=0;for(b=f.firstChild;b;b=b.nextSibling)if(b.nodeType===1){e++;if(a===b)break}}return e-=d,e===c||e%c===0&&e/c>=0}:function(b){var c=b;switch(a){case"only":case"first":while(c=c.previousSibling)if(c.nodeType===1)return!1;if(a==="first")return!0;c=b;case"last":while(c=c.nextSibling)if(c.nodeType===1)return!1;return!0}}},PSEUDO:function(a,b){var c,d=e.pseudos[a]||e.setFilters[a.toLowerCase()]||bc.error("unsupported pseudo: "+a);return d[o]?d(b):d.length>1?(c=[a,a,"",b],e.setFilters.hasOwnProperty(a.toLowerCase())?z(function(a,c){var e,f=d(a,b),g=f.length;while(g--)e=y.call(a,f[g]),a[e]=!(c[e]=f[g])}):function(a){return d(a,0,c)}):d}},pseudos:{not:z(function(a){var b=[],c=[],d=i(a.replace(L,"$1"));return d[o]?z(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)if(f=g[h])a[h]=!(b[h]=f)}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:z(function(a){return function(b){return bc(a,b).length>0}}),contains:z(function(a){return function(b){return(b.textContent||b.innerText||f(b)).indexOf(a)>-1}}),enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&!!a.checked||b==="option"&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},parent:function(a){return!e.pseudos.empty(a)},empty:function(a){var b;a=a.firstChild;while(a){if(a.nodeName>"@"||(b=a.nodeType)===3||b===4)return!1;a=a.nextSibling}return!0},header:function(a){return T.test(a.nodeName)},text:function(a){var b,c;return a.nodeName.toLowerCase()==="input"&&(b=a.type)==="text"&&((c=a.getAttribute("type"))==null||c.toLowerCase()===b)},radio:bd("radio"),checkbox:bd("checkbox"),file:bd("file"),password:bd("password"),image:bd("image"),submit:be("submit"),reset:be("reset"),button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&a.type==="button"||b==="button"},input:function(a){return U.test(a.nodeName)},focus:function(a){var b=a.ownerDocument;return a===b.activeElement&&(!b.hasFocus||b.hasFocus())&&(!!a.type||!!a.href)},active:function(a){return a===a.ownerDocument.activeElement},first:bf(function(a,b,c){return[0]}),last:bf(function(a,b,c){return[b-1]}),eq:bf(function(a,b,c){return[c<0?c+b:c]}),even:bf(function(a,b,c){for(var d=0;d=0;)a.push(d);return a}),gt:bf(function(a,b,c){for(var d=c<0?c+b:c;++d",a.querySelectorAll("[selected]").length||e.push("\\["+E+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),a.querySelectorAll(":checked").length||e.push(":checked")}),X(function(a){a.innerHTML="

    ",a.querySelectorAll("[test^='']").length&&e.push("[*^$]="+E+"*(?:\"\"|'')"),a.innerHTML="",a.querySelectorAll(":enabled").length||e.push(":enabled",":disabled")}),e=new RegExp(e.join("|")),bp=function(a,d,f,g,h){if(!g&&!h&&(!e||!e.test(a))){var i,j,k=!0,l=o,m=d,n=d.nodeType===9&&a;if(d.nodeType===1&&d.nodeName.toLowerCase()!=="object"){i=bh(a),(k=d.getAttribute("id"))?l=k.replace(c,"\\$&"):d.setAttribute("id",l),l="[id='"+l+"'] ",j=i.length;while(j--)i[j]=l+i[j].join("");m=R.test(a)&&d.parentNode||d,n=i.join(",")}if(n)try{return w.apply(f,x.call(m.querySelectorAll(n),0)),f}catch(p){}finally{k||d.removeAttribute("id")}}return b(a,d,f,g,h)},h&&(X(function(b){a=h.call(b,"div");try{h.call(b,"[test!='']:sizzle"),f.push("!=",J)}catch(c){}}),f=new RegExp(f.join("|")),bc.matchesSelector=function(b,c){c=c.replace(d,"='$1']");if(!g(b)&&!f.test(c)&&(!e||!e.test(c)))try{var i=h.call(b,c);if(i||a||b.document&&b.document.nodeType!==11)return i}catch(j){}return bc(c,null,null,[b]).length>0})}(),e.pseudos.nth=e.pseudos.eq,e.filters=bq.prototype=e.pseudos,e.setFilters=new bq,bc.attr=p.attr,p.find=bc,p.expr=bc.selectors,p.expr[":"]=p.expr.pseudos,p.unique=bc.uniqueSort,p.text=bc.getText,p.isXMLDoc=bc.isXML,p.contains=bc.contains}(a);var bc=/Until$/,bd=/^(?:parents|prev(?:Until|All))/,be=/^.[^:#\[\.,]*$/,bf=p.expr.match.needsContext,bg={children:!0,contents:!0,next:!0,prev:!0};p.fn.extend({find:function(a){var b,c,d,e,f,g,h=this;if(typeof a!="string")return p(a).filter(function(){for(b=0,c=h.length;b0)for(e=d;e=0:p.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c,d=0,e=this.length,f=[],g=bf.test(a)||typeof a!="string"?p(a,b||this.context):0;for(;d-1:p.find.matchesSelector(c,a)){f.push(c);break}c=c.parentNode}}return f=f.length>1?p.unique(f):f,this.pushStack(f,"closest",a)},index:function(a){return a?typeof a=="string"?p.inArray(this[0],p(a)):p.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(a,b){var c=typeof a=="string"?p(a,b):p.makeArray(a&&a.nodeType?[a]:a),d=p.merge(this.get(),c);return this.pushStack(bh(c[0])||bh(d[0])?d:p.unique(d))},addBack:function(a){return this.add(a==null?this.prevObject:this.prevObject.filter(a))}}),p.fn.andSelf=p.fn.addBack,p.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return p.dir(a,"parentNode")},parentsUntil:function(a,b,c){return p.dir(a,"parentNode",c)},next:function(a){return bi(a,"nextSibling")},prev:function(a){return bi(a,"previousSibling")},nextAll:function(a){return p.dir(a,"nextSibling")},prevAll:function(a){return p.dir(a,"previousSibling")},nextUntil:function(a,b,c){return p.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return p.dir(a,"previousSibling",c)},siblings:function(a){return p.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return p.sibling(a.firstChild)},contents:function(a){return p.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:p.merge([],a.childNodes)}},function(a,b){p.fn[a]=function(c,d){var e=p.map(this,b,c);return bc.test(a)||(d=c),d&&typeof d=="string"&&(e=p.filter(d,e)),e=this.length>1&&!bg[a]?p.unique(e):e,this.length>1&&bd.test(a)&&(e=e.reverse()),this.pushStack(e,a,k.call(arguments).join(","))}}),p.extend({filter:function(a,b,c){return c&&(a=":not("+a+")"),b.length===1?p.find.matchesSelector(b[0],a)?[b[0]]:[]:p.find.matches(a,b)},dir:function(a,c,d){var e=[],f=a[c];while(f&&f.nodeType!==9&&(d===b||f.nodeType!==1||!p(f).is(d)))f.nodeType===1&&e.push(f),f=f[c];return e},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var bl="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",bm=/ jQuery\d+="(?:null|\d+)"/g,bn=/^\s+/,bo=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bp=/<([\w:]+)/,bq=/]","i"),bv=/^(?:checkbox|radio)$/,bw=/checked\s*(?:[^=]|=\s*.checked.)/i,bx=/\/(java|ecma)script/i,by=/^\s*\s*$/g,bz={option:[1,""],legend:[1,"
    ","
    "],thead:[1,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],col:[2,"","
    "],area:[1,"",""],_default:[0,"",""]},bA=bk(e),bB=bA.appendChild(e.createElement("div"));bz.optgroup=bz.option,bz.tbody=bz.tfoot=bz.colgroup=bz.caption=bz.thead,bz.th=bz.td,p.support.htmlSerialize||(bz._default=[1,"X
    ","
    "]),p.fn.extend({text:function(a){return p.access(this,function(a){return a===b?p.text(this):this.empty().append((this[0]&&this[0].ownerDocument||e).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(p.isFunction(a))return this.each(function(b){p(this).wrapAll(a.call(this,b))});if(this[0]){var b=p(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return p.isFunction(a)?this.each(function(b){p(this).wrapInner(a.call(this,b))}):this.each(function(){var b=p(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=p.isFunction(a);return this.each(function(c){p(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){p.nodeName(this,"body")||p(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(a,this.firstChild)})},before:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(a,this),"before",this.selector)}},after:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(this,a),"after",this.selector)}},remove:function(a,b){var c,d=0;for(;(c=this[d])!=null;d++)if(!a||p.filter(a,[c]).length)!b&&c.nodeType===1&&(p.cleanData(c.getElementsByTagName("*")),p.cleanData([c])),c.parentNode&&c.parentNode.removeChild(c);return this},empty:function(){var a,b=0;for(;(a=this[b])!=null;b++){a.nodeType===1&&p.cleanData(a.getElementsByTagName("*"));while(a.firstChild)a.removeChild(a.firstChild)}return this},clone:function(a,b){return a=a==null?!1:a,b=b==null?a:b,this.map(function(){return p.clone(this,a,b)})},html:function(a){return p.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(bm,""):b;if(typeof a=="string"&&!bs.test(a)&&(p.support.htmlSerialize||!bu.test(a))&&(p.support.leadingWhitespace||!bn.test(a))&&!bz[(bp.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(bo,"<$1>");try{for(;d1&&typeof j=="string"&&bw.test(j))return this.each(function(){p(this).domManip(a,c,d)});if(p.isFunction(j))return this.each(function(e){var f=p(this);a[0]=j.call(this,e,c?f.html():b),f.domManip(a,c,d)});if(this[0]){e=p.buildFragment(a,this,k),g=e.fragment,f=g.firstChild,g.childNodes.length===1&&(g=f);if(f){c=c&&p.nodeName(f,"tr");for(h=e.cacheable||l-1;i0?this.clone(!0):this).get(),p(g[e])[b](d),f=f.concat(d);return this.pushStack(f,a,g.selector)}}),p.extend({clone:function(a,b,c){var d,e,f,g;p.support.html5Clone||p.isXMLDoc(a)||!bu.test("<"+a.nodeName+">")?g=a.cloneNode(!0):(bB.innerHTML=a.outerHTML,bB.removeChild(g=bB.firstChild));if((!p.support.noCloneEvent||!p.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!p.isXMLDoc(a)){bE(a,g),d=bF(a),e=bF(g);for(f=0;d[f];++f)e[f]&&bE(d[f],e[f])}if(b){bD(a,g);if(c){d=bF(a),e=bF(g);for(f=0;d[f];++f)bD(d[f],e[f])}}return d=e=null,g},clean:function(a,b,c,d){var f,g,h,i,j,k,l,m,n,o,q,r,s=b===e&&bA,t=[];if(!b||typeof b.createDocumentFragment=="undefined")b=e;for(f=0;(h=a[f])!=null;f++){typeof h=="number"&&(h+="");if(!h)continue;if(typeof h=="string")if(!br.test(h))h=b.createTextNode(h);else{s=s||bk(b),l=b.createElement("div"),s.appendChild(l),h=h.replace(bo,"<$1>"),i=(bp.exec(h)||["",""])[1].toLowerCase(),j=bz[i]||bz._default,k=j[0],l.innerHTML=j[1]+h+j[2];while(k--)l=l.lastChild;if(!p.support.tbody){m=bq.test(h),n=i==="table"&&!m?l.firstChild&&l.firstChild.childNodes:j[1]===""&&!m?l.childNodes:[];for(g=n.length-1;g>=0;--g)p.nodeName(n[g],"tbody")&&!n[g].childNodes.length&&n[g].parentNode.removeChild(n[g])}!p.support.leadingWhitespace&&bn.test(h)&&l.insertBefore(b.createTextNode(bn.exec(h)[0]),l.firstChild),h=l.childNodes,l.parentNode.removeChild(l)}h.nodeType?t.push(h):p.merge(t,h)}l&&(h=l=s=null);if(!p.support.appendChecked)for(f=0;(h=t[f])!=null;f++)p.nodeName(h,"input")?bG(h):typeof h.getElementsByTagName!="undefined"&&p.grep(h.getElementsByTagName("input"),bG);if(c){q=function(a){if(!a.type||bx.test(a.type))return d?d.push(a.parentNode?a.parentNode.removeChild(a):a):c.appendChild(a)};for(f=0;(h=t[f])!=null;f++)if(!p.nodeName(h,"script")||!q(h))c.appendChild(h),typeof h.getElementsByTagName!="undefined"&&(r=p.grep(p.merge([],h.getElementsByTagName("script")),q),t.splice.apply(t,[f+1,0].concat(r)),f+=r.length)}return t},cleanData:function(a,b){var c,d,e,f,g=0,h=p.expando,i=p.cache,j=p.support.deleteExpando,k=p.event.special;for(;(e=a[g])!=null;g++)if(b||p.acceptData(e)){d=e[h],c=d&&i[d];if(c){if(c.events)for(f in c.events)k[f]?p.event.remove(e,f):p.removeEvent(e,f,c.handle);i[d]&&(delete i[d],j?delete e[h]:e.removeAttribute?e.removeAttribute(h):e[h]=null,p.deletedIds.push(d))}}}}),function(){var a,b;p.uaMatch=function(a){a=a.toLowerCase();var b=/(chrome)[ \/]([\w.]+)/.exec(a)||/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||a.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},a=p.uaMatch(g.userAgent),b={},a.browser&&(b[a.browser]=!0,b.version=a.version),b.chrome?b.webkit=!0:b.webkit&&(b.safari=!0),p.browser=b,p.sub=function(){function a(b,c){return new a.fn.init(b,c)}p.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function c(c,d){return d&&d instanceof p&&!(d instanceof a)&&(d=a(d)),p.fn.init.call(this,c,d,b)},a.fn.init.prototype=a.fn;var b=a(e);return a}}();var bH,bI,bJ,bK=/alpha\([^)]*\)/i,bL=/opacity=([^)]*)/,bM=/^(top|right|bottom|left)$/,bN=/^(none|table(?!-c[ea]).+)/,bO=/^margin/,bP=new RegExp("^("+q+")(.*)$","i"),bQ=new RegExp("^("+q+")(?!px)[a-z%]+$","i"),bR=new RegExp("^([-+])=("+q+")","i"),bS={},bT={position:"absolute",visibility:"hidden",display:"block"},bU={letterSpacing:0,fontWeight:400},bV=["Top","Right","Bottom","Left"],bW=["Webkit","O","Moz","ms"],bX=p.fn.toggle;p.fn.extend({css:function(a,c){return p.access(this,function(a,c,d){return d!==b?p.style(a,c,d):p.css(a,c)},a,c,arguments.length>1)},show:function(){return b$(this,!0)},hide:function(){return b$(this)},toggle:function(a,b){var c=typeof a=="boolean";return p.isFunction(a)&&p.isFunction(b)?bX.apply(this,arguments):this.each(function(){(c?a:bZ(this))?p(this).show():p(this).hide()})}}),p.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bH(a,"opacity");return c===""?"1":c}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":p.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!a||a.nodeType===3||a.nodeType===8||!a.style)return;var f,g,h,i=p.camelCase(c),j=a.style;c=p.cssProps[i]||(p.cssProps[i]=bY(j,i)),h=p.cssHooks[c]||p.cssHooks[i];if(d===b)return h&&"get"in h&&(f=h.get(a,!1,e))!==b?f:j[c];g=typeof d,g==="string"&&(f=bR.exec(d))&&(d=(f[1]+1)*f[2]+parseFloat(p.css(a,c)),g="number");if(d==null||g==="number"&&isNaN(d))return;g==="number"&&!p.cssNumber[i]&&(d+="px");if(!h||!("set"in h)||(d=h.set(a,d,e))!==b)try{j[c]=d}catch(k){}},css:function(a,c,d,e){var f,g,h,i=p.camelCase(c);return c=p.cssProps[i]||(p.cssProps[i]=bY(a.style,i)),h=p.cssHooks[c]||p.cssHooks[i],h&&"get"in h&&(f=h.get(a,!0,e)),f===b&&(f=bH(a,c)),f==="normal"&&c in bU&&(f=bU[c]),d||e!==b?(g=parseFloat(f),d||p.isNumeric(g)?g||0:f):f},swap:function(a,b,c){var d,e,f={};for(e in b)f[e]=a.style[e],a.style[e]=b[e];d=c.call(a);for(e in b)a.style[e]=f[e];return d}}),a.getComputedStyle?bH=function(b,c){var d,e,f,g,h=a.getComputedStyle(b,null),i=b.style;return h&&(d=h[c],d===""&&!p.contains(b.ownerDocument,b)&&(d=p.style(b,c)),bQ.test(d)&&bO.test(c)&&(e=i.width,f=i.minWidth,g=i.maxWidth,i.minWidth=i.maxWidth=i.width=d,d=h.width,i.width=e,i.minWidth=f,i.maxWidth=g)),d}:e.documentElement.currentStyle&&(bH=function(a,b){var c,d,e=a.currentStyle&&a.currentStyle[b],f=a.style;return e==null&&f&&f[b]&&(e=f[b]),bQ.test(e)&&!bM.test(b)&&(c=f.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":e,e=f.pixelLeft+"px",f.left=c,d&&(a.runtimeStyle.left=d)),e===""?"auto":e}),p.each(["height","width"],function(a,b){p.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth===0&&bN.test(bH(a,"display"))?p.swap(a,bT,function(){return cb(a,b,d)}):cb(a,b,d)},set:function(a,c,d){return b_(a,c,d?ca(a,b,d,p.support.boxSizing&&p.css(a,"boxSizing")==="border-box"):0)}}}),p.support.opacity||(p.cssHooks.opacity={get:function(a,b){return bL.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=p.isNumeric(b)?"alpha(opacity="+b*100+")":"",f=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&p.trim(f.replace(bK,""))===""&&c.removeAttribute){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bK.test(f)?f.replace(bK,e):f+" "+e}}),p(function(){p.support.reliableMarginRight||(p.cssHooks.marginRight={get:function(a,b){return p.swap(a,{display:"inline-block"},function(){if(b)return bH(a,"marginRight")})}}),!p.support.pixelPosition&&p.fn.position&&p.each(["top","left"],function(a,b){p.cssHooks[b]={get:function(a,c){if(c){var d=bH(a,b);return bQ.test(d)?p(a).position()[b]+"px":d}}}})}),p.expr&&p.expr.filters&&(p.expr.filters.hidden=function(a){return a.offsetWidth===0&&a.offsetHeight===0||!p.support.reliableHiddenOffsets&&(a.style&&a.style.display||bH(a,"display"))==="none"},p.expr.filters.visible=function(a){return!p.expr.filters.hidden(a)}),p.each({margin:"",padding:"",border:"Width"},function(a,b){p.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bV[d]+b]=e[d]||e[d-2]||e[0];return f}},bO.test(a)||(p.cssHooks[a+b].set=b_)});var cd=/%20/g,ce=/\[\]$/,cf=/\r?\n/g,cg=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,ch=/^(?:select|textarea)/i;p.fn.extend({serialize:function(){return p.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?p.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ch.test(this.nodeName)||cg.test(this.type))}).map(function(a,b){var c=p(this).val();return c==null?null:p.isArray(c)?p.map(c,function(a,c){return{name:b.name,value:a.replace(cf,"\r\n")}}):{name:b.name,value:c.replace(cf,"\r\n")}}).get()}}),p.param=function(a,c){var d,e=[],f=function(a,b){b=p.isFunction(b)?b():b==null?"":b,e[e.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=p.ajaxSettings&&p.ajaxSettings.traditional);if(p.isArray(a)||a.jquery&&!p.isPlainObject(a))p.each(a,function(){f(this.name,this.value)});else for(d in a)ci(d,a[d],c,f);return e.join("&").replace(cd,"+")};var cj,ck,cl=/#.*$/,cm=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,cn=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,co=/^(?:GET|HEAD)$/,cp=/^\/\//,cq=/\?/,cr=/)<[^<]*)*<\/script>/gi,cs=/([?&])_=[^&]*/,ct=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,cu=p.fn.load,cv={},cw={},cx=["*/"]+["*"];try{ck=f.href}catch(cy){ck=e.createElement("a"),ck.href="",ck=ck.href}cj=ct.exec(ck.toLowerCase())||[],p.fn.load=function(a,c,d){if(typeof a!="string"&&cu)return cu.apply(this,arguments);if(!this.length)return this;var e,f,g,h=this,i=a.indexOf(" ");return i>=0&&(e=a.slice(i,a.length),a=a.slice(0,i)),p.isFunction(c)?(d=c,c=b):c&&typeof c=="object"&&(f="POST"),p.ajax({url:a,type:f,dataType:"html",data:c,complete:function(a,b){d&&h.each(d,g||[a.responseText,b,a])}}).done(function(a){g=arguments,h.html(e?p("
    ").append(a.replace(cr,"")).find(e):a)}),this},p.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){p.fn[b]=function(a){return this.on(b,a)}}),p.each(["get","post"],function(a,c){p[c]=function(a,d,e,f){return p.isFunction(d)&&(f=f||e,e=d,d=b),p.ajax({type:c,url:a,data:d,success:e,dataType:f})}}),p.extend({getScript:function(a,c){return p.get(a,b,c,"script")},getJSON:function(a,b,c){return p.get(a,b,c,"json")},ajaxSetup:function(a,b){return b?cB(a,p.ajaxSettings):(b=a,a=p.ajaxSettings),cB(a,b),a},ajaxSettings:{url:ck,isLocal:cn.test(cj[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":cx},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":p.parseJSON,"text xml":p.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:cz(cv),ajaxTransport:cz(cw),ajax:function(a,c){function y(a,c,f,i){var k,s,t,u,w,y=c;if(v===2)return;v=2,h&&clearTimeout(h),g=b,e=i||"",x.readyState=a>0?4:0,f&&(u=cC(l,x,f));if(a>=200&&a<300||a===304)l.ifModified&&(w=x.getResponseHeader("Last-Modified"),w&&(p.lastModified[d]=w),w=x.getResponseHeader("Etag"),w&&(p.etag[d]=w)),a===304?(y="notmodified",k=!0):(k=cD(l,u),y=k.state,s=k.data,t=k.error,k=!t);else{t=y;if(!y||a)y="error",a<0&&(a=0)}x.status=a,x.statusText=(c||y)+"",k?o.resolveWith(m,[s,y,x]):o.rejectWith(m,[x,y,t]),x.statusCode(r),r=b,j&&n.trigger("ajax"+(k?"Success":"Error"),[x,l,k?s:t]),q.fireWith(m,[x,y]),j&&(n.trigger("ajaxComplete",[x,l]),--p.active||p.event.trigger("ajaxStop"))}typeof a=="object"&&(c=a,a=b),c=c||{};var d,e,f,g,h,i,j,k,l=p.ajaxSetup({},c),m=l.context||l,n=m!==l&&(m.nodeType||m instanceof p)?p(m):p.event,o=p.Deferred(),q=p.Callbacks("once memory"),r=l.statusCode||{},t={},u={},v=0,w="canceled",x={readyState:0,setRequestHeader:function(a,b){if(!v){var c=a.toLowerCase();a=u[c]=u[c]||a,t[a]=b}return this},getAllResponseHeaders:function(){return v===2?e:null},getResponseHeader:function(a){var c;if(v===2){if(!f){f={};while(c=cm.exec(e))f[c[1].toLowerCase()]=c[2]}c=f[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){return v||(l.mimeType=a),this},abort:function(a){return a=a||w,g&&g.abort(a),y(0,a),this}};o.promise(x),x.success=x.done,x.error=x.fail,x.complete=q.add,x.statusCode=function(a){if(a){var b;if(v<2)for(b in a)r[b]=[r[b],a[b]];else b=a[x.status],x.always(b)}return this},l.url=((a||l.url)+"").replace(cl,"").replace(cp,cj[1]+"//"),l.dataTypes=p.trim(l.dataType||"*").toLowerCase().split(s),l.crossDomain==null&&(i=ct.exec(l.url.toLowerCase())||!1,l.crossDomain=i&&i.join(":")+(i[3]?"":i[1]==="http:"?80:443)!==cj.join(":")+(cj[3]?"":cj[1]==="http:"?80:443)),l.data&&l.processData&&typeof l.data!="string"&&(l.data=p.param(l.data,l.traditional)),cA(cv,l,c,x);if(v===2)return x;j=l.global,l.type=l.type.toUpperCase(),l.hasContent=!co.test(l.type),j&&p.active++===0&&p.event.trigger("ajaxStart");if(!l.hasContent){l.data&&(l.url+=(cq.test(l.url)?"&":"?")+l.data,delete l.data),d=l.url;if(l.cache===!1){var z=p.now(),A=l.url.replace(cs,"$1_="+z);l.url=A+(A===l.url?(cq.test(l.url)?"&":"?")+"_="+z:"")}}(l.data&&l.hasContent&&l.contentType!==!1||c.contentType)&&x.setRequestHeader("Content-Type",l.contentType),l.ifModified&&(d=d||l.url,p.lastModified[d]&&x.setRequestHeader("If-Modified-Since",p.lastModified[d]),p.etag[d]&&x.setRequestHeader("If-None-Match",p.etag[d])),x.setRequestHeader("Accept",l.dataTypes[0]&&l.accepts[l.dataTypes[0]]?l.accepts[l.dataTypes[0]]+(l.dataTypes[0]!=="*"?", "+cx+"; q=0.01":""):l.accepts["*"]);for(k in l.headers)x.setRequestHeader(k,l.headers[k]);if(!l.beforeSend||l.beforeSend.call(m,x,l)!==!1&&v!==2){w="abort";for(k in{success:1,error:1,complete:1})x[k](l[k]);g=cA(cw,l,c,x);if(!g)y(-1,"No Transport");else{x.readyState=1,j&&n.trigger("ajaxSend",[x,l]),l.async&&l.timeout>0&&(h=setTimeout(function(){x.abort("timeout")},l.timeout));try{v=1,g.send(t,y)}catch(B){if(v<2)y(-1,B);else throw B}}return x}return x.abort()},active:0,lastModified:{},etag:{}});var cE=[],cF=/\?/,cG=/(=)\?(?=&|$)|\?\?/,cH=p.now();p.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=cE.pop()||p.expando+"_"+cH++;return this[a]=!0,a}}),p.ajaxPrefilter("json jsonp",function(c,d,e){var f,g,h,i=c.data,j=c.url,k=c.jsonp!==!1,l=k&&cG.test(j),m=k&&!l&&typeof i=="string"&&!(c.contentType||"").indexOf("application/x-www-form-urlencoded")&&cG.test(i);if(c.dataTypes[0]==="jsonp"||l||m)return f=c.jsonpCallback=p.isFunction(c.jsonpCallback)?c.jsonpCallback():c.jsonpCallback,g=a[f],l?c.url=j.replace(cG,"$1"+f):m?c.data=i.replace(cG,"$1"+f):k&&(c.url+=(cF.test(j)?"&":"?")+c.jsonp+"="+f),c.converters["script json"]=function(){return h||p.error(f+" was not called"),h[0]},c.dataTypes[0]="json",a[f]=function(){h=arguments},e.always(function(){a[f]=g,c[f]&&(c.jsonpCallback=d.jsonpCallback,cE.push(f)),h&&p.isFunction(g)&&g(h[0]),h=g=b}),"script"}),p.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){return p.globalEval(a),a}}}),p.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),p.ajaxTransport("script",function(a){if(a.crossDomain){var c,d=e.head||e.getElementsByTagName("head")[0]||e.documentElement;return{send:function(f,g){c=e.createElement("script"),c.async="async",a.scriptCharset&&(c.charset=a.scriptCharset),c.src=a.url,c.onload=c.onreadystatechange=function(a,e){if(e||!c.readyState||/loaded|complete/.test(c.readyState))c.onload=c.onreadystatechange=null,d&&c.parentNode&&d.removeChild(c),c=b,e||g(200,"success")},d.insertBefore(c,d.firstChild)},abort:function(){c&&c.onload(0,1)}}}});var cI,cJ=a.ActiveXObject?function(){for(var a in cI)cI[a](0,1)}:!1,cK=0;p.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&cL()||cM()}:cL,function(a){p.extend(p.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(p.ajaxSettings.xhr()),p.support.ajax&&p.ajaxTransport(function(c){if(!c.crossDomain||p.support.cors){var d;return{send:function(e,f){var g,h,i=c.xhr();c.username?i.open(c.type,c.url,c.async,c.username,c.password):i.open(c.type,c.url,c.async);if(c.xhrFields)for(h in c.xhrFields)i[h]=c.xhrFields[h];c.mimeType&&i.overrideMimeType&&i.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(h in e)i.setRequestHeader(h,e[h])}catch(j){}i.send(c.hasContent&&c.data||null),d=function(a,e){var h,j,k,l,m;try{if(d&&(e||i.readyState===4)){d=b,g&&(i.onreadystatechange=p.noop,cJ&&delete cI[g]);if(e)i.readyState!==4&&i.abort();else{h=i.status,k=i.getAllResponseHeaders(),l={},m=i.responseXML,m&&m.documentElement&&(l.xml=m);try{l.text=i.responseText}catch(a){}try{j=i.statusText}catch(n){j=""}!h&&c.isLocal&&!c.crossDomain?h=l.text?200:404:h===1223&&(h=204)}}}catch(o){e||f(-1,o)}l&&f(h,j,l,k)},c.async?i.readyState===4?setTimeout(d,0):(g=++cK,cJ&&(cI||(cI={},p(a).unload(cJ)),cI[g]=d),i.onreadystatechange=d):d()},abort:function(){d&&d(0,1)}}}});var cN,cO,cP=/^(?:toggle|show|hide)$/,cQ=new RegExp("^(?:([-+])=|)("+q+")([a-z%]*)$","i"),cR=/queueHooks$/,cS=[cY],cT={"*":[function(a,b){var c,d,e=this.createTween(a,b),f=cQ.exec(b),g=e.cur(),h=+g||0,i=1,j=20;if(f){c=+f[2],d=f[3]||(p.cssNumber[a]?"":"px");if(d!=="px"&&h){h=p.css(e.elem,a,!0)||c||1;do i=i||".5",h=h/i,p.style(e.elem,a,h+d);while(i!==(i=e.cur()/g)&&i!==1&&--j)}e.unit=d,e.start=h,e.end=f[1]?h+(f[1]+1)*c:c}return e}]};p.Animation=p.extend(cW,{tweener:function(a,b){p.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");var c,d=0,e=a.length;for(;d-1,j={},k={},l,m;i?(k=e.position(),l=k.top,m=k.left):(l=parseFloat(g)||0,m=parseFloat(h)||0),p.isFunction(b)&&(b=b.call(a,c,f)),b.top!=null&&(j.top=b.top-f.top+l),b.left!=null&&(j.left=b.left-f.left+m),"using"in b?b.using.call(a,j):e.css(j)}},p.fn.extend({position:function(){if(!this[0])return;var a=this[0],b=this.offsetParent(),c=this.offset(),d=c_.test(b[0].nodeName)?{top:0,left:0}:b.offset();return c.top-=parseFloat(p.css(a,"marginTop"))||0,c.left-=parseFloat(p.css(a,"marginLeft"))||0,d.top+=parseFloat(p.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(p.css(b[0],"borderLeftWidth"))||0,{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||e.body;while(a&&!c_.test(a.nodeName)&&p.css(a,"position")==="static")a=a.offsetParent;return a||e.body})}}),p.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);p.fn[a]=function(e){return p.access(this,function(a,e,f){var g=da(a);if(f===b)return g?c in g?g[c]:g.document.documentElement[e]:a[e];g?g.scrollTo(d?p(g).scrollLeft():f,d?f:p(g).scrollTop()):a[e]=f},a,e,arguments.length,null)}}),p.each({Height:"height",Width:"width"},function(a,c){p.each({padding:"inner"+a,content:c,"":"outer"+a},function(d,e){p.fn[e]=function(e,f){var g=arguments.length&&(d||typeof e!="boolean"),h=d||(e===!0||f===!0?"margin":"border");return p.access(this,function(c,d,e){var f;return p.isWindow(c)?c.document.documentElement["client"+a]:c.nodeType===9?(f=c.documentElement,Math.max(c.body["scroll"+a],f["scroll"+a],c.body["offset"+a],f["offset"+a],f["client"+a])):e===b?p.css(c,d,e,h):p.style(c,d,e,h)},c,g?e:b,g,null)}})}),a.jQuery=a.$=p,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return p})})(window); \ No newline at end of file diff --git a/blayer-webif/jquery.js b/blayer-webif/jquery.js new file mode 100644 index 0000000..f65cf1d --- /dev/null +++ b/blayer-webif/jquery.js @@ -0,0 +1,2 @@ +/*! jQuery v1.8.2 jquery.com | jquery.org/license */ +(function(a,b){function G(a){var b=F[a]={};return p.each(a.split(s),function(a,c){b[c]=!0}),b}function J(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(I,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:+d+""===d?+d:H.test(d)?p.parseJSON(d):d}catch(f){}p.data(a,c,d)}else d=b}return d}function K(a){var b;for(b in a){if(b==="data"&&p.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function ba(){return!1}function bb(){return!0}function bh(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function bi(a,b){do a=a[b];while(a&&a.nodeType!==1);return a}function bj(a,b,c){b=b||0;if(p.isFunction(b))return p.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return p.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=p.grep(a,function(a){return a.nodeType===1});if(be.test(b))return p.filter(b,d,!c);b=p.filter(b,d)}return p.grep(a,function(a,d){return p.inArray(a,b)>=0===c})}function bk(a){var b=bl.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function bC(a,b){return a.getElementsByTagName(b)[0]||a.appendChild(a.ownerDocument.createElement(b))}function bD(a,b){if(b.nodeType!==1||!p.hasData(a))return;var c,d,e,f=p._data(a),g=p._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;d").appendTo(e.body),c=b.css("display");b.remove();if(c==="none"||c===""){bI=e.body.appendChild(bI||p.extend(e.createElement("iframe"),{frameBorder:0,width:0,height:0}));if(!bJ||!bI.createElement)bJ=(bI.contentWindow||bI.contentDocument).document,bJ.write(""),bJ.close();b=bJ.body.appendChild(bJ.createElement(a)),c=bH(b,"display"),e.body.removeChild(bI)}return bS[a]=c,c}function ci(a,b,c,d){var e;if(p.isArray(b))p.each(b,function(b,e){c||ce.test(a)?d(a,e):ci(a+"["+(typeof e=="object"?b:"")+"]",e,c,d)});else if(!c&&p.type(b)==="object")for(e in b)ci(a+"["+e+"]",b[e],c,d);else d(a,b)}function cz(a){return function(b,c){typeof b!="string"&&(c=b,b="*");var d,e,f,g=b.toLowerCase().split(s),h=0,i=g.length;if(p.isFunction(c))for(;h)[^>]*$|#([\w\-]*)$)/,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^[\],:{}\s]*$/,x=/(?:^|:|,)(?:\s*\[)+/g,y=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,z=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,A=/^-ms-/,B=/-([\da-z])/gi,C=function(a,b){return(b+"").toUpperCase()},D=function(){e.addEventListener?(e.removeEventListener("DOMContentLoaded",D,!1),p.ready()):e.readyState==="complete"&&(e.detachEvent("onreadystatechange",D),p.ready())},E={};p.fn=p.prototype={constructor:p,init:function(a,c,d){var f,g,h,i;if(!a)return this;if(a.nodeType)return this.context=this[0]=a,this.length=1,this;if(typeof a=="string"){a.charAt(0)==="<"&&a.charAt(a.length-1)===">"&&a.length>=3?f=[null,a,null]:f=u.exec(a);if(f&&(f[1]||!c)){if(f[1])return c=c instanceof p?c[0]:c,i=c&&c.nodeType?c.ownerDocument||c:e,a=p.parseHTML(f[1],i,!0),v.test(f[1])&&p.isPlainObject(c)&&this.attr.call(a,c,!0),p.merge(this,a);g=e.getElementById(f[2]);if(g&&g.parentNode){if(g.id!==f[2])return d.find(a);this.length=1,this[0]=g}return this.context=e,this.selector=a,this}return!c||c.jquery?(c||d).find(a):this.constructor(c).find(a)}return p.isFunction(a)?d.ready(a):(a.selector!==b&&(this.selector=a.selector,this.context=a.context),p.makeArray(a,this))},selector:"",jquery:"1.8.2",length:0,size:function(){return this.length},toArray:function(){return k.call(this)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=p.merge(this.constructor(),a);return d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")"),d},each:function(a,b){return p.each(this,a,b)},ready:function(a){return p.ready.promise().done(a),this},eq:function(a){return a=+a,a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(k.apply(this,arguments),"slice",k.call(arguments).join(","))},map:function(a){return this.pushStack(p.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:j,sort:[].sort,splice:[].splice},p.fn.init.prototype=p.fn,p.extend=p.fn.extend=function(){var a,c,d,e,f,g,h=arguments[0]||{},i=1,j=arguments.length,k=!1;typeof h=="boolean"&&(k=h,h=arguments[1]||{},i=2),typeof h!="object"&&!p.isFunction(h)&&(h={}),j===i&&(h=this,--i);for(;i0)return;d.resolveWith(e,[p]),p.fn.trigger&&p(e).trigger("ready").off("ready")},isFunction:function(a){return p.type(a)==="function"},isArray:Array.isArray||function(a){return p.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):E[m.call(a)]||"object"},isPlainObject:function(a){if(!a||p.type(a)!=="object"||a.nodeType||p.isWindow(a))return!1;try{if(a.constructor&&!n.call(a,"constructor")&&!n.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||n.call(a,d)},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},error:function(a){throw new Error(a)},parseHTML:function(a,b,c){var d;return!a||typeof a!="string"?null:(typeof b=="boolean"&&(c=b,b=0),b=b||e,(d=v.exec(a))?[b.createElement(d[1])]:(d=p.buildFragment([a],b,c?null:[]),p.merge([],(d.cacheable?p.clone(d.fragment):d.fragment).childNodes)))},parseJSON:function(b){if(!b||typeof b!="string")return null;b=p.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(w.test(b.replace(y,"@").replace(z,"]").replace(x,"")))return(new Function("return "+b))();p.error("Invalid JSON: "+b)},parseXML:function(c){var d,e;if(!c||typeof c!="string")return null;try{a.DOMParser?(e=new DOMParser,d=e.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(f){d=b}return(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&p.error("Invalid XML: "+c),d},noop:function(){},globalEval:function(b){b&&r.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(A,"ms-").replace(B,C)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,c,d){var e,f=0,g=a.length,h=g===b||p.isFunction(a);if(d){if(h){for(e in a)if(c.apply(a[e],d)===!1)break}else for(;f0&&a[0]&&a[i-1]||i===0||p.isArray(a));if(j)for(;h-1)i.splice(c,1),e&&(c<=g&&g--,c<=h&&h--)}),this},has:function(a){return p.inArray(a,i)>-1},empty:function(){return i=[],this},disable:function(){return i=j=c=b,this},disabled:function(){return!i},lock:function(){return j=b,c||l.disable(),this},locked:function(){return!j},fireWith:function(a,b){return b=b||[],b=[a,b.slice?b.slice():b],i&&(!d||j)&&(e?j.push(b):k(b)),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!d}};return l},p.extend({Deferred:function(a){var b=[["resolve","done",p.Callbacks("once memory"),"resolved"],["reject","fail",p.Callbacks("once memory"),"rejected"],["notify","progress",p.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return p.Deferred(function(c){p.each(b,function(b,d){var f=d[0],g=a[b];e[d[1]](p.isFunction(g)?function(){var a=g.apply(this,arguments);a&&p.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f+"With"](this===e?c:this,[a])}:c[f])}),a=null}).promise()},promise:function(a){return a!=null?p.extend(a,d):d}},e={};return d.pipe=d.then,p.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[a^1][2].disable,b[2][2].lock),e[f[0]]=g.fire,e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=k.call(arguments),d=c.length,e=d!==1||a&&p.isFunction(a.promise)?d:0,f=e===1?a:p.Deferred(),g=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?k.call(arguments):d,c===h?f.notifyWith(b,c):--e||f.resolveWith(b,c)}},h,i,j;if(d>1){h=new Array(d),i=new Array(d),j=new Array(d);for(;b
    a",c=n.getElementsByTagName("*"),d=n.getElementsByTagName("a")[0],d.style.cssText="top:1px;float:left;opacity:.5";if(!c||!c.length)return{};f=e.createElement("select"),g=f.appendChild(e.createElement("option")),h=n.getElementsByTagName("input")[0],b={leadingWhitespace:n.firstChild.nodeType===3,tbody:!n.getElementsByTagName("tbody").length,htmlSerialize:!!n.getElementsByTagName("link").length,style:/top/.test(d.getAttribute("style")),hrefNormalized:d.getAttribute("href")==="/a",opacity:/^0.5/.test(d.style.opacity),cssFloat:!!d.style.cssFloat,checkOn:h.value==="on",optSelected:g.selected,getSetAttribute:n.className!=="t",enctype:!!e.createElement("form").enctype,html5Clone:e.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",boxModel:e.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},h.checked=!0,b.noCloneChecked=h.cloneNode(!0).checked,f.disabled=!0,b.optDisabled=!g.disabled;try{delete n.test}catch(o){b.deleteExpando=!1}!n.addEventListener&&n.attachEvent&&n.fireEvent&&(n.attachEvent("onclick",m=function(){b.noCloneEvent=!1}),n.cloneNode(!0).fireEvent("onclick"),n.detachEvent("onclick",m)),h=e.createElement("input"),h.value="t",h.setAttribute("type","radio"),b.radioValue=h.value==="t",h.setAttribute("checked","checked"),h.setAttribute("name","t"),n.appendChild(h),i=e.createDocumentFragment(),i.appendChild(n.lastChild),b.checkClone=i.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=h.checked,i.removeChild(h),i.appendChild(n);if(n.attachEvent)for(k in{submit:!0,change:!0,focusin:!0})j="on"+k,l=j in n,l||(n.setAttribute(j,"return;"),l=typeof n[j]=="function"),b[k+"Bubbles"]=l;return p(function(){var c,d,f,g,h="padding:0;margin:0;border:0;display:block;overflow:hidden;",i=e.getElementsByTagName("body")[0];if(!i)return;c=e.createElement("div"),c.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",i.insertBefore(c,i.firstChild),d=e.createElement("div"),c.appendChild(d),d.innerHTML="
    t
    ",f=d.getElementsByTagName("td"),f[0].style.cssText="padding:0;margin:0;border:0;display:none",l=f[0].offsetHeight===0,f[0].style.display="",f[1].style.display="none",b.reliableHiddenOffsets=l&&f[0].offsetHeight===0,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",b.boxSizing=d.offsetWidth===4,b.doesNotIncludeMarginInBodyOffset=i.offsetTop!==1,a.getComputedStyle&&(b.pixelPosition=(a.getComputedStyle(d,null)||{}).top!=="1%",b.boxSizingReliable=(a.getComputedStyle(d,null)||{width:"4px"}).width==="4px",g=e.createElement("div"),g.style.cssText=d.style.cssText=h,g.style.marginRight=g.style.width="0",d.style.width="1px",d.appendChild(g),b.reliableMarginRight=!parseFloat((a.getComputedStyle(g,null)||{}).marginRight)),typeof d.style.zoom!="undefined"&&(d.innerHTML="",d.style.cssText=h+"width:1px;padding:1px;display:inline;zoom:1",b.inlineBlockNeedsLayout=d.offsetWidth===3,d.style.display="block",d.style.overflow="visible",d.innerHTML="
    ",d.firstChild.style.width="5px",b.shrinkWrapBlocks=d.offsetWidth!==3,c.style.zoom=1),i.removeChild(c),c=d=f=g=null}),i.removeChild(n),c=d=f=g=h=i=n=null,b}();var H=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,I=/([A-Z])/g;p.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(p.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){return a=a.nodeType?p.cache[a[p.expando]]:a[p.expando],!!a&&!K(a)},data:function(a,c,d,e){if(!p.acceptData(a))return;var f,g,h=p.expando,i=typeof c=="string",j=a.nodeType,k=j?p.cache:a,l=j?a[h]:a[h]&&h;if((!l||!k[l]||!e&&!k[l].data)&&i&&d===b)return;l||(j?a[h]=l=p.deletedIds.pop()||p.guid++:l=h),k[l]||(k[l]={},j||(k[l].toJSON=p.noop));if(typeof c=="object"||typeof c=="function")e?k[l]=p.extend(k[l],c):k[l].data=p.extend(k[l].data,c);return f=k[l],e||(f.data||(f.data={}),f=f.data),d!==b&&(f[p.camelCase(c)]=d),i?(g=f[c],g==null&&(g=f[p.camelCase(c)])):g=f,g},removeData:function(a,b,c){if(!p.acceptData(a))return;var d,e,f,g=a.nodeType,h=g?p.cache:a,i=g?a[p.expando]:p.expando;if(!h[i])return;if(b){d=c?h[i]:h[i].data;if(d){p.isArray(b)||(b in d?b=[b]:(b=p.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,f=b.length;e1,null,!1))},removeData:function(a){return this.each(function(){p.removeData(this,a)})}}),p.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=p._data(a,b),c&&(!d||p.isArray(c)?d=p._data(a,b,p.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=p.queue(a,b),d=c.length,e=c.shift(),f=p._queueHooks(a,b),g=function(){p.dequeue(a,b)};e==="inprogress"&&(e=c.shift(),d--),e&&(b==="fx"&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return p._data(a,c)||p._data(a,c,{empty:p.Callbacks("once memory").add(function(){p.removeData(a,b+"queue",!0),p.removeData(a,c,!0)})})}}),p.fn.extend({queue:function(a,c){var d=2;return typeof a!="string"&&(c=a,a="fx",d--),arguments.length1)},removeAttr:function(a){return this.each(function(){p.removeAttr(this,a)})},prop:function(a,b){return p.access(this,p.prop,a,b,arguments.length>1)},removeProp:function(a){return a=p.propFix[a]||a,this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,f,g,h;if(p.isFunction(a))return this.each(function(b){p(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(s);for(c=0,d=this.length;c=0)d=d.replace(" "+c[f]+" "," ");e.className=a?p.trim(d):""}}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";return p.isFunction(a)?this.each(function(c){p(this).toggleClass(a.call(this,c,this.className,b),b)}):this.each(function(){if(c==="string"){var e,f=0,g=p(this),h=b,i=a.split(s);while(e=i[f++])h=d?h:!g.hasClass(e),g[h?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&p._data(this,"__className__",this.className),this.className=this.className||a===!1?"":p._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c=0)return!0;return!1},val:function(a){var c,d,e,f=this[0];if(!arguments.length){if(f)return c=p.valHooks[f.type]||p.valHooks[f.nodeName.toLowerCase()],c&&"get"in c&&(d=c.get(f,"value"))!==b?d:(d=f.value,typeof d=="string"?d.replace(P,""):d==null?"":d);return}return e=p.isFunction(a),this.each(function(d){var f,g=p(this);if(this.nodeType!==1)return;e?f=a.call(this,d,g.val()):f=a,f==null?f="":typeof f=="number"?f+="":p.isArray(f)&&(f=p.map(f,function(a){return a==null?"":a+""})),c=p.valHooks[this.type]||p.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,f,"value")===b)this.value=f})}}),p.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,f=a.selectedIndex,g=[],h=a.options,i=a.type==="select-one";if(f<0)return null;c=i?f:0,d=i?f+1:h.length;for(;c=0}),c.length||(a.selectedIndex=-1),c}}},attrFn:{},attr:function(a,c,d,e){var f,g,h,i=a.nodeType;if(!a||i===3||i===8||i===2)return;if(e&&p.isFunction(p.fn[c]))return p(a)[c](d);if(typeof a.getAttribute=="undefined")return p.prop(a,c,d);h=i!==1||!p.isXMLDoc(a),h&&(c=c.toLowerCase(),g=p.attrHooks[c]||(T.test(c)?M:L));if(d!==b){if(d===null){p.removeAttr(a,c);return}return g&&"set"in g&&h&&(f=g.set(a,d,c))!==b?f:(a.setAttribute(c,d+""),d)}return g&&"get"in g&&h&&(f=g.get(a,c))!==null?f:(f=a.getAttribute(c),f===null?b:f)},removeAttr:function(a,b){var c,d,e,f,g=0;if(b&&a.nodeType===1){d=b.split(s);for(;g=0}})});var V=/^(?:textarea|input|select)$/i,W=/^([^\.]*|)(?:\.(.+)|)$/,X=/(?:^|\s)hover(\.\S+|)\b/,Y=/^key/,Z=/^(?:mouse|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=function(a){return p.event.special.hover?a:a.replace(X,"mouseenter$1 mouseleave$1")};p.event={add:function(a,c,d,e,f){var g,h,i,j,k,l,m,n,o,q,r;if(a.nodeType===3||a.nodeType===8||!c||!d||!(g=p._data(a)))return;d.handler&&(o=d,d=o.handler,f=o.selector),d.guid||(d.guid=p.guid++),i=g.events,i||(g.events=i={}),h=g.handle,h||(g.handle=h=function(a){return typeof p!="undefined"&&(!a||p.event.triggered!==a.type)?p.event.dispatch.apply(h.elem,arguments):b},h.elem=a),c=p.trim(_(c)).split(" ");for(j=0;j=0&&(s=s.slice(0,-1),i=!0),s.indexOf(".")>=0&&(t=s.split("."),s=t.shift(),t.sort());if((!f||p.event.customEvent[s])&&!p.event.global[s])return;c=typeof c=="object"?c[p.expando]?c:new p.Event(s,c):new p.Event(s),c.type=s,c.isTrigger=!0,c.exclusive=i,c.namespace=t.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+t.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,m=s.indexOf(":")<0?"on"+s:"";if(!f){h=p.cache;for(j in h)h[j].events&&h[j].events[s]&&p.event.trigger(c,d,h[j].handle.elem,!0);return}c.result=b,c.target||(c.target=f),d=d!=null?p.makeArray(d):[],d.unshift(c),n=p.event.special[s]||{};if(n.trigger&&n.trigger.apply(f,d)===!1)return;q=[[f,n.bindType||s]];if(!g&&!n.noBubble&&!p.isWindow(f)){r=n.delegateType||s,k=$.test(r+s)?f:f.parentNode;for(l=f;k;k=k.parentNode)q.push([k,r]),l=k;l===(f.ownerDocument||e)&&q.push([l.defaultView||l.parentWindow||a,r])}for(j=0;j=0:p.find(m,this,null,[f]).length),h[m]&&j.push(l);j.length&&u.push({elem:f,matches:j})}o.length>q&&u.push({elem:this,matches:o.slice(q)});for(d=0;d0?this.on(b,null,a,c):this.trigger(b)},Y.test(b)&&(p.event.fixHooks[b]=p.event.keyHooks),Z.test(b)&&(p.event.fixHooks[b]=p.event.mouseHooks)}),function(a,b){function bc(a,b,c,d){c=c||[],b=b||r;var e,f,i,j,k=b.nodeType;if(!a||typeof a!="string")return c;if(k!==1&&k!==9)return[];i=g(b);if(!i&&!d)if(e=P.exec(a))if(j=e[1]){if(k===9){f=b.getElementById(j);if(!f||!f.parentNode)return c;if(f.id===j)return c.push(f),c}else if(b.ownerDocument&&(f=b.ownerDocument.getElementById(j))&&h(b,f)&&f.id===j)return c.push(f),c}else{if(e[2])return w.apply(c,x.call(b.getElementsByTagName(a),0)),c;if((j=e[3])&&_&&b.getElementsByClassName)return w.apply(c,x.call(b.getElementsByClassName(j),0)),c}return bp(a.replace(L,"$1"),b,c,d,i)}function bd(a){return function(b){var c=b.nodeName.toLowerCase();return c==="input"&&b.type===a}}function be(a){return function(b){var c=b.nodeName.toLowerCase();return(c==="input"||c==="button")&&b.type===a}}function bf(a){return z(function(b){return b=+b,z(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function bg(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}function bh(a,b){var c,d,f,g,h,i,j,k=C[o][a];if(k)return b?0:k.slice(0);h=a,i=[],j=e.preFilter;while(h){if(!c||(d=M.exec(h)))d&&(h=h.slice(d[0].length)),i.push(f=[]);c=!1;if(d=N.exec(h))f.push(c=new q(d.shift())),h=h.slice(c.length),c.type=d[0].replace(L," ");for(g in e.filter)(d=W[g].exec(h))&&(!j[g]||(d=j[g](d,r,!0)))&&(f.push(c=new q(d.shift())),h=h.slice(c.length),c.type=g,c.matches=d);if(!c)break}return b?h.length:h?bc.error(a):C(a,i).slice(0)}function bi(a,b,d){var e=b.dir,f=d&&b.dir==="parentNode",g=u++;return b.first?function(b,c,d){while(b=b[e])if(f||b.nodeType===1)return a(b,c,d)}:function(b,d,h){if(!h){var i,j=t+" "+g+" ",k=j+c;while(b=b[e])if(f||b.nodeType===1){if((i=b[o])===k)return b.sizset;if(typeof i=="string"&&i.indexOf(j)===0){if(b.sizset)return b}else{b[o]=k;if(a(b,d,h))return b.sizset=!0,b;b.sizset=!1}}}else while(b=b[e])if(f||b.nodeType===1)if(a(b,d,h))return b}}function bj(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function bk(a,b,c,d,e){var f,g=[],h=0,i=a.length,j=b!=null;for(;h-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==l)||((b=c).nodeType?j(a,c,d):k(a,c,d))}];for(;i1&&bj(m),i>1&&a.slice(0,i-1).join("").replace(L,"$1"),c,i0,f=a.length>0,g=function(h,i,j,k,m){var n,o,p,q=[],s=0,u="0",x=h&&[],y=m!=null,z=l,A=h||f&&e.find.TAG("*",m&&i.parentNode||i),B=t+=z==null?1:Math.E;y&&(l=i!==r&&i,c=g.el);for(;(n=A[u])!=null;u++){if(f&&n){for(o=0;p=a[o];o++)if(p(n,i,j)){k.push(n);break}y&&(t=B,c=++g.el)}d&&((n=!p&&n)&&s--,h&&x.push(n))}s+=u;if(d&&u!==s){for(o=0;p=b[o];o++)p(x,q,i,j);if(h){if(s>0)while(u--)!x[u]&&!q[u]&&(q[u]=v.call(k));q=bk(q)}w.apply(k,q),y&&!h&&q.length>0&&s+b.length>1&&bc.uniqueSort(k)}return y&&(t=B,l=z),x};return g.el=0,d?z(g):g}function bo(a,b,c,d){var e=0,f=b.length;for(;e2&&(j=h[0]).type==="ID"&&b.nodeType===9&&!f&&e.relative[h[1].type]){b=e.find.ID(j.matches[0].replace(V,""),b,f)[0];if(!b)return c;a=a.slice(h.shift().length)}for(g=W.POS.test(a)?-1:h.length-1;g>=0;g--){j=h[g];if(e.relative[k=j.type])break;if(l=e.find[k])if(d=l(j.matches[0].replace(V,""),R.test(h[0].type)&&b.parentNode||b,f)){h.splice(g,1),a=d.length&&h.join("");if(!a)return w.apply(c,x.call(d,0)),c;break}}}return i(a,m)(d,b,f,c,R.test(a)),c}function bq(){}var c,d,e,f,g,h,i,j,k,l,m=!0,n="undefined",o=("sizcache"+Math.random()).replace(".",""),q=String,r=a.document,s=r.documentElement,t=0,u=0,v=[].pop,w=[].push,x=[].slice,y=[].indexOf||function(a){var b=0,c=this.length;for(;be.cacheLength&&delete a[b.shift()],a[c]=d},a)},B=A(),C=A(),D=A(),E="[\\x20\\t\\r\\n\\f]",F="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",G=F.replace("w","w#"),H="([*^$|!~]?=)",I="\\["+E+"*("+F+")"+E+"*(?:"+H+E+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+G+")|)|)"+E+"*\\]",J=":("+F+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:"+I+")|[^:]|\\\\.)*|.*))\\)|)",K=":(even|odd|eq|gt|lt|nth|first|last)(?:\\("+E+"*((?:-\\d)?\\d*)"+E+"*\\)|)(?=[^-]|$)",L=new RegExp("^"+E+"+|((?:^|[^\\\\])(?:\\\\.)*)"+E+"+$","g"),M=new RegExp("^"+E+"*,"+E+"*"),N=new RegExp("^"+E+"*([\\x20\\t\\r\\n\\f>+~])"+E+"*"),O=new RegExp(J),P=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,Q=/^:not/,R=/[\x20\t\r\n\f]*[+~]/,S=/:not\($/,T=/h\d/i,U=/input|select|textarea|button/i,V=/\\(?!\\)/g,W={ID:new RegExp("^#("+F+")"),CLASS:new RegExp("^\\.("+F+")"),NAME:new RegExp("^\\[name=['\"]?("+F+")['\"]?\\]"),TAG:new RegExp("^("+F.replace("w","w*")+")"),ATTR:new RegExp("^"+I),PSEUDO:new RegExp("^"+J),POS:new RegExp(K,"i"),CHILD:new RegExp("^:(only|nth|first|last)-child(?:\\("+E+"*(even|odd|(([+-]|)(\\d*)n|)"+E+"*(?:([+-]|)"+E+"*(\\d+)|))"+E+"*\\)|)","i"),needsContext:new RegExp("^"+E+"*[>+~]|"+K,"i")},X=function(a){var b=r.createElement("div");try{return a(b)}catch(c){return!1}finally{b=null}},Y=X(function(a){return a.appendChild(r.createComment("")),!a.getElementsByTagName("*").length}),Z=X(function(a){return a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!==n&&a.firstChild.getAttribute("href")==="#"}),$=X(function(a){a.innerHTML="";var b=typeof a.lastChild.getAttribute("multiple");return b!=="boolean"&&b!=="string"}),_=X(function(a){return a.innerHTML="",!a.getElementsByClassName||!a.getElementsByClassName("e").length?!1:(a.lastChild.className="e",a.getElementsByClassName("e").length===2)}),ba=X(function(a){a.id=o+0,a.innerHTML="
    ",s.insertBefore(a,s.firstChild);var b=r.getElementsByName&&r.getElementsByName(o).length===2+r.getElementsByName(o+0).length;return d=!r.getElementById(o),s.removeChild(a),b});try{x.call(s.childNodes,0)[0].nodeType}catch(bb){x=function(a){var b,c=[];for(;b=this[a];a++)c.push(b);return c}}bc.matches=function(a,b){return bc(a,null,null,b)},bc.matchesSelector=function(a,b){return bc(b,null,null,[a]).length>0},f=bc.getText=function(a){var b,c="",d=0,e=a.nodeType;if(e){if(e===1||e===9||e===11){if(typeof a.textContent=="string")return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=f(a)}else if(e===3||e===4)return a.nodeValue}else for(;b=a[d];d++)c+=f(b);return c},g=bc.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?b.nodeName!=="HTML":!1},h=bc.contains=s.contains?function(a,b){var c=a.nodeType===9?a.documentElement:a,d=b&&b.parentNode;return a===d||!!(d&&d.nodeType===1&&c.contains&&c.contains(d))}:s.compareDocumentPosition?function(a,b){return b&&!!(a.compareDocumentPosition(b)&16)}:function(a,b){while(b=b.parentNode)if(b===a)return!0;return!1},bc.attr=function(a,b){var c,d=g(a);return d||(b=b.toLowerCase()),(c=e.attrHandle[b])?c(a):d||$?a.getAttribute(b):(c=a.getAttributeNode(b),c?typeof a[b]=="boolean"?a[b]?b:null:c.specified?c.value:null:null)},e=bc.selectors={cacheLength:50,createPseudo:z,match:W,attrHandle:Z?{}:{href:function(a){return a.getAttribute("href",2)},type:function(a){return a.getAttribute("type")}},find:{ID:d?function(a,b,c){if(typeof b.getElementById!==n&&!c){var d=b.getElementById(a);return d&&d.parentNode?[d]:[]}}:function(a,c,d){if(typeof c.getElementById!==n&&!d){var e=c.getElementById(a);return e?e.id===a||typeof e.getAttributeNode!==n&&e.getAttributeNode("id").value===a?[e]:b:[]}},TAG:Y?function(a,b){if(typeof b.getElementsByTagName!==n)return b.getElementsByTagName(a)}:function(a,b){var c=b.getElementsByTagName(a);if(a==="*"){var d,e=[],f=0;for(;d=c[f];f++)d.nodeType===1&&e.push(d);return e}return c},NAME:ba&&function(a,b){if(typeof b.getElementsByName!==n)return b.getElementsByName(name)},CLASS:_&&function(a,b,c){if(typeof b.getElementsByClassName!==n&&!c)return b.getElementsByClassName(a)}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(V,""),a[3]=(a[4]||a[5]||"").replace(V,""),a[2]==="~="&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),a[1]==="nth"?(a[2]||bc.error(a[0]),a[3]=+(a[3]?a[4]+(a[5]||1):2*(a[2]==="even"||a[2]==="odd")),a[4]=+(a[6]+a[7]||a[2]==="odd")):a[2]&&bc.error(a[0]),a},PSEUDO:function(a){var b,c;if(W.CHILD.test(a[0]))return null;if(a[3])a[2]=a[3];else if(b=a[4])O.test(b)&&(c=bh(b,!0))&&(c=b.indexOf(")",b.length-c)-b.length)&&(b=b.slice(0,c),a[0]=a[0].slice(0,c)),a[2]=b;return a.slice(0,3)}},filter:{ID:d?function(a){return a=a.replace(V,""),function(b){return b.getAttribute("id")===a}}:function(a){return a=a.replace(V,""),function(b){var c=typeof b.getAttributeNode!==n&&b.getAttributeNode("id");return c&&c.value===a}},TAG:function(a){return a==="*"?function(){return!0}:(a=a.replace(V,"").toLowerCase(),function(b){return b.nodeName&&b.nodeName.toLowerCase()===a})},CLASS:function(a){var b=B[o][a];return b||(b=B(a,new RegExp("(^|"+E+")"+a+"("+E+"|$)"))),function(a){return b.test(a.className||typeof a.getAttribute!==n&&a.getAttribute("class")||"")}},ATTR:function(a,b,c){return function(d,e){var f=bc.attr(d,a);return f==null?b==="!=":b?(f+="",b==="="?f===c:b==="!="?f!==c:b==="^="?c&&f.indexOf(c)===0:b==="*="?c&&f.indexOf(c)>-1:b==="$="?c&&f.substr(f.length-c.length)===c:b==="~="?(" "+f+" ").indexOf(c)>-1:b==="|="?f===c||f.substr(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d){return a==="nth"?function(a){var b,e,f=a.parentNode;if(c===1&&d===0)return!0;if(f){e=0;for(b=f.firstChild;b;b=b.nextSibling)if(b.nodeType===1){e++;if(a===b)break}}return e-=d,e===c||e%c===0&&e/c>=0}:function(b){var c=b;switch(a){case"only":case"first":while(c=c.previousSibling)if(c.nodeType===1)return!1;if(a==="first")return!0;c=b;case"last":while(c=c.nextSibling)if(c.nodeType===1)return!1;return!0}}},PSEUDO:function(a,b){var c,d=e.pseudos[a]||e.setFilters[a.toLowerCase()]||bc.error("unsupported pseudo: "+a);return d[o]?d(b):d.length>1?(c=[a,a,"",b],e.setFilters.hasOwnProperty(a.toLowerCase())?z(function(a,c){var e,f=d(a,b),g=f.length;while(g--)e=y.call(a,f[g]),a[e]=!(c[e]=f[g])}):function(a){return d(a,0,c)}):d}},pseudos:{not:z(function(a){var b=[],c=[],d=i(a.replace(L,"$1"));return d[o]?z(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)if(f=g[h])a[h]=!(b[h]=f)}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:z(function(a){return function(b){return bc(a,b).length>0}}),contains:z(function(a){return function(b){return(b.textContent||b.innerText||f(b)).indexOf(a)>-1}}),enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&!!a.checked||b==="option"&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},parent:function(a){return!e.pseudos.empty(a)},empty:function(a){var b;a=a.firstChild;while(a){if(a.nodeName>"@"||(b=a.nodeType)===3||b===4)return!1;a=a.nextSibling}return!0},header:function(a){return T.test(a.nodeName)},text:function(a){var b,c;return a.nodeName.toLowerCase()==="input"&&(b=a.type)==="text"&&((c=a.getAttribute("type"))==null||c.toLowerCase()===b)},radio:bd("radio"),checkbox:bd("checkbox"),file:bd("file"),password:bd("password"),image:bd("image"),submit:be("submit"),reset:be("reset"),button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&a.type==="button"||b==="button"},input:function(a){return U.test(a.nodeName)},focus:function(a){var b=a.ownerDocument;return a===b.activeElement&&(!b.hasFocus||b.hasFocus())&&(!!a.type||!!a.href)},active:function(a){return a===a.ownerDocument.activeElement},first:bf(function(a,b,c){return[0]}),last:bf(function(a,b,c){return[b-1]}),eq:bf(function(a,b,c){return[c<0?c+b:c]}),even:bf(function(a,b,c){for(var d=0;d=0;)a.push(d);return a}),gt:bf(function(a,b,c){for(var d=c<0?c+b:c;++d",a.querySelectorAll("[selected]").length||e.push("\\["+E+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),a.querySelectorAll(":checked").length||e.push(":checked")}),X(function(a){a.innerHTML="

    ",a.querySelectorAll("[test^='']").length&&e.push("[*^$]="+E+"*(?:\"\"|'')"),a.innerHTML="",a.querySelectorAll(":enabled").length||e.push(":enabled",":disabled")}),e=new RegExp(e.join("|")),bp=function(a,d,f,g,h){if(!g&&!h&&(!e||!e.test(a))){var i,j,k=!0,l=o,m=d,n=d.nodeType===9&&a;if(d.nodeType===1&&d.nodeName.toLowerCase()!=="object"){i=bh(a),(k=d.getAttribute("id"))?l=k.replace(c,"\\$&"):d.setAttribute("id",l),l="[id='"+l+"'] ",j=i.length;while(j--)i[j]=l+i[j].join("");m=R.test(a)&&d.parentNode||d,n=i.join(",")}if(n)try{return w.apply(f,x.call(m.querySelectorAll(n),0)),f}catch(p){}finally{k||d.removeAttribute("id")}}return b(a,d,f,g,h)},h&&(X(function(b){a=h.call(b,"div");try{h.call(b,"[test!='']:sizzle"),f.push("!=",J)}catch(c){}}),f=new RegExp(f.join("|")),bc.matchesSelector=function(b,c){c=c.replace(d,"='$1']");if(!g(b)&&!f.test(c)&&(!e||!e.test(c)))try{var i=h.call(b,c);if(i||a||b.document&&b.document.nodeType!==11)return i}catch(j){}return bc(c,null,null,[b]).length>0})}(),e.pseudos.nth=e.pseudos.eq,e.filters=bq.prototype=e.pseudos,e.setFilters=new bq,bc.attr=p.attr,p.find=bc,p.expr=bc.selectors,p.expr[":"]=p.expr.pseudos,p.unique=bc.uniqueSort,p.text=bc.getText,p.isXMLDoc=bc.isXML,p.contains=bc.contains}(a);var bc=/Until$/,bd=/^(?:parents|prev(?:Until|All))/,be=/^.[^:#\[\.,]*$/,bf=p.expr.match.needsContext,bg={children:!0,contents:!0,next:!0,prev:!0};p.fn.extend({find:function(a){var b,c,d,e,f,g,h=this;if(typeof a!="string")return p(a).filter(function(){for(b=0,c=h.length;b0)for(e=d;e=0:p.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c,d=0,e=this.length,f=[],g=bf.test(a)||typeof a!="string"?p(a,b||this.context):0;for(;d-1:p.find.matchesSelector(c,a)){f.push(c);break}c=c.parentNode}}return f=f.length>1?p.unique(f):f,this.pushStack(f,"closest",a)},index:function(a){return a?typeof a=="string"?p.inArray(this[0],p(a)):p.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(a,b){var c=typeof a=="string"?p(a,b):p.makeArray(a&&a.nodeType?[a]:a),d=p.merge(this.get(),c);return this.pushStack(bh(c[0])||bh(d[0])?d:p.unique(d))},addBack:function(a){return this.add(a==null?this.prevObject:this.prevObject.filter(a))}}),p.fn.andSelf=p.fn.addBack,p.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return p.dir(a,"parentNode")},parentsUntil:function(a,b,c){return p.dir(a,"parentNode",c)},next:function(a){return bi(a,"nextSibling")},prev:function(a){return bi(a,"previousSibling")},nextAll:function(a){return p.dir(a,"nextSibling")},prevAll:function(a){return p.dir(a,"previousSibling")},nextUntil:function(a,b,c){return p.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return p.dir(a,"previousSibling",c)},siblings:function(a){return p.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return p.sibling(a.firstChild)},contents:function(a){return p.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:p.merge([],a.childNodes)}},function(a,b){p.fn[a]=function(c,d){var e=p.map(this,b,c);return bc.test(a)||(d=c),d&&typeof d=="string"&&(e=p.filter(d,e)),e=this.length>1&&!bg[a]?p.unique(e):e,this.length>1&&bd.test(a)&&(e=e.reverse()),this.pushStack(e,a,k.call(arguments).join(","))}}),p.extend({filter:function(a,b,c){return c&&(a=":not("+a+")"),b.length===1?p.find.matchesSelector(b[0],a)?[b[0]]:[]:p.find.matches(a,b)},dir:function(a,c,d){var e=[],f=a[c];while(f&&f.nodeType!==9&&(d===b||f.nodeType!==1||!p(f).is(d)))f.nodeType===1&&e.push(f),f=f[c];return e},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var bl="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",bm=/ jQuery\d+="(?:null|\d+)"/g,bn=/^\s+/,bo=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bp=/<([\w:]+)/,bq=/]","i"),bv=/^(?:checkbox|radio)$/,bw=/checked\s*(?:[^=]|=\s*.checked.)/i,bx=/\/(java|ecma)script/i,by=/^\s*\s*$/g,bz={option:[1,""],legend:[1,"
    ","
    "],thead:[1,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],col:[2,"","
    "],area:[1,"",""],_default:[0,"",""]},bA=bk(e),bB=bA.appendChild(e.createElement("div"));bz.optgroup=bz.option,bz.tbody=bz.tfoot=bz.colgroup=bz.caption=bz.thead,bz.th=bz.td,p.support.htmlSerialize||(bz._default=[1,"X
    ","
    "]),p.fn.extend({text:function(a){return p.access(this,function(a){return a===b?p.text(this):this.empty().append((this[0]&&this[0].ownerDocument||e).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(p.isFunction(a))return this.each(function(b){p(this).wrapAll(a.call(this,b))});if(this[0]){var b=p(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return p.isFunction(a)?this.each(function(b){p(this).wrapInner(a.call(this,b))}):this.each(function(){var b=p(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=p.isFunction(a);return this.each(function(c){p(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){p.nodeName(this,"body")||p(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(a,this.firstChild)})},before:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(a,this),"before",this.selector)}},after:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(this,a),"after",this.selector)}},remove:function(a,b){var c,d=0;for(;(c=this[d])!=null;d++)if(!a||p.filter(a,[c]).length)!b&&c.nodeType===1&&(p.cleanData(c.getElementsByTagName("*")),p.cleanData([c])),c.parentNode&&c.parentNode.removeChild(c);return this},empty:function(){var a,b=0;for(;(a=this[b])!=null;b++){a.nodeType===1&&p.cleanData(a.getElementsByTagName("*"));while(a.firstChild)a.removeChild(a.firstChild)}return this},clone:function(a,b){return a=a==null?!1:a,b=b==null?a:b,this.map(function(){return p.clone(this,a,b)})},html:function(a){return p.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(bm,""):b;if(typeof a=="string"&&!bs.test(a)&&(p.support.htmlSerialize||!bu.test(a))&&(p.support.leadingWhitespace||!bn.test(a))&&!bz[(bp.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(bo,"<$1>");try{for(;d1&&typeof j=="string"&&bw.test(j))return this.each(function(){p(this).domManip(a,c,d)});if(p.isFunction(j))return this.each(function(e){var f=p(this);a[0]=j.call(this,e,c?f.html():b),f.domManip(a,c,d)});if(this[0]){e=p.buildFragment(a,this,k),g=e.fragment,f=g.firstChild,g.childNodes.length===1&&(g=f);if(f){c=c&&p.nodeName(f,"tr");for(h=e.cacheable||l-1;i0?this.clone(!0):this).get(),p(g[e])[b](d),f=f.concat(d);return this.pushStack(f,a,g.selector)}}),p.extend({clone:function(a,b,c){var d,e,f,g;p.support.html5Clone||p.isXMLDoc(a)||!bu.test("<"+a.nodeName+">")?g=a.cloneNode(!0):(bB.innerHTML=a.outerHTML,bB.removeChild(g=bB.firstChild));if((!p.support.noCloneEvent||!p.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!p.isXMLDoc(a)){bE(a,g),d=bF(a),e=bF(g);for(f=0;d[f];++f)e[f]&&bE(d[f],e[f])}if(b){bD(a,g);if(c){d=bF(a),e=bF(g);for(f=0;d[f];++f)bD(d[f],e[f])}}return d=e=null,g},clean:function(a,b,c,d){var f,g,h,i,j,k,l,m,n,o,q,r,s=b===e&&bA,t=[];if(!b||typeof b.createDocumentFragment=="undefined")b=e;for(f=0;(h=a[f])!=null;f++){typeof h=="number"&&(h+="");if(!h)continue;if(typeof h=="string")if(!br.test(h))h=b.createTextNode(h);else{s=s||bk(b),l=b.createElement("div"),s.appendChild(l),h=h.replace(bo,"<$1>"),i=(bp.exec(h)||["",""])[1].toLowerCase(),j=bz[i]||bz._default,k=j[0],l.innerHTML=j[1]+h+j[2];while(k--)l=l.lastChild;if(!p.support.tbody){m=bq.test(h),n=i==="table"&&!m?l.firstChild&&l.firstChild.childNodes:j[1]===""&&!m?l.childNodes:[];for(g=n.length-1;g>=0;--g)p.nodeName(n[g],"tbody")&&!n[g].childNodes.length&&n[g].parentNode.removeChild(n[g])}!p.support.leadingWhitespace&&bn.test(h)&&l.insertBefore(b.createTextNode(bn.exec(h)[0]),l.firstChild),h=l.childNodes,l.parentNode.removeChild(l)}h.nodeType?t.push(h):p.merge(t,h)}l&&(h=l=s=null);if(!p.support.appendChecked)for(f=0;(h=t[f])!=null;f++)p.nodeName(h,"input")?bG(h):typeof h.getElementsByTagName!="undefined"&&p.grep(h.getElementsByTagName("input"),bG);if(c){q=function(a){if(!a.type||bx.test(a.type))return d?d.push(a.parentNode?a.parentNode.removeChild(a):a):c.appendChild(a)};for(f=0;(h=t[f])!=null;f++)if(!p.nodeName(h,"script")||!q(h))c.appendChild(h),typeof h.getElementsByTagName!="undefined"&&(r=p.grep(p.merge([],h.getElementsByTagName("script")),q),t.splice.apply(t,[f+1,0].concat(r)),f+=r.length)}return t},cleanData:function(a,b){var c,d,e,f,g=0,h=p.expando,i=p.cache,j=p.support.deleteExpando,k=p.event.special;for(;(e=a[g])!=null;g++)if(b||p.acceptData(e)){d=e[h],c=d&&i[d];if(c){if(c.events)for(f in c.events)k[f]?p.event.remove(e,f):p.removeEvent(e,f,c.handle);i[d]&&(delete i[d],j?delete e[h]:e.removeAttribute?e.removeAttribute(h):e[h]=null,p.deletedIds.push(d))}}}}),function(){var a,b;p.uaMatch=function(a){a=a.toLowerCase();var b=/(chrome)[ \/]([\w.]+)/.exec(a)||/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||a.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},a=p.uaMatch(g.userAgent),b={},a.browser&&(b[a.browser]=!0,b.version=a.version),b.chrome?b.webkit=!0:b.webkit&&(b.safari=!0),p.browser=b,p.sub=function(){function a(b,c){return new a.fn.init(b,c)}p.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function c(c,d){return d&&d instanceof p&&!(d instanceof a)&&(d=a(d)),p.fn.init.call(this,c,d,b)},a.fn.init.prototype=a.fn;var b=a(e);return a}}();var bH,bI,bJ,bK=/alpha\([^)]*\)/i,bL=/opacity=([^)]*)/,bM=/^(top|right|bottom|left)$/,bN=/^(none|table(?!-c[ea]).+)/,bO=/^margin/,bP=new RegExp("^("+q+")(.*)$","i"),bQ=new RegExp("^("+q+")(?!px)[a-z%]+$","i"),bR=new RegExp("^([-+])=("+q+")","i"),bS={},bT={position:"absolute",visibility:"hidden",display:"block"},bU={letterSpacing:0,fontWeight:400},bV=["Top","Right","Bottom","Left"],bW=["Webkit","O","Moz","ms"],bX=p.fn.toggle;p.fn.extend({css:function(a,c){return p.access(this,function(a,c,d){return d!==b?p.style(a,c,d):p.css(a,c)},a,c,arguments.length>1)},show:function(){return b$(this,!0)},hide:function(){return b$(this)},toggle:function(a,b){var c=typeof a=="boolean";return p.isFunction(a)&&p.isFunction(b)?bX.apply(this,arguments):this.each(function(){(c?a:bZ(this))?p(this).show():p(this).hide()})}}),p.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bH(a,"opacity");return c===""?"1":c}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":p.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!a||a.nodeType===3||a.nodeType===8||!a.style)return;var f,g,h,i=p.camelCase(c),j=a.style;c=p.cssProps[i]||(p.cssProps[i]=bY(j,i)),h=p.cssHooks[c]||p.cssHooks[i];if(d===b)return h&&"get"in h&&(f=h.get(a,!1,e))!==b?f:j[c];g=typeof d,g==="string"&&(f=bR.exec(d))&&(d=(f[1]+1)*f[2]+parseFloat(p.css(a,c)),g="number");if(d==null||g==="number"&&isNaN(d))return;g==="number"&&!p.cssNumber[i]&&(d+="px");if(!h||!("set"in h)||(d=h.set(a,d,e))!==b)try{j[c]=d}catch(k){}},css:function(a,c,d,e){var f,g,h,i=p.camelCase(c);return c=p.cssProps[i]||(p.cssProps[i]=bY(a.style,i)),h=p.cssHooks[c]||p.cssHooks[i],h&&"get"in h&&(f=h.get(a,!0,e)),f===b&&(f=bH(a,c)),f==="normal"&&c in bU&&(f=bU[c]),d||e!==b?(g=parseFloat(f),d||p.isNumeric(g)?g||0:f):f},swap:function(a,b,c){var d,e,f={};for(e in b)f[e]=a.style[e],a.style[e]=b[e];d=c.call(a);for(e in b)a.style[e]=f[e];return d}}),a.getComputedStyle?bH=function(b,c){var d,e,f,g,h=a.getComputedStyle(b,null),i=b.style;return h&&(d=h[c],d===""&&!p.contains(b.ownerDocument,b)&&(d=p.style(b,c)),bQ.test(d)&&bO.test(c)&&(e=i.width,f=i.minWidth,g=i.maxWidth,i.minWidth=i.maxWidth=i.width=d,d=h.width,i.width=e,i.minWidth=f,i.maxWidth=g)),d}:e.documentElement.currentStyle&&(bH=function(a,b){var c,d,e=a.currentStyle&&a.currentStyle[b],f=a.style;return e==null&&f&&f[b]&&(e=f[b]),bQ.test(e)&&!bM.test(b)&&(c=f.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":e,e=f.pixelLeft+"px",f.left=c,d&&(a.runtimeStyle.left=d)),e===""?"auto":e}),p.each(["height","width"],function(a,b){p.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth===0&&bN.test(bH(a,"display"))?p.swap(a,bT,function(){return cb(a,b,d)}):cb(a,b,d)},set:function(a,c,d){return b_(a,c,d?ca(a,b,d,p.support.boxSizing&&p.css(a,"boxSizing")==="border-box"):0)}}}),p.support.opacity||(p.cssHooks.opacity={get:function(a,b){return bL.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=p.isNumeric(b)?"alpha(opacity="+b*100+")":"",f=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&p.trim(f.replace(bK,""))===""&&c.removeAttribute){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bK.test(f)?f.replace(bK,e):f+" "+e}}),p(function(){p.support.reliableMarginRight||(p.cssHooks.marginRight={get:function(a,b){return p.swap(a,{display:"inline-block"},function(){if(b)return bH(a,"marginRight")})}}),!p.support.pixelPosition&&p.fn.position&&p.each(["top","left"],function(a,b){p.cssHooks[b]={get:function(a,c){if(c){var d=bH(a,b);return bQ.test(d)?p(a).position()[b]+"px":d}}}})}),p.expr&&p.expr.filters&&(p.expr.filters.hidden=function(a){return a.offsetWidth===0&&a.offsetHeight===0||!p.support.reliableHiddenOffsets&&(a.style&&a.style.display||bH(a,"display"))==="none"},p.expr.filters.visible=function(a){return!p.expr.filters.hidden(a)}),p.each({margin:"",padding:"",border:"Width"},function(a,b){p.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bV[d]+b]=e[d]||e[d-2]||e[0];return f}},bO.test(a)||(p.cssHooks[a+b].set=b_)});var cd=/%20/g,ce=/\[\]$/,cf=/\r?\n/g,cg=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,ch=/^(?:select|textarea)/i;p.fn.extend({serialize:function(){return p.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?p.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ch.test(this.nodeName)||cg.test(this.type))}).map(function(a,b){var c=p(this).val();return c==null?null:p.isArray(c)?p.map(c,function(a,c){return{name:b.name,value:a.replace(cf,"\r\n")}}):{name:b.name,value:c.replace(cf,"\r\n")}}).get()}}),p.param=function(a,c){var d,e=[],f=function(a,b){b=p.isFunction(b)?b():b==null?"":b,e[e.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=p.ajaxSettings&&p.ajaxSettings.traditional);if(p.isArray(a)||a.jquery&&!p.isPlainObject(a))p.each(a,function(){f(this.name,this.value)});else for(d in a)ci(d,a[d],c,f);return e.join("&").replace(cd,"+")};var cj,ck,cl=/#.*$/,cm=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,cn=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,co=/^(?:GET|HEAD)$/,cp=/^\/\//,cq=/\?/,cr=/)<[^<]*)*<\/script>/gi,cs=/([?&])_=[^&]*/,ct=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,cu=p.fn.load,cv={},cw={},cx=["*/"]+["*"];try{ck=f.href}catch(cy){ck=e.createElement("a"),ck.href="",ck=ck.href}cj=ct.exec(ck.toLowerCase())||[],p.fn.load=function(a,c,d){if(typeof a!="string"&&cu)return cu.apply(this,arguments);if(!this.length)return this;var e,f,g,h=this,i=a.indexOf(" ");return i>=0&&(e=a.slice(i,a.length),a=a.slice(0,i)),p.isFunction(c)?(d=c,c=b):c&&typeof c=="object"&&(f="POST"),p.ajax({url:a,type:f,dataType:"html",data:c,complete:function(a,b){d&&h.each(d,g||[a.responseText,b,a])}}).done(function(a){g=arguments,h.html(e?p("
    ").append(a.replace(cr,"")).find(e):a)}),this},p.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){p.fn[b]=function(a){return this.on(b,a)}}),p.each(["get","post"],function(a,c){p[c]=function(a,d,e,f){return p.isFunction(d)&&(f=f||e,e=d,d=b),p.ajax({type:c,url:a,data:d,success:e,dataType:f})}}),p.extend({getScript:function(a,c){return p.get(a,b,c,"script")},getJSON:function(a,b,c){return p.get(a,b,c,"json")},ajaxSetup:function(a,b){return b?cB(a,p.ajaxSettings):(b=a,a=p.ajaxSettings),cB(a,b),a},ajaxSettings:{url:ck,isLocal:cn.test(cj[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":cx},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":p.parseJSON,"text xml":p.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:cz(cv),ajaxTransport:cz(cw),ajax:function(a,c){function y(a,c,f,i){var k,s,t,u,w,y=c;if(v===2)return;v=2,h&&clearTimeout(h),g=b,e=i||"",x.readyState=a>0?4:0,f&&(u=cC(l,x,f));if(a>=200&&a<300||a===304)l.ifModified&&(w=x.getResponseHeader("Last-Modified"),w&&(p.lastModified[d]=w),w=x.getResponseHeader("Etag"),w&&(p.etag[d]=w)),a===304?(y="notmodified",k=!0):(k=cD(l,u),y=k.state,s=k.data,t=k.error,k=!t);else{t=y;if(!y||a)y="error",a<0&&(a=0)}x.status=a,x.statusText=(c||y)+"",k?o.resolveWith(m,[s,y,x]):o.rejectWith(m,[x,y,t]),x.statusCode(r),r=b,j&&n.trigger("ajax"+(k?"Success":"Error"),[x,l,k?s:t]),q.fireWith(m,[x,y]),j&&(n.trigger("ajaxComplete",[x,l]),--p.active||p.event.trigger("ajaxStop"))}typeof a=="object"&&(c=a,a=b),c=c||{};var d,e,f,g,h,i,j,k,l=p.ajaxSetup({},c),m=l.context||l,n=m!==l&&(m.nodeType||m instanceof p)?p(m):p.event,o=p.Deferred(),q=p.Callbacks("once memory"),r=l.statusCode||{},t={},u={},v=0,w="canceled",x={readyState:0,setRequestHeader:function(a,b){if(!v){var c=a.toLowerCase();a=u[c]=u[c]||a,t[a]=b}return this},getAllResponseHeaders:function(){return v===2?e:null},getResponseHeader:function(a){var c;if(v===2){if(!f){f={};while(c=cm.exec(e))f[c[1].toLowerCase()]=c[2]}c=f[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){return v||(l.mimeType=a),this},abort:function(a){return a=a||w,g&&g.abort(a),y(0,a),this}};o.promise(x),x.success=x.done,x.error=x.fail,x.complete=q.add,x.statusCode=function(a){if(a){var b;if(v<2)for(b in a)r[b]=[r[b],a[b]];else b=a[x.status],x.always(b)}return this},l.url=((a||l.url)+"").replace(cl,"").replace(cp,cj[1]+"//"),l.dataTypes=p.trim(l.dataType||"*").toLowerCase().split(s),l.crossDomain==null&&(i=ct.exec(l.url.toLowerCase())||!1,l.crossDomain=i&&i.join(":")+(i[3]?"":i[1]==="http:"?80:443)!==cj.join(":")+(cj[3]?"":cj[1]==="http:"?80:443)),l.data&&l.processData&&typeof l.data!="string"&&(l.data=p.param(l.data,l.traditional)),cA(cv,l,c,x);if(v===2)return x;j=l.global,l.type=l.type.toUpperCase(),l.hasContent=!co.test(l.type),j&&p.active++===0&&p.event.trigger("ajaxStart");if(!l.hasContent){l.data&&(l.url+=(cq.test(l.url)?"&":"?")+l.data,delete l.data),d=l.url;if(l.cache===!1){var z=p.now(),A=l.url.replace(cs,"$1_="+z);l.url=A+(A===l.url?(cq.test(l.url)?"&":"?")+"_="+z:"")}}(l.data&&l.hasContent&&l.contentType!==!1||c.contentType)&&x.setRequestHeader("Content-Type",l.contentType),l.ifModified&&(d=d||l.url,p.lastModified[d]&&x.setRequestHeader("If-Modified-Since",p.lastModified[d]),p.etag[d]&&x.setRequestHeader("If-None-Match",p.etag[d])),x.setRequestHeader("Accept",l.dataTypes[0]&&l.accepts[l.dataTypes[0]]?l.accepts[l.dataTypes[0]]+(l.dataTypes[0]!=="*"?", "+cx+"; q=0.01":""):l.accepts["*"]);for(k in l.headers)x.setRequestHeader(k,l.headers[k]);if(!l.beforeSend||l.beforeSend.call(m,x,l)!==!1&&v!==2){w="abort";for(k in{success:1,error:1,complete:1})x[k](l[k]);g=cA(cw,l,c,x);if(!g)y(-1,"No Transport");else{x.readyState=1,j&&n.trigger("ajaxSend",[x,l]),l.async&&l.timeout>0&&(h=setTimeout(function(){x.abort("timeout")},l.timeout));try{v=1,g.send(t,y)}catch(B){if(v<2)y(-1,B);else throw B}}return x}return x.abort()},active:0,lastModified:{},etag:{}});var cE=[],cF=/\?/,cG=/(=)\?(?=&|$)|\?\?/,cH=p.now();p.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=cE.pop()||p.expando+"_"+cH++;return this[a]=!0,a}}),p.ajaxPrefilter("json jsonp",function(c,d,e){var f,g,h,i=c.data,j=c.url,k=c.jsonp!==!1,l=k&&cG.test(j),m=k&&!l&&typeof i=="string"&&!(c.contentType||"").indexOf("application/x-www-form-urlencoded")&&cG.test(i);if(c.dataTypes[0]==="jsonp"||l||m)return f=c.jsonpCallback=p.isFunction(c.jsonpCallback)?c.jsonpCallback():c.jsonpCallback,g=a[f],l?c.url=j.replace(cG,"$1"+f):m?c.data=i.replace(cG,"$1"+f):k&&(c.url+=(cF.test(j)?"&":"?")+c.jsonp+"="+f),c.converters["script json"]=function(){return h||p.error(f+" was not called"),h[0]},c.dataTypes[0]="json",a[f]=function(){h=arguments},e.always(function(){a[f]=g,c[f]&&(c.jsonpCallback=d.jsonpCallback,cE.push(f)),h&&p.isFunction(g)&&g(h[0]),h=g=b}),"script"}),p.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){return p.globalEval(a),a}}}),p.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),p.ajaxTransport("script",function(a){if(a.crossDomain){var c,d=e.head||e.getElementsByTagName("head")[0]||e.documentElement;return{send:function(f,g){c=e.createElement("script"),c.async="async",a.scriptCharset&&(c.charset=a.scriptCharset),c.src=a.url,c.onload=c.onreadystatechange=function(a,e){if(e||!c.readyState||/loaded|complete/.test(c.readyState))c.onload=c.onreadystatechange=null,d&&c.parentNode&&d.removeChild(c),c=b,e||g(200,"success")},d.insertBefore(c,d.firstChild)},abort:function(){c&&c.onload(0,1)}}}});var cI,cJ=a.ActiveXObject?function(){for(var a in cI)cI[a](0,1)}:!1,cK=0;p.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&cL()||cM()}:cL,function(a){p.extend(p.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(p.ajaxSettings.xhr()),p.support.ajax&&p.ajaxTransport(function(c){if(!c.crossDomain||p.support.cors){var d;return{send:function(e,f){var g,h,i=c.xhr();c.username?i.open(c.type,c.url,c.async,c.username,c.password):i.open(c.type,c.url,c.async);if(c.xhrFields)for(h in c.xhrFields)i[h]=c.xhrFields[h];c.mimeType&&i.overrideMimeType&&i.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(h in e)i.setRequestHeader(h,e[h])}catch(j){}i.send(c.hasContent&&c.data||null),d=function(a,e){var h,j,k,l,m;try{if(d&&(e||i.readyState===4)){d=b,g&&(i.onreadystatechange=p.noop,cJ&&delete cI[g]);if(e)i.readyState!==4&&i.abort();else{h=i.status,k=i.getAllResponseHeaders(),l={},m=i.responseXML,m&&m.documentElement&&(l.xml=m);try{l.text=i.responseText}catch(a){}try{j=i.statusText}catch(n){j=""}!h&&c.isLocal&&!c.crossDomain?h=l.text?200:404:h===1223&&(h=204)}}}catch(o){e||f(-1,o)}l&&f(h,j,l,k)},c.async?i.readyState===4?setTimeout(d,0):(g=++cK,cJ&&(cI||(cI={},p(a).unload(cJ)),cI[g]=d),i.onreadystatechange=d):d()},abort:function(){d&&d(0,1)}}}});var cN,cO,cP=/^(?:toggle|show|hide)$/,cQ=new RegExp("^(?:([-+])=|)("+q+")([a-z%]*)$","i"),cR=/queueHooks$/,cS=[cY],cT={"*":[function(a,b){var c,d,e=this.createTween(a,b),f=cQ.exec(b),g=e.cur(),h=+g||0,i=1,j=20;if(f){c=+f[2],d=f[3]||(p.cssNumber[a]?"":"px");if(d!=="px"&&h){h=p.css(e.elem,a,!0)||c||1;do i=i||".5",h=h/i,p.style(e.elem,a,h+d);while(i!==(i=e.cur()/g)&&i!==1&&--j)}e.unit=d,e.start=h,e.end=f[1]?h+(f[1]+1)*c:c}return e}]};p.Animation=p.extend(cW,{tweener:function(a,b){p.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");var c,d=0,e=a.length;for(;d-1,j={},k={},l,m;i?(k=e.position(),l=k.top,m=k.left):(l=parseFloat(g)||0,m=parseFloat(h)||0),p.isFunction(b)&&(b=b.call(a,c,f)),b.top!=null&&(j.top=b.top-f.top+l),b.left!=null&&(j.left=b.left-f.left+m),"using"in b?b.using.call(a,j):e.css(j)}},p.fn.extend({position:function(){if(!this[0])return;var a=this[0],b=this.offsetParent(),c=this.offset(),d=c_.test(b[0].nodeName)?{top:0,left:0}:b.offset();return c.top-=parseFloat(p.css(a,"marginTop"))||0,c.left-=parseFloat(p.css(a,"marginLeft"))||0,d.top+=parseFloat(p.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(p.css(b[0],"borderLeftWidth"))||0,{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||e.body;while(a&&!c_.test(a.nodeName)&&p.css(a,"position")==="static")a=a.offsetParent;return a||e.body})}}),p.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);p.fn[a]=function(e){return p.access(this,function(a,e,f){var g=da(a);if(f===b)return g?c in g?g[c]:g.document.documentElement[e]:a[e];g?g.scrollTo(d?p(g).scrollLeft():f,d?f:p(g).scrollTop()):a[e]=f},a,e,arguments.length,null)}}),p.each({Height:"height",Width:"width"},function(a,c){p.each({padding:"inner"+a,content:c,"":"outer"+a},function(d,e){p.fn[e]=function(e,f){var g=arguments.length&&(d||typeof e!="boolean"),h=d||(e===!0||f===!0?"margin":"border");return p.access(this,function(c,d,e){var f;return p.isWindow(c)?c.document.documentElement["client"+a]:c.nodeType===9?(f=c.documentElement,Math.max(c.body["scroll"+a],f["scroll"+a],c.body["offset"+a],f["offset"+a],f["client"+a])):e===b?p.css(c,d,e,h):p.style(c,d,e,h)},c,g?e:b,g,null)}})}),a.jQuery=a.$=p,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return p})})(window); \ No newline at end of file diff --git a/blayer-webif/style.css b/blayer-webif/style.css new file mode 100644 index 0000000..e8f25b5 --- /dev/null +++ b/blayer-webif/style.css @@ -0,0 +1,87 @@ +body { + background-color: #2f2f2f; + font-family: "lucida grande",tahoma, verdana, sans-serif; + font-size: 0.8em; + color: #cccccc; +} + +h1 { + font-size: 1.5em; +} + +#containerleft { + width: 46%; + margin-left: auto; + padding: 20px; + float: left; +} +#containerright { + width: 46%; + padding: 20px; + float: left; +} + +div.box { + border-radius: 10px; + border-color: #000000; + border-width: 1px; + border-style: solid; + /*width: 50%;*/ + background-color: #3d3d3d; + margin: auto; + margin-bottom: 30px; + padding: 10px; + box-shadow: 3px 3px 3px 3px #272727; +} + +p.boxhead { + color: #006483; + font-weight: bold; + margin-top: 0px; +} + +#tagtable td:first-child { + font-style: italic; + padding-right: 30px; +} +#tagtable td { + font-size: 0.8em; +} + +#filenames a, #filenames a:visited { + text-decoration: none; + color: #cccccc; +} + +.notificationtext { + padding-left: 20px; + color: #ffffff; + font-size: 0.7em; +} + +.controlbuttons { + padding: 10px; +} +.controlbuttons a, .controlbutton a:visited { + padding: 5px; + border: 1px solid #cccccc; + border-radius: 4px; + color: #cccccc; + text-decoration: none; +} + +.controlbuttons a:hover { + background: #006483; +} + +.label { + font-style: italic; +} + +.clear { + clear:both; + width: 0px; + height: 0px; +} + + diff --git a/blayer.cfg b/blayer.cfg new file mode 100644 index 0000000..c6a80c0 --- /dev/null +++ b/blayer.cfg @@ -0,0 +1,17 @@ +[control] +bind_host = 0.0.0.0 +bind_port = 4243 + +[player] +animation_dir = /var/tmp/blup2/ +playlist = /var/tmp/blup2/playlist.txt +shuffle = true +autorefresh = true +looptime = 3 +maxplaynext = 3 + +[output] +output = blp:localhost:4242 +#output = blp:bastel0:4242 +#output = 'shell' + diff --git a/blayer.py b/blayer.py new file mode 100644 index 0000000..abcf568 --- /dev/null +++ b/blayer.py @@ -0,0 +1,428 @@ +#!/usr/bin/python + +import threading +import random +import os.path +import os +import time +import socket +import asyncore +import asynchat +import cmd +import ConfigParser +import sys +import json + +import blup.animation as Animation +import blup.output as BlupOutput +import blup.frame as Frame + + +CONFIG_DEFAULTS = { + 'output': 'shell', + 'playlist': '', + 'animation_dir': '.', + 'enabled': False, + 'shuffle': False, + 'autorefresh': False +} + +def find(f, list): + for x in list: + if f(x): + return x + +class AnimationInfo(): + def __init__(self, filename): + self.__filename = filename + self.__tags = None + self.__valid = None + + @property + def filesize(self): + try: + statinfo = os.stat(self.__filename) + return statinfo.st_size + except OSError: + return -1 + + @property + def mtime(self): + try: + statinfo = os.stat(self.__filename) + return statinfo.st_mtime + except OSError: + return -1 + + @property + def tags(self): + return self.__tags + + @property + def valid(self): + if self.__valid == None: + try: + self.check() + except Animation.AnimationFileError: + pass + return self.__valid + + @property + def filename(self): + return self.__filename + + def check(self): + self.__valid = False + self.__tags = None + + anim = Animation.load(self.__filename) + + self.__valid = True + self.__tags = anim.tags + + +class AnimationDatabase(): + def __init__(self, animDir): + self.__animDir = animDir + self.__animInfos = [] + self.__lock = threading.Lock() + + def update(self): + with self.__lock: + oldAnimInfos = self.__animInfos + animInfos = [] + + for (dirpath, dirnames, filenames) in os.walk(self.__animDir): + files = map(lambda f: os.path.join(dirpath, f), filenames) + files = map(lambda f: os.path.relpath(os.path.join(dirpath, f), self.__animDir), filenames) + animInfos.extend(map(lambda f: AnimationInfo(f), files)) + #animInfos = filter(lambda a: a.valid, animInfos) + + newAnimInfos = [] + for animInfo in animInfos: + oldAnimInfo = find(lambda a: a.filename == animInfo.filename, oldAnimInfos) + if oldAnimInfo is not None and oldAnimInfo.mtime == animInfo.mtime: + newAnimInfos.append(oldAnimInfo) + else: + if animInfo.valid: + newAnimInfos.append(animInfo) + + self.__animInfos = newAnimInfos + + def findAnimation(self, filename): + with self.__lock: + animInfo = find(lambda a: a.filename == filename, self.__animInfos) + if animInfo is not None: + return animInfo + else: + raise Exception('animation not found!') + + def containsAnimation(self, filename): + with self.__lock: + animInfo = find(lambda a: a.filename == filename, self.__animInfos) + return (animInfo is not None) + + def getAllFilenames(self): + with self.__lock: + return map(lambda a: a.filename, self.__animInfos) + + +#class Playlist(list): +# def __init__(self, animationDatabase, filename=None): +# self.__animDb = animationDatabase +# self.__filename = filename +# if filename == None: +# self.append(self.__animDb.getAllFilenames()) +# else: +# f = open(filename, 'r') +# lines = f.read().split('\n') +# f.close() +# +# for line in lines: +# if self.__animDb.containsAnimation(line): +# self.append(line) +# +# def refresh(self): +# #f = open(filename, 'r') +# #lines = f.read().split('\n') +# #f.close() +# pass +# +# #for line in lines: +# # if self.__animDb.containsAnimation(line): +# # self.append(line) + + + +class PlayerThread(threading.Thread): + def __init__(self, database, output): + threading.Thread.__init__(self) + self.__playlist = [] + self.database = database + self.__playNext = [] + self.maxPlayNext = 3 + self.__output = output + self.shuffle = False + self.autoRefresh = True + self.loopTime = 10 + self.gap = 800 + self.__running = False + self.__player = Animation.AnimationPlayer() + self.__currentIndex = 0 + self.__currentFile = '' + self.__paused = False + + @property + def currentFile(self): + if not self.__paused: + return self.__currentFile + else: + return "" + + @property + def currentFileInfo(self): + if not self.__paused: + return self.database.findAnimation(self.__currentFile) + else: + # TODO: mhh... + return None + + @property + def paused(self): + return self.__paused + + def playNext(self, next): + if len(self.__playNext) < self.maxPlayNext: + self.__playNext.append(next) + else: + raise Exception + + def next(self): + self.__player.stop() + + def togglePaused(self): + self.__paused = not self.__paused + if self.__paused: + self.__player.stop() + + def refreshPlaylist(self): + self.database.update() + self.__playlist = self.database.getAllFilenames() + + def loadPlaylistFile(self): + raise Exception('not yet implemented :(') + + def terminate(self): + self.__player.stop() + self.__running = False + + def run(self): + self.__running = True + while self.__running: + anim = None + if self.autoRefresh: # and self.__currentIndex == len(self.__playlist) - 1: + self.refreshPlaylist() + + if len(self.__playNext) == 0: + if len(self.__playlist) == 0: + print 'busywait!!' + continue + + if self.shuffle: + self.__currentFile = self.__playlist[random.randint(0, len(self.__playlist) - 1)] + else: + if self.__currentIndex >= len(self.__playlist): + self.__currentIndex = 0 + self.__currentFile = self.__playlist[self.__currentIndex] + self.__currentIndex += 1 + else: + self.__currentFile = self.__playNext.pop(0) + anim = Animation.load(self.__currentFile) + + count = 1 + if anim.tags.has_key('loop') and anim.tags['loop'].lower() in ['yes', 'true']: + if anim.duration < self.loopTime * 1000: + count = (self.loopTime * 1000) / anim.duration + + + print 'playing:', anim + print 'tags:', anim.tags + self.__player.play(anim, self.__output, count=count) + print 'elapsed: ', self.__player.elapsed + + if self.__paused: + # TODO: use correct frame size + self.__output.sendFrame(Frame.Frame(18,8)) + while self.__paused: + time.sleep(1) + print 'busywait!!' + + if self.gap > 0 and self.__running: + # TODO: use correct frame size + self.__output.sendFrame(Frame.Frame(18,8)) + time.sleep(self.gap / 1000.0) + + +class PlayerControlClientHandler(asynchat.async_chat): + def __init__(self, sock, player): + asynchat.async_chat.__init__(self, sock=sock) + self.set_terminator('\n') + self.__buffer = '' + self.__player = player + + def collect_incoming_data(self, data): + self.__buffer += data + + def sendPacket(self, data): + self.send(json.dumps(data) + '\n') + + def found_terminator(self): + try: + data = json.loads(self.__buffer) + except ValueError: + self.sendPacket({'error': 'invalid json'}) + return + finally: + self.__buffer = '' + + try: + cmd = data['cmd'] + except (TypeError, KeyError): + self.sendPacket({'error': 'no command given'}) + return + + if cmd == 'refresh': + self.__player.refreshPlaylist() + self.sendPacket({'refresh': 'ok'}) + elif cmd == 'next': + self.__player.next() + self.sendPacket({'next': 'ok'}) + elif cmd == 'togglepaused': + self.__player.togglePaused() + self.sendPacket({'togglepaused': 'ok', 'paused': self.__player.paused}) + elif cmd == 'getpaused': + self.sendPacket({'paused': self.__player.paused}) + elif cmd == 'playnext': + if data.has_key('filename') and type(data['filename']) in [str, unicode]: + try: + self.__player.playNext(data['filename']) + except Exception: + self.sendPacket({'error':'too many animations queued'}) + else: + self.sendPacket({'playnext':'ok'}) + else: + self.sendPacket({'error': 'no or invalid animation file'}) + elif cmd == 'getallfilenames': + self.sendPacket({'allfilenames': self.__player.database.getAllFilenames()}) + elif cmd == 'getcurrentfileinfo': + if not self.__player.paused: + info = self.__player.currentFileInfo.tags + else: + info = {} + self.sendPacket({'currentfileinfo': info}) + elif cmd == 'getcurrentfilename': + self.sendPacket({'currentfilename': self.__player.currentFile}) + else: + self.sendPacket({'error': 'unknown command'}) + + +class PlayerControlServer(asyncore.dispatcher): + def __init__(self, host, port, player): + asyncore.dispatcher.__init__(self) + self.create_socket(socket.AF_INET, socket.SOCK_STREAM) + self.set_reuse_addr() + self.bind((host, port)) + self.listen(5) + self.__player = player + + def handle_accept(self): + pair = self.accept() + if pair is not None: + sock, addr = pair + print 'control connetcion from', addr + PlayerControlClientHandler(sock, self.__player) + + + + + +# +# main +# + +cfg = ConfigParser.SafeConfigParser(CONFIG_DEFAULTS) +cfg.read('/var/tmp/blup2/blayer.cfg') + +try: + out = BlupOutput.getOutput(cfg.get('output', 'output')) +except ConfigParser.NoSectionError: + sys.err.write('config error: missing \'output\' section') +except ConfigParser.NoOptionError: + sys.err.write('config error: missing \'output\' option in \'output\' section') + +try: + animation_dir = cfg.get('player', 'animation_dir') +except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): + animation_dir = '.' + +db = AnimationDatabase(animation_dir) + +print 'updating database...' +db.update() +print 'done with updating database...' +print 'updating database (again)...' +db.update() +print 'done with updating database...' +#print db.getAllFilenames() + +#try: +# playlist = Playlist(db, cfg.get('player', 'playlist')) +# print 'file-playluist:', playlist +#except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): +# playlist = Playlist(db) +# print 'db-playluist:', playlist + +blayer = PlayerThread(db, out) +blayer.refreshPlaylist() +#sys.exit(0) + +try: + blayer.loopTime = cfg.getint('player', 'looptime') +except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): + pass + +try: + blayer.shuffle = cfg.getboolean('player', 'shuffle') +except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): + pass + +try: + blayer.autoRefresh = cfg.getboolean('player', 'autorefresh') +except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): + pass + +try: + blayer.maxPlayNext = cfg.getint('player', 'maxplaynext') +except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): + pass + +blayer.start() + +try: + cser = PlayerControlServer(cfg.get('control', 'bind_host'), cfg.getint('control', 'bind_port'), blayer) +except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): + cser = None + + +try: + + if cser is not None: + asyncore.loop() + else: + blayer.join() +except KeyboardInterrupt: + print 'nawk!!' + blayer.terminate() + + + diff --git a/blayer.py.old b/blayer.py.old new file mode 100644 index 0000000..842f951 --- /dev/null +++ b/blayer.py.old @@ -0,0 +1,379 @@ +#!/usr/bin/python + +import threading +import random +import os.path +import os +import time +import socket +import asyncore +import asynchat +import cmd +import ConfigParser +import sys + +import blup.animation as Animation +import blup.output as BlupOutput +import blup.frame as Frame + + +CONFIG_DEFAULTS = { + 'output': 'shell', + 'playlist': '', + 'animation_dir': '.', + 'enabled': False, + 'shuffle': False, + 'autorefresh': False +} + +def find(f, list): + for x in list: + if f(x): + return x + +class AnimationInfo(): + def __init__(self, filename): + self.__filename = filename + self.__tags = None + self.__valid = None + + @property + def filesize(self): + try: + statinfo = os.stat(self.__filename) + return statinfo.st_size + except OSError: + return -1 + + @property + def mtime(self): + try: + statinfo = os.stat(self.__filename) + return statinfo.st_mtime + except OSError: + return -1 + + @property + def tags(self): + return self.__tags + + @property + def valid(self): + if self.__valid == None: + try: + self.check() + except Animation.AnimationFileError: + pass + return self.__valid + + @property + def filename(self): + return self.__filename + + def check(self): + self.__valid = False + self.__tags = None + + anim = Animation.load(self.__filename) + + self.__valid = True + self.__tags = anim.tags + + +class AnimationDatabase(): + def __init__(self, animDir): + self.__animDir = animDir + self.__animInfos = [] + self.__lock = threading.Lock() + + def update(self): + with self.__lock: + oldAnimInfos = self.__animInfos + animInfos = [] + + for (dirpath, dirnames, filenames) in os.walk(self.__animDir): + files = map(lambda f: os.path.join(dirpath, f), filenames) + files = map(lambda f: os.path.relpath(os.path.join(dirpath, f), self.__animDir), filenames) + animInfos.extend(map(lambda f: AnimationInfo(f), files)) + #animInfos = filter(lambda a: a.valid, animInfos) + + newAnimInfos = [] + for animInfo in animInfos: + oldAnimInfo = find(lambda a: a.filename == animInfo.filename, oldAnimInfos) + if oldAnimInfo is not None and oldAnimInfo.mtime == animInfo.mtime: + newAnimInfos.append(oldAnimInfo) + else: + if animInfo.valid: + newAnimInfos.append(animInfo) + + self.__animInfos = newAnimInfos + + def findAnimation(self, filename): + with self.__lock: + animInfo = find(lambda a: a.filename == filename, self.__animInfos) + if animInfo is not None: + return animInfo + else: + raise Exception('animation not found!') + + def containsAnimation(self, filename): + with self.__lock: + animInfo = find(lambda a: a.filename == filename, self.__animInfos) + return (animInfo is not None) + + def getAllFilenames(self): + with self.__lock: + return map(lambda a: a.filename, self.__animInfos) + + +#class Playlist(list): +# def __init__(self, animationDatabase, filename=None): +# self.__animDb = animationDatabase +# self.__filename = filename +# if filename == None: +# self.append(self.__animDb.getAllFilenames()) +# else: +# f = open(filename, 'r') +# lines = f.read().split('\n') +# f.close() +# +# for line in lines: +# if self.__animDb.containsAnimation(line): +# self.append(line) +# +# def refresh(self): +# #f = open(filename, 'r') +# #lines = f.read().split('\n') +# #f.close() +# pass +# +# #for line in lines: +# # if self.__animDb.containsAnimation(line): +# # self.append(line) + + + +class PlayerThread(threading.Thread): + def __init__(self, database, playlist, output): + threading.Thread.__init__(self) + self.__playlist = playlist + self.__database = database + self.__playNext = [] + self.maxPlayNext = 3 + self.__output = output + self.shuffle = False + self.autoRefresh = True + self.loopTime = 10 + self.gap = 800 + self.__running = False + self.__player = Animation.AnimationPlayer() + self.__currentIndex = 0 + self.__currentFile = '' + + @property + def currentFile(self): + return self.__currentFile + + def playNext(self, next): + if len(self.__playNext) < self.__maxPlayNext: + self.__playNext.append(next) + else: + raise Exception + + def refreshPlaylist(self): + self.__database.update() + self.__playlist = self.__database.getAllFilenames() + + def loadPlaylistFile(self): + raise Exception('not yet implemented :(') + + def terminate(self): + self.__player.stop() + self.__running = False + + def run(self): + self.__running = True + while self.__running: + anim = None + if self.autoRefresh: # and self.__currentIndex == len(self.__playlist) - 1: + self.refreshPlaylist() + + if len(self.__playNext) == 0: + if len(self.__playlist) == 0: + print 'busywait!!' + continue + + if self.shuffle: + self.__currentFile = self.__playlist[random.randint(0, len(self.__playlist) - 1)] + else: + if self.__currentIndex >= len(self.__playlist): + self.__currentIndex = 0 + self.__currentFile = self.__playlist[self.__currentIndex] + self.__currentIndex += 1 + else: + self.__currentFile = self.__playNext.pop(0) + anim = Animation.load(self.__currentFile) + + count = 1 + if anim.tags.has_key('loop') and anim.tags['loop'].lower() in ['yes', 'true']: + if anim.duration < self.loopTime * 1000: + count = (self.loopTime * 1000) / anim.duration + + + print 'playing:', anim + print 'tags:', anim.tags + self.__player.play(anim, self.__output, count=count) + + if self.gap > 0 and self.__running: + self.__output.sendFrame(Frame.Frame(18,8)) + time.sleep(self.gap / 1000.0) + + +class PlayerControlClientHandler(asynchat.async_chat): + def __init__(self, sock, player): + asynchat.async_chat.__init__(self, sock=sock) + self.set_terminator('\n') + self.__buffer = '' + self.__player = player + + def collect_incoming_data(self, data): + self.__buffer += data + + def found_terminator(self): + args = self.__buffer.split(' ') + args = filter(lambda a: len(a) > 0, args) + if len(args) == 0: + return + self.__buffer = '' + cmd = args.pop(0) + + if cmd == 'refresh': + self.__playlist.refresh() + self.send('ok\n') + elif cmd == 'status': + self.send('playing %s\n' % (self.__player.getCurrentFile())) + elif cmd == 'getplaylist': + plist = '\n'.join(self.__playlist.getPlaylist()) + self.send(plist + '\n') + elif cmd == 'playnext' and len(args) == 1: + try: + self.__player.playNext(args[0]) + self.send('ok\n') + except Exception: + self.send('error\n') + elif cmd == 'loadplaylist' and len(args) == 1: + try: + if args[0] == '.': + self.__playlist.loadDir() + else: + self.__playlist.loadFile(args[0]) + self.send('ok\n') + except: + self.send('error\n') + elif cmd == 'quit': + self.send('bye\n') + self.close() + else: + self.send('unknown command\n') + + +class PlayerControlServer(asyncore.dispatcher): + def __init__(self, host, port, player, playlist): + asyncore.dispatcher.__init__(self) + self.create_socket(socket.AF_INET, socket.SOCK_STREAM) + self.set_reuse_addr() + self.bind((host, port)) + self.listen(5) + self.__player = player + self.__playlist = playlist + + def handle_accept(self): + pair = self.accept() + if pair is not None: + sock, addr = pair + print 'control connetcion from', addr + PlayerControlClientHandler(sock, self.__player, self.__playlist) + + + + + +# +# main +# + +cfg = ConfigParser.SafeConfigParser(CONFIG_DEFAULTS) +cfg.read('/var/tmp/blup2/blayer.cfg') + +try: + out = BlupOutput.getOutput(cfg.get('output', 'output')) +except ConfigParser.NoSectionError: + sys.err.write('config error: missing \'output\' section') +except ConfigParser.NoOptionError: + sys.err.write('config error: missing \'output\' option in \'output\' section') + +try: + animation_dir = cfg.get('player', 'animation_dir') +except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): + animation_dir = '.' + +db = AnimationDatabase(animation_dir) + +print 'updating database...' +db.update() +print 'done with updating database...' +print 'updating database (again)...' +db.update() +print 'done with updating database...' +#print db.getAllFilenames() + +try: + playlist = Playlist(db, cfg.get('player', 'playlist')) + print 'file-playluist:', playlist +except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): + playlist = Playlist(db) + print 'db-playluist:', playlist + +blayer = PlayerThread(db, playlist, out) +#sys.exit(0) + +try: + blayer.setLoopTime(cfg.getint('player', 'looptime')) +except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): + pass + +try: + blayer.setShuffle(cfg.getboolean('player', 'shuffle')) +except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): + pass + +try: + blayer.setAutoRefresh(cfg.getboolean('player', 'autorefresh')) +except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): + pass + +try: + blayer.setMaxPlayNext(cfg.getint('player', 'maxplaynext')) +except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): + pass + +blayer.start() + +try: + cser = PlayerControlServer(cfg.get('control', 'bind_host'), cfg.getint('control', 'bind_port'), blayer, playlist) +except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): + cser = None + + +try: + + if cser is not None: + asyncore.loop() + else: + blayer.join() +except KeyboardInterrupt: + print 'nawk!!' + blayer.terminate() + + + diff --git a/blmplay.py b/blmplay.py new file mode 100644 index 0000000..88474ad --- /dev/null +++ b/blmplay.py @@ -0,0 +1,68 @@ +#!/usr/bin/python + +import sys +import getopt + +import blup.animation +import blup.output + +def printUsage(errMsg=None): + if errMsg is not None: + print 'error: %s\n' % (errMsg) + print 'usage: %s [OPTIONS] FILENAME' % (sys.argv[0]) + print 'supported options:' + print ' -o OUTPUT where to output the frames (default: shell)' + print ' --output OUTPUT\n' + print ' -h print this text' + print ' --help' + +def main(): + try: + (opts, args) = getopt.gnu_getopt(sys.argv, 'ho:', ['help', 'output=']) + opts = dict(opts) + except getopt.GetoptError as e: + printUsage(e.msg) + sys.exit(1) + + if opts.has_key('--help'): + printUsage() + sys.exit(0) + + if opts.has_key('-o'): + output = opts['-o'] + elif opts.has_key('--output'): + output = opts['--output'] + else: + output = 'shell' + + try: + out = blup.output.getOutput(output) + except blup.output.IllegalOutputSpecificationError: + print 'illegal output specification' + print 'available outputs:' + print blup.output.getOutputDescriptions() + sys.exit(1) + except Exception as e: + print 'could not initialize output: %s' % (str(e)) + sys.exit(1) + + if len(args) != 2: + printUsage() + sys.exit(1) + + try: + anim = blup.animation.load(args[1]) + except blup.animation.AnimationFileError: + print 'could not load animation' + sys.exit(1) + + player = blup.animation.AnimationPlayer() + try: + while True: + player.play(anim, out) + except KeyboardInterrupt: + sys.exit(0) + +if __name__ == "__main__": + main() + diff --git a/blphub.py b/blphub.py new file mode 100755 index 0000000..fe7def0 --- /dev/null +++ b/blphub.py @@ -0,0 +1,167 @@ +#!/usr/bin/env python3 + +import threading +import socket +import time +import getopt +import sys + +import blup.BLP +import blup.output + + +class BLPStreamReceiverThread(threading.Thread): + def __init__(self, addr, port, hub, protocol=blup.BLP.PROTO_BLP): + threading.Thread.__init__(self) + self.__addr = addr + self.__port = port + self.__hub = hub + self.__protocol = protocol + self.__running = False + + def run(self): + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.bind((self.__addr, self.__port)) + sock.settimeout(1) + + self.__running = True + while self.__running: + try: + data, addr = sock.recvfrom(2048) + except socket.timeout: + continue + + try: + frame = blup.BLP.blp2frame(data, protocol=self.__protocol) + except ValueError: + print('got an invalid frame from', addr) + else: + self.__hub.sendFrame(frame, self) + + def terminate(self): + self.__running = False + +class BLPHub(): + def __init__(self, output): + self.__output = output + self.__streams = {} + self.__lock = threading.Lock() + self.__lastFrameTimeout = -1 + self.__lastFramePriority = -1 + + def sendFrame(self, frame, stream): + with self.__lock: + if stream not in self.__streams.keys(): + return False + + priority = self.__streams[stream]['priority'] + currentTime = int(time.time() * 1000) + + accept = False + if priority >= self.__lastFramePriority: + accept = True + else: + if currentTime > self.__lastFrameTimeout: + accept = True + + if accept: + self.__lastFramePriority = priority + self.__lastFrameTimeout = ( + currentTime + self.__streams[stream]['timeout'] + ) + self.__output.sendFrame(frame) + return True + + return False + + def addStream(self, stream, priority, timeout): + if stream not in self.__streams.keys(): + self.__streams[stream] = {'priority': priority, 'timeout': timeout} + +def printUsage(errMsg=None): + if errMsg is not None: + print('error: %s\n' % (errMsg)) + print('usage: %s [OPTIONS] INPUTSTREAM ...' % (sys.argv[0])) + print('where INPUTSTREAM is given as HOST:PORT:PRIORITY:TIMEOUT') + print('supported options:') + print(' -o OUTPUT where to put the received frames') + print(' --eblp use EBLP instead of BLP') + print(' --e3blp use E3BLP instead of BLP') + print(' -h print this text') + print(' --help') + +def main(): + (opts, args) = getopt.gnu_getopt(sys.argv, 'hs:o:', + ['help', 'eblp', 'e3blp']) + opts = dict(opts) + + if '--help' in opts or '-h' in opts: + printUsage() + sys.exit(0) + + if '-o' in opts: + output = opts['-o'] + else: + output = 'shell' + + if '--eblp' in opts: + protocol = blup.BLP.PROTO_EBLP + elif '--e3blp' in opts: + protocol = blup.BLP.PROTO_E3BLP + else: + protocol = blup.BLP.PROTO_BLP + + if '--eblp' in opts and '--e3blp' in opts: + printUsage('please only specify one of --eblp or --e3blp') + sys.exit(1) + + streams = [] + for arg in args[1:]: + try: + (host, port, priority, timeout) = arg.split(':') + port = int(port) + priority = int(priority) + timeout = int(timeout) + if port <= 0 or timeout < 0: + raise ValueError + except ValueError as e: + printUsage('illegal stream specification') + sys.exit(1) + streams.append((host, port, priority, timeout)) + + if len(streams) == 0: + printUsage('please specify at least one stream') + sys.exit(1) + + try: + out = blup.output.getOutput(output) + except ValueError: + printUsage('could not initialize output') + sys.exit(1) + + hub = BLPHub(out) + + streamThreads = [] + firstThread = None + for (host, port, priority, timeout) in streams: + stream = BLPStreamReceiverThread(host, port, hub, protocol) + hub.addStream(stream, priority, timeout) + if firstThread is None: + firstThread = stream + else: + streamThreads.append(stream) + stream.start() + + try: + firstThread.run() + except KeyboardInterrupt: + if len(streamThreads) > 0: + print('waiting for listeners to terminate...') + for stream in streamThreads: + stream.terminate() + for stream in streamThreads: + stream.join() + +if __name__ == '__main__': + main() + diff --git a/blphub.sh b/blphub.sh new file mode 100755 index 0000000..460854d --- /dev/null +++ b/blphub.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +python blphub.py -o blp:bastel0:4242 localhost:4242:1:1000 localhost:42421:2:3500 + diff --git a/blpserver.py b/blpserver.py new file mode 100644 index 0000000..b732d53 --- /dev/null +++ b/blpserver.py @@ -0,0 +1,88 @@ + +import socket +import getopt +import sys + +from blup.BLP import BLPServer +import blup.BLP +import blup.output + + +def printUsage(errMsg=None): + if errMsg is not None: + print 'error: %s\n' % (errMsg) + print 'usage: %s [OPTIONS]' % (sys.argv[0]) + print 'supported options:' + print ' -a ADDR address to listen for packets (default: 127.0.0.1)' + print ' --address ADDR\n' + print ' -p PORT port to listen for packets (default: 4242)' + print ' --port PORT\n' + print ' -o OUTPUT where to put the recieved frames (default: shell)' + print ' --output OUTPUT\n' + print ' --eblp use EBLP instead of BLP\n' + print ' --e3blp use E3BLP instead of BLP\n' + print ' -h print this text' + print ' --help' + + +def main(): + try: + (opts, args) = getopt.gnu_getopt(sys.argv, 'ha:p:o:', ['help', 'address=', 'port=', 'output=', 'eblp', 'e3blp']) + opts = dict(opts) + except getopt.GetoptError as e: + printUsage(e.msg) + sys.exit(1) + + if opts.has_key('--help'): + printUsage() + sys.exit(0) + + if opts.has_key('-a'): + addr = opts['-a'] + elif opts.has_key('--address'): + addr = opts['--address'] + else: + addr = '127.0.0.1' + + if opts.has_key('-p'): + port = opts['-p'] + elif opts.has_key('--port'): + port = opts['--port'] + else: + port = 4242 + + if opts.has_key('-o'): + output = opts['-o'] + elif opts.has_key('--output'): + output = opts['--output'] + else: + output = 'shell' + + if opts.has_key('--eblp'): + protocol = blup.BLP.PROTO_EBLP + elif opts.has_key('--e3blp'): + protocol = blup.BLP.PROTO_E3BLP + else: + protocol = blup.BLP.PROTO_BLP + + if opts.has_key('--eblp') and opts.has_key('--e3blp'): + printUsage('please only specify one of --eblp or --e3blp') + sys.exit(1) + + try: + port = int(port) + except ValueError: + printUsage('invalid port specified') + sys.exit(1) + + out = blup.output.getOutput(output) + srv = BLPServer(output=out, addr=addr, port=port, protocol=protocol) + + try: + srv.serve() + except KeyboardInterrupt: + pass + +if __name__ == '__main__': + main() + diff --git a/blup/BLP.py b/blup/BLP.py new file mode 100644 index 0000000..e695a8b --- /dev/null +++ b/blup/BLP.py @@ -0,0 +1,174 @@ +""" +This is a BLP and EBLP implementation as described here: +https://wiki.blinkenarea.org/index.php/BlinkenlightsProtocolEnglish + +""" + +import threading +import socket +import struct + +from .frame import Frame +from .frame import FrameDimension + +BLP_MAGIC = b'\xde\xad\xbe\xef' +EBLP_MAGIC = b'\xfe\xed\xbe\xef' +E3BLP_MAGIC = b'\xca\xfe\xbe\xef' + +PROTO_BLP = 0 +PROTO_EBLP = 1 +PROTO_E3BLP = 2 +PROTOCOLS = [PROTO_BLP, PROTO_EBLP, PROTO_E3BLP] + +def frame2blp(frame, protocol=PROTO_BLP, depth=16): + if protocol not in PROTOCOLS: + raise ValueError('Unknown protocol: %d' % (protocol)) + if frame.depth != 2 and protocol == PROTO_BLP: + raise ValueError('BLP does not support greyscale.') + if frame.channels != 3 and protocol == PROTO_E3BLP: + raise ValueError('E3BLP does only support 3-channel frames.') + + (w, h) = frame.size + + # magic value + if protocol == PROTO_BLP: + packet = BLP_MAGIC + elif protocol == PROTO_EBLP: + packet = EBLP_MAGIC + elif protocol == PROTO_E3BLP: + packet = E3BLP_MAGIC + + # frame number, not yet implemented + packet += b'\x00\x00\x00\x00' + + # frame dimension + packet += struct.pack('>HH', w, h) + + for y in range(h): + for x in range(w): + if protocol == PROTO_BLP: + if frame.getPixel(x, y) == 1: + packet += b'\x01' + else: + packet += b'\x00' + elif protocol == PROTO_EBLP: + packet += struct.pack('B', frame.getPixel(x, y)) + elif protocol == PROTO_E3BLP: + (r,g,b) = frame.getPixel(x, y) + packet += struct.pack('BBB', r, g, b) + + return packet + +def blp2frame(packet, protocol=PROTO_BLP, depth=None): + if protocol not in PROTOCOLS: + raise ValueError('Unknown protocol: %d' % (protocol)) + if len(packet) < 13: + raise ValueError('packet is too short') + if ( (packet[0:4] != BLP_MAGIC and protocol == PROTO_BLP) or + (packet[0:4] != EBLP_MAGIC and protocol == PROTO_EBLP) or + (packet[0:4] != E3BLP_MAGIC and protocol == PROTO_E3BLP) ): + raise ValueError('MAGIC does not match') + + w = (packet[8]<<8) + packet[9] + h = (packet[10]<<8) + packet[11] + + if ( (protocol == PROTO_E3BLP and len(packet) != (w*h*3 + 12)) or + (protocol in [PROTO_BLP, PROTO_EBLP] and len(packet) != (w*h + 12)) ): + print(len(packet)) + raise ValueError('packet size does not match') + + if protocol == PROTO_BLP: + frm = Frame(FrameDimension(w, h, 2, 1)) + elif protocol == PROTO_EBLP: + if depth is None: + depth = 16 + frm = Frame(FrameDimension(w, h, depth, 1)) + elif protocol == PROTO_E3BLP: + if depth is None: + depth = 8 + frm = Frame(FrameDimension(w, h, depth, 3)) + + pixels = [] + for y in range(h): + row = [] + for x in range(w): + if protocol == PROTO_BLP: + if packet[12 + y*w + x] == '\x01': + #frm.setPixel(x, y, 1) + row.append(1) + else: + #frm.setPixel(x, y, 0) + row.append(0) + elif protocol == PROTO_EBLP: + row.append(packet[12 + y*w + x]) + elif protocol == PROTO_E3BLP: + pos = 12 + (y*w + x) * 3 + r = packet[pos] + g = packet[pos + 1] + b = packet[pos + 2] + row.append((r,g,b)) + pixels.append(row) + frm.pixels = pixels + + return frm + +class BLPServer: + def __init__(self, addr='127.0.0.1', port=4242, output=None, + protocol=PROTO_BLP): + if protocol not in PROTOCOLS: + raise ValueError('Unknown protocol: %d' % (protocol)) + + self.__addr = addr + self.__port = port + self.__output = output + self.__sock = None + self.__protocol = protocol + + if not output: + raise ValueError('serving without output isn\'t supported, yet') + + def bind(self): + self.__sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.__sock.bind((self.__addr, self.__port)) + + def handle(self): + if self.__sock is None: + return + + data, addr = self.__sock.recvfrom(1024) + try: + frame = blp2frame(data, protocol=self.__protocol) + except ValueError as e: + print('got an invalid frame from', addr) + print(e) + return + else: + self.__output.sendFrame(frame) + + def serve(self): + self.bind() + while True: + self.handle() + +class BLPServerThread(threading.Thread, BLPServer): + def __init__(self, addr, port, output, protocol=PROTO_BLP): + if protocol not in PROTOCOLS: + raise ValueError('Unknown protocol: %d' % (protocol)) + + threading.Thread.__init__(self) + BLPServer.__init__(self, addr, port, output, protocol) + self.__running = False + + def run(self): + self.bind() + self.__running = True + while self.__running: self.handle() + + def serve(self): + self.start() + + def terminate(self): + self.__running = False + + + diff --git a/blup/__init__.py b/blup/__init__.py new file mode 100644 index 0000000..ef05e95 --- /dev/null +++ b/blup/__init__.py @@ -0,0 +1,3 @@ + +# foo bar + diff --git a/blup/animation.py b/blup/animation.py new file mode 100644 index 0000000..3a39fe6 --- /dev/null +++ b/blup/animation.py @@ -0,0 +1,344 @@ +""" +This module is part of the 'blup' package and defines an animation class along +with the necessary functions to read animations from files. + +""" + +import re +import time +import xml.etree.ElementTree as ET + +from frame import Frame +from frame import FrameDimension + + +class AnimationFileError(Exception): + def __init__(self, value): + self.__value = value + def __str__(self): + return repr(self.__value) + +class AnimationFrame(Frame): + """ This class constitutes a single frame of an animation. """ + + def __init__(self, dimension, delay=100): + """ Initialize the frame with given dimension and delay. """ + Frame.__init__(self, dimension) + self.delay = int(delay) + +class AnimationIterator(object): + """ + This iterator can be used to loop over the animation's frames using + python's 'for' statement. + + """ + + def __init__(self, animation): + """ Initialize the iterator. """ + self.__animation = animation + self.__pos = 0 + + def next(self): + """ Return the next frame (if available). """ + if self.__pos >= len(self.__animation): + raise StopIteration + else: + self.__pos += 1 + return self.__animation[self.__pos - 1] + +class Animation(object): + """ This class constitutes an animation. """ + + def __init__(self, dimension): + """ Initialize the animation (without frames). """ + self.__dimension = dimension + self.__frames = [] + self.tags = {} + + + @property + def dimension(self): + return self.__dimension + + @property + def width(self): + return self.__dimension.width + @property + def height(self): + return self.__dimension.height + @property + def depth(self): + return self.__dimension.depth + @property + def channels(self): + return self.__dimension.channels + + @property + def duration(self): + """ Compute the duration of the animation. """ + return sum(map(lambda f: f.delay, self.__frames)) + + def addFrame(self, frame): + """ Append a frame to the animation. """ + if not isinstance(frame, Frame): + raise ValueError('frame is no \'Frame\' instance') + + if self.dimension != frame.dimension: + raise ValueError(('frame dimension %s does not match animation ' + + 'dimension %s' ) + % (str(self.dimension), str(frame.dimension))) + + self.__frames.append(frame) + + def __iter__(self): + """ Return an iterator to loop over the animation's frames. """ + return AnimationIterator(self) + + def __len__(self): + """ Return the number of frames. """ + return len(self.__frames) + + def __getitem__(self, i): + """ Retrieve a certain frame from the animation. """ + if i < 0 or i >= len(self): + raise IndexError('frame index out of bounds') + return self.__frames[i] + + def __delitem__(self, i): + """ Delete a certain frame from the animation. """ + if i < 0 or i >= len(self): + raise IndexError('frame index out of bounds') + del self.__frames[i] + + +class AnimationPlayer(object): + """ + This class can be used to play an Animation using an output class that + implements the 'sendFrame()' function. + + """ + + def __init__(self): + """ Initialize the player. """ + self.__playing = False + self.__elapsed = 0 + + @property + def elapsed(self): + return self.__elapsed + + def play(self, animation, output, count=1): + """ Play the animation 'count' times on 'output'. """ + self.__playing = True + self.__elapsed = 0 + for i in range(count): + for frame in animation: + output.sendFrame(frame) + + delay = frame.delay / 1000.0 + time.sleep(delay) + self.__elapsed += delay + if not self.__playing: + break + if not self.__playing: + break + + def stop(self): + """ Stop the player, if playing. """ + self.__playing = False + +def loadBlm(filename): + """ Parse a blm file and return an Animation object. """ + + f = open(filename, 'r') + + w = h = 0 + tags = {} + + header = True + framedelay = 100 + frame = [] + animframes = [] + for line in f: + if header: + m = re.match('^\s*#\s+BlinkenLights Movie (\d+)x(\d+)', line) + if m: + w = int(m.group(1)) + h = int(m.group(2)) + continue + + m = re.match('^\s*#\s+(.+)\s+=\s+(.+)\s+$', line) + if m: + key = m.group(1) + val = m.group(2) + tags[key] = val + continue + + m = re.match('^@(\d+)\s+$', line) + if m: + if header: + header = False + framedelay = int(m.group(1)) + if len(frame) > 0: + if h == 0: + h = len(frame) + else: + if len(frame) != h: + raise AnimationFileError( + 'frame height does not match specified or ' + + 'previous height') + + frm = AnimationFrame(FrameDimension(w, h, 2, 1), + delay=framedelay) + frm.pixels = frame + print frame.pixels + animframes.append(frm) + frame = [] + continue + + if not header: + if line.strip() == '' or line.strip().startswith('#'): + continue + + if re.match('^(0|1)+$', line): + line = line.strip() + if w == 0: + w = len(line) + else: + if len(line) != w: + raise AnimationFileError( + 'frame width does not match specified or ' + + 'previous width') + row = map(lambda c: int(c), list(line)) + frame.append(row) + + if h != 0 and len(frame) == h: + frm = AnimationFrame(FrameDimension(w, h, 2, 1), + delay=framedelay) + frm.pixels = frame + animframes.append(frm) + frame = [] + + continue + + f.close() + + if len(frame) > 0: + frm = AnimationFrame(FrameDimension(w, h, 2, 1), delay=framedelay) + frm.pixels = frame + animframes.append(frm) + + if len(animframes) == 0: + raise AnimationFileError('no frames found in this file') + + + a = Animation(FrameDimension(w, h, 2, 1)) + a.tags = tags + for f in animframes: + a.addFrame(f) + + return a + + +def loadBml(filename): + """ Parse a bml file and return an Animation object. """ + + try: + tree = ET.parse(filename) + except ET.ParseError: + raise AnimationFileError('invalid xml') + root = tree.getroot() + + if root.tag != 'blm': + raise AnimationFileError('the root tag needs to be a \'blm\' tag') + + try: + width = int(root.attrib['width']) + height = int(root.attrib['height']) + bits = int(root.attrib['bits']) + channels = int(root.attrib['channels']) + + dim = FrameDimension(width, height, 2**bits, channels) + except KeyError as e: + raise AnimationFileError('the root tag does not contain a ' + + '\'%s\' attribute.' % (e[0])) + + if channels not in [1, 3]: + raise AnimationFileError('unsupported channel count') + + tags = {} + frames = [] + + for child in root: + if child.tag == 'header': + for headerchild in child: + tags[headerchild.tag] = headerchild.text + # TODO (maybe) raise an exception in case no 'title' is given + + elif child.tag == 'frame': + delay = int(child.attrib['duration']) + currentframe = AnimationFrame(dim, delay) + rows = [] + for row in child: + charsPerPixel = channels + if bits > 4: + charsPerPixel *= 2 + + rowPixels = [] + for i in xrange(0, width * charsPerPixel, charsPerPixel): + if channels == 1: + pixel = row.text[i:(i + charsPerPixel)] + rowPixels.append(int(pixel, 16)) + elif channels == 3: + charsPerColor = charsPerPixel / 3 + r = row.text[i:(i + charsPerColor)] + g = row.text[(i + charsPerColor):(i + 2*charsPerColor)] + b = row.text[(i + 2*charsPerColor):(i + charsPerPixel)] + rowPixels.append((int(r, 16), int(g, 16), int(b, 16))) + rows.append(rowPixels) + currentframe.pixels = rows + frames.append(currentframe) + + a = Animation(dim) + a.tags = tags + for f in frames: + a.addFrame(f) + + return a + + + + + + + + +def load(filename): + """ + (Try to) parse the given file using all available parser functions and + return the Animation afterwards. + + """ + + loaders = [ loadBlm, loadBml ] + + anim = None + for loader in loaders: + try: + anim = loader(filename) + except AnimationFileError as e: + pass + if anim: + break + + if not anim: + raise AnimationFileError('file could not be read') + + return anim + + + + + + + diff --git a/blup/font.py b/blup/font.py new file mode 100644 index 0000000..753267e --- /dev/null +++ b/blup/font.py @@ -0,0 +1,97 @@ +""" +This module is part of the 'blup' package and defines a font class as well as +some methods to load fonts from files and draw text on frames. + +""" + +import re + +class FontFileError(Exception): + def __init__(self, value): + self.__value = value + def __str__(self): + return repr(self.__value) + +class Font(object): + + def __init__(self): + self.__chars = {} + self.__spacing = 1 + self.__height = -1 + + @property + def spacing(self): + return self.__spacing + + @spacing.setter + def spacing(self, value): + if type(self.value) == int and self.value > 0: + self.__spacing = value + else: + raise ValueError('illegal spacing given') + + @property + def height(self): + if self.__height == -1: + raise ValueError('this font does not contain any characters') + else: + return self.__height + + def __setitem__(self, item, value): + height = len(value) + if self.__height == -1: + self.__height = height + elif height != self.__height: + raise ValueError('character height does not match font height') + + if len(set(map(len, value))) != 1: + raise ValueError('character lines differ in width') + + allowedValues = set([0,1]) + for line in value: + if set(line) != allowedValues: + raise ValueError('character contains invalid pixel value') + + self.__chars[item] = value + + def __getitem__(self, item): + return self.__chars[item] + + +charPixels = None +def load(filename): + f = open(filename, 'r') + data = f.read() + + f = Font() + for line in f: + m = re.match('^\s*§(\d+)\s*$', line) + if m: + f.spacing = int(m.group(1)) + continue + + m = re.match('^\s*@(\d+)\|(\d+)\s*$', line): + if m: + charCode = int(m.group(1)) + charWidth = int(m.group(2)) + charPixels = [] + continue + + m = re.match('^\s*([01]+)\s*$', line): + if m and charPixels != None: + charRow = m.group(1) + if len(charRow) < charWidth: + raise FontFileError('char row does not contain enough pixels') + row = [] + for i in xrange(charWidth): + row.append(int(charRow[i])) + charPixels.append(row) + + continue + + + + + + + diff --git a/blup/frame.py b/blup/frame.py new file mode 100644 index 0000000..2d339b8 --- /dev/null +++ b/blup/frame.py @@ -0,0 +1,152 @@ +""" +This module is part of the 'blup' package and contains the definition of a +basic Frame class. + +""" + +class FrameDimension(object): + """ This class serves as container for the dimensions of a frame. """ + + def __init__(self, width=18, height=8, depth=2, channels=1): + self.width = width + self.height = height + self.depth = depth + self.channels = channels + + def size(self): + return (self.width, self.height) + + def depth(self): + return self.depth + + def channels(self): + return self.channels + + def __eq__(self, other): + if ( self.width == other.width and self.height == other.height and + self.depth == other.depth and self.channels == other.channels ): + return True + else: + return False + + def __ne__(self, other): + return not self.__eq__(other) + + def __str__(self): + return "(%d, %d, %d, %d)" % (self.width, self.height, self.depth, + self.channels) + +class Frame(object): + """ This class describes a single frame. """ + + def __init__(self, dimension): + """ + Initialize the frame with the given dimensions. The 'depth' parameter + describes, how many different values a single pixel can have. + + """ + self.__dimension = dimension + width = dimension.width + height = dimension.height + if dimension.channels == 1: + self.__pixels = [ [ 0 ] * width for i in range(height) ] + else: + self.__pixels = [ [ tuple([0]*dimension.channels) ] * width for i in range(height) ] + + @property + def dimension(self): + return self.__dimension + + @property + def size(self): + return (self.__dimension.width, self.__dimension.height) + @property + def depth(self): + return self.__dimension.depth + @property + def channels(self): + return self.__dimension.channels + + @property + def pixels(self): + return self.__pixels + + @pixels.setter + def pixels(self, pixels): + if len(pixels) != self.__dimension.height: + raise ValueError('not enough rows given') + widths = set(map(lambda r: len(r), pixels)) + if len(widths) != 1: + raise ValueError('rows are not of the same length') + if widths.pop() != self.__dimension.width: + raise ValueError('rows are too short') + + # TODO check depth + # TODO check number of channels + + self.__pixels = pixels + + def transform(self, depth): + """ Transform the frame to a frame with a different depth. """ + oldmaxval = self.__dimension.depth - 1 + newmaxval = depth - 1 + factor = (newmaxval*1.0) / (oldmaxval*1.0) + newdim = FrameDimension(self.__dimension.width, + self.__dimension.height, + depth, + self.__dimension.channels) + newframe = Frame(newdim) + + transformfkt = lambda p: int(round(p * factor)) + for y in range(self.__dimension.height): + if self.__dimension.channels == 1: + newframe.__pixels[y] = map(transformfkt, self.__pixels[y]) + else: + for x in range(self.__dimension.width): + newframe.__pixels[y][x] = tuple(map(transformfkt, p)) + + return newframe + + def getPixel(self, x, y): + """ Return the pixel value at (x, y). """ + return self.__pixels[y][x] + + def setPixel(self, x, y, value): + """ Set the pixel value at (x, y). """ + if self.__dimension.channels == 1: + if value < 0 or value >= self.__dimension.depth: + raise ValueError('pixel value not in depth range') + else: + if len(value) != self.__dimension.channels: + raise ValueError('channel count does not match') + for v in value: + if v < 0 or v >= self.__dimension.depth: + raise ValueError('pixel value not in depth range') + self.__pixels[y][x] = value + + + def getPixelsAsBytes(self): + """ Return the frame as a byte string. """ + if self.__dimension.channels != 1: + raise Exception('cannot convert a multi-channel frame') + + bytes = '' + for y in range(self.__dimension.width): + for x in range(self.__dimension.height): + bytes += chr(self.__pixels[x][y]) + return bytes + + def invert(self): + """ Ivert the frame. """ + for y in range(self.__dimension.height): + for x in range(self.__dimension.width): + if self.__dimension.channels == 1: + self.__pixels[x][y] = ((self.__dimension.depth - 1) - + self.__pixels[x][y]) + else: + self.__pixels[y][x] = tuple(map( + lambda x: ((self.depth - 1) - x), + self.__pixels[y][x])) + + + diff --git a/blup/output.py b/blup/output.py new file mode 100644 index 0000000..1d183c4 --- /dev/null +++ b/blup/output.py @@ -0,0 +1,444 @@ +""" +This module is part of the 'blup' package and provides several output modules +to send frames as well as a mechanism to create them from a string +specification. + +""" + +import re +import serial +import socket + +from blup import BLP + + +# +# ---- exceptions ---- +# + +class IllegalOutputSpecificationError(Exception): + """ + Will be thrown when a string is given to getOutput (see below) that is not + matched by any output modules regex. + + """ + def __str__(self): + return __class__.__name__ + + +# +# ---- generic output class to be subclassed ---- +# + +class Output(object): + def sendFrame(self, frame): + raise Exception('not implemented - just a dummy class') + + +# +# ---- MCUFOutput (MicroController Unit Frame) ---- +# + +class MCUFOutput(Output): + """ + This output module allows to send frames to blinkendevices over a serial + line using the MCUF protocol. + + """ + + moduleRegexDesc = 'mcuf:DEVICE:BAUDRATE[:DEPTH]' + moduleRegex = '^mcuf:([^:]+):(\d+)(:(\d+))?$' + + def __init__(self, port, baudrate, depth=0): + """ + Initialize the module with given port and baudrate. If depth is + specified, every frame will be transformed, if it not already has the + correct depth. + + """ + + self.__port = port + self.__baudrate = baudrate + self.__ser = serial.Serial(port=port, baudrate=baudrate) + self.__depth = depth + + @classmethod + def fromRegexMatch(cls, regexMatch): + """ Create an instance from a RegexMatch (matched with moduleRegex) """ + port = regexMatch.group(1) + baudrate = int(regexMatch.group(2)) + if regexMatch.group(4): + depth = int(regexMatch.group(4)) + else: + depth = 0 + return cls(port, baudrate, depth) + + def sendFrame(self, frame): + """ Pass a frame to the module. """ + (w, h) = frame.size + + if self.__depth != 0: + if frame.depth != self.__depth: + frame = frame.transform(self.__depth) + maxval = frame.depth - 1 + + framedata = '' + for y in range(h): + for x in range(w): + framedata += chr(frame.getPixel(x, y)) + + packet = '\x23\x54\x26\x66' + packet += '%s%s%s%s' % (chr(h>>8), chr(h & 0xff), + chr(w>>8), chr(w & 0xff)) + packet += '\x00\x01\x00%s' % (chr(maxval)) + packet += framedata + + self.__ser.write(packet) + + return True + + +# +# ---- SerialBlupOutput ---- +# + +class SerialBlupOutput(Output): + """ + This output module allows sending frames to 18x8 blinkendevices with + depth 2 over a serial line, by sending every column as a byte. + + """ + + moduleRegexDesc = 'serialblup:DEVICE:BAUDRATE' + moduleRegex = '^serialblup:([^:]+):(\d+)$' + + def __init__(self, port, baudrate): + """ Initialize the output module with the given port. """ + self.__port = port + self.__baudrate = baudrate + self.__ser = serial.Serial(port=port, baudrate=baudrate) + self.__stx2 = 'a' + self.__etx2 = 'b' + self.__stx8 = 'q' + self.__etx8 = 'b' + self.__stxRGB = chr(0xca) + self.__etxRGB = chr(0xfe) + + @classmethod + def fromRegexMatch(cls, regexMatch): + """ Create an instance from a RegexMatch (matched with moduleRegex) """ + port = regexMatch.group(1) + baudrate = int(regexMatch.group(2)) + return cls(port, baudrate) + + def sendFrame(self, frame): + """ Pass a frame to the module. """ + if frame.depth not in [2, 16] and frame.channels == 1: + raise ValueError('%s only supports frames of depth 2 or 16' % + (self.__class__.__name__)) + + if frame.channels not in [1, 3]: + raise ValueError('%s only supports frames with 1 or 3 channels' % + (self.__class__.__name__)) + + if frame.size != (18, 8): + raise ValueError('%s currently only supports 18x8 frames' % + (self.__class__.__name__)) + + if frame.channels == 1 and frame.depth == 2: + bytes = [ 0 ] * 18 + for x in range(18): + for y in range(8): + if frame.getPixel(x, y) == 1: + bytes[x] += 2**(7 - y) + + packet = self.__stx2 + ''.join(map(lambda b: chr(b), bytes)) + self.__etx2 + elif frame.channels == 1 and frame.depth == 16: + bytes = [ 0 ] * (18*4) + for x in range(18): + for y in range(8): + pixelval = frame.getPixel(x, y) + pixelnum = y*18 + x + bytes[pixelnum/2] += (pixelval << ((pixelnum % 2) * 4)) + + packet = self.__stx8 + ''.join(map(lambda b: chr(b), bytes)) + self.__etx8 + elif frame.channels == 3: + bytes = [ 0 ] * (18*8*3) + for y in range(8): + for x in range(18): + (r,g,b) = frame.getPixel(x, y) + pixelnum = y*18 + x + bytes[3*pixelnum] = r + bytes[3*pixelnum + 1] = g + bytes[3*pixelnum + 2] = b + packet = self.__stxRGB + ''.join(map(lambda b: chr(b), bytes)) + self.__etxRGB + + self.__ser.write(packet) + + return True + +# +# ---- ShellOuput ---- +# + +class ShellOutput(Output): + """ This output module prints frames with depth 2 on the terminal. """ + + moduleRegexDesc = 'shell[:ON_CHAR:OFF_CHAR]' + moduleRegex = '^shell(:(.):(.))?$' + + def __init__(self, onChar='#', offChar=' '): + """ Initialize the output. """ + self.__onChar = onChar + self.__offChar = offChar + self.__initialized = False + + @classmethod + def fromRegexMatch(cls, regexMatch): + """ Create an instance from a RegexMatch (matched with moduleRegex) """ + if regexMatch.group(2) is not None: + return cls(regexMatch.group(2), regexMatch.group(3)) + else: + return cls() + + def sendFrame(self, frame): + """ Pass a frame to the module. """ + if frame.depth != 2: + raise ValueError('%s only supports frames of depth 2' % + (self.__class__.__name__)) + + (width, height) = frame.size + + if not self.__initialized: + self.__initialized = True + print('\n' * height) + + print('\x1b[%dA' % (height + 2)) + for y in range(height): + line = '' + for x in range(width): + if frame.getPixel(x, y) == 1: + line += self.__onChar + else: + line += self.__offChar + print(line) + print('') + + return True + +# +# ---- ColorfulShellOuput ---- +# + +class ColorfulShellOutput(Output): + """ This output module prints frames on the terminal, using colors. """ + + moduleRegexDesc = 'colorfulshell' + moduleRegex = '^colorfulshell?$' + + def __init__(self): + """ Initialize the output. """ + self.__initialized = False + + @classmethod + def fromRegexMatch(cls, regexMatch): + """ Create an instance from a RegexMatch (matched with moduleRegex) """ + return cls() + + def sendFrame(self, frame): + """ Pass a frame to the module. """ + if frame.channels not in [1, 3]: + raise ValueError('%s only supports frames with 1 or 3 channels' % + (self.__class__.__name__)) + + (width, height) = frame.size + + if not self.__initialized: + self.__initialized = True + print('\n' * height) + + print('\x1b[%dA' % (height + 2)) + for y in range(height): + line = '' + for x in range(width): + if frame.channels == 3: + (r,g,b) = frame.getPixel(x, y) + if frame.depth != 5: + factor = 5 / ((frame.depth - 1) * 1.0) + r = round(r*factor) + g = round(g*factor) + b = round(b*factor) + colorval = 36*r + 6*g + b + 16 + + line += '\033[48;05;%dm ' % (colorval) + elif frame.channels == 1: + colorval = frame.getPixel(x, y) + if frame.depth != 24: + factor = (23 / ((frame.depth - 1) * 1.0)) + colorval = 232 + colorval * factor + + line += '\033[48;05;%dm ' % (colorval) + print(line) + print('') + + return True +# +# ---- BLPOutput ---- +# + +class BLPOutput(Output): + """ + This output module sends frames over the network using the UDP-based BLP + protocol. + + """ + + moduleRegexDesc = 'blp[:HOST:PORT]' + moduleRegex = '^(blp|eblp|e3blp)(:(.+):(\d+))?$' + + def __init__(self, host='127.0.0.1', port=4242, protocol=BLP.PROTO_BLP): + """ Initialize the output. Frames are sent to host:port. """ + self.__host = host + self.__port = port + self.__sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.__protocol = protocol + + @classmethod + def fromRegexMatch(cls, regexMatch): + """ Create an instance from a RegexMatch (matched with moduleRegex) """ + if regexMatch.group(1) == 'blp': + protocol = BLP.PROTO_BLP + elif regexMatch.group(1) == 'eblp': + protocol = BLP.PROTO_EBLP + elif regexMatch.group(1) == 'e3blp': + protocol = BLP.PROTO_E3BLP + + if regexMatch.group(3) and regexMatch.group(4): + host = regexMatch.group(3) + port = int(regexMatch.group(4)) + return cls(host, port, protocol) + else: + return cls(protocol=protocol) + + def sendFrame(self, frame): + """ Pass a frame to the module. """ + packet = BLP.frame2blp(frame, protocol=self.__protocol) + self.__sock.sendto(packet, (self.__host, self.__port)) + return True + +# +# ---- BlinkenbuntHDOutput ---- +# + +class BlinkenbuntHDOutput(Output): + moduleRegexDesc = 'bbunthd' + moduleRegex = '^bbunthd$' + + def __init__(self): + import neopixel + self.w = 22 + self.h = 16 + LED_COUNT = self.w * self.h + LED_PIN = 13 + LED_FREQ_HZ = 800000 + LED_DMA = 5 + LED_BRIGHTNESS = 50 + LED_INVERT = False + LED_PWM = 1 + + self.strip = neopixel.Adafruit_NeoPixel( + LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, + LED_BRIGHTNESS, LED_PWM + ) + self.strip.begin() + self._color_cls = neopixel.Color + + @classmethod + def fromRegexMatch(cls, regexMatch): + return cls() + + def sendFrame(self, frame): + for y in range(self.h): + for x in range(self.w): + if x%2 == 0: + lednum = x*self.h + y + else: + lednum = x*self.h - y + 15 + pix = frame.getPixel(x, y) + print(pix) + self.strip.setPixelColor(lednum, self._color_cls(*pix)) + self.strip.show() + +# +# ---- PygameOutput ---- +# + +class PygameOutput(Output): + moduleRegexDesc = 'pygame' + moduleRegex = '^pygame$' + + def __init__(self, width=22, height=16, scalex=8, scaley=8): + self.width = width + self.height = height + self.scalex = scalex + self.scaley = scaley + import pygame + self.__pygame = pygame + self.screen = pygame.display.set_mode((width*scalex, height*scaley)) + pygame.display.update() + + @classmethod + def fromRegexMatch(cls, regexMatch): + return cls() + + def sendFrame(self, frame): + pygame = self.__pygame + for y in range(self.height): + for x in range(self.width): + rect = (x*self.scalex, y*self.scaley, self.scalex, self.scaley) + pygame.draw.rect(self.screen, frame.getPixel(x, y), rect, 0) + pygame.display.update() + #import time + #time.sleep(0.1) + + + +# +# ---- output creator ;) ---- +# + +__outputModules = { + SerialBlupOutput.moduleRegex: SerialBlupOutput, + MCUFOutput.moduleRegex: MCUFOutput, + ShellOutput.moduleRegex: ShellOutput, + ColorfulShellOutput.moduleRegex: ColorfulShellOutput, + BLPOutput.moduleRegex: BLPOutput, + BlinkenbuntHDOutput.moduleRegex: BlinkenbuntHDOutput, + PygameOutput.moduleRegex: PygameOutput, +} + +def getOutput(outputSpec): + """ + Return an output module instance described by the given specification. + + """ + + for regex in __outputModules.keys(): + m = re.match(regex, outputSpec) + if m: + return __outputModules[regex].fromRegexMatch(regexMatch=m) + + raise IllegalOutputSpecificationError() + +def getOutputDescriptions(): + """ + Return a string containing the moduleRegexDesc of every available output + module. + + """ + + s = "" + for outputModule in __outputModules.values(): + s += " %s\n" % (outputModule.moduleRegexDesc) + return s + diff --git a/colormarq.py b/colormarq.py new file mode 100644 index 0000000..e4b6e0b --- /dev/null +++ b/colormarq.py @@ -0,0 +1,24 @@ +#!/usr/bin/python + +import sys + +import blup.animation +import writebml + +anim = blup.animation.load(sys.argv[1]) +plasmaanim = blup.animation.load('/tmp/plasma.bml') +newanim = blup.animation.Animation(anim.dimension) + +plasmaFrame = 0 +for f in anim: + newframe = blup.animation.AnimationFrame(f.dimension, f.delay) + for x in range(anim.dimension.width): + for y in range(anim.dimension.height): + p = f.getPixel(x,y) + if max(p) > 14: + newframe.setPixel(x,y, plasmaanim[plasmaFrame].getPixel(x,y)) + newanim.addFrame(newframe) + plasmaFrame += 1 + +writebml.writeBml(newanim, '/tmp/colormarq.bml') + diff --git a/colortest.py b/colortest.py new file mode 100644 index 0000000..7965f26 --- /dev/null +++ b/colortest.py @@ -0,0 +1,44 @@ +#!/usr/bin/python + +import colorsys +import time + +import blup.output +import blup.frame + +dim = blup.frame.FrameDimension(18,8,16,3) +frame = blup.frame.Frame(dim) + +HEIGHT = 8 +WIDTH = 18 + +#h = 0.0 +#for y in range(HEIGHT): +# h += 1.0 / (HEIGHT * 1.0) +# v = 0 +# s = 0.7 +# for x in range(WIDTH): +# v += 1.0 / (WIDTH * 1.0) +# print s +# color = colorsys.hsv_to_rgb(h, s, v) +# color = tuple(map(lambda c: int(round(c * 15)), color)) +# print color +# frame.setPixel(x, y, color) + +for y in range(HEIGHT): + for x in range(WIDTH): + h = (x * 1.0) / (WIDTH - 1.0) + s = 1.0 - (y * 1.0) / (HEIGHT - 0.0) + v = 1.0 - (y * 1.0) / (HEIGHT - 0.0) + color = colorsys.hsv_to_rgb(h, s, v) + color = tuple(map(lambda c: int(round(c * 15)), color)) + frame.setPixel(x, y, color) + +out = blup.output.getOutput('e3blp') +while True: + out.sendFrame(frame) + time.sleep(0.4) + + + + diff --git a/colorwischer.py b/colorwischer.py new file mode 100644 index 0000000..ee0b252 --- /dev/null +++ b/colorwischer.py @@ -0,0 +1,34 @@ +#!/usr/bin/python + +import sys + +import blup.animation +import blup.frame +import writebml +import random + +dim = blup.frame.FrameDimension(18, 8, 16, 3) +newanim = blup.animation.Animation(dim) + + +num = 10 +horiz = True +delay = 100 +for i in range(num): + newcolor = (random.randint(0, 15), random.randint(0, 15), random.randint(0, 15)) + horiz = not horiz + newframe = blup.animation.AnimationFrame(dim, delay) + if horiz: + for x in range(dim.size()[0]): + for y in range(dim.size()[1]): + newframe.setPixel(x, y, newcolor) + newanim.addFrame(newframe) + else: + for y in range(dim.size()[1]): + for x in range(dim.size()[0]): + newframe.setPixel(x, y, newcolor) + newanim.addFrame(newframe) + + + +writebml.writeBml(newanim, '/tmp/colorwischer.bml') diff --git a/eq.py b/eq.py new file mode 100644 index 0000000..0c23f07 --- /dev/null +++ b/eq.py @@ -0,0 +1,104 @@ +# Major library imports +import atexit +import pyaudio +from numpy import zeros, short, fromstring, array +from numpy.fft import fft + +import colorsys +import time +import blup.frame +import blup.output + +#NUM_SAMPLES = 512 +NUM_SAMPLES = 36 +#NUM_SAMPLES = 18 +#NUM_SAMPLES = 16 +#SAMPLING_RATE = 11025 +#SAMPLING_RATE = 11024 / 2 +SAMPLING_RATE = 1100 + +_stream = None + +def read_fft(): + global _stream + pa = None + + def cleanup_audio(): + if _stream: + _stream.stop_stream() + _stream.close() + pa.terminate() + + if _stream is None: + pa = pyaudio.PyAudio() + _stream = pa.open(format=pyaudio.paInt16, channels=1, + rate=SAMPLING_RATE, + input=True, frames_per_buffer=NUM_SAMPLES) + atexit.register(cleanup_audio) + + audio_data = fromstring(_stream.read(NUM_SAMPLES), dtype=short) + normalized_data = audio_data / 32768.0 + + return fft(normalized_data)[1:1+NUM_SAMPLES/2] + +def flatten_fft(scale = 1.0): + """ + Produces a nicer graph, I'm not sure if this is correct + """ + for i, v in enumerate(read_fft()): + yield scale * (i * v) / NUM_SAMPLES + + +def makeColor(x): + hue = (7-x) / 7.0 / 3.0 -0.1 + if hue < 0: + hue += 1 + rgb = map(lambda x: int(round(x*7)), colorsys.hsv_to_rgb(hue, 1, 1)) + return rgb +def makeSpectrumFrame(spectrum): + f = blup.frame.Frame(blup.frame.FrameDimension(18,8,8,3)) + for x, y in enumerate(spectrum): + if y > 7: + y = 7 + y = int(y) + for i in range(y): + #val = (7,7,7) + val = makeColor(i) + f.setPixel(x, 7-i, val) + return f + +def draw(): + '''Draw 3 different colour graphs''' + global NUM_SAMPLES + audio = array(list(flatten_fft(scale = 80))) + freqs = len(audio) + bass, mid, treble = triple(audio) + + colours = (0.5, 1.0, 0.5), (1, 1, 0), (1, 0.2, 0.5) + + fill(0, 0, 1) + rect(0, 0, WIDTH, 400) + translate(50, 200) + + for spectrum, col in zip((bass, mid, treble), colours): + fill(col) + for i, s in enumerate(spectrum): + rect(i, 0, 1, -abs(s)) + else: + translate(i, 0) + + audio = array(list(flatten_fft(scale = 80))) + + +out = blup.output.getOutput('colorfulshell') +while True: + audio = array(list(flatten_fft(scale=80))) + #audio = map(abs, audio) + #print len(audio) + frame = makeSpectrumFrame([ int((x/18.0)*8) for x in range(18)]) + frame = makeSpectrumFrame(audio) + #print map(int,audio) + out.sendFrame(frame) + #time.sleep(0.1) + + diff --git a/fire,py b/fire,py new file mode 100644 index 0000000..8d5063a --- /dev/null +++ b/fire,py @@ -0,0 +1,2 @@ +#!/usr/bin/python + diff --git a/fire.py b/fire.py new file mode 100644 index 0000000..8b1589f --- /dev/null +++ b/fire.py @@ -0,0 +1,63 @@ +#!/usr/bin/python + +import colorsys +import random + +import blup.frame +import blup.output +import blup.animation + +# thanks to +# http://quiteuseful.co.uk/post/96424751/beautiful-algorithms-1-fire-part-2 + + +dim = blup.frame.FrameDimension(18,8,16,3) +anim = blup.animation.Animation(dim) + +def getRandomColor(maxval): + return mmeap(lambda x: int(round(x*maxval)), colorsys.hsv_to_rgb(random.random() % 0.16666666, 1, 1)) + +def colorAvg(colors): + r = 0 + g = 0 + b = 0 + for c in colors: + r += c[0] + g += c[1] + b += c[2] + avg = (r,g,b) + avg = map(lambda x: int(round(x / (len(colors) * 1.0))), avg) + return avg + + +for i in range(100): + randRow = [ getRandomColor(dim.depth - 1) for i in range(dim.width) ] + print randRow + + f = blup.animation.AnimationFrame(dim, 60) + + for y in range(dim.height - 1, -1, -1): + for x in range(dim.width): + colors = [] + if x >=1 : + colors.append(f.getPixel(x-1,y)) + if x < dim.width - 1: + pass + colors.append(f.getPixel(x+1,y)) + if y < dim.height - 1: + colors.append(f.getPixel(x, y+1)) + else: + #colors.append(randRow[y]) + colors.append(getRandomColor(dim.depth - 1)) + #colors = [randRow[x]] + c = colorAvg(colors) + f.setPixel(x, y, c) + anim.addFrame(f) + +#out = blup.output.getOutput('e3blp:bbunt:4242') +out = blup.output.getOutput('colorfulshell') +player = blup.animation.AnimationPlayer() +player.play(anim, out) + + + diff --git a/http2telnet.py b/http2telnet.py new file mode 100644 index 0000000..a4a90ce --- /dev/null +++ b/http2telnet.py @@ -0,0 +1,84 @@ + +from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler +import socket +import cgi +import json +import time +import os.path +import threading + +TELNET_HOST = 'localhost' +TELNET_PORT = 4243 + +DOCROOT = '/var/tmp/blup2/blayer-webif' +UPLOAD_DIR = '/var/tmp/blup2/blm/incoming' + +sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +sock.connect((TELNET_HOST, TELNET_PORT)) +socklock = threading.Lock() + +class HTTPRequestHandler(BaseHTTPRequestHandler): + def do_GET(self): + self.send_response(200) + self.send_header('Content-type', 'text/html') + self.end_headers() + print self.path + #self.wfile.write('
    ') + + p = os.path.join(DOCROOT, self.path[1:]) + print p + if os.path.isfile(p): + f = open(p, 'r') + data = f.read() + f.close() + self.wfile.write(data) + + + + def do_POST(self): + + if self.path == '/do_upload': + formdata = cgi.FieldStorage( + fp=self.rfile, headers=self.headers, + environ={'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': self.headers['Content-Type']}) + + fileField = formdata['file'] + + f = open(os.path.join(UPLOAD_DIR, fileField.filename), 'w') + f.write(fileField.file.read()) + f.close() + + sock.send(json.dumps({"cmd": "refresh"}) + '\n') + print 'server answer to refresh cmd: ', sock.recv(1024) + + self.send_response(200); + self.end_headers(); + + return + + postdata = self.rfile.read(int(self.headers.getheader('content-length'))) + #self.wfile.write('test') + + if postdata[-1:] != '\n': + postdata += '\n' + print 'postdata:', postdata + + with socklock: + sock.send(postdata) + time.sleep(0.1) + resp = sock.recv(1024 * 10) + #sock.shutdown(socket.SHUT_RDWR) + #sock.close() + + self.send_response(200) + self.send_header('Content-type', 'text/html') + self.send_header('Content-length', len(resp)) + self.end_headers() + print 'resp:', resp + self.wfile.write(resp) + + + +server = HTTPServer(('0.0.0.0',8080), HTTPRequestHandler) +server.serve_forever() + diff --git a/loadbbm.tmp b/loadbbm.tmp new file mode 100644 index 0000000..3ec82d5 --- /dev/null +++ b/loadbbm.tmp @@ -0,0 +1,43 @@ + +def loadBbm(filename): + """ Parse a bbm file and return an Animation object. """ + + def getint16(vals): + return (cals[2]>>8) + vals[3] + def getint32(vals): + return (vals[0]>>24) + (vals[1]>>16) + (cals[2]>>8) + vals[3] + + f = open(filename, 'r') + rawdata = f.read() + f.close() + + data = [ ord(x) for x in rawdata ] + + if getint32(data[0:4]) != 0x23542666: + raise ValueError('magic does not match') + + height = getint16(data[4:6]) + width = getint16(data[6:8]) + channels = getint16(data[8:10]) + maxval = getint16(data[10:12]) + + framecnt = getint32(data[10:14]) + duration = getint32(data[14:18]) + frameptr = getint32(data[18:22]) + + # TODO: parse additional headers + + if rawdata[frameptr:(frameptr+4)] != 'frms': + raise ValueError('frame pointer does not point to frame start marker') + + framedata = data[frameptr + 4:] + framesize = width*height*channels + + frames = [] + while True: + pass + + raise Exception('uarrgh!!') + + + diff --git a/lugsaar b/lugsaar new file mode 100644 index 0000000..8389c1e --- /dev/null +++ b/lugsaar @@ -0,0 +1,23 @@ + # BlinkenLights Movie 18x8 + # loop = yes + +@200 +000000000000000000 +000011100011100000 +000111110111110000 +000111111111110000 +000011111111100000 +000000111110000000 +000000001000000000 +000000000000000000 + +@800 +000011100011100000 +000111110111110000 +001111111111111000 +001111111111111000 +000111111111110000 +000011111111100000 +000000111110000000 +000000001000000000 + diff --git a/matrix.py b/matrix.py new file mode 100644 index 0000000..90e1cfd --- /dev/null +++ b/matrix.py @@ -0,0 +1,13 @@ +#!/usr/bin/pyton + +import blup.frame +import blup.output +import blup.animation + +out = blup.output.getOutput('e3blp') + +points = [] + + +for i in + diff --git a/miniplayer.py b/miniplayer.py new file mode 100644 index 0000000..aa05c43 --- /dev/null +++ b/miniplayer.py @@ -0,0 +1,183 @@ +#!/usr/bin/python + +import threading +import os +import os.path +import random +import time +import sys +import getopt + +import blup.output +import blup.animation +import blup.frame + + +class AnimationLoaderThread(threading.Thread): + """ + This class is used by the player to pre-load an animation while another one + is being played. + + """ + + def __init__(self, filename): + threading.Thread.__init__(self) + self.__filename = filename + self.__anim = None + self.__error = False + + def run(self): + try: + self.__anim = blup.animation.load(self.__filename) + except Exception as e: + print '%s: %s while loading %s' % (self.__class__.__name__, + repr(e), + self.__filename) + self.__error = True + self.__anim = None + + def getAnim(self): + self.join() + return self.__anim + + +class MiniPlayer(object): + """ + Minimal animation player that does nothing but randomly selecting + animations out of a directory and playing them. + + """ + + def __init__(self, animDir, output): + self.__animDir = animDir + self.__output = output + self.loopTime = 10000 + self.gap = 800 + self.maxPlayNext = 3 + self.__playNext = [] + self.__running = False + + def terminate(self): + self.__running = False + + def playNext(self, filename): + """ Add an animation to the queue. """ + + if filename.find('/') >= 0: + raise ValueError('filename must not contain slashes') + + if ( os.path.isfile(os.path.join(self.__animDir, filename)) and + len(self.__playNext) < self.maxPlayNext ): + self.__playNext.append(filename) + else: + return False + + def getNextFilename(self): + """ + Return the next animation filename to be played, either from the queue, + or randomly chosen. + + """ + + if len(self.__playNext) > 0 and os.path.isfile(self.__playNext[0]): + return self.__playNext.pop(0) + else: + files = os.listdir(self.__animDir) + return random.choice(files) + + def run(self): + """ + Runs the player until terminate() gets called (e.g. by another thread). + + """ + + self.__running = True + currentAnim = None + player = blup.animation.AnimationPlayer() + + while self.__running: + + # begin to load the next animation + filename = os.path.join(self.__animDir, self.getNextFilename()) + loader = AnimationLoaderThread(filename) + loader.start() + + # play the animation in case it had been successfully loaded before + if currentAnim is not None: + if currentAnim.duration < self.loopTime: + count = self.loopTime / currentAnim.duration + else: + count = 1 + player.play(currentAnim, self.__output, count=count) + + # show a blank frame for some thime + if self.gap > 0 and self.__running: + # TODO: use correct frame size + dim = blup.frame.FrameDimension(18,8,8,3) + self.__output.sendFrame(blup.frame.Frame(dim)) + time.sleep(self.gap / 1000.0) + + # get the next animation from the loader + currentAnim = loader.getAnim() + + +def printUsage(errMsg=None): + if errMsg is not None: + print 'error: %s\n' % (errMsg) + print 'usage: %s [OPTIONS] PATH' % (sys.argv[0]) + print 'where PATH is the directory containing the animations to play' + print 'supported options:' + print ' -o OUTPUT where to output the frames (default: shell)' + print ' --output OUTPUT\n' + print ' -h print this text' + print ' --help' + +def main(): + try: + (opts, args) = getopt.gnu_getopt(sys.argv, 'ho:', ['help', 'output=']) + opts = dict(opts) + except getopt.GetoptError as e: + printUsage(e.msg) + sys.exit(1) + + if opts.has_key('--help'): + printUsage() + sys.exit(0) + + if opts.has_key('-o'): + output = opts['-o'] + elif opts.has_key('--output'): + output = opts['--output'] + else: + output = 'shell' + + try: + out = blup.output.getOutput(output) + except blup.output.IllegalOutputSpecificationError: + print 'illegal output specification' + print 'available outputs:' + print blup.output.getOutputDescriptions() + sys.exit(1) + except Exception as e: + print 'could not initialize output: %s' % (str(e)) + sys.exit(1) + + if len(args) != 2: + printUsage() + sys.exit(1) + else: + animDir = args[1] + + if not os.path.isdir(animDir): + print '%s is not a directory' % (animDir) + sys.exit(1) + + p = MiniPlayer(animDir, out) + try: + p.run() + except KeyboardInterrupt: + sys.exit(0) + +if __name__ == '__main__': + main() + diff --git a/mrmcd13.bml b/mrmcd13.bml new file mode 100644 index 0000000..9653615 --- /dev/null +++ b/mrmcd13.bml @@ -0,0 +1,873 @@ + + + + 000000000000000000000000000000000000000000000000f00000 + 000000000000000000000000000000000000000000000000f00f00 + 000000000000000000000000000000000000000000000000f00000 + 000000000000000000000000000000000000000000000000f00000 + 000000000000000000000000000000000000000000000000f00000 + 000000000000000000000000000000000000000000000000f00000 + 000000000000000000000000000000000000000000000000f00000 + 000000000000000000000000000000000000000000000000000000 + + + 000000000000000000000000000000000000000000000f00000000 + 000000000000000000000000000000000000000000000f00f00000 + 000000000000000000000000000000000000000000000f00000f00 + 000000000000000000000000000000000000000000000f00000000 + 000000000000000000000000000000000000000000000f00000000 + 000000000000000000000000000000000000000000000f00000000 + 000000000000000000000000000000000000000000000f00000000 + 000000000000000000000000000000000000000000000000000000 + + + 000000000000000000000000000000000000000000f00000000000 + 000000000000000000000000000000000000000000f00f00000000 + 000000000000000000000000000000000000000000f00000f00000 + 000000000000000000000000000000000000000000f00000000f00 + 000000000000000000000000000000000000000000f00000000000 + 000000000000000000000000000000000000000000f00000000000 + 000000000000000000000000000000000000000000f00000000000 + 000000000000000000000000000000000000000000000000000000 + + + 000000000000000000000000000000000000000f00000000000000 + 000000000000000000000000000000000000000f00f00000000000 + 000000000000000000000000000000000000000f00000f00000f00 + 000000000000000000000000000000000000000f00000000f00000 + 000000000000000000000000000000000000000f00000000000000 + 000000000000000000000000000000000000000f00000000000000 + 000000000000000000000000000000000000000f00000000000000 + 000000000000000000000000000000000000000000000000000000 + + + 000000000000000000000000000000000000f00000000000000000 + 000000000000000000000000000000000000f00f00000000000f00 + 000000000000000000000000000000000000f00000f00000f00000 + 000000000000000000000000000000000000f00000000f00000000 + 000000000000000000000000000000000000f00000000000000000 + 000000000000000000000000000000000000f00000000000000000 + 000000000000000000000000000000000000f00000000000000000 + 000000000000000000000000000000000000000000000000000000 + + + 000000000000000000000000000000000f00000000000000000f00 + 000000000000000000000000000000000f00f00000000000f00f00 + 000000000000000000000000000000000f00000f00000f00000f00 + 000000000000000000000000000000000f00000000f00000000f00 + 000000000000000000000000000000000f00000000000000000f00 + 000000000000000000000000000000000f00000000000000000f00 + 000000000000000000000000000000000f00000000000000000f00 + 000000000000000000000000000000000000000000000000000000 + + + 000000000000000000000000000000f00000000000000000f00000 + 000000000000000000000000000000f00f00000000000f00f00000 + 000000000000000000000000000000f00000f00000f00000f00000 + 000000000000000000000000000000f00000000f00000000f00000 + 000000000000000000000000000000f00000000000000000f00000 + 000000000000000000000000000000f00000000000000000f00000 + 000000000000000000000000000000f00000000000000000f00000 + 000000000000000000000000000000000000000000000000000000 + + + 000000000000000000000000000f00000000000000000f00000f00 + 000000000000000000000000000f00f00000000000f00f00000f00 + 000000000000000000000000000f00000f00000f00000f00000f00 + 000000000000000000000000000f00000000f00000000f00000f00 + 000000000000000000000000000f00000000000000000f00000f00 + 000000000000000000000000000f00000000000000000f00000f00 + 000000000000000000000000000f00000000000000000f00000f00 + 000000000000000000000000000000000000000000000000000000 + + + 000000000000000000000000f00000000000000000f00000f00f00 + 000000000000000000000000f00f00000000000f00f00000f00000 + 000000000000000000000000f00000f00000f00000f00000f00000 + 000000000000000000000000f00000000f00000000f00000f00f00 + 000000000000000000000000f00000000000000000f00000f00000 + 000000000000000000000000f00000000000000000f00000f00000 + 000000000000000000000000f00000000000000000f00000f00000 + 000000000000000000000000000000000000000000000000000000 + + + 000000000000000000000f00000000000000000f00000f00f00f00 + 000000000000000000000f00f00000000000f00f00000f00000000 + 000000000000000000000f00000f00000f00000f00000f00000000 + 000000000000000000000f00000000f00000000f00000f00f00f00 + 000000000000000000000f00000000000000000f00000f00000000 + 000000000000000000000f00000000000000000f00000f00000000 + 000000000000000000000f00000000000000000f00000f00000000 + 000000000000000000000000000000000000000000000000000000 + + + 000000000000000000f00000000000000000f00000f00f00f00f00 + 000000000000000000f00f00000000000f00f00000f00000000000 + 000000000000000000f00000f00000f00000f00000f00000000000 + 000000000000000000f00000000f00000000f00000f00f00f00f00 + 000000000000000000f00000000000000000f00000f00000000f00 + 000000000000000000f00000000000000000f00000f00000000000 + 000000000000000000f00000000000000000f00000f00000000000 + 000000000000000000000000000000000000000000000000000000 + + + 000000000000000f00000000000000000f00000f00f00f00f00f00 + 000000000000000f00f00000000000f00f00000f00000000000000 + 000000000000000f00000f00000f00000f00000f00000000000000 + 000000000000000f00000000f00000000f00000f00f00f00f00f00 + 000000000000000f00000000000000000f00000f00000000f00000 + 000000000000000f00000000000000000f00000f00000000000f00 + 000000000000000f00000000000000000f00000f00000000000000 + 000000000000000000000000000000000000000000000000000000 + + + 000000000000f00000000000000000f00000f00f00f00f00f00f00 + 000000000000f00f00000000000f00f00000f00000000000000000 + 000000000000f00000f00000f00000f00000f00000000000000000 + 000000000000f00000000f00000000f00000f00f00f00f00f00f00 + 000000000000f00000000000000000f00000f00000000f00000000 + 000000000000f00000000000000000f00000f00000000000f00000 + 000000000000f00000000000000000f00000f00000000000000f00 + 000000000000000000000000000000000000000000000000000000 + + + 000000000f00000000000000000f00000f00f00f00f00f00f00000 + 000000000f00f00000000000f00f00000f00000000000000000f00 + 000000000f00000f00000f00000f00000f00000000000000000f00 + 000000000f00000000f00000000f00000f00f00f00f00f00f00000 + 000000000f00000000000000000f00000f00000000f00000000000 + 000000000f00000000000000000f00000f00000000000f00000000 + 000000000f00000000000000000f00000f00000000000000f00000 + 000000000000000000000000000000000000000000000000000000 + + + 000000f00000000000000000f00000f00f00f00f00f00f00000000 + 000000f00f00000000000f00f00000f00000000000000000f00000 + 000000f00000f00000f00000f00000f00000000000000000f00000 + 000000f00000000f00000000f00000f00f00f00f00f00f00000000 + 000000f00000000000000000f00000f00000000f00000000000000 + 000000f00000000000000000f00000f00000000000f00000000000 + 000000f00000000000000000f00000f00000000000000f00000000 + 000000000000000000000000000000000000000000000000000000 + + + 000f00000000000000000f00000f00f00f00f00f00f00000000f00 + 000f00f00000000000f00f00000f00000000000000000f00000f00 + 000f00000f00000f00000f00000f00000000000000000f00000f00 + 000f00000000f00000000f00000f00f00f00f00f00f00000000f00 + 000f00000000000000000f00000f00000000f00000000000000f00 + 000f00000000000000000f00000f00000000000f00000000000f00 + 000f00000000000000000f00000f00000000000000f00000000f00 + 000000000000000000000000000000000000000000000000000000 + + + f00000000000000000f00000f00f00f00f00f00f00000000f00000 + f00f00000000000f00f00000f00000000000000000f00000f00f00 + f00000f00000f00000f00000f00000000000000000f00000f00000 + f00000000f00000000f00000f00f00f00f00f00f00000000f00000 + f00000000000000000f00000f00000000f00000000000000f00000 + f00000000000000000f00000f00000000000f00000000000f00000 + f00000000000000000f00000f00000000000000f00000000f00000 + 000000000000000000000000000000000000000000000000000000 + + + 000000000000000f00000f00f00f00f00f00f00000000f00000000 + f00000000000f00f00000f00000000000000000f00000f00f00000 + 000f00000f00000f00000f00000000000000000f00000f00000f00 + 000000f00000000f00000f00f00f00f00f00f00000000f00000000 + 000000000000000f00000f00000000f00000000000000f00000000 + 000000000000000f00000f00000000000f00000000000f00000000 + 000000000000000f00000f00000000000000f00000000f00000000 + 000000000000000000000000000000000000000000000000000000 + + + 000000000000f00000f00f00f00f00f00f00000000f00000000000 + 000000000f00f00000f00000000000000000f00000f00f00000000 + f00000f00000f00000f00000000000000000f00000f00000f00000 + 000f00000000f00000f00f00f00f00f00f00000000f00000000f00 + 000000000000f00000f00000000f00000000000000f00000000000 + 000000000000f00000f00000000000f00000000000f00000000000 + 000000000000f00000f00000000000000f00000000f00000000000 + 000000000000000000000000000000000000000000000000000000 + + + 000000000f00000f00f00f00f00f00f00000000f00000000000000 + 000000f00f00000f00000000000000000f00000f00f00000000000 + 000f00000f00000f00000000000000000f00000f00000f00000f00 + f00000000f00000f00f00f00f00f00f00000000f00000000f00000 + 000000000f00000f00000000f00000000000000f00000000000000 + 000000000f00000f00000000000f00000000000f00000000000000 + 000000000f00000f00000000000000f00000000f00000000000000 + 000000000000000000000000000000000000000000000000000000 + + + 000000f00000f00f00f00f00f00f00000000f00000000000000000 + 000f00f00000f00000000000000000f00000f00f00000000000f00 + f00000f00000f00000000000000000f00000f00000f00000f00000 + 000000f00000f00f00f00f00f00f00000000f00000000f00000000 + 000000f00000f00000000f00000000000000f00000000000000000 + 000000f00000f00000000000f00000000000f00000000000000000 + 000000f00000f00000000000000f00000000f00000000000000000 + 000000000000000000000000000000000000000000000000000000 + + + 000f00000f00f00f00f00f00f00000000f00000000000000000f00 + f00f00000f00000000000000000f00000f00f00000000000f00f00 + 000f00000f00000000000000000f00000f00000f00000f00000f00 + 000f00000f00f00f00f00f00f00000000f00000000f00000000f00 + 000f00000f00000000f00000000000000f00000000000000000f00 + 000f00000f00000000000f00000000000f00000000000000000f00 + 000f00000f00000000000000f00000000f00000000000000000f00 + 000000000000000000000000000000000000000000000000000000 + + + f00000f00f00f00f00f00f00000000f00000000000000000f00000 + f00000f00000000000000000f00000f00f00000000000f00f00000 + f00000f00000000000000000f00000f00000f00000f00000f00000 + f00000f00f00f00f00f00f00000000f00000000f00000000f00000 + f00000f00000000f00000000000000f00000000000000000f00000 + f00000f00000000000f00000000000f00000000000000000f00000 + f00000f00000000000000f00000000f00000000000000000f00000 + 000000000000000000000000000000000000000000000000000000 + + + 000f00f00f00f00f00f00000000f00000000000000000f00000000 + 000f00000000000000000f00000f00f00000000000f00f00000f00 + 000f00000000000000000f00000f00000f00000f00000f00000f00 + 000f00f00f00f00f00f00000000f00000000f00000000f00000f00 + 000f00000000f00000000000000f00000000000000000f00000f00 + 000f00000000000f00000000000f00000000000000000f00000f00 + 000f00000000000000f00000000f00000000000000000f00000000 + 000000000000000000000000000000000000000000000000000000 + + + f00f00f00f00f00f00000000f00000000000000000f00000000f00 + f00000000000000000f00000f00f00000000000f00f00000f00000 + f00000000000000000f00000f00000f00000f00000f00000f00000 + f00f00f00f00f00f00000000f00000000f00000000f00000f00000 + f00000000f00000000000000f00000000000000000f00000f00000 + f00000000000f00000000000f00000000000000000f00000f00000 + f00000000000000f00000000f00000000000000000f00000000f00 + 000000000000000000000000000000000000000000000000000000 + + + f00f00f00f00f00000000f00000000000000000f00000000f00f00 + 000000000000000f00000f00f00000000000f00f00000f00000000 + 000000000000000f00000f00000f00000f00000f00000f00000000 + f00f00f00f00f00000000f00000000f00000000f00000f00000000 + 000000f00000000000000f00000000000000000f00000f00000000 + 000000000f00000000000f00000000000000000f00000f00000000 + 000000000000f00000000f00000000000000000f00000000f00f00 + 000000000000000000000000000000000000000000000000000000 + + + f00f00f00f00000000f00000000000000000f00000000f00f00f00 + 000000000000f00000f00f00000000000f00f00000f00000000000 + 000000000000f00000f00000f00000f00000f00000f00000000000 + f00f00f00f00000000f00000000f00000000f00000f00000000000 + 000f00000000000000f00000000000000000f00000f00000000000 + 000000f00000000000f00000000000000000f00000f00000000000 + 000000000f00000000f00000000000000000f00000000f00f00f00 + 000000000000000000000000000000000000000000000000000000 + + + f00f00f00000000f00000000000000000f00000000f00f00f00f00 + 000000000f00000f00f00000000000f00f00000f00000000000000 + 000000000f00000f00000f00000f00000f00000f00000000000000 + f00f00f00000000f00000000f00000000f00000f00000000000000 + f00000000000000f00000000000000000f00000f00000000000000 + 000f00000000000f00000000000000000f00000f00000000000000 + 000000f00000000f00000000000000000f00000000f00f00f00f00 + 000000000000000000000000000000000000000000000000000000 + + + f00f00000000f00000000000000000f00000000f00f00f00f00f00 + 000000f00000f00f00000000000f00f00000f00000000000000000 + 000000f00000f00000f00000f00000f00000f00000000000000000 + f00f00000000f00000000f00000000f00000f00000000000000000 + 000000000000f00000000000000000f00000f00000000000000000 + f00000000000f00000000000000000f00000f00000000000000000 + 000f00000000f00000000000000000f00000000f00f00f00f00f00 + 000000000000000000000000000000000000000000000000000000 + + + f00000000f00000000000000000f00000000f00f00f00f00f00f00 + 000f00000f00f00000000000f00f00000f00000000000000000000 + 000f00000f00000f00000f00000f00000f00000000000000000000 + f00000000f00000000f00000000f00000f00000000000000000000 + 000000000f00000000000000000f00000f00000000000000000000 + 000000000f00000000000000000f00000f00000000000000000000 + f00000000f00000000000000000f00000000f00f00f00f00f00f00 + 000000000000000000000000000000000000000000000000000000 + + + 000000f00000000000000000f00000000f00f00f00f00f00f00000 + f00000f00f00000000000f00f00000f00000000000000000000000 + f00000f00000f00000f00000f00000f00000000000000000000000 + 000000f00000000f00000000f00000f00000000000000000000000 + 000000f00000000000000000f00000f00000000000000000000000 + 000000f00000000000000000f00000f00000000000000000000000 + 000000f00000000000000000f00000000f00f00f00f00f00f00000 + 000000000000000000000000000000000000000000000000000000 + + + 000f00000000000000000f00000000f00f00f00f00f00f00000f00 + 000f00f00000000000f00f00000f00000000000000000000000f00 + 000f00000f00000f00000f00000f00000000000000000000000f00 + 000f00000000f00000000f00000f00000000000000000000000f00 + 000f00000000000000000f00000f00000000000000000000000f00 + 000f00000000000000000f00000f00000000000000000000000f00 + 000f00000000000000000f00000000f00f00f00f00f00f00000f00 + 000000000000000000000000000000000000000000000000000000 + + + f00000000000000000f00000000f00f00f00f00f00f00000f00f00 + f00f00000000000f00f00000f00000000000000000000000f00000 + f00000f00000f00000f00000f00000000000000000000000f00000 + f00000000f00000000f00000f00000000000000000000000f00000 + f00000000000000000f00000f00000000000000000000000f00000 + f00000000000000000f00000f00000000000000000000000f00000 + f00000000000000000f00000000f00f00f00f00f00f00000f00f00 + 000000000000000000000000000000000000000000000000000000 + + + 000000000000000f00000000f00f00f00f00f00f00000f00f00f00 + f00000000000f00f00000f00000000000000000000000f00000000 + 000f00000f00000f00000f00000000000000000000000f00000000 + 000000f00000000f00000f00000000000000000000000f00000000 + 000000000000000f00000f00000000000000000000000f00000000 + 000000000000000f00000f00000000000000000000000f00000000 + 000000000000000f00000000f00f00f00f00f00f00000f00f00f00 + 000000000000000000000000000000000000000000000000000000 + + + 000000000000f00000000f00f00f00f00f00f00000f00f00f00f00 + 000000000f00f00000f00000000000000000000000f00000000000 + f00000f00000f00000f00000000000000000000000f00000000000 + 000f00000000f00000f00000000000000000000000f00000000000 + 000000000000f00000f00000000000000000000000f00000000000 + 000000000000f00000f00000000000000000000000f00000000000 + 000000000000f00000000f00f00f00f00f00f00000f00f00f00f00 + 000000000000000000000000000000000000000000000000000000 + + + 000000000f00000000f00f00f00f00f00f00000f00f00f00f00f00 + 000000f00f00000f00000000000000000000000f00000000000000 + 000f00000f00000f00000000000000000000000f00000000000000 + f00000000f00000f00000000000000000000000f00000000000000 + 000000000f00000f00000000000000000000000f00000000000000 + 000000000f00000f00000000000000000000000f00000000000000 + 000000000f00000000f00f00f00f00f00f00000f00f00f00f00f00 + 000000000000000000000000000000000000000000000000000000 + + + 000000f00000000f00f00f00f00f00f00000f00f00f00f00f00000 + 000f00f00000f00000000000000000000000f00000000000000f00 + f00000f00000f00000000000000000000000f00000000000000000 + 000000f00000f00000000000000000000000f00000000000000000 + 000000f00000f00000000000000000000000f00000000000000000 + 000000f00000f00000000000000000000000f00000000000000f00 + 000000f00000000f00f00f00f00f00f00000f00f00f00f00f00000 + 000000000000000000000000000000000000000000000000000000 + + + 000f00000000f00f00f00f00f00f00000f00f00f00f00f00000000 + f00f00000f00000000000000000000000f00000000000000f00000 + 000f00000f00000000000000000000000f00000000000000000f00 + 000f00000f00000000000000000000000f00000000000000000f00 + 000f00000f00000000000000000000000f00000000000000000f00 + 000f00000f00000000000000000000000f00000000000000f00000 + 000f00000000f00f00f00f00f00f00000f00f00f00f00f00000000 + 000000000000000000000000000000000000000000000000000000 + + + f00000000f00f00f00f00f00f00000f00f00f00f00f00000000000 + f00000f00000000000000000000000f00000000000000f00000000 + f00000f00000000000000000000000f00000000000000000f00000 + f00000f00000000000000000000000f00000000000000000f00000 + f00000f00000000000000000000000f00000000000000000f00000 + f00000f00000000000000000000000f00000000000000f00000000 + f00000000f00f00f00f00f00f00000f00f00f00f00f00000000000 + 000000000000000000000000000000000000000000000000000000 + + + 000000f00f00f00f00f00f00000f00f00f00f00f00000000000000 + 000f00000000000000000000000f00000000000000f00000000f00 + 000f00000000000000000000000f00000000000000000f00000000 + 000f00000000000000000000000f00000000000000000f00000000 + 000f00000000000000000000000f00000000000000000f00000000 + 000f00000000000000000000000f00000000000000f00000000f00 + 000000f00f00f00f00f00f00000f00f00f00f00f00000000000f00 + 000000000000000000000000000000000000000000000000000000 + + + 000f00f00f00f00f00f00000f00f00f00f00f00000000000000f00 + f00000000000000000000000f00000000000000f00000000f00000 + f00000000000000000000000f00000000000000000f00000000000 + f00000000000000000000000f00000000000000000f00000000000 + f00000000000000000000000f00000000000000000f00000000f00 + f00000000000000000000000f00000000000000f00000000f00000 + 000f00f00f00f00f00f00000f00f00f00f00f00000000000f00f00 + 000000000000000000000000000000000000000000000000000000 + + + f00f00f00f00f00f00000f00f00f00f00f00000000000000f00f00 + 000000000000000000000f00000000000000f00000000f00000000 + 000000000000000000000f00000000000000000f00000000000000 + 000000000000000000000f00000000000000000f00000000000f00 + 000000000000000000000f00000000000000000f00000000f00000 + 000000000000000000000f00000000000000f00000000f00000000 + f00f00f00f00f00f00000f00f00f00f00f00000000000f00f00f00 + 000000000000000000000000000000000000000000000000000000 + + + f00f00f00f00f00000f00f00f00f00f00000000000000f00f00f00 + 000000000000000000f00000000000000f00000000f00000000000 + 000000000000000000f00000000000000000f00000000000000000 + 000000000000000000f00000000000000000f00000000000f00f00 + 000000000000000000f00000000000000000f00000000f00000000 + 000000000000000000f00000000000000f00000000f00000000000 + f00f00f00f00f00000f00f00f00f00f00000000000f00f00f00f00 + 000000000000000000000000000000000000000000000000000000 + + + f00f00f00f00000f00f00f00f00f00000000000000f00f00f00f00 + 000000000000000f00000000000000f00000000f00000000000000 + 000000000000000f00000000000000000f00000000000000000000 + 000000000000000f00000000000000000f00000000000f00f00f00 + 000000000000000f00000000000000000f00000000f00000000000 + 000000000000000f00000000000000f00000000f00000000000000 + f00f00f00f00000f00f00f00f00f00000000000f00f00f00f00f00 + 000000000000000000000000000000000000000000000000000000 + + + f00f00f00000f00f00f00f00f00000000000000f00f00f00f00f00 + 000000000000f00000000000000f00000000f00000000000000000 + 000000000000f00000000000000000f00000000000000000000000 + 000000000000f00000000000000000f00000000000f00f00f00f00 + 000000000000f00000000000000000f00000000f00000000000000 + 000000000000f00000000000000f00000000f00000000000000000 + f00f00f00000f00f00f00f00f00000000000f00f00f00f00f00f00 + 000000000000000000000000000000000000000000000000000000 + + + f00f00000f00f00f00f00f00000000000000f00f00f00f00f00000 + 000000000f00000000000000f00000000f00000000000000000f00 + 000000000f00000000000000000f00000000000000000000000f00 + 000000000f00000000000000000f00000000000f00f00f00f00000 + 000000000f00000000000000000f00000000f00000000000000000 + 000000000f00000000000000f00000000f00000000000000000000 + f00f00000f00f00f00f00f00000000000f00f00f00f00f00f00f00 + 000000000000000000000000000000000000000000000000000000 + + + f00000f00f00f00f00f00000000000000f00f00f00f00f00000000 + 000000f00000000000000f00000000f00000000000000000f00000 + 000000f00000000000000000f00000000000000000000000f00000 + 000000f00000000000000000f00000000000f00f00f00f00000000 + 000000f00000000000000000f00000000f00000000000000000000 + 000000f00000000000000f00000000f00000000000000000000000 + f00000f00f00f00f00f00000000000f00f00f00f00f00f00f00000 + 000000000000000000000000000000000000000000000000000000 + + + 000f00f00f00f00f00000000000000f00f00f00f00f00000000000 + 000f00000000000000f00000000f00000000000000000f00000f00 + 000f00000000000000000f00000000000000000000000f00000f00 + 000f00000000000000000f00000000000f00f00f00f00000000f00 + 000f00000000000000000f00000000f00000000000000000000f00 + 000f00000000000000f00000000f00000000000000000000000f00 + 000f00f00f00f00f00000000000f00f00f00f00f00f00f00000000 + 000000000000000000000000000000000000000000000000000000 + + + f00f00f00f00f00000000000000f00f00f00f00f00000000000f00 + f00000000000000f00000000f00000000000000000f00000f00000 + f00000000000000000f00000000000000000000000f00000f00000 + f00000000000000000f00000000000f00f00f00f00000000f00000 + f00000000000000000f00000000f00000000000000000000f00000 + f00000000000000f00000000f00000000000000000000000f00000 + f00f00f00f00f00000000000f00f00f00f00f00f00f00000000f00 + 000000000000000000000000000000000000000000000000000000 + + + f00f00f00f00000000000000f00f00f00f00f00000000000f00f00 + 000000000000f00000000f00000000000000000f00000f00000000 + 000000000000000f00000000000000000000000f00000f00000000 + 000000000000000f00000000000f00f00f00f00000000f00000000 + 000000000000000f00000000f00000000000000000000f00000000 + 000000000000f00000000f00000000000000000000000f00000000 + f00f00f00f00000000000f00f00f00f00f00f00f00000000f00f00 + 000000000000000000000000000000000000000000000000000000 + + + f00f00f00000000000000f00f00f00f00f00000000000f00f00f00 + 000000000f00000000f00000000000000000f00000f00000000000 + 000000000000f00000000000000000000000f00000f00000000000 + 000000000000f00000000000f00f00f00f00000000f00000000000 + 000000000000f00000000f00000000000000000000f00000000000 + 000000000f00000000f00000000000000000000000f00000000000 + f00f00f00000000000f00f00f00f00f00f00f00000000f00f00f00 + 000000000000000000000000000000000000000000000000000000 + + + f00f00000000000000f00f00f00f00f00000000000f00f00f00f00 + 000000f00000000f00000000000000000f00000f00000000000000 + 000000000f00000000000000000000000f00000f00000000000000 + 000000000f00000000000f00f00f00f00000000f00000000000000 + 000000000f00000000f00000000000000000000f00000000000000 + 000000f00000000f00000000000000000000000f00000000000000 + f00f00000000000f00f00f00f00f00f00f00000000f00f00f00f00 + 000000000000000000000000000000000000000000000000000000 + + + f00000000000000f00f00f00f00f00000000000f00f00f00f00f00 + 000f00000000f00000000000000000f00000f00000000000000000 + 000000f00000000000000000000000f00000f00000000000000000 + 000000f00000000000f00f00f00f00000000f00000000000000000 + 000000f00000000f00000000000000000000f00000000000000000 + 000f00000000f00000000000000000000000f00000000000000000 + f00000000000f00f00f00f00f00f00f00000000f00f00f00f00f00 + 000000000000000000000000000000000000000000000000000000 + + + 000000000000f00f00f00f00f00000000000f00f00f00f00f00000 + f00000000f00000000000000000f00000f00000000000000000f00 + 000f00000000000000000000000f00000f00000000000000000f00 + 000f00000000000f00f00f00f00000000f00000000000000000f00 + 000f00000000f00000000000000000000f00000000000000000f00 + f00000000f00000000000000000000000f00000000000000000f00 + 000000000f00f00f00f00f00f00f00000000f00f00f00f00f00000 + 000000000000000000000000000000000000000000000000000000 + + + 000000000f00f00f00f00f00000000000f00f00f00f00f00000000 + 000000f00000000000000000f00000f00000000000000000f00000 + f00000000000000000000000f00000f00000000000000000f00000 + f00000000000f00f00f00f00000000f00000000000000000f00000 + f00000000f00000000000000000000f00000000000000000f00000 + 000000f00000000000000000000000f00000000000000000f00000 + 000000f00f00f00f00f00f00f00000000f00f00f00f00f00000000 + 000000000000000000000000000000000000000000000000000000 + + + 000000f00f00f00f00f00000000000f00f00f00f00f00000000000 + 000f00000000000000000f00000f00000000000000000f00000000 + 000000000000000000000f00000f00000000000000000f00000f00 + 000000000f00f00f00f00000000f00000000000000000f00000000 + 000000f00000000000000000000f00000000000000000f00000000 + 000f00000000000000000000000f00000000000000000f00000000 + 000f00f00f00f00f00f00f00000000f00f00f00f00f00000000000 + 000000000000000000000000000000000000000000000000000000 + + + 000f00f00f00f00f00000000000f00f00f00f00f00000000000000 + f00000000000000000f00000f00000000000000000f00000000f00 + 000000000000000000f00000f00000000000000000f00000f00000 + 000000f00f00f00f00000000f00000000000000000f00000000000 + 000f00000000000000000000f00000000000000000f00000000000 + f00000000000000000000000f00000000000000000f00000000000 + f00f00f00f00f00f00f00000000f00f00f00f00f00000000000000 + 000000000000000000000000000000000000000000000000000000 + + + f00f00f00f00f00000000000f00f00f00f00f00000000000000f00 + 000000000000000f00000f00000000000000000f00000000f00000 + 000000000000000f00000f00000000000000000f00000f00000000 + 000f00f00f00f00000000f00000000000000000f00000000000000 + f00000000000000000000f00000000000000000f00000000000000 + 000000000000000000000f00000000000000000f00000000000000 + f00f00f00f00f00f00000000f00f00f00f00f00000000000000000 + 000000000000000000000000000000000000000000000000000000 + + + f00f00f00f00000000000f00f00f00f00f00000000000000f00f00 + 000000000000f00000f00000000000000000f00000000f00000f00 + 000000000000f00000f00000000000000000f00000f00000000f00 + f00f00f00f00000000f00000000000000000f00000000000000f00 + 000000000000000000f00000000000000000f00000000000000f00 + 000000000000000000f00000000000000000f00000000000000f00 + f00f00f00f00f00000000f00f00f00f00f00000000000000000f00 + 000000000000000000000000000000000000000000000000000000 + + + f00f00f00000000000f00f00f00f00f00000000000000f00f00000 + 000000000f00000f00000000000000000f00000000f00000f00000 + 000000000f00000f00000000000000000f00000f00000000f00000 + f00f00f00000000f00000000000000000f00000000000000f00000 + 000000000000000f00000000000000000f00000000000000f00000 + 000000000000000f00000000000000000f00000000000000f00000 + f00f00f00f00000000f00f00f00f00f00000000000000000f00000 + 000000000000000000000000000000000000000000000000000000 + + + f00f00000000000f00f00f00f00f00000000000000f00f00000000 + 000000f00000f00000000000000000f00000000f00000f00000f00 + 000000f00000f00000000000000000f00000f00000000f00000000 + f00f00000000f00000000000000000f00000000000000f00000000 + 000000000000f00000000000000000f00000000000000f00000000 + 000000000000f00000000000000000f00000000000000f00000f00 + f00f00f00000000f00f00f00f00f00000000000000000f00000000 + 000000000000000000000000000000000000000000000000000000 + + + f00000000000f00f00f00f00f00000000000000f00f00000000f00 + 000f00000f00000000000000000f00000000f00000f00000f00000 + 000f00000f00000000000000000f00000f00000000f00000000000 + f00000000f00000000000000000f00000000000000f00000000000 + 000000000f00000000000000000f00000000000000f00000000000 + 000000000f00000000000000000f00000000000000f00000f00000 + f00f00000000f00f00f00f00f00000000000000000f00000000f00 + 000000000000000000000000000000000000000000000000000000 + + + 000000000f00f00f00f00f00000000000000f00f00000000f00f00 + f00000f00000000000000000f00000000f00000f00000f00000000 + f00000f00000000000000000f00000f00000000f00000000000000 + 000000f00000000000000000f00000000000000f00000000000f00 + 000000f00000000000000000f00000000000000f00000000000000 + 000000f00000000000000000f00000000000000f00000f00000000 + f00000000f00f00f00f00f00000000000000000f00000000f00f00 + 000000000000000000000000000000000000000000000000000000 + + + 000000f00f00f00f00f00000000000000f00f00000000f00f00f00 + 000f00000000000000000f00000000f00000f00000f00000000000 + 000f00000000000000000f00000f00000000f00000000000000000 + 000f00000000000000000f00000000000000f00000000000f00f00 + 000f00000000000000000f00000000000000f00000000000000000 + 000f00000000000000000f00000000000000f00000f00000000000 + 000000f00f00f00f00f00000000000000000f00000000f00f00f00 + 000000000000000000000000000000000000000000000000000000 + + + 000f00f00f00f00f00000000000000f00f00000000f00f00f00f00 + f00000000000000000f00000000f00000f00000f00000000000000 + f00000000000000000f00000f00000000f00000000000000000000 + f00000000000000000f00000000000000f00000000000f00f00f00 + f00000000000000000f00000000000000f00000000000000000000 + f00000000000000000f00000000000000f00000f00000000000000 + 000f00f00f00f00f00000000000000000f00000000f00f00f00f00 + 000000000000000000000000000000000000000000000000000000 + + + f00f00f00f00f00000000000000f00f00000000f00f00f00f00f00 + 000000000000000f00000000f00000f00000f00000000000000000 + 000000000000000f00000f00000000f00000000000000000000000 + 000000000000000f00000000000000f00000000000f00f00f00f00 + 000000000000000f00000000000000f00000000000000000000000 + 000000000000000f00000000000000f00000f00000000000000000 + f00f00f00f00f00000000000000000f00000000f00f00f00f00f00 + 000000000000000000000000000000000000000000000000000000 + + + f00f00f00f00000000000000f00f00000000f00f00f00f00f00000 + 000000000000f00000000f00000f00000f00000000000000000f00 + 000000000000f00000f00000000f00000000000000000000000f00 + 000000000000f00000000000000f00000000000f00f00f00f00000 + 000000000000f00000000000000f00000000000000000000000f00 + 000000000000f00000000000000f00000f00000000000000000f00 + f00f00f00f00000000000000000f00000000f00f00f00f00f00000 + 000000000000000000000000000000000000000000000000000000 + + + f00f00f00000000000000f00f00000000f00f00f00f00f00000000 + 000000000f00000000f00000f00000f00000000000000000f00000 + 000000000f00000f00000000f00000000000000000000000f00000 + 000000000f00000000000000f00000000000f00f00f00f00000000 + 000000000f00000000000000f00000000000000000000000f00000 + 000000000f00000000000000f00000f00000000000000000f00000 + f00f00f00000000000000000f00000000f00f00f00f00f00000000 + 000000000000000000000000000000000000000000000000000000 + + + f00f00000000000000f00f00000000f00f00f00f00f00000000000 + 000000f00000000f00000f00000f00000000000000000f00000000 + 000000f00000f00000000f00000000000000000000000f00000000 + 000000f00000000000000f00000000000f00f00f00f00000000000 + 000000f00000000000000f00000000000000000000000f00000000 + 000000f00000000000000f00000f00000000000000000f00000000 + f00f00000000000000000f00000000f00f00f00f00f00000000000 + 000000000000000000000000000000000000000000000000000000 + + + f00000000000000f00f00000000f00f00f00f00f00000000000000 + 000f00000000f00000f00000f00000000000000000f00000000000 + 000f00000f00000000f00000000000000000000000f00000000000 + 000f00000000000000f00000000000f00f00f00f00000000000000 + 000f00000000000000f00000000000000000000000f00000000000 + 000f00000000000000f00000f00000000000000000f00000000000 + f00000000000000000f00000000f00f00f00f00f00000000000000 + 000000000000000000000000000000000000000000000000000000 + + + 000000000000f00f00000000f00f00f00f00f00000000000000000 + f00000000f00000f00000f00000000000000000f00000000000000 + f00000f00000000f00000000000000000000000f00000000000000 + f00000000000000f00000000000f00f00f00f00000000000000000 + f00000000000000f00000000000000000000000f00000000000000 + f00000000000000f00000f00000000000000000f00000000000000 + 000000000000000f00000000f00f00f00f00f00000000000000000 + 000000000000000000000000000000000000000000000000000000 + + + 000000000f00f00000000f00f00f00f00f00000000000000000000 + 000000f00000f00000f00000000000000000f00000000000000000 + 000f00000000f00000000000000000000000f00000000000000000 + 000000000000f00000000000f00f00f00f00000000000000000000 + 000000000000f00000000000000000000000f00000000000000000 + 000000000000f00000f00000000000000000f00000000000000000 + 000000000000f00000000f00f00f00f00f00000000000000000000 + 000000000000000000000000000000000000000000000000000000 + + + 000000f00f00000000f00f00f00f00f00000000000000000000000 + 000f00000f00000f00000000000000000f00000000000000000000 + f00000000f00000000000000000000000f00000000000000000000 + 000000000f00000000000f00f00f00f00000000000000000000000 + 000000000f00000000000000000000000f00000000000000000000 + 000000000f00000f00000000000000000f00000000000000000000 + 000000000f00000000f00f00f00f00f00000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + + + 000f00f00000000f00f00f00f00f00000000000000000000000000 + f00000f00000f00000000000000000f00000000000000000000000 + 000000f00000000000000000000000f00000000000000000000000 + 000000f00000000000f00f00f00f00000000000000000000000000 + 000000f00000000000000000000000f00000000000000000000000 + 000000f00000f00000000000000000f00000000000000000000000 + 000000f00000000f00f00f00f00f00000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + + + f00f00000000f00f00f00f00f00000000000000000000000000000 + 000f00000f00000000000000000f00000000000000000000000000 + 000f00000000000000000000000f00000000000000000000000000 + 000f00000000000f00f00f00f00000000000000000000000000000 + 000f00000000000000000000000f00000000000000000000000000 + 000f00000f00000000000000000f00000000000000000000000000 + 000f00000000f00f00f00f00f00000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + + + f00000000f00f00f00f00f00000000000000000000000000000000 + f00000f00000000000000000f00000000000000000000000000000 + f00000000000000000000000f00000000000000000000000000000 + f00000000000f00f00f00f00000000000000000000000000000000 + f00000000000000000000000f00000000000000000000000000000 + f00000f00000000000000000f00000000000000000000000000000 + f00000000f00f00f00f00f00000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + + + 000000f00f00f00f00f00000000000000000000000000000000000 + 000f00000000000000000f00000000000000000000000000000000 + 000000000000000000000f00000000000000000000000000000000 + 000000000f00f00f00f00000000000000000000000000000000000 + 000000000000000000000f00000000000000000000000000000000 + 000f00000000000000000f00000000000000000000000000000000 + 000000f00f00f00f00f00000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + + + 000f00f00f00f00f00000000000000000000000000000000000000 + f00000000000000000f00000000000000000000000000000000000 + 000000000000000000f00000000000000000000000000000000000 + 000000f00f00f00f00000000000000000000000000000000000000 + 000000000000000000f00000000000000000000000000000000000 + f00000000000000000f00000000000000000000000000000000000 + 000f00f00f00f00f00000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + + + f00f00f00f00f00000000000000000000000000000000000000000 + 000000000000000f00000000000000000000000000000000000000 + 000000000000000f00000000000000000000000000000000000000 + 000f00f00f00f00000000000000000000000000000000000000000 + 000000000000000f00000000000000000000000000000000000000 + 000000000000000f00000000000000000000000000000000000000 + f00f00f00f00f00000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + + + f00f00f00f00000000000000000000000000000000000000000000 + 000000000000f00000000000000000000000000000000000000000 + 000000000000f00000000000000000000000000000000000000000 + f00f00f00f00000000000000000000000000000000000000000000 + 000000000000f00000000000000000000000000000000000000000 + 000000000000f00000000000000000000000000000000000000000 + f00f00f00f00000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + + + f00f00f00000000000000000000000000000000000000000000000 + 000000000f00000000000000000000000000000000000000000000 + 000000000f00000000000000000000000000000000000000000000 + f00f00f00000000000000000000000000000000000000000000000 + 000000000f00000000000000000000000000000000000000000000 + 000000000f00000000000000000000000000000000000000000000 + f00f00f00000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + + + f00f00000000000000000000000000000000000000000000000000 + 000000f00000000000000000000000000000000000000000000000 + 000000f00000000000000000000000000000000000000000000000 + f00f00000000000000000000000000000000000000000000000000 + 000000f00000000000000000000000000000000000000000000000 + 000000f00000000000000000000000000000000000000000000000 + f00f00000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + + + f00000000000000000000000000000000000000000000000000000 + 000f00000000000000000000000000000000000000000000000000 + 000f00000000000000000000000000000000000000000000000000 + f00000000000000000000000000000000000000000000000000000 + 000f00000000000000000000000000000000000000000000000000 + 000f00000000000000000000000000000000000000000000000000 + f00000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + + + 000000000000000000000000000000000000000000000000000000 + f00000000000000000000000000000000000000000000000000000 + f00000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + f00000000000000000000000000000000000000000000000000000 + f00000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + + + 000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + + + 000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + + + 000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + + diff --git a/playlist.txt b/playlist.txt new file mode 100644 index 0000000..a28a85e --- /dev/null +++ b/playlist.txt @@ -0,0 +1,147 @@ +#heart.blm +#countdown.blm +test.blm +klonfish-dancing.blm +blm/invasion.blm +blm/torus.blm +blm/antiwar.blm +blm/billard.blm +blm/x-ball.blm +blm/game.blm +blm/tropfen.blm +blm/moewe_frontal.blm +blm/fireworks_2.blm +blm/thunderstorm.blm +blm/biker.blm +blm/fantasticspace.blm +blm/allyourbase.blm +blm/21st_century_man.blm +blm/dont_try_at_home.blm +blm/spin.blm +blm/heart.blm +blm/encyclops.blm +blm/fant.blm +blm/schnecke.blm +blm/peao.blm +blm/falling_pix.blm +blm/tetris.blm +blm/text.blm +blm/landing_zone.blm +blm/dance_micky_chickee.blm +blm/supermaennchen.blm +blm/allon.blm +blm/kame.blm +blm/kiss.blm +blm/fallingrows.blm +blm/jesus.blm +blm/vlhurg.blm +blm/wasserhahn.blm +blm/gewaber.blm +blm/tla_them.blm +blm/quix_glitter.blm +blm/herz.blm +blm/sanduhr.blm +blm/defect_car.blm +blm/peepshow.blm +blm/countdown.blm +blm/luftballoons.blm +blm/impaexpa.blm +blm/wein_trinken.blm +blm/coffee.blm +blm/camel.blm +blm/matrix.blm +blm/life.blm +blm/snake.blm +blm/windshield.blm +blm/raumschiff_enterprise.blm +blm/pumpkin.blm +blm/yeastman.blm +blm/genomix.blm +blm/bad_luck.blm +blm/om_sweet_om.blm +blm/invaders_andreas.blm +blm/manifesto5.blm +blm/chat_noir.blm +blm/rakete.blm +blm/manifesto3.blm +blm/relativity.blm +blm/fussballer.blm +blm/sinus.blm +blm/love_triangles.blm +blm/ascii_people.blm +blm/hanoi.blm +blm/monster.blm +blm/come_together.blm +blm/matrix_maennchen.blm +blm/silent_night.blm +blm/halt.blm +blm/das_leben_ist_schoen.blm +blm/moving_car.blm +blm/spirals.blm +blm/mandel.blm +blm/alloff.blm +blm/manifesto1.blm +blm/quantorenlyrik.blm +blm/manifesto4.blm +blm/warterad.blm +blm/surprise.blm +blm/oneon.blm +blm/manifesto7.blm +blm/fireworks.blm +blm/labyrinth.blm +blm/brainfuck.blm +blm/die_erde.blm +blm/klo.blm +blm/psychowarrior.blm +blm/timeless.blm +blm/auge.blm +blm/worm.blm +blm/colagoboom.blm +blm/musterbeispiel.blm +blm/fallingpixels.blm +blm/franknstoned.blm +blm/rollo_grow.blm +blm/illuminati.blm +blm/3rd_advent.blm +blm/chaosknoten.blm +blm/be_happy.blm +blm/binary_god.blm +blm/kingzilla.blm +blm/paradox.blm +blm/dj.blm +blm/hut.blm +blm/sortofselftest.blm +blm/blubb.blm +blm/bleeeh.blm +blm/rain.blm +blm/flash.blm +blm/femina_light.blm +blm/gator_rogat.blm +blm/speedit.blm +blm/scooter.blm +blm/pixie_in_the_box.blm +blm/zeichen.blm +blm/humanistic.blm +blm/pacman_saga.blm +blm/die_autobahn.blm +blm/mytest3.blm +blm/yin_yang.blm +blm/klettermensch.blm +blm/baustein.blm +blm/pwm.blm +blm/babelfish.blm +blm/mytest2.blm +blm/winslows.blm +blm/dance.blm +blm/martelo.blm +blm/raindrops.blm +blm/g.blm +blm/clear.blm +blm/sport_haelt_fit.blm +blm/kreise.blm +blm/railroad.blm +blm/3D_cube.blm +blm/balloon.blm +blm/flipp.blm +blm/mytest.blm +blm/tux.blm diff --git a/playlist2.txt b/playlist2.txt new file mode 100644 index 0000000..c125249 --- /dev/null +++ b/playlist2.txt @@ -0,0 +1,143 @@ +blm/invasion.blm +blm/torus.blm +blm/antiwar.blm +blm/billard.blm +blm/x-ball.blm +blm/game.blm +blm/tropfen.blm +blm/moewe_frontal.blm +blm/fireworks_2.blm +blm/thunderstorm.blm +blm/biker.blm +blm/fantasticspace.blm +blm/allyourbase.blm +blm/21st_century_man.blm +blm/dont_try_at_home.blm +blm/spin.blm +blm/heart.blm +blm/encyclops.blm +blm/fant.blm +blm/schnecke.blm +blm/peao.blm +blm/falling_pix.blm +blm/tetris.blm +blm/text.blm +blm/landing_zone.blm +blm/dance_micky_chickee.blm +blm/supermaennchen.blm +blm/allon.blm +blm/kame.blm +blm/kiss.blm +blm/fallingrows.blm +blm/jesus.blm +blm/vlhurg.blm +blm/wasserhahn.blm +blm/gewaber.blm +blm/tla_them.blm +blm/quix_glitter.blm +blm/herz.blm +blm/sanduhr.blm +blm/defect_car.blm +blm/peepshow.blm +blm/countdown.blm +blm/luftballoons.blm +blm/impaexpa.blm +blm/wein_trinken.blm +blm/coffee.blm +blm/camel.blm +blm/matrix.blm +blm/life.blm +blm/snake.blm +blm/windshield.blm +blm/raumschiff_enterprise.blm +blm/pumpkin.blm +blm/yeastman.blm +blm/genomix.blm +blm/bad_luck.blm +blm/om_sweet_om.blm +blm/invaders_andreas.blm +blm/manifesto5.blm +blm/chat_noir.blm +blm/rakete.blm +blm/manifesto3.blm +blm/relativity.blm +blm/fussballer.blm +blm/sinus.blm +blm/love_triangles.blm +blm/ascii_people.blm +blm/hanoi.blm +blm/monster.blm +blm/come_together.blm +blm/matrix_maennchen.blm +blm/silent_night.blm +blm/halt.blm +blm/das_leben_ist_schoen.blm +blm/moving_car.blm +blm/spirals.blm +blm/mandel.blm +blm/alloff.blm +blm/manifesto1.blm +blm/quantorenlyrik.blm +blm/manifesto4.blm +blm/warterad.blm +blm/surprise.blm +blm/oneon.blm +blm/manifesto7.blm +blm/fireworks.blm +blm/labyrinth.blm +blm/brainfuck.blm +blm/die_erde.blm +blm/klo.blm +blm/psychowarrior.blm +blm/timeless.blm +blm/auge.blm +blm/worm.blm +blm/colagoboom.blm +blm/musterbeispiel.blm +blm/fallingpixels.blm +blm/franknstoned.blm +blm/rollo_grow.blm +blm/illuminati.blm +blm/3rd_advent.blm +blm/chaosknoten.blm +blm/be_happy.blm +blm/binary_god.blm +blm/kingzilla.blm +blm/paradox.blm +blm/dj.blm +blm/hut.blm +blm/sortofselftest.blm +blm/blubb.blm +blm/bleeeh.blm +blm/rain.blm +blm/flash.blm +blm/femina_light.blm +blm/gator_rogat.blm +blm/speedit.blm +blm/scooter.blm +blm/pixie_in_the_box.blm +blm/zeichen.blm +blm/humanistic.blm +blm/pacman_saga.blm +blm/die_autobahn.blm +blm/mytest3.blm +blm/yin_yang.blm +blm/klettermensch.blm +blm/baustein.blm +blm/pwm.blm +blm/babelfish.blm +blm/mytest2.blm +blm/winslows.blm +blm/dance.blm +blm/martelo.blm +blm/raindrops.blm +blm/g.blm +blm/clear.blm +blm/sport_haelt_fit.blm +blm/kreise.blm +blm/railroad.blm +blm/3D_cube.blm +blm/balloon.blm +blm/flipp.blm +blm/mytest.blm +blm/tux.blm diff --git a/pong.py b/pong.py new file mode 100644 index 0000000..d751ede --- /dev/null +++ b/pong.py @@ -0,0 +1,456 @@ +#!/usr/bin/python + +import time +import random +import sys +import colorsys + +import blup.frame +import blup.output + + +__numbers = { + 1: [[0,0,1,0,0],[0,0,1,0,0],[0,0,1,0,0],[0,0,1,0,0],[0,0,1,0,0,]], + 2: [[1,1,1,1,1],[0,0,0,0,1],[1,1,1,1,1],[1,0,0,0,0],[1,1,1,1,1,]], + 3: [[1,1,1,1,1],[0,0,0,0,1],[0,0,1,1,1],[0,0,0,0,1],[1,1,1,1,1,]], + 4: [[1,0,0,0,1],[1,0,0,0,1],[1,1,1,1,1],[0,0,0,0,1],[0,0,0,0,1,]], + 5: [[1,1,1,1,1],[1,0,0,0,0],[1,1,1,1,1],[0,0,0,0,1],[1,1,1,1,1,]], + 6: [[1,1,1,1,1],[1,0,0,0,0],[1,1,1,1,1],[1,0,0,0,1],[1,1,1,1,1,]], + 7: [[1,1,1,1,1],[0,0,0,0,1],[0,0,0,0,1],[0,0,0,0,1],[0,0,0,0,1,]], + 8: [[1,1,1,1,1],[1,0,0,0,1],[1,1,1,1,1],[1,0,0,0,1],[1,1,1,1,1,]], + 9: [[1,1,1,1,1],[1,0,0,0,1],[1,1,1,1,1],[0,0,0,0,1],[1,1,1,1,1,]], + 0: [[1,1,1,1,1],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1],[1,1,1,1,1,]], +} + +class Paddle(object): + def __init__(self, playground, xpos, ypos, size): + self.__playground = playground + self.__xpos = xpos + self.__size = size + self.__ypos = ypos + self.__nextMove = 0 + + @property + def nextMove(self): + return self.__nextMove + @nextMove.setter + def nextMove(self, value): + if value in [-1, 0, 1]: + self.__nextMove = value + else: + raise ValueError('invalid move') + + @property + def xpos(self): + return self.__xpos + @property + def ypos(self): + return self.__ypos + @property + def size(self): + return self.__size + @property + def nextMove(self): + return self.__nextMove + + def containsPoint(self, x, y): + if x == self.__xpos and y in range(self.__ypos, self.__ypos + self.__size): + return True + else: + return False + + def nextMoveUp(self): + self.__nextMove = -1 + + def nextMoveDown(self): + self.__nextMove = 1 + + def doNextMove(self): + if self.__nextMove is not 0: + if self.__nextMove == -1 and self.__ypos > 0: + self.__ypos -= 1 + elif self.__nextMove == 1 and self.__ypos + self.__size < self.__playground.height: + self.__ypos += 1 + self.__nextMove = 0 + +class Wall(object): + HORIZONTAL = 1 + VERTICAL = 2 + def __init__(self, orientation): + self.orientation = orientation + +class Ball(object): + def __init__(self, playground, xpos, ypos, xspeed, yspeed): + self.__playground = playground + self.__xpos = xpos + self.__ypos = ypos + self.__xspeed = xspeed + self.__yspeed = yspeed + self.__hitCallbacks = [] + + @property + def xpos(self): + return self.__xpos + @property + def ypos(self): + return self.__ypos + @property + def xspeed(self): + return self.__xspeed + @property + def yspeed(self): + return self.__yspeed + @xpos.setter + def xpos(self, value): + self.__xpos = value + @ypos.setter + def ypos(self, value): + self.__ypos = value + @xspeed.setter + def xspeed(self, value): + self.__xspeed = value + @yspeed.setter + def yspeed(self, value): + self.__yspeed = value + + def addHitCallback(self, callback): + self.__hitCallbacks.append(callback) + + def doHitCallbacks(self, obj): + for callback in self.__hitCallbacks: + callback(obj) + + def move(self, ignorePaddles=False): + if self.__xspeed == 0 and self.__yspeed == 0: + return + foundpos = False + while not foundpos: + if self.__xspeed == 0 and self.__yspeed == 0: + break + newx = self.__xpos + self.__xspeed + newy = self.__ypos + self.__yspeed + + newobj = self.__playground.getObjectAtPosition(newx, newy) + if isinstance(newobj, Wall): + self.doHitCallbacks(newobj) + + # bounce off at horizontal wall + if newobj.orientation == Wall.HORIZONTAL: + self.__yspeed *= -1 + else: + #self.__xspeed *= -1 + foundpos = True + elif isinstance(newobj, Paddle) and not ignorePaddles: + self.doHitCallbacks(newobj) + self.__xspeed *= -1 + + # bounce off at the paddle + if self.__yspeed == 0: + if self.__playground.getObjectAtPosition(newobj.xpos, newobj.ypos - 1) is None: + self.__yspeed = -1 + elif self.__plauground.getObjectAtPosition(newobj.xpos, newobj.ypos + 1) is None: + self.__yspeed = 1 + else: + if newobj.xpos < self.__xpos: + if self.__playground.getObjectAtPosition(self.__xpos - 1, self.__ypos) is None: + self.__yspeed *= -1 + if newobj.xpos > self.__xpos: + if self.__playground.getObjectAtPosition(self.__xpos + 1, self.__ypos) is None: + self.__yspeed *= -1 + + if newobj.nextMove != 0 and random.randint(0, 2) == 0: + self.__yspeed += newobj.nextMove + elif abs(self.__yspeed) != 1: + self.__yspeed = 1 if self.__yspeed > 0 else -1 + else: + adjobj = self.__playground.getObjectAtPosition(newx, self.__ypos) + if not ignorePaddles and isinstance(adjobj, Paddle): + self.doHitCallbacks(adjobj) + self.__xspeed *= -1 + else: + foundpos = True + + + self.__xpos = newx + self.__ypos = newy + +class Playground(object): + def __init__(self, width, height, paddlesize=3): + self.__width = width + self.__height = height + + paddleLeft = Paddle(self, 0, (height - paddlesize)/2, paddlesize) + paddleRight = Paddle(self, width - 1, (height - paddlesize)/2, paddlesize) + self.__paddles = [paddleLeft, paddleRight] + + self.__ball = Ball(self, 0, 0, 1, 1) + self.__gameTickCallbacks = [] + self.__newRoundCallbacks = [] + + @property + def width(self): + return self.__width + @property + def height(self): + return self.__height + @property + def leftPaddle(self): + return self.__paddles[0] + @property + def rightPaddle(self): + return self.__paddles[1] + @property + def ball(self): + return self.__ball + + def containsPoint(self, x, y): + if x >= 0 and x < self.width and y >= 0 and y < self.height: + return True + else: + return False + + def addGameTickCallback(self, callback): + self.__gameTickCallbacks.append(callback) + print 'registered callback',callback + def addNewRoundCallback(self, callback): + self.__newRoundCallbacks.append(callback) + + def getObjectAtPosition(self, x, y): + if x >= self.__width or x < 0: + return Wall(Wall.VERTICAL) + elif y >= self.__height or y < 0: + return Wall(Wall.HORIZONTAL) + elif y == self.__ball.ypos and x == self.__ball.xpos: + return self.__ball + else: + for paddle in self.__paddles: + if paddle.containsPoint(x, y): + return paddle + + def play(self, serve=None): + leftPaddle = self.__paddles[0] + rightPaddle = self.__paddles[1] + ball = self.__ball + + if serve not in self.__paddles: + serve = self.__paddles[random.randint(0, 1)] + + ball.ypos = self.__height / 2 + ball.xpos = self.__width / 2 + if serve == rightPaddle and self.__width % 2 == 1: + ball.xpos += 1 + + ball.yspeed = 0 + if serve == rightPaddle: + ball.xspeed = 1 + else: + ball.yspeed = -1 + + for callback in self.__newRoundCallbacks: + callback() + + while True: + time.sleep(0.1) + ball.move() + leftPaddle.doNextMove() + rightPaddle.doNextMove() + + if not self.containsPoint(ball.xpos, ball.ypos): + break + + for callback in self.__gameTickCallbacks: + print callback + callback() + + if ball.xpos >= self.width: + return leftPaddle + elif ball.xpos <= 0: + return rightPaddle + + +def getRandomColor(maxval): + return map(lambda x: int(round(x*maxval)), colorsys.hsv_to_rgb(random.random(), 1, 1)) + #return map(lambda x: int(round(x*maxval)), colorsys.hsv_to_rgb(random.random(), random.random(), random.random())) + + +class PlaygroundPainter(object): + def __init__(self, out, dimension, playground): + self.__out = out + self.__playground = playground + self.__dimension = dimension + self.__playground.addGameTickCallback(self.update) + self.__playground.ball.addHitCallback(self.ballhit) + + if dimension.channels == 1: + self.__ballColor = 1 + self.__paddleColor = 1 + else: + self.__ballColor = getRandomColor(dimension.depth - 1) + self.__leftPaddleColor = getRandomColor(dimension.depth - 1) + self.__rightPaddleColor = getRandomColor(dimension.depth - 1) + + def update(self): + frame = blup.frame.Frame(self.__dimension) + #self.__paddleColor = getRandomColor(self.__dimension.depth - 1) + + if self.__dimension.channels == 3: + for x in range(self.__dimension.width): + for y in range(self.__dimension.height): + frame.setPixel(x, y, (0,0,0)) + + frame.setPixel(self.__playground.ball.xpos, self.__playground.ball.ypos, self.__ballColor) + + for i in range(self.__playground.leftPaddle.size): + frame.setPixel(self.__playground.leftPaddle.xpos, self.__playground.leftPaddle.ypos + i, self.__leftPaddleColor) + for i in range(self.__playground.rightPaddle.size): + frame.setPixel(self.__playground.rightPaddle.xpos, self.__playground.rightPaddle.ypos + i, self.__rightPaddleColor) + + self.__out.sendFrame(frame) + + def ballhit(self, obj): + if self.__dimension.channels == 3: + if isinstance(obj, Paddle): + self.__ballColor = getRandomColor(self.__dimension.depth - 1) + if obj.xpos == 0: + self.__leftPaddleColor = self.__ballColor + else: + self.__rightPaddleColor = self.__ballColor + + + + + +class PongBot(object): + def __init__(self, playground, ownPaddle, dullness=0): + self.__playground = playground + self.__ownPaddle = ownPaddle + self.__dullness = dullness + self.__playground.addGameTickCallback(self.onGameTick) + self.__playground.addNewRoundCallback(self.onNewRound) + self.__playground.ball.addHitCallback(self.onHit) + self.__hitpoint = None + self.__oldHitpoint = None + self.__dull = False + + def updateHitpoint(self): + origball = self.__playground.ball + ball = Ball(self.__playground, origball.xpos, origball.ypos, origball.xspeed, origball.yspeed) + ball.move() + hitpoint = None + while hitpoint is None: + if ball.xpos >= self.__playground.width - 1 or ball.xpos <= 0: + hitpoint = (ball.xpos, ball.ypos) + break + + ball.move(ignorePaddles=True) + self.__oldHitpoint = self.__hitpoint + self.__hitpoint = hitpoint + + def onHit(self, obj): + print 'hit!', obj + if isinstance(obj, Paddle) and obj is not self.__ownPaddle: + self.updateHitpoint() + + if self.__dullness > random.randint(0, 4): + self.__dull = True + else: + self.__dull = False + + def onNewRound(self): + self.updateHitpoint() + self.__oldHitpoint = None + self.__dull = False + + def onGameTick(self): + #self.updateHitpoint() + print 'hitpoint', self.__hitpoint + (hitx, hity) = self.__hitpoint + + + + if hitx == self.__ownPaddle.xpos: + if abs(self.__playground.ball.xpos - self.__ownPaddle.xpos) < 15: + if not self.__dull: + if not self.__ownPaddle.containsPoint(hitx, hity): + print 'moving!!' + if self.__ownPaddle.ypos < hity: + self.__ownPaddle.nextMoveDown() + else: + self.__ownPaddle.nextMoveUp() + else: + if self.__ownPaddle.containsPoint(hitx, hity): + print 'moving!!' + if hity < self.__ownPaddle.size: + self.__ownPaddle.nextMoveDown() + else: + self.__ownPaddle.nextMoveUp() + + elif self.__dull: + r = random.randint(-1, 1) + if r == -1: + self.__ownPaddle.nextMoveUp() + elif r == 1: + self.__ownPaddle.nextMoveDown() + + +def displayScore(out, dimension, leftScore, rightScore, delay=0): + leftNumber = __numbers[leftScore] + rightNumber = __numbers[rightScore] + + frame = blup.frame.Frame(dimension) + + if dimension.channels == 1: + dotcolor = 1 + numcolor = 1 + else: + dotcolor = getRandomColor(dimension.depth - 1) + numcolor = getRandomColor(dimension.depth - 1) + + frame.setPixel(8, 1, dotcolor) + frame.setPixel(9, 1, dotcolor) + frame.setPixel(8, 2, dotcolor) + frame.setPixel(9, 2, dotcolor) + frame.setPixel(8, 4, dotcolor) + frame.setPixel(9, 4, dotcolor) + frame.setPixel(8, 5, dotcolor) + frame.setPixel(9, 5, dotcolor) + + for x in range(5): + for y in range(5): + if leftNumber[y][x] == 1: + frame.setPixel(x+1, y+1, numcolor) + if rightNumber[y][x] == 1: + frame.setPixel(x+12, y+1, numcolor) + + out.sendFrame(frame) + if delay > 0: + time.sleep(delay / 1000.0) + + + +def main(): + print 'running a \'Bot vs. Bot\' round...' + #out = blup.output.getOutput('blp:localhost:42421') + #out = blup.output.getOutput('e3blp:bastel0:4242') + out = blup.output.getOutput('e3blp:localhost:4242') + #out = blup.output.getOutput('e3blp:bbunt:42429') + #dim = blup.frame.FrameDimension(18, 8, 8, 3) + dim = blup.frame.FrameDimension(22, 16, 255, 3) + + playground = Playground(22, 16, paddlesize=4) + pp = PlaygroundPainter(out, dim, playground) + + leftPaddle = playground.leftPaddle + rightPaddle = playground.rightPaddle + ball = playground.ball + + leftBot = PongBot(playground, leftPaddle, 1) + rightBot = PongBot(playground, rightPaddle, 1) + + print playground.play() + + +if __name__ == '__main__': + main() + + diff --git a/r0ketpong.py b/r0ketpong.py new file mode 100644 index 0000000..fa8b206 --- /dev/null +++ b/r0ketpong.py @@ -0,0 +1,178 @@ +#!/usr/bin/python + +import time +import threading +import sys + +import blup.frame +import blup.output +sys.path.append('/var/tmp/r0ket/tools/game') +#sys.path.append('/var/tmp/r0ket_misc/game_noprint') +import r0ketrem0te.game +import pong + +logo = [ +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], +[1,1,1,0,1,1,1,1,0,1,0,0,1,0,0,1,1,0], +[1,0,1,0,1,0,0,1,0,1,1,0,1,0,1,0,0,1], +[1,0,1,0,1,0,0,1,0,1,0,1,1,0,1,0,0,0], +[1,1,1,0,1,0,0,1,0,1,0,0,1,0,1,0,1,1], +[1,0,0,0,1,0,0,1,0,1,0,0,1,0,1,0,0,1], +[1,0,0,0,1,1,1,1,0,1,0,0,1,0,0,1,1,0], +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] +] +onePlayer = [ +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], +[0,0,1,1,0,0,1,1,1,1,0,0,0,1,1,1,0,0], +[0,1,0,1,0,0,1,0,0,1,0,0,1,0,0,0,1,0], +[0,0,0,1,0,0,1,0,0,1,0,0,0,0,0,1,0,0], +[0,0,0,1,0,0,1,1,1,1,0,0,0,0,1,0,0,0], +[0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0], +[0,0,1,1,1,0,1,0,0,0,0,0,0,0,1,0,0,0], +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] +] + +def convertPixels(dimension, pixels): + p = [] + maxval = dimension.depth - 1 + for i in range(len(pixels)): + row = [] + for j in range(len(pixels[i])): + if pixels[i][j] == 1: + row.append((maxval, maxval, maxval)) + else: + row.append((0,0,0)) + p.append(row) + return p + +class R0ketPongPlayer(object): + def __init__(self, playground, ownPaddle, r0ketPlayer): + self.__playground = playground + self.__ownPaddle = ownPaddle + self.__r0ketPlayer = r0ketPlayer + self.__ready = False + + @property + def ownPaddle(self): + return self.__ownPaddle + + @property + def ready(self): + return self.__ready + + def receivedPacket(self, packet): + if self.__r0ketPlayer is not None: + if packet.id == self.__r0ketPlayer.id: + if isinstance(packet, r0ketrem0te.packets.Button): + print 'button=', packet.button + if packet.button == 1: + self.__ownPaddle.nextMoveUp() + print 'up' + if packet.button == 2: + self.__ownPaddle.nextMoveDown() + print 'down' + if packet.button == 16: + self.__ready = True + +class R0ketPong(object): + def __init__(self, out, color=False, port='/dev/ttyACM0', gameName='pong'): + self.__playground = None + self.__out = out + self.__rem0te = r0ketrem0te.game.Game(port, gameName, 83, 87, [ord(x) for x in 'REM0T'], 2, False) + self.__rem0te.registerPlayerCallback(self.playerCallback) + self.__color = color + + self.__players = [] + self.__playersChangedCondition = threading.Condition() + + if color: + self.__dimension = blup.frame.FrameDimension(18, 8, 8, 3) + else: + self.__dimension = blup.frame.FrameDimension(18, 8, 2, 1) + + def playerCallback(self, action, player): + if self.__playground is None: + return + print '\n\n\n\nplayer added',player,'\n\n\n\n' + if action == 'added' and len(self.__players) < 2: + if len(self.__players) == 0: + playerPaddle = self.__playground.leftPaddle + else: + playerPaddle = self.__playground.rightPaddle + player = R0ketPongPlayer(self.__playground, playerPaddle, player) + self.__rem0te.bridge.registerCallback(player.receivedPacket) + self.__players.append(player) + with self.__playersChangedCondition: + self.__playersChangedCondition.notifyAll() + + def runGame(self): + print 'starting a game...' + scoreLeft = 0 + scoreRight = 0 + + self.__playground = pong.Playground(18, 8) + pp = pong.PlaygroundPainter(out, self.__dimension, self.__playground) + + while len(self.__players) == 0: + print 'waiting for player...' + with self.__playersChangedCondition: + self.__playersChangedCondition.wait() + + frame = blup.frame.Frame(self.__dimension) + global onePlayer + if self.__color: + frame.pixels = convertPixels(self.__dimension, onePlayer) + else: + frame.pixels = onePlayer + self.__out.sendFrame(frame) + + while len(self.__players) == 1 and not self.__players[0].ready: + print 'waiting for another player...' + time.sleep(1) + self.__out.sendFrame(frame) + + frame = blup.frame.Frame(self.__dimension) + global logo + if self.__color: + frame.pixels = convertPixels(self.__dimension, logo) + else: + frame.pixels = logo + self.__out.sendFrame(frame) + time.sleep(1) + + if len(self.__players) == 1: + bot = pong.PongBot(self.__playground, self.__playground.rightPaddle, 2) + self.__players.append(bot) + + while max(scoreLeft, scoreRight) < 9: + winner = self.__playground.play() + if winner is self.__players[0].ownPaddle: + scoreLeft += 1 + else: + scoreRight += 1 + pong.displayScore(self.__out, self.__dimension, scoreLeft, scoreRight, 3000) + + for i in range(3): + frame = blup.frame.Frame(self.__dimension) + self.__out.sendFrame(frame) + time.sleep(0.5) + pong.displayScore(self.__out, self.__dimension, scoreLeft, scoreRight, 500) + frame = blup.frame.Frame(self.__dimension) + self.__out.sendFrame(frame) + + self.__playground = None + self.__players = [] + + + +out = blup.output.getOutput('e3blp:localhost:4242') + + +p0ng = R0ketPong(out, color=True) +while True: + p0ng.runGame() + +sys.exit(0) + + + diff --git a/randomPlayer.py b/randomPlayer.py new file mode 100644 index 0000000..7c256dd --- /dev/null +++ b/randomPlayer.py @@ -0,0 +1,4 @@ +#!/usr/bin/python + +import blup + diff --git a/robot.bml b/robot.bml new file mode 100644 index 0000000..24a2930 --- /dev/null +++ b/robot.bml @@ -0,0 +1,28 @@ + + +
    + Blimp (version 0.1.5) +
    + + + 000000000FF00F0FF0000000000000000000FF00F0FF0000000000 + 0000000000F00F00F00000000000000000000F00F00F0000000000 + 000000000000090000000000000000000000000090000000000000 + 0000000000F00F00F00000900000000000000F00F00F0000090000 + 0000900900F0FF00F00900900000000900900F0FF00F0090090000 + 0000900000F00F00F00000000000000900000F00F00F0000000000 + 0000000000F00F00F00000000000000000000F00F00F0000000000 + 000000000090000090000000000000000000090000090000000000 + + + + 000000000FF00F0FF0000000000000000000FF00F0FF0000000000 + 0000000000F00F00F00000000000000000000F00F00F0000000000 + 000000000000090000000000000000000000000090000000000000 + 0000900000F00F00F00000000000000900000F00F00F0000000000 + 0000900900F0FF00F00900900000000900900F0FF00F0090090000 + 0000000000F00F00F00000900000000000000F00F00F0000090000 + 0000000000F00F00F00000000000000000000F00F00F0000000000 + 000000000090000090000000000000000000090000090000000000 + +
    diff --git a/smileys.bml b/smileys.bml new file mode 100644 index 0000000..8f583c1 --- /dev/null +++ b/smileys.bml @@ -0,0 +1,39 @@ + + +
    + Blimp (version 0.1.5) +
    + + + 000000000000000000000000000000000000000000000000000000 + 0000FF0000000FF000000FF0000000FF0000000F0F000000F0F000 + 000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + 0000FF0000000FF000000FF0000000FF0000000F0F000000F0F000 + 0000000FF0FF000000000000FF0FF0000000000000F0FF0F000000 + 000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + + + + 000000000000000000000000000000000000000000000000000000 + 0000F00000000F0000000F00000000F0000000000F00000000F000 + 000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + 0000F00000000F0000000F00000000F0000000000F00000000F000 + 0000000F00F0000000000000F00F0000000000000000F00F000000 + 000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + + + + 000000000000000000000000000000000000000000000000000000 + 000FFF000000FFF000000FFF000000FFF000000FFF000000FFF000 + 000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + 000FFF000000FFF000000FFF000000FFF000000FFF000000FFF000 + 000000FFFFFF000000000000FFFFFF000000000000FFFFFF000000 + 000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + +
    diff --git a/smileys2.bml b/smileys2.bml new file mode 100644 index 0000000..035ee4f --- /dev/null +++ b/smileys2.bml @@ -0,0 +1,50 @@ + + +
    + Blimp (version 0.1.5) +
    + + + 000000000000000000000000000000000000000000000000000000 + 000000000000000000000FF0000000FF0000000F0F000000F0F000 + 0000FF0000000FF000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + 000000000000000000000FF0000000FF0000000F0F000000F0F000 + 0000FF0000000FF000000000FF0FF0000000000000F0FF0F000000 + 0000000FF0FF000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + + + + 000000000000000000000000000000000000000000000000000000 + 0000F00000000F000000000000000000000000000F00000000F000 + 000000000000000000000F00000000F00000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + 0000F00000000F000000000000000000000000000F00000000F000 + 0000000F00F0000000000F00000000F0000000000000F00F000000 + 000000000000000000000000F00F00000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + + + + 000000000000000000000000000000000000000000000000000000 + 000FFF000000FFF000000FFF000000FFF000000000000000000000 + 000000000000000000000000000000000000000FFF000000FFF000 + 000000000000000000000000000000000000000000000000000000 + 000FFF000000FFF000000FFF000000FFF000000000000000000000 + 000000FFFFFF000000000000FFFFFF000000000FFF000000FFF000 + 000000000000000000000000000000000000000000FFFFFF000000 + 000000000000000000000000000000000000000000000000000000 + + + + 000000000000000000000000000000000000000000000000000000 + 000000FF0000000FF0000FF0000000FF0000FF0000000FF0000000 + 000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + 000000FF0000000FF0000FF0000000FF0000FF0000000FF0000000 + 000000000FF0FF0000000000FF0FF0000000000FF0FF0000000000 + 000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000 + +
    diff --git a/staticSinePlasma.py b/staticSinePlasma.py new file mode 100644 index 0000000..2a265be --- /dev/null +++ b/staticSinePlasma.py @@ -0,0 +1,135 @@ +#!/usr/bin/python + +# inspired by: +# http://lodev.org/cgtutor/plasma.html + +import sys +import time +import math +#import pygame +import colorsys +import blup.frame +import blup.output + + +class Plasma(object): + def __init__(self, width, height): + self.width = width + self.height = height + + self.pixelvalues = [[0] * height for i in xrange(width)] + self.pixelsReady = False + self.offset = 0 + self.update() + + @property + def minValue(self): + return min(map(min, self.pixelvalues)) + + @property + def maxValue(self): + return max(map(max, self.pixelvalues)) + + def applyPalette(self, palette): + min = self.minValue + max = self.maxValue + norm = lambda x: ((x - min) / max) + + pixeldata = [ [None] * self.height for i in xrange(self.width) ] + + for x in xrange(self.width): + rowvalues = self.pixelvalues[x] + for y in xrange(self.height): + pixeldata[x][y] = palette.getColorValue(norm(rowvalues[y])) + + return pixeldata + + def update(self): + #if self.pixelsReady: + # return + self.offset += 1 + + for x in xrange(self.width): + for y in xrange(self.height): + xx = x+self.offset + yy = y+self.offset + p = 128 + 128 * math.sin(xx / 9.0) + p += 128 + 128 * math.sin(yy / 16.0) + p += 128 + 128 * math.sin((yy + xx) / 16.0) + p += 128 + 128 * math.sin(math.sqrt(yy*yy + xx*xx) / 16.0) + p += 128 + 8 * math.sin(math.sqrt(yy* xx) / 16.0) + self.pixelvalues[x][y] = p + self.pixelsReady = True + +class Palette(object): + def __init__(self): + self.offset = 0 + self.cache = {} + + def getColorValue(self, x): + x = round(x, 3) + if self.cache.has_key(x): + return self.cache[x] + c = colorsys.hsv_to_rgb((x + self.offset) % 1, 1, 1) + c = map(lambda x: int(x * 255), c) + self.cache[x] = c + return c + + def update(self): + self.cache = {} + self.offset += 0.01 + if self.offset > 1: + self.offset -= 1 + +############################################## + +w = 22 +h = 16 + +#w = 80 +#h = 18 + +scalex = 30 +scaley = 60 + +plasma = Plasma(w,h) +palette= Palette() + +#screen = pygame.display.set_mode((w*scalex, h*scaley)) +#pygame.display.update() + +#dim = blup.frame.FrameDimension(w, h, 8, 3) +dim = blup.frame.FrameDimension(w, h, 256, 3) +#out = blup.output.getOutput('serialblup:/dev/ttyUSB0:115200') +#out = blup.output.getOutput('colorfulshell') +#out = blup.output.getOutput('e3blp:bastel0:4242') +out = blup.output.getOutput('e3blp') + +while True: + #screen.fill((0,0,0)) + + #print 'updating palette...' + palette.update() + #print 'updating plasma...' + plasma.update() + #print 'applying palette...' + pixeldata = plasma.applyPalette(palette) + + #print 'drawing...' + f = blup.frame.Frame(dim) + for x in xrange(w): + #rowdata = pixeldata[x] + for y in xrange(h): + #pygame.draw.rect(screen, pixeldata[x][y], (x*scalex, y*scaley, scalex, scaley), 0) + #pygame.draw.circle(screen, rowdata[y], (x, y), 1, 1) + i= f.setPixel(x, y, pixeldata[x][y]) + #print frame.channels, frame.depth + #print 'done drawing' + + out.sendFrame(f) + + #pygame.display.update() + #print 'done updating' + #time.sleep(0.05) + + diff --git a/staticSinePlasma_neopixel.py b/staticSinePlasma_neopixel.py new file mode 100644 index 0000000..0913b59 --- /dev/null +++ b/staticSinePlasma_neopixel.py @@ -0,0 +1,152 @@ +#!/usr/bin/python + +# inspired by: +# http://lodev.org/cgtutor/plasma.html + +import sys +import time +import math +#import pygame +import colorsys +from neopixel import * + + +class Plasma(object): + def __init__(self, width, height): + self.width = width + self.height = height + + self.pixelvalues = [[0] * height for i in xrange(width)] + self.pixelsReady = False + self.offset = 0 + self.update() + + @property + def minValue(self): + return min(map(min, self.pixelvalues)) + + @property + def maxValue(self): + return max(map(max, self.pixelvalues)) + + def applyPalette(self, palette): + min = self.minValue + max = self.maxValue + norm = lambda x: ((x - min) / max) + + pixeldata = [ [None] * self.height for i in xrange(self.width) ] + + for x in xrange(self.width): + rowvalues = self.pixelvalues[x] + for y in xrange(self.height): + pixeldata[x][y] = palette.getColorValue(norm(rowvalues[y])) + + return pixeldata + + def update(self): + #if self.pixelsReady: + # return + self.offset += 1 + + for x in xrange(self.width): + for y in xrange(self.height): + xx = x+self.offset + yy = y+self.offset + #xx = 10*x+self.offset + #yy = 10*y+self.offset + p = 128 + 128 * math.sin(xx / 9.0) + p += 128 + 128 * math.sin(yy / 16.0) + p += 128 + 128 * math.sin((yy + xx) / 16.0) + p += 128 + 128 * math.sin(math.sqrt(yy*yy + xx*xx) / 16.0) + p += 128 + 8 * math.sin(math.sqrt(yy* xx) / 16.0) + self.pixelvalues[x][y] = p + self.pixelsReady = True + +class Palette(object): + def __init__(self): + self.offset = 0 + self.cache = {} + + def getColorValue(self, x): + x = round(x, 3) + if self.cache.has_key(x): + return self.cache[x] + c = colorsys.hsv_to_rgb((x + self.offset) % 1, 1, 1) + #c = map(lambda x: int(x * 7), c) + depth = 255 + c = map(lambda x: int(x * depth), c) + self.cache[x] = c + return c + + def update(self): + self.cache = {} + self.offset += 0.01 + if self.offset > 1: + self.offset -= 1 + +############################################## + +w = 22 +h = 16 + +#w = 80 +#h = 18 + +scalex = 30 +scaley = 60 + +plasma = Plasma(w,h) +palette= Palette() + +#screen = pygame.display.set_mode((w*scalex, h*scaley)) +#pygame.display.update() + +##dim = blup.frame.FrameDimension(w, h, 8, 3) +#dim = blup.frame.FrameDimension(w, h, 8, 3) +##out = blup.output.getOutput('serialblup:/dev/ttyUSB0:115200') +##out = blup.output.getOutput('colorfulshell') +##out = blup.output.getOutput('e3blp:bastel0:4242') +#out = blup.output.getOutput('e3blp') + + +# LED strip configuration: +LED_COUNT = 352 # Number of LED pixels. +LED_PIN = 13 # GPIO pin connected to the pixels (must support PWM!). +LED_FREQ_HZ = 800000 # LED signal frequency in hertz (usually 800khz) +LED_DMA = 5 # DMA channel to use for generating signal (try 5) +LED_BRIGHTNESS = 50 # Set to 0 for darkest and 255 for brightest +LED_INVERT = False # True to invert the signal (when using NPN transistor level shift) +LED_PWM = 1 + +strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS, LED_PWM) +strip.begin() + +while True: + #screen.fill((0,0,0)) + + #print 'updating palette...' + palette.update() + #print 'updating plasma...' + plasma.update() + #print 'applying palette...' + pixeldata = plasma.applyPalette(palette) + + #print 'drawing...' + #f = blup.frame.Frame(dim) + for y in xrange(h): + for x in xrange(w): + if x%2 == 0: + lednum = x*h + y + else: + lednum = x*h - y + 15 + strip.setPixelColor(lednum, Color(*pixeldata[x][y])) + strip.show() + #print 'done drawing' + + #out.sendFrame(f) + + #pygame.display.update() + #print 'done updating' + #time.sleep(0.01) + + diff --git a/test.txt b/test.txt new file mode 100644 index 0000000..e36a058 --- /dev/null +++ b/test.txt @@ -0,0 +1 @@ +test.blm diff --git a/ttrs.py b/ttrs.py new file mode 100755 index 0000000..61f4860 --- /dev/null +++ b/ttrs.py @@ -0,0 +1,311 @@ +#!/usr/bin/env python3 + +import time +import enum +import math +import collections +import blup.frame +import blup.output +import random + + +class InvalidMoveError(Exception): + pass + +class Point(collections.namedtuple('Point', ['x', 'y'])): + __slots__ = () + + def __add__(self, other): + return Point(self.x + other.x, self.y + other.y) + + def floored(self): + return Point(math.floor(self.x), math.floor(self.y)) + +Block = collections.namedtuple('Block', ['pos', 'color']) + + +class Tetrimino(): + def __init__(self, shape, playground, pos): + self.shape = shape + self.playground = playground + self.pos = pos + + @property + def width(self): + x = list(map(lambda s: s.pos.x, self.shape)) + return max(x) - min(x) + + @property + def height(self): + y = list(map(lambda s: s.pos.y, self.shape)) + return max(y) - min(y) + + @property + def blocks(self): + ret = { Block((self.pos + b.pos).floored(), b.color) for b in + self.shape } + return ret + + def __calc_points(self, pos=None, shape=None): + if pos is None: + pos = self.pos + if shape is None: + shape = self.shape + return { (pos + b.pos).floored() for b in shape } + + @property + def points(self): + return self.__calc_points() + + def __check_collision(self, newpoints): + if not self.playground.contains_points(newpoints): + raise InvalidMoveError('out of playground bounds') + print(self.playground.block_points) + print(self.playground.blocks) + print('new', newpoints) + if not self.playground.block_points.isdisjoint(newpoints): + raise InvalidMoveError('new position already occupied') + other_mino_points = self.playground.mino_points - self.points + if not other_mino_points.isdisjoint(newpoints): + raise InvalidMoveError('other Tetrimino at new position') + + def rotate(self, ccw=False): + if ccw: + transform = lambda s: Block(Point(s.pos.y, -s.pos.x), s.color) + else: + transform = lambda s: Block(Point(-s.pos.y, s.pos.x), s.color) + newshape = set(map(transform, self.shape)) + newpoints = self.__calc_points(shape=newshape) + self.__check_collision(newpoints) + self.shape = newshape + + def move(self, m): + newpos = self.pos + m + newpoints = self.__calc_points(pos=newpos) + self.__check_collision(newpoints) + self.pos = newpos + + + + + + +class TetriL(Tetrimino): + def __init__(self, playground, pos): + color = (255,165,0) + points = [(-1, 1), (-1, 0), (0, 0), (1, 0)] + shape = { Block(Point(x, y), color) for (x, y) in points } + Tetrimino.__init__(self, shape, playground, pos) + + +class TetriJ(Tetrimino): + def __init__(self, playground, pos): + color = (0,0,255) + points = [(-1, 0), (0, 0), (1, 0), (1, 1)] + shape = { Block(Point(x, y), color) for (x, y) in points } + Tetrimino.__init__(self, shape, playground, pos) + + +class TetriI(Tetrimino): + def __init__(self, playground, pos): + color = (0,255,255) + points = {(1.5, -0.5), (0.5, -0.5), (-0.5, -0.5), (-1.5, -0.5)} + shape = { Block(Point(x, y), color) for (x, y) in points } + Tetrimino.__init__(self, shape, playground, pos) + + +class TetriO(Tetrimino): + def __init__(self, playground, pos): + color = (255,255,0) + points = {(-0.5, -0.5), (-0.5, 0.5), (0.5, -0.5), (0.5, 0.5)} + shape = { Block(Point(x, y), color) for (x, y) in points } + Tetrimino.__init__(self, shape, playground, pos) + + +class TetriS(Tetrimino): + def __init__(self, playground, pos): + color = (128,255,0) + points = {(-1, 0), (0, 0), (0, -1), (1, -1)} + shape = { Block(Point(x, y), color) for (x, y) in points } + Tetrimino.__init__(self, shape, playground, pos) + + +class TetriZ(Tetrimino): + def __init__(self, playground, pos): + color = (255,0,0) + points = {(-1, 0), (0, 0), (0, 1), (1, 1)} + shape = { Block(Point(x, y), color) for (x, y) in points } + Tetrimino.__init__(self, shape, playground, pos) + + +class TetriT(Tetrimino): + def __init__(self, playground, pos): + color = (128,0,128) + points = {(-1, 0), (0, 0), (1, 0), (0, 1)} + shape = { Block(Point(x, y), color) for (x, y) in points } + Tetrimino.__init__(self, shape, playground, pos) + + +class Playground(): + def __init__(self, width=10, height=22): + self.width = width + self.height = height + self.blocks = set() + self.minos = set() + + @property + def block_points(self): + return set([ b.pos for b in self.blocks ]) + + @property + def mino_points(self): + return set.union(set(), *[ m.points for m in self.minos ]) + + def contains_points(self, points): + for p in points: + if p.x >= self.width or p.y >= self.height or p.x < 0 or p.y < 0: + return False + return True + + def paint(self, frame, xpos, ypos): + for x in range(frame.dimension.width): + for y in range(frame.dimension.height): + frame.setPixel(x, y, (0, 0, 0)) + for b in set.union(self.blocks, *[ m.blocks for m in self.minos ]): + frame.setPixel(xpos + int(b.pos.x), ypos + int(b.pos.y), b.color) + + +class TtrsGame(): + def __init__(self, playground): + self.playground = playground + self.running = False + self.next_move = 0 + self.rotate = False + self.drop = False + self.tick_callbacks = [] + + def add_tick_callback(self, cb): + self.tick_callbacks.append(cb) + + def run(self): + self.running = True + spawnpos = Point(self.playground.width // 2, 0) + mino = None + TICK_TIME = 0.1 + FALL_INTERVAL = 5 + MOVE_INTERVAL = 1 + ticks = 0 + lastfall = 0 + lastmove = 0 + while self.running: + for cb in self.tick_callbacks: + cb() + #time.sleep(TICK_TIME) + ticks += 1 + + x = input() + if x == 'a': + self.next_move = -1 + elif x == 'd': + self.next_move = 1 + elif x == 'w': + self.rotate = True + elif x == 's': + self.drop = True + else: + self.drop = False + self.rotate = False + self.next_move = 0 + + + if mino is None: + newminocls = random.choice(Tetrimino.__subclasses__()) + mino = newminocls(self.playground, spawnpos) + self.playground.minos = {mino} + + for y in range(self.playground.height): + row = { Point(x, y) for x in range(self.playground.width) } + if row.issubset(self.playground.block_points): + for b in list(self.playground.blocks): + if b.pos.y == y: + self.playground.blocks.remove(b) + elif b.pos.y < y: + self.playground.blocks.remove(b) + newb = Block(b.pos + Point(0, 1), b.color) + self.playground.blocks.add(newb) + + + + if ticks - lastfall >= FALL_INTERVAL or self.drop: + lastfall = ticks + try: + mino.move(Point(0, 1)) + except InvalidMoveError: + self.playground.blocks.update(mino.blocks) + mino = None + self.playground.minos = set() + continue + + if ticks - lastmove >= MOVE_INTERVAL and self.next_move != 0: + lastmove = ticks + try: + mino.move(Point(self.next_move, 0)) + except InvalidMoveError: + pass + self.next_move = 0 + + if self.rotate: + self.rotate = False + try: + mino.rotate() + except InvalidMoveError: + pass + + + + + + + + + + +if __name__ == '__main__': + w = 22 + h = 16 + + dim = blup.frame.FrameDimension(w, h, 256, 3) + frame = blup.frame.Frame(dim) + out = blup.output.getOutput('e3blp') + + pg = Playground(10, 16) + pg.paint(frame, 0, 0) + out.sendFrame(frame) + time.sleep(0.5) + + def repaint(): + pg.paint(frame, 0, 0) + out.sendFrame(frame) + + game = TtrsGame(pg) + game.add_tick_callback(repaint) + game.run() + + #t = TetriL(pg, Point(2, 2)) + #pg.minos.add(t) + #pg.paint(frame, 0, 0) + #out.sendFrame(frame) + #time.sleep(0.5) + + #for i in range(10): + # t.rotate(ccw=(i>=5)) + # pg.paint(frame, 0, 0) + # out.sendFrame(frame) + # time.sleep(0.1) + + + + + + + diff --git a/writebml.py b/writebml.py new file mode 100644 index 0000000..f81e590 --- /dev/null +++ b/writebml.py @@ -0,0 +1,49 @@ + + +import math +import xml.etree.ElementTree as ET + +def writeBml(anim, filename): + root = ET.Element('blm') + root.attrib['width'] = str(anim.dimension.width) + root.attrib['height'] = str(anim.dimension.height) + bits = int(math.ceil(math.log(anim.dimension.depth, 2))) + root.attrib['bits'] = str(bits) + root.attrib['channels'] = str(anim.dimension.channels) + + header = ET.Element('header') + for (name, val) in anim.tags: + elem = ET.Element(name) + elem.text = str(val) + header.append(elem) + root.append(header) + + for animFrame in anim: + frame = ET.Element('frame') + frame.attrib['duration'] = str(animFrame.delay) + for y in range(anim.dimension.height): + rowData = '' + for x in range(anim.dimension.width): + p = animFrame.getPixel(x,y) + if type(p) == tuple or type(p) == list: + for v in p: + if bits >= 5: + rowData += '%02x' % (v) + else: + rowData += '%x' % (v) + else: + if bits >= 5: + rowData += '%02x' % (p) + else: + rowData += '%x' % (p) + + row = ET.Element('row') + row.text = rowData + frame.append(row) + root.append(frame) + + f = open(filename, 'w') + ET.ElementTree(root).write(f) + f.close() + +