=a.length)return r?r.call(u,o):e?o.sort(e):o;for(var l,s,f,h,g=-1,p=o.length,m=a[c++],d=new i;++g=a.length)return n;var r=[],i=o[e++];return n.forEach(function(n,i){r.push({key:n,values:t(i,e)})}),i?r.sort(function(n,t){return i(n.key,t.key)}):r}var e,r,u={},a=[],o=[];return u.map=function(t,e){return n(e,t,0)},u.entries=function(e){return t(n(ya.map,e,0),0)},u.key=function(n){return a.push(n),u},u.sortKeys=function(n){return o[a.length-1]=n,u},u.sortValues=function(n){return e=n,u},u.rollup=function(n){return r=n,u},u},ya.set=function(n){var t=new u;if(n)for(var e=0,r=n.length;r>e;++e)t.add(n[e]);return t},r(u,{has:function(n){return qa+n in this},add:function(n){return this[qa+n]=!0,n},remove:function(n){return n=qa+n,n in this&&delete this[n]},values:function(){var n=[];return this.forEach(function(t){n.push(t)}),n},forEach:function(n){for(var t in this)t.charCodeAt(0)===Ta&&n.call(this,t.substring(1))}}),ya.behavior={},ya.rebind=function(n,t){for(var e,r=1,i=arguments.length;++r=0&&(r=n.substring(e+1),n=n.substring(0,e)),n)return arguments.length<2?this[n].on(r):this[n].on(r,t);if(2===arguments.length){if(null==t)for(n in this)this.hasOwnProperty(n)&&this[n].on(r,null);return this}},ya.event=null,ya.requote=function(n){return n.replace(ja,"\\$&")};var ja=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g,La={}.__proto__?function(n,t){n.__proto__=t}:function(n,t){for(var e in t)n[e]=t[e]},Ha=function(n,t){return t.querySelector(n)},Fa=function(n,t){return t.querySelectorAll(n)},Pa=xa[o(xa,"matchesSelector")],Oa=function(n,t){return Pa.call(n,t)};"function"==typeof Sizzle&&(Ha=function(n,t){return Sizzle(n,t)[0]||null},Fa=function(n,t){return Sizzle.uniqueSort(Sizzle(n,t))},Oa=Sizzle.matchesSelector),ya.selection=function(){return Ia};var Ya=ya.selection.prototype=[];Ya.select=function(n){var t,e,r,i,u=[];n=v(n);for(var a=-1,o=this.length;++a=0&&(e=n.substring(0,t),n=n.substring(t+1)),Ra.hasOwnProperty(e)?{space:Ra[e],local:n}:n}},Ya.attr=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node();return n=ya.ns.qualify(n),n.local?e.getAttributeNS(n.space,n.local):e.getAttribute(n)}for(t in n)this.each(M(t,n[t]));return this}return this.each(M(n,t))},Ya.classed=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node(),r=(n=n.trim().split(/^|\s+/g)).length,i=-1;if(t=e.classList){for(;++ir){if("string"!=typeof n){2>r&&(t="");for(e in n)this.each(S(e,n[e],t));return this}if(2>r)return ba.getComputedStyle(this.node(),null).getPropertyValue(n);e=""}return this.each(S(n,t,e))},Ya.property=function(n,t){if(arguments.length<2){if("string"==typeof n)return this.node()[n];for(t in n)this.each(E(t,n[t]));return this}return this.each(E(n,t))},Ya.text=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.textContent=null==t?"":t}:null==n?function(){this.textContent=""}:function(){this.textContent=n}):this.node().textContent},Ya.html=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.innerHTML=null==t?"":t}:null==n?function(){this.innerHTML=""}:function(){this.innerHTML=n}):this.node().innerHTML},Ya.append=function(n){return n=k(n),this.select(function(){return this.appendChild(n.apply(this,arguments))})},Ya.insert=function(n,t){return n=k(n),t=v(t),this.select(function(){return this.insertBefore(n.apply(this,arguments),t.apply(this,arguments))})},Ya.remove=function(){return this.each(function(){var n=this.parentNode;n&&n.removeChild(this)})},Ya.data=function(n,t){function e(n,e){var r,u,a,o=n.length,f=e.length,h=Math.min(o,f),g=new Array(f),p=new Array(f),m=new Array(o);if(t){var d,v=new i,y=new i,M=[];for(r=-1;++rr;++r)p[r]=A(e[r]);for(;o>r;++r)m[r]=n[r]}p.update=g,p.parentNode=g.parentNode=m.parentNode=n.parentNode,c.push(p),l.push(g),s.push(m)}var r,u,a=-1,o=this.length;if(!arguments.length){for(n=new Array(o=(r=this[0]).length);++au;u++){i.push(t=[]),t.parentNode=(e=this[u]).parentNode;for(var o=0,c=e.length;c>o;o++)(r=e[o])&&n.call(r,r.__data__,o)&&t.push(r)}return d(i)},Ya.order=function(){for(var n=-1,t=this.length;++n=0;)(e=r[i])&&(u&&u!==e.nextSibling&&u.parentNode.insertBefore(e,u),u=e);return this},Ya.sort=function(n){n=q.apply(this,arguments);for(var t=-1,e=this.length;++tn;n++)for(var e=this[n],r=0,i=e.length;i>r;r++){var u=e[r];if(u)return u}return null},Ya.size=function(){var n=0;return this.each(function(){++n}),n};var Ua=[];ya.selection.enter=C,ya.selection.enter.prototype=Ua,Ua.append=Ya.append,Ua.empty=Ya.empty,Ua.node=Ya.node,Ua.call=Ya.call,Ua.size=Ya.size,Ua.select=function(n){for(var t,e,r,i,u,a=[],o=-1,c=this.length;++or){if("string"!=typeof n){2>r&&(t=!1);for(e in n)this.each(D(e,n[e],t));return this}if(2>r)return(r=this.node()["__on"+n])&&r._;e=!1}return this.each(D(n,t,e))};var Va=ya.map({mouseenter:"mouseover",mouseleave:"mouseout"});Va.forEach(function(n){"on"+n in Ma&&Va.remove(n)});var Xa=o(xa.style,"userSelect"),Za=0;ya.mouse=function(n){return F(n,p())};var Ba=/WebKit/.test(ba.navigator.userAgent)?-1:0;ya.touches=function(n,t){return arguments.length<2&&(t=p().touches),t?za(t).map(function(t){var e=F(n,t);return e.identifier=t.identifier,e}):[]},ya.behavior.drag=function(){function n(){this.on("mousedown.drag",a).on("touchstart.drag",o)}function t(){return ya.event.changedTouches[0].identifier}function e(n,t){return ya.touches(n).filter(function(n){return n.identifier===t})[0]}function r(n,t,e,r){return function(){function a(){if(!s)return o();var n=t(s,g),e=n[0]-m[0],r=n[1]-m[1];d|=e|r,m=n,f({type:"drag",x:n[0]+c[0],y:n[1]+c[1],dx:e,dy:r})}function o(){v.on(e+"."+p,null).on(r+"."+p,null),y(d&&ya.event.target===h),f({type:"dragend"})}var c,l=this,s=l.parentNode,f=i.of(l,arguments),h=ya.event.target,g=n(),p=null==g?"drag":"drag-"+g,m=t(s,g),d=0,v=ya.select(ba).on(e+"."+p,a).on(r+"."+p,o),y=H();u?(c=u.apply(l,arguments),c=[c.x-m[0],c.y-m[1]]):c=[0,0],f({type:"dragstart"})}}var i=m(n,"drag","dragstart","dragend"),u=null,a=r(s,ya.mouse,"mousemove","mouseup"),o=r(t,e,"touchmove","touchend");return n.origin=function(t){return arguments.length?(u=t,n):u},ya.rebind(n,i,"on")},ya.behavior.zoom=function(){function n(){this.on(w,o).on(Ja+".zoom",l).on(S,s).on("dblclick.zoom",f).on(k,c)}function t(n){return[(n[0]-x[0])/b,(n[1]-x[1])/b]}function e(n){return[n[0]*b+x[0],n[1]*b+x[1]]}function r(n){b=Math.max(_[0],Math.min(_[1],n))}function i(n,t){t=e(t),x[0]+=n[0]-t[0],x[1]+=n[1]-t[1]}function u(){v&&v.domain(d.range().map(function(n){return(n-x[0])/b}).map(d.invert)),M&&M.domain(y.range().map(function(n){return(n-x[1])/b}).map(y.invert))}function a(n){u(),n({type:"zoom",scale:b,translate:x})}function o(){function n(){c=1,i(ya.mouse(r),f),a(u)}function e(){l.on(S,ba===r?s:null).on(E,null),h(c&&ya.event.target===o)}var r=this,u=q.of(r,arguments),o=ya.event.target,c=0,l=ya.select(ba).on(S,n).on(E,e),f=t(ya.mouse(r)),h=H()}function c(){function n(){var n=ya.touches(h);return f=b,s={},n.forEach(function(n){s[n.identifier]=t(n)}),n}function e(){var t=Date.now(),e=n();if(1===e.length){if(500>t-p){var u=e[0],o=s[u.identifier];r(2*b),i(u,o),g(),a(m)}p=t}else if(e.length>1){var u=e[0],c=e[1],l=u[0]-c[0],f=u[1]-c[1];d=l*l+f*f}}function u(){var n=ya.touches(h),t=n[0],e=s[t.identifier];if(u=n[1]){var u,o=s[u.identifier],c=ya.event.scale;if(null==c){var l=(l=u[0]-t[0])*l+(l=u[1]-t[1])*l;c=d&&Math.sqrt(l/d)}t=[(t[0]+u[0])/2,(t[1]+u[1])/2],e=[(e[0]+o[0])/2,(e[1]+o[1])/2],r(c*f)}p=null,i(t,e),a(m)}function l(){ya.event.touches.length?n():(v.on(A,null).on(N,null),y.on(w,o).on(k,c),M())}var s,f,h=this,m=q.of(h,arguments),d=0,v=ya.select(ba).on(A,u).on(N,l),y=ya.select(h).on(w,null).on(k,e),M=H();e()}function l(){g(),h||(h=t(ya.mouse(this))),r(Math.pow(2,.002*$a())*b),i(ya.mouse(this),h),a(q.of(this,arguments))}function s(){h=null}function f(){var n=ya.mouse(this),e=t(n),u=Math.log(b)/Math.LN2;r(Math.pow(2,ya.event.shiftKey?Math.ceil(u)-1:Math.floor(u)+1)),i(n,e),a(q.of(this,arguments))}var h,p,d,v,y,M,x=[0,0],b=1,_=Wa,w="mousedown.zoom",S="mousemove.zoom",E="mouseup.zoom",k="touchstart.zoom",A="touchmove.zoom",N="touchend.zoom",q=m(n,"zoom");return n.translate=function(t){return arguments.length?(x=t.map(Number),u(),n):x},n.scale=function(t){return arguments.length?(b=+t,u(),n):b},n.scaleExtent=function(t){return arguments.length?(_=null==t?Wa:t.map(Number),n):_},n.x=function(t){return arguments.length?(v=t,d=t.copy(),x=[0,0],b=1,n):v},n.y=function(t){return arguments.length?(M=t,y=t.copy(),x=[0,0],b=1,n):M},ya.rebind(n,q,"on")};var $a,Wa=[0,1/0],Ja="onwheel"in Ma?($a=function(){return-ya.event.deltaY*(ya.event.deltaMode?120:1)},"wheel"):"onmousewheel"in Ma?($a=function(){return ya.event.wheelDelta},"mousewheel"):($a=function(){return-ya.event.detail},"MozMousePixelScroll");P.prototype.toString=function(){return this.rgb()+""},ya.hsl=function(n,t,e){return 1===arguments.length?n instanceof Y?O(n.h,n.s,n.l):lt(""+n,st,O):O(+n,+t,+e)};var Ga=Y.prototype=new P;Ga.brighter=function(n){return n=Math.pow(.7,arguments.length?n:1),O(this.h,this.s,this.l/n)},Ga.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),O(this.h,this.s,n*this.l)},Ga.rgb=function(){return R(this.h,this.s,this.l)};var Ka=Math.PI,Qa=1e-6,no=Qa*Qa,to=Ka/180,eo=180/Ka;ya.hcl=function(n,t,e){return 1===arguments.length?n instanceof W?$(n.h,n.c,n.l):n instanceof K?nt(n.l,n.a,n.b):nt((n=ft((n=ya.rgb(n)).r,n.g,n.b)).l,n.a,n.b):$(+n,+t,+e)};var ro=W.prototype=new P;ro.brighter=function(n){return $(this.h,this.c,Math.min(100,this.l+io*(arguments.length?n:1)))},ro.darker=function(n){return $(this.h,this.c,Math.max(0,this.l-io*(arguments.length?n:1)))},ro.rgb=function(){return J(this.h,this.c,this.l).rgb()},ya.lab=function(n,t,e){return 1===arguments.length?n instanceof K?G(n.l,n.a,n.b):n instanceof W?J(n.l,n.c,n.h):ft((n=ya.rgb(n)).r,n.g,n.b):G(+n,+t,+e)};var io=18,uo=.95047,ao=1,oo=1.08883,co=K.prototype=new P;co.brighter=function(n){return G(Math.min(100,this.l+io*(arguments.length?n:1)),this.a,this.b)},co.darker=function(n){return G(Math.max(0,this.l-io*(arguments.length?n:1)),this.a,this.b)},co.rgb=function(){return Q(this.l,this.a,this.b)},ya.rgb=function(n,t,e){return 1===arguments.length?n instanceof ot?at(n.r,n.g,n.b):lt(""+n,at,R):at(~~n,~~t,~~e)};var lo=ot.prototype=new P;lo.brighter=function(n){n=Math.pow(.7,arguments.length?n:1);var t=this.r,e=this.g,r=this.b,i=30;return t||e||r?(t&&i>t&&(t=i),e&&i>e&&(e=i),r&&i>r&&(r=i),at(Math.min(255,~~(t/n)),Math.min(255,~~(e/n)),Math.min(255,~~(r/n)))):at(i,i,i)},lo.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),at(~~(n*this.r),~~(n*this.g),~~(n*this.b))},lo.hsl=function(){return st(this.r,this.g,this.b)},lo.toString=function(){return"#"+ct(this.r)+ct(this.g)+ct(this.b)};var so=ya.map({aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074});so.forEach(function(n,t){so.set(n,it(t))}),ya.functor=pt,ya.xhr=dt(mt),ya.dsv=function(n,t){function e(n,e,u){arguments.length<3&&(u=e,e=null);var a=ya.xhr(n,t,u);return a.row=function(n){return arguments.length?a.response(null==(e=n)?r:i(n)):e},a.row(e)}function r(n){return e.parse(n.responseText)}function i(n){return function(t){return e.parse(t.responseText,n)}}function a(t){return t.map(o).join(n)}function o(n){return c.test(n)?'"'+n.replace(/\"/g,'""')+'"':n}var c=new RegExp('["'+n+"\n]"),l=n.charCodeAt(0);return e.parse=function(n,t){var r;return e.parseRows(n,function(n,e){if(r)return r(n,e-1);var i=new Function("d","return {"+n.map(function(n,t){return JSON.stringify(n)+": d["+t+"]"}).join(",")+"}");r=t?function(n,e){return t(i(n),e)}:i})},e.parseRows=function(n,t){function e(){if(s>=c)return a;if(i)return i=!1,u;var t=s;if(34===n.charCodeAt(t)){for(var e=t;e++s;){var r=n.charCodeAt(s++),o=1;if(10===r)i=!0;else if(13===r)i=!0,10===n.charCodeAt(s)&&(++s,++o);else if(r!==l)continue;return n.substring(t,s-o)}return n.substring(t)}for(var r,i,u={},a={},o=[],c=n.length,s=0,f=0;(r=e())!==a;){for(var h=[];r!==u&&r!==a;)h.push(r),r=e();(!t||(h=t(h,f++)))&&o.push(h)}return o},e.format=function(t){if(Array.isArray(t[0]))return e.formatRows(t);var r=new u,i=[];return t.forEach(function(n){for(var t in n)r.has(t)||i.push(r.add(t))}),[i.map(o).join(n)].concat(t.map(function(t){return i.map(function(n){return o(t[n])}).join(n)})).join("\n")},e.formatRows=function(n){return n.map(a).join("\n")},e},ya.csv=ya.dsv(",","text/csv"),ya.tsv=ya.dsv(" ","text/tab-separated-values");var fo,ho,go,po,mo,vo=ba[o(ba,"requestAnimationFrame")]||function(n){setTimeout(n,17)};ya.timer=function(n,t,e){var r=arguments.length;2>r&&(t=0),3>r&&(e=Date.now());var i=e+t,u={callback:n,time:i,next:null};ho?ho.next=u:fo=u,ho=u,go||(po=clearTimeout(po),go=1,vo(Mt))},ya.timer.flush=function(){bt(),_t()};var yo=".",Mo=",",xo=[3,3],bo="$",_o=["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"].map(wt);ya.formatPrefix=function(n,t){var e=0;return n&&(0>n&&(n*=-1),t&&(n=ya.round(n,St(n,t))),e=1+Math.floor(1e-12+Math.log(n)/Math.LN10),e=Math.max(-24,Math.min(24,3*Math.floor((0>=e?e+1:e-1)/3)))),_o[8+e/3]},ya.round=function(n,t){return t?Math.round(n*(t=Math.pow(10,t)))/t:Math.round(n)},ya.format=function(n){var t=wo.exec(n),e=t[1]||" ",r=t[2]||">",i=t[3]||"",u=t[4]||"",a=t[5],o=+t[6],c=t[7],l=t[8],s=t[9],f=1,h="",g=!1;switch(l&&(l=+l.substring(1)),(a||"0"===e&&"="===r)&&(a=e="0",r="=",c&&(o-=Math.floor((o-1)/4))),s){case"n":c=!0,s="g";break;case"%":f=100,h="%",s="f";break;case"p":f=100,h="%",s="r";break;case"b":case"o":case"x":case"X":"#"===u&&(u="0"+s.toLowerCase());case"c":case"d":g=!0,l=0;break;case"s":f=-1,s="r"}"#"===u?u="":"$"===u&&(u=bo),"r"!=s||l||(s="g"),null!=l&&("g"==s?l=Math.max(1,Math.min(21,l)):("e"==s||"f"==s)&&(l=Math.max(0,Math.min(20,l)))),s=So.get(s)||Et;var p=a&&c;return function(n){if(g&&n%1)return"";var t=0>n||0===n&&0>1/n?(n=-n,"-"):i;if(0>f){var m=ya.formatPrefix(n,l);n=m.scale(n),h=m.symbol}else n*=f;n=s(n,l);var d=n.lastIndexOf("."),v=0>d?n:n.substring(0,d),y=0>d?"":yo+n.substring(d+1);!a&&c&&(v=Eo(v));var M=u.length+v.length+y.length+(p?0:t.length),x=o>M?new Array(M=o-M+1).join(e):"";return p&&(v=Eo(x+v)),t+=u,n=v+y,("<"===r?t+n+x:">"===r?x+t+n:"^"===r?x.substring(0,M>>=1)+t+n+x.substring(M):t+(p?n:x+n))+h}};var wo=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i,So=ya.map({b:function(n){return n.toString(2)},c:function(n){return String.fromCharCode(n)},o:function(n){return n.toString(8)},x:function(n){return n.toString(16)},X:function(n){return n.toString(16).toUpperCase()},g:function(n,t){return n.toPrecision(t)},e:function(n,t){return n.toExponential(t)},f:function(n,t){return n.toFixed(t)},r:function(n,t){return(n=ya.round(n,St(n,t))).toFixed(Math.max(0,Math.min(20,St(n*(1+1e-15),t))))}}),Eo=mt;if(xo){var ko=xo.length;Eo=function(n){for(var t=n.length,e=[],r=0,i=xo[0];t>0&&i>0;)e.push(n.substring(t-=i,t+i)),i=xo[r=(r+1)%ko];return e.reverse().join(Mo)}}ya.geo={},kt.prototype={s:0,t:0,add:function(n){At(n,this.t,Ao),At(Ao.s,this.s,this),this.s?this.t+=Ao.t:this.s=Ao.t},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var Ao=new kt;ya.geo.stream=function(n,t){n&&No.hasOwnProperty(n.type)?No[n.type](n,t):Nt(n,t)};var No={Feature:function(n,t){Nt(n.geometry,t)},FeatureCollection:function(n,t){for(var e=n.features,r=-1,i=e.length;++rn?4*Ka+n:n,zo.lineStart=zo.lineEnd=zo.point=s}};ya.geo.bounds=function(){function n(n,t){M.push(x=[s=n,h=n]),f>t&&(f=t),t>g&&(g=t)}function t(t,e){var r=zt([t*to,e*to]);if(v){var i=jt(v,r),u=[i[1],-i[0],0],a=jt(u,i);Ft(a),a=Pt(a);var c=t-p,l=c>0?1:-1,m=a[0]*eo*l,d=Math.abs(c)>180;if(d^(m>l*p&&l*t>m)){var y=a[1]*eo;y>g&&(g=y)}else if(m=(m+360)%360-180,d^(m>l*p&&l*t>m)){var y=-a[1]*eo;f>y&&(f=y)}else f>e&&(f=e),e>g&&(g=e);d?p>t?o(s,t)>o(s,h)&&(h=t):o(t,h)>o(s,h)&&(s=t):h>=s?(s>t&&(s=t),t>h&&(h=t)):t>p?o(s,t)>o(s,h)&&(h=t):o(t,h)>o(s,h)&&(s=t)}else n(t,e);v=r,p=t}function e(){b.point=t}function r(){x[0]=s,x[1]=h,b.point=n,v=null}function i(n,e){if(v){var r=n-p;y+=Math.abs(r)>180?r+(r>0?360:-360):r}else m=n,d=e;zo.point(n,e),t(n,e)}function u(){zo.lineStart()}function a(){i(m,d),zo.lineEnd(),Math.abs(y)>Qa&&(s=-(h=180)),x[0]=s,x[1]=h,v=null}function o(n,t){return(t-=n)<0?t+360:t}function c(n,t){return n[0]-t[0]}function l(n,t){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:nCo?(s=-(h=180),f=-(g=90)):y>Qa?g=90:-Qa>y&&(f=-90),x[0]=s,x[1]=h}};return function(n){g=h=-(s=f=1/0),M=[],ya.geo.stream(n,b);var t=M.length;if(t){M.sort(c);for(var e,r=1,i=M[0],u=[i];t>r;++r)e=M[r],l(e[0],i)||l(e[1],i)?(o(i[0],e[1])>o(i[0],i[1])&&(i[1]=e[1]),o(e[0],i[1])>o(i[0],i[1])&&(i[0]=e[0])):u.push(i=e);for(var a,e,p=-1/0,t=u.length-1,r=0,i=u[t];t>=r;i=e,++r)e=u[r],(a=o(i[1],e[0]))>p&&(p=a,s=e[0],h=i[1])}return M=x=null,1/0===s||1/0===f?[[0/0,0/0],[0/0,0/0]]:[[s,f],[h,g]]}}(),ya.geo.centroid=function(n){Do=jo=Lo=Ho=Fo=Po=Oo=Yo=Ro=Uo=Io=0,ya.geo.stream(n,Vo);var t=Ro,e=Uo,r=Io,i=t*t+e*e+r*r;return no>i&&(t=Po,e=Oo,r=Yo,Qa>jo&&(t=Lo,e=Ho,r=Fo),i=t*t+e*e+r*r,no>i)?[0/0,0/0]:[Math.atan2(e,t)*eo,V(r/Math.sqrt(i))*eo]};var Do,jo,Lo,Ho,Fo,Po,Oo,Yo,Ro,Uo,Io,Vo={sphere:s,point:Yt,lineStart:Ut,lineEnd:It,polygonStart:function(){Vo.lineStart=Vt},polygonEnd:function(){Vo.lineStart=Ut}},Xo=$t(Xt,Qt,te,ee),Zo=[-Ka,0],Bo=1e9;(ya.geo.conicEqualArea=function(){return oe(ce)}).raw=ce,ya.geo.albers=function(){return ya.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},ya.geo.albersUsa=function(){function n(n){var u=n[0],a=n[1];return t=null,e(u,a),t||(r(u,a),t)||i(u,a),t}var t,e,r,i,u=ya.geo.albers(),a=ya.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]),o=ya.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),c={point:function(n,e){t=[n,e]}};return n.invert=function(n){var t=u.scale(),e=u.translate(),r=(n[0]-e[0])/t,i=(n[1]-e[1])/t;return(i>=.12&&.234>i&&r>=-.425&&-.214>r?a:i>=.166&&.234>i&&r>=-.214&&-.115>r?o:u).invert(n)},n.stream=function(n){var t=u.stream(n),e=a.stream(n),r=o.stream(n);return{point:function(n,i){t.point(n,i),e.point(n,i),r.point(n,i)},sphere:function(){t.sphere(),e.sphere(),r.sphere()},lineStart:function(){t.lineStart(),e.lineStart(),r.lineStart()},lineEnd:function(){t.lineEnd(),e.lineEnd(),r.lineEnd()},polygonStart:function(){t.polygonStart(),e.polygonStart(),r.polygonStart()},polygonEnd:function(){t.polygonEnd(),e.polygonEnd(),r.polygonEnd()}}},n.precision=function(t){return arguments.length?(u.precision(t),a.precision(t),o.precision(t),n):u.precision()},n.scale=function(t){return arguments.length?(u.scale(t),a.scale(.35*t),o.scale(t),n.translate(u.translate())):u.scale()},n.translate=function(t){if(!arguments.length)return u.translate();var l=u.scale(),s=+t[0],f=+t[1];return e=u.translate(t).clipExtent([[s-.455*l,f-.238*l],[s+.455*l,f+.238*l]]).stream(c).point,r=a.translate([s-.307*l,f+.201*l]).clipExtent([[s-.425*l+Qa,f+.12*l+Qa],[s-.214*l-Qa,f+.234*l-Qa]]).stream(c).point,i=o.translate([s-.205*l,f+.212*l]).clipExtent([[s-.214*l+Qa,f+.166*l+Qa],[s-.115*l-Qa,f+.234*l-Qa]]).stream(c).point,n},n.scale(1070)};var $o,Wo,Jo,Go,Ko,Qo,nc={point:s,lineStart:s,lineEnd:s,polygonStart:function(){Wo=0,nc.lineStart=le},polygonEnd:function(){nc.lineStart=nc.lineEnd=nc.point=s,$o+=Math.abs(Wo/2)}},tc={point:se,lineStart:s,lineEnd:s,polygonStart:s,polygonEnd:s},ec={point:ge,lineStart:pe,lineEnd:me,polygonStart:function(){ec.lineStart=de},polygonEnd:function(){ec.point=ge,ec.lineStart=pe,ec.lineEnd=me}};ya.geo.path=function(){function n(n){return n&&("function"==typeof o&&u.pointRadius(+o.apply(this,arguments)),a&&a.valid||(a=i(u)),ya.geo.stream(n,a)),u.result()}function t(){return a=null,n}var e,r,i,u,a,o=4.5;return n.area=function(n){return $o=0,ya.geo.stream(n,i(nc)),$o},n.centroid=function(n){return Lo=Ho=Fo=Po=Oo=Yo=Ro=Uo=Io=0,ya.geo.stream(n,i(ec)),Io?[Ro/Io,Uo/Io]:Yo?[Po/Yo,Oo/Yo]:Fo?[Lo/Fo,Ho/Fo]:[0/0,0/0]},n.bounds=function(n){return Ko=Qo=-(Jo=Go=1/0),ya.geo.stream(n,i(tc)),[[Jo,Go],[Ko,Qo]]},n.projection=function(n){return arguments.length?(i=(e=n)?n.stream||Me(n):mt,t()):e},n.context=function(n){return arguments.length?(u=null==(r=n)?new fe:new ve(n),"function"!=typeof o&&u.pointRadius(o),t()):r},n.pointRadius=function(t){return arguments.length?(o="function"==typeof t?t:(u.pointRadius(+t),+t),n):o},n.projection(ya.geo.albersUsa()).context(null)},ya.geo.projection=xe,ya.geo.projectionMutator=be,(ya.geo.equirectangular=function(){return xe(we)}).raw=we.invert=we,ya.geo.rotation=function(n){function t(t){return t=n(t[0]*to,t[1]*to),t[0]*=eo,t[1]*=eo,t}return n=Se(n[0]%360*to,n[1]*to,n.length>2?n[2]*to:0),t.invert=function(t){return t=n.invert(t[0]*to,t[1]*to),t[0]*=eo,t[1]*=eo,t},t},ya.geo.circle=function(){function n(){var n="function"==typeof r?r.apply(this,arguments):r,t=Se(-n[0]*to,-n[1]*to,0).invert,i=[];return e(null,null,1,{point:function(n,e){i.push(n=t(n,e)),n[0]*=eo,n[1]*=eo}}),{type:"Polygon",coordinates:[i]}}var t,e,r=[0,0],i=6;return n.origin=function(t){return arguments.length?(r=t,n):r},n.angle=function(r){return arguments.length?(e=Ne((t=+r)*to,i*to),n):t},n.precision=function(r){return arguments.length?(e=Ne(t*to,(i=+r)*to),n):i},n.angle(90)},ya.geo.distance=function(n,t){var e,r=(t[0]-n[0])*to,i=n[1]*to,u=t[1]*to,a=Math.sin(r),o=Math.cos(r),c=Math.sin(i),l=Math.cos(i),s=Math.sin(u),f=Math.cos(u);return Math.atan2(Math.sqrt((e=f*a)*e+(e=l*s-c*f*o)*e),c*s+l*f*o)},ya.geo.graticule=function(){function n(){return{type:"MultiLineString",coordinates:t()}}function t(){return ya.range(Math.ceil(u/d)*d,i,d).map(h).concat(ya.range(Math.ceil(l/v)*v,c,v).map(g)).concat(ya.range(Math.ceil(r/p)*p,e,p).filter(function(n){return Math.abs(n%d)>Qa}).map(s)).concat(ya.range(Math.ceil(o/m)*m,a,m).filter(function(n){return Math.abs(n%v)>Qa}).map(f))}var e,r,i,u,a,o,c,l,s,f,h,g,p=10,m=p,d=90,v=360,y=2.5;return n.lines=function(){return t().map(function(n){return{type:"LineString",coordinates:n}})},n.outline=function(){return{type:"Polygon",coordinates:[h(u).concat(g(c).slice(1),h(i).reverse().slice(1),g(l).reverse().slice(1))]}},n.extent=function(t){return arguments.length?n.majorExtent(t).minorExtent(t):n.minorExtent()},n.majorExtent=function(t){return arguments.length?(u=+t[0][0],i=+t[1][0],l=+t[0][1],c=+t[1][1],u>i&&(t=u,u=i,i=t),l>c&&(t=l,l=c,c=t),n.precision(y)):[[u,l],[i,c]]},n.minorExtent=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],o=+t[0][1],a=+t[1][1],r>e&&(t=r,r=e,e=t),o>a&&(t=o,o=a,a=t),n.precision(y)):[[r,o],[e,a]]},n.step=function(t){return arguments.length?n.majorStep(t).minorStep(t):n.minorStep()},n.majorStep=function(t){return arguments.length?(d=+t[0],v=+t[1],n):[d,v]},n.minorStep=function(t){return arguments.length?(p=+t[0],m=+t[1],n):[p,m]},n.precision=function(t){return arguments.length?(y=+t,s=Te(o,a,90),f=Ce(r,e,y),h=Te(l,c,90),g=Ce(u,i,y),n):y},n.majorExtent([[-180,-90+Qa],[180,90-Qa]]).minorExtent([[-180,-80-Qa],[180,80+Qa]])},ya.geo.greatArc=function(){function n(){return{type:"LineString",coordinates:[t||r.apply(this,arguments),e||i.apply(this,arguments)]}}var t,e,r=ze,i=De;return n.distance=function(){return ya.geo.distance(t||r.apply(this,arguments),e||i.apply(this,arguments))},n.source=function(e){return arguments.length?(r=e,t="function"==typeof e?null:e,n):r},n.target=function(t){return arguments.length?(i=t,e="function"==typeof t?null:t,n):i},n.precision=function(){return arguments.length?n:0},n},ya.geo.interpolate=function(n,t){return je(n[0]*to,n[1]*to,t[0]*to,t[1]*to)},ya.geo.length=function(n){return rc=0,ya.geo.stream(n,ic),rc};var rc,ic={sphere:s,point:s,lineStart:Le,lineEnd:s,polygonStart:s,polygonEnd:s},uc=He(function(n){return Math.sqrt(2/(1+n))},function(n){return 2*Math.asin(n/2)});(ya.geo.azimuthalEqualArea=function(){return xe(uc)}).raw=uc;var ac=He(function(n){var t=Math.acos(n);return t&&t/Math.sin(t)},mt);(ya.geo.azimuthalEquidistant=function(){return xe(ac)}).raw=ac,(ya.geo.conicConformal=function(){return oe(Fe)}).raw=Fe,(ya.geo.conicEquidistant=function(){return oe(Pe)}).raw=Pe;var oc=He(function(n){return 1/n},Math.atan);(ya.geo.gnomonic=function(){return xe(oc)}).raw=oc,Oe.invert=function(n,t){return[n,2*Math.atan(Math.exp(t))-Ka/2]},(ya.geo.mercator=function(){return Ye(Oe)}).raw=Oe;var cc=He(function(){return 1},Math.asin);(ya.geo.orthographic=function(){return xe(cc)}).raw=cc;var lc=He(function(n){return 1/(1+n)},function(n){return 2*Math.atan(n)});(ya.geo.stereographic=function(){return xe(lc)}).raw=lc,Re.invert=function(n,t){return[Math.atan2(X(n),Math.cos(t)),V(Math.sin(t)/Z(n))]},(ya.geo.transverseMercator=function(){return Ye(Re)}).raw=Re,ya.geom={},ya.svg={},ya.svg.line=function(){return Ue(mt)};var sc=ya.map({linear:Xe,"linear-closed":Ze,step:Be,"step-before":$e,"step-after":We,basis:tr,"basis-open":er,"basis-closed":rr,bundle:ir,cardinal:Ke,"cardinal-open":Je,"cardinal-closed":Ge,monotone:sr});
+sc.forEach(function(n,t){t.key=n,t.closed=/-closed$/.test(n)});var fc=[0,2/3,1/3,0],hc=[0,1/3,2/3,0],gc=[0,1/6,2/3,1/6];ya.geom.hull=function(n){function t(n){if(n.length<3)return[];var t,i,u,a,o,c,l,s,f,h,g,p,m=pt(e),d=pt(r),v=n.length,y=v-1,M=[],x=[],b=0;if(m===Ie&&r===Ve)t=n;else for(u=0,t=[];v>u;++u)t.push([+m.call(this,i=n[u],u),+d.call(this,i,u)]);for(u=1;v>u;++u)(t[u][1]u;++u)u!==b&&(c=t[u][1]-t[b][1],o=t[u][0]-t[b][0],M.push({angle:Math.atan2(c,o),index:u}));for(M.sort(function(n,t){return n.angle-t.angle}),g=M[0].angle,h=M[0].index,f=0,u=1;y>u;++u){if(a=M[u].index,g==M[u].angle){if(o=t[h][0]-t[b][0],c=t[h][1]-t[b][1],l=t[a][0]-t[b][0],s=t[a][1]-t[b][1],o*o+c*c>=l*l+s*s){M[u].index=-1;continue}M[f].index=-1}g=M[u].angle,f=u,h=a}for(x.push(b),u=0,a=0;2>u;++a)M[a].index>-1&&(x.push(M[a].index),u++);for(p=x.length;y>a;++a)if(!(M[a].index<0)){for(;!fr(x[p-2],x[p-1],M[a].index,t);)--p;x[p++]=M[a].index}var _=[];for(u=p-1;u>=0;--u)_.push(n[x[u]]);return _}var e=Ie,r=Ve;return arguments.length?t(n):(t.x=function(n){return arguments.length?(e=n,t):e},t.y=function(n){return arguments.length?(r=n,t):r},t)},ya.geom.polygon=function(n){return La(n,pc),n};var pc=ya.geom.polygon.prototype=[];pc.area=function(){for(var n,t=-1,e=this.length,r=this[e-1],i=0;++to;o++)e.push([i,t[o],t[o+1]])}),e},ya.geom.voronoi=function(n){function t(n){var t,u,a,o=n.map(function(){return[]}),c=pt(e),l=pt(r),s=n.length,f=1e6;if(c===Ie&&l===Ve)t=n;else for(t=new Array(s),a=0;s>a;++a)t[a]=[+c.call(this,u=n[a],a),+l.call(this,u,a)];if(mr(t,function(n){var t,e,r,i,u,a;1===n.a&&n.b>=0?(t=n.ep.r,e=n.ep.l):(t=n.ep.l,e=n.ep.r),1===n.a?(u=t?t.y:-f,r=n.c-n.b*u,a=e?e.y:f,i=n.c-n.b*a):(r=t?t.x:-f,u=n.c-n.a*r,i=e?e.x:f,a=n.c-n.a*i);var c=[r,u],l=[i,a];o[n.region.l.index].push(c,l),o[n.region.r.index].push(c,l)}),o=o.map(function(n,e){var r=t[e][0],i=t[e][1],u=n.map(function(n){return Math.atan2(n[0]-r,n[1]-i)}),a=ya.range(n.length).sort(function(n,t){return u[n]-u[t]});return a.filter(function(n,t){return!t||u[n]-u[a[t-1]]>Qa}).map(function(t){return n[t]})}),o.forEach(function(n,e){var r=n.length;if(!r)return n.push([-f,-f],[-f,f],[f,f],[f,-f]);if(!(r>2)){var i=t[e],u=n[0],a=n[1],o=i[0],c=i[1],l=u[0],s=u[1],h=a[0],g=a[1],p=Math.abs(h-l),m=g-s;if(Math.abs(m)c?-f:f;n.push([-f,d],[f,d])}else if(Qa>p){var v=l>o?-f:f;n.push([v,-f],[v,f])}else{var d=(l-o)*(g-s)>(h-l)*(s-c)?f:-f,y=Math.abs(m)-p;Math.abs(y)m?d:-d,d]):(y>0&&(d*=-1),n.push([-f,d],[f,d]))}}}),i)for(a=0;s>a;++a)i.clip(o[a]);for(a=0;s>a;++a)o[a].point=n[a];return o}var e=Ie,r=Ve,i=null;return arguments.length?t(n):(t.x=function(n){return arguments.length?(e=n,t):e},t.y=function(n){return arguments.length?(r=n,t):r},t.clipExtent=function(n){if(!arguments.length)return i&&[i[0],i[2]];if(null==n)i=null;else{var e=+n[0][0],r=+n[0][1],u=+n[1][0],a=+n[1][1];i=ya.geom.polygon([[e,r],[e,a],[u,a],[u,r]])}return t},t.size=function(n){return arguments.length?t.clipExtent(n&&[[0,0],n]):i&&i[2]},t.links=function(n){var t,i,u,a=n.map(function(){return[]}),o=[],c=pt(e),l=pt(r),s=n.length;if(c===Ie&&l===Ve)t=n;else for(t=new Array(s),u=0;s>u;++u)t[u]=[+c.call(this,i=n[u],u),+l.call(this,i,u)];return mr(t,function(t){var e=t.region.l.index,r=t.region.r.index;a[e][r]||(a[e][r]=a[r][e]=!0,o.push({source:n[e],target:n[r]}))}),o},t.triangles=function(n){if(e===Ie&&r===Ve)return ya.geom.delaunay(n);for(var t,i=new Array(c),u=pt(e),a=pt(r),o=-1,c=n.length;++o=l,h=r>=s,g=(h<<1)+f;n.leaf=!1,n=n.nodes[g]||(n.nodes[g]=yr()),f?i=l:o=l,h?a=s:c=s,u(n,t,e,r,i,a,o,c)}var s,f,h,g,p,m,d,v,y,M=pt(o),x=pt(c);if(null!=t)m=t,d=e,v=r,y=i;else if(v=y=-(m=d=1/0),f=[],h=[],p=n.length,a)for(g=0;p>g;++g)s=n[g],s.xv&&(v=s.x),s.y>y&&(y=s.y),f.push(s.x),h.push(s.y);else for(g=0;p>g;++g){var b=+M(s=n[g],g),_=+x(s,g);m>b&&(m=b),d>_&&(d=_),b>v&&(v=b),_>y&&(y=_),f.push(b),h.push(_)}var w=v-m,S=y-d;w>S?y=d+w:v=m+S;var E=yr();if(E.add=function(n){u(E,n,+M(n,++g),+x(n,g),m,d,v,y)},E.visit=function(n){Mr(n,E,m,d,v,y)},g=-1,null==t){for(;++g=0?n.substring(0,t):n,r=t>=0?n.substring(t+1):"in";return e=yc.get(e)||vc,r=Mc.get(r)||mt,kr(r(e.apply(null,Array.prototype.slice.call(arguments,1))))},ya.interpolateHcl=Or,ya.interpolateHsl=Yr,ya.interpolateLab=Rr,ya.interpolateRound=Ur,ya.transform=function(n){var t=Ma.createElementNS(ya.ns.prefix.svg,"g");return(ya.transform=function(n){if(null!=n){t.setAttribute("transform",n);var e=t.transform.baseVal.consolidate()}return new Ir(e?e.matrix:xc)})(n)},Ir.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var xc={a:1,b:0,c:0,d:1,e:0,f:0};ya.interpolateTransform=Br,ya.layout={},ya.layout.bundle=function(){return function(n){for(var t=[],e=-1,r=n.length;++e(i-e)*o){var c=t.charge*o*o;return n.px-=u*c,n.py-=a*c,!0}if(t.point&&isFinite(o)){var c=t.pointCharge*o*o;n.px-=u*c,n.py-=a*c}}return!t.charge}}function t(n){n.px=ya.event.x,n.py=ya.event.y,o.resume()}var e,r,i,u,a,o={},c=ya.dispatch("start","tick","end"),l=[1,1],s=.9,f=bc,h=_c,g=-30,p=.1,m=.8,d=[],v=[];return o.tick=function(){if((r*=.99)<.005)return c.end({type:"end",alpha:r=0}),!0;var t,e,o,f,h,m,y,M,x,b=d.length,_=v.length;for(e=0;_>e;++e)o=v[e],f=o.source,h=o.target,M=h.x-f.x,x=h.y-f.y,(m=M*M+x*x)&&(m=r*u[e]*((m=Math.sqrt(m))-i[e])/m,M*=m,x*=m,h.x-=M*(y=f.weight/(h.weight+f.weight)),h.y-=x*y,f.x+=M*(y=1-y),f.y+=x*y);if((y=r*p)&&(M=l[0]/2,x=l[1]/2,e=-1,y))for(;++e0?n:0:n>0&&(c.start({type:"start",alpha:r=n}),ya.timer(o.tick)),o):r},o.start=function(){function n(n,r){for(var i,u=t(e),a=-1,o=u.length;++ar;++r)c[r]=[];for(r=0;m>r;++r){var n=v[r];c[n.source.index].push(n.target),c[n.target.index].push(n.source)}}return c[e]}var e,r,c,s,p=d.length,m=v.length,y=l[0],M=l[1];for(e=0;p>e;++e)(s=d[e]).index=e,s.weight=0;for(e=0;m>e;++e)s=v[e],"number"==typeof s.source&&(s.source=d[s.source]),"number"==typeof s.target&&(s.target=d[s.target]),++s.source.weight,++s.target.weight;for(e=0;p>e;++e)s=d[e],isNaN(s.x)&&(s.x=n("x",y)),isNaN(s.y)&&(s.y=n("y",M)),isNaN(s.px)&&(s.px=s.x),isNaN(s.py)&&(s.py=s.y);if(i=[],"function"==typeof f)for(e=0;m>e;++e)i[e]=+f.call(this,v[e],e);else for(e=0;m>e;++e)i[e]=f;if(u=[],"function"==typeof h)for(e=0;m>e;++e)u[e]=+h.call(this,v[e],e);else for(e=0;m>e;++e)u[e]=h;if(a=[],"function"==typeof g)for(e=0;p>e;++e)a[e]=+g.call(this,d[e],e);else for(e=0;p>e;++e)a[e]=g;return o.resume()},o.resume=function(){return o.alpha(.1)},o.stop=function(){return o.alpha(0)},o.drag=function(){return e||(e=ya.behavior.drag().origin(mt).on("dragstart.force",Qr).on("drag.force",t).on("dragend.force",ni)),arguments.length?(this.on("mouseover.force",ti).on("mouseout.force",ei).call(e),void 0):e},ya.rebind(o,c,"on")};var bc=20,_c=1;ya.layout.hierarchy=function(){function n(t,a,o){var c=i.call(e,t,a);if(t.depth=a,o.push(t),c&&(l=c.length)){for(var l,s,f=-1,h=t.children=[],g=0,p=a+1;++fg;++g)for(i.call(n,l[0][g],p=m[g],s[0][g][1]),h=1;d>h;++h)i.call(n,l[h][g],p+=s[h-1][g][1],s[h][g][1]);return o}var t=mt,e=hi,r=gi,i=fi,u=li,a=si;return n.values=function(e){return arguments.length?(t=e,n):t},n.order=function(t){return arguments.length?(e="function"==typeof t?t:Sc.get(t)||hi,n):e},n.offset=function(t){return arguments.length?(r="function"==typeof t?t:Ec.get(t)||gi,n):r},n.x=function(t){return arguments.length?(u=t,n):u},n.y=function(t){return arguments.length?(a=t,n):a},n.out=function(t){return arguments.length?(i=t,n):i},n};var Sc=ya.map({"inside-out":function(n){var t,e,r=n.length,i=n.map(pi),u=n.map(mi),a=ya.range(r).sort(function(n,t){return i[n]-i[t]}),o=0,c=0,l=[],s=[];for(t=0;r>t;++t)e=a[t],c>o?(o+=u[e],l.push(e)):(c+=u[e],s.push(e));return s.reverse().concat(l)},reverse:function(n){return ya.range(n.length).reverse()},"default":hi}),Ec=ya.map({silhouette:function(n){var t,e,r,i=n.length,u=n[0].length,a=[],o=0,c=[];for(e=0;u>e;++e){for(t=0,r=0;i>t;t++)r+=n[t][e][1];r>o&&(o=r),a.push(r)}for(e=0;u>e;++e)c[e]=(o-a[e])/2;return c},wiggle:function(n){var t,e,r,i,u,a,o,c,l,s=n.length,f=n[0],h=f.length,g=[];for(g[0]=c=l=0,e=1;h>e;++e){for(t=0,i=0;s>t;++t)i+=n[t][e][1];for(t=0,u=0,o=f[e][0]-f[e-1][0];s>t;++t){for(r=0,a=(n[t][e][1]-n[t][e-1][1])/(2*o);t>r;++r)a+=(n[r][e][1]-n[r][e-1][1])/o;u+=a*n[t][e][1]}g[e]=c-=i?u/i*o:0,l>c&&(l=c)}for(e=0;h>e;++e)g[e]-=l;return g},expand:function(n){var t,e,r,i=n.length,u=n[0].length,a=1/i,o=[];for(e=0;u>e;++e){for(t=0,r=0;i>t;t++)r+=n[t][e][1];if(r)for(t=0;i>t;t++)n[t][e][1]/=r;else for(t=0;i>t;t++)n[t][e][1]=a}for(e=0;u>e;++e)o[e]=0;return o},zero:gi});ya.layout.histogram=function(){function n(n,u){for(var a,o,c=[],l=n.map(e,this),s=r.call(this,l,u),f=i.call(this,s,l,u),u=-1,h=l.length,g=f.length-1,p=t?1:1/h;++u0)for(u=-1;++u=s[0]&&o<=s[1]&&(a=c[ya.bisect(f,o,1,g)-1],a.y+=p,a.push(n[u]));return c}var t=!0,e=Number,r=Mi,i=vi;return n.value=function(t){return arguments.length?(e=t,n):e},n.range=function(t){return arguments.length?(r=pt(t),n):r},n.bins=function(t){return arguments.length?(i="number"==typeof t?function(n){return yi(n,t)}:pt(t),n):i},n.frequency=function(e){return arguments.length?(t=!!e,n):t},n},ya.layout.tree=function(){function n(n,u){function a(n,t){var r=n.children,i=n._tree;if(r&&(u=r.length)){for(var u,o,l,s=r[0],f=s,h=-1;++h0&&(qi(Ti(o,n,r),n,i),l+=i,s+=i),f+=o._tree.mod,l+=u._tree.mod,h+=c._tree.mod,s+=a._tree.mod;o&&!_i(a)&&(a._tree.thread=o,a._tree.mod+=f-s),u&&!bi(c)&&(c._tree.thread=u,c._tree.mod+=l-h,r=n)}return r}var l=t.call(this,n,u),s=l[0];Ai(s,function(n,t){n._tree={ancestor:n,prelim:0,mod:0,change:0,shift:0,number:t?t._tree.number+1:0}}),a(s),o(s,-s._tree.prelim);var f=wi(s,Ei),h=wi(s,Si),g=wi(s,ki),p=f.x-e(f,h)/2,m=h.x+e(h,f)/2,d=g.depth||1;return Ai(s,i?function(n){n.x*=r[0],n.y=n.depth*r[1],delete n._tree}:function(n){n.x=(n.x-p)/(m-p)*r[0],n.y=n.depth/d*r[1],delete n._tree}),l}var t=ya.layout.hierarchy().sort(null).value(null),e=xi,r=[1,1],i=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(i=null==(r=t),n):i?null:r},n.nodeSize=function(t){return arguments.length?(i=null!=(r=t),n):i?r:null},ii(n,t)},ya.layout.pack=function(){function n(n,u){var a=e.call(this,n,u),o=a[0],c=i[0],l=i[1],s=null==t?Math.sqrt:"function"==typeof t?t:function(){return t};if(o.x=o.y=0,Ai(o,function(n){n.r=+s(n.value)}),Ai(o,Li),r){var f=r*(t?1:Math.max(2*o.r/c,2*o.r/l))/2;Ai(o,function(n){n.r+=f}),Ai(o,Li),Ai(o,function(n){n.r-=f})}return Pi(o,c/2,l/2,t?1:1/Math.max(2*o.r/c,2*o.r/l)),a}var t,e=ya.layout.hierarchy().sort(Ci),r=0,i=[1,1];return n.size=function(t){return arguments.length?(i=t,n):i},n.radius=function(e){return arguments.length?(t=null==e||"function"==typeof e?e:+e,n):t},n.padding=function(t){return arguments.length?(r=+t,n):r},ii(n,e)},ya.layout.cluster=function(){function n(n,u){var a,o=t.call(this,n,u),c=o[0],l=0;Ai(c,function(n){var t=n.children;t&&t.length?(n.x=Ri(t),n.y=Yi(t)):(n.x=a?l+=e(n,a):0,n.y=0,a=n)});var s=Ui(c),f=Ii(c),h=s.x-e(s,f)/2,g=f.x+e(f,s)/2;return Ai(c,i?function(n){n.x=(n.x-c.x)*r[0],n.y=(c.y-n.y)*r[1]}:function(n){n.x=(n.x-h)/(g-h)*r[0],n.y=(1-(c.y?n.y/c.y:1))*r[1]}),o}var t=ya.layout.hierarchy().sort(null).value(null),e=xi,r=[1,1],i=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(i=null==(r=t),n):i?null:r},n.nodeSize=function(t){return arguments.length?(i=null!=(r=t),n):i?r:null},ii(n,t)},ya.layout.treemap=function(){function n(n,t){for(var e,r,i=-1,u=n.length;++it?0:t),e.area=isNaN(r)||0>=r?0:r}function t(e){var u=e.children;if(u&&u.length){var a,o,c,l=f(e),s=[],h=u.slice(),p=1/0,m="slice"===g?l.dx:"dice"===g?l.dy:"slice-dice"===g?1&e.depth?l.dy:l.dx:Math.min(l.dx,l.dy);for(n(h,l.dx*l.dy/e.value),s.area=0;(c=h.length)>0;)s.push(a=h[c-1]),s.area+=a.area,"squarify"!==g||(o=r(s,m))<=p?(h.pop(),p=o):(s.area-=s.pop().area,i(s,m,l,!1),m=Math.min(l.dx,l.dy),s.length=s.area=0,p=1/0);s.length&&(i(s,m,l,!0),s.length=s.area=0),u.forEach(t)}}function e(t){var r=t.children;if(r&&r.length){var u,a=f(t),o=r.slice(),c=[];for(n(o,a.dx*a.dy/t.value),c.area=0;u=o.pop();)c.push(u),c.area+=u.area,null!=u.z&&(i(c,u.z?a.dx:a.dy,a,!o.length),c.length=c.area=0);r.forEach(e)}}function r(n,t){for(var e,r=n.area,i=0,u=1/0,a=-1,o=n.length;++ae&&(u=e),e>i&&(i=e));return r*=r,t*=t,r?Math.max(t*i*p/r,r/(t*u*p)):1/0}function i(n,t,e,r){var i,u=-1,a=n.length,o=e.x,l=e.y,s=t?c(n.area/t):0;if(t==e.dx){for((r||s>e.dy)&&(s=e.dy);++ue.dx)&&(s=e.dx);++u e&&(t=1),1>e&&(n=0),function(){var e,r,i;do e=2*Math.random()-1,r=2*Math.random()-1,i=e*e+r*r;while(!i||i>1);return n+t*e*Math.sqrt(-2*Math.log(i)/i)}},logNormal:function(){var n=ya.random.normal.apply(ya,arguments);return function(){return Math.exp(n())}},irwinHall:function(n){return function(){for(var t=0,e=0;n>e;e++)t+=Math.random();return t/n}}},ya.scale={};var kc={floor:mt,ceil:mt};ya.scale.linear=function(){return Ki([0,1],[0,1],Sr,!1)},ya.scale.log=function(){return uu(ya.scale.linear().domain([0,1]),10,!0,[1,10])};var Ac=ya.format(".0e"),Nc={floor:function(n){return-Math.ceil(-n)},ceil:function(n){return-Math.floor(-n)}};ya.scale.pow=function(){return au(ya.scale.linear(),1,[0,1])},ya.scale.sqrt=function(){return ya.scale.pow().exponent(.5)},ya.scale.ordinal=function(){return cu([],{t:"range",a:[[]]})},ya.scale.category10=function(){return ya.scale.ordinal().range(qc)},ya.scale.category20=function(){return ya.scale.ordinal().range(Tc)},ya.scale.category20b=function(){return ya.scale.ordinal().range(Cc)},ya.scale.category20c=function(){return ya.scale.ordinal().range(zc)};var qc=[2062260,16744206,2924588,14034728,9725885,9197131,14907330,8355711,12369186,1556175].map(ut),Tc=[2062260,11454440,16744206,16759672,2924588,10018698,14034728,16750742,9725885,12955861,9197131,12885140,14907330,16234194,8355711,13092807,12369186,14408589,1556175,10410725].map(ut),Cc=[3750777,5395619,7040719,10264286,6519097,9216594,11915115,13556636,9202993,12426809,15186514,15190932,8666169,11356490,14049643,15177372,8077683,10834324,13528509,14589654].map(ut),zc=[3244733,7057110,10406625,13032431,15095053,16616764,16625259,16634018,3253076,7652470,10607003,13101504,7695281,10394312,12369372,14342891,6513507,9868950,12434877,14277081].map(ut);ya.scale.quantile=function(){return lu([],[])},ya.scale.quantize=function(){return su(0,1,[0,1])},ya.scale.threshold=function(){return fu([.5],[0,1])},ya.scale.identity=function(){return hu([0,1])},ya.svg.arc=function(){function n(){var n=t.apply(this,arguments),u=e.apply(this,arguments),a=r.apply(this,arguments)+Dc,o=i.apply(this,arguments)+Dc,c=(a>o&&(c=a,a=o,o=c),o-a),l=Ka>c?"0":"1",s=Math.cos(a),f=Math.sin(a),h=Math.cos(o),g=Math.sin(o);return c>=jc?n?"M0,"+u+"A"+u+","+u+" 0 1,1 0,"+-u+"A"+u+","+u+" 0 1,1 0,"+u+"M0,"+n+"A"+n+","+n+" 0 1,0 0,"+-n+"A"+n+","+n+" 0 1,0 0,"+n+"Z":"M0,"+u+"A"+u+","+u+" 0 1,1 0,"+-u+"A"+u+","+u+" 0 1,1 0,"+u+"Z":n?"M"+u*s+","+u*f+"A"+u+","+u+" 0 "+l+",1 "+u*h+","+u*g+"L"+n*h+","+n*g+"A"+n+","+n+" 0 "+l+",0 "+n*s+","+n*f+"Z":"M"+u*s+","+u*f+"A"+u+","+u+" 0 "+l+",1 "+u*h+","+u*g+"L0,0"+"Z"}var t=gu,e=pu,r=mu,i=du;return n.innerRadius=function(e){return arguments.length?(t=pt(e),n):t},n.outerRadius=function(t){return arguments.length?(e=pt(t),n):e},n.startAngle=function(t){return arguments.length?(r=pt(t),n):r},n.endAngle=function(t){return arguments.length?(i=pt(t),n):i},n.centroid=function(){var n=(t.apply(this,arguments)+e.apply(this,arguments))/2,u=(r.apply(this,arguments)+i.apply(this,arguments))/2+Dc;return[Math.cos(u)*n,Math.sin(u)*n]},n};var Dc=-Ka/2,jc=2*Ka-1e-6;ya.svg.line.radial=function(){var n=Ue(vu);return n.radius=n.x,delete n.x,n.angle=n.y,delete n.y,n},$e.reverse=We,We.reverse=$e,ya.svg.area=function(){return yu(mt)},ya.svg.area.radial=function(){var n=yu(vu);return n.radius=n.x,delete n.x,n.innerRadius=n.x0,delete n.x0,n.outerRadius=n.x1,delete n.x1,n.angle=n.y,delete n.y,n.startAngle=n.y0,delete n.y0,n.endAngle=n.y1,delete n.y1,n},ya.svg.chord=function(){function n(n,o){var c=t(this,u,n,o),l=t(this,a,n,o);return"M"+c.p0+r(c.r,c.p1,c.a1-c.a0)+(e(c,l)?i(c.r,c.p1,c.r,c.p0):i(c.r,c.p1,l.r,l.p0)+r(l.r,l.p1,l.a1-l.a0)+i(l.r,l.p1,c.r,c.p0))+"Z"}function t(n,t,e,r){var i=t.call(n,e,r),u=o.call(n,i,r),a=c.call(n,i,r)+Dc,s=l.call(n,i,r)+Dc;return{r:u,a0:a,a1:s,p0:[u*Math.cos(a),u*Math.sin(a)],p1:[u*Math.cos(s),u*Math.sin(s)]}}function e(n,t){return n.a0==t.a0&&n.a1==t.a1}function r(n,t,e){return"A"+n+","+n+" 0 "+ +(e>Ka)+",1 "+t}function i(n,t,e,r){return"Q 0,0 "+r}var u=ze,a=De,o=Mu,c=mu,l=du;return n.radius=function(t){return arguments.length?(o=pt(t),n):o},n.source=function(t){return arguments.length?(u=pt(t),n):u},n.target=function(t){return arguments.length?(a=pt(t),n):a},n.startAngle=function(t){return arguments.length?(c=pt(t),n):c},n.endAngle=function(t){return arguments.length?(l=pt(t),n):l},n},ya.svg.diagonal=function(){function n(n,i){var u=t.call(this,n,i),a=e.call(this,n,i),o=(u.y+a.y)/2,c=[u,{x:u.x,y:o},{x:a.x,y:o},a];return c=c.map(r),"M"+c[0]+"C"+c[1]+" "+c[2]+" "+c[3]}var t=ze,e=De,r=xu;return n.source=function(e){return arguments.length?(t=pt(e),n):t},n.target=function(t){return arguments.length?(e=pt(t),n):e},n.projection=function(t){return arguments.length?(r=t,n):r},n},ya.svg.diagonal.radial=function(){var n=ya.svg.diagonal(),t=xu,e=n.projection;return n.projection=function(n){return arguments.length?e(bu(t=n)):t},n},ya.svg.symbol=function(){function n(n,r){return(Lc.get(t.call(this,n,r))||Su)(e.call(this,n,r))}var t=wu,e=_u;return n.type=function(e){return arguments.length?(t=pt(e),n):t},n.size=function(t){return arguments.length?(e=pt(t),n):e},n};var Lc=ya.map({circle:Su,cross:function(n){var t=Math.sqrt(n/5)/2;return"M"+-3*t+","+-t+"H"+-t+"V"+-3*t+"H"+t+"V"+-t+"H"+3*t+"V"+t+"H"+t+"V"+3*t+"H"+-t+"V"+t+"H"+-3*t+"Z"},diamond:function(n){var t=Math.sqrt(n/(2*Oc)),e=t*Oc;return"M0,"+-t+"L"+e+",0"+" 0,"+t+" "+-e+",0"+"Z"},square:function(n){var t=Math.sqrt(n)/2;return"M"+-t+","+-t+"L"+t+","+-t+" "+t+","+t+" "+-t+","+t+"Z"},"triangle-down":function(n){var t=Math.sqrt(n/Pc),e=t*Pc/2;return"M0,"+e+"L"+t+","+-e+" "+-t+","+-e+"Z"},"triangle-up":function(n){var t=Math.sqrt(n/Pc),e=t*Pc/2;return"M0,"+-e+"L"+t+","+e+" "+-t+","+e+"Z"}});ya.svg.symbolTypes=Lc.keys();var Hc,Fc,Pc=Math.sqrt(3),Oc=Math.tan(30*to),Yc=[],Rc=0;Yc.call=Ya.call,Yc.empty=Ya.empty,Yc.node=Ya.node,Yc.size=Ya.size,ya.transition=function(n){return arguments.length?Hc?n.transition():n:Ia.transition()},ya.transition.prototype=Yc,Yc.select=function(n){var t,e,r,i=this.id,u=[];n=v(n);for(var a=-1,o=this.length;++au;u++){i.push(t=[]);for(var e=this[u],o=0,c=e.length;c>o;o++)(r=e[o])&&n.call(r,r.__data__,o)&&t.push(r)}return Eu(i,this.id)},Yc.tween=function(n,t){var e=this.id;return arguments.length<2?this.node().__transition__[e].tween.get(n):T(this,null==t?function(t){t.__transition__[e].tween.remove(n)}:function(r){r.__transition__[e].tween.set(n,t)})},Yc.attr=function(n,t){function e(){this.removeAttribute(o)}function r(){this.removeAttributeNS(o.space,o.local)}function i(n){return null==n?e:(n+="",function(){var t,e=this.getAttribute(o);return e!==n&&(t=a(e,n),function(n){this.setAttribute(o,t(n))})})}function u(n){return null==n?r:(n+="",function(){var t,e=this.getAttributeNS(o.space,o.local);return e!==n&&(t=a(e,n),function(n){this.setAttributeNS(o.space,o.local,t(n))})})}if(arguments.length<2){for(t in n)this.attr(t,n[t]);return this}var a="transform"==n?Br:Sr,o=ya.ns.qualify(n);return ku(this,"attr."+n,t,o.local?u:i)},Yc.attrTween=function(n,t){function e(n,e){var r=t.call(this,n,e,this.getAttribute(i));return r&&function(n){this.setAttribute(i,r(n))}}function r(n,e){var r=t.call(this,n,e,this.getAttributeNS(i.space,i.local));return r&&function(n){this.setAttributeNS(i.space,i.local,r(n))}}var i=ya.ns.qualify(n);return this.tween("attr."+n,i.local?r:e)},Yc.style=function(n,t,e){function r(){this.style.removeProperty(n)}function i(t){return null==t?r:(t+="",function(){var r,i=ba.getComputedStyle(this,null).getPropertyValue(n);return i!==t&&(r=Sr(i,t),function(t){this.style.setProperty(n,r(t),e)})})}var u=arguments.length;if(3>u){if("string"!=typeof n){2>u&&(t="");for(e in n)this.style(e,n[e],t);return this}e=""}return ku(this,"style."+n,t,i)},Yc.styleTween=function(n,t,e){function r(r,i){var u=t.call(this,r,i,ba.getComputedStyle(this,null).getPropertyValue(n));return u&&function(t){this.style.setProperty(n,u(t),e)}}return arguments.length<3&&(e=""),this.tween("style."+n,r)},Yc.text=function(n){return ku(this,"text",n,Au)},Yc.remove=function(){return this.each("end.transition",function(){var n;!this.__transition__&&(n=this.parentNode)&&n.removeChild(this)})},Yc.ease=function(n){var t=this.id;return arguments.length<1?this.node().__transition__[t].ease:("function"!=typeof n&&(n=ya.ease.apply(ya,arguments)),T(this,function(e){e.__transition__[t].ease=n}))},Yc.delay=function(n){var t=this.id;return T(this,"function"==typeof n?function(e,r,i){e.__transition__[t].delay=0|n.call(e,e.__data__,r,i)}:(n|=0,function(e){e.__transition__[t].delay=n}))},Yc.duration=function(n){var t=this.id;return T(this,"function"==typeof n?function(e,r,i){e.__transition__[t].duration=Math.max(1,0|n.call(e,e.__data__,r,i))}:(n=Math.max(1,0|n),function(e){e.__transition__[t].duration=n}))},Yc.each=function(n,t){var e=this.id;if(arguments.length<2){var r=Fc,i=Hc;Hc=e,T(this,function(t,r,i){Fc=t.__transition__[e],n.call(t,t.__data__,r,i)}),Fc=r,Hc=i}else T(this,function(r){var i=r.__transition__[e];(i.event||(i.event=ya.dispatch("start","end"))).on(n,t)});return this},Yc.transition=function(){for(var n,t,e,r,i=this.id,u=++Rc,a=[],o=0,c=this.length;c>o;o++){a.push(n=[]);for(var t=this[o],l=0,s=t.length;s>l;l++)(e=t[l])&&(r=Object.create(e.__transition__[i]),r.delay+=r.duration,Nu(e,l,u,r)),n.push(e)}return Eu(a,u)},ya.svg.axis=function(){function n(n){n.each(function(){var n,f=ya.select(this),h=null==l?e.ticks?e.ticks.apply(e,c):e.domain():l,g=null==t?e.tickFormat?e.tickFormat.apply(e,c):String:t,p=Cu(e,h,s),m=f.selectAll(".tick.minor").data(p,String),d=m.enter().insert("line",".tick").attr("class","tick minor").style("opacity",1e-6),v=ya.transition(m.exit()).style("opacity",1e-6).remove(),y=ya.transition(m).style("opacity",1),M=f.selectAll(".tick.major").data(h,String),x=M.enter().insert("g",".domain").attr("class","tick major").style("opacity",1e-6),b=ya.transition(M.exit()).style("opacity",1e-6).remove(),_=ya.transition(M).style("opacity",1),w=Bi(e),S=f.selectAll(".domain").data([0]),E=(S.enter().append("path").attr("class","domain"),ya.transition(S)),k=e.copy(),A=this.__chart__||k;
+this.__chart__=k,x.append("line"),x.append("text");var N=x.select("line"),q=_.select("line"),T=M.select("text").text(g),C=x.select("text"),z=_.select("text");switch(r){case"bottom":n=qu,d.attr("y2",u),y.attr("x2",0).attr("y2",u),N.attr("y2",i),C.attr("y",Math.max(i,0)+o),q.attr("x2",0).attr("y2",i),z.attr("x",0).attr("y",Math.max(i,0)+o),T.attr("dy",".71em").style("text-anchor","middle"),E.attr("d","M"+w[0]+","+a+"V0H"+w[1]+"V"+a);break;case"top":n=qu,d.attr("y2",-u),y.attr("x2",0).attr("y2",-u),N.attr("y2",-i),C.attr("y",-(Math.max(i,0)+o)),q.attr("x2",0).attr("y2",-i),z.attr("x",0).attr("y",-(Math.max(i,0)+o)),T.attr("dy","0em").style("text-anchor","middle"),E.attr("d","M"+w[0]+","+-a+"V0H"+w[1]+"V"+-a);break;case"left":n=Tu,d.attr("x2",-u),y.attr("x2",-u).attr("y2",0),N.attr("x2",-i),C.attr("x",-(Math.max(i,0)+o)),q.attr("x2",-i).attr("y2",0),z.attr("x",-(Math.max(i,0)+o)).attr("y",0),T.attr("dy",".32em").style("text-anchor","end"),E.attr("d","M"+-a+","+w[0]+"H0V"+w[1]+"H"+-a);break;case"right":n=Tu,d.attr("x2",u),y.attr("x2",u).attr("y2",0),N.attr("x2",i),C.attr("x",Math.max(i,0)+o),q.attr("x2",i).attr("y2",0),z.attr("x",Math.max(i,0)+o).attr("y",0),T.attr("dy",".32em").style("text-anchor","start"),E.attr("d","M"+a+","+w[0]+"H0V"+w[1]+"H"+a)}if(e.rangeBand){var D=k.rangeBand()/2,j=function(n){return k(n)+D};x.call(n,j),_.call(n,j)}else x.call(n,A),_.call(n,k),b.call(n,k),d.call(n,A),y.call(n,k),v.call(n,k)})}var t,e=ya.scale.linear(),r=Uc,i=6,u=6,a=6,o=3,c=[10],l=null,s=0;return n.scale=function(t){return arguments.length?(e=t,n):e},n.orient=function(t){return arguments.length?(r=t in Ic?t+"":Uc,n):r},n.ticks=function(){return arguments.length?(c=arguments,n):c},n.tickValues=function(t){return arguments.length?(l=t,n):l},n.tickFormat=function(e){return arguments.length?(t=e,n):t},n.tickSize=function(t,e){if(!arguments.length)return i;var r=arguments.length-1;return i=+t,u=r>1?+e:i,a=r>0?+arguments[r]:i,n},n.tickPadding=function(t){return arguments.length?(o=+t,n):o},n.tickSubdivide=function(t){return arguments.length?(s=+t,n):s},n};var Uc="bottom",Ic={top:1,right:1,bottom:1,left:1};ya.svg.brush=function(){function n(u){u.each(function(){var u,a=ya.select(this),s=a.selectAll(".background").data([0]),f=a.selectAll(".extent").data([0]),h=a.selectAll(".resize").data(l,String);a.style("pointer-events","all").on("mousedown.brush",i).on("touchstart.brush",i),s.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),f.enter().append("rect").attr("class","extent").style("cursor","move"),h.enter().append("g").attr("class",function(n){return"resize "+n}).style("cursor",function(n){return Vc[n]}).append("rect").attr("x",function(n){return/[ew]$/.test(n)?-3:null}).attr("y",function(n){return/^[ns]/.test(n)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),h.style("display",n.empty()?"none":null),h.exit().remove(),o&&(u=Bi(o),s.attr("x",u[0]).attr("width",u[1]-u[0]),e(a)),c&&(u=Bi(c),s.attr("y",u[0]).attr("height",u[1]-u[0]),r(a)),t(a)})}function t(n){n.selectAll(".resize").attr("transform",function(n){return"translate("+s[+/e$/.test(n)][0]+","+s[+/^s/.test(n)][1]+")"})}function e(n){n.select(".extent").attr("x",s[0][0]),n.selectAll(".extent,.n>rect,.s>rect").attr("width",s[1][0]-s[0][0])}function r(n){n.select(".extent").attr("y",s[0][1]),n.selectAll(".extent,.e>rect,.w>rect").attr("height",s[1][1]-s[0][1])}function i(){function i(){var n=ya.event.changedTouches;return n?ya.touches(M,n)[0]:ya.mouse(M)}function l(){32==ya.event.keyCode&&(k||(v=null,N[0]-=s[1][0],N[1]-=s[1][1],k=2),g())}function h(){32==ya.event.keyCode&&2==k&&(N[0]+=s[1][0],N[1]+=s[1][1],k=0,g())}function p(){var n=i(),u=!1;y&&(n[0]+=y[0],n[1]+=y[1]),k||(ya.event.altKey?(v||(v=[(s[0][0]+s[1][0])/2,(s[0][1]+s[1][1])/2]),N[0]=s[+(n[0]l?(i=r,r=l):i=l),s[0][e]!==r||s[1][e]!==i?(u=null,s[0][e]=r,s[1][e]=i,!0):void 0}function d(){p(),_.style("pointer-events","all").selectAll(".resize").style("display",n.empty()?"none":null),ya.select("body").style("cursor",null),q.on("mousemove.brush",null).on("mouseup.brush",null).on("touchmove.brush",null).on("touchend.brush",null).on("keydown.brush",null).on("keyup.brush",null),A(),b({type:"brushend"})}var v,y,M=this,x=ya.select(ya.event.target),b=a.of(M,arguments),_=ya.select(M),w=x.datum(),S=!/^(n|s)$/.test(w)&&o,E=!/^(e|w)$/.test(w)&&c,k=x.classed("extent"),A=H(),N=i(),q=ya.select(ba).on("keydown.brush",l).on("keyup.brush",h);if(ya.event.changedTouches?q.on("touchmove.brush",p).on("touchend.brush",d):q.on("mousemove.brush",p).on("mouseup.brush",d),k)N[0]=s[0][0]-N[0],N[1]=s[0][1]-N[1];else if(w){var T=+/w$/.test(w),C=+/^n/.test(w);y=[s[1-T][0]-N[0],s[1-C][1]-N[1]],N[0]=s[T][0],N[1]=s[C][1]}else ya.event.altKey&&(v=N.slice());_.style("pointer-events","none").selectAll(".resize").style("display",null),ya.select("body").style("cursor",x.style("cursor")),b({type:"brushstart"}),p()}var u,a=m(n,"brushstart","brush","brushend"),o=null,c=null,l=Xc[0],s=[[0,0],[0,0]],f=[!0,!0];return n.x=function(t){return arguments.length?(o=t,l=Xc[!o<<1|!c],n):o},n.y=function(t){return arguments.length?(c=t,l=Xc[!o<<1|!c],n):c},n.clamp=function(t){return arguments.length?(o&&c?f=[!!t[0],!!t[1]]:(o||c)&&(f[+!o]=!!t),n):o&&c?f:o||c?f[+!o]:null},n.extent=function(t){var e,r,i,a,l;return arguments.length?(u=[[0,0],[0,0]],o&&(e=t[0],r=t[1],c&&(e=e[0],r=r[0]),u[0][0]=e,u[1][0]=r,o.invert&&(e=o(e),r=o(r)),e>r&&(l=e,e=r,r=l),s[0][0]=0|e,s[1][0]=0|r),c&&(i=t[0],a=t[1],o&&(i=i[1],a=a[1]),u[0][1]=i,u[1][1]=a,c.invert&&(i=c(i),a=c(a)),i>a&&(l=i,i=a,a=l),s[0][1]=0|i,s[1][1]=0|a),n):(t=u||s,o&&(e=t[0][0],r=t[1][0],u||(e=s[0][0],r=s[1][0],o.invert&&(e=o.invert(e),r=o.invert(r)),e>r&&(l=e,e=r,r=l))),c&&(i=t[0][1],a=t[1][1],u||(i=s[0][1],a=s[1][1],c.invert&&(i=c.invert(i),a=c.invert(a)),i>a&&(l=i,i=a,a=l))),o&&c?[[e,i],[r,a]]:o?[e,r]:c&&[i,a])},n.clear=function(){return u=null,s[0][0]=s[0][1]=s[1][0]=s[1][1]=0,n},n.empty=function(){return o&&s[0][0]===s[1][0]||c&&s[0][1]===s[1][1]},ya.rebind(n,a,"on")};var Vc={n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Xc=[["n","e","s","w","nw","ne","se","sw"],["e","w"],["n","s"],[]];ya.time={};var Zc=Date,Bc=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];zu.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){$c.setUTCDate.apply(this._,arguments)},setDay:function(){$c.setUTCDay.apply(this._,arguments)},setFullYear:function(){$c.setUTCFullYear.apply(this._,arguments)},setHours:function(){$c.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){$c.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){$c.setUTCMinutes.apply(this._,arguments)},setMonth:function(){$c.setUTCMonth.apply(this._,arguments)},setSeconds:function(){$c.setUTCSeconds.apply(this._,arguments)},setTime:function(){$c.setTime.apply(this._,arguments)}};var $c=Date.prototype,Wc="%a %b %e %X %Y",Jc="%m/%d/%Y",Gc="%H:%M:%S",Kc=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],Qc=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],nl=["January","February","March","April","May","June","July","August","September","October","November","December"],tl=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];ya.time.year=Du(function(n){return n=ya.time.day(n),n.setMonth(0,1),n},function(n,t){n.setFullYear(n.getFullYear()+t)},function(n){return n.getFullYear()}),ya.time.years=ya.time.year.range,ya.time.years.utc=ya.time.year.utc.range,ya.time.day=Du(function(n){var t=new Zc(2e3,0);return t.setFullYear(n.getFullYear(),n.getMonth(),n.getDate()),t},function(n,t){n.setDate(n.getDate()+t)},function(n){return n.getDate()-1}),ya.time.days=ya.time.day.range,ya.time.days.utc=ya.time.day.utc.range,ya.time.dayOfYear=function(n){var t=ya.time.year(n);return Math.floor((n-t-6e4*(n.getTimezoneOffset()-t.getTimezoneOffset()))/864e5)},Bc.forEach(function(n,t){n=n.toLowerCase(),t=7-t;var e=ya.time[n]=Du(function(n){return(n=ya.time.day(n)).setDate(n.getDate()-(n.getDay()+t)%7),n},function(n,t){n.setDate(n.getDate()+7*Math.floor(t))},function(n){var e=ya.time.year(n).getDay();return Math.floor((ya.time.dayOfYear(n)+(e+t)%7)/7)-(e!==t)});ya.time[n+"s"]=e.range,ya.time[n+"s"].utc=e.utc.range,ya.time[n+"OfYear"]=function(n){var e=ya.time.year(n).getDay();return Math.floor((ya.time.dayOfYear(n)+(e+t)%7)/7)}}),ya.time.week=ya.time.sunday,ya.time.weeks=ya.time.sunday.range,ya.time.weeks.utc=ya.time.sunday.utc.range,ya.time.weekOfYear=ya.time.sundayOfYear,ya.time.format=function(n){function t(t){for(var r,i,u,a=[],o=-1,c=0;++o=12?"PM":"AM"},S:function(n,t){return Pu(n.getSeconds(),t,2)},U:function(n,t){return Pu(ya.time.sundayOfYear(n),t,2)},w:function(n){return n.getDay()},W:function(n,t){return Pu(ya.time.mondayOfYear(n),t,2)},x:ya.time.format(Jc),X:ya.time.format(Gc),y:function(n,t){return Pu(n.getFullYear()%100,t,2)},Y:function(n,t){return Pu(n.getFullYear()%1e4,t,4)},Z:aa,"%":function(){return"%"}},gl={a:Ou,A:Yu,b:Vu,B:Xu,c:Zu,d:Qu,e:Qu,H:ta,I:ta,j:na,L:ia,m:Ku,M:ea,p:ua,S:ra,U:Uu,w:Ru,W:Iu,x:Bu,X:$u,y:Ju,Y:Wu,"%":oa},pl=/^\s*\d+/,ml=ya.map({am:0,pm:1});ya.time.format.utc=function(n){function t(n){try{Zc=zu;var t=new Zc;return t._=n,e(t)}finally{Zc=Date}}var e=ya.time.format(n);return t.parse=function(n){try{Zc=zu;var t=e.parse(n);return t&&t._}finally{Zc=Date}},t.toString=e.toString,t};var dl=ya.time.format.utc("%Y-%m-%dT%H:%M:%S.%LZ");ya.time.format.iso=Date.prototype.toISOString&&+new Date("2000-01-01T00:00:00.000Z")?ca:dl,ca.parse=function(n){var t=new Date(n);return isNaN(t)?null:t},ca.toString=dl.toString,ya.time.second=Du(function(n){return new Zc(1e3*Math.floor(n/1e3))},function(n,t){n.setTime(n.getTime()+1e3*Math.floor(t))},function(n){return n.getSeconds()}),ya.time.seconds=ya.time.second.range,ya.time.seconds.utc=ya.time.second.utc.range,ya.time.minute=Du(function(n){return new Zc(6e4*Math.floor(n/6e4))},function(n,t){n.setTime(n.getTime()+6e4*Math.floor(t))},function(n){return n.getMinutes()}),ya.time.minutes=ya.time.minute.range,ya.time.minutes.utc=ya.time.minute.utc.range,ya.time.hour=Du(function(n){var t=n.getTimezoneOffset()/60;return new Zc(36e5*(Math.floor(n/36e5-t)+t))},function(n,t){n.setTime(n.getTime()+36e5*Math.floor(t))},function(n){return n.getHours()}),ya.time.hours=ya.time.hour.range,ya.time.hours.utc=ya.time.hour.utc.range,ya.time.month=Du(function(n){return n=ya.time.day(n),n.setDate(1),n},function(n,t){n.setMonth(n.getMonth()+t)},function(n){return n.getMonth()}),ya.time.months=ya.time.month.range,ya.time.months.utc=ya.time.month.utc.range;var vl=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],yl=[[ya.time.second,1],[ya.time.second,5],[ya.time.second,15],[ya.time.second,30],[ya.time.minute,1],[ya.time.minute,5],[ya.time.minute,15],[ya.time.minute,30],[ya.time.hour,1],[ya.time.hour,3],[ya.time.hour,6],[ya.time.hour,12],[ya.time.day,1],[ya.time.day,2],[ya.time.week,1],[ya.time.month,1],[ya.time.month,3],[ya.time.year,1]],Ml=[[ya.time.format("%Y"),Xt],[ya.time.format("%B"),function(n){return n.getMonth()}],[ya.time.format("%b %d"),function(n){return 1!=n.getDate()}],[ya.time.format("%a %d"),function(n){return n.getDay()&&1!=n.getDate()}],[ya.time.format("%I %p"),function(n){return n.getHours()}],[ya.time.format("%I:%M"),function(n){return n.getMinutes()}],[ya.time.format(":%S"),function(n){return n.getSeconds()}],[ya.time.format(".%L"),function(n){return n.getMilliseconds()}]],xl=ya.scale.linear(),bl=fa(Ml);yl.year=function(n,t){return xl.domain(n.map(ga)).ticks(t).map(ha)},ya.time.scale=function(){return la(ya.scale.linear(),yl,bl)};var _l=yl.map(function(n){return[n[0].utc,n[1]]}),wl=[[ya.time.format.utc("%Y"),Xt],[ya.time.format.utc("%B"),function(n){return n.getUTCMonth()}],[ya.time.format.utc("%b %d"),function(n){return 1!=n.getUTCDate()}],[ya.time.format.utc("%a %d"),function(n){return n.getUTCDay()&&1!=n.getUTCDate()}],[ya.time.format.utc("%I %p"),function(n){return n.getUTCHours()}],[ya.time.format.utc("%I:%M"),function(n){return n.getUTCMinutes()}],[ya.time.format.utc(":%S"),function(n){return n.getUTCSeconds()}],[ya.time.format.utc(".%L"),function(n){return n.getUTCMilliseconds()}]],Sl=fa(wl);return _l.year=function(n,t){return xl.domain(n.map(ma)).ticks(t).map(pa)},ya.time.scale.utc=function(){return la(ya.scale.linear(),_l,Sl)},ya.text=dt(function(n){return n.responseText}),ya.json=function(n,t){return vt(n,"application/json",da,t)},ya.html=function(n,t){return vt(n,"text/html",va,t)},ya.xml=dt(function(n){return n.responseXML}),ya}();
\ No newline at end of file
diff --git a/fire_tablet_dashboard/assets/javascripts/dashing.gridster.coffee b/fire_tablet_dashboard/assets/javascripts/dashing.gridster.coffee
new file mode 100644
index 00000000..e25c5618
--- /dev/null
+++ b/fire_tablet_dashboard/assets/javascripts/dashing.gridster.coffee
@@ -0,0 +1,37 @@
+#= require_directory ./gridster
+
+# This file enables gridster integration (http://gridster.net/)
+# Delete it if you'd rather handle the layout yourself.
+# You'll miss out on a lot if you do, but we won't hold it against you.
+
+Dashing.gridsterLayout = (positions) ->
+ Dashing.customGridsterLayout = true
+ positions = positions.replace(/^"|"$/g, '')
+ positions = $.parseJSON(positions)
+ widgets = $("[data-row^=]")
+ for widget, index in widgets
+ $(widget).attr('data-row', positions[index].row)
+ $(widget).attr('data-col', positions[index].col)
+
+Dashing.getWidgetPositions = ->
+ $(".gridster ul:first").gridster().data('gridster').serialize()
+
+Dashing.showGridsterInstructions = ->
+ newWidgetPositions = Dashing.getWidgetPositions()
+
+ unless JSON.stringify(newWidgetPositions) == JSON.stringify(Dashing.currentWidgetPositions)
+ Dashing.currentWidgetPositions = newWidgetPositions
+ $('#save-gridster').slideDown()
+ $('#gridster-code').text("
+
+ ")
+
+$ ->
+ $('#save-gridster').leanModal()
+
+ $('#save-gridster').click ->
+ $('#save-gridster').slideUp()
diff --git a/fire_tablet_dashboard/assets/javascripts/gridster/jquery.gridster.js b/fire_tablet_dashboard/assets/javascripts/gridster/jquery.gridster.js
new file mode 100644
index 00000000..9451c9aa
--- /dev/null
+++ b/fire_tablet_dashboard/assets/javascripts/gridster/jquery.gridster.js
@@ -0,0 +1,3263 @@
+/*! gridster.js - v0.1.0 - 2013-02-15
+* http://gridster.net/
+* Copyright (c) 2013 ducksboard; Licensed MIT */
+
+;(function($, window, document, undefined){
+ /**
+ * Creates objects with coordinates (x1, y1, x2, y2, cx, cy, width, height)
+ * to simulate DOM elements on the screen.
+ * Coords is used by Gridster to create a faux grid with any DOM element can
+ * collide.
+ *
+ * @class Coords
+ * @param {HTMLElement|Object} obj The jQuery HTMLElement or a object with: left,
+ * top, width and height properties.
+ * @return {Object} Coords instance.
+ * @constructor
+ */
+ function Coords(obj) {
+ if (obj[0] && $.isPlainObject(obj[0])) {
+ this.data = obj[0];
+ }else {
+ this.el = obj;
+ }
+
+ this.isCoords = true;
+ this.coords = {};
+ this.init();
+ return this;
+ }
+
+
+ var fn = Coords.prototype;
+
+
+ fn.init = function(){
+ this.set();
+ this.original_coords = this.get();
+ };
+
+
+ fn.set = function(update, not_update_offsets) {
+ var el = this.el;
+
+ if (el && !update) {
+ this.data = el.offset();
+ this.data.width = el.width();
+ this.data.height = el.height();
+ }
+
+ if (el && update && !not_update_offsets) {
+ var offset = el.offset();
+ this.data.top = offset.top;
+ this.data.left = offset.left;
+ }
+
+ var d = this.data;
+
+ this.coords.x1 = d.left;
+ this.coords.y1 = d.top;
+ this.coords.x2 = d.left + d.width;
+ this.coords.y2 = d.top + d.height;
+ this.coords.cx = d.left + (d.width / 2);
+ this.coords.cy = d.top + (d.height / 2);
+ this.coords.width = d.width;
+ this.coords.height = d.height;
+ this.coords.el = el || false ;
+
+ return this;
+ };
+
+
+ fn.update = function(data){
+ if (!data && !this.el) {
+ return this;
+ }
+
+ if (data) {
+ var new_data = $.extend({}, this.data, data);
+ this.data = new_data;
+ return this.set(true, true);
+ }
+
+ this.set(true);
+ return this;
+ };
+
+
+ fn.get = function(){
+ return this.coords;
+ };
+
+
+ //jQuery adapter
+ $.fn.coords = function() {
+ if (this.data('coords') ) {
+ return this.data('coords');
+ }
+
+ var ins = new Coords(this, arguments[0]);
+ this.data('coords', ins);
+ return ins;
+ };
+
+}(jQuery, window, document));
+
+;(function($, window, document, undefined){
+
+ var defaults = {
+ colliders_context: document.body
+ // ,on_overlap: function(collider_data){},
+ // on_overlap_start : function(collider_data){},
+ // on_overlap_stop : function(collider_data){}
+ };
+
+
+ /**
+ * Detects collisions between a DOM element against other DOM elements or
+ * Coords objects.
+ *
+ * @class Collision
+ * @uses Coords
+ * @param {HTMLElement} el The jQuery wrapped HTMLElement.
+ * @param {HTMLElement|Array} colliders Can be a jQuery collection
+ * of HTMLElements or an Array of Coords instances.
+ * @param {Object} [options] An Object with all options you want to
+ * overwrite:
+ * @param {Function} [options.on_overlap_start] Executes a function the first
+ * time each `collider ` is overlapped.
+ * @param {Function} [options.on_overlap_stop] Executes a function when a
+ * `collider` is no longer collided.
+ * @param {Function} [options.on_overlap] Executes a function when the
+ * mouse is moved during the collision.
+ * @return {Object} Collision instance.
+ * @constructor
+ */
+ function Collision(el, colliders, options) {
+ this.options = $.extend(defaults, options);
+ this.$element = el;
+ this.last_colliders = [];
+ this.last_colliders_coords = [];
+ if (typeof colliders === 'string' || colliders instanceof jQuery) {
+ this.$colliders = $(colliders,
+ this.options.colliders_context).not(this.$element);
+ }else{
+ this.colliders = $(colliders);
+ }
+
+ this.init();
+ }
+
+
+ var fn = Collision.prototype;
+
+
+ fn.init = function() {
+ this.find_collisions();
+ };
+
+
+ fn.overlaps = function(a, b) {
+ var x = false;
+ var y = false;
+
+ if ((b.x1 >= a.x1 && b.x1 <= a.x2) ||
+ (b.x2 >= a.x1 && b.x2 <= a.x2) ||
+ (a.x1 >= b.x1 && a.x2 <= b.x2)
+ ) { x = true; }
+
+ if ((b.y1 >= a.y1 && b.y1 <= a.y2) ||
+ (b.y2 >= a.y1 && b.y2 <= a.y2) ||
+ (a.y1 >= b.y1 && a.y2 <= b.y2)
+ ) { y = true; }
+
+ return (x && y);
+ };
+
+
+ fn.detect_overlapping_region = function(a, b){
+ var regionX = '';
+ var regionY = '';
+
+ if (a.y1 > b.cy && a.y1 < b.y2) { regionX = 'N'; }
+ if (a.y2 > b.y1 && a.y2 < b.cy) { regionX = 'S'; }
+ if (a.x1 > b.cx && a.x1 < b.x2) { regionY = 'W'; }
+ if (a.x2 > b.x1 && a.x2 < b.cx) { regionY = 'E'; }
+
+ return (regionX + regionY) || 'C';
+ };
+
+
+ fn.calculate_overlapped_area_coords = function(a, b){
+ var x1 = Math.max(a.x1, b.x1);
+ var y1 = Math.max(a.y1, b.y1);
+ var x2 = Math.min(a.x2, b.x2);
+ var y2 = Math.min(a.y2, b.y2);
+
+ return $({
+ left: x1,
+ top: y1,
+ width : (x2 - x1),
+ height: (y2 - y1)
+ }).coords().get();
+ };
+
+
+ fn.calculate_overlapped_area = function(coords){
+ return (coords.width * coords.height);
+ };
+
+
+ fn.manage_colliders_start_stop = function(new_colliders_coords, start_callback, stop_callback){
+ var last = this.last_colliders_coords;
+
+ for (var i = 0, il = last.length; i < il; i++) {
+ if ($.inArray(last[i], new_colliders_coords) === -1) {
+ start_callback.call(this, last[i]);
+ }
+ }
+
+ for (var j = 0, jl = new_colliders_coords.length; j < jl; j++) {
+ if ($.inArray(new_colliders_coords[j], last) === -1) {
+ stop_callback.call(this, new_colliders_coords[j]);
+ }
+
+ }
+ };
+
+
+ fn.find_collisions = function(player_data_coords){
+ var self = this;
+ var colliders_coords = [];
+ var colliders_data = [];
+ var $colliders = (this.colliders || this.$colliders);
+ var count = $colliders.length;
+ var player_coords = self.$element.coords()
+ .update(player_data_coords || false).get();
+
+ while(count--){
+ var $collider = self.$colliders ?
+ $($colliders[count]) : $colliders[count];
+ var $collider_coords_ins = ($collider.isCoords) ?
+ $collider : $collider.coords();
+ var collider_coords = $collider_coords_ins.get();
+ var overlaps = self.overlaps(player_coords, collider_coords);
+
+ if (!overlaps) {
+ continue;
+ }
+
+ var region = self.detect_overlapping_region(
+ player_coords, collider_coords);
+
+ //todo: make this an option
+ if (region === 'C'){
+ var area_coords = self.calculate_overlapped_area_coords(
+ player_coords, collider_coords);
+ var area = self.calculate_overlapped_area(area_coords);
+ var collider_data = {
+ area: area,
+ area_coords : area_coords,
+ region: region,
+ coords: collider_coords,
+ player_coords: player_coords,
+ el: $collider
+ };
+
+ if (self.options.on_overlap) {
+ self.options.on_overlap.call(this, collider_data);
+ }
+ colliders_coords.push($collider_coords_ins);
+ colliders_data.push(collider_data);
+ }
+ }
+
+ if (self.options.on_overlap_stop || self.options.on_overlap_start) {
+ this.manage_colliders_start_stop(colliders_coords,
+ self.options.on_overlap_stop, self.options.on_overlap_start);
+ }
+
+ this.last_colliders_coords = colliders_coords;
+
+ return colliders_data;
+ };
+
+
+ fn.get_closest_colliders = function(player_data_coords){
+ var colliders = this.find_collisions(player_data_coords);
+
+ colliders.sort(function(a, b) {
+ /* if colliders are being overlapped by the "C" (center) region,
+ * we have to set a lower index in the array to which they are placed
+ * above in the grid. */
+ if (a.region === 'C' && b.region === 'C') {
+ if (a.coords.y1 < b.coords.y1 || a.coords.x1 < b.coords.x1) {
+ return - 1;
+ }else{
+ return 1;
+ }
+ }
+
+ if (a.area < b.area) {
+ return 1;
+ }
+
+ return 1;
+ });
+ return colliders;
+ };
+
+
+ //jQuery adapter
+ $.fn.collision = function(collider, options) {
+ return new Collision( this, collider, options );
+ };
+
+
+}(jQuery, window, document));
+
+;(function(window, undefined) {
+ /* Debounce and throttle functions taken from underscore.js */
+ window.debounce = function(func, wait, immediate) {
+ var timeout;
+ return function() {
+ var context = this, args = arguments;
+ var later = function() {
+ timeout = null;
+ if (!immediate) func.apply(context, args);
+ };
+ if (immediate && !timeout) func.apply(context, args);
+ clearTimeout(timeout);
+ timeout = setTimeout(later, wait);
+ };
+ };
+
+
+ window.throttle = function(func, wait) {
+ var context, args, timeout, throttling, more, result;
+ var whenDone = debounce(
+ function(){ more = throttling = false; }, wait);
+ return function() {
+ context = this; args = arguments;
+ var later = function() {
+ timeout = null;
+ if (more) func.apply(context, args);
+ whenDone();
+ };
+ if (!timeout) timeout = setTimeout(later, wait);
+ if (throttling) {
+ more = true;
+ } else {
+ result = func.apply(context, args);
+ }
+ whenDone();
+ throttling = true;
+ return result;
+ };
+ };
+
+})(window);
+
+;(function($, window, document, undefined){
+
+ var defaults = {
+ items: '.gs_w',
+ distance: 1,
+ limit: true,
+ offset_left: 0,
+ autoscroll: true,
+ ignore_dragging: ['INPUT', 'TEXTAREA', 'SELECT', 'BUTTON'],
+ handle: null
+ // drag: function(e){},
+ // start : function(e, ui){},
+ // stop : function(e){}
+ };
+
+ var $window = $(window);
+ var isTouch = !!('ontouchstart' in window);
+ var pointer_events = {
+ start: isTouch ? 'touchstart' : 'mousedown.draggable',
+ move: isTouch ? 'touchmove' : 'mousemove.draggable',
+ end: isTouch ? 'touchend' : 'mouseup.draggable'
+ };
+
+ /**
+ * Basic drag implementation for DOM elements inside a container.
+ * Provide start/stop/drag callbacks.
+ *
+ * @class Draggable
+ * @param {HTMLElement} el The HTMLelement that contains all the widgets
+ * to be dragged.
+ * @param {Object} [options] An Object with all options you want to
+ * overwrite:
+ * @param {HTMLElement|String} [options.items] Define who will
+ * be the draggable items. Can be a CSS Selector String or a
+ * collection of HTMLElements.
+ * @param {Number} [options.distance] Distance in pixels after mousedown
+ * the mouse must move before dragging should start.
+ * @param {Boolean} [options.limit] Constrains dragging to the width of
+ * the container
+ * @param {offset_left} [options.offset_left] Offset added to the item
+ * that is being dragged.
+ * @param {Number} [options.drag] Executes a callback when the mouse is
+ * moved during the dragging.
+ * @param {Number} [options.start] Executes a callback when the drag
+ * starts.
+ * @param {Number} [options.stop] Executes a callback when the drag stops.
+ * @return {Object} Returns `el`.
+ * @constructor
+ */
+ function Draggable(el, options) {
+ this.options = $.extend({}, defaults, options);
+ this.$body = $(document.body);
+ this.$container = $(el);
+ this.$dragitems = $(this.options.items, this.$container);
+ this.is_dragging = false;
+ this.player_min_left = 0 + this.options.offset_left;
+ this.init();
+ }
+
+ var fn = Draggable.prototype;
+
+ fn.init = function() {
+ this.calculate_positions();
+ this.$container.css('position', 'relative');
+ this.disabled = false;
+ this.events();
+
+ this.on_window_resize = throttle($.proxy(this.calculate_positions, this), 200);
+ $(window).bind('resize', this.on_window_resize);
+ };
+
+ fn.events = function() {
+ this.proxied_on_select_start = $.proxy(this.on_select_start, this);
+ this.$container.on('selectstart', this.proxied_on_select_start);
+
+ this.proxied_drag_handler = $.proxy(this.drag_handler, this);
+ this.$container.on(pointer_events.start, this.options.items, this.proxied_drag_handler);
+
+ this.proxied_pointer_events_end = $.proxy(function(e) {
+ this.is_dragging = false;
+ if (this.disabled) { return; }
+ this.$body.off(pointer_events.move);
+ if (this.drag_start) {
+ this.on_dragstop(e);
+ }
+ }, this);
+ this.$body.on(pointer_events.end, this.proxied_pointer_events_end);
+ };
+
+ fn.get_actual_pos = function($el) {
+ var pos = $el.position();
+ return pos;
+ };
+
+
+ fn.get_mouse_pos = function(e) {
+ if (isTouch) {
+ var oe = e.originalEvent;
+ e = oe.touches.length ? oe.touches[0] : oe.changedTouches[0];
+ }
+
+ return {
+ left: e.clientX,
+ top: e.clientY
+ };
+ };
+
+
+ fn.get_offset = function(e) {
+ e.preventDefault();
+ var mouse_actual_pos = this.get_mouse_pos(e);
+ var diff_x = Math.round(
+ mouse_actual_pos.left - this.mouse_init_pos.left);
+ var diff_y = Math.round(mouse_actual_pos.top - this.mouse_init_pos.top);
+
+ var left = Math.round(this.el_init_offset.left + diff_x - this.baseX);
+ var top = Math.round(
+ this.el_init_offset.top + diff_y - this.baseY + this.scrollOffset);
+
+ if (this.options.limit) {
+ if (left > this.player_max_left) {
+ left = this.player_max_left;
+ }else if(left < this.player_min_left) {
+ left = this.player_min_left;
+ }
+ }
+
+ return {
+ left: left,
+ top: top,
+ mouse_left: mouse_actual_pos.left,
+ mouse_top: mouse_actual_pos.top
+ };
+ };
+
+
+ fn.manage_scroll = function(offset) {
+ /* scroll document */
+ var nextScrollTop;
+ var scrollTop = $window.scrollTop();
+ var min_window_y = scrollTop;
+ var max_window_y = min_window_y + this.window_height;
+
+ var mouse_down_zone = max_window_y - 50;
+ var mouse_up_zone = min_window_y + 50;
+
+ var abs_mouse_left = offset.mouse_left;
+ var abs_mouse_top = min_window_y + offset.mouse_top;
+
+ var max_player_y = (this.doc_height - this.window_height +
+ this.player_height);
+
+ if (abs_mouse_top >= mouse_down_zone) {
+ nextScrollTop = scrollTop + 30;
+ if (nextScrollTop < max_player_y) {
+ $window.scrollTop(nextScrollTop);
+ this.scrollOffset = this.scrollOffset + 30;
+ }
+ }
+
+ if (abs_mouse_top <= mouse_up_zone) {
+ nextScrollTop = scrollTop - 30;
+ if (nextScrollTop > 0) {
+ $window.scrollTop(nextScrollTop);
+ this.scrollOffset = this.scrollOffset - 30;
+ }
+ }
+ };
+
+
+ fn.calculate_positions = function(e) {
+ this.window_height = $window.height();
+ };
+
+
+ fn.drag_handler = function(e) {
+ var node = e.target.nodeName;
+ if (this.disabled || e.which !== 1 && !isTouch) {
+ return;
+ }
+
+ if (this.ignore_drag(e)) {
+ return;
+ }
+
+ var self = this;
+ var first = true;
+ this.$player = $(e.currentTarget);
+
+ this.el_init_pos = this.get_actual_pos(this.$player);
+ this.mouse_init_pos = this.get_mouse_pos(e);
+ this.offsetY = this.mouse_init_pos.top - this.el_init_pos.top;
+
+ this.on_pointer_events_move = function(mme){
+ var mouse_actual_pos = self.get_mouse_pos(mme);
+ var diff_x = Math.abs(
+ mouse_actual_pos.left - self.mouse_init_pos.left);
+ var diff_y = Math.abs(
+ mouse_actual_pos.top - self.mouse_init_pos.top);
+ if (!(diff_x > self.options.distance ||
+ diff_y > self.options.distance)
+ ) {
+ return false;
+ }
+
+ if (first) {
+ first = false;
+ self.on_dragstart.call(self, mme);
+ return false;
+ }
+
+ if (self.is_dragging === true) {
+ self.on_dragmove.call(self, mme);
+ }
+
+ return false;
+ };
+
+ this.$body.on(pointer_events.move, this.on_pointer_events_move);
+
+ return false;
+ };
+
+
+ fn.on_dragstart = function(e) {
+ e.preventDefault();
+ this.drag_start = true;
+ this.is_dragging = true;
+ var offset = this.$container.offset();
+ this.baseX = Math.round(offset.left);
+ this.baseY = Math.round(offset.top);
+ this.doc_height = $(document).height();
+
+ if (this.options.helper === 'clone') {
+ this.$helper = this.$player.clone()
+ .appendTo(this.$container).addClass('helper');
+ this.helper = true;
+ }else{
+ this.helper = false;
+ }
+ this.scrollOffset = 0;
+ this.el_init_offset = this.$player.offset();
+ this.player_width = this.$player.width();
+ this.player_height = this.$player.height();
+ this.player_max_left = (this.$container.width() - this.player_width +
+ this.options.offset_left);
+
+ if (this.options.start) {
+ this.options.start.call(this.$player, e, {
+ helper: this.helper ? this.$helper : this.$player
+ });
+ }
+ return false;
+ };
+
+
+ fn.on_dragmove = function(e) {
+ var offset = this.get_offset(e);
+
+ this.options.autoscroll && this.manage_scroll(offset);
+
+ (this.helper ? this.$helper : this.$player).css({
+ 'position': 'absolute',
+ 'left' : offset.left,
+ 'top' : offset.top
+ });
+
+ var ui = {
+ 'position': {
+ 'left': offset.left,
+ 'top': offset.top
+ }
+ };
+
+ if (this.options.drag) {
+ this.options.drag.call(this.$player, e, ui);
+ }
+ return false;
+ };
+
+
+ fn.on_dragstop = function(e) {
+ var offset = this.get_offset(e);
+ this.drag_start = false;
+
+ var ui = {
+ 'position': {
+ 'left': offset.left,
+ 'top': offset.top
+ }
+ };
+
+ if (this.options.stop) {
+ this.options.stop.call(this.$player, e, ui);
+ }
+
+ if (this.helper) {
+ this.$helper.remove();
+ }
+
+ return false;
+ };
+
+ fn.on_select_start = function(e) {
+ if (this.disabled) { return; }
+
+ if (this.ignore_drag(e)) {
+ return;
+ }
+
+ return false;
+ };
+
+ fn.enable = function() {
+ this.disabled = false;
+ };
+
+ fn.disable = function() {
+ this.disabled = true;
+ };
+
+
+ fn.destroy = function(){
+ this.disable();
+
+ this.$container.off('selectstart', this.proxied_on_select_start);
+ this.$container.off(pointer_events.start, this.proxied_drag_handler);
+ this.$body.off(pointer_events.end, this.proxied_pointer_events_end);
+ this.$body.off(pointer_events.move, this.on_pointer_events_move);
+ $(window).unbind('resize', this.on_window_resize);
+
+ $.removeData(this.$container, 'drag');
+ };
+
+ fn.ignore_drag = function(event) {
+ if (this.options.handle) {
+ return !$(event.target).is(this.options.handle);
+ }
+
+ return $.inArray(event.target.nodeName, this.options.ignore_dragging) >= 0;
+ };
+
+ //jQuery adapter
+ $.fn.drag = function ( options ) {
+ return this.each(function () {
+ if (!$.data(this, 'drag')) {
+ $.data(this, 'drag', new Draggable( this, options ));
+ }
+ });
+ };
+
+
+}(jQuery, window, document));
+
+;(function($, window, document, undefined) {
+
+ var defaults = {
+ namespace: '',
+ widget_selector: 'li',
+ widget_margins: [10, 10],
+ widget_base_dimensions: [400, 225],
+ extra_rows: 0,
+ extra_cols: 0,
+ min_cols: 1,
+ min_rows: 15,
+ max_size_x: 6,
+ autogenerate_stylesheet: true,
+ avoid_overlapped_widgets: true,
+ serialize_params: function($w, wgd) {
+ return {
+ col: wgd.col,
+ row: wgd.row,
+ size_x: wgd.size_x,
+ size_y: wgd.size_y
+ };
+ },
+ collision: {},
+ draggable: {
+ distance: 4
+ }
+ };
+
+
+ /**
+ * @class Gridster
+ * @uses Draggable
+ * @uses Collision
+ * @param {HTMLElement} el The HTMLelement that contains all the widgets.
+ * @param {Object} [options] An Object with all options you want to
+ * overwrite:
+ * @param {HTMLElement|String} [options.widget_selector] Define who will
+ * be the draggable widgets. Can be a CSS Selector String or a
+ * collection of HTMLElements
+ * @param {Array} [options.widget_margins] Margin between widgets.
+ * The first index for the horizontal margin (left, right) and
+ * the second for the vertical margin (top, bottom).
+ * @param {Array} [options.widget_base_dimensions] Base widget dimensions
+ * in pixels. The first index for the width and the second for the
+ * height.
+ * @param {Number} [options.extra_cols] Add more columns in addition to
+ * those that have been calculated.
+ * @param {Number} [options.extra_rows] Add more rows in addition to
+ * those that have been calculated.
+ * @param {Number} [options.min_cols] The minimum required columns.
+ * @param {Number} [options.min_rows] The minimum required rows.
+ * @param {Number} [options.max_size_x] The maximum number of columns
+ * that a widget can span.
+ * @param {Boolean} [options.autogenerate_stylesheet] If true, all the
+ * CSS required to position all widgets in their respective columns
+ * and rows will be generated automatically and injected to the
+ * `` of the document. You can set this to false, and write
+ * your own CSS targeting rows and cols via data-attributes like so:
+ * `[data-col="1"] { left: 10px; }`
+ * @param {Boolean} [options.avoid_overlapped_widgets] Avoid that widgets loaded
+ * from the DOM can be overlapped. It is helpful if the positions were
+ * bad stored in the database or if there was any conflict.
+ * @param {Function} [options.serialize_params] Return the data you want
+ * for each widget in the serialization. Two arguments are passed:
+ * `$w`: the jQuery wrapped HTMLElement, and `wgd`: the grid
+ * coords object (`col`, `row`, `size_x`, `size_y`).
+ * @param {Object} [options.collision] An Object with all options for
+ * Collision class you want to overwrite. See Collision docs for
+ * more info.
+ * @param {Object} [options.draggable] An Object with all options for
+ * Draggable class you want to overwrite. See Draggable docs for more
+ * info.
+ *
+ * @constructor
+ */
+ function Gridster(el, options) {
+ this.options = $.extend(true, defaults, options);
+ this.$el = $(el);
+ this.$wrapper = this.$el.parent();
+ this.$widgets = this.$el.children(this.options.widget_selector).addClass('gs_w');
+ this.widgets = [];
+ this.$changed = $([]);
+ this.wrapper_width = this.$wrapper.width();
+ this.min_widget_width = (this.options.widget_margins[0] * 2) +
+ this.options.widget_base_dimensions[0];
+ this.min_widget_height = (this.options.widget_margins[1] * 2) +
+ this.options.widget_base_dimensions[1];
+ this.init();
+ }
+
+ Gridster.generated_stylesheets = [];
+
+ var fn = Gridster.prototype;
+
+ fn.init = function() {
+ this.generate_grid_and_stylesheet();
+ this.get_widgets_from_DOM();
+ this.set_dom_grid_height();
+ this.$wrapper.addClass('ready');
+ this.draggable();
+
+ this.on_window_resize = throttle($.proxy(this.recalculate_faux_grid, this), 200);
+
+ $(window).bind('resize', this.on_window_resize);
+ };
+
+
+ /**
+ * Disables dragging.
+ *
+ * @method disable
+ * @return {Class} Returns the instance of the Gridster Class.
+ */
+ fn.disable = function() {
+ this.$wrapper.find('.player-revert').removeClass('player-revert');
+ this.drag_api.disable();
+ return this;
+ };
+
+
+ /**
+ * Enables dragging.
+ *
+ * @method enable
+ * @return {Class} Returns the instance of the Gridster Class.
+ */
+ fn.enable = function() {
+ this.drag_api.enable();
+ return this;
+ };
+
+
+ /**
+ * Add a new widget to the grid.
+ *
+ * @method add_widget
+ * @param {String|HTMLElement} html The string representing the HTML of the widget
+ * or the HTMLElement.
+ * @param {Number} [size_x] The nº of rows the widget occupies horizontally.
+ * @param {Number} [size_y] The nº of columns the widget occupies vertically.
+ * @param {Number} [col] The column the widget should start in.
+ * @param {Number} [row] The row the widget should start in.
+ * @return {HTMLElement} Returns the jQuery wrapped HTMLElement representing.
+ * the widget that was just created.
+ */
+ fn.add_widget = function(html, size_x, size_y, col, row) {
+ var pos;
+ size_x || (size_x = 1);
+ size_y || (size_y = 1);
+
+ if (!col & !row) {
+ pos = this.next_position(size_x, size_y);
+ }else{
+ pos = {
+ col: col,
+ row: row
+ };
+
+ this.empty_cells(col, row, size_x, size_y);
+ }
+
+ var $w = $(html).attr({
+ 'data-col': pos.col,
+ 'data-row': pos.row,
+ 'data-sizex' : size_x,
+ 'data-sizey' : size_y
+ }).addClass('gs_w').appendTo(this.$el).hide();
+
+ this.$widgets = this.$widgets.add($w);
+
+ this.register_widget($w);
+
+ this.add_faux_rows(pos.size_y);
+ //this.add_faux_cols(pos.size_x);
+
+ this.set_dom_grid_height();
+
+ return $w.fadeIn();
+ };
+
+
+
+ /**
+ * Change the size of a widget.
+ *
+ * @method resize_widget
+ * @param {HTMLElement} $widget The jQuery wrapped HTMLElement
+ * representing the widget.
+ * @param {Number} size_x The number of columns that will occupy the widget.
+ * @param {Number} size_y The number of rows that will occupy the widget.
+ * @return {HTMLElement} Returns $widget.
+ */
+ fn.resize_widget = function($widget, size_x, size_y) {
+ var wgd = $widget.coords().grid;
+ size_x || (size_x = wgd.size_x);
+ size_y || (size_y = wgd.size_y);
+
+ if (size_x > this.cols) {
+ size_x = this.cols;
+ }
+
+ var old_cells_occupied = this.get_cells_occupied(wgd);
+ var old_size_x = wgd.size_x;
+ var old_size_y = wgd.size_y;
+ var old_col = wgd.col;
+ var new_col = old_col;
+ var wider = size_x > old_size_x;
+ var taller = size_y > old_size_y;
+
+ if (old_col + size_x - 1 > this.cols) {
+ var diff = old_col + (size_x - 1) - this.cols;
+ var c = old_col - diff;
+ new_col = Math.max(1, c);
+ }
+
+ var new_grid_data = {
+ col: new_col,
+ row: wgd.row,
+ size_x: size_x,
+ size_y: size_y
+ };
+
+ var new_cells_occupied = this.get_cells_occupied(new_grid_data);
+
+ var empty_cols = [];
+ $.each(old_cells_occupied.cols, function(i, col) {
+ if ($.inArray(col, new_cells_occupied.cols) === -1) {
+ empty_cols.push(col);
+ }
+ });
+
+ var occupied_cols = [];
+ $.each(new_cells_occupied.cols, function(i, col) {
+ if ($.inArray(col, old_cells_occupied.cols) === -1) {
+ occupied_cols.push(col);
+ }
+ });
+
+ var empty_rows = [];
+ $.each(old_cells_occupied.rows, function(i, row) {
+ if ($.inArray(row, new_cells_occupied.rows) === -1) {
+ empty_rows.push(row);
+ }
+ });
+
+ var occupied_rows = [];
+ $.each(new_cells_occupied.rows, function(i, row) {
+ if ($.inArray(row, old_cells_occupied.rows) === -1) {
+ occupied_rows.push(row);
+ }
+ });
+
+ this.remove_from_gridmap(wgd);
+
+ if (occupied_cols.length) {
+ var cols_to_empty = [
+ new_col, wgd.row, size_x, Math.min(old_size_y, size_y), $widget
+ ];
+ this.empty_cells.apply(this, cols_to_empty);
+ }
+
+ if (occupied_rows.length) {
+ var rows_to_empty = [new_col, wgd.row, size_x, size_y, $widget];
+ this.empty_cells.apply(this, rows_to_empty);
+ }
+
+ wgd.col = new_col;
+ wgd.size_x = size_x;
+ wgd.size_y = size_y;
+ this.add_to_gridmap(new_grid_data, $widget);
+
+ //update coords instance attributes
+ $widget.data('coords').update({
+ width: (size_x * this.options.widget_base_dimensions[0] +
+ ((size_x - 1) * this.options.widget_margins[0]) * 2),
+ height: (size_y * this.options.widget_base_dimensions[1] +
+ ((size_y - 1) * this.options.widget_margins[1]) * 2)
+ });
+
+ if (size_y > old_size_y) {
+ this.add_faux_rows(size_y - old_size_y);
+ }
+
+ if (size_x > old_size_x) {
+ this.add_faux_cols(size_x - old_size_x);
+ }
+
+ $widget.attr({
+ 'data-col': new_col,
+ 'data-sizex': size_x,
+ 'data-sizey': size_y
+ });
+
+ if (empty_cols.length) {
+ var cols_to_remove_holes = [
+ empty_cols[0], wgd.row,
+ empty_cols.length,
+ Math.min(old_size_y, size_y),
+ $widget
+ ];
+
+ this.remove_empty_cells.apply(this, cols_to_remove_holes);
+ }
+
+ if (empty_rows.length) {
+ var rows_to_remove_holes = [
+ new_col, wgd.row, size_x, size_y, $widget
+ ];
+ this.remove_empty_cells.apply(this, rows_to_remove_holes);
+ }
+
+ return $widget;
+ };
+
+ /**
+ * Move down widgets in cells represented by the arguments col, row, size_x,
+ * size_y
+ *
+ * @method empty_cells
+ * @param {Number} col The column where the group of cells begin.
+ * @param {Number} row The row where the group of cells begin.
+ * @param {Number} size_x The number of columns that the group of cells
+ * occupy.
+ * @param {Number} size_y The number of rows that the group of cells
+ * occupy.
+ * @param {HTMLElement} $exclude Exclude widgets from being moved.
+ * @return {Class} Returns the instance of the Gridster Class.
+ */
+ fn.empty_cells = function(col, row, size_x, size_y, $exclude) {
+ var $nexts = this.widgets_below({
+ col: col,
+ row: row - size_y,
+ size_x: size_x,
+ size_y: size_y
+ });
+
+ $nexts.not($exclude).each($.proxy(function(i, w) {
+ var wgd = $(w).coords().grid;
+ if (!(wgd.row <= (row + size_y - 1))) { return; }
+ var diff = (row + size_y) - wgd.row;
+ this.move_widget_down($(w), diff);
+ }, this));
+
+ this.set_dom_grid_height();
+
+ return this;
+ };
+
+
+ /**
+ * Move up widgets below cells represented by the arguments col, row, size_x,
+ * size_y.
+ *
+ * @method remove_empty_cells
+ * @param {Number} col The column where the group of cells begin.
+ * @param {Number} row The row where the group of cells begin.
+ * @param {Number} size_x The number of columns that the group of cells
+ * occupy.
+ * @param {Number} size_y The number of rows that the group of cells
+ * occupy.
+ * @param {HTMLElement} exclude Exclude widgets from being moved.
+ * @return {Class} Returns the instance of the Gridster Class.
+ */
+ fn.remove_empty_cells = function(col, row, size_x, size_y, exclude) {
+ var $nexts = this.widgets_below({
+ col: col,
+ row: row,
+ size_x: size_x,
+ size_y: size_y
+ });
+
+ $nexts.not(exclude).each($.proxy(function(i, widget) {
+ this.move_widget_up( $(widget), size_y );
+ }, this));
+
+ this.set_dom_grid_height();
+
+ return this;
+ };
+
+
+ /**
+ * Get the most left column below to add a new widget.
+ *
+ * @method next_position
+ * @param {Number} size_x The nº of rows the widget occupies horizontally.
+ * @param {Number} size_y The nº of columns the widget occupies vertically.
+ * @return {Object} Returns a grid coords object representing the future
+ * widget coords.
+ */
+ fn.next_position = function(size_x, size_y) {
+ size_x || (size_x = 1);
+ size_y || (size_y = 1);
+ var ga = this.gridmap;
+ var cols_l = ga.length;
+ var valid_pos = [];
+ var rows_l;
+
+ for (var c = 1; c < cols_l; c++) {
+ rows_l = ga[c].length;
+ for (var r = 1; r <= rows_l; r++) {
+ var can_move_to = this.can_move_to({
+ size_x: size_x,
+ size_y: size_y
+ }, c, r);
+
+ if (can_move_to) {
+ valid_pos.push({
+ col: c,
+ row: r,
+ size_y: size_y,
+ size_x: size_x
+ });
+ }
+ }
+ }
+
+ if (valid_pos.length) {
+ return this.sort_by_row_and_col_asc(valid_pos)[0];
+ }
+ return false;
+ };
+
+
+ /**
+ * Remove a widget from the grid.
+ *
+ * @method remove_widget
+ * @param {HTMLElement} el The jQuery wrapped HTMLElement you want to remove.
+ * @param {Boolean|Function} silent If true, widgets below the removed one
+ * will not move up. If a Function is passed it will be used as callback.
+ * @param {Function} callback Function executed when the widget is removed.
+ * @return {Class} Returns the instance of the Gridster Class.
+ */
+ fn.remove_widget = function(el, silent, callback) {
+ var $el = el instanceof jQuery ? el : $(el);
+ var wgd = $el.coords().grid;
+
+ // if silent is a function assume it's a callback
+ if ($.isFunction(silent)) {
+ callback = silent;
+ silent = false;
+ }
+
+ this.cells_occupied_by_placeholder = {};
+ this.$widgets = this.$widgets.not($el);
+
+ var $nexts = this.widgets_below($el);
+
+ this.remove_from_gridmap(wgd);
+
+ $el.fadeOut($.proxy(function() {
+ $el.remove();
+
+ if (!silent) {
+ $nexts.each($.proxy(function(i, widget) {
+ this.move_widget_up( $(widget), wgd.size_y );
+ }, this));
+ }
+
+ this.set_dom_grid_height();
+
+ if (callback) {
+ callback.call(this, el);
+ }
+ }, this));
+ };
+
+
+ /**
+ * Remove all widgets from the grid.
+ *
+ * @method remove_all_widgets
+ * @param {Function} callback Function executed for each widget removed.
+ * @return {Class} Returns the instance of the Gridster Class.
+ */
+ fn.remove_all_widgets = function(callback) {
+ this.$widgets.each($.proxy(function(i, el){
+ this.remove_widget(el, true, callback);
+ }, this));
+
+ return this;
+ };
+
+
+ /**
+ * Returns a serialized array of the widgets in the grid.
+ *
+ * @method serialize
+ * @param {HTMLElement} [$widgets] The collection of jQuery wrapped
+ * HTMLElements you want to serialize. If no argument is passed all widgets
+ * will be serialized.
+ * @return {Array} Returns an Array of Objects with the data specified in
+ * the serialize_params option.
+ */
+ fn.serialize = function($widgets) {
+ $widgets || ($widgets = this.$widgets);
+ var result = [];
+ $widgets.each($.proxy(function(i, widget) {
+ result.push(this.options.serialize_params(
+ $(widget), $(widget).coords().grid ) );
+ }, this));
+
+ return result;
+ };
+
+
+ /**
+ * Returns a serialized array of the widgets that have changed their
+ * position.
+ *
+ * @method serialize_changed
+ * @return {Array} Returns an Array of Objects with the data specified in
+ * the serialize_params option.
+ */
+ fn.serialize_changed = function() {
+ return this.serialize(this.$changed);
+ };
+
+
+ /**
+ * Creates the grid coords object representing the widget a add it to the
+ * mapped array of positions.
+ *
+ * @method register_widget
+ * @return {Array} Returns the instance of the Gridster class.
+ */
+ fn.register_widget = function($el) {
+
+ var wgd = {
+ 'col': parseInt($el.attr('data-col'), 10),
+ 'row': parseInt($el.attr('data-row'), 10),
+ 'size_x': parseInt($el.attr('data-sizex'), 10),
+ 'size_y': parseInt($el.attr('data-sizey'), 10),
+ 'el': $el
+ };
+
+ if (this.options.avoid_overlapped_widgets &&
+ !this.can_move_to(
+ {size_x: wgd.size_x, size_y: wgd.size_y}, wgd.col, wgd.row)
+ ) {
+ wgd = this.next_position(wgd.size_x, wgd.size_y);
+ wgd.el = $el;
+ $el.attr({
+ 'data-col': wgd.col,
+ 'data-row': wgd.row,
+ 'data-sizex': wgd.size_x,
+ 'data-sizey': wgd.size_y
+ });
+ }
+
+ // attach Coord object to player data-coord attribute
+ $el.data('coords', $el.coords());
+
+ // Extend Coord object with grid position info
+ $el.data('coords').grid = wgd;
+
+ this.add_to_gridmap(wgd, $el);
+
+ return this;
+ };
+
+
+ /**
+ * Update in the mapped array of positions the value of cells represented by
+ * the grid coords object passed in the `grid_data` param.
+ *
+ * @param {Object} grid_data The grid coords object representing the cells
+ * to update in the mapped array.
+ * @param {HTMLElement|Boolean} value Pass `false` or the jQuery wrapped
+ * HTMLElement, depends if you want to delete an existing position or add
+ * a new one.
+ * @method update_widget_position
+ * @return {Class} Returns the instance of the Gridster Class.
+ */
+ fn.update_widget_position = function(grid_data, value) {
+ this.for_each_cell_occupied(grid_data, function(col, row) {
+ if (!this.gridmap[col]) { return this; }
+ this.gridmap[col][row] = value;
+ });
+ return this;
+ };
+
+
+ /**
+ * Remove a widget from the mapped array of positions.
+ *
+ * @method remove_from_gridmap
+ * @param {Object} grid_data The grid coords object representing the cells
+ * to update in the mapped array.
+ * @return {Class} Returns the instance of the Gridster Class.
+ */
+ fn.remove_from_gridmap = function(grid_data) {
+ return this.update_widget_position(grid_data, false);
+ };
+
+
+ /**
+ * Add a widget to the mapped array of positions.
+ *
+ * @method add_to_gridmap
+ * @param {Object} grid_data The grid coords object representing the cells
+ * to update in the mapped array.
+ * @param {HTMLElement|Boolean} value The value to set in the specified
+ * position .
+ * @return {Class} Returns the instance of the Gridster Class.
+ */
+ fn.add_to_gridmap = function(grid_data, value) {
+ this.update_widget_position(grid_data, value || grid_data.el);
+
+ if (grid_data.el) {
+ var $widgets = this.widgets_below(grid_data.el);
+ $widgets.each($.proxy(function(i, widget) {
+ this.move_widget_up( $(widget));
+ }, this));
+ }
+ };
+
+
+ /**
+ * Make widgets draggable.
+ *
+ * @uses Draggable
+ * @method draggable
+ * @return {Class} Returns the instance of the Gridster Class.
+ */
+ fn.draggable = function() {
+ var self = this;
+ var draggable_options = $.extend(true, {}, this.options.draggable, {
+ offset_left: this.options.widget_margins[0],
+ start: function(event, ui) {
+ self.$widgets.filter('.player-revert')
+ .removeClass('player-revert');
+
+ self.$player = $(this);
+ self.$helper = self.options.draggable.helper === 'clone' ?
+ $(ui.helper) : self.$player;
+ self.helper = !self.$helper.is(self.$player);
+
+ self.on_start_drag.call(self, event, ui);
+ self.$el.trigger('gridster:dragstart');
+ },
+ stop: function(event, ui) {
+ self.on_stop_drag.call(self, event, ui);
+ self.$el.trigger('gridster:dragstop');
+ },
+ drag: throttle(function(event, ui) {
+ self.on_drag.call(self, event, ui);
+ self.$el.trigger('gridster:drag');
+ }, 60)
+ });
+
+ this.drag_api = this.$el.drag(draggable_options).data('drag');
+ return this;
+ };
+
+
+ /**
+ * This function is executed when the player begins to be dragged.
+ *
+ * @method on_start_drag
+ * @param {Event} event The original browser event
+ * @param {Object} ui A prepared ui object.
+ */
+ fn.on_start_drag = function(event, ui) {
+
+ this.$helper.add(this.$player).add(this.$wrapper).addClass('dragging');
+
+ this.$player.addClass('player');
+ this.player_grid_data = this.$player.coords().grid;
+ this.placeholder_grid_data = $.extend({}, this.player_grid_data);
+
+ //set new grid height along the dragging period
+ this.$el.css('height', this.$el.height() +
+ (this.player_grid_data.size_y * this.min_widget_height));
+
+ var colliders = this.faux_grid;
+ var coords = this.$player.data('coords').coords;
+
+ this.cells_occupied_by_player = this.get_cells_occupied(
+ this.player_grid_data);
+ this.cells_occupied_by_placeholder = this.get_cells_occupied(
+ this.placeholder_grid_data);
+
+ this.last_cols = [];
+ this.last_rows = [];
+
+
+ // see jquery.collision.js
+ this.collision_api = this.$helper.collision(
+ colliders, this.options.collision);
+
+ this.$preview_holder = $(' ', {
+ 'class': 'preview-holder',
+ 'data-row': this.$player.attr('data-row'),
+ 'data-col': this.$player.attr('data-col'),
+ css: {
+ width: coords.width,
+ height: coords.height
+ }
+ }).appendTo(this.$el);
+
+ if (this.options.draggable.start) {
+ this.options.draggable.start.call(this, event, ui);
+ }
+ };
+
+
+ /**
+ * This function is executed when the player is being dragged.
+ *
+ * @method on_drag
+ * @param {Event} event The original browser event
+ * @param {Object} ui A prepared ui object.
+ */
+ fn.on_drag = function(event, ui) {
+ //break if dragstop has been fired
+ if (this.$player === null) {
+ return false;
+ }
+
+ var abs_offset = {
+ left: ui.position.left + this.baseX,
+ top: ui.position.top + this.baseY
+ };
+
+ this.colliders_data = this.collision_api.get_closest_colliders(
+ abs_offset);
+
+ this.on_overlapped_column_change(
+ this.on_start_overlapping_column,
+ this.on_stop_overlapping_column
+ );
+
+ this.on_overlapped_row_change(
+ this.on_start_overlapping_row,
+ this.on_stop_overlapping_row
+ );
+
+ if (this.helper && this.$player) {
+ this.$player.css({
+ 'left': ui.position.left,
+ 'top': ui.position.top
+ });
+ }
+
+ if (this.options.draggable.drag) {
+ this.options.draggable.drag.call(this, event, ui);
+ }
+ };
+
+ /**
+ * This function is executed when the player stops being dragged.
+ *
+ * @method on_stop_drag
+ * @param {Event} event The original browser event
+ * @param {Object} ui A prepared ui object.
+ */
+ fn.on_stop_drag = function(event, ui) {
+ this.$helper.add(this.$player).add(this.$wrapper)
+ .removeClass('dragging');
+
+ ui.position.left = ui.position.left + this.baseX;
+ ui.position.top = ui.position.top + this.baseY;
+ this.colliders_data = this.collision_api.get_closest_colliders(ui.position);
+
+ this.on_overlapped_column_change(
+ this.on_start_overlapping_column,
+ this.on_stop_overlapping_column
+ );
+
+ this.on_overlapped_row_change(
+ this.on_start_overlapping_row,
+ this.on_stop_overlapping_row
+ );
+
+ this.$player.addClass('player-revert').removeClass('player')
+ .attr({
+ 'data-col': this.placeholder_grid_data.col,
+ 'data-row': this.placeholder_grid_data.row
+ }).css({
+ 'left': '',
+ 'top': ''
+ });
+
+ this.$changed = this.$changed.add(this.$player);
+
+ this.cells_occupied_by_player = this.get_cells_occupied(
+ this.placeholder_grid_data);
+ this.set_cells_player_occupies(
+ this.placeholder_grid_data.col, this.placeholder_grid_data.row);
+
+ this.$player.coords().grid.row = this.placeholder_grid_data.row;
+ this.$player.coords().grid.col = this.placeholder_grid_data.col;
+
+ if (this.options.draggable.stop) {
+ this.options.draggable.stop.call(this, event, ui);
+ }
+
+ this.$preview_holder.remove();
+
+ this.$player = null;
+ this.$helper = null;
+ this.placeholder_grid_data = {};
+ this.player_grid_data = {};
+ this.cells_occupied_by_placeholder = {};
+ this.cells_occupied_by_player = {};
+
+ this.set_dom_grid_height();
+ };
+
+
+ /**
+ * Executes the callbacks passed as arguments when a column begins to be
+ * overlapped or stops being overlapped.
+ *
+ * @param {Function} start_callback Function executed when a new column
+ * begins to be overlapped. The column is passed as first argument.
+ * @param {Function} stop_callback Function executed when a column stops
+ * being overlapped. The column is passed as first argument.
+ * @method on_overlapped_column_change
+ * @return {Class} Returns the instance of the Gridster Class.
+ */
+ fn.on_overlapped_column_change = function(start_callback, stop_callback) {
+ if (!this.colliders_data.length) {
+ return this;
+ }
+ var cols = this.get_targeted_columns(
+ this.colliders_data[0].el.data.col);
+
+ var last_n_cols = this.last_cols.length;
+ var n_cols = cols.length;
+ var i;
+
+ for (i = 0; i < n_cols; i++) {
+ if ($.inArray(cols[i], this.last_cols) === -1) {
+ (start_callback || $.noop).call(this, cols[i]);
+ }
+ }
+
+ for (i = 0; i< last_n_cols; i++) {
+ if ($.inArray(this.last_cols[i], cols) === -1) {
+ (stop_callback || $.noop).call(this, this.last_cols[i]);
+ }
+ }
+
+ this.last_cols = cols;
+
+ return this;
+ };
+
+
+ /**
+ * Executes the callbacks passed as arguments when a row starts to be
+ * overlapped or stops being overlapped.
+ *
+ * @param {Function} start_callback Function executed when a new row begins
+ * to be overlapped. The row is passed as first argument.
+ * @param {Function} end_callback Function executed when a row stops being
+ * overlapped. The row is passed as first argument.
+ * @method on_overlapped_row_change
+ * @return {Class} Returns the instance of the Gridster Class.
+ */
+ fn.on_overlapped_row_change = function(start_callback, end_callback) {
+ if (!this.colliders_data.length) {
+ return this;
+ }
+ var rows = this.get_targeted_rows(this.colliders_data[0].el.data.row);
+ var last_n_rows = this.last_rows.length;
+ var n_rows = rows.length;
+ var i;
+
+ for (i = 0; i < n_rows; i++) {
+ if ($.inArray(rows[i], this.last_rows) === -1) {
+ (start_callback || $.noop).call(this, rows[i]);
+ }
+ }
+
+ for (i = 0; i < last_n_rows; i++) {
+ if ($.inArray(this.last_rows[i], rows) === -1) {
+ (end_callback || $.noop).call(this, this.last_rows[i]);
+ }
+ }
+
+ this.last_rows = rows;
+ };
+
+
+ /**
+ * Sets the current position of the player
+ *
+ * @param {Number} col
+ * @param {Number} row
+ * @param {Boolean} no_player
+ * @method set_player
+ * @return {object}
+ */
+ fn.set_player = function(col, row, no_player) {
+ var self = this;
+ if (!no_player) {
+ this.empty_cells_player_occupies();
+ }
+ var cell = !no_player ? self.colliders_data[0].el.data : {col: col};
+ var to_col = cell.col;
+ var to_row = row || cell.row;
+
+ this.player_grid_data = {
+ col: to_col,
+ row: to_row,
+ size_y : this.player_grid_data.size_y,
+ size_x : this.player_grid_data.size_x
+ };
+
+ this.cells_occupied_by_player = this.get_cells_occupied(
+ this.player_grid_data);
+
+ var $overlapped_widgets = this.get_widgets_overlapped(
+ this.player_grid_data);
+
+ var constraints = this.widgets_constraints($overlapped_widgets);
+
+ this.manage_movements(constraints.can_go_up, to_col, to_row);
+ this.manage_movements(constraints.can_not_go_up, to_col, to_row);
+
+ /* if there is not widgets overlapping in the new player position,
+ * update the new placeholder position. */
+ if (!$overlapped_widgets.length) {
+ var pp = this.can_go_player_up(this.player_grid_data);
+ if (pp !== false) {
+ to_row = pp;
+ }
+ this.set_placeholder(to_col, to_row);
+ }
+
+ return {
+ col: to_col,
+ row: to_row
+ };
+ };
+
+
+ /**
+ * See which of the widgets in the $widgets param collection can go to
+ * a upper row and which not.
+ *
+ * @method widgets_contraints
+ * @param {jQuery} $widgets A jQuery wrapped collection of
+ * HTMLElements.
+ * @return {object} Returns a literal Object with two keys: `can_go_up` &
+ * `can_not_go_up`. Each contains a set of HTMLElements.
+ */
+ fn.widgets_constraints = function($widgets) {
+ var $widgets_can_go_up = $([]);
+ var $widgets_can_not_go_up;
+ var wgd_can_go_up = [];
+ var wgd_can_not_go_up = [];
+
+ $widgets.each($.proxy(function(i, w) {
+ var $w = $(w);
+ var wgd = $w.coords().grid;
+ if (this.can_go_widget_up(wgd)) {
+ $widgets_can_go_up = $widgets_can_go_up.add($w);
+ wgd_can_go_up.push(wgd);
+ }else{
+ wgd_can_not_go_up.push(wgd);
+ }
+ }, this));
+
+ $widgets_can_not_go_up = $widgets.not($widgets_can_go_up);
+
+ return {
+ can_go_up: this.sort_by_row_asc(wgd_can_go_up),
+ can_not_go_up: this.sort_by_row_desc(wgd_can_not_go_up)
+ };
+ };
+
+
+ /**
+ * Sorts an Array of grid coords objects (representing the grid coords of
+ * each widget) in ascending way.
+ *
+ * @method sort_by_row_asc
+ * @param {Array} widgets Array of grid coords objects
+ * @return {Array} Returns the array sorted.
+ */
+ fn.sort_by_row_asc = function(widgets) {
+ widgets = widgets.sort(function(a, b) {
+ if (!a.row) {
+ a = $(a).coords().grid;
+ b = $(b).coords().grid;
+ }
+
+ if (a.row > b.row) {
+ return 1;
+ }
+ return -1;
+ });
+
+ return widgets;
+ };
+
+
+ /**
+ * Sorts an Array of grid coords objects (representing the grid coords of
+ * each widget) placing first the empty cells upper left.
+ *
+ * @method sort_by_row_and_col_asc
+ * @param {Array} widgets Array of grid coords objects
+ * @return {Array} Returns the array sorted.
+ */
+ fn.sort_by_row_and_col_asc = function(widgets) {
+ widgets = widgets.sort(function(a, b) {
+ if (a.row > b.row || a.row === b.row && a.col > b.col) {
+ return 1;
+ }
+ return -1;
+ });
+
+ return widgets;
+ };
+
+
+ /**
+ * Sorts an Array of grid coords objects by column (representing the grid
+ * coords of each widget) in ascending way.
+ *
+ * @method sort_by_col_asc
+ * @param {Array} widgets Array of grid coords objects
+ * @return {Array} Returns the array sorted.
+ */
+ fn.sort_by_col_asc = function(widgets) {
+ widgets = widgets.sort(function(a, b) {
+ if (a.col > b.col) {
+ return 1;
+ }
+ return -1;
+ });
+
+ return widgets;
+ };
+
+
+ /**
+ * Sorts an Array of grid coords objects (representing the grid coords of
+ * each widget) in descending way.
+ *
+ * @method sort_by_row_desc
+ * @param {Array} widgets Array of grid coords objects
+ * @return {Array} Returns the array sorted.
+ */
+ fn.sort_by_row_desc = function(widgets) {
+ widgets = widgets.sort(function(a, b) {
+ if (a.row + a.size_y < b.row + b.size_y) {
+ return 1;
+ }
+ return -1;
+ });
+ return widgets;
+ };
+
+
+ /**
+ * Sorts an Array of grid coords objects (representing the grid coords of
+ * each widget) in descending way.
+ *
+ * @method manage_movements
+ * @param {jQuery} $widgets A jQuery collection of HTMLElements
+ * representing the widgets you want to move.
+ * @param {Number} to_col The column to which we want to move the widgets.
+ * @param {Number} to_row The row to which we want to move the widgets.
+ * @return {Class} Returns the instance of the Gridster Class.
+ */
+ fn.manage_movements = function($widgets, to_col, to_row) {
+ $.each($widgets, $.proxy(function(i, w) {
+ var wgd = w;
+ var $w = wgd.el;
+
+ var can_go_widget_up = this.can_go_widget_up(wgd);
+
+ if (can_go_widget_up) {
+ //target CAN go up
+ //so move widget up
+ this.move_widget_to($w, can_go_widget_up);
+ this.set_placeholder(to_col, can_go_widget_up + wgd.size_y);
+
+ } else {
+ //target can't go up
+ var can_go_player_up = this.can_go_player_up(
+ this.player_grid_data);
+
+ if (!can_go_player_up) {
+ // target can't go up
+ // player cant't go up
+ // so we need to move widget down to a position that dont
+ // overlaps player
+ var y = (to_row + this.player_grid_data.size_y) - wgd.row;
+
+ this.move_widget_down($w, y);
+ this.set_placeholder(to_col, to_row);
+ }
+ }
+ }, this));
+
+ return this;
+ };
+
+ /**
+ * Determines if there is a widget in the row and col given. Or if the
+ * HTMLElement passed as first argument is the player.
+ *
+ * @method is_player
+ * @param {Number|HTMLElement} col_or_el A jQuery wrapped collection of
+ * HTMLElements.
+ * @param {Number} [row] The column to which we want to move the widgets.
+ * @return {Boolean} Returns true or false.
+ */
+ fn.is_player = function(col_or_el, row) {
+ if (row && !this.gridmap[col_or_el]) { return false; }
+ var $w = row ? this.gridmap[col_or_el][row] : col_or_el;
+ return $w && ($w.is(this.$player) || $w.is(this.$helper));
+ };
+
+
+ /**
+ * Determines if the widget that is being dragged is currently over the row
+ * and col given.
+ *
+ * @method is_player_in
+ * @param {Number} col The column to check.
+ * @param {Number} row The row to check.
+ * @return {Boolean} Returns true or false.
+ */
+ fn.is_player_in = function(col, row) {
+ var c = this.cells_occupied_by_player || {};
+ return $.inArray(col, c.cols) >= 0 && $.inArray(row, c.rows) >= 0;
+ };
+
+
+ /**
+ * Determines if the placeholder is currently over the row and col given.
+ *
+ * @method is_placeholder_in
+ * @param {Number} col The column to check.
+ * @param {Number} row The row to check.
+ * @return {Boolean} Returns true or false.
+ */
+ fn.is_placeholder_in = function(col, row) {
+ var c = this.cells_occupied_by_placeholder || {};
+ return this.is_placeholder_in_col(col) && $.inArray(row, c.rows) >= 0;
+ };
+
+
+ /**
+ * Determines if the placeholder is currently over the column given.
+ *
+ * @method is_placeholder_in_col
+ * @param {Number} col The column to check.
+ * @return {Boolean} Returns true or false.
+ */
+ fn.is_placeholder_in_col = function(col) {
+ var c = this.cells_occupied_by_placeholder || [];
+ return $.inArray(col, c.cols) >= 0;
+ };
+
+
+ /**
+ * Determines if the cell represented by col and row params is empty.
+ *
+ * @method is_empty
+ * @param {Number} col The column to check.
+ * @param {Number} row The row to check.
+ * @return {Boolean} Returns true or false.
+ */
+ fn.is_empty = function(col, row) {
+ if (typeof this.gridmap[col] !== 'undefined' &&
+ typeof this.gridmap[col][row] !== 'undefined' &&
+ this.gridmap[col][row] === false
+ ) {
+ return true;
+ }
+ return false;
+ };
+
+
+ /**
+ * Determines if the cell represented by col and row params is occupied.
+ *
+ * @method is_occupied
+ * @param {Number} col The column to check.
+ * @param {Number} row The row to check.
+ * @return {Boolean} Returns true or false.
+ */
+ fn.is_occupied = function(col, row) {
+ if (!this.gridmap[col]) {
+ return false;
+ }
+
+ if (this.gridmap[col][row]) {
+ return true;
+ }
+ return false;
+ };
+
+
+ /**
+ * Determines if there is a widget in the cell represented by col/row params.
+ *
+ * @method is_widget
+ * @param {Number} col The column to check.
+ * @param {Number} row The row to check.
+ * @return {Boolean|HTMLElement} Returns false if there is no widget,
+ * else returns the jQuery HTMLElement
+ */
+ fn.is_widget = function(col, row) {
+ var cell = this.gridmap[col];
+ if (!cell) {
+ return false;
+ }
+
+ cell = cell[row];
+
+ if (cell) {
+ return cell;
+ }
+
+ return false;
+ };
+
+
+ /**
+ * Determines if there is a widget in the cell represented by col/row
+ * params and if this is under the widget that is being dragged.
+ *
+ * @method is_widget_under_player
+ * @param {Number} col The column to check.
+ * @param {Number} row The row to check.
+ * @return {Boolean} Returns true or false.
+ */
+ fn.is_widget_under_player = function(col, row) {
+ if (this.is_widget(col, row)) {
+ return this.is_player_in(col, row);
+ }
+ return false;
+ };
+
+
+ /**
+ * Get widgets overlapping with the player or with the object passed
+ * representing the grid cells.
+ *
+ * @method get_widgets_under_player
+ * @return {HTMLElement} Returns a jQuery collection of HTMLElements
+ */
+ fn.get_widgets_under_player = function(cells) {
+ cells || (cells = this.cells_occupied_by_player || {cols: [], rows: []});
+ var $widgets = $([]);
+
+ $.each(cells.cols, $.proxy(function(i, col) {
+ $.each(cells.rows, $.proxy(function(i, row) {
+ if(this.is_widget(col, row)) {
+ $widgets = $widgets.add(this.gridmap[col][row]);
+ }
+ }, this));
+ }, this));
+
+ return $widgets;
+ };
+
+
+ /**
+ * Put placeholder at the row and column specified.
+ *
+ * @method set_placeholder
+ * @param {Number} col The column to which we want to move the
+ * placeholder.
+ * @param {Number} row The row to which we want to move the
+ * placeholder.
+ * @return {Class} Returns the instance of the Gridster Class.
+ */
+ fn.set_placeholder = function(col, row) {
+ var phgd = $.extend({}, this.placeholder_grid_data);
+ var $nexts = this.widgets_below({
+ col: phgd.col,
+ row: phgd.row,
+ size_y: phgd.size_y,
+ size_x: phgd.size_x
+ });
+
+ // Prevents widgets go out of the grid
+ var right_col = (col + phgd.size_x - 1);
+ if (right_col > this.cols) {
+ col = col - (right_col - col);
+ }
+
+ var moved_down = this.placeholder_grid_data.row < row;
+ var changed_column = this.placeholder_grid_data.col !== col;
+
+ this.placeholder_grid_data.col = col;
+ this.placeholder_grid_data.row = row;
+
+ this.cells_occupied_by_placeholder = this.get_cells_occupied(
+ this.placeholder_grid_data);
+
+ this.$preview_holder.attr({
+ 'data-row' : row,
+ 'data-col' : col
+ });
+
+ if (moved_down || changed_column) {
+ $nexts.each($.proxy(function(i, widget) {
+ this.move_widget_up(
+ $(widget), this.placeholder_grid_data.col - col + phgd.size_y);
+ }, this));
+ }
+
+
+ var $widgets_under_ph = this.get_widgets_under_player(this.cells_occupied_by_placeholder);
+ if ($widgets_under_ph.length) {
+ $widgets_under_ph.each($.proxy(function(i, widget) {
+ var $w = $(widget);
+ this.move_widget_down(
+ $w, row + phgd.size_y - $w.data('coords').grid.row);
+ }, this));
+ }
+
+ };
+
+
+ /**
+ * Determines whether the player can move to a position above.
+ *
+ * @method can_go_player_up
+ * @param {Object} widget_grid_data The actual grid coords object of the
+ * player.
+ * @return {Number|Boolean} If the player can be moved to an upper row
+ * returns the row number, else returns false.
+ */
+ fn.can_go_player_up = function(widget_grid_data) {
+ var p_bottom_row = widget_grid_data.row + widget_grid_data.size_y - 1;
+ var result = true;
+ var upper_rows = [];
+ var min_row = 10000;
+ var $widgets_under_player = this.get_widgets_under_player();
+
+ /* generate an array with columns as index and array with upper rows
+ * empty as value */
+ this.for_each_column_occupied(widget_grid_data, function(tcol) {
+ var grid_col = this.gridmap[tcol];
+ var r = p_bottom_row + 1;
+ upper_rows[tcol] = [];
+
+ while (--r > 0) {
+ if (this.is_empty(tcol, r) || this.is_player(tcol, r) ||
+ this.is_widget(tcol, r) &&
+ grid_col[r].is($widgets_under_player)
+ ) {
+ upper_rows[tcol].push(r);
+ min_row = r < min_row ? r : min_row;
+ }else{
+ break;
+ }
+ }
+
+ if (upper_rows[tcol].length === 0) {
+ result = false;
+ return true; //break
+ }
+
+ upper_rows[tcol].sort();
+ });
+
+ if (!result) { return false; }
+
+ return this.get_valid_rows(widget_grid_data, upper_rows, min_row);
+ };
+
+
+ /**
+ * Determines whether a widget can move to a position above.
+ *
+ * @method can_go_widget_up
+ * @param {Object} widget_grid_data The actual grid coords object of the
+ * widget we want to check.
+ * @return {Number|Boolean} If the widget can be moved to an upper row
+ * returns the row number, else returns false.
+ */
+ fn.can_go_widget_up = function(widget_grid_data) {
+ var p_bottom_row = widget_grid_data.row + widget_grid_data.size_y - 1;
+ var result = true;
+ var upper_rows = [];
+ var min_row = 10000;
+
+ /* generate an array with columns as index and array with topmost rows
+ * empty as value */
+ this.for_each_column_occupied(widget_grid_data, function(tcol) {
+ var grid_col = this.gridmap[tcol];
+ upper_rows[tcol] = [];
+
+ var r = p_bottom_row + 1;
+ // iterate over each row
+ while (--r > 0) {
+ if (this.is_widget(tcol, r) && !this.is_player_in(tcol, r)) {
+ if (!grid_col[r].is(widget_grid_data.el)) {
+ break;
+ }
+ }
+
+ if (!this.is_player(tcol, r) &&
+ !this.is_placeholder_in(tcol, r) &&
+ !this.is_player_in(tcol, r)) {
+ upper_rows[tcol].push(r);
+ }
+
+ if (r < min_row) {
+ min_row = r;
+ }
+ }
+
+ if (upper_rows[tcol].length === 0) {
+ result = false;
+ return true; //break
+ }
+
+ upper_rows[tcol].sort();
+ });
+
+ if (!result) { return false; }
+
+ return this.get_valid_rows(widget_grid_data, upper_rows, min_row);
+ };
+
+
+ /**
+ * Search a valid row for the widget represented by `widget_grid_data' in
+ * the `upper_rows` array. Iteration starts from row specified in `min_row`.
+ *
+ * @method get_valid_rows
+ * @param {Object} widget_grid_data The actual grid coords object of the
+ * player.
+ * @param {Array} upper_rows An array with columns as index and arrays
+ * of valid rows as values.
+ * @param {Number} min_row The upper row from which the iteration will start.
+ * @return {Number|Boolean} Returns the upper row valid from the `upper_rows`
+ * for the widget in question.
+ */
+ fn.get_valid_rows = function(widget_grid_data, upper_rows, min_row) {
+ var p_top_row = widget_grid_data.row;
+ var p_bottom_row = widget_grid_data.row + widget_grid_data.size_y - 1;
+ var size_y = widget_grid_data.size_y;
+ var r = min_row - 1;
+ var valid_rows = [];
+
+ while (++r <= p_bottom_row ) {
+ var common = true;
+ $.each(upper_rows, function(col, rows) {
+ if ($.isArray(rows) && $.inArray(r, rows) === -1) {
+ common = false;
+ }
+ });
+
+ if (common === true) {
+ valid_rows.push(r);
+ if (valid_rows.length === size_y) {
+ break;
+ }
+ }
+ }
+
+ var new_row = false;
+ if (size_y === 1) {
+ if (valid_rows[0] !== p_top_row) {
+ new_row = valid_rows[0] || false;
+ }
+ }else{
+ if (valid_rows[0] !== p_top_row) {
+ new_row = this.get_consecutive_numbers_index(
+ valid_rows, size_y);
+ }
+ }
+
+ return new_row;
+ };
+
+
+ fn.get_consecutive_numbers_index = function(arr, size_y) {
+ var max = arr.length;
+ var result = [];
+ var first = true;
+ var prev = -1; // or null?
+
+ for (var i=0; i < max; i++) {
+ if (first || arr[i] === prev + 1) {
+ result.push(i);
+ if (result.length === size_y) {
+ break;
+ }
+ first = false;
+ }else{
+ result = [];
+ first = true;
+ }
+
+ prev = arr[i];
+ }
+
+ return result.length >= size_y ? arr[result[0]] : false;
+ };
+
+
+ /**
+ * Get widgets overlapping with the player.
+ *
+ * @method get_widgets_overlapped
+ * @return {jQuery} Returns a jQuery collection of HTMLElements.
+ */
+ fn.get_widgets_overlapped = function() {
+ var $w;
+ var $widgets = $([]);
+ var used = [];
+ var rows_from_bottom = this.cells_occupied_by_player.rows.slice(0);
+ rows_from_bottom.reverse();
+
+ $.each(this.cells_occupied_by_player.cols, $.proxy(function(i, col) {
+ $.each(rows_from_bottom, $.proxy(function(i, row) {
+ // if there is a widget in the player position
+ if (!this.gridmap[col]) { return true; } //next iteration
+ var $w = this.gridmap[col][row];
+ if (this.is_occupied(col, row) && !this.is_player($w) &&
+ $.inArray($w, used) === -1
+ ) {
+ $widgets = $widgets.add($w);
+ used.push($w);
+ }
+
+ }, this));
+ }, this));
+
+ return $widgets;
+ };
+
+
+ /**
+ * This callback is executed when the player begins to collide with a column.
+ *
+ * @method on_start_overlapping_column
+ * @param {Number} col The collided column.
+ * @return {jQuery} Returns a jQuery collection of HTMLElements.
+ */
+ fn.on_start_overlapping_column = function(col) {
+ this.set_player(col, false);
+ };
+
+
+ /**
+ * A callback executed when the player begins to collide with a row.
+ *
+ * @method on_start_overlapping_row
+ * @param {Number} row The collided row.
+ * @return {jQuery} Returns a jQuery collection of HTMLElements.
+ */
+ fn.on_start_overlapping_row = function(row) {
+ this.set_player(false, row);
+ };
+
+
+ /**
+ * A callback executed when the the player ends to collide with a column.
+ *
+ * @method on_stop_overlapping_column
+ * @param {Number} col The collided row.
+ * @return {jQuery} Returns a jQuery collection of HTMLElements.
+ */
+ fn.on_stop_overlapping_column = function(col) {
+ this.set_player(col, false);
+
+ var self = this;
+ this.for_each_widget_below(col, this.cells_occupied_by_player.rows[0],
+ function(tcol, trow) {
+ self.move_widget_up(this, self.player_grid_data.size_y);
+ });
+ };
+
+
+ /**
+ * This callback is executed when the player ends to collide with a row.
+ *
+ * @method on_stop_overlapping_row
+ * @param {Number} row The collided row.
+ * @return {jQuery} Returns a jQuery collection of HTMLElements.
+ */
+ fn.on_stop_overlapping_row = function(row) {
+ this.set_player(false, row);
+
+ var self = this;
+ var cols = this.cells_occupied_by_player.cols;
+ for (var c = 0, cl = cols.length; c < cl; c++) {
+ this.for_each_widget_below(cols[c], row, function(tcol, trow) {
+ self.move_widget_up(this, self.player_grid_data.size_y);
+ });
+ }
+ };
+
+
+ /**
+ * Move a widget to a specific row. The cell or cells must be empty.
+ * If the widget has widgets below, all of these widgets will be moved also
+ * if they can.
+ *
+ * @method move_widget_to
+ * @param {HTMLElement} $widget The jQuery wrapped HTMLElement of the
+ * widget is going to be moved.
+ * @return {Class} Returns the instance of the Gridster Class.
+ */
+ fn.move_widget_to = function($widget, row) {
+ var self = this;
+ var widget_grid_data = $widget.coords().grid;
+ var diff = row - widget_grid_data.row;
+ var $next_widgets = this.widgets_below($widget);
+
+ var can_move_to_new_cell = this.can_move_to(
+ widget_grid_data, widget_grid_data.col, row, $widget);
+
+ if (can_move_to_new_cell === false) {
+ return false;
+ }
+
+ this.remove_from_gridmap(widget_grid_data);
+ widget_grid_data.row = row;
+ this.add_to_gridmap(widget_grid_data);
+ $widget.attr('data-row', row);
+ this.$changed = this.$changed.add($widget);
+
+
+ $next_widgets.each(function(i, widget) {
+ var $w = $(widget);
+ var wgd = $w.coords().grid;
+ var can_go_up = self.can_go_widget_up(wgd);
+ if (can_go_up && can_go_up !== wgd.row) {
+ self.move_widget_to($w, can_go_up);
+ }
+ });
+
+ return this;
+ };
+
+
+ /**
+ * Move up the specified widget and all below it.
+ *
+ * @method move_widget_up
+ * @param {HTMLElement} $widget The widget you want to move.
+ * @param {Number} [y_units] The number of cells that the widget has to move.
+ * @return {Class} Returns the instance of the Gridster Class.
+ */
+ fn.move_widget_up = function($widget, y_units) {
+ var el_grid_data = $widget.coords().grid;
+ var actual_row = el_grid_data.row;
+ var moved = [];
+ var can_go_up = true;
+ y_units || (y_units = 1);
+
+ if (!this.can_go_up($widget)) { return false; } //break;
+
+ this.for_each_column_occupied(el_grid_data, function(col) {
+ // can_go_up
+ if ($.inArray($widget, moved) === -1) {
+ var widget_grid_data = $widget.coords().grid;
+ var next_row = actual_row - y_units;
+ next_row = this.can_go_up_to_row(
+ widget_grid_data, col, next_row);
+
+ if (!next_row) {
+ return true;
+ }
+
+ var $next_widgets = this.widgets_below($widget);
+
+ this.remove_from_gridmap(widget_grid_data);
+ widget_grid_data.row = next_row;
+ this.add_to_gridmap(widget_grid_data);
+ $widget.attr('data-row', widget_grid_data.row);
+ this.$changed = this.$changed.add($widget);
+
+ moved.push($widget);
+
+ $next_widgets.each($.proxy(function(i, widget) {
+ this.move_widget_up($(widget), y_units);
+ }, this));
+ }
+ });
+
+ };
+
+
+ /**
+ * Move down the specified widget and all below it.
+ *
+ * @method move_widget_down
+ * @param {jQuery} $widget The jQuery object representing the widget
+ * you want to move.
+ * @param {Number} y_units The number of cells that the widget has to move.
+ * @return {Class} Returns the instance of the Gridster Class.
+ */
+ fn.move_widget_down = function($widget, y_units) {
+ var el_grid_data = $widget.coords().grid;
+ var actual_row = el_grid_data.row;
+ var moved = [];
+ var y_diff = y_units;
+
+ if (!$widget) { return false; }
+
+ if ($.inArray($widget, moved) === -1) {
+
+ var widget_grid_data = $widget.coords().grid;
+ var next_row = actual_row + y_units;
+ var $next_widgets = this.widgets_below($widget);
+
+ this.remove_from_gridmap(widget_grid_data);
+
+ $next_widgets.each($.proxy(function(i, widget) {
+ var $w = $(widget);
+ var wd = $w.coords().grid;
+ var tmp_y = this.displacement_diff(
+ wd, widget_grid_data, y_diff);
+
+ if (tmp_y > 0) {
+ this.move_widget_down($w, tmp_y);
+ }
+ }, this));
+
+ widget_grid_data.row = next_row;
+ this.update_widget_position(widget_grid_data, $widget);
+ $widget.attr('data-row', widget_grid_data.row);
+ this.$changed = this.$changed.add($widget);
+
+ moved.push($widget);
+ }
+ };
+
+
+ /**
+ * Check if the widget can move to the specified row, else returns the
+ * upper row possible.
+ *
+ * @method can_go_up_to_row
+ * @param {Number} widget_grid_data The current grid coords object of the
+ * widget.
+ * @param {Number} col The target column.
+ * @param {Number} row The target row.
+ * @return {Boolean|Number} Returns the row number if the widget can move
+ * to the target position, else returns false.
+ */
+ fn.can_go_up_to_row = function(widget_grid_data, col, row) {
+ var ga = this.gridmap;
+ var result = true;
+ var urc = []; // upper_rows_in_columns
+ var actual_row = widget_grid_data.row;
+ var r;
+
+ /* generate an array with columns as index and array with
+ * upper rows empty in the column */
+ this.for_each_column_occupied(widget_grid_data, function(tcol) {
+ var grid_col = ga[tcol];
+ urc[tcol] = [];
+
+ r = actual_row;
+ while (r--) {
+ if (this.is_empty(tcol, r) &&
+ !this.is_placeholder_in(tcol, r)
+ ) {
+ urc[tcol].push(r);
+ }else{
+ break;
+ }
+ }
+
+ if (!urc[tcol].length) {
+ result = false;
+ return true;
+ }
+
+ });
+
+ if (!result) { return false; }
+
+ /* get common rows starting from upper position in all the columns
+ * that widget occupies */
+ r = row;
+ for (r = 1; r < actual_row; r++) {
+ var common = true;
+
+ for (var uc = 0, ucl = urc.length; uc < ucl; uc++) {
+ if (urc[uc] && $.inArray(r, urc[uc]) === -1) {
+ common = false;
+ }
+ }
+
+ if (common === true) {
+ result = r;
+ break;
+ }
+ }
+
+ return result;
+ };
+
+
+ fn.displacement_diff = function(widget_grid_data, parent_bgd, y_units) {
+ var actual_row = widget_grid_data.row;
+ var diffs = [];
+ var parent_max_y = parent_bgd.row + parent_bgd.size_y;
+
+ this.for_each_column_occupied(widget_grid_data, function(col) {
+ var temp_y_units = 0;
+
+ for (var r = parent_max_y; r < actual_row; r++) {
+ if (this.is_empty(col, r)) {
+ temp_y_units = temp_y_units + 1;
+ }
+ }
+
+ diffs.push(temp_y_units);
+ });
+
+ var max_diff = Math.max.apply(Math, diffs);
+ y_units = (y_units - max_diff);
+
+ return y_units > 0 ? y_units : 0;
+ };
+
+
+ /**
+ * Get widgets below a widget.
+ *
+ * @method widgets_below
+ * @param {HTMLElement} $el The jQuery wrapped HTMLElement.
+ * @return {jQuery} A jQuery collection of HTMLElements.
+ */
+ fn.widgets_below = function($el) {
+ var el_grid_data = $.isPlainObject($el) ? $el : $el.coords().grid;
+ var self = this;
+ var ga = this.gridmap;
+ var next_row = el_grid_data.row + el_grid_data.size_y - 1;
+ var $nexts = $([]);
+
+ this.for_each_column_occupied(el_grid_data, function(col) {
+ self.for_each_widget_below(col, next_row, function(tcol, trow) {
+ if (!self.is_player(this) && $.inArray(this, $nexts) === -1) {
+ $nexts = $nexts.add(this);
+ return true; // break
+ }
+ });
+ });
+
+ return this.sort_by_row_asc($nexts);
+ };
+
+
+ /**
+ * Update the array of mapped positions with the new player position.
+ *
+ * @method set_cells_player_occupies
+ * @param {Number} col The new player col.
+ * @param {Number} col The new player row.
+ * @return {Class} Returns the instance of the Gridster Class.
+ */
+ fn.set_cells_player_occupies = function(col, row) {
+ this.remove_from_gridmap(this.placeholder_grid_data);
+ this.placeholder_grid_data.col = col;
+ this.placeholder_grid_data.row = row;
+ this.add_to_gridmap(this.placeholder_grid_data, this.$player);
+ return this;
+ };
+
+
+ /**
+ * Remove from the array of mapped positions the reference to the player.
+ *
+ * @method empty_cells_player_occupies
+ * @return {Class} Returns the instance of the Gridster Class.
+ */
+ fn.empty_cells_player_occupies = function() {
+ this.remove_from_gridmap(this.placeholder_grid_data);
+ return this;
+ };
+
+
+ fn.can_go_up = function($el) {
+ var el_grid_data = $el.coords().grid;
+ var initial_row = el_grid_data.row;
+ var prev_row = initial_row - 1;
+ var ga = this.gridmap;
+ var upper_rows_by_column = [];
+
+ var result = true;
+ if (initial_row === 1) { return false; }
+
+ this.for_each_column_occupied(el_grid_data, function(col) {
+ var $w = this.is_widget(col, prev_row);
+
+ if (this.is_occupied(col, prev_row) ||
+ this.is_player(col, prev_row) ||
+ this.is_placeholder_in(col, prev_row) ||
+ this.is_player_in(col, prev_row)
+ ) {
+ result = false;
+ return true; //break
+ }
+ });
+
+ return result;
+ };
+
+
+
+ /**
+ * Check if it's possible to move a widget to a specific col/row. It takes
+ * into account the dimensions (`size_y` and `size_x` attrs. of the grid
+ * coords object) the widget occupies.
+ *
+ * @method can_move_to
+ * @param {Object} widget_grid_data The grid coords object that represents
+ * the widget.
+ * @param {Object} col The col to check.
+ * @param {Object} row The row to check.
+ * @param {Number} [max_row] The max row allowed.
+ * @return {Boolean} Returns true if all cells are empty, else return false.
+ */
+ fn.can_move_to = function(widget_grid_data, col, row, max_row) {
+ var ga = this.gridmap;
+ var $w = widget_grid_data.el;
+ var future_wd = {
+ size_y: widget_grid_data.size_y,
+ size_x: widget_grid_data.size_x,
+ col: col,
+ row: row
+ };
+ var result = true;
+
+ //Prevents widgets go out of the grid
+ var right_col = col + widget_grid_data.size_x - 1;
+ if (right_col > this.cols) {
+ return false;
+ }
+
+ if (max_row && max_row < row + widget_grid_data.size_y - 1) {
+ return false;
+ }
+
+ this.for_each_cell_occupied(future_wd, function(tcol, trow) {
+ var $tw = this.is_widget(tcol, trow);
+ if ($tw && (!widget_grid_data.el || $tw.is($w))) {
+ result = false;
+ }
+ });
+
+ return result;
+ };
+
+
+ /**
+ * Given the leftmost column returns all columns that are overlapping
+ * with the player.
+ *
+ * @method get_targeted_columns
+ * @param {Number} [from_col] The leftmost column.
+ * @return {Array} Returns an array with column numbers.
+ */
+ fn.get_targeted_columns = function(from_col) {
+ var max = (from_col || this.player_grid_data.col) +
+ (this.player_grid_data.size_x - 1);
+ var cols = [];
+ for (var col = from_col; col <= max; col++) {
+ cols.push(col);
+ }
+ return cols;
+ };
+
+
+ /**
+ * Given the upper row returns all rows that are overlapping with the player.
+ *
+ * @method get_targeted_rows
+ * @param {Number} [from_row] The upper row.
+ * @return {Array} Returns an array with row numbers.
+ */
+ fn.get_targeted_rows = function(from_row) {
+ var max = (from_row || this.player_grid_data.row) +
+ (this.player_grid_data.size_y - 1);
+ var rows = [];
+ for (var row = from_row; row <= max; row++) {
+ rows.push(row);
+ }
+ return rows;
+ };
+
+ /**
+ * Get all columns and rows that a widget occupies.
+ *
+ * @method get_cells_occupied
+ * @param {Object} el_grid_data The grid coords object of the widget.
+ * @return {Object} Returns an object like `{ cols: [], rows: []}`.
+ */
+ fn.get_cells_occupied = function(el_grid_data) {
+ var cells = { cols: [], rows: []};
+ var i;
+ if (arguments[1] instanceof jQuery) {
+ el_grid_data = arguments[1].coords().grid;
+ }
+
+ for (i = 0; i < el_grid_data.size_x; i++) {
+ var col = el_grid_data.col + i;
+ cells.cols.push(col);
+ }
+
+ for (i = 0; i < el_grid_data.size_y; i++) {
+ var row = el_grid_data.row + i;
+ cells.rows.push(row);
+ }
+
+ return cells;
+ };
+
+
+ /**
+ * Iterate over the cells occupied by a widget executing a function for
+ * each one.
+ *
+ * @method for_each_cell_occupied
+ * @param {Object} el_grid_data The grid coords object that represents the
+ * widget.
+ * @param {Function} callback The function to execute on each column
+ * iteration. Column and row are passed as arguments.
+ * @return {Class} Returns the instance of the Gridster Class.
+ */
+ fn.for_each_cell_occupied = function(grid_data, callback) {
+ this.for_each_column_occupied(grid_data, function(col) {
+ this.for_each_row_occupied(grid_data, function(row) {
+ callback.call(this, col, row);
+ });
+ });
+ return this;
+ };
+
+
+ /**
+ * Iterate over the columns occupied by a widget executing a function for
+ * each one.
+ *
+ * @method for_each_column_occupied
+ * @param {Object} el_grid_data The grid coords object that represents
+ * the widget.
+ * @param {Function} callback The function to execute on each column
+ * iteration. The column number is passed as first argument.
+ * @return {Class} Returns the instance of the Gridster Class.
+ */
+ fn.for_each_column_occupied = function(el_grid_data, callback) {
+ for (var i = 0; i < el_grid_data.size_x; i++) {
+ var col = el_grid_data.col + i;
+ callback.call(this, col, el_grid_data);
+ }
+ };
+
+
+ /**
+ * Iterate over the rows occupied by a widget executing a function for
+ * each one.
+ *
+ * @method for_each_row_occupied
+ * @param {Object} el_grid_data The grid coords object that represents
+ * the widget.
+ * @param {Function} callback The function to execute on each column
+ * iteration. The row number is passed as first argument.
+ * @return {Class} Returns the instance of the Gridster Class.
+ */
+ fn.for_each_row_occupied = function(el_grid_data, callback) {
+ for (var i = 0; i < el_grid_data.size_y; i++) {
+ var row = el_grid_data.row + i;
+ callback.call(this, row, el_grid_data);
+ }
+ };
+
+
+
+ fn._traversing_widgets = function(type, direction, col, row, callback) {
+ var ga = this.gridmap;
+ if (!ga[col]) { return; }
+
+ var cr, max;
+ var action = type + '/' + direction;
+ if (arguments[2] instanceof jQuery) {
+ var el_grid_data = arguments[2].coords().grid;
+ col = el_grid_data.col;
+ row = el_grid_data.row;
+ callback = arguments[3];
+ }
+ var matched = [];
+ var trow = row;
+
+
+ var methods = {
+ 'for_each/above': function() {
+ while (trow--) {
+ if (trow > 0 && this.is_widget(col, trow) &&
+ $.inArray(ga[col][trow], matched) === -1
+ ) {
+ cr = callback.call(ga[col][trow], col, trow);
+ matched.push(ga[col][trow]);
+ if (cr) { break; }
+ }
+ }
+ },
+ 'for_each/below': function() {
+ for (trow = row + 1, max = ga[col].length; trow < max; trow++) {
+ if (this.is_widget(col, trow) &&
+ $.inArray(ga[col][trow], matched) === -1
+ ) {
+ cr = callback.call(ga[col][trow], col, trow);
+ matched.push(ga[col][trow]);
+ if (cr) { break; }
+ }
+ }
+ }
+ };
+
+ if (methods[action]) {
+ methods[action].call(this);
+ }
+ };
+
+
+ /**
+ * Iterate over each widget above the column and row specified.
+ *
+ * @method for_each_widget_above
+ * @param {Number} col The column to start iterating.
+ * @param {Number} row The row to start iterating.
+ * @param {Function} callback The function to execute on each widget
+ * iteration. The value of `this` inside the function is the jQuery
+ * wrapped HTMLElement.
+ * @return {Class} Returns the instance of the Gridster Class.
+ */
+ fn.for_each_widget_above = function(col, row, callback) {
+ this._traversing_widgets('for_each', 'above', col, row, callback);
+ return this;
+ };
+
+
+ /**
+ * Iterate over each widget below the column and row specified.
+ *
+ * @method for_each_widget_below
+ * @param {Number} col The column to start iterating.
+ * @param {Number} row The row to start iterating.
+ * @param {Function} callback The function to execute on each widget
+ * iteration. The value of `this` inside the function is the jQuery wrapped
+ * HTMLElement.
+ * @return {Class} Returns the instance of the Gridster Class.
+ */
+ fn.for_each_widget_below = function(col, row, callback) {
+ this._traversing_widgets('for_each', 'below', col, row, callback);
+ return this;
+ };
+
+
+ /**
+ * Returns the highest occupied cell in the grid.
+ *
+ * @method get_highest_occupied_cell
+ * @return {Object} Returns an object with `col` and `row` numbers.
+ */
+ fn.get_highest_occupied_cell = function() {
+ var r;
+ var gm = this.gridmap;
+ var rows = [];
+ var row_in_col = [];
+ for (var c = gm.length - 1; c >= 1; c--) {
+ for (r = gm[c].length - 1; r >= 1; r--) {
+ if (this.is_widget(c, r)) {
+ rows.push(r);
+ row_in_col[r] = c;
+ break;
+ }
+ }
+ }
+
+ var highest_row = Math.max.apply(Math, rows);
+
+ this.highest_occupied_cell = {
+ col: row_in_col[highest_row],
+ row: highest_row
+ };
+
+ return this.highest_occupied_cell;
+ };
+
+
+ fn.get_widgets_from = function(col, row) {
+ var ga = this.gridmap;
+ var $widgets = $();
+
+ if (col) {
+ $widgets = $widgets.add(
+ this.$widgets.filter(function() {
+ var tcol = $(this).attr('data-col');
+ return (tcol === col || tcol > col);
+ })
+ );
+ }
+
+ if (row) {
+ $widgets = $widgets.add(
+ this.$widgets.filter(function() {
+ var trow = $(this).attr('data-row');
+ return (trow === row || trow > row);
+ })
+ );
+ }
+
+ return $widgets;
+ };
+
+
+ /**
+ * Set the current height of the parent grid.
+ *
+ * @method set_dom_grid_height
+ * @return {Object} Returns the instance of the Gridster class.
+ */
+ fn.set_dom_grid_height = function() {
+ var r = this.get_highest_occupied_cell().row;
+ this.$el.css('height', r * this.min_widget_height);
+ return this;
+ };
+
+
+ /**
+ * It generates the neccessary styles to position the widgets.
+ *
+ * @method generate_stylesheet
+ * @param {Number} rows Number of columns.
+ * @param {Number} cols Number of rows.
+ * @return {Object} Returns the instance of the Gridster class.
+ */
+ fn.generate_stylesheet = function(opts) {
+ var styles = '';
+ var max_size_x = this.options.max_size_x;
+ var max_rows = 0;
+ var max_cols = 0;
+ var i;
+ var rules;
+
+ opts || (opts = {});
+ opts.cols || (opts.cols = this.cols);
+ opts.rows || (opts.rows = this.rows);
+ opts.namespace || (opts.namespace = this.options.namespace);
+ opts.widget_base_dimensions ||
+ (opts.widget_base_dimensions = this.options.widget_base_dimensions);
+ opts.widget_margins ||
+ (opts.widget_margins = this.options.widget_margins);
+ opts.min_widget_width = (opts.widget_margins[0] * 2) +
+ opts.widget_base_dimensions[0];
+ opts.min_widget_height = (opts.widget_margins[1] * 2) +
+ opts.widget_base_dimensions[1];
+
+ // don't duplicate stylesheets for the same configuration
+ var serialized_opts = $.param(opts);
+ if ($.inArray(serialized_opts, Gridster.generated_stylesheets) >= 0) {
+ return false;
+ }
+
+ Gridster.generated_stylesheets.push(serialized_opts);
+
+ /* generate CSS styles for cols */
+ for (i = opts.cols; i >= 0; i--) {
+ styles += (opts.namespace + ' [data-col="'+ (i + 1) + '"] { left:' +
+ ((i * opts.widget_base_dimensions[0]) +
+ (i * opts.widget_margins[0]) +
+ ((i + 1) * opts.widget_margins[0])) + 'px;} ');
+ }
+
+ /* generate CSS styles for rows */
+ for (i = opts.rows; i >= 0; i--) {
+ styles += (opts.namespace + ' [data-row="' + (i + 1) + '"] { top:' +
+ ((i * opts.widget_base_dimensions[1]) +
+ (i * opts.widget_margins[1]) +
+ ((i + 1) * opts.widget_margins[1]) ) + 'px;} ');
+ }
+
+ for (var y = 1; y <= opts.rows; y++) {
+ styles += (opts.namespace + ' [data-sizey="' + y + '"] { height:' +
+ (y * opts.widget_base_dimensions[1] +
+ (y - 1) * (opts.widget_margins[1] * 2)) + 'px;}');
+ }
+
+ for (var x = 1; x <= max_size_x; x++) {
+ styles += (opts.namespace + ' [data-sizex="' + x + '"] { width:' +
+ (x * opts.widget_base_dimensions[0] +
+ (x - 1) * (opts.widget_margins[0] * 2)) + 'px;}');
+ }
+
+ return this.add_style_tag(styles);
+ };
+
+
+ /**
+ * Injects the given CSS as string to the head of the document.
+ *
+ * @method add_style_tag
+ * @param {String} css The styles to apply.
+ * @return {Object} Returns the instance of the Gridster class.
+ */
+ fn.add_style_tag = function(css) {
+ var d = document;
+ var tag = d.createElement('style');
+
+ d.getElementsByTagName('head')[0].appendChild(tag);
+ tag.setAttribute('type', 'text/css');
+
+ if (tag.styleSheet) {
+ tag.styleSheet.cssText = css;
+ }else{
+ tag.appendChild(document.createTextNode(css));
+ }
+ return this;
+ };
+
+
+ /**
+ * Generates a faux grid to collide with it when a widget is dragged and
+ * detect row or column that we want to go.
+ *
+ * @method generate_faux_grid
+ * @param {Number} rows Number of columns.
+ * @param {Number} cols Number of rows.
+ * @return {Object} Returns the instance of the Gridster class.
+ */
+ fn.generate_faux_grid = function(rows, cols) {
+ this.faux_grid = [];
+ this.gridmap = [];
+ var col;
+ var row;
+ for (col = cols; col > 0; col--) {
+ this.gridmap[col] = [];
+ for (row = rows; row > 0; row--) {
+ this.add_faux_cell(row, col);
+ }
+ }
+ return this;
+ };
+
+
+ /**
+ * Add cell to the faux grid.
+ *
+ * @method add_faux_cell
+ * @param {Number} row The row for the new faux cell.
+ * @param {Number} col The col for the new faux cell.
+ * @return {Object} Returns the instance of the Gridster class.
+ */
+ fn.add_faux_cell = function(row, col) {
+ var coords = $({
+ left: this.baseX + ((col - 1) * this.min_widget_width),
+ top: this.baseY + (row -1) * this.min_widget_height,
+ width: this.min_widget_width,
+ height: this.min_widget_height,
+ col: col,
+ row: row,
+ original_col: col,
+ original_row: row
+ }).coords();
+
+ if (!$.isArray(this.gridmap[col])) {
+ this.gridmap[col] = [];
+ }
+
+ this.gridmap[col][row] = false;
+ this.faux_grid.push(coords);
+
+ return this;
+ };
+
+
+ /**
+ * Add rows to the faux grid.
+ *
+ * @method add_faux_rows
+ * @param {Number} rows The number of rows you want to add to the faux grid.
+ * @return {Object} Returns the instance of the Gridster class.
+ */
+ fn.add_faux_rows = function(rows) {
+ var actual_rows = this.rows;
+ var max_rows = actual_rows + (rows || 1);
+
+ for (var r = max_rows; r > actual_rows; r--) {
+ for (var c = this.cols; c >= 1; c--) {
+ this.add_faux_cell(r, c);
+ }
+ }
+
+ this.rows = max_rows;
+
+ if (this.options.autogenerate_stylesheet) {
+ this.generate_stylesheet();
+ }
+
+ return this;
+ };
+
+ /**
+ * Add cols to the faux grid.
+ *
+ * @method add_faux_cols
+ * @param {Number} cols The number of cols you want to add to the faux grid.
+ * @return {Object} Returns the instance of the Gridster class.
+ */
+ fn.add_faux_cols = function(cols) {
+ var actual_cols = this.cols;
+ var max_cols = actual_cols + (cols || 1);
+
+ for (var c = actual_cols; c < max_cols; c++) {
+ for (var r = this.rows; r >= 1; r--) {
+ this.add_faux_cell(r, c);
+ }
+ }
+
+ this.cols = max_cols;
+
+ if (this.options.autogenerate_stylesheet) {
+ this.generate_stylesheet();
+ }
+
+ return this;
+ };
+
+
+ /**
+ * Recalculates the offsets for the faux grid. You need to use it when
+ * the browser is resized.
+ *
+ * @method recalculate_faux_grid
+ * @return {Object} Returns the instance of the Gridster class.
+ */
+ fn.recalculate_faux_grid = function() {
+ var aw = this.$wrapper.width();
+ this.baseX = ($(window).width() - aw) / 2;
+ this.baseY = this.$wrapper.offset().top;
+
+ $.each(this.faux_grid, $.proxy(function(i, coords) {
+ this.faux_grid[i] = coords.update({
+ left: this.baseX + (coords.data.col -1) * this.min_widget_width,
+ top: this.baseY + (coords.data.row -1) * this.min_widget_height
+ });
+
+ }, this));
+
+ return this;
+ };
+
+
+ /**
+ * Get all widgets in the DOM and register them.
+ *
+ * @method get_widgets_from_DOM
+ * @return {Object} Returns the instance of the Gridster class.
+ */
+ fn.get_widgets_from_DOM = function() {
+ this.$widgets.each($.proxy(function(i, widget) {
+ this.register_widget($(widget));
+ }, this));
+ return this;
+ };
+
+
+ /**
+ * Calculate columns and rows to be set based on the configuration
+ * parameters, grid dimensions, etc ...
+ *
+ * @method generate_grid_and_stylesheet
+ * @return {Object} Returns the instance of the Gridster class.
+ */
+ fn.generate_grid_and_stylesheet = function() {
+ var aw = this.$wrapper.width();
+ var ah = this.$wrapper.height();
+
+ var cols = Math.floor(aw / this.min_widget_width) +
+ this.options.extra_cols;
+
+ var actual_cols = this.$widgets.map(function() {
+ return $(this).attr('data-col');
+ });
+ actual_cols = Array.prototype.slice.call(actual_cols, 0);
+ //needed to pass tests with phantomjs
+ actual_cols.length || (actual_cols = [0]);
+
+ var min_cols = Math.max.apply(Math, actual_cols);
+
+ // get all rows that could be occupied by the current widgets
+ var max_rows = this.options.extra_rows;
+ this.$widgets.each(function(i, w) {
+ max_rows += (+$(w).attr('data-sizey'));
+ });
+
+ this.cols = Math.max(min_cols, cols, this.options.min_cols);
+ this.rows = Math.max(max_rows, this.options.min_rows);
+
+ this.baseX = ($(window).width() - aw) / 2;
+ this.baseY = this.$wrapper.offset().top;
+
+ if (this.options.autogenerate_stylesheet) {
+ this.generate_stylesheet();
+ }
+
+ return this.generate_faux_grid(this.rows, this.cols);
+ };
+
+ /**
+ * Destroy this gridster by removing any sign of its presence, making it easy to avoid memory leaks
+ *
+ * @method destroy
+ * @return {undefined}
+ */
+ fn.destroy = function(){
+ // remove bound callback on window resize
+ $(window).unbind('resize', this.on_window_resize);
+
+ if(this.drag_api){
+ this.drag_api.destroy();
+ }
+
+ // lastly, remove gridster element
+ // this will additionally cause any data associated to this element to be removed, including this
+ // very gridster instance
+ this.$el.remove();
+ };
+
+
+ //jQuery adapter
+ $.fn.gridster = function(options) {
+ return this.each(function() {
+ if (!$(this).data('gridster')) {
+ $(this).data('gridster', new Gridster( this, options ));
+ }
+ });
+ };
+
+ $.Gridster = fn;
+
+}(jQuery, window, document));
\ No newline at end of file
diff --git a/fire_tablet_dashboard/assets/javascripts/gridster/jquery.leanModal.min.js b/fire_tablet_dashboard/assets/javascripts/gridster/jquery.leanModal.min.js
new file mode 100644
index 00000000..a5772dd8
--- /dev/null
+++ b/fire_tablet_dashboard/assets/javascripts/gridster/jquery.leanModal.min.js
@@ -0,0 +1,5 @@
+// leanModal v1.1 by Ray Stone - http://finelysliced.com.au
+// Dual licensed under the MIT and GPL
+
+(function($){$.fn.extend({leanModal:function(options){var defaults={top:100,overlay:0.5,closeButton:null};var overlay=$("
");$("body").append(overlay);options=$.extend(defaults,options);return this.each(function(){var o=options;$(this).click(function(e){var modal_id=$(this).attr("href");$("#lean_overlay").click(function(){close_modal(modal_id)});$(o.closeButton).click(function(){close_modal(modal_id)});var modal_height=$(modal_id).outerHeight();var modal_width=$(modal_id).outerWidth();
+$("#lean_overlay").css({"display":"block",opacity:0});$("#lean_overlay").fadeTo(200,o.overlay);$(modal_id).css({"display":"block","position":"fixed","opacity":0,"z-index":11000,"left":50+"%","margin-left":-(modal_width/2)+"px","top":o.top+"px"});$(modal_id).fadeTo(200,1);e.preventDefault()})});function close_modal(modal_id){$("#lean_overlay").fadeOut(200);$(modal_id).css({"display":"none"})}}})})(jQuery);
diff --git a/fire_tablet_dashboard/assets/javascripts/jquery.knob.js b/fire_tablet_dashboard/assets/javascripts/jquery.knob.js
new file mode 100644
index 00000000..32246387
--- /dev/null
+++ b/fire_tablet_dashboard/assets/javascripts/jquery.knob.js
@@ -0,0 +1,646 @@
+/*!jQuery Knob*/
+/**
+ * Downward compatible, touchable dial
+ *
+ * Version: 1.2.0 (15/07/2012)
+ * Requires: jQuery v1.7+
+ *
+ * Copyright (c) 2012 Anthony Terrien
+ * Under MIT and GPL licenses:
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * Thanks to vor, eskimoblood, spiffistan, FabrizioC
+ */
+$(function () {
+
+ /**
+ * Kontrol library
+ */
+ "use strict";
+
+ /**
+ * Definition of globals and core
+ */
+ var k = {}, // kontrol
+ max = Math.max,
+ min = Math.min;
+
+ k.c = {};
+ k.c.d = $(document);
+ k.c.t = function (e) {
+ return e.originalEvent.touches.length - 1;
+ };
+
+ /**
+ * Kontrol Object
+ *
+ * Definition of an abstract UI control
+ *
+ * Each concrete component must call this one.
+ *
+ * k.o.call(this);
+ *
+ */
+ k.o = function () {
+ var s = this;
+
+ this.o = null; // array of options
+ this.$ = null; // jQuery wrapped element
+ this.i = null; // mixed HTMLInputElement or array of HTMLInputElement
+ this.g = null; // 2D graphics context for 'pre-rendering'
+ this.v = null; // value ; mixed array or integer
+ this.cv = null; // change value ; not commited value
+ this.x = 0; // canvas x position
+ this.y = 0; // canvas y position
+ this.$c = null; // jQuery canvas element
+ this.c = null; // rendered canvas context
+ this.t = 0; // touches index
+ this.isInit = false;
+ this.fgColor = null; // main color
+ this.pColor = null; // previous color
+ this.dH = null; // draw hook
+ this.cH = null; // change hook
+ this.eH = null; // cancel hook
+ this.rH = null; // release hook
+
+ this.run = function () {
+ var cf = function (e, conf) {
+ var k;
+ for (k in conf) {
+ s.o[k] = conf[k];
+ }
+ s.init();
+ s._configure()
+ ._draw();
+ };
+
+ if(this.$.data('kontroled')) return;
+ this.$.data('kontroled', true);
+
+ this.extend();
+ this.o = $.extend(
+ {
+ // Config
+ min : this.$.data('min') || 0,
+ max : this.$.data('max') || 100,
+ stopper : true,
+ readOnly : this.$.data('readonly'),
+
+ // UI
+ cursor : (this.$.data('cursor') === true && 30)
+ || this.$.data('cursor')
+ || 0,
+ thickness : this.$.data('thickness') || 0.35,
+ width : this.$.data('width') || 200,
+ height : this.$.data('height') || 200,
+ displayInput : this.$.data('displayinput') == null || this.$.data('displayinput'),
+ displayPrevious : this.$.data('displayprevious'),
+ fgColor : this.$.data('fgcolor') || '#87CEEB',
+ inline : false,
+
+ // Hooks
+ draw : null, // function () {}
+ change : null, // function (value) {}
+ cancel : null, // function () {}
+ release : null // function (value) {}
+ }, this.o
+ );
+
+ // routing value
+ if(this.$.is('fieldset')) {
+
+ // fieldset = array of integer
+ this.v = {};
+ this.i = this.$.find('input')
+ this.i.each(function(k) {
+ var $this = $(this);
+ s.i[k] = $this;
+ s.v[k] = $this.val();
+
+ $this.bind(
+ 'change'
+ , function () {
+ var val = {};
+ val[k] = $this.val();
+ s.val(val);
+ }
+ );
+ });
+ this.$.find('legend').remove();
+
+ } else {
+ // input = integer
+ this.i = this.$;
+ this.v = this.$.val();
+ (this.v == '') && (this.v = this.o.min);
+
+ this.$.bind(
+ 'change'
+ , function () {
+ s.val(s.$.val());
+ }
+ );
+ }
+
+ (!this.o.displayInput) && this.$.hide();
+
+ this.$c = $(' ');
+ this.c = this.$c[0].getContext("2d");
+
+ this.$
+ .wrap($('
'))
+ .before(this.$c);
+
+ if (this.v instanceof Object) {
+ this.cv = {};
+ this.copy(this.v, this.cv);
+ } else {
+ this.cv = this.v;
+ }
+
+ this.$
+ .bind("configure", cf)
+ .parent()
+ .bind("configure", cf);
+
+ this._listen()
+ ._configure()
+ ._xy()
+ .init();
+
+ this.isInit = true;
+
+ this._draw();
+
+ return this;
+ };
+
+ this._draw = function () {
+
+ // canvas pre-rendering
+ var d = true,
+ c = document.createElement('canvas');
+
+ c.width = s.o.width;
+ c.height = s.o.height;
+ s.g = c.getContext('2d');
+
+ s.clear();
+
+ s.dH
+ && (d = s.dH());
+
+ (d !== false) && s.draw();
+
+ s.c.drawImage(c, 0, 0);
+ c = null;
+ };
+
+ this._touch = function (e) {
+
+ var touchMove = function (e) {
+
+ var v = s.xy2val(
+ e.originalEvent.touches[s.t].pageX,
+ e.originalEvent.touches[s.t].pageY
+ );
+
+ if (v == s.cv) return;
+
+ if (
+ s.cH
+ && (s.cH(v) === false)
+ ) return;
+
+
+ s.change(v);
+ s._draw();
+ };
+
+ // get touches index
+ this.t = k.c.t(e);
+
+ // First touch
+ touchMove(e);
+
+ // Touch events listeners
+ k.c.d
+ .bind("touchmove.k", touchMove)
+ .bind(
+ "touchend.k"
+ , function () {
+ k.c.d.unbind('touchmove.k touchend.k');
+
+ if (
+ s.rH
+ && (s.rH(s.cv) === false)
+ ) return;
+
+ s.val(s.cv);
+ }
+ );
+
+ return this;
+ };
+
+ this._mouse = function (e) {
+
+ var mouseMove = function (e) {
+ var v = s.xy2val(e.pageX, e.pageY);
+ if (v == s.cv) return;
+
+ if (
+ s.cH
+ && (s.cH(v) === false)
+ ) return;
+
+ s.change(v);
+ s._draw();
+ };
+
+ // First click
+ mouseMove(e);
+
+ // Mouse events listeners
+ k.c.d
+ .bind("mousemove.k", mouseMove)
+ .bind(
+ // Escape key cancel current change
+ "keyup.k"
+ , function (e) {
+ if (e.keyCode === 27) {
+ k.c.d.unbind("mouseup.k mousemove.k keyup.k");
+
+ if (
+ s.eH
+ && (s.eH() === false)
+ ) return;
+
+ s.cancel();
+ }
+ }
+ )
+ .bind(
+ "mouseup.k"
+ , function (e) {
+ k.c.d.unbind('mousemove.k mouseup.k keyup.k');
+
+ if (
+ s.rH
+ && (s.rH(s.cv) === false)
+ ) return;
+
+ s.val(s.cv);
+ }
+ );
+
+ return this;
+ };
+
+ this._xy = function () {
+ var o = this.$c.offset();
+ this.x = o.left;
+ this.y = o.top;
+ return this;
+ };
+
+ this._listen = function () {
+
+ if (!this.o.readOnly) {
+ this.$c
+ .bind(
+ "mousedown"
+ , function (e) {
+ e.preventDefault();
+ s._xy()._mouse(e);
+ }
+ )
+ .bind(
+ "touchstart"
+ , function (e) {
+ e.preventDefault();
+ s._xy()._touch(e);
+ }
+ );
+ this.listen();
+ } else {
+ this.$.attr('readonly', 'readonly');
+ }
+
+ return this;
+ };
+
+ this._configure = function () {
+
+ // Hooks
+ if (this.o.draw) this.dH = this.o.draw;
+ if (this.o.change) this.cH = this.o.change;
+ if (this.o.cancel) this.eH = this.o.cancel;
+ if (this.o.release) this.rH = this.o.release;
+
+ if (this.o.displayPrevious) {
+ this.pColor = this.h2rgba(this.o.fgColor, "0.4");
+ this.fgColor = this.h2rgba(this.o.fgColor, "0.6");
+ } else {
+ this.fgColor = this.o.fgColor;
+ }
+
+ return this;
+ };
+
+ this._clear = function () {
+ this.$c[0].width = this.$c[0].width;
+ };
+
+ // Abstract methods
+ this.listen = function () {}; // on start, one time
+ this.extend = function () {}; // each time configure triggered
+ this.init = function () {}; // each time configure triggered
+ this.change = function (v) {}; // on change
+ this.val = function (v) {}; // on release
+ this.xy2val = function (x, y) {}; //
+ this.draw = function () {}; // on change / on release
+ this.clear = function () { this._clear(); };
+
+ // Utils
+ this.h2rgba = function (h, a) {
+ var rgb;
+ h = h.substring(1,7)
+ rgb = [parseInt(h.substring(0,2),16)
+ ,parseInt(h.substring(2,4),16)
+ ,parseInt(h.substring(4,6),16)];
+ return "rgba(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + "," + a + ")";
+ };
+
+ this.copy = function (f, t) {
+ for (var i in f) { t[i] = f[i]; }
+ };
+ };
+
+
+ /**
+ * k.Dial
+ */
+ k.Dial = function () {
+ k.o.call(this);
+
+ this.startAngle = null;
+ this.xy = null;
+ this.radius = null;
+ this.lineWidth = null;
+ this.cursorExt = null;
+ this.w2 = null;
+ this.PI2 = 2*Math.PI;
+
+ this.extend = function () {
+ this.o = $.extend(
+ {
+ bgColor : this.$.data('bgcolor') || '#EEEEEE',
+ angleOffset : this.$.data('angleoffset') || 0,
+ angleArc : this.$.data('anglearc') || 360,
+ inline : true
+ }, this.o
+ );
+ };
+
+ this.val = function (v) {
+ if (null != v) {
+ this.cv = this.o.stopper ? max(min(v, this.o.max), this.o.min) : v;
+ this.v = this.cv;
+ this.$.val(this.v);
+ this._draw();
+ } else {
+ return this.v;
+ }
+ };
+
+ this.xy2val = function (x, y) {
+ var a, ret;
+
+ a = Math.atan2(
+ x - (this.x + this.w2)
+ , - (y - this.y - this.w2)
+ ) - this.angleOffset;
+
+ if(this.angleArc != this.PI2 && (a < 0) && (a > -0.5)) {
+ // if isset angleArc option, set to min if .5 under min
+ a = 0;
+ } else if (a < 0) {
+ a += this.PI2;
+ }
+
+ ret = ~~ (0.5 + (a * (this.o.max - this.o.min) / this.angleArc))
+ + this.o.min;
+
+ this.o.stopper
+ && (ret = max(min(ret, this.o.max), this.o.min));
+
+ return ret;
+ };
+
+ this.listen = function () {
+ // bind MouseWheel
+ var s = this,
+ mw = function (e) {
+ e.preventDefault();
+
+ var ori = e.originalEvent
+ ,deltaX = ori.detail || ori.wheelDeltaX
+ ,deltaY = ori.detail || ori.wheelDeltaY
+ ,v = parseInt(s.$.val()) + (deltaX>0 || deltaY>0 ? 1 : deltaX<0 || deltaY<0 ? -1 : 0);
+
+ if (
+ s.cH
+ && (s.cH(v) === false)
+ ) return;
+
+ s.val(v);
+ }
+ , kval, to, m = 1, kv = {37:-1, 38:1, 39:1, 40:-1};
+
+ this.$
+ .bind(
+ "keydown"
+ ,function (e) {
+ var kc = e.keyCode;
+ kval = parseInt(String.fromCharCode(kc));
+
+ if (isNaN(kval)) {
+
+ (kc !== 13) // enter
+ && (kc !== 8) // bs
+ && (kc !== 9) // tab
+ && (kc !== 189) // -
+ && e.preventDefault();
+
+ // arrows
+ if ($.inArray(kc,[37,38,39,40]) > -1) {
+ e.preventDefault();
+
+ var v = parseInt(s.$.val()) + kv[kc] * m;
+
+ s.o.stopper
+ && (v = max(min(v, s.o.max), s.o.min));
+
+ s.change(v);
+ s._draw();
+
+ // long time keydown speed-up
+ to = window.setTimeout(
+ function () { m*=2; }
+ ,30
+ );
+ }
+ }
+ }
+ )
+ .bind(
+ "keyup"
+ ,function (e) {
+ if (isNaN(kval)) {
+ if (to) {
+ window.clearTimeout(to);
+ to = null;
+ m = 1;
+ s.val(s.$.val());
+ }
+ } else {
+ // kval postcond
+ (s.$.val() > s.o.max && s.$.val(s.o.max))
+ || (s.$.val() < s.o.min && s.$.val(s.o.min));
+ }
+
+ }
+ );
+
+ this.$c.bind("mousewheel DOMMouseScroll", mw);
+ this.$.bind("mousewheel DOMMouseScroll", mw)
+ };
+
+ this.init = function () {
+
+ if (
+ this.v < this.o.min
+ || this.v > this.o.max
+ ) this.v = this.o.min;
+
+ this.$.val(this.v);
+ this.w2 = this.o.width / 2;
+ this.cursorExt = this.o.cursor / 100;
+ this.xy = this.w2;
+ this.lineWidth = this.xy * this.o.thickness;
+ this.radius = this.xy - this.lineWidth / 2;
+
+ this.o.angleOffset
+ && (this.o.angleOffset = isNaN(this.o.angleOffset) ? 0 : this.o.angleOffset);
+
+ this.o.angleArc
+ && (this.o.angleArc = isNaN(this.o.angleArc) ? this.PI2 : this.o.angleArc);
+
+ // deg to rad
+ this.angleOffset = this.o.angleOffset * Math.PI / 180;
+ this.angleArc = this.o.angleArc * Math.PI / 180;
+
+ // compute start and end angles
+ this.startAngle = 1.5 * Math.PI + this.angleOffset;
+ this.endAngle = 1.5 * Math.PI + this.angleOffset + this.angleArc;
+
+ var s = max(
+ String(Math.abs(this.o.max)).length
+ , String(Math.abs(this.o.min)).length
+ , 2
+ ) + 2;
+
+ this.o.displayInput
+ && this.i.css({
+ 'width' : ((this.o.width / 2 + 4) >> 0) + 'px'
+ ,'height' : ((this.o.width / 3) >> 0) + 'px'
+ ,'position' : 'absolute'
+ ,'vertical-align' : 'middle'
+ ,'margin-top' : ((this.o.width / 3) >> 0) + 'px'
+ ,'margin-left' : '-' + ((this.o.width * 3 / 4 + 2) >> 0) + 'px'
+ ,'border' : 0
+ ,'background' : 'none'
+ ,'font' : 'bold ' + ((this.o.width / s) >> 0) + 'px Arial'
+ ,'text-align' : 'center'
+ ,'color' : this.o.fgColor
+ ,'padding' : '0px'
+ ,'-webkit-appearance': 'none'
+ })
+ || this.i.css({
+ 'width' : '0px'
+ ,'visibility' : 'hidden'
+ });
+ };
+
+ this.change = function (v) {
+ this.cv = v;
+ this.$.val(v);
+ };
+
+ this.angle = function (v) {
+ return (v - this.o.min) * this.angleArc / (this.o.max - this.o.min);
+ };
+
+ this.draw = function () {
+
+ var c = this.g, // context
+ a = this.angle(this.cv) // Angle
+ , sat = this.startAngle // Start angle
+ , eat = sat + a // End angle
+ , sa, ea // Previous angles
+ , r = 1;
+
+ c.lineWidth = this.lineWidth;
+
+ this.o.cursor
+ && (sat = eat - this.cursorExt)
+ && (eat = eat + this.cursorExt);
+
+ c.beginPath();
+ c.strokeStyle = this.o.bgColor;
+ c.arc(this.xy, this.xy, this.radius, this.endAngle, this.startAngle, true);
+ c.stroke();
+
+ if (this.o.displayPrevious) {
+ ea = this.startAngle + this.angle(this.v);
+ sa = this.startAngle;
+ this.o.cursor
+ && (sa = ea - this.cursorExt)
+ && (ea = ea + this.cursorExt);
+
+ c.beginPath();
+ c.strokeStyle = this.pColor;
+ c.arc(this.xy, this.xy, this.radius, sa, ea, false);
+ c.stroke();
+ r = (this.cv == this.v);
+ }
+
+ c.beginPath();
+ c.strokeStyle = r ? this.o.fgColor : this.fgColor ;
+ c.arc(this.xy, this.xy, this.radius, sat, eat, false);
+ c.stroke();
+ };
+
+ this.cancel = function () {
+ this.val(this.v);
+ };
+ };
+
+ $.fn.dial = $.fn.knob = function (o) {
+ return this.each(
+ function () {
+ var d = new k.Dial();
+ d.o = o;
+ d.$ = $(this);
+ d.run();
+ }
+ ).parent();
+ };
+
+});
\ No newline at end of file
diff --git a/fire_tablet_dashboard/assets/javascripts/rickshaw-1.4.3.min.js b/fire_tablet_dashboard/assets/javascripts/rickshaw-1.4.3.min.js
new file mode 100644
index 00000000..fac600ff
--- /dev/null
+++ b/fire_tablet_dashboard/assets/javascripts/rickshaw-1.4.3.min.js
@@ -0,0 +1,2 @@
+var Rickshaw={namespace:function(namespace,obj){var parts=namespace.split(".");var parent=Rickshaw;for(var i=1,length=parts.length;i=3){if(s.data[2].xthis.window.xMax)isInRange=false;return isInRange}return true};this.onUpdate=function(callback){this.updateCallbacks.push(callback)};this.registerRenderer=function(renderer){this._renderers=this._renderers||{};this._renderers[renderer.name]=renderer};this.configure=function(args){if(args.width||args.height){this.setSize(args)}Rickshaw.keys(this.defaults).forEach(function(k){this[k]=k in args?args[k]:k in this?this[k]:this.defaults[k]},this);this.setRenderer(args.renderer||this.renderer.name,args)};this.setRenderer=function(r,args){if(typeof r=="function"){this.renderer=new r({graph:self});this.registerRenderer(this.renderer)}else{if(!this._renderers[r]){throw"couldn't find renderer "+r}this.renderer=this._renderers[r]}if(typeof args=="object"){this.renderer.configure(args)}};this.setSize=function(args){args=args||{};if(typeof window!==undefined){var style=window.getComputedStyle(this.element,null);var elementWidth=parseInt(style.getPropertyValue("width"),10);var elementHeight=parseInt(style.getPropertyValue("height"),10)}this.width=args.width||elementWidth||400;this.height=args.height||elementHeight||250;this.vis&&this.vis.attr("width",this.width).attr("height",this.height)};this.initialize(args)};Rickshaw.namespace("Rickshaw.Fixtures.Color");Rickshaw.Fixtures.Color=function(){this.schemes={};this.schemes.spectrum14=["#ecb796","#dc8f70","#b2a470","#92875a","#716c49","#d2ed82","#bbe468","#a1d05d","#e7cbe6","#d8aad6","#a888c2","#9dc2d3","#649eb9","#387aa3"].reverse();this.schemes.spectrum2000=["#57306f","#514c76","#646583","#738394","#6b9c7d","#84b665","#a7ca50","#bfe746","#e2f528","#fff726","#ecdd00","#d4b11d","#de8800","#de4800","#c91515","#9a0000","#7b0429","#580839","#31082b"];this.schemes.spectrum2001=["#2f243f","#3c2c55","#4a3768","#565270","#6b6b7c","#72957f","#86ad6e","#a1bc5e","#b8d954","#d3e04e","#ccad2a","#cc8412","#c1521d","#ad3821","#8a1010","#681717","#531e1e","#3d1818","#320a1b"];this.schemes.classic9=["#423d4f","#4a6860","#848f39","#a2b73c","#ddcb53","#c5a32f","#7d5836","#963b20","#7c2626","#491d37","#2f254a"].reverse();this.schemes.httpStatus={503:"#ea5029",502:"#d23f14",500:"#bf3613",410:"#efacea",409:"#e291dc",403:"#f457e8",408:"#e121d2",401:"#b92dae",405:"#f47ceb",404:"#a82a9f",400:"#b263c6",301:"#6fa024",302:"#87c32b",307:"#a0d84c",304:"#28b55c",200:"#1a4f74",206:"#27839f",201:"#52adc9",202:"#7c979f",203:"#a5b8bd",204:"#c1cdd1"};this.schemes.colorwheel=["#b5b6a9","#858772","#785f43","#96557e","#4682b4","#65b9ac","#73c03a","#cb513a"].reverse();this.schemes.cool=["#5e9d2f","#73c03a","#4682b4","#7bc3b8","#a9884e","#c1b266","#a47493","#c09fb5"];this.schemes.munin=["#00cc00","#0066b3","#ff8000","#ffcc00","#330099","#990099","#ccff00","#ff0000","#808080","#008f00","#00487d","#b35a00","#b38f00","#6b006b","#8fb300","#b30000","#bebebe","#80ff80","#80c9ff","#ffc080","#ffe680","#aa80ff","#ee00cc","#ff8080","#666600","#ffbfff","#00ffcc","#cc6699","#999900"]};Rickshaw.namespace("Rickshaw.Fixtures.RandomData");Rickshaw.Fixtures.RandomData=function(timeInterval){var addData;timeInterval=timeInterval||1;var lastRandomValue=200;var timeBase=Math.floor((new Date).getTime()/1e3);this.addData=function(data){var randomValue=Math.random()*100+15+lastRandomValue;var index=data[0].length;var counter=1;data.forEach(function(series){var randomVariance=Math.random()*20;var v=randomValue/25+counter++ +(Math.cos(index*counter*11/960)+2)*15+(Math.cos(index/7)+2)*7+(Math.cos(index/17)+2)*1;series.push({x:index*timeInterval+timeBase,y:v+randomVariance})});lastRandomValue=randomValue*.85};this.removeData=function(data){data.forEach(function(series){series.shift()});timeBase+=timeInterval}};Rickshaw.namespace("Rickshaw.Fixtures.Time");Rickshaw.Fixtures.Time=function(){var tzOffset=(new Date).getTimezoneOffset()*60;var self=this;this.months=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];this.units=[{name:"decade",seconds:86400*365.25*10,formatter:function(d){return parseInt(d.getUTCFullYear()/10,10)*10}},{name:"year",seconds:86400*365.25,formatter:function(d){return d.getUTCFullYear()}},{name:"month",seconds:86400*30.5,formatter:function(d){return self.months[d.getUTCMonth()]}},{name:"week",seconds:86400*7,formatter:function(d){return self.formatDate(d)}},{name:"day",seconds:86400,formatter:function(d){return d.getUTCDate()}},{name:"6 hour",seconds:3600*6,formatter:function(d){return self.formatTime(d)}},{name:"hour",seconds:3600,formatter:function(d){return self.formatTime(d)}},{name:"15 minute",seconds:60*15,formatter:function(d){return self.formatTime(d)}},{name:"minute",seconds:60,formatter:function(d){return d.getUTCMinutes()}},{name:"15 second",seconds:15,formatter:function(d){return d.getUTCSeconds()+"s"}},{name:"second",seconds:1,formatter:function(d){return d.getUTCSeconds()+"s"}}];this.unit=function(unitName){return this.units.filter(function(unit){return unitName==unit.name}).shift()};this.formatDate=function(d){return d3.time.format("%b %e")(d)};this.formatTime=function(d){return d.toUTCString().match(/(\d+:\d+):/)[1]};this.ceil=function(time,unit){var nearFuture;var rounded;if(unit.name=="month"){nearFuture=new Date((time+unit.seconds-1)*1e3);rounded=new Date(0);rounded.setUTCFullYear(nearFuture.getUTCFullYear());rounded.setUTCMonth(nearFuture.getUTCMonth());rounded.setUTCDate(1);rounded.setUTCHours(0);rounded.setUTCMinutes(0);rounded.setUTCSeconds(0);rounded.setUTCMilliseconds(0);return rounded.getTime()/1e3}if(unit.name=="year"){nearFuture=new Date((time+unit.seconds-1)*1e3);rounded=new Date(0);rounded.setUTCFullYear(nearFuture.getUTCFullYear());rounded.setUTCMonth(0);rounded.setUTCDate(1);rounded.setUTCHours(0);rounded.setUTCMinutes(0);rounded.setUTCSeconds(0);rounded.setUTCMilliseconds(0);return rounded.getTime()/1e3}return Math.ceil(time/unit.seconds)*unit.seconds}};Rickshaw.namespace("Rickshaw.Fixtures.Time.Local");Rickshaw.Fixtures.Time.Local=function(){var tzOffset=(new Date).getTimezoneOffset()*60;var self=this;this.months=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];this.units=[{name:"decade",seconds:86400*365.25*10,formatter:function(d){return parseInt(d.getFullYear()/10,10)*10}},{name:"year",seconds:86400*365.25,formatter:function(d){return d.getFullYear()}},{name:"month",seconds:86400*30.5,formatter:function(d){return self.months[d.getMonth()]}},{name:"week",seconds:86400*7,formatter:function(d){return self.formatDate(d)}},{name:"day",seconds:86400,formatter:function(d){return d.getDate()}},{name:"6 hour",seconds:3600*6,formatter:function(d){return self.formatTime(d)}},{name:"hour",seconds:3600,formatter:function(d){return self.formatTime(d)}},{name:"15 minute",seconds:60*15,formatter:function(d){return self.formatTime(d)}},{name:"minute",seconds:60,formatter:function(d){return d.getMinutes()}},{name:"15 second",seconds:15,formatter:function(d){return d.getSeconds()+"s"}},{name:"second",seconds:1,formatter:function(d){return d.getSeconds()+"s"}}];this.unit=function(unitName){return this.units.filter(function(unit){return unitName==unit.name}).shift()};this.formatDate=function(d){return d3.time.format("%b %e")(d)};this.formatTime=function(d){return d.toString().match(/(\d+:\d+):/)[1]};this.ceil=function(time,unit){var nearFuture;var rounded;if(unit.name=="day"){nearFuture=new Date((time+unit.seconds-1)*1e3);rounded=new Date(0);rounded.setMilliseconds(0);rounded.setSeconds(0);rounded.setMinutes(0);rounded.setHours(0);rounded.setDate(nearFuture.getDate());rounded.setMonth(nearFuture.getMonth());rounded.setFullYear(nearFuture.getFullYear());return rounded.getTime()/1e3}if(unit.name=="month"){nearFuture=new Date((time+unit.seconds-1)*1e3);rounded=new Date(0);rounded.setMilliseconds(0);rounded.setSeconds(0);rounded.setMinutes(0);rounded.setHours(0);rounded.setDate(1);rounded.setMonth(nearFuture.getMonth());rounded.setFullYear(nearFuture.getFullYear());return rounded.getTime()/1e3}if(unit.name=="year"){nearFuture=new Date((time+unit.seconds-1)*1e3);rounded=new Date(0);rounded.setFullYear(nearFuture.getFullYear());rounded.setMilliseconds(0);rounded.setSeconds(0);rounded.setMinutes(0);rounded.setHours(0);rounded.setDate(1);rounded.setMonth(0);return rounded.getTime()/1e3}return Math.ceil(time/unit.seconds)*unit.seconds}};Rickshaw.namespace("Rickshaw.Fixtures.Number");Rickshaw.Fixtures.Number.formatKMBT=function(y){var abs_y=Math.abs(y);if(abs_y>=1e12){return y/1e12+"T"}else if(abs_y>=1e9){return y/1e9+"B"}else if(abs_y>=1e6){return y/1e6+"M"}else if(abs_y>=1e3){return y/1e3+"K"}else if(abs_y<1&&y>0){return y.toFixed(2)}else if(abs_y===0){return""}else{return y}};Rickshaw.Fixtures.Number.formatBase1024KMGTP=function(y){var abs_y=Math.abs(y);if(abs_y>=0x4000000000000){return y/0x4000000000000+"P"}else if(abs_y>=1099511627776){return y/1099511627776+"T"}else if(abs_y>=1073741824){return y/1073741824+"G"}else if(abs_y>=1048576){return y/1048576+"M"}else if(abs_y>=1024){return y/1024+"K"}else if(abs_y<1&&y>0){return y.toFixed(2)}else if(abs_y===0){return""}else{return y}};Rickshaw.namespace("Rickshaw.Color.Palette");Rickshaw.Color.Palette=function(args){var color=new Rickshaw.Fixtures.Color;args=args||{};this.schemes={};this.scheme=color.schemes[args.scheme]||args.scheme||color.schemes.colorwheel;this.runningIndex=0;this.generatorIndex=0;if(args.interpolatedStopCount){var schemeCount=this.scheme.length-1;var i,j,scheme=[];for(i=0;iself.graph.x.range()[1]){if(annotation.element){annotation.line.classList.add("offscreen");annotation.element.style.display="none"}annotation.boxes.forEach(function(box){if(box.rangeElement)box.rangeElement.classList.add("offscreen")});return}if(!annotation.element){var element=annotation.element=document.createElement("div");element.classList.add("annotation");this.elements.timeline.appendChild(element);element.addEventListener("click",function(e){element.classList.toggle("active");annotation.line.classList.toggle("active");annotation.boxes.forEach(function(box){if(box.rangeElement)box.rangeElement.classList.toggle("active")})},false)}annotation.element.style.left=left+"px";annotation.element.style.display="block";annotation.boxes.forEach(function(box){var element=box.element;if(!element){element=box.element=document.createElement("div");element.classList.add("content");element.innerHTML=box.content;annotation.element.appendChild(element);annotation.line=document.createElement("div");annotation.line.classList.add("annotation_line");self.graph.element.appendChild(annotation.line);if(box.end){box.rangeElement=document.createElement("div");box.rangeElement.classList.add("annotation_range");self.graph.element.appendChild(box.rangeElement)}}if(box.end){var annotationRangeStart=left;var annotationRangeEnd=Math.min(self.graph.x(box.end),self.graph.x.range()[1]);if(annotationRangeStart>annotationRangeEnd){annotationRangeEnd=left;annotationRangeStart=Math.max(self.graph.x(box.end),self.graph.x.range()[0])}var annotationRangeWidth=annotationRangeEnd-annotationRangeStart;box.rangeElement.style.left=annotationRangeStart+"px";box.rangeElement.style.width=annotationRangeWidth+"px";box.rangeElement.classList.remove("offscreen")}annotation.line.classList.remove("offscreen");annotation.line.style.left=left+"px"})},this)};this.graph.onUpdate(function(){self.update()})};Rickshaw.namespace("Rickshaw.Graph.Axis.Time");Rickshaw.Graph.Axis.Time=function(args){var self=this;this.graph=args.graph;this.elements=[];this.ticksTreatment=args.ticksTreatment||"plain";this.fixedTimeUnit=args.timeUnit;var time=args.timeFixture||new Rickshaw.Fixtures.Time;this.appropriateTimeUnit=function(){var unit;var units=time.units;var domain=this.graph.x.domain();var rangeSeconds=domain[1]-domain[0];units.forEach(function(u){if(Math.floor(rangeSeconds/u.seconds)>=2){unit=unit||u}});return unit||time.units[time.units.length-1]};this.tickOffsets=function(){var domain=this.graph.x.domain();var unit=this.fixedTimeUnit||this.appropriateTimeUnit();var count=Math.ceil((domain[1]-domain[0])/unit.seconds);var runningTick=domain[0];var offsets=[];for(var i=0;iself.graph.x.range()[1])return;var element=document.createElement("div");element.style.left=self.graph.x(o.value)+"px";element.classList.add("x_tick");element.classList.add(self.ticksTreatment);var title=document.createElement("div");title.classList.add("title");title.innerHTML=o.unit.formatter(new Date(o.value*1e3));element.appendChild(title);self.graph.element.appendChild(element);self.elements.push(element)})};this.graph.onUpdate(function(){self.render()})};Rickshaw.namespace("Rickshaw.Graph.Axis.X");Rickshaw.Graph.Axis.X=function(args){var self=this;var berthRate=.1;this.initialize=function(args){this.graph=args.graph;this.orientation=args.orientation||"top";this.pixelsPerTick=args.pixelsPerTick||75;if(args.ticks)this.staticTicks=args.ticks;if(args.tickValues)this.tickValues=args.tickValues;this.tickSize=args.tickSize||4;this.ticksTreatment=args.ticksTreatment||"plain";if(args.element){this.element=args.element;this._discoverSize(args.element,args);this.vis=d3.select(args.element).append("svg:svg").attr("height",this.height).attr("width",this.width).attr("class","rickshaw_graph x_axis_d3");this.element=this.vis[0][0];this.element.style.position="relative";this.setSize({width:args.width,height:args.height})}else{this.vis=this.graph.vis}this.graph.onUpdate(function(){self.render()})};this.setSize=function(args){args=args||{};if(!this.element)return;this._discoverSize(this.element.parentNode,args);this.vis.attr("height",this.height).attr("width",this.width*(1+berthRate));var berth=Math.floor(this.width*berthRate/2);this.element.style.left=-1*berth+"px"};this.render=function(){if(this.graph.width!==this._renderWidth)this.setSize({auto:true});var axis=d3.svg.axis().scale(this.graph.x).orient(this.orientation);axis.tickFormat(args.tickFormat||function(x){return x});if(this.tickValues)axis.tickValues(this.tickValues);this.ticks=this.staticTicks||Math.floor(this.graph.width/this.pixelsPerTick);var berth=Math.floor(this.width*berthRate/2)||0;var transform;if(this.orientation=="top"){var yOffset=this.height||this.graph.height;transform="translate("+berth+","+yOffset+")"}else{transform="translate("+berth+", 0)"}if(this.element){this.vis.selectAll("*").remove()}this.vis.append("svg:g").attr("class",["x_ticks_d3",this.ticksTreatment].join(" ")).attr("transform",transform).call(axis.ticks(this.ticks).tickSubdivide(0).tickSize(this.tickSize));var gridSize=(this.orientation=="bottom"?1:-1)*this.graph.height;this.graph.vis.append("svg:g").attr("class","x_grid_d3").call(axis.ticks(this.ticks).tickSubdivide(0).tickSize(gridSize));this._renderHeight=this.graph.height};this._discoverSize=function(element,args){if(typeof window!=="undefined"){var style=window.getComputedStyle(element,null);var elementHeight=parseInt(style.getPropertyValue("height"),10);if(!args.auto){var elementWidth=parseInt(style.getPropertyValue("width"),10)}}this.width=(args.width||elementWidth||this.graph.width)*(1+berthRate);this.height=args.height||elementHeight||40};this.initialize(args)};Rickshaw.namespace("Rickshaw.Graph.Axis.Y");Rickshaw.Graph.Axis.Y=Rickshaw.Class.create({initialize:function(args){this.graph=args.graph;this.orientation=args.orientation||"right";this.pixelsPerTick=args.pixelsPerTick||75;if(args.ticks)this.staticTicks=args.ticks;if(args.tickValues)this.tickValues=args.tickValues;this.tickSize=args.tickSize||4;this.ticksTreatment=args.ticksTreatment||"plain";this.tickFormat=args.tickFormat||function(y){return y};this.berthRate=.1;if(args.element){this.element=args.element;this.vis=d3.select(args.element).append("svg:svg").attr("class","rickshaw_graph y_axis");this.element=this.vis[0][0];this.element.style.position="relative";this.setSize({width:args.width,height:args.height})}else{this.vis=this.graph.vis}var self=this;this.graph.onUpdate(function(){self.render()})},setSize:function(args){args=args||{};if(!this.element)return;if(typeof window!=="undefined"){var style=window.getComputedStyle(this.element.parentNode,null);var elementWidth=parseInt(style.getPropertyValue("width"),10);if(!args.auto){var elementHeight=parseInt(style.getPropertyValue("height"),10)}}this.width=args.width||elementWidth||this.graph.width*this.berthRate;this.height=args.height||elementHeight||this.graph.height;this.vis.attr("width",this.width).attr("height",this.height*(1+this.berthRate));var berth=this.height*this.berthRate;if(this.orientation=="left"){this.element.style.top=-1*berth+"px"}},render:function(){if(this.graph.height!==this._renderHeight)this.setSize({auto:true});this.ticks=this.staticTicks||Math.floor(this.graph.height/this.pixelsPerTick);var axis=this._drawAxis(this.graph.y);this._drawGrid(axis);this._renderHeight=this.graph.height},_drawAxis:function(scale){var axis=d3.svg.axis().scale(scale).orient(this.orientation);axis.tickFormat(this.tickFormat);if(this.tickValues)axis.tickValues(this.tickValues);if(this.orientation=="left"){var berth=this.height*this.berthRate;var transform="translate("+this.width+", "+berth+")"}if(this.element){this.vis.selectAll("*").remove()}this.vis.append("svg:g").attr("class",["y_ticks",this.ticksTreatment].join(" ")).attr("transform",transform).call(axis.ticks(this.ticks).tickSubdivide(0).tickSize(this.tickSize));return axis},_drawGrid:function(axis){var gridSize=(this.orientation=="right"?1:-1)*this.graph.width;this.graph.vis.append("svg:g").attr("class","y_grid").call(axis.ticks(this.ticks).tickSubdivide(0).tickSize(gridSize))}});Rickshaw.namespace("Rickshaw.Graph.Axis.Y.Scaled");Rickshaw.Graph.Axis.Y.Scaled=Rickshaw.Class.create(Rickshaw.Graph.Axis.Y,{initialize:function($super,args){if(typeof args.scale==="undefined"){throw new Error("Scaled requires scale")}this.scale=args.scale;if(typeof args.grid==="undefined"){this.grid=true}else{this.grid=args.grid}$super(args)},_drawAxis:function($super,scale){var adjustedScale=this.scale.copy().range(scale.range());return $super(adjustedScale)},_drawGrid:function($super,axis){if(this.grid){$super(axis)}}});Rickshaw.namespace("Rickshaw.Graph.Behavior.Series.Highlight");Rickshaw.Graph.Behavior.Series.Highlight=function(args){this.graph=args.graph;this.legend=args.legend;var self=this;var colorSafe={};var activeLine=null;var disabledColor=args.disabledColor||function(seriesColor){return d3.interpolateRgb(seriesColor,d3.rgb("#d8d8d8"))(.8).toString()};this.addHighlightEvents=function(l){l.element.addEventListener("mouseover",function(e){if(activeLine)return;else activeLine=l;self.legend.lines.forEach(function(line,index){if(l===line){if(index>0&&self.graph.renderer.unstack&&(line.series.renderer?line.series.renderer.unstack:true)){var seriesIndex=self.graph.series.length-index-1;
+line.originalIndex=seriesIndex;var series=self.graph.series.splice(seriesIndex,1)[0];self.graph.series.push(series)}return}colorSafe[line.series.name]=colorSafe[line.series.name]||line.series.color;line.series.color=disabledColor(line.series.color)});self.graph.update()},false);l.element.addEventListener("mouseout",function(e){if(!activeLine)return;else activeLine=null;self.legend.lines.forEach(function(line){if(l===line&&line.hasOwnProperty("originalIndex")){var series=self.graph.series.pop();self.graph.series.splice(line.originalIndex,0,series);delete line.originalIndex}if(colorSafe[line.series.name]){line.series.color=colorSafe[line.series.name]}});self.graph.update()},false)};if(this.legend){this.legend.lines.forEach(function(l){self.addHighlightEvents(l)})}};Rickshaw.namespace("Rickshaw.Graph.Behavior.Series.Order");Rickshaw.Graph.Behavior.Series.Order=function(args){this.graph=args.graph;this.legend=args.legend;var self=this;if(typeof window.$=="undefined"){throw"couldn't find jQuery at window.$"}if(typeof window.$.ui=="undefined"){throw"couldn't find jQuery UI at window.$.ui"}$(function(){$(self.legend.list).sortable({containment:"parent",tolerance:"pointer",update:function(event,ui){var series=[];$(self.legend.list).find("li").each(function(index,item){if(!item.series)return;series.push(item.series)});for(var i=self.graph.series.length-1;i>=0;i--){self.graph.series[i]=series.shift()}self.graph.update()}});$(self.legend.list).disableSelection()});this.graph.onUpdate(function(){var h=window.getComputedStyle(self.legend.element).height;self.legend.element.style.height=h})};Rickshaw.namespace("Rickshaw.Graph.Behavior.Series.Toggle");Rickshaw.Graph.Behavior.Series.Toggle=function(args){this.graph=args.graph;this.legend=args.legend;var self=this;this.addAnchor=function(line){var anchor=document.createElement("a");anchor.innerHTML="✔";anchor.classList.add("action");line.element.insertBefore(anchor,line.element.firstChild);anchor.onclick=function(e){if(line.series.disabled){line.series.enable();line.element.classList.remove("disabled")}else{if(this.graph.series.filter(function(s){return!s.disabled}).length<=1)return;line.series.disable();line.element.classList.add("disabled")}}.bind(this);var label=line.element.getElementsByTagName("span")[0];label.onclick=function(e){var disableAllOtherLines=line.series.disabled;if(!disableAllOtherLines){for(var i=0;idomainX){dataIndex=Math.abs(domainX-data[i].x)yMax)yMax=y});if(series[0].xxMax)xMax=series[series.length-1].x});xMin-=(xMax-xMin)*this.padding.left;xMax+=(xMax-xMin)*this.padding.right;yMin=this.graph.min==="auto"?yMin:this.graph.min||0;yMax=this.graph.max===undefined?yMax:this.graph.max;if(this.graph.min==="auto"||yMin<0){yMin-=(yMax-yMin)*this.padding.bottom}if(this.graph.max===undefined){yMax+=(yMax-yMin)*this.padding.top}return{x:[xMin,xMax],y:[yMin,yMax]}},render:function(args){args=args||{};var graph=this.graph;var series=args.series||graph.series;var vis=args.vis||graph.vis;vis.selectAll("*").remove();var data=series.filter(function(s){return!s.disabled}).map(function(s){return s.stack});var nodes=vis.selectAll("path").data(data).enter().append("svg:path").attr("d",this.seriesPathFactory());var i=0;series.forEach(function(series){if(series.disabled)return;series.path=nodes[0][i++];this._styleSeries(series)},this)},_styleSeries:function(series){var fill=this.fill?series.color:"none";var stroke=this.stroke?series.color:"none";series.path.setAttribute("fill",fill);series.path.setAttribute("stroke",stroke);series.path.setAttribute("stroke-width",this.strokeWidth);series.path.setAttribute("class",series.className)},configure:function(args){args=args||{};Rickshaw.keys(this.defaults()).forEach(function(key){if(!args.hasOwnProperty(key)){this[key]=this[key]||this.graph[key]||this.defaults()[key];return}if(typeof this.defaults()[key]=="object"){Rickshaw.keys(this.defaults()[key]).forEach(function(k){this[key][k]=args[key][k]!==undefined?args[key][k]:this[key][k]!==undefined?this[key][k]:this.defaults()[key][k]},this)}else{this[key]=args[key]!==undefined?args[key]:this[key]!==undefined?this[key]:this.graph[key]!==undefined?this.graph[key]:this.defaults()[key]}},this)},setStrokeWidth:function(strokeWidth){if(strokeWidth!==undefined){this.strokeWidth=strokeWidth}},setTension:function(tension){if(tension!==undefined){this.tension=tension}}});Rickshaw.namespace("Rickshaw.Graph.Renderer.Line");Rickshaw.Graph.Renderer.Line=Rickshaw.Class.create(Rickshaw.Graph.Renderer,{name:"line",defaults:function($super){return Rickshaw.extend($super(),{unstack:true,fill:false,stroke:true})},seriesPathFactory:function(){var graph=this.graph;var factory=d3.svg.line().x(function(d){return graph.x(d.x)}).y(function(d){return graph.y(d.y)}).interpolate(this.graph.interpolation).tension(this.tension);factory.defined&&factory.defined(function(d){return d.y!==null});return factory}});Rickshaw.namespace("Rickshaw.Graph.Renderer.Stack");Rickshaw.Graph.Renderer.Stack=Rickshaw.Class.create(Rickshaw.Graph.Renderer,{name:"stack",defaults:function($super){return Rickshaw.extend($super(),{fill:true,stroke:false,unstack:false})},seriesPathFactory:function(){var graph=this.graph;var factory=d3.svg.area().x(function(d){return graph.x(d.x)}).y0(function(d){return graph.y(d.y0)}).y1(function(d){return graph.y(d.y+d.y0)}).interpolate(this.graph.interpolation).tension(this.tension);factory.defined&&factory.defined(function(d){return d.y!==null});return factory}});Rickshaw.namespace("Rickshaw.Graph.Renderer.Bar");Rickshaw.Graph.Renderer.Bar=Rickshaw.Class.create(Rickshaw.Graph.Renderer,{name:"bar",defaults:function($super){var defaults=Rickshaw.extend($super(),{gapSize:.05,unstack:false});delete defaults.tension;return defaults},initialize:function($super,args){args=args||{};this.gapSize=args.gapSize||this.gapSize;$super(args)},domain:function($super){var domain=$super();var frequentInterval=this._frequentInterval(this.graph.stackedData.slice(-1).shift());domain.x[1]+=Number(frequentInterval.magnitude);return domain},barWidth:function(series){var frequentInterval=this._frequentInterval(series.stack);var barWidth=this.graph.x(series.stack[0].x+frequentInterval.magnitude*(1-this.gapSize));return barWidth},render:function(args){args=args||{};var graph=this.graph;var series=args.series||graph.series;var vis=args.vis||graph.vis;vis.selectAll("*").remove();var barWidth=this.barWidth(series.active()[0]);var barXOffset=0;var activeSeriesCount=series.filter(function(s){return!s.disabled}).length;var seriesBarWidth=this.unstack?barWidth/activeSeriesCount:barWidth;var transform=function(d){var matrix=[1,0,0,d.y<0?-1:1,0,d.y<0?graph.y.magnitude(Math.abs(d.y))*2:0];return"matrix("+matrix.join(",")+")"};series.forEach(function(series){if(series.disabled)return;var barWidth=this.barWidth(series);var nodes=vis.selectAll("path").data(series.stack.filter(function(d){return d.y!==null})).enter().append("svg:rect").attr("x",function(d){return graph.x(d.x)+barXOffset}).attr("y",function(d){return graph.y(d.y0+Math.abs(d.y))*(d.y<0?-1:1)}).attr("width",seriesBarWidth).attr("height",function(d){return graph.y.magnitude(Math.abs(d.y))}).attr("transform",transform);Array.prototype.forEach.call(nodes[0],function(n){n.setAttribute("fill",series.color)});if(this.unstack)barXOffset+=seriesBarWidth},this)},_frequentInterval:function(data){var intervalCounts={};for(var i=0;i0){this[0].data.forEach(function(plot){item.data.push({x:plot.x,y:0})})}else if(item.data.length===0){item.data.push({x:this.timeBase-(this.timeInterval||0),y:0})}this.push(item);if(this.legend){this.legend.addLine(this.itemByName(item.name))}},addData:function(data,x){var index=this.getIndex();Rickshaw.keys(data).forEach(function(name){if(!this.itemByName(name)){this.addItem({name:name})}},this);this.forEach(function(item){item.data.push({x:x||(index*this.timeInterval||1)+this.timeBase,y:data[item.name]||0})},this)},getIndex:function(){return this[0]&&this[0].data&&this[0].data.length?this[0].data.length:0},itemByName:function(name){for(var i=0;i1;i--){this.currentSize+=1;this.currentIndex+=1;this.forEach(function(item){item.data.unshift({x:((i-1)*this.timeInterval||1)+this.timeBase,y:0,i:i})},this)}}},addData:function($super,data,x){$super(data,x);this.currentSize+=1;this.currentIndex+=1;if(this.maxDataPoints!==undefined){while(this.currentSize>this.maxDataPoints){this.dropData()}}},dropData:function(){this.forEach(function(item){item.data.splice(0,1)});this.currentSize-=1},getIndex:function(){return this.currentIndex}});
\ No newline at end of file
diff --git a/fire_tablet_dashboard/assets/stylesheets/application.scss b/fire_tablet_dashboard/assets/stylesheets/application.scss
new file mode 100644
index 00000000..20e009fa
--- /dev/null
+++ b/fire_tablet_dashboard/assets/stylesheets/application.scss
@@ -0,0 +1,257 @@
+/*
+ //=require_directory .
+ //=require_tree ../../widgets
+*/
+// ----------------------------------------------------------------------------
+// Sass declarations
+// ----------------------------------------------------------------------------
+$background-color: #222;
+$text-color: #fff;
+
+$background-warning-color-1: #e82711;
+$background-warning-color-2: #9b2d23;
+$text-warning-color: #fff;
+
+$background-danger-color-1: #eeae32;
+$background-danger-color-2: #ff9618;
+$text-danger-color: #fff;
+
+@-webkit-keyframes status-warning-background {
+ 0% { background-color: $background-warning-color-1; }
+ 50% { background-color: $background-warning-color-2; }
+ 100% { background-color: $background-warning-color-1; }
+}
+@-webkit-keyframes status-danger-background {
+ 0% { background-color: $background-danger-color-1; }
+ 50% { background-color: $background-danger-color-2; }
+ 100% { background-color: $background-danger-color-1; }
+}
+@mixin animation($animation-name, $duration, $function, $animation-iteration-count:""){
+ -webkit-animation: $animation-name $duration $function #{$animation-iteration-count};
+ -moz-animation: $animation-name $duration $function #{$animation-iteration-count};
+ -ms-animation: $animation-name $duration $function #{$animation-iteration-count};
+}
+
+// ----------------------------------------------------------------------------
+// Base styles
+// ----------------------------------------------------------------------------
+html {
+ font-size: 100%;
+ -webkit-text-size-adjust: 100%;
+ -ms-text-size-adjust: 100%;
+}
+
+body {
+ margin: 0;
+ background-color: $background-color;
+ font-size: 20px;
+ color: $text-color;
+ font-family: 'Open Sans', "Helvetica Neue", Helvetica, Arial, sans-serif;
+}
+
+b, strong {
+ font-weight: bold;
+}
+
+a {
+ text-decoration: none;
+ color: inherit;
+}
+
+img {
+ border: 0;
+ -ms-interpolation-mode: bicubic;
+ vertical-align: middle;
+}
+
+img, object {
+ max-width: 100%;
+}
+
+iframe {
+ max-width: 100%;
+}
+
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+ width: 100%;
+}
+
+td {
+ vertical-align: middle;
+}
+
+ul, ol {
+ padding: 0;
+ margin: 0;
+}
+
+h1, h2, h3, h4, h5, p {
+ padding: 0;
+ margin: 0;
+}
+h1 {
+ margin-bottom: 12px;
+ text-align: center;
+ font-size: 30px;
+ font-weight: 400;
+}
+h2 {
+ text-transform: uppercase;
+ font-size: 76px;
+ font-weight: 700;
+ color: $text-color;
+}
+h3 {
+ font-size: 25px;
+ font-weight: 600;
+ color: $text-color;
+}
+
+// ----------------------------------------------------------------------------
+// Base widget styles
+// ----------------------------------------------------------------------------
+.gridster {
+ margin: 0px auto;
+}
+
+.icon-background {
+ width: 100%!important;
+ height: 100%;
+ position: absolute;
+ left: 0;
+ top: 0;
+ opacity: 0.1;
+ font-size: 275px;
+ text-align: center;
+ margin-top: 82px;
+}
+
+.list-nostyle {
+ list-style: none;
+}
+
+.gridster ul {
+ list-style: none;
+}
+
+.gs_w {
+ width: 100%;
+ display: table;
+ cursor: pointer;
+}
+
+.widget {
+ padding: 25px 12px;
+ text-align: center;
+ width: 100%;
+ display: table-cell;
+ vertical-align: middle;
+}
+
+.widget.status-warning {
+ background-color: $background-warning-color-1;
+ @include animation(status-warning-background, 2s, ease, infinite);
+
+ .icon-warning-sign {
+ display: inline-block;
+ }
+
+ .title, .more-info {
+ color: $text-warning-color;
+ }
+}
+
+.widget.status-danger {
+ color: $text-danger-color;
+ background-color: $background-danger-color-1;
+ @include animation(status-danger-background, 2s, ease, infinite);
+
+ .icon-warning-sign {
+ display: inline-block;
+ }
+
+ .title, .more-info {
+ color: $text-danger-color;
+ }
+}
+
+.more-info {
+ font-size: 15px;
+ position: absolute;
+ bottom: 32px;
+ left: 0;
+ right: 0;
+}
+
+.updated-at {
+ font-size: 15px;
+ position: absolute;
+ bottom: 12px;
+ left: 0;
+ right: 0;
+}
+
+#save-gridster {
+ display: none;
+ position: fixed;
+ top: 0;
+ margin: 0px auto;
+ left: 50%;
+ z-index: 1000;
+ background: black;
+ width: 190px;
+ text-align: center;
+ border: 1px solid white;
+ border-top: 0px;
+ margin-left: -95px;
+ padding: 15px;
+}
+
+#save-gridster:hover {
+ padding-top: 25px;
+}
+
+#saving-instructions {
+ display: none;
+ padding: 10px;
+ width: 500px;
+ height: 122px;
+ z-index: 1000;
+ background: white;
+ top: 100px;
+ color: black;
+ font-size: 15px;
+ padding-bottom: 4px;
+
+ textarea {
+ white-space: nowrap;
+ width: 494px;
+ height: 80px;
+ }
+}
+
+#lean_overlay {
+ position: fixed;
+ z-index:100;
+ top: 0px;
+ left: 0px;
+ height:100%;
+ width:100%;
+ background: #000;
+ display: none;
+}
+
+#container {
+ padding-top: 5px;
+}
+
+
+// ----------------------------------------------------------------------------
+// Clearfix
+// ----------------------------------------------------------------------------
+.clearfix:before, .clearfix:after { content: "\0020"; display: block; height: 0; overflow: hidden; }
+.clearfix:after { clear: both; }
+.clearfix { zoom: 1; }
+
diff --git a/fire_tablet_dashboard/assets/stylesheets/font-awesome.css b/fire_tablet_dashboard/assets/stylesheets/font-awesome.css
new file mode 100644
index 00000000..69ae8434
--- /dev/null
+++ b/fire_tablet_dashboard/assets/stylesheets/font-awesome.css
@@ -0,0 +1,1479 @@
+/*!
+ * Font Awesome 3.2.1
+ * the iconic font designed for Bootstrap
+ * ------------------------------------------------------------------------------
+ * The full suite of pictographic icons, examples, and documentation can be
+ * found at http://fontawesome.io. Stay up to date on Twitter at
+ * http://twitter.com/fontawesome.
+ *
+ * License
+ * ------------------------------------------------------------------------------
+ * - The Font Awesome font is licensed under SIL OFL 1.1 -
+ * http://scripts.sil.org/OFL
+ * - Font Awesome CSS, LESS, and SASS files are licensed under MIT License -
+ * http://opensource.org/licenses/mit-license.html
+ * - Font Awesome documentation licensed under CC BY 3.0 -
+ * http://creativecommons.org/licenses/by/3.0/
+ * - Attribution is no longer required in Font Awesome 3.0, but much appreciated:
+ * "Font Awesome by Dave Gandy - http://fontawesome.io"
+ *
+ * Author - Dave Gandy
+ * ------------------------------------------------------------------------------
+ * Email: dave@fontawesome.io
+ * Twitter: http://twitter.com/davegandy
+ * Work: Lead Product Designer @ Kyruus - http://kyruus.com
+ */
+/* FONT PATH
+ * -------------------------- */
+@font-face {
+ font-family: 'FontAwesome';
+ src: url('../assets/fontawesome-webfont.eot?v=3.2.1');
+ src: url('../assets/fontawesome-webfont.eot?#iefix&v=3.2.1') format('embedded-opentype'), url('../assets/fontawesome-webfont.woff?v=3.2.1') format('woff'), url('../assets/fontawesome-webfont.ttf?v=3.2.1') format('truetype'), url('../assets/fontawesome-webfont.svg#fontawesomeregular?v=3.2.1') format('svg');
+ font-weight: normal;
+ font-style: normal;
+}
+/* FONT AWESOME CORE
+ * -------------------------- */
+[class^="icon-"],
+[class*=" icon-"] {
+ font-family: FontAwesome;
+ font-weight: normal;
+ font-style: normal;
+ text-decoration: inherit;
+ -webkit-font-smoothing: antialiased;
+ *margin-right: .3em;
+}
+[class^="icon-"]:before,
+[class*=" icon-"]:before {
+ text-decoration: inherit;
+ display: inline-block;
+ speak: none;
+}
+/* makes the font 33% larger relative to the icon container */
+.icon-large:before {
+ vertical-align: -10%;
+ font-size: 1.3333333333333333em;
+}
+/* makes sure icons active on rollover in links */
+a [class^="icon-"],
+a [class*=" icon-"] {
+ display: inline;
+}
+/* increased font size for icon-large */
+[class^="icon-"].icon-fixed-width,
+[class*=" icon-"].icon-fixed-width {
+ display: inline-block;
+ width: 1.1428571428571428em;
+ text-align: right;
+ padding-right: 0.2857142857142857em;
+}
+[class^="icon-"].icon-fixed-width.icon-large,
+[class*=" icon-"].icon-fixed-width.icon-large {
+ width: 1.4285714285714286em;
+}
+.icons-ul {
+ margin-left: 2.142857142857143em;
+ list-style-type: none;
+}
+.icons-ul > li {
+ position: relative;
+}
+.icons-ul .icon-li {
+ position: absolute;
+ left: -2.142857142857143em;
+ width: 2.142857142857143em;
+ text-align: center;
+ line-height: inherit;
+}
+[class^="icon-"].hide,
+[class*=" icon-"].hide {
+ display: none;
+}
+.icon-muted {
+ color: #eeeeee;
+}
+.icon-light {
+ color: #ffffff;
+}
+.icon-dark {
+ color: #333333;
+}
+.icon-border {
+ border: solid 1px #eeeeee;
+ padding: .2em .25em .15em;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+}
+.icon-2x {
+ font-size: 2em;
+}
+.icon-2x.icon-border {
+ border-width: 2px;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+.icon-3x {
+ font-size: 3em;
+}
+.icon-3x.icon-border {
+ border-width: 3px;
+ -webkit-border-radius: 5px;
+ -moz-border-radius: 5px;
+ border-radius: 5px;
+}
+.icon-4x {
+ font-size: 4em;
+}
+.icon-4x.icon-border {
+ border-width: 4px;
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+}
+.icon-5x {
+ font-size: 5em;
+}
+.icon-5x.icon-border {
+ border-width: 5px;
+ -webkit-border-radius: 7px;
+ -moz-border-radius: 7px;
+ border-radius: 7px;
+}
+.pull-right {
+ float: right;
+}
+.pull-left {
+ float: left;
+}
+[class^="icon-"].pull-left,
+[class*=" icon-"].pull-left {
+ margin-right: .3em;
+}
+[class^="icon-"].pull-right,
+[class*=" icon-"].pull-right {
+ margin-left: .3em;
+}
+/* BOOTSTRAP SPECIFIC CLASSES
+ * -------------------------- */
+/* Bootstrap 2.0 sprites.less reset */
+[class^="icon-"],
+[class*=" icon-"] {
+ display: inline;
+ width: auto;
+ height: auto;
+ line-height: normal;
+ vertical-align: baseline;
+ background-image: none;
+ background-position: 0% 0%;
+ background-repeat: repeat;
+ margin-top: 0;
+}
+/* more sprites.less reset */
+.icon-white,
+.nav-pills > .active > a > [class^="icon-"],
+.nav-pills > .active > a > [class*=" icon-"],
+.nav-list > .active > a > [class^="icon-"],
+.nav-list > .active > a > [class*=" icon-"],
+.navbar-inverse .nav > .active > a > [class^="icon-"],
+.navbar-inverse .nav > .active > a > [class*=" icon-"],
+.dropdown-menu > li > a:hover > [class^="icon-"],
+.dropdown-menu > li > a:hover > [class*=" icon-"],
+.dropdown-menu > .active > a > [class^="icon-"],
+.dropdown-menu > .active > a > [class*=" icon-"],
+.dropdown-submenu:hover > a > [class^="icon-"],
+.dropdown-submenu:hover > a > [class*=" icon-"] {
+ background-image: none;
+}
+/* keeps Bootstrap styles with and without icons the same */
+.btn [class^="icon-"].icon-large,
+.nav [class^="icon-"].icon-large,
+.btn [class*=" icon-"].icon-large,
+.nav [class*=" icon-"].icon-large {
+ line-height: .9em;
+}
+.btn [class^="icon-"].icon-spin,
+.nav [class^="icon-"].icon-spin,
+.btn [class*=" icon-"].icon-spin,
+.nav [class*=" icon-"].icon-spin {
+ display: inline-block;
+}
+.nav-tabs [class^="icon-"],
+.nav-pills [class^="icon-"],
+.nav-tabs [class*=" icon-"],
+.nav-pills [class*=" icon-"],
+.nav-tabs [class^="icon-"].icon-large,
+.nav-pills [class^="icon-"].icon-large,
+.nav-tabs [class*=" icon-"].icon-large,
+.nav-pills [class*=" icon-"].icon-large {
+ line-height: .9em;
+}
+.btn [class^="icon-"].pull-left.icon-2x,
+.btn [class*=" icon-"].pull-left.icon-2x,
+.btn [class^="icon-"].pull-right.icon-2x,
+.btn [class*=" icon-"].pull-right.icon-2x {
+ margin-top: .18em;
+}
+.btn [class^="icon-"].icon-spin.icon-large,
+.btn [class*=" icon-"].icon-spin.icon-large {
+ line-height: .8em;
+}
+.btn.btn-small [class^="icon-"].pull-left.icon-2x,
+.btn.btn-small [class*=" icon-"].pull-left.icon-2x,
+.btn.btn-small [class^="icon-"].pull-right.icon-2x,
+.btn.btn-small [class*=" icon-"].pull-right.icon-2x {
+ margin-top: .25em;
+}
+.btn.btn-large [class^="icon-"],
+.btn.btn-large [class*=" icon-"] {
+ margin-top: 0;
+}
+.btn.btn-large [class^="icon-"].pull-left.icon-2x,
+.btn.btn-large [class*=" icon-"].pull-left.icon-2x,
+.btn.btn-large [class^="icon-"].pull-right.icon-2x,
+.btn.btn-large [class*=" icon-"].pull-right.icon-2x {
+ margin-top: .05em;
+}
+.btn.btn-large [class^="icon-"].pull-left.icon-2x,
+.btn.btn-large [class*=" icon-"].pull-left.icon-2x {
+ margin-right: .2em;
+}
+.btn.btn-large [class^="icon-"].pull-right.icon-2x,
+.btn.btn-large [class*=" icon-"].pull-right.icon-2x {
+ margin-left: .2em;
+}
+/* Fixes alignment in nav lists */
+.nav-list [class^="icon-"],
+.nav-list [class*=" icon-"] {
+ line-height: inherit;
+}
+/* EXTRAS
+ * -------------------------- */
+/* Stacked and layered icon */
+.icon-stack {
+ position: relative;
+ display: inline-block;
+ width: 2em;
+ height: 2em;
+ line-height: 2em;
+ vertical-align: -35%;
+}
+.icon-stack [class^="icon-"],
+.icon-stack [class*=" icon-"] {
+ display: block;
+ text-align: center;
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ font-size: 1em;
+ line-height: inherit;
+ *line-height: 2em;
+}
+.icon-stack .icon-stack-base {
+ font-size: 2em;
+ *line-height: 1em;
+}
+/* Animated rotating icon */
+.icon-spin {
+ display: inline-block;
+ -moz-animation: spin 2s infinite linear;
+ -o-animation: spin 2s infinite linear;
+ -webkit-animation: spin 2s infinite linear;
+ animation: spin 2s infinite linear;
+}
+/* Prevent stack and spinners from being taken inline when inside a link */
+a .icon-stack,
+a .icon-spin {
+ display: inline-block;
+ text-decoration: none;
+}
+@-moz-keyframes spin {
+ 0% {
+ -moz-transform: rotate(0deg);
+ }
+ 100% {
+ -moz-transform: rotate(359deg);
+ }
+}
+@-webkit-keyframes spin {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(359deg);
+ }
+}
+@-o-keyframes spin {
+ 0% {
+ -o-transform: rotate(0deg);
+ }
+ 100% {
+ -o-transform: rotate(359deg);
+ }
+}
+@-ms-keyframes spin {
+ 0% {
+ -ms-transform: rotate(0deg);
+ }
+ 100% {
+ -ms-transform: rotate(359deg);
+ }
+}
+@keyframes spin {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(359deg);
+ }
+}
+/* Icon rotations and mirroring */
+.icon-rotate-90:before {
+ -webkit-transform: rotate(90deg);
+ -moz-transform: rotate(90deg);
+ -ms-transform: rotate(90deg);
+ -o-transform: rotate(90deg);
+ transform: rotate(90deg);
+ filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1);
+}
+.icon-rotate-180:before {
+ -webkit-transform: rotate(180deg);
+ -moz-transform: rotate(180deg);
+ -ms-transform: rotate(180deg);
+ -o-transform: rotate(180deg);
+ transform: rotate(180deg);
+ filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2);
+}
+.icon-rotate-270:before {
+ -webkit-transform: rotate(270deg);
+ -moz-transform: rotate(270deg);
+ -ms-transform: rotate(270deg);
+ -o-transform: rotate(270deg);
+ transform: rotate(270deg);
+ filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);
+}
+.icon-flip-horizontal:before {
+ -webkit-transform: scale(-1, 1);
+ -moz-transform: scale(-1, 1);
+ -ms-transform: scale(-1, 1);
+ -o-transform: scale(-1, 1);
+ transform: scale(-1, 1);
+}
+.icon-flip-vertical:before {
+ -webkit-transform: scale(1, -1);
+ -moz-transform: scale(1, -1);
+ -ms-transform: scale(1, -1);
+ -o-transform: scale(1, -1);
+ transform: scale(1, -1);
+}
+/* ensure rotation occurs inside anchor tags */
+a .icon-rotate-90:before,
+a .icon-rotate-180:before,
+a .icon-rotate-270:before,
+a .icon-flip-horizontal:before,
+a .icon-flip-vertical:before {
+ display: inline-block;
+}
+/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen
+ readers do not read off random characters that represent icons */
+.icon-glass:before {
+ content: "\f000";
+}
+.icon-music:before {
+ content: "\f001";
+}
+.icon-search:before {
+ content: "\f002";
+}
+.icon-envelope-alt:before {
+ content: "\f003";
+}
+.icon-heart:before {
+ content: "\f004";
+}
+.icon-star:before {
+ content: "\f005";
+}
+.icon-star-empty:before {
+ content: "\f006";
+}
+.icon-user:before {
+ content: "\f007";
+}
+.icon-film:before {
+ content: "\f008";
+}
+.icon-th-large:before {
+ content: "\f009";
+}
+.icon-th:before {
+ content: "\f00a";
+}
+.icon-th-list:before {
+ content: "\f00b";
+}
+.icon-ok:before {
+ content: "\f00c";
+}
+.icon-remove:before {
+ content: "\f00d";
+}
+.icon-zoom-in:before {
+ content: "\f00e";
+}
+.icon-zoom-out:before {
+ content: "\f010";
+}
+.icon-power-off:before,
+.icon-off:before {
+ content: "\f011";
+}
+.icon-signal:before {
+ content: "\f012";
+}
+.icon-gear:before,
+.icon-cog:before {
+ content: "\f013";
+}
+.icon-trash:before {
+ content: "\f014";
+}
+.icon-home:before {
+ content: "\f015";
+}
+.icon-file-alt:before {
+ content: "\f016";
+}
+.icon-time:before {
+ content: "\f017";
+}
+.icon-road:before {
+ content: "\f018";
+}
+.icon-download-alt:before {
+ content: "\f019";
+}
+.icon-download:before {
+ content: "\f01a";
+}
+.icon-upload:before {
+ content: "\f01b";
+}
+.icon-inbox:before {
+ content: "\f01c";
+}
+.icon-play-circle:before {
+ content: "\f01d";
+}
+.icon-rotate-right:before,
+.icon-repeat:before {
+ content: "\f01e";
+}
+.icon-refresh:before {
+ content: "\f021";
+}
+.icon-list-alt:before {
+ content: "\f022";
+}
+.icon-lock:before {
+ content: "\f023";
+}
+.icon-flag:before {
+ content: "\f024";
+}
+.icon-headphones:before {
+ content: "\f025";
+}
+.icon-volume-off:before {
+ content: "\f026";
+}
+.icon-volume-down:before {
+ content: "\f027";
+}
+.icon-volume-up:before {
+ content: "\f028";
+}
+.icon-qrcode:before {
+ content: "\f029";
+}
+.icon-barcode:before {
+ content: "\f02a";
+}
+.icon-tag:before {
+ content: "\f02b";
+}
+.icon-tags:before {
+ content: "\f02c";
+}
+.icon-book:before {
+ content: "\f02d";
+}
+.icon-bookmark:before {
+ content: "\f02e";
+}
+.icon-print:before {
+ content: "\f02f";
+}
+.icon-camera:before {
+ content: "\f030";
+}
+.icon-font:before {
+ content: "\f031";
+}
+.icon-bold:before {
+ content: "\f032";
+}
+.icon-italic:before {
+ content: "\f033";
+}
+.icon-text-height:before {
+ content: "\f034";
+}
+.icon-text-width:before {
+ content: "\f035";
+}
+.icon-align-left:before {
+ content: "\f036";
+}
+.icon-align-center:before {
+ content: "\f037";
+}
+.icon-align-right:before {
+ content: "\f038";
+}
+.icon-align-justify:before {
+ content: "\f039";
+}
+.icon-list:before {
+ content: "\f03a";
+}
+.icon-indent-left:before {
+ content: "\f03b";
+}
+.icon-indent-right:before {
+ content: "\f03c";
+}
+.icon-facetime-video:before {
+ content: "\f03d";
+}
+.icon-picture:before {
+ content: "\f03e";
+}
+.icon-pencil:before {
+ content: "\f040";
+}
+.icon-map-marker:before {
+ content: "\f041";
+}
+.icon-adjust:before {
+ content: "\f042";
+}
+.icon-tint:before {
+ content: "\f043";
+}
+.icon-edit:before {
+ content: "\f044";
+}
+.icon-share:before {
+ content: "\f045";
+}
+.icon-check:before {
+ content: "\f046";
+}
+.icon-move:before {
+ content: "\f047";
+}
+.icon-step-backward:before {
+ content: "\f048";
+}
+.icon-fast-backward:before {
+ content: "\f049";
+}
+.icon-backward:before {
+ content: "\f04a";
+}
+.icon-play:before {
+ content: "\f04b";
+}
+.icon-pause:before {
+ content: "\f04c";
+}
+.icon-stop:before {
+ content: "\f04d";
+}
+.icon-forward:before {
+ content: "\f04e";
+}
+.icon-fast-forward:before {
+ content: "\f050";
+}
+.icon-step-forward:before {
+ content: "\f051";
+}
+.icon-eject:before {
+ content: "\f052";
+}
+.icon-chevron-left:before {
+ content: "\f053";
+}
+.icon-chevron-right:before {
+ content: "\f054";
+}
+.icon-plus-sign:before {
+ content: "\f055";
+}
+.icon-minus-sign:before {
+ content: "\f056";
+}
+.icon-remove-sign:before {
+ content: "\f057";
+}
+.icon-ok-sign:before {
+ content: "\f058";
+}
+.icon-question-sign:before {
+ content: "\f059";
+}
+.icon-info-sign:before {
+ content: "\f05a";
+}
+.icon-screenshot:before {
+ content: "\f05b";
+}
+.icon-remove-circle:before {
+ content: "\f05c";
+}
+.icon-ok-circle:before {
+ content: "\f05d";
+}
+.icon-ban-circle:before {
+ content: "\f05e";
+}
+.icon-arrow-left:before {
+ content: "\f060";
+}
+.icon-arrow-right:before {
+ content: "\f061";
+}
+.icon-arrow-up:before {
+ content: "\f062";
+}
+.icon-arrow-down:before {
+ content: "\f063";
+}
+.icon-mail-forward:before,
+.icon-share-alt:before {
+ content: "\f064";
+}
+.icon-resize-full:before {
+ content: "\f065";
+}
+.icon-resize-small:before {
+ content: "\f066";
+}
+.icon-plus:before {
+ content: "\f067";
+}
+.icon-minus:before {
+ content: "\f068";
+}
+.icon-asterisk:before {
+ content: "\f069";
+}
+.icon-exclamation-sign:before {
+ content: "\f06a";
+}
+.icon-gift:before {
+ content: "\f06b";
+}
+.icon-leaf:before {
+ content: "\f06c";
+}
+.icon-fire:before {
+ content: "\f06d";
+}
+.icon-eye-open:before {
+ content: "\f06e";
+}
+.icon-eye-close:before {
+ content: "\f070";
+}
+.icon-warning-sign:before {
+ content: "\f071";
+}
+.icon-plane:before {
+ content: "\f072";
+}
+.icon-calendar:before {
+ content: "\f073";
+}
+.icon-random:before {
+ content: "\f074";
+}
+.icon-comment:before {
+ content: "\f075";
+}
+.icon-magnet:before {
+ content: "\f076";
+}
+.icon-chevron-up:before {
+ content: "\f077";
+}
+.icon-chevron-down:before {
+ content: "\f078";
+}
+.icon-retweet:before {
+ content: "\f079";
+}
+.icon-shopping-cart:before {
+ content: "\f07a";
+}
+.icon-folder-close:before {
+ content: "\f07b";
+}
+.icon-folder-open:before {
+ content: "\f07c";
+}
+.icon-resize-vertical:before {
+ content: "\f07d";
+}
+.icon-resize-horizontal:before {
+ content: "\f07e";
+}
+.icon-bar-chart:before {
+ content: "\f080";
+}
+.icon-twitter-sign:before {
+ content: "\f081";
+}
+.icon-facebook-sign:before {
+ content: "\f082";
+}
+.icon-camera-retro:before {
+ content: "\f083";
+}
+.icon-key:before {
+ content: "\f084";
+}
+.icon-gears:before,
+.icon-cogs:before {
+ content: "\f085";
+}
+.icon-comments:before {
+ content: "\f086";
+}
+.icon-thumbs-up-alt:before {
+ content: "\f087";
+}
+.icon-thumbs-down-alt:before {
+ content: "\f088";
+}
+.icon-star-half:before {
+ content: "\f089";
+}
+.icon-heart-empty:before {
+ content: "\f08a";
+}
+.icon-signout:before {
+ content: "\f08b";
+}
+.icon-linkedin-sign:before {
+ content: "\f08c";
+}
+.icon-pushpin:before {
+ content: "\f08d";
+}
+.icon-external-link:before {
+ content: "\f08e";
+}
+.icon-signin:before {
+ content: "\f090";
+}
+.icon-trophy:before {
+ content: "\f091";
+}
+.icon-github-sign:before {
+ content: "\f092";
+}
+.icon-upload-alt:before {
+ content: "\f093";
+}
+.icon-lemon:before {
+ content: "\f094";
+}
+.icon-phone:before {
+ content: "\f095";
+}
+.icon-unchecked:before,
+.icon-check-empty:before {
+ content: "\f096";
+}
+.icon-bookmark-empty:before {
+ content: "\f097";
+}
+.icon-phone-sign:before {
+ content: "\f098";
+}
+.icon-twitter:before {
+ content: "\f099";
+}
+.icon-facebook:before {
+ content: "\f09a";
+}
+.icon-github:before {
+ content: "\f09b";
+}
+.icon-unlock:before {
+ content: "\f09c";
+}
+.icon-credit-card:before {
+ content: "\f09d";
+}
+.icon-rss:before {
+ content: "\f09e";
+}
+.icon-hdd:before {
+ content: "\f0a0";
+}
+.icon-bullhorn:before {
+ content: "\f0a1";
+}
+.icon-bell:before {
+ content: "\f0a2";
+}
+.icon-certificate:before {
+ content: "\f0a3";
+}
+.icon-hand-right:before {
+ content: "\f0a4";
+}
+.icon-hand-left:before {
+ content: "\f0a5";
+}
+.icon-hand-up:before {
+ content: "\f0a6";
+}
+.icon-hand-down:before {
+ content: "\f0a7";
+}
+.icon-circle-arrow-left:before {
+ content: "\f0a8";
+}
+.icon-circle-arrow-right:before {
+ content: "\f0a9";
+}
+.icon-circle-arrow-up:before {
+ content: "\f0aa";
+}
+.icon-circle-arrow-down:before {
+ content: "\f0ab";
+}
+.icon-globe:before {
+ content: "\f0ac";
+}
+.icon-wrench:before {
+ content: "\f0ad";
+}
+.icon-tasks:before {
+ content: "\f0ae";
+}
+.icon-filter:before {
+ content: "\f0b0";
+}
+.icon-briefcase:before {
+ content: "\f0b1";
+}
+.icon-fullscreen:before {
+ content: "\f0b2";
+}
+.icon-group:before {
+ content: "\f0c0";
+}
+.icon-link:before {
+ content: "\f0c1";
+}
+.icon-cloud:before {
+ content: "\f0c2";
+}
+.icon-beaker:before {
+ content: "\f0c3";
+}
+.icon-cut:before {
+ content: "\f0c4";
+}
+.icon-copy:before {
+ content: "\f0c5";
+}
+.icon-paperclip:before,
+.icon-paper-clip:before {
+ content: "\f0c6";
+}
+.icon-save:before {
+ content: "\f0c7";
+}
+.icon-sign-blank:before {
+ content: "\f0c8";
+}
+.icon-reorder:before {
+ content: "\f0c9";
+}
+.icon-list-ul:before {
+ content: "\f0ca";
+}
+.icon-list-ol:before {
+ content: "\f0cb";
+}
+.icon-strikethrough:before {
+ content: "\f0cc";
+}
+.icon-underline:before {
+ content: "\f0cd";
+}
+.icon-table:before {
+ content: "\f0ce";
+}
+.icon-magic:before {
+ content: "\f0d0";
+}
+.icon-truck:before {
+ content: "\f0d1";
+}
+.icon-pinterest:before {
+ content: "\f0d2";
+}
+.icon-pinterest-sign:before {
+ content: "\f0d3";
+}
+.icon-google-plus-sign:before {
+ content: "\f0d4";
+}
+.icon-google-plus:before {
+ content: "\f0d5";
+}
+.icon-money:before {
+ content: "\f0d6";
+}
+.icon-caret-down:before {
+ content: "\f0d7";
+}
+.icon-caret-up:before {
+ content: "\f0d8";
+}
+.icon-caret-left:before {
+ content: "\f0d9";
+}
+.icon-caret-right:before {
+ content: "\f0da";
+}
+.icon-columns:before {
+ content: "\f0db";
+}
+.icon-sort:before {
+ content: "\f0dc";
+}
+.icon-sort-down:before {
+ content: "\f0dd";
+}
+.icon-sort-up:before {
+ content: "\f0de";
+}
+.icon-envelope:before {
+ content: "\f0e0";
+}
+.icon-linkedin:before {
+ content: "\f0e1";
+}
+.icon-rotate-left:before,
+.icon-undo:before {
+ content: "\f0e2";
+}
+.icon-legal:before {
+ content: "\f0e3";
+}
+.icon-dashboard:before {
+ content: "\f0e4";
+}
+.icon-comment-alt:before {
+ content: "\f0e5";
+}
+.icon-comments-alt:before {
+ content: "\f0e6";
+}
+.icon-bolt:before {
+ content: "\f0e7";
+}
+.icon-sitemap:before {
+ content: "\f0e8";
+}
+.icon-umbrella:before {
+ content: "\f0e9";
+}
+.icon-paste:before {
+ content: "\f0ea";
+}
+.icon-lightbulb:before {
+ content: "\f0eb";
+}
+.icon-exchange:before {
+ content: "\f0ec";
+}
+.icon-cloud-download:before {
+ content: "\f0ed";
+}
+.icon-cloud-upload:before {
+ content: "\f0ee";
+}
+.icon-user-md:before {
+ content: "\f0f0";
+}
+.icon-stethoscope:before {
+ content: "\f0f1";
+}
+.icon-suitcase:before {
+ content: "\f0f2";
+}
+.icon-bell-alt:before {
+ content: "\f0f3";
+}
+.icon-coffee:before {
+ content: "\f0f4";
+}
+.icon-food:before {
+ content: "\f0f5";
+}
+.icon-file-text-alt:before {
+ content: "\f0f6";
+}
+.icon-building:before {
+ content: "\f0f7";
+}
+.icon-hospital:before {
+ content: "\f0f8";
+}
+.icon-ambulance:before {
+ content: "\f0f9";
+}
+.icon-medkit:before {
+ content: "\f0fa";
+}
+.icon-fighter-jet:before {
+ content: "\f0fb";
+}
+.icon-beer:before {
+ content: "\f0fc";
+}
+.icon-h-sign:before {
+ content: "\f0fd";
+}
+.icon-plus-sign-alt:before {
+ content: "\f0fe";
+}
+.icon-double-angle-left:before {
+ content: "\f100";
+}
+.icon-double-angle-right:before {
+ content: "\f101";
+}
+.icon-double-angle-up:before {
+ content: "\f102";
+}
+.icon-double-angle-down:before {
+ content: "\f103";
+}
+.icon-angle-left:before {
+ content: "\f104";
+}
+.icon-angle-right:before {
+ content: "\f105";
+}
+.icon-angle-up:before {
+ content: "\f106";
+}
+.icon-angle-down:before {
+ content: "\f107";
+}
+.icon-desktop:before {
+ content: "\f108";
+}
+.icon-laptop:before {
+ content: "\f109";
+}
+.icon-tablet:before {
+ content: "\f10a";
+}
+.icon-mobile-phone:before {
+ content: "\f10b";
+}
+.icon-circle-blank:before {
+ content: "\f10c";
+}
+.icon-quote-left:before {
+ content: "\f10d";
+}
+.icon-quote-right:before {
+ content: "\f10e";
+}
+.icon-spinner:before {
+ content: "\f110";
+}
+.icon-circle:before {
+ content: "\f111";
+}
+.icon-mail-reply:before,
+.icon-reply:before {
+ content: "\f112";
+}
+.icon-github-alt:before {
+ content: "\f113";
+}
+.icon-folder-close-alt:before {
+ content: "\f114";
+}
+.icon-folder-open-alt:before {
+ content: "\f115";
+}
+.icon-expand-alt:before {
+ content: "\f116";
+}
+.icon-collapse-alt:before {
+ content: "\f117";
+}
+.icon-smile:before {
+ content: "\f118";
+}
+.icon-frown:before {
+ content: "\f119";
+}
+.icon-meh:before {
+ content: "\f11a";
+}
+.icon-gamepad:before {
+ content: "\f11b";
+}
+.icon-keyboard:before {
+ content: "\f11c";
+}
+.icon-flag-alt:before {
+ content: "\f11d";
+}
+.icon-flag-checkered:before {
+ content: "\f11e";
+}
+.icon-terminal:before {
+ content: "\f120";
+}
+.icon-code:before {
+ content: "\f121";
+}
+.icon-reply-all:before {
+ content: "\f122";
+}
+.icon-mail-reply-all:before {
+ content: "\f122";
+}
+.icon-star-half-full:before,
+.icon-star-half-empty:before {
+ content: "\f123";
+}
+.icon-location-arrow:before {
+ content: "\f124";
+}
+.icon-crop:before {
+ content: "\f125";
+}
+.icon-code-fork:before {
+ content: "\f126";
+}
+.icon-unlink:before {
+ content: "\f127";
+}
+.icon-question:before {
+ content: "\f128";
+}
+.icon-info:before {
+ content: "\f129";
+}
+.icon-exclamation:before {
+ content: "\f12a";
+}
+.icon-superscript:before {
+ content: "\f12b";
+}
+.icon-subscript:before {
+ content: "\f12c";
+}
+.icon-eraser:before {
+ content: "\f12d";
+}
+.icon-puzzle-piece:before {
+ content: "\f12e";
+}
+.icon-microphone:before {
+ content: "\f130";
+}
+.icon-microphone-off:before {
+ content: "\f131";
+}
+.icon-shield:before {
+ content: "\f132";
+}
+.icon-calendar-empty:before {
+ content: "\f133";
+}
+.icon-fire-extinguisher:before {
+ content: "\f134";
+}
+.icon-rocket:before {
+ content: "\f135";
+}
+.icon-maxcdn:before {
+ content: "\f136";
+}
+.icon-chevron-sign-left:before {
+ content: "\f137";
+}
+.icon-chevron-sign-right:before {
+ content: "\f138";
+}
+.icon-chevron-sign-up:before {
+ content: "\f139";
+}
+.icon-chevron-sign-down:before {
+ content: "\f13a";
+}
+.icon-html5:before {
+ content: "\f13b";
+}
+.icon-css3:before {
+ content: "\f13c";
+}
+.icon-anchor:before {
+ content: "\f13d";
+}
+.icon-unlock-alt:before {
+ content: "\f13e";
+}
+.icon-bullseye:before {
+ content: "\f140";
+}
+.icon-ellipsis-horizontal:before {
+ content: "\f141";
+}
+.icon-ellipsis-vertical:before {
+ content: "\f142";
+}
+.icon-rss-sign:before {
+ content: "\f143";
+}
+.icon-play-sign:before {
+ content: "\f144";
+}
+.icon-ticket:before {
+ content: "\f145";
+}
+.icon-minus-sign-alt:before {
+ content: "\f146";
+}
+.icon-check-minus:before {
+ content: "\f147";
+}
+.icon-level-up:before {
+ content: "\f148";
+}
+.icon-level-down:before {
+ content: "\f149";
+}
+.icon-check-sign:before {
+ content: "\f14a";
+}
+.icon-edit-sign:before {
+ content: "\f14b";
+}
+.icon-external-link-sign:before {
+ content: "\f14c";
+}
+.icon-share-sign:before {
+ content: "\f14d";
+}
+.icon-compass:before {
+ content: "\f14e";
+}
+.icon-collapse:before {
+ content: "\f150";
+}
+.icon-collapse-top:before {
+ content: "\f151";
+}
+.icon-expand:before {
+ content: "\f152";
+}
+.icon-euro:before,
+.icon-eur:before {
+ content: "\f153";
+}
+.icon-gbp:before {
+ content: "\f154";
+}
+.icon-dollar:before,
+.icon-usd:before {
+ content: "\f155";
+}
+.icon-rupee:before,
+.icon-inr:before {
+ content: "\f156";
+}
+.icon-yen:before,
+.icon-jpy:before {
+ content: "\f157";
+}
+.icon-renminbi:before,
+.icon-cny:before {
+ content: "\f158";
+}
+.icon-won:before,
+.icon-krw:before {
+ content: "\f159";
+}
+.icon-bitcoin:before,
+.icon-btc:before {
+ content: "\f15a";
+}
+.icon-file:before {
+ content: "\f15b";
+}
+.icon-file-text:before {
+ content: "\f15c";
+}
+.icon-sort-by-alphabet:before {
+ content: "\f15d";
+}
+.icon-sort-by-alphabet-alt:before {
+ content: "\f15e";
+}
+.icon-sort-by-attributes:before {
+ content: "\f160";
+}
+.icon-sort-by-attributes-alt:before {
+ content: "\f161";
+}
+.icon-sort-by-order:before {
+ content: "\f162";
+}
+.icon-sort-by-order-alt:before {
+ content: "\f163";
+}
+.icon-thumbs-up:before {
+ content: "\f164";
+}
+.icon-thumbs-down:before {
+ content: "\f165";
+}
+.icon-youtube-sign:before {
+ content: "\f166";
+}
+.icon-youtube:before {
+ content: "\f167";
+}
+.icon-xing:before {
+ content: "\f168";
+}
+.icon-xing-sign:before {
+ content: "\f169";
+}
+.icon-youtube-play:before {
+ content: "\f16a";
+}
+.icon-dropbox:before {
+ content: "\f16b";
+}
+.icon-stackexchange:before {
+ content: "\f16c";
+}
+.icon-instagram:before {
+ content: "\f16d";
+}
+.icon-flickr:before {
+ content: "\f16e";
+}
+.icon-adn:before {
+ content: "\f170";
+}
+.icon-bitbucket:before {
+ content: "\f171";
+}
+.icon-bitbucket-sign:before {
+ content: "\f172";
+}
+.icon-tumblr:before {
+ content: "\f173";
+}
+.icon-tumblr-sign:before {
+ content: "\f174";
+}
+.icon-long-arrow-down:before {
+ content: "\f175";
+}
+.icon-long-arrow-up:before {
+ content: "\f176";
+}
+.icon-long-arrow-left:before {
+ content: "\f177";
+}
+.icon-long-arrow-right:before {
+ content: "\f178";
+}
+.icon-apple:before {
+ content: "\f179";
+}
+.icon-windows:before {
+ content: "\f17a";
+}
+.icon-android:before {
+ content: "\f17b";
+}
+.icon-linux:before {
+ content: "\f17c";
+}
+.icon-dribbble:before {
+ content: "\f17d";
+}
+.icon-skype:before {
+ content: "\f17e";
+}
+.icon-foursquare:before {
+ content: "\f180";
+}
+.icon-trello:before {
+ content: "\f181";
+}
+.icon-female:before {
+ content: "\f182";
+}
+.icon-male:before {
+ content: "\f183";
+}
+.icon-gittip:before {
+ content: "\f184";
+}
+.icon-sun:before {
+ content: "\f185";
+}
+.icon-moon:before {
+ content: "\f186";
+}
+.icon-archive:before {
+ content: "\f187";
+}
+.icon-bug:before {
+ content: "\f188";
+}
+.icon-vk:before {
+ content: "\f189";
+}
+.icon-weibo:before {
+ content: "\f18a";
+}
+.icon-renren:before {
+ content: "\f18b";
+}
diff --git a/fire_tablet_dashboard/assets/stylesheets/jquery.gridster.css b/fire_tablet_dashboard/assets/stylesheets/jquery.gridster.css
new file mode 100644
index 00000000..d5124848
--- /dev/null
+++ b/fire_tablet_dashboard/assets/stylesheets/jquery.gridster.css
@@ -0,0 +1,57 @@
+/*! gridster.js - v0.1.0 - 2012-08-14
+* http://gridster.net/
+* Copyright (c) 2012 ducksboard; Licensed MIT */
+
+.gridster {
+ position:relative;
+}
+
+.gridster > * {
+ margin: 0 auto;
+ -webkit-transition: height .4s;
+ -moz-transition: height .4s;
+ -o-transition: height .4s;
+ -ms-transition: height .4s;
+ transition: height .4s;
+}
+
+.gridster .gs_w{
+ z-index: 2;
+ position: absolute;
+}
+
+.ready .gs_w:not(.preview-holder) {
+ -webkit-transition: opacity .3s, left .3s, top .3s;
+ -moz-transition: opacity .3s, left .3s, top .3s;
+ -o-transition: opacity .3s, left .3s, top .3s;
+ transition: opacity .3s, left .3s, top .3s;
+}
+
+.gridster .preview-holder {
+ z-index: 1;
+ position: absolute;
+ background-color: #fff;
+ border-color: #fff;
+ opacity: 0.3;
+}
+
+.gridster .player-revert {
+ z-index: 10!important;
+ -webkit-transition: left .3s, top .3s!important;
+ -moz-transition: left .3s, top .3s!important;
+ -o-transition: left .3s, top .3s!important;
+ transition: left .3s, top .3s!important;
+}
+
+.gridster .dragging {
+ z-index: 10!important;
+ -webkit-transition: all 0s !important;
+ -moz-transition: all 0s !important;
+ -o-transition: all 0s !important;
+ transition: all 0s !important;
+}
+
+/* Uncomment this if you set helper : "clone" in draggable options */
+/*.gridster .player {
+ opacity:0;
+}*/
\ No newline at end of file
diff --git a/fire_tablet_dashboard/config.ru b/fire_tablet_dashboard/config.ru
new file mode 100644
index 00000000..6e1076a0
--- /dev/null
+++ b/fire_tablet_dashboard/config.ru
@@ -0,0 +1,18 @@
+require 'dashing'
+
+configure do
+ set :auth_token, 'YOUR_AUTH_TOKEN'
+
+ helpers do
+ def protected!
+ # Put any authentication code you want in here.
+ # This method is run before accessing any resource.
+ end
+ end
+end
+
+map Sinatra::Application.assets_prefix do
+ run Sinatra::Application.sprockets
+end
+
+run Sinatra::Application
\ No newline at end of file
diff --git a/fire_tablet_dashboard/dashboards/layout.erb b/fire_tablet_dashboard/dashboards/layout.erb
new file mode 100644
index 00000000..eae97859
--- /dev/null
+++ b/fire_tablet_dashboard/dashboards/layout.erb
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+ <%= yield_content(:title) %>
+
+
+
+
+
+
+
+
+
+
+
+ <%= yield %>
+
+
+ <% if development? %>
+
+
Paste the following at the top of <%= params[:dashboard] %>.erb
+
+
+ Save this layout
+ <% end %>
+
+
\ No newline at end of file
diff --git a/fire_tablet_dashboard/dashboards/sample.erb b/fire_tablet_dashboard/dashboards/sample.erb
new file mode 100644
index 00000000..d41d5383
--- /dev/null
+++ b/fire_tablet_dashboard/dashboards/sample.erb
@@ -0,0 +1,25 @@
+<% content_for :title do %>My super sweet dashboard<% end %>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Try this: curl -d '{ "auth_token": "YOUR_AUTH_TOKEN", "text": "Hey, Look what I can do!" }' \http://<%=request.host%>:<%=request.port%>/widgets/welcome
+
\ No newline at end of file
diff --git a/fire_tablet_dashboard/dashboards/sampletv.erb b/fire_tablet_dashboard/dashboards/sampletv.erb
new file mode 100644
index 00000000..cf4b39c2
--- /dev/null
+++ b/fire_tablet_dashboard/dashboards/sampletv.erb
@@ -0,0 +1,56 @@
+
+
+
+<% content_for :title do %>1080p dashboard<% end %>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Try this: curl -d '{ "auth_token": "YOUR_AUTH_TOKEN", "text": "Hey, Look what I can do!" }' \http://<%=request.host%>:<%=request.port%>/widgets/welcome
+
\ No newline at end of file
diff --git a/fire_tablet_dashboard/jobs/buzzwords.rb b/fire_tablet_dashboard/jobs/buzzwords.rb
new file mode 100644
index 00000000..2668d5a9
--- /dev/null
+++ b/fire_tablet_dashboard/jobs/buzzwords.rb
@@ -0,0 +1,9 @@
+buzzwords = ['Paradigm shift', 'Leverage', 'Pivoting', 'Turn-key', 'Streamlininess', 'Exit strategy', 'Synergy', 'Enterprise', 'Web 2.0']
+buzzword_counts = Hash.new({ value: 0 })
+
+SCHEDULER.every '2s' do
+ random_buzzword = buzzwords.sample
+ buzzword_counts[random_buzzword] = { label: random_buzzword, value: (buzzword_counts[random_buzzword][:value] + 1) % 30 }
+
+ send_event('buzzwords', { items: buzzword_counts.values })
+end
\ No newline at end of file
diff --git a/fire_tablet_dashboard/jobs/convergence.rb b/fire_tablet_dashboard/jobs/convergence.rb
new file mode 100644
index 00000000..1da2a5b3
--- /dev/null
+++ b/fire_tablet_dashboard/jobs/convergence.rb
@@ -0,0 +1,14 @@
+# Populate the graph with some random points
+points = []
+(1..10).each do |i|
+ points << { x: i, y: rand(50) }
+end
+last_x = points.last[:x]
+
+SCHEDULER.every '2s' do
+ points.shift
+ last_x += 1
+ points << { x: last_x, y: rand(50) }
+
+ send_event('convergence', points: points)
+end
\ No newline at end of file
diff --git a/fire_tablet_dashboard/jobs/sample.rb b/fire_tablet_dashboard/jobs/sample.rb
new file mode 100644
index 00000000..1fe008d4
--- /dev/null
+++ b/fire_tablet_dashboard/jobs/sample.rb
@@ -0,0 +1,13 @@
+current_valuation = 0
+current_karma = 0
+
+SCHEDULER.every '2s' do
+ last_valuation = current_valuation
+ last_karma = current_karma
+ current_valuation = rand(100)
+ current_karma = rand(200000)
+
+ send_event('valuation', { current: current_valuation, last: last_valuation })
+ send_event('karma', { current: current_karma, last: last_karma })
+ send_event('synergy', { value: rand(100) })
+end
\ No newline at end of file
diff --git a/fire_tablet_dashboard/jobs/twitter.rb b/fire_tablet_dashboard/jobs/twitter.rb
new file mode 100644
index 00000000..7b2bd6fb
--- /dev/null
+++ b/fire_tablet_dashboard/jobs/twitter.rb
@@ -0,0 +1,28 @@
+require 'twitter'
+
+
+#### Get your twitter keys & secrets:
+#### https://dev.twitter.com/docs/auth/tokens-devtwittercom
+twitter = Twitter::REST::Client.new do |config|
+ config.consumer_key = 'YOUR_CONSUMER_KEY'
+ config.consumer_secret = 'YOUR_CONSUMER_SECRET'
+ config.access_token = 'YOUR_OAUTH_TOKEN'
+ config.access_token_secret = 'YOUR_OAUTH_SECRET'
+end
+
+search_term = URI::encode('#todayilearned')
+
+SCHEDULER.every '10m', :first_in => 0 do |job|
+ begin
+ tweets = twitter.search("#{search_term}")
+
+ if tweets
+ tweets = tweets.map do |tweet|
+ { name: tweet.user.name, body: tweet.text, avatar: tweet.user.profile_image_url_https }
+ end
+ send_event('twitter_mentions', comments: tweets)
+ end
+ rescue Twitter::Error
+ puts "\e[33mFor the twitter widget to work, you need to put in your twitter API keys in the jobs/twitter.rb file.\e[0m"
+ end
+end
\ No newline at end of file
diff --git a/fire_tablet_dashboard/public/404.html b/fire_tablet_dashboard/public/404.html
new file mode 100644
index 00000000..1a8f334e
--- /dev/null
+++ b/fire_tablet_dashboard/public/404.html
@@ -0,0 +1,26 @@
+
+
+
+ This Dashboard doesn't exist.
+
+
+
+
+
+
+
Drats! That Dashboard doesn't exist.
+
You may have mistyped the address or the page may have moved.
+
+
+
\ No newline at end of file
diff --git a/fire_tablet_dashboard/public/favicon.ico b/fire_tablet_dashboard/public/favicon.ico
new file mode 100644
index 00000000..29a408f9
Binary files /dev/null and b/fire_tablet_dashboard/public/favicon.ico differ
diff --git a/fire_tablet_dashboard/widgets/clock/clock.coffee b/fire_tablet_dashboard/widgets/clock/clock.coffee
new file mode 100644
index 00000000..cd6b1163
--- /dev/null
+++ b/fire_tablet_dashboard/widgets/clock/clock.coffee
@@ -0,0 +1,18 @@
+class Dashing.Clock extends Dashing.Widget
+
+ ready: ->
+ setInterval(@startTime, 500)
+
+ startTime: =>
+ today = new Date()
+
+ h = today.getHours()
+ m = today.getMinutes()
+ s = today.getSeconds()
+ m = @formatTime(m)
+ s = @formatTime(s)
+ @set('time', h + ":" + m + ":" + s)
+ @set('date', today.toDateString())
+
+ formatTime: (i) ->
+ if i < 10 then "0" + i else i
\ No newline at end of file
diff --git a/fire_tablet_dashboard/widgets/clock/clock.html b/fire_tablet_dashboard/widgets/clock/clock.html
new file mode 100644
index 00000000..06759d4a
--- /dev/null
+++ b/fire_tablet_dashboard/widgets/clock/clock.html
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/fire_tablet_dashboard/widgets/clock/clock.scss b/fire_tablet_dashboard/widgets/clock/clock.scss
new file mode 100644
index 00000000..19e91bf1
--- /dev/null
+++ b/fire_tablet_dashboard/widgets/clock/clock.scss
@@ -0,0 +1,13 @@
+// ----------------------------------------------------------------------------
+// Sass declarations
+// ----------------------------------------------------------------------------
+$background-color: #dc5945;
+
+// ----------------------------------------------------------------------------
+// Widget-clock styles
+// ----------------------------------------------------------------------------
+.widget-clock {
+
+ background-color: $background-color;
+
+}
diff --git a/fire_tablet_dashboard/widgets/comments/comments.coffee b/fire_tablet_dashboard/widgets/comments/comments.coffee
new file mode 100644
index 00000000..ff659ecc
--- /dev/null
+++ b/fire_tablet_dashboard/widgets/comments/comments.coffee
@@ -0,0 +1,24 @@
+class Dashing.Comments extends Dashing.Widget
+
+ @accessor 'quote', ->
+ "“#{@get('current_comment')?.body}”"
+
+ ready: ->
+ @currentIndex = 0
+ @commentElem = $(@node).find('.comment-container')
+ @nextComment()
+ @startCarousel()
+
+ onData: (data) ->
+ @currentIndex = 0
+
+ startCarousel: ->
+ setInterval(@nextComment, 8000)
+
+ nextComment: =>
+ comments = @get('comments')
+ if comments
+ @commentElem.fadeOut =>
+ @currentIndex = (@currentIndex + 1) % comments.length
+ @set 'current_comment', comments[@currentIndex]
+ @commentElem.fadeIn()
diff --git a/fire_tablet_dashboard/widgets/comments/comments.html b/fire_tablet_dashboard/widgets/comments/comments.html
new file mode 100644
index 00000000..7c580be1
--- /dev/null
+++ b/fire_tablet_dashboard/widgets/comments/comments.html
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/fire_tablet_dashboard/widgets/comments/comments.scss b/fire_tablet_dashboard/widgets/comments/comments.scss
new file mode 100644
index 00000000..2ace30ec
--- /dev/null
+++ b/fire_tablet_dashboard/widgets/comments/comments.scss
@@ -0,0 +1,33 @@
+// ----------------------------------------------------------------------------
+// Sass declarations
+// ----------------------------------------------------------------------------
+$background-color: #eb9c3c;
+
+$title-color: rgba(255, 255, 255, 0.7);
+$moreinfo-color: rgba(255, 255, 255, 0.7);
+
+// ----------------------------------------------------------------------------
+// Widget-comment styles
+// ----------------------------------------------------------------------------
+.widget-comments {
+
+ background-color: $background-color;
+
+ .title {
+ color: $title-color;
+ margin-bottom: 15px;
+ }
+
+ .name {
+ padding-left: 5px;
+ }
+
+ .comment-container {
+ display: none;
+ }
+
+ .more-info {
+ color: $moreinfo-color;
+ }
+
+}
diff --git a/fire_tablet_dashboard/widgets/graph/graph.coffee b/fire_tablet_dashboard/widgets/graph/graph.coffee
new file mode 100644
index 00000000..28ce88e1
--- /dev/null
+++ b/fire_tablet_dashboard/widgets/graph/graph.coffee
@@ -0,0 +1,36 @@
+class Dashing.Graph extends Dashing.Widget
+
+ @accessor 'current', ->
+ return @get('displayedValue') if @get('displayedValue')
+ points = @get('points')
+ if points
+ points[points.length - 1].y
+
+ ready: ->
+ container = $(@node).parent()
+ # Gross hacks. Let's fix this.
+ width = (Dashing.widget_base_dimensions[0] * container.data("sizex")) + Dashing.widget_margins[0] * 2 * (container.data("sizex") - 1)
+ height = (Dashing.widget_base_dimensions[1] * container.data("sizey"))
+ @graph = new Rickshaw.Graph(
+ element: @node
+ width: width
+ height: height
+ renderer: @get("graphtype")
+ series: [
+ {
+ color: "#fff",
+ data: [{x:0, y:0}]
+ }
+ ]
+ )
+
+ @graph.series[0].data = @get('points') if @get('points')
+
+ x_axis = new Rickshaw.Graph.Axis.Time(graph: @graph)
+ y_axis = new Rickshaw.Graph.Axis.Y(graph: @graph, tickFormat: Rickshaw.Fixtures.Number.formatKMBT)
+ @graph.render()
+
+ onData: (data) ->
+ if @graph
+ @graph.series[0].data = data.points
+ @graph.render()
diff --git a/fire_tablet_dashboard/widgets/graph/graph.html b/fire_tablet_dashboard/widgets/graph/graph.html
new file mode 100644
index 00000000..456dd0fa
--- /dev/null
+++ b/fire_tablet_dashboard/widgets/graph/graph.html
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/fire_tablet_dashboard/widgets/graph/graph.scss b/fire_tablet_dashboard/widgets/graph/graph.scss
new file mode 100644
index 00000000..1000b6ff
--- /dev/null
+++ b/fire_tablet_dashboard/widgets/graph/graph.scss
@@ -0,0 +1,65 @@
+// ----------------------------------------------------------------------------
+// Sass declarations
+// ----------------------------------------------------------------------------
+$background-color: #dc5945;
+
+$title-color: rgba(255, 255, 255, 0.7);
+$moreinfo-color: rgba(255, 255, 255, 0.3);
+$tick-color: rgba(0, 0, 0, 0.4);
+
+
+// ----------------------------------------------------------------------------
+// Widget-graph styles
+// ----------------------------------------------------------------------------
+.widget-graph {
+
+ background-color: $background-color;
+ position: relative;
+
+
+ svg {
+ position: absolute;
+ opacity: 0.4;
+ fill-opacity: 0.4;
+ left: 0px;
+ top: 0px;
+ }
+
+ .title, .value {
+ position: relative;
+ z-index: 99;
+ }
+
+ .title {
+ color: $title-color;
+ }
+
+ .more-info {
+ color: $moreinfo-color;
+ font-weight: 600;
+ font-size: 20px;
+ margin-top: 0;
+ }
+
+ .x_tick {
+ position: absolute;
+ bottom: 0;
+ .title {
+ font-size: 20px;
+ color: $tick-color;
+ opacity: 0.5;
+ padding-bottom: 3px;
+ }
+ }
+
+ .y_ticks {
+ font-size: 20px;
+ fill: $tick-color;
+ fill-opacity: 1;
+ }
+
+ .domain {
+ display: none;
+ }
+
+}
diff --git a/fire_tablet_dashboard/widgets/iframe/iframe.coffee b/fire_tablet_dashboard/widgets/iframe/iframe.coffee
new file mode 100644
index 00000000..e3ee9477
--- /dev/null
+++ b/fire_tablet_dashboard/widgets/iframe/iframe.coffee
@@ -0,0 +1,9 @@
+class Dashing.Iframe extends Dashing.Widget
+
+ ready: ->
+ # This is fired when the widget is done being rendered
+
+ onData: (data) ->
+ # Handle incoming data
+ # You can access the html node of this widget with `@node`
+ # Example: $(@node).fadeOut().fadeIn() will make the node flash each time data comes in.
diff --git a/fire_tablet_dashboard/widgets/iframe/iframe.html b/fire_tablet_dashboard/widgets/iframe/iframe.html
new file mode 100644
index 00000000..385f6daa
--- /dev/null
+++ b/fire_tablet_dashboard/widgets/iframe/iframe.html
@@ -0,0 +1 @@
+
diff --git a/fire_tablet_dashboard/widgets/iframe/iframe.scss b/fire_tablet_dashboard/widgets/iframe/iframe.scss
new file mode 100644
index 00000000..6827a1b7
--- /dev/null
+++ b/fire_tablet_dashboard/widgets/iframe/iframe.scss
@@ -0,0 +1,8 @@
+.widget-iframe {
+ padding: 3px 0px 0px 0px !important;
+
+ iframe {
+ width: 100%;
+ height: 100%;
+ }
+}
diff --git a/fire_tablet_dashboard/widgets/image/image.coffee b/fire_tablet_dashboard/widgets/image/image.coffee
new file mode 100644
index 00000000..c3892c0d
--- /dev/null
+++ b/fire_tablet_dashboard/widgets/image/image.coffee
@@ -0,0 +1,9 @@
+class Dashing.Image extends Dashing.Widget
+
+ ready: ->
+ # This is fired when the widget is done being rendered
+
+ onData: (data) ->
+ # Handle incoming data
+ # You can access the html node of this widget with `@node`
+ # Example: $(@node).fadeOut().fadeIn() will make the node flash each time data comes in.
diff --git a/fire_tablet_dashboard/widgets/image/image.html b/fire_tablet_dashboard/widgets/image/image.html
new file mode 100644
index 00000000..41a88eb9
--- /dev/null
+++ b/fire_tablet_dashboard/widgets/image/image.html
@@ -0,0 +1 @@
+
diff --git a/fire_tablet_dashboard/widgets/image/image.scss b/fire_tablet_dashboard/widgets/image/image.scss
new file mode 100644
index 00000000..0b1a3163
--- /dev/null
+++ b/fire_tablet_dashboard/widgets/image/image.scss
@@ -0,0 +1,13 @@
+// ----------------------------------------------------------------------------
+// Sass declarations
+// ----------------------------------------------------------------------------
+$background-color: #4b4b4b;
+
+// ----------------------------------------------------------------------------
+// Widget-image styles
+// ----------------------------------------------------------------------------
+.widget-image {
+
+ background-color: $background-color;
+
+}
diff --git a/fire_tablet_dashboard/widgets/list/list.coffee b/fire_tablet_dashboard/widgets/list/list.coffee
new file mode 100644
index 00000000..00280731
--- /dev/null
+++ b/fire_tablet_dashboard/widgets/list/list.coffee
@@ -0,0 +1,6 @@
+class Dashing.List extends Dashing.Widget
+ ready: ->
+ if @get('unordered')
+ $(@node).find('ol').remove()
+ else
+ $(@node).find('ul').remove()
diff --git a/fire_tablet_dashboard/widgets/list/list.html b/fire_tablet_dashboard/widgets/list/list.html
new file mode 100644
index 00000000..07e04716
--- /dev/null
+++ b/fire_tablet_dashboard/widgets/list/list.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fire_tablet_dashboard/widgets/list/list.scss b/fire_tablet_dashboard/widgets/list/list.scss
new file mode 100644
index 00000000..bce70105
--- /dev/null
+++ b/fire_tablet_dashboard/widgets/list/list.scss
@@ -0,0 +1,60 @@
+// ----------------------------------------------------------------------------
+// Sass declarations
+// ----------------------------------------------------------------------------
+$background-color: #12b0c5;
+$value-color: #fff;
+
+$title-color: rgba(255, 255, 255, 0.7);
+$label-color: rgba(255, 255, 255, 0.7);
+$moreinfo-color: rgba(255, 255, 255, 0.7);
+
+// ----------------------------------------------------------------------------
+// Widget-list styles
+// ----------------------------------------------------------------------------
+.widget-list {
+
+ background-color: $background-color;
+ vertical-align: top;
+
+ .title {
+ color: $title-color;
+ }
+
+ ol, ul {
+ margin: 0 15px;
+ text-align: left;
+ color: $label-color;
+ }
+
+ ol {
+ list-style-position: inside;
+ }
+
+ li {
+ margin-bottom: 5px;
+ }
+
+ .list-nostyle {
+ list-style: none;
+ }
+
+ .label {
+ color: $label-color;
+ }
+
+ .value {
+ float: right;
+ margin-left: 12px;
+ font-weight: 600;
+ color: $value-color;
+ }
+
+ .updated-at {
+ color: rgba(0, 0, 0, 0.3);
+ }
+
+ .more-info {
+ color: $moreinfo-color;
+ }
+
+}
diff --git a/fire_tablet_dashboard/widgets/meter/meter.coffee b/fire_tablet_dashboard/widgets/meter/meter.coffee
new file mode 100644
index 00000000..b823ec7a
--- /dev/null
+++ b/fire_tablet_dashboard/widgets/meter/meter.coffee
@@ -0,0 +1,14 @@
+class Dashing.Meter extends Dashing.Widget
+
+ @accessor 'value', Dashing.AnimatedValue
+
+ constructor: ->
+ super
+ @observe 'value', (value) ->
+ $(@node).find(".meter").val(value).trigger('change')
+
+ ready: ->
+ meter = $(@node).find(".meter")
+ meter.attr("data-bgcolor", meter.css("background-color"))
+ meter.attr("data-fgcolor", meter.css("color"))
+ meter.knob()
diff --git a/fire_tablet_dashboard/widgets/meter/meter.html b/fire_tablet_dashboard/widgets/meter/meter.html
new file mode 100644
index 00000000..16f1f06a
--- /dev/null
+++ b/fire_tablet_dashboard/widgets/meter/meter.html
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/fire_tablet_dashboard/widgets/meter/meter.scss b/fire_tablet_dashboard/widgets/meter/meter.scss
new file mode 100644
index 00000000..da9ff0b4
--- /dev/null
+++ b/fire_tablet_dashboard/widgets/meter/meter.scss
@@ -0,0 +1,35 @@
+// ----------------------------------------------------------------------------
+// Sass declarations
+// ----------------------------------------------------------------------------
+$background-color: #9c4274;
+
+$title-color: rgba(255, 255, 255, 0.7);
+$moreinfo-color: rgba(255, 255, 255, 0.3);
+
+$meter-background: darken($background-color, 15%);
+
+// ----------------------------------------------------------------------------
+// Widget-meter styles
+// ----------------------------------------------------------------------------
+.widget-meter {
+
+ background-color: $background-color;
+
+ input.meter {
+ background-color: $meter-background;
+ color: #fff;
+ }
+
+ .title {
+ color: $title-color;
+ }
+
+ .more-info {
+ color: $moreinfo-color;
+ }
+
+ .updated-at {
+ color: rgba(0, 0, 0, 0.3);
+ }
+
+}
diff --git a/fire_tablet_dashboard/widgets/number/number.coffee b/fire_tablet_dashboard/widgets/number/number.coffee
new file mode 100644
index 00000000..645ee7f3
--- /dev/null
+++ b/fire_tablet_dashboard/widgets/number/number.coffee
@@ -0,0 +1,24 @@
+class Dashing.Number extends Dashing.Widget
+ @accessor 'current', Dashing.AnimatedValue
+
+ @accessor 'difference', ->
+ if @get('last')
+ last = parseInt(@get('last'))
+ current = parseInt(@get('current'))
+ if last != 0
+ diff = Math.abs(Math.round((current - last) / last * 100))
+ "#{diff}%"
+ else
+ ""
+
+ @accessor 'arrow', ->
+ if @get('last')
+ if parseInt(@get('current')) > parseInt(@get('last')) then 'icon-arrow-up' else 'icon-arrow-down'
+
+ onData: (data) ->
+ if data.status
+ # clear existing "status-*" classes
+ $(@get('node')).attr 'class', (i,c) ->
+ c.replace /\bstatus-\S+/g, ''
+ # add new class
+ $(@get('node')).addClass "status-#{data.status}"
diff --git a/fire_tablet_dashboard/widgets/number/number.html b/fire_tablet_dashboard/widgets/number/number.html
new file mode 100644
index 00000000..c82e5f4a
--- /dev/null
+++ b/fire_tablet_dashboard/widgets/number/number.html
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fire_tablet_dashboard/widgets/number/number.scss b/fire_tablet_dashboard/widgets/number/number.scss
new file mode 100644
index 00000000..88757941
--- /dev/null
+++ b/fire_tablet_dashboard/widgets/number/number.scss
@@ -0,0 +1,39 @@
+// ----------------------------------------------------------------------------
+// Sass declarations
+// ----------------------------------------------------------------------------
+$background-color: #47bbb3;
+$value-color: #fff;
+
+$title-color: rgba(255, 255, 255, 0.7);
+$moreinfo-color: rgba(255, 255, 255, 0.7);
+
+// ----------------------------------------------------------------------------
+// Widget-number styles
+// ----------------------------------------------------------------------------
+.widget-number {
+
+ background-color: $background-color;
+
+ .title {
+ color: $title-color;
+ }
+
+ .value {
+ color: $value-color;
+ }
+
+ .change-rate {
+ font-weight: 500;
+ font-size: 30px;
+ color: $value-color;
+ }
+
+ .more-info {
+ color: $moreinfo-color;
+ }
+
+ .updated-at {
+ color: rgba(0, 0, 0, 0.3);
+ }
+
+}
diff --git a/fire_tablet_dashboard/widgets/text/text.coffee b/fire_tablet_dashboard/widgets/text/text.coffee
new file mode 100644
index 00000000..1741d8b4
--- /dev/null
+++ b/fire_tablet_dashboard/widgets/text/text.coffee
@@ -0,0 +1 @@
+class Dashing.Text extends Dashing.Widget
diff --git a/fire_tablet_dashboard/widgets/text/text.html b/fire_tablet_dashboard/widgets/text/text.html
new file mode 100644
index 00000000..decd109d
--- /dev/null
+++ b/fire_tablet_dashboard/widgets/text/text.html
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/fire_tablet_dashboard/widgets/text/text.scss b/fire_tablet_dashboard/widgets/text/text.scss
new file mode 100644
index 00000000..45d790ee
--- /dev/null
+++ b/fire_tablet_dashboard/widgets/text/text.scss
@@ -0,0 +1,32 @@
+// ----------------------------------------------------------------------------
+// Sass declarations
+// ----------------------------------------------------------------------------
+$background-color: #ec663c;
+
+$title-color: rgba(255, 255, 255, 0.7);
+$moreinfo-color: rgba(255, 255, 255, 0.7);
+
+// ----------------------------------------------------------------------------
+// Widget-text styles
+// ----------------------------------------------------------------------------
+.widget-text {
+
+ background-color: $background-color;
+
+ .title {
+ color: $title-color;
+ }
+
+ .more-info {
+ color: $moreinfo-color;
+ }
+
+ .updated-at {
+ color: rgba(255, 255, 255, 0.7);
+ }
+
+
+ &.large h3 {
+ font-size: 65px;
+ }
+}
+ +