a.y)}function m(a,b,c,d,e){var f=-3*b+9*c-9*d+3*e,g=a*f+6*b-12*c+6*d;return a*g-3*b+3*c}function n(a,b,c,d,e,f,g,h,i){null==i&&(i=1),i=i>1?1:0>i?0:i;for(var j=i/2,k=12,l=[-.1252,.1252,-.3678,.3678,-.5873,.5873,-.7699,.7699,-.9041,.9041,-.9816,.9816],n=[.2491,.2491,.2335,.2335,.2032,.2032,.1601,.1601,.1069,.1069,.0472,.0472],o=0,p=0;k>p;p++){var q=j*l[p]+j,r=m(q,a,c,e,g),s=m(q,b,d,f,h),t=r*r+s*s;o+=n[p]*N.sqrt(t)}return j*o}function o(a,b,c,d,e,f,g,h,i){if(!(0>i||n(a,b,c,d,e,f,g,h)o;)l/=2,m+=(i>j?1:-1)*l,j=n(a,b,c,d,e,f,g,h,m);return m}}function p(a,b,c,d,e,f,g,h){if(!(Q(a,c) Q(e,g)||Q(b,d)
Q(f,h))){var i=(a*d-b*c)*(e-g)-(a-c)*(e*h-f*g),j=(a*d-b*c)*(f-h)-(b-d)*(e*h-f*g),k=(a-c)*(f-h)-(b-d)*(e-g);if(k){var l=i/k,m=j/k,n=+l.toFixed(2),o=+m.toFixed(2);if(!(n<+P(a,c).toFixed(2)||n>+Q(a,c).toFixed(2)||n<+P(e,g).toFixed(2)||n>+Q(e,g).toFixed(2)||o<+P(b,d).toFixed(2)||o>+Q(b,d).toFixed(2)||o<+P(f,h).toFixed(2)||o>+Q(f,h).toFixed(2)))return{x:l,y:m}}}}function q(a,b,c){var d=j(a),e=j(b);if(!l(d,e))return c?0:[];for(var f=n.apply(0,a),g=n.apply(0,b),h=~~(f/8),k=~~(g/8),m=[],o=[],q={},r=c?0:[],s=0;h+1>s;s++){var t=i.apply(0,a.concat(s/h));m.push({x:t.x,y:t.y,t:s/h})}for(s=0;k+1>s;s++)t=i.apply(0,b.concat(s/k)),o.push({x:t.x,y:t.y,t:s/k});for(s=0;h>s;s++)for(var u=0;k>u;u++){var v=m[s],w=m[s+1],x=o[u],y=o[u+1],z=S(w.x-v.x)<.001?"y":"x",A=S(y.x-x.x)<.001?"y":"x",B=p(v.x,v.y,w.x,w.y,x.x,x.y,y.x,y.y);if(B){if(q[B.x.toFixed(4)]==B.y.toFixed(4))continue;q[B.x.toFixed(4)]=B.y.toFixed(4);var C=v.t+S((B[z]-v[z])/(w[z]-v[z]))*(w.t-v.t),D=x.t+S((B[A]-x[A])/(y[A]-x[A]))*(y.t-x.t);C>=0&&1>=C&&D>=0&&1>=D&&(c?r++:r.push({x:B.x,y:B.y,t1:C,t2:D}))}}return r}function r(a,b){return t(a,b)}function s(a,b){return t(a,b,1)}function t(a,b,c){a=E(a),b=E(b);for(var d,e,f,g,h,i,j,k,l,m,n=c?0:[],o=0,p=a.length;p>o;o++){var r=a[o];if("M"==r[0])d=h=r[1],e=i=r[2];else{"C"==r[0]?(l=[d,e].concat(r.slice(1)),d=l[6],e=l[7]):(l=[d,e,d,e,h,i,h,i],d=h,e=i);for(var s=0,t=b.length;t>s;s++){var u=b[s];if("M"==u[0])f=j=u[1],g=k=u[2];else{"C"==u[0]?(m=[f,g].concat(u.slice(1)),f=m[6],g=m[7]):(m=[f,g,f,g,j,k,j,k],f=j,g=k);var v=q(l,m,c);if(c)n+=v;else{for(var w=0,x=v.length;x>w;w++)v[w].segment1=o,v[w].segment2=s,v[w].bez1=l,v[w].bez2=m;n=n.concat(v)}}}}}return n}function u(a,b,c){var d=v(a);return k(d,b,c)&&t(a,[["M",b,c],["H",d.x2+10]],1)%2==1}function v(a){var b=c(a);if(b.bbox)return J(b.bbox);if(!a)return d();a=E(a);for(var e,f=0,g=0,h=[],i=[],j=0,k=a.length;k>j;j++)if(e=a[j],"M"==e[0])f=e[1],g=e[2],h.push(f),i.push(g);else{var l=D(f,g,e[1],e[2],e[3],e[4],e[5],e[6]);h=h.concat(l.min.x,l.max.x),i=i.concat(l.min.y,l.max.y),f=e[5],g=e[6]}var m=P.apply(0,h),n=P.apply(0,i),o=Q.apply(0,h),p=Q.apply(0,i),q=d(m,n,o-m,p-n);return b.bbox=J(q),q}function w(a,b,c,d,f){if(f)return[["M",+a+ +f,b],["l",c-2*f,0],["a",f,f,0,0,1,f,f],["l",0,d-2*f],["a",f,f,0,0,1,-f,f],["l",2*f-c,0],["a",f,f,0,0,1,-f,-f],["l",0,2*f-d],["a",f,f,0,0,1,f,-f],["z"]];var g=[["M",a,b],["l",c,0],["l",0,d],["l",-c,0],["z"]];return g.toString=e,g}function x(a,b,c,d,f){if(null==f&&null==d&&(d=c),a=+a,b=+b,c=+c,d=+d,null!=f)var g=Math.PI/180,h=a+c*Math.cos(-d*g),i=a+c*Math.cos(-f*g),j=b+c*Math.sin(-d*g),k=b+c*Math.sin(-f*g),l=[["M",h,j],["A",c,c,0,+(f-d>180),0,i,k]];else l=[["M",a,b],["m",0,-d],["a",c,d,0,1,1,0,2*d],["a",c,d,0,1,1,0,-2*d],["z"]];return l.toString=e,l}function y(b){var d=c(b),g=String.prototype.toLowerCase;if(d.rel)return f(d.rel);a.is(b,"array")&&a.is(b&&b[0],"array")||(b=a.parsePathString(b));var h=[],i=0,j=0,k=0,l=0,m=0;"M"==b[0][0]&&(i=b[0][1],j=b[0][2],k=i,l=j,m++,h.push(["M",i,j]));for(var n=m,o=b.length;o>n;n++){var p=h[n]=[],q=b[n];if(q[0]!=g.call(q[0]))switch(p[0]=g.call(q[0]),p[0]){case"a":p[1]=q[1],p[2]=q[2],p[3]=q[3],p[4]=q[4],p[5]=q[5],p[6]=+(q[6]-i).toFixed(3),p[7]=+(q[7]-j).toFixed(3);break;case"v":p[1]=+(q[1]-j).toFixed(3);break;case"m":k=q[1],l=q[2];default:for(var r=1,s=q.length;s>r;r++)p[r]=+(q[r]-(r%2?i:j)).toFixed(3)}else{p=h[n]=[],"m"==q[0]&&(k=q[1]+i,l=q[2]+j);for(var t=0,u=q.length;u>t;t++)h[n][t]=q[t]}var v=h[n].length;switch(h[n][0]){case"z":i=k,j=l;break;case"h":i+=+h[n][v-1];break;case"v":j+=+h[n][v-1];break;default:i+=+h[n][v-2],j+=+h[n][v-1]}}return h.toString=e,d.rel=f(h),h}function z(b){var d=c(b);if(d.abs)return f(d.abs);if(I(b,"array")&&I(b&&b[0],"array")||(b=a.parsePathString(b)),!b||!b.length)return[["M",0,0]];var g,h=[],i=0,j=0,k=0,l=0,m=0;"M"==b[0][0]&&(i=+b[0][1],j=+b[0][2],k=i,l=j,m++,h[0]=["M",i,j]);for(var n,o,p=3==b.length&&"M"==b[0][0]&&"R"==b[1][0].toUpperCase()&&"Z"==b[2][0].toUpperCase(),q=m,r=b.length;r>q;q++){if(h.push(n=[]),o=b[q],g=o[0],g!=g.toUpperCase())switch(n[0]=g.toUpperCase(),n[0]){case"A":n[1]=o[1],n[2]=o[2],n[3]=o[3],n[4]=o[4],n[5]=o[5],n[6]=+o[6]+i,n[7]=+o[7]+j;break;case"V":n[1]=+o[1]+j;break;case"H":n[1]=+o[1]+i;break;case"R":for(var s=[i,j].concat(o.slice(1)),t=2,u=s.length;u>t;t++)s[t]=+s[t]+i,s[++t]=+s[t]+j;h.pop(),h=h.concat(G(s,p));break;case"O":h.pop(),s=x(i,j,o[1],o[2]),s.push(s[0]),h=h.concat(s);break;case"U":h.pop(),h=h.concat(x(i,j,o[1],o[2],o[3])),n=["U"].concat(h[h.length-1].slice(-2));break;case"M":k=+o[1]+i,l=+o[2]+j;default:for(t=1,u=o.length;u>t;t++)n[t]=+o[t]+(t%2?i:j)}else if("R"==g)s=[i,j].concat(o.slice(1)),h.pop(),h=h.concat(G(s,p)),n=["R"].concat(o.slice(-2));else if("O"==g)h.pop(),s=x(i,j,o[1],o[2]),s.push(s[0]),h=h.concat(s);else if("U"==g)h.pop(),h=h.concat(x(i,j,o[1],o[2],o[3])),n=["U"].concat(h[h.length-1].slice(-2));else for(var v=0,w=o.length;w>v;v++)n[v]=o[v];if(g=g.toUpperCase(),"O"!=g)switch(n[0]){case"Z":i=+k,j=+l;break;case"H":i=n[1];break;case"V":j=n[1];break;case"M":k=n[n.length-2],l=n[n.length-1];default:i=n[n.length-2],j=n[n.length-1]}}return h.toString=e,d.abs=f(h),h}function A(a,b,c,d){return[a,b,c,d,c,d]}function B(a,b,c,d,e,f){var g=1/3,h=2/3;return[g*a+h*c,g*b+h*d,g*e+h*c,g*f+h*d,e,f]}function C(b,c,d,e,f,g,h,i,j,k){var l,m=120*O/180,n=O/180*(+f||0),o=[],p=a._.cacher(function(a,b,c){var d=a*N.cos(c)-b*N.sin(c),e=a*N.sin(c)+b*N.cos(c);return{x:d,y:e}});if(k)y=k[0],z=k[1],w=k[2],x=k[3];else{l=p(b,c,-n),b=l.x,c=l.y,l=p(i,j,-n),i=l.x,j=l.y;var q=(N.cos(O/180*f),N.sin(O/180*f),(b-i)/2),r=(c-j)/2,s=q*q/(d*d)+r*r/(e*e);s>1&&(s=N.sqrt(s),d=s*d,e=s*e);var t=d*d,u=e*e,v=(g==h?-1:1)*N.sqrt(S((t*u-t*r*r-u*q*q)/(t*r*r+u*q*q))),w=v*d*r/e+(b+i)/2,x=v*-e*q/d+(c+j)/2,y=N.asin(((c-x)/e).toFixed(9)),z=N.asin(((j-x)/e).toFixed(9));y=w>b?O-y:y,z=w>i?O-z:z,0>y&&(y=2*O+y),0>z&&(z=2*O+z),h&&y>z&&(y-=2*O),!h&&z>y&&(z-=2*O)}var A=z-y;if(S(A)>m){var B=z,D=i,E=j;z=y+m*(h&&z>y?1:-1),i=w+d*N.cos(z),j=x+e*N.sin(z),o=C(i,j,d,e,f,0,h,D,E,[z,B,w,x])}A=z-y;var F=N.cos(y),G=N.sin(y),H=N.cos(z),I=N.sin(z),J=N.tan(A/4),K=4/3*d*J,L=4/3*e*J,M=[b,c],P=[b+K*G,c-L*F],Q=[i+K*I,j-L*H],R=[i,j];if(P[0]=2*M[0]-P[0],P[1]=2*M[1]-P[1],k)return[P,Q,R].concat(o);o=[P,Q,R].concat(o).join().split(",");for(var T=[],U=0,V=o.length;V>U;U++)T[U]=U%2?p(o[U-1],o[U],n).y:p(o[U],o[U+1],n).x;return T}function D(a,b,c,d,e,f,g,h){for(var i,j,k,l,m,n,o,p,q=[],r=[[],[]],s=0;2>s;++s)if(0==s?(j=6*a-12*c+6*e,i=-3*a+9*c-9*e+3*g,k=3*c-3*a):(j=6*b-12*d+6*f,i=-3*b+9*d-9*f+3*h,k=3*d-3*b),S(i)<1e-12){if(S(j)<1e-12)continue;l=-k/j,l>0&&1>l&&q.push(l)}else o=j*j-4*k*i,p=N.sqrt(o),0>o||(m=(-j+p)/(2*i),m>0&&1>m&&q.push(m),n=(-j-p)/(2*i),n>0&&1>n&&q.push(n));for(var t,u=q.length,v=u;u--;)l=q[u],t=1-l,r[0][u]=t*t*t*a+3*t*t*l*c+3*t*l*l*e+l*l*l*g,r[1][u]=t*t*t*b+3*t*t*l*d+3*t*l*l*f+l*l*l*h;return r[0][v]=a,r[1][v]=b,r[0][v+1]=g,r[1][v+1]=h,r[0].length=r[1].length=v+2,{min:{x:P.apply(0,r[0]),y:P.apply(0,r[1])},max:{x:Q.apply(0,r[0]),y:Q.apply(0,r[1])}}}function E(a,b){var d=!b&&c(a);if(!b&&d.curve)return f(d.curve);for(var e=z(a),g=b&&z(b),h={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},i={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},j=(function(a,b,c){var d,e;if(!a)return["C",b.x,b.y,b.x,b.y,b.x,b.y];switch(!(a[0]in{T:1,Q:1})&&(b.qx=b.qy=null),a[0]){case"M":b.X=a[1],b.Y=a[2];break;case"A":a=["C"].concat(C.apply(0,[b.x,b.y].concat(a.slice(1))));break;case"S":"C"==c||"S"==c?(d=2*b.x-b.bx,e=2*b.y-b.by):(d=b.x,e=b.y),a=["C",d,e].concat(a.slice(1));break;case"T":"Q"==c||"T"==c?(b.qx=2*b.x-b.qx,b.qy=2*b.y-b.qy):(b.qx=b.x,b.qy=b.y),a=["C"].concat(B(b.x,b.y,b.qx,b.qy,a[1],a[2]));break;case"Q":b.qx=a[1],b.qy=a[2],a=["C"].concat(B(b.x,b.y,a[1],a[2],a[3],a[4]));break;case"L":a=["C"].concat(A(b.x,b.y,a[1],a[2]));break;case"H":a=["C"].concat(A(b.x,b.y,a[1],b.y));break;case"V":a=["C"].concat(A(b.x,b.y,b.x,a[1]));break;case"Z":a=["C"].concat(A(b.x,b.y,b.X,b.Y))}return a}),k=function(a,b){if(a[b].length>7){a[b].shift();for(var c=a[b];c.length;)m[b]="A",g&&(n[b]="A"),a.splice(b++,0,["C"].concat(c.splice(0,6)));a.splice(b,1),r=Q(e.length,g&&g.length||0)}},l=function(a,b,c,d,f){a&&b&&"M"==a[f][0]&&"M"!=b[f][0]&&(b.splice(f,0,["M",d.x,d.y]),c.bx=0,c.by=0,c.x=a[f][1],c.y=a[f][2],r=Q(e.length,g&&g.length||0))},m=[],n=[],o="",p="",q=0,r=Q(e.length,g&&g.length||0);r>q;q++){e[q]&&(o=e[q][0]),"C"!=o&&(m[q]=o,q&&(p=m[q-1])),e[q]=j(e[q],h,p),"A"!=m[q]&&"C"==o&&(m[q]="C"),k(e,q),g&&(g[q]&&(o=g[q][0]),"C"!=o&&(n[q]=o,q&&(p=n[q-1])),g[q]=j(g[q],i,p),"A"!=n[q]&&"C"==o&&(n[q]="C"),k(g,q)),l(e,g,h,i,q),l(g,e,i,h,q);var s=e[q],t=g&&g[q],u=s.length,v=g&&t.length;h.x=s[u-2],h.y=s[u-1],h.bx=M(s[u-4])||h.x,h.by=M(s[u-3])||h.y,i.bx=g&&(M(t[v-4])||i.x),i.by=g&&(M(t[v-3])||i.y),i.x=g&&t[v-2],i.y=g&&t[v-1]}return g||(d.curve=f(e)),g?[e,g]:e}function F(a,b){if(!b)return a;var c,d,e,f,g,h,i;for(a=E(a),e=0,g=a.length;g>e;e++)for(i=a[e],f=1,h=i.length;h>f;f+=2)c=b.x(i[f],i[f+1]),d=b.y(i[f],i[f+1]),i[f]=c,i[f+1]=d;return a}function G(a,b){for(var c=[],d=0,e=a.length;e-2*!b>d;d+=2){var f=[{x:+a[d-2],y:+a[d-1]},{x:+a[d],y:+a[d+1]},{x:+a[d+2],y:+a[d+3]},{x:+a[d+4],y:+a[d+5]}];b?d?e-4==d?f[3]={x:+a[0],y:+a[1]}:e-2==d&&(f[2]={x:+a[0],y:+a[1]},f[3]={x:+a[2],y:+a[3]}):f[0]={x:+a[e-2],y:+a[e-1]}:e-4==d?f[3]=f[2]:d||(f[0]={x:+a[d],y:+a[d+1]}),c.push(["C",(-f[0].x+6*f[1].x+f[2].x)/6,(-f[0].y+6*f[1].y+f[2].y)/6,(f[1].x+6*f[2].x-f[3].x)/6,(f[1].y+6*f[2].y-f[3].y)/6,f[2].x,f[2].y])}return c}var H=b.prototype,I=a.is,J=a._.clone,K="hasOwnProperty",L=/,?([a-z]),?/gi,M=parseFloat,N=Math,O=N.PI,P=N.min,Q=N.max,R=N.pow,S=N.abs,T=h(1),U=h(),V=h(0,1),W=a._unit2px,X={path:function(a){return a.attr("path")},circle:function(a){var b=W(a);return x(b.cx,b.cy,b.r)},ellipse:function(a){var b=W(a);return x(b.cx||0,b.cy||0,b.rx,b.ry)},rect:function(a){var b=W(a);return w(b.x||0,b.y||0,b.width,b.height,b.rx,b.ry)},image:function(a){var b=W(a);return w(b.x||0,b.y||0,b.width,b.height)},line:function(a){return"M"+[a.attr("x1")||0,a.attr("y1")||0,a.attr("x2"),a.attr("y2")]},polyline:function(a){return"M"+a.attr("points")},polygon:function(a){return"M"+a.attr("points")+"z"},deflt:function(a){var b=a.node.getBBox();return w(b.x,b.y,b.width,b.height)}};a.path=c,a.path.getTotalLength=T,a.path.getPointAtLength=U,a.path.getSubpath=function(a,b,c){if(this.getTotalLength(a)-c<1e-6)return V(a,b).end;var d=V(a,c,1);return b?V(d,b).end:d},H.getTotalLength=function(){return this.node.getTotalLength?this.node.getTotalLength():void 0},H.getPointAtLength=function(a){return U(this.attr("d"),a)},H.getSubpath=function(b,c){return a.path.getSubpath(this.attr("d"),b,c)},a._.box=d,a.path.findDotsAtSegment=i,a.path.bezierBBox=j,a.path.isPointInsideBBox=k,a.path.isBBoxIntersect=l,a.path.intersection=r,a.path.intersectionNumber=s,a.path.isPointInside=u,a.path.getBBox=v,a.path.get=X,a.path.toRelative=y,a.path.toAbsolute=z,a.path.toCubic=E,a.path.map=F,a.path.toString=e,a.path.clone=f}),d.plugin(function(a){var d=Math.max,e=Math.min,f=function(a){if(this.items=[],this.bindings={},this.length=0,this.type="set",a)for(var b=0,c=a.length;c>b;b++)a[b]&&(this[this.items.length]=this.items[this.items.length]=a[b],this.length++)},g=f.prototype;g.push=function(){for(var a,b,c=0,d=arguments.length;d>c;c++)a=arguments[c],a&&(b=this.items.length,this[b]=this.items[b]=a,this.length++);return this},g.pop=function(){return this.length&&delete this[this.length--],this.items.pop()},g.forEach=function(a,b){for(var c=0,d=this.items.length;d>c;c++)if(a.call(b,this.items[c],c)===!1)return this;return this},g.animate=function(d,e,f,g){"function"!=typeof f||f.length||(g=f,f=c.linear),d instanceof a._.Animation&&(g=d.callback,f=d.easing,e=f.dur,d=d.attr);var h=arguments;if(a.is(d,"array")&&a.is(h[h.length-1],"array"))var i=!0;var j,k=function(){j?this.b=j:j=this.b},l=0,m=g&&function(){l++==this.length&&g.call(this)};return this.forEach(function(a,c){b.once("snap.animcreated."+a.id,k),i?h[c]&&a.animate.apply(a,h[c]):a.animate(d,e,f,m)})},g.remove=function(){for(;this.length;)this.pop().remove();return this},g.bind=function(a,b,c){var d={};if("function"==typeof b)this.bindings[a]=b;else{var e=c||a;this.bindings[a]=function(a){d[e]=a,b.attr(d)}}return this},g.attr=function(a){var b={};for(var c in a)this.bindings[c]?this.bindings[c](a[c]):b[c]=a[c];for(var d=0,e=this.items.length;e>d;d++)this.items[d].attr(b);return this},g.clear=function(){for(;this.length;)this.pop()},g.splice=function(a,b){a=0>a?d(this.length+a,0):a,b=d(0,e(this.length-a,b));var c,g=[],h=[],i=[];for(c=2;cc;c++)h.push(this[a+c]);for(;cc?i[c]:g[c-j];for(c=this.items.length=this.length-=b-j;this[c];)delete this[c++];return new f(h)},g.exclude=function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]==a)return this.splice(b,1),!0;return!1},g.insertAfter=function(a){for(var b=this.items.length;b--;)this.items[b].insertAfter(a);return this},g.getBBox=function(){for(var a=[],b=[],c=[],f=[],g=this.items.length;g--;)if(!this.items[g].removed){var h=this.items[g].getBBox();a.push(h.x),b.push(h.y),c.push(h.x+h.width),f.push(h.y+h.height)}return a=e.apply(0,a),b=e.apply(0,b),c=d.apply(0,c),f=d.apply(0,f),{x:a,y:b,x2:c,y2:f,width:c-a,height:f-b,cx:a+(c-a)/2,cy:b+(f-b)/2}},g.clone=function(a){a=new f;for(var b=0,c=this.items.length;c>b;b++)a.push(this.items[b].clone());return a},g.toString=function(){return"Snap‘s set"},g.type="set",a.set=function(){var a=new f;return arguments.length&&a.push.apply(a,Array.prototype.slice.call(arguments,0)),a}}),d.plugin(function(a,c){function d(a){var b=a[0];switch(b.toLowerCase()){case"t":return[b,0,0];case"m":return[b,1,0,0,1,0,0];case"r":return 4==a.length?[b,0,a[2],a[3]]:[b,0];case"s":return 5==a.length?[b,1,1,a[3],a[4]]:3==a.length?[b,1,1]:[b,1]}}function e(b,c,e){c=m(c).replace(/\.{3}|\u2026/g,b),b=a.parseTransformString(b)||[],c=a.parseTransformString(c)||[];for(var f,g,h,k,l=Math.max(b.length,c.length),n=[],o=[],p=0;l>p;p++){if(h=b[p]||d(c[p]),k=c[p]||d(h),h[0]!=k[0]||"r"==h[0].toLowerCase()&&(h[2]!=k[2]||h[3]!=k[3])||"s"==h[0].toLowerCase()&&(h[3]!=k[3]||h[4]!=k[4])){b=a._.transform2matrix(b,e()),c=a._.transform2matrix(c,e()),n=[["m",b.a,b.b,b.c,b.d,b.e,b.f]],o=[["m",c.a,c.b,c.c,c.d,c.e,c.f]];break}for(n[p]=[],o[p]=[],f=0,g=Math.max(h.length,k.length);g>f;f++)f in h&&(n[p][f]=h[f]),f in k&&(o[p][f]=k[f])}return{from:j(n),to:j(o),f:i(n)}}function f(a){return a}function g(a){return function(b){return+b.toFixed(3)+a}}function h(b){return a.rgb(b[0],b[1],b[2])}function i(a){var b,c,d,e,f,g,h=0,i=[];for(b=0,c=a.length;c>b;b++){for(f="[",g=['"'+a[b][0]+'"'],d=1,e=a[b].length;e>d;d++)g[d]="val["+h++ +"]";
+f+=g+"]",i[b]=f}return Function("val","return Snap.path.toString.call(["+i+"])")}function j(a){for(var b=[],c=0,d=a.length;d>c;c++)for(var e=1,f=a[c].length;f>e;e++)b.push(a[c][e]);return b}var k={},l=/[a-z]+$/i,m=String;k.stroke=k.fill="colour",c.prototype.equal=function(a,c){return b("snap.util.equal",this,a,c).firstDefined()},b.on("snap.util.equal",function(b,c){var d,n,o=m(this.attr(b)||""),p=this;if(o==+o&&c==+c)return{from:+o,to:+c,f:f};if("colour"==k[b])return d=a.color(o),n=a.color(c),{from:[d.r,d.g,d.b,d.opacity],to:[n.r,n.g,n.b,n.opacity],f:h};if("transform"==b||"gradientTransform"==b||"patternTransform"==b)return c instanceof a.Matrix&&(c=c.toTransformString()),a._.rgTransform.test(c)||(c=a._.svgTransform2string(c)),e(o,c,function(){return p.getBBox(1)});if("d"==b||"path"==b)return d=a.path.toCubic(o,c),{from:j(d[0]),to:j(d[1]),f:i(d[0])};if("points"==b)return d=m(o).split(a._.separator),n=m(c).split(a._.separator),{from:d,to:n,f:function(a){return a}};aUnit=o.match(l);var q=m(c).match(l);return aUnit&&aUnit==q?{from:parseFloat(o),to:parseFloat(c),f:g(aUnit)}:{from:this.asPX(b),to:this.asPX(b,c),f:f}})}),d.plugin(function(a,c,d,e){for(var f=c.prototype,g="hasOwnProperty",h=("createTouch"in e.doc),i=["click","dblclick","mousedown","mousemove","mouseout","mouseover","mouseup","touchstart","touchmove","touchend","touchcancel"],j={mousedown:"touchstart",mousemove:"touchmove",mouseup:"touchend"},k=(function(a,b){var c="y"==a?"scrollTop":"scrollLeft",d=b&&b.node?b.node.ownerDocument:e.doc;return d[c in d.documentElement?"documentElement":"body"][c]}),l=function(){this.returnValue=!1},m=function(){return this.originalEvent.preventDefault()},n=function(){this.cancelBubble=!0},o=function(){return this.originalEvent.stopPropagation()},p=function(){return e.doc.addEventListener?function(a,b,c,d){var e=h&&j[b]?j[b]:b,f=function(e){var f=k("y",d),i=k("x",d);if(h&&j[g](b))for(var l=0,n=e.targetTouches&&e.targetTouches.length;n>l;l++)if(e.targetTouches[l].target==a||a.contains(e.targetTouches[l].target)){var p=e;e=e.targetTouches[l],e.originalEvent=p,e.preventDefault=m,e.stopPropagation=o;break}var q=e.clientX+i,r=e.clientY+f;return c.call(d,e,q,r)};return b!==e&&a.addEventListener(b,f,!1),a.addEventListener(e,f,!1),function(){return b!==e&&a.removeEventListener(b,f,!1),a.removeEventListener(e,f,!1),!0}}:e.doc.attachEvent?function(a,b,c,d){var e=function(a){a=a||d.node.ownerDocument.window.event;var b=k("y",d),e=k("x",d),f=a.clientX+e,g=a.clientY+b;return a.preventDefault=a.preventDefault||l,a.stopPropagation=a.stopPropagation||n,c.call(d,a,f,g)};a.attachEvent("on"+b,e);var f=function(){return a.detachEvent("on"+b,e),!0};return f}:void 0}(),q=[],r=function(a){for(var c,d=a.clientX,e=a.clientY,f=k("y"),g=k("x"),i=q.length;i--;){if(c=q[i],h){for(var j,l=a.touches&&a.touches.length;l--;)if(j=a.touches[l],j.identifier==c.el._drag.id||c.el.node.contains(j.target)){d=j.clientX,e=j.clientY,(a.originalEvent?a.originalEvent:a).preventDefault();break}}else a.preventDefault();{var m=c.el.node;m.nextSibling,m.parentNode,m.style.display}d+=g,e+=f,b("snap.drag.move."+c.el.id,c.move_scope||c.el,d-c.el._drag.x,e-c.el._drag.y,d,e,a)}},s=function(c){a.unmousemove(r).unmouseup(s);for(var d,e=q.length;e--;)d=q[e],d.el._drag={},b("snap.drag.end."+d.el.id,d.end_scope||d.start_scope||d.move_scope||d.el,c);q=[]},t=i.length;t--;)!function(b){a[b]=f[b]=function(c,d){return a.is(c,"function")&&(this.events=this.events||[],this.events.push({name:b,f:c,unbind:p(this.node||document,b,c,d||this)})),this},a["un"+b]=f["un"+b]=function(a){for(var c=this.events||[],d=c.length;d--;)if(c[d].name==b&&(c[d].f==a||!a))return c[d].unbind(),c.splice(d,1),!c.length&&delete this.events,this;return this}}(i[t]);f.hover=function(a,b,c,d){return this.mouseover(a,c).mouseout(b,d||c)},f.unhover=function(a,b){return this.unmouseover(a).unmouseout(b)};var u=[];f.drag=function(c,d,e,f,g,h){function i(i,j,k){(i.originalEvent||i).preventDefault(),this._drag.x=j,this._drag.y=k,this._drag.id=i.identifier,!q.length&&a.mousemove(r).mouseup(s),q.push({el:this,move_scope:f,start_scope:g,end_scope:h}),d&&b.on("snap.drag.start."+this.id,d),c&&b.on("snap.drag.move."+this.id,c),e&&b.on("snap.drag.end."+this.id,e),b("snap.drag.start."+this.id,g||f||this,j,k,i)}if(!arguments.length){var j;return this.drag(function(a,b){this.attr({transform:j+(j?"T":"t")+[a,b]})},function(){j=this.transform().local})}return this._drag={},u.push({el:this,start:i}),this.mousedown(i),this},f.undrag=function(){for(var c=u.length;c--;)u[c].el==this&&(this.unmousedown(u[c].start),u.splice(c,1),b.unbind("snap.drag.*."+this.id));return!u.length&&a.unmousemove(r).unmouseup(s),this}}),d.plugin(function(a,c,d){var e=(c.prototype,d.prototype),f=/^\s*url\((.+)\)/,g=String,h=a._.$;a.filter={},e.filter=function(b){var d=this;"svg"!=d.type&&(d=d.paper);var e=a.parse(g(b)),f=a._.id(),i=(d.node.offsetWidth,d.node.offsetHeight,h("filter"));return h(i,{id:f,filterUnits:"userSpaceOnUse"}),i.appendChild(e.node),d.defs.appendChild(i),new c(i)},b.on("snap.util.getattr.filter",function(){b.stop();var c=h(this.node,"filter");if(c){var d=g(c).match(f);return d&&a.select(d[1])}}),b.on("snap.util.attr.filter",function(d){if(d instanceof c&&"filter"==d.type){b.stop();var e=d.node.id;e||(h(d.node,{id:d.id}),e=d.id),h(this.node,{filter:a.url(e)})}d&&"none"!=d||(b.stop(),this.node.removeAttribute("filter"))}),a.filter.blur=function(b,c){null==b&&(b=2);var d=null==c?b:[b,c];return a.format(' ',{def:d})},a.filter.blur.toString=function(){return this()},a.filter.shadow=function(b,c,d,e,f){return"string"==typeof d&&(e=d,f=e,d=4),"string"!=typeof e&&(f=e,e="#000"),e=e||"#000",null==d&&(d=4),null==f&&(f=1),null==b&&(b=0,c=2),null==c&&(c=b),e=a.color(e),a.format(' ',{color:e,dx:b,dy:c,blur:d,opacity:f})},a.filter.shadow.toString=function(){return this()},a.filter.grayscale=function(b){return null==b&&(b=1),a.format(' ',{a:.2126+.7874*(1-b),b:.7152-.7152*(1-b),c:.0722-.0722*(1-b),d:.2126-.2126*(1-b),e:.7152+.2848*(1-b),f:.0722-.0722*(1-b),g:.2126-.2126*(1-b),h:.0722+.9278*(1-b)})},a.filter.grayscale.toString=function(){return this()},a.filter.sepia=function(b){return null==b&&(b=1),a.format(' ',{a:.393+.607*(1-b),b:.769-.769*(1-b),c:.189-.189*(1-b),d:.349-.349*(1-b),e:.686+.314*(1-b),f:.168-.168*(1-b),g:.272-.272*(1-b),h:.534-.534*(1-b),i:.131+.869*(1-b)})},a.filter.sepia.toString=function(){return this()},a.filter.saturate=function(b){return null==b&&(b=1),a.format(' ',{amount:1-b})},a.filter.saturate.toString=function(){return this()},a.filter.hueRotate=function(b){return b=b||0,a.format(' ',{angle:b})},a.filter.hueRotate.toString=function(){return this()},a.filter.invert=function(b){return null==b&&(b=1),a.format(' ',{amount:b,amount2:1-b})},a.filter.invert.toString=function(){return this()},a.filter.brightness=function(b){return null==b&&(b=1),a.format(' ',{amount:b})},a.filter.brightness.toString=function(){return this()},a.filter.contrast=function(b){return null==b&&(b=1),a.format(' ',{amount:b,amount2:.5-b/2})},a.filter.contrast.toString=function(){return this()}}),d});
+/*! svg4everybody v1.0.0 | github.com/jonathantneal/svg4everybody */
+
+(function (document, uses, requestAnimationFrame, CACHE, IE9TO11) {
+ function embed(svg, g) {
+ if (g) {
+ var
+ viewBox = g.getAttribute('viewBox'),
+ fragment = document.createDocumentFragment(),
+ clone = g.cloneNode(true);
+
+ if (viewBox) {
+ svg.setAttribute('viewBox', viewBox);
+ }
+
+ while (clone.childNodes.length) {
+ fragment.appendChild(clone.childNodes[0]);
+ }
+
+ svg.appendChild(fragment);
+ }
+ }
+
+ function onload() {
+ var xhr = this, x = document.createElement('x'), s = xhr.s;
+
+ x.innerHTML = xhr.responseText;
+
+ xhr.onload = function () {
+ s.splice(0).map(function (array) {
+ embed(array[0], x.querySelector('#' + array[1].replace(/(\W)/g, '\\$1')));
+ });
+ };
+
+ xhr.onload();
+ }
+
+ function onframe() {
+ var use;
+
+ while ((use = uses[0])) {
+ var
+ svg = use.parentNode,
+ url = use.getAttribute('xlink:href').split('#'),
+ url_root = url[0],
+ url_hash = url[1];
+
+ svg.removeChild(use);
+
+ if (url_root.length) {
+ var xhr = CACHE[url_root] = CACHE[url_root] || new XMLHttpRequest();
+
+ if (!xhr.s) {
+ xhr.s = [];
+
+ xhr.open('GET', url_root);
+
+ xhr.onload = onload;
+
+ xhr.send();
+ }
+
+ xhr.s.push([svg, url_hash]);
+
+ if (xhr.readyState === 4) {
+ xhr.onload();
+ }
+
+ } else {
+ embed(svg, document.getElementById(url_hash));
+ }
+ }
+
+ requestAnimationFrame(onframe);
+ }
+
+ if (IE9TO11) {
+ onframe();
+ }
+})(
+ document,
+ document.getElementsByTagName('use'),
+ window.requestAnimationFrame || window.setTimeout,
+ {},
+ /Trident\/[567]\b/.test(navigator.userAgent) || (navigator.userAgent.match(/AppleWebKit\/(\d+)/) || [])[1] < 537
+);
+/*! mobile-detect - v1.3.2 - 2016-03-31
+https://github.com/hgoebl/mobile-detect.js */
+!function(a,b){a(function(){"use strict";function a(a,b){return null!=a&&null!=b&&a.toLowerCase()===b.toLowerCase()}function c(a,b){var c,d,e=a.length;if(!e||!b)return!1;for(c=b.toLowerCase(),d=0;e>d;++d)if(c===a[d].toLowerCase())return!0;return!1}function d(a){for(var b in a)h.call(a,b)&&(a[b]=new RegExp(a[b],"i"))}function e(a,b){this.ua=a||"",this._cache={},this.maxPhoneWidth=b||600}var f={};f.mobileDetectRules={phones:{iPhone:"\\biPhone\\b|\\biPod\\b",BlackBerry:"BlackBerry|\\bBB10\\b|rim[0-9]+",HTC:"HTC|HTC.*(Sensation|Evo|Vision|Explorer|6800|8100|8900|A7272|S510e|C110e|Legend|Desire|T8282)|APX515CKT|Qtek9090|APA9292KT|HD_mini|Sensation.*Z710e|PG86100|Z715e|Desire.*(A8181|HD)|ADR6200|ADR6400L|ADR6425|001HT|Inspire 4G|Android.*\\bEVO\\b|T-Mobile G1|Z520m",Nexus:"Nexus One|Nexus S|Galaxy.*Nexus|Android.*Nexus.*Mobile|Nexus 4|Nexus 5|Nexus 6",Dell:"Dell.*Streak|Dell.*Aero|Dell.*Venue|DELL.*Venue Pro|Dell Flash|Dell Smoke|Dell Mini 3iX|XCD28|XCD35|\\b001DL\\b|\\b101DL\\b|\\bGS01\\b",Motorola:"Motorola|DROIDX|DROID BIONIC|\\bDroid\\b.*Build|Android.*Xoom|HRI39|MOT-|A1260|A1680|A555|A853|A855|A953|A955|A956|Motorola.*ELECTRIFY|Motorola.*i1|i867|i940|MB200|MB300|MB501|MB502|MB508|MB511|MB520|MB525|MB526|MB611|MB612|MB632|MB810|MB855|MB860|MB861|MB865|MB870|ME501|ME502|ME511|ME525|ME600|ME632|ME722|ME811|ME860|ME863|ME865|MT620|MT710|MT716|MT720|MT810|MT870|MT917|Motorola.*TITANIUM|WX435|WX445|XT300|XT301|XT311|XT316|XT317|XT319|XT320|XT390|XT502|XT530|XT531|XT532|XT535|XT603|XT610|XT611|XT615|XT681|XT701|XT702|XT711|XT720|XT800|XT806|XT860|XT862|XT875|XT882|XT883|XT894|XT901|XT907|XT909|XT910|XT912|XT928|XT926|XT915|XT919|XT925|XT1021|\\bMoto E\\b",Samsung:"Samsung|SM-G9250|GT-19300|SGH-I337|BGT-S5230|GT-B2100|GT-B2700|GT-B2710|GT-B3210|GT-B3310|GT-B3410|GT-B3730|GT-B3740|GT-B5510|GT-B5512|GT-B5722|GT-B6520|GT-B7300|GT-B7320|GT-B7330|GT-B7350|GT-B7510|GT-B7722|GT-B7800|GT-C3010|GT-C3011|GT-C3060|GT-C3200|GT-C3212|GT-C3212I|GT-C3262|GT-C3222|GT-C3300|GT-C3300K|GT-C3303|GT-C3303K|GT-C3310|GT-C3322|GT-C3330|GT-C3350|GT-C3500|GT-C3510|GT-C3530|GT-C3630|GT-C3780|GT-C5010|GT-C5212|GT-C6620|GT-C6625|GT-C6712|GT-E1050|GT-E1070|GT-E1075|GT-E1080|GT-E1081|GT-E1085|GT-E1087|GT-E1100|GT-E1107|GT-E1110|GT-E1120|GT-E1125|GT-E1130|GT-E1160|GT-E1170|GT-E1175|GT-E1180|GT-E1182|GT-E1200|GT-E1210|GT-E1225|GT-E1230|GT-E1390|GT-E2100|GT-E2120|GT-E2121|GT-E2152|GT-E2220|GT-E2222|GT-E2230|GT-E2232|GT-E2250|GT-E2370|GT-E2550|GT-E2652|GT-E3210|GT-E3213|GT-I5500|GT-I5503|GT-I5700|GT-I5800|GT-I5801|GT-I6410|GT-I6420|GT-I7110|GT-I7410|GT-I7500|GT-I8000|GT-I8150|GT-I8160|GT-I8190|GT-I8320|GT-I8330|GT-I8350|GT-I8530|GT-I8700|GT-I8703|GT-I8910|GT-I9000|GT-I9001|GT-I9003|GT-I9010|GT-I9020|GT-I9023|GT-I9070|GT-I9082|GT-I9100|GT-I9103|GT-I9220|GT-I9250|GT-I9300|GT-I9305|GT-I9500|GT-I9505|GT-M3510|GT-M5650|GT-M7500|GT-M7600|GT-M7603|GT-M8800|GT-M8910|GT-N7000|GT-S3110|GT-S3310|GT-S3350|GT-S3353|GT-S3370|GT-S3650|GT-S3653|GT-S3770|GT-S3850|GT-S5210|GT-S5220|GT-S5229|GT-S5230|GT-S5233|GT-S5250|GT-S5253|GT-S5260|GT-S5263|GT-S5270|GT-S5300|GT-S5330|GT-S5350|GT-S5360|GT-S5363|GT-S5369|GT-S5380|GT-S5380D|GT-S5560|GT-S5570|GT-S5600|GT-S5603|GT-S5610|GT-S5620|GT-S5660|GT-S5670|GT-S5690|GT-S5750|GT-S5780|GT-S5830|GT-S5839|GT-S6102|GT-S6500|GT-S7070|GT-S7200|GT-S7220|GT-S7230|GT-S7233|GT-S7250|GT-S7500|GT-S7530|GT-S7550|GT-S7562|GT-S7710|GT-S8000|GT-S8003|GT-S8500|GT-S8530|GT-S8600|SCH-A310|SCH-A530|SCH-A570|SCH-A610|SCH-A630|SCH-A650|SCH-A790|SCH-A795|SCH-A850|SCH-A870|SCH-A890|SCH-A930|SCH-A950|SCH-A970|SCH-A990|SCH-I100|SCH-I110|SCH-I400|SCH-I405|SCH-I500|SCH-I510|SCH-I515|SCH-I600|SCH-I730|SCH-I760|SCH-I770|SCH-I830|SCH-I910|SCH-I920|SCH-I959|SCH-LC11|SCH-N150|SCH-N300|SCH-R100|SCH-R300|SCH-R351|SCH-R400|SCH-R410|SCH-T300|SCH-U310|SCH-U320|SCH-U350|SCH-U360|SCH-U365|SCH-U370|SCH-U380|SCH-U410|SCH-U430|SCH-U450|SCH-U460|SCH-U470|SCH-U490|SCH-U540|SCH-U550|SCH-U620|SCH-U640|SCH-U650|SCH-U660|SCH-U700|SCH-U740|SCH-U750|SCH-U810|SCH-U820|SCH-U900|SCH-U940|SCH-U960|SCS-26UC|SGH-A107|SGH-A117|SGH-A127|SGH-A137|SGH-A157|SGH-A167|SGH-A177|SGH-A187|SGH-A197|SGH-A227|SGH-A237|SGH-A257|SGH-A437|SGH-A517|SGH-A597|SGH-A637|SGH-A657|SGH-A667|SGH-A687|SGH-A697|SGH-A707|SGH-A717|SGH-A727|SGH-A737|SGH-A747|SGH-A767|SGH-A777|SGH-A797|SGH-A817|SGH-A827|SGH-A837|SGH-A847|SGH-A867|SGH-A877|SGH-A887|SGH-A897|SGH-A927|SGH-B100|SGH-B130|SGH-B200|SGH-B220|SGH-C100|SGH-C110|SGH-C120|SGH-C130|SGH-C140|SGH-C160|SGH-C170|SGH-C180|SGH-C200|SGH-C207|SGH-C210|SGH-C225|SGH-C230|SGH-C417|SGH-C450|SGH-D307|SGH-D347|SGH-D357|SGH-D407|SGH-D415|SGH-D780|SGH-D807|SGH-D980|SGH-E105|SGH-E200|SGH-E315|SGH-E316|SGH-E317|SGH-E335|SGH-E590|SGH-E635|SGH-E715|SGH-E890|SGH-F300|SGH-F480|SGH-I200|SGH-I300|SGH-I320|SGH-I550|SGH-I577|SGH-I600|SGH-I607|SGH-I617|SGH-I627|SGH-I637|SGH-I677|SGH-I700|SGH-I717|SGH-I727|SGH-i747M|SGH-I777|SGH-I780|SGH-I827|SGH-I847|SGH-I857|SGH-I896|SGH-I897|SGH-I900|SGH-I907|SGH-I917|SGH-I927|SGH-I937|SGH-I997|SGH-J150|SGH-J200|SGH-L170|SGH-L700|SGH-M110|SGH-M150|SGH-M200|SGH-N105|SGH-N500|SGH-N600|SGH-N620|SGH-N625|SGH-N700|SGH-N710|SGH-P107|SGH-P207|SGH-P300|SGH-P310|SGH-P520|SGH-P735|SGH-P777|SGH-Q105|SGH-R210|SGH-R220|SGH-R225|SGH-S105|SGH-S307|SGH-T109|SGH-T119|SGH-T139|SGH-T209|SGH-T219|SGH-T229|SGH-T239|SGH-T249|SGH-T259|SGH-T309|SGH-T319|SGH-T329|SGH-T339|SGH-T349|SGH-T359|SGH-T369|SGH-T379|SGH-T409|SGH-T429|SGH-T439|SGH-T459|SGH-T469|SGH-T479|SGH-T499|SGH-T509|SGH-T519|SGH-T539|SGH-T559|SGH-T589|SGH-T609|SGH-T619|SGH-T629|SGH-T639|SGH-T659|SGH-T669|SGH-T679|SGH-T709|SGH-T719|SGH-T729|SGH-T739|SGH-T746|SGH-T749|SGH-T759|SGH-T769|SGH-T809|SGH-T819|SGH-T839|SGH-T919|SGH-T929|SGH-T939|SGH-T959|SGH-T989|SGH-U100|SGH-U200|SGH-U800|SGH-V205|SGH-V206|SGH-X100|SGH-X105|SGH-X120|SGH-X140|SGH-X426|SGH-X427|SGH-X475|SGH-X495|SGH-X497|SGH-X507|SGH-X600|SGH-X610|SGH-X620|SGH-X630|SGH-X700|SGH-X820|SGH-X890|SGH-Z130|SGH-Z150|SGH-Z170|SGH-ZX10|SGH-ZX20|SHW-M110|SPH-A120|SPH-A400|SPH-A420|SPH-A460|SPH-A500|SPH-A560|SPH-A600|SPH-A620|SPH-A660|SPH-A700|SPH-A740|SPH-A760|SPH-A790|SPH-A800|SPH-A820|SPH-A840|SPH-A880|SPH-A900|SPH-A940|SPH-A960|SPH-D600|SPH-D700|SPH-D710|SPH-D720|SPH-I300|SPH-I325|SPH-I330|SPH-I350|SPH-I500|SPH-I600|SPH-I700|SPH-L700|SPH-M100|SPH-M220|SPH-M240|SPH-M300|SPH-M305|SPH-M320|SPH-M330|SPH-M350|SPH-M360|SPH-M370|SPH-M380|SPH-M510|SPH-M540|SPH-M550|SPH-M560|SPH-M570|SPH-M580|SPH-M610|SPH-M620|SPH-M630|SPH-M800|SPH-M810|SPH-M850|SPH-M900|SPH-M910|SPH-M920|SPH-M930|SPH-N100|SPH-N200|SPH-N240|SPH-N300|SPH-N400|SPH-Z400|SWC-E100|SCH-i909|GT-N7100|GT-N7105|SCH-I535|SM-N900A|SGH-I317|SGH-T999L|GT-S5360B|GT-I8262|GT-S6802|GT-S6312|GT-S6310|GT-S5312|GT-S5310|GT-I9105|GT-I8510|GT-S6790N|SM-G7105|SM-N9005|GT-S5301|GT-I9295|GT-I9195|SM-C101|GT-S7392|GT-S7560|GT-B7610|GT-I5510|GT-S7582|GT-S7530E|GT-I8750|SM-G9006V|SM-G9008V|SM-G9009D|SM-G900A|SM-G900D|SM-G900F|SM-G900H|SM-G900I|SM-G900J|SM-G900K|SM-G900L|SM-G900M|SM-G900P|SM-G900R4|SM-G900S|SM-G900T|SM-G900V|SM-G900W8|SHV-E160K|SCH-P709|SCH-P729|SM-T2558|GT-I9205|SM-G9350",LG:"\\bLG\\b;|LG[- ]?(C800|C900|E400|E610|E900|E-900|F160|F180K|F180L|F180S|730|855|L160|LS740|LS840|LS970|LU6200|MS690|MS695|MS770|MS840|MS870|MS910|P500|P700|P705|VM696|AS680|AS695|AX840|C729|E970|GS505|272|C395|E739BK|E960|L55C|L75C|LS696|LS860|P769BK|P350|P500|P509|P870|UN272|US730|VS840|VS950|LN272|LN510|LS670|LS855|LW690|MN270|MN510|P509|P769|P930|UN200|UN270|UN510|UN610|US670|US740|US760|UX265|UX840|VN271|VN530|VS660|VS700|VS740|VS750|VS910|VS920|VS930|VX9200|VX11000|AX840A|LW770|P506|P925|P999|E612|D955|D802|MS323)",Sony:"SonyST|SonyLT|SonyEricsson|SonyEricssonLT15iv|LT18i|E10i|LT28h|LT26w|SonyEricssonMT27i|C5303|C6902|C6903|C6906|C6943|D2533",Asus:"Asus.*Galaxy|PadFone.*Mobile",Micromax:"Micromax.*\\b(A210|A92|A88|A72|A111|A110Q|A115|A116|A110|A90S|A26|A51|A35|A54|A25|A27|A89|A68|A65|A57|A90)\\b",Palm:"PalmSource|Palm",Vertu:"Vertu|Vertu.*Ltd|Vertu.*Ascent|Vertu.*Ayxta|Vertu.*Constellation(F|Quest)?|Vertu.*Monika|Vertu.*Signature",Pantech:"PANTECH|IM-A850S|IM-A840S|IM-A830L|IM-A830K|IM-A830S|IM-A820L|IM-A810K|IM-A810S|IM-A800S|IM-T100K|IM-A725L|IM-A780L|IM-A775C|IM-A770K|IM-A760S|IM-A750K|IM-A740S|IM-A730S|IM-A720L|IM-A710K|IM-A690L|IM-A690S|IM-A650S|IM-A630K|IM-A600S|VEGA PTL21|PT003|P8010|ADR910L|P6030|P6020|P9070|P4100|P9060|P5000|CDM8992|TXT8045|ADR8995|IS11PT|P2030|P6010|P8000|PT002|IS06|CDM8999|P9050|PT001|TXT8040|P2020|P9020|P2000|P7040|P7000|C790",Fly:"IQ230|IQ444|IQ450|IQ440|IQ442|IQ441|IQ245|IQ256|IQ236|IQ255|IQ235|IQ245|IQ275|IQ240|IQ285|IQ280|IQ270|IQ260|IQ250",Wiko:"KITE 4G|HIGHWAY|GETAWAY|STAIRWAY|DARKSIDE|DARKFULL|DARKNIGHT|DARKMOON|SLIDE|WAX 4G|RAINBOW|BLOOM|SUNSET|GOA|LENNY|BARRY|IGGY|OZZY|CINK FIVE|CINK PEAX|CINK PEAX 2|CINK SLIM|CINK SLIM 2|CINK +|CINK KING|CINK PEAX|CINK SLIM|SUBLIM",iMobile:"i-mobile (IQ|i-STYLE|idea|ZAA|Hitz)",SimValley:"\\b(SP-80|XT-930|SX-340|XT-930|SX-310|SP-360|SP60|SPT-800|SP-120|SPT-800|SP-140|SPX-5|SPX-8|SP-100|SPX-8|SPX-12)\\b",Wolfgang:"AT-B24D|AT-AS50HD|AT-AS40W|AT-AS55HD|AT-AS45q2|AT-B26D|AT-AS50Q",Alcatel:"Alcatel",Nintendo:"Nintendo 3DS",Amoi:"Amoi",INQ:"INQ",GenericPhone:"Tapatalk|PDA;|SAGEM|\\bmmp\\b|pocket|\\bpsp\\b|symbian|Smartphone|smartfon|treo|up.browser|up.link|vodafone|\\bwap\\b|nokia|Series40|Series60|S60|SonyEricsson|N900|MAUI.*WAP.*Browser"},tablets:{iPad:"iPad|iPad.*Mobile",NexusTablet:"Android.*Nexus[\\s]+(7|9|10)",SamsungTablet:"SAMSUNG.*Tablet|Galaxy.*Tab|SC-01C|GT-P1000|GT-P1003|GT-P1010|GT-P3105|GT-P6210|GT-P6800|GT-P6810|GT-P7100|GT-P7300|GT-P7310|GT-P7500|GT-P7510|SCH-I800|SCH-I815|SCH-I905|SGH-I957|SGH-I987|SGH-T849|SGH-T859|SGH-T869|SPH-P100|GT-P3100|GT-P3108|GT-P3110|GT-P5100|GT-P5110|GT-P6200|GT-P7320|GT-P7511|GT-N8000|GT-P8510|SGH-I497|SPH-P500|SGH-T779|SCH-I705|SCH-I915|GT-N8013|GT-P3113|GT-P5113|GT-P8110|GT-N8010|GT-N8005|GT-N8020|GT-P1013|GT-P6201|GT-P7501|GT-N5100|GT-N5105|GT-N5110|SHV-E140K|SHV-E140L|SHV-E140S|SHV-E150S|SHV-E230K|SHV-E230L|SHV-E230S|SHW-M180K|SHW-M180L|SHW-M180S|SHW-M180W|SHW-M300W|SHW-M305W|SHW-M380K|SHW-M380S|SHW-M380W|SHW-M430W|SHW-M480K|SHW-M480S|SHW-M480W|SHW-M485W|SHW-M486W|SHW-M500W|GT-I9228|SCH-P739|SCH-I925|GT-I9200|GT-P5200|GT-P5210|GT-P5210X|SM-T311|SM-T310|SM-T310X|SM-T210|SM-T210R|SM-T211|SM-P600|SM-P601|SM-P605|SM-P900|SM-P901|SM-T217|SM-T217A|SM-T217S|SM-P6000|SM-T3100|SGH-I467|XE500|SM-T110|GT-P5220|GT-I9200X|GT-N5110X|GT-N5120|SM-P905|SM-T111|SM-T2105|SM-T315|SM-T320|SM-T320X|SM-T321|SM-T520|SM-T525|SM-T530NU|SM-T230NU|SM-T330NU|SM-T900|XE500T1C|SM-P605V|SM-P905V|SM-T337V|SM-T537V|SM-T707V|SM-T807V|SM-P600X|SM-P900X|SM-T210X|SM-T230|SM-T230X|SM-T325|GT-P7503|SM-T531|SM-T330|SM-T530|SM-T705|SM-T705C|SM-T535|SM-T331|SM-T800|SM-T700|SM-T537|SM-T807|SM-P907A|SM-T337A|SM-T537A|SM-T707A|SM-T807A|SM-T237|SM-T807P|SM-P607T|SM-T217T|SM-T337T|SM-T807T|SM-T116NQ|SM-P550|SM-T350|SM-T550|SM-T9000|SM-P9000|SM-T705Y|SM-T805|GT-P3113|SM-T710|SM-T810|SM-T815|SM-T360|SM-T533|SM-T113|SM-T335|SM-T715|SM-T560|SM-T670|SM-T677|SM-T377|SM-T567|SM-T357T|SM-T555|SM-T561",Kindle:"Kindle|Silk.*Accelerated|Android.*\\b(KFOT|KFTT|KFJWI|KFJWA|KFOTE|KFSOWI|KFTHWI|KFTHWA|KFAPWI|KFAPWA|WFJWAE|KFSAWA|KFSAWI|KFASWI)\\b",SurfaceTablet:"Windows NT [0-9.]+; ARM;.*(Tablet|ARMBJS)",HPTablet:"HP Slate (7|8|10)|HP ElitePad 900|hp-tablet|EliteBook.*Touch|HP 8|Slate 21|HP SlateBook 10",AsusTablet:"^.*PadFone((?!Mobile).)*$|Transformer|TF101|TF101G|TF300T|TF300TG|TF300TL|TF700T|TF700KL|TF701T|TF810C|ME171|ME301T|ME302C|ME371MG|ME370T|ME372MG|ME172V|ME173X|ME400C|Slider SL101|\\bK00F\\b|\\bK00C\\b|\\bK00E\\b|\\bK00L\\b|TX201LA|ME176C|ME102A|\\bM80TA\\b|ME372CL|ME560CG|ME372CG|ME302KL| K010 | K017 |ME572C|ME103K|ME170C|ME171C|\\bME70C\\b|ME581C|ME581CL|ME8510C|ME181C",BlackBerryTablet:"PlayBook|RIM Tablet",HTCtablet:"HTC_Flyer_P512|HTC Flyer|HTC Jetstream|HTC-P715a|HTC EVO View 4G|PG41200|PG09410",MotorolaTablet:"xoom|sholest|MZ615|MZ605|MZ505|MZ601|MZ602|MZ603|MZ604|MZ606|MZ607|MZ608|MZ609|MZ615|MZ616|MZ617",NookTablet:"Android.*Nook|NookColor|nook browser|BNRV200|BNRV200A|BNTV250|BNTV250A|BNTV400|BNTV600|LogicPD Zoom2",AcerTablet:"Android.*; \\b(A100|A101|A110|A200|A210|A211|A500|A501|A510|A511|A700|A701|W500|W500P|W501|W501P|W510|W511|W700|G100|G100W|B1-A71|B1-710|B1-711|A1-810|A1-811|A1-830)\\b|W3-810|\\bA3-A10\\b|\\bA3-A11\\b",ToshibaTablet:"Android.*(AT100|AT105|AT200|AT205|AT270|AT275|AT300|AT305|AT1S5|AT500|AT570|AT700|AT830)|TOSHIBA.*FOLIO",LGTablet:"\\bL-06C|LG-V909|LG-V900|LG-V700|LG-V510|LG-V500|LG-V410|LG-V400|LG-VK810\\b",FujitsuTablet:"Android.*\\b(F-01D|F-02F|F-05E|F-10D|M532|Q572)\\b",PrestigioTablet:"PMP3170B|PMP3270B|PMP3470B|PMP7170B|PMP3370B|PMP3570C|PMP5870C|PMP3670B|PMP5570C|PMP5770D|PMP3970B|PMP3870C|PMP5580C|PMP5880D|PMP5780D|PMP5588C|PMP7280C|PMP7280C3G|PMP7280|PMP7880D|PMP5597D|PMP5597|PMP7100D|PER3464|PER3274|PER3574|PER3884|PER5274|PER5474|PMP5097CPRO|PMP5097|PMP7380D|PMP5297C|PMP5297C_QUAD|PMP812E|PMP812E3G|PMP812F|PMP810E|PMP880TD|PMT3017|PMT3037|PMT3047|PMT3057|PMT7008|PMT5887|PMT5001|PMT5002",LenovoTablet:"Lenovo TAB|Idea(Tab|Pad)( A1|A10| K1|)|ThinkPad([ ]+)?Tablet|Lenovo.*(S2109|S2110|S5000|S6000|K3011|A3000|A3500|A1000|A2107|A2109|A1107|A5500|A7600|B6000|B8000|B8080)(-|)(FL|F|HV|H|)",DellTablet:"Venue 11|Venue 8|Venue 7|Dell Streak 10|Dell Streak 7",YarvikTablet:"Android.*\\b(TAB210|TAB211|TAB224|TAB250|TAB260|TAB264|TAB310|TAB360|TAB364|TAB410|TAB411|TAB420|TAB424|TAB450|TAB460|TAB461|TAB464|TAB465|TAB467|TAB468|TAB07-100|TAB07-101|TAB07-150|TAB07-151|TAB07-152|TAB07-200|TAB07-201-3G|TAB07-210|TAB07-211|TAB07-212|TAB07-214|TAB07-220|TAB07-400|TAB07-485|TAB08-150|TAB08-200|TAB08-201-3G|TAB08-201-30|TAB09-100|TAB09-211|TAB09-410|TAB10-150|TAB10-201|TAB10-211|TAB10-400|TAB10-410|TAB13-201|TAB274EUK|TAB275EUK|TAB374EUK|TAB462EUK|TAB474EUK|TAB9-200)\\b",MedionTablet:"Android.*\\bOYO\\b|LIFE.*(P9212|P9514|P9516|S9512)|LIFETAB",ArnovaTablet:"AN10G2|AN7bG3|AN7fG3|AN8G3|AN8cG3|AN7G3|AN9G3|AN7dG3|AN7dG3ST|AN7dG3ChildPad|AN10bG3|AN10bG3DT|AN9G2",IntensoTablet:"INM8002KP|INM1010FP|INM805ND|Intenso Tab|TAB1004",IRUTablet:"M702pro",MegafonTablet:"MegaFon V9|\\bZTE V9\\b|Android.*\\bMT7A\\b",EbodaTablet:"E-Boda (Supreme|Impresspeed|Izzycomm|Essential)",AllViewTablet:"Allview.*(Viva|Alldro|City|Speed|All TV|Frenzy|Quasar|Shine|TX1|AX1|AX2)",ArchosTablet:"\\b(101G9|80G9|A101IT)\\b|Qilive 97R|Archos5|\\bARCHOS (70|79|80|90|97|101|FAMILYPAD|)(b|)(G10| Cobalt| TITANIUM(HD|)| Xenon| Neon|XSK| 2| XS 2| PLATINUM| CARBON|GAMEPAD)\\b",AinolTablet:"NOVO7|NOVO8|NOVO10|Novo7Aurora|Novo7Basic|NOVO7PALADIN|novo9-Spark",SonyTablet:"Sony.*Tablet|Xperia Tablet|Sony Tablet S|SO-03E|SGPT12|SGPT13|SGPT114|SGPT121|SGPT122|SGPT123|SGPT111|SGPT112|SGPT113|SGPT131|SGPT132|SGPT133|SGPT211|SGPT212|SGPT213|SGP311|SGP312|SGP321|EBRD1101|EBRD1102|EBRD1201|SGP351|SGP341|SGP511|SGP512|SGP521|SGP541|SGP551|SGP621|SGP612|SOT31",PhilipsTablet:"\\b(PI2010|PI3000|PI3100|PI3105|PI3110|PI3205|PI3210|PI3900|PI4010|PI7000|PI7100)\\b",CubeTablet:"Android.*(K8GT|U9GT|U10GT|U16GT|U17GT|U18GT|U19GT|U20GT|U23GT|U30GT)|CUBE U8GT",CobyTablet:"MID1042|MID1045|MID1125|MID1126|MID7012|MID7014|MID7015|MID7034|MID7035|MID7036|MID7042|MID7048|MID7127|MID8042|MID8048|MID8127|MID9042|MID9740|MID9742|MID7022|MID7010",MIDTablet:"M9701|M9000|M9100|M806|M1052|M806|T703|MID701|MID713|MID710|MID727|MID760|MID830|MID728|MID933|MID125|MID810|MID732|MID120|MID930|MID800|MID731|MID900|MID100|MID820|MID735|MID980|MID130|MID833|MID737|MID960|MID135|MID860|MID736|MID140|MID930|MID835|MID733|MID4X10",MSITablet:"MSI \\b(Primo 73K|Primo 73L|Primo 81L|Primo 77|Primo 93|Primo 75|Primo 76|Primo 73|Primo 81|Primo 91|Primo 90|Enjoy 71|Enjoy 7|Enjoy 10)\\b",SMiTTablet:"Android.*(\\bMID\\b|MID-560|MTV-T1200|MTV-PND531|MTV-P1101|MTV-PND530)",RockChipTablet:"Android.*(RK2818|RK2808A|RK2918|RK3066)|RK2738|RK2808A",FlyTablet:"IQ310|Fly Vision",bqTablet:"Android.*(bq)?.*(Elcano|Curie|Edison|Maxwell|Kepler|Pascal|Tesla|Hypatia|Platon|Newton|Livingstone|Cervantes|Avant|Aquaris E10)|Maxwell.*Lite|Maxwell.*Plus",HuaweiTablet:"MediaPad|MediaPad 7 Youth|IDEOS S7|S7-201c|S7-202u|S7-101|S7-103|S7-104|S7-105|S7-106|S7-201|S7-Slim",NecTablet:"\\bN-06D|\\bN-08D",PantechTablet:"Pantech.*P4100",BronchoTablet:"Broncho.*(N701|N708|N802|a710)",VersusTablet:"TOUCHPAD.*[78910]|\\bTOUCHTAB\\b",ZyncTablet:"z1000|Z99 2G|z99|z930|z999|z990|z909|Z919|z900",PositivoTablet:"TB07STA|TB10STA|TB07FTA|TB10FTA",NabiTablet:"Android.*\\bNabi",KoboTablet:"Kobo Touch|\\bK080\\b|\\bVox\\b Build|\\bArc\\b Build",DanewTablet:"DSlide.*\\b(700|701R|702|703R|704|802|970|971|972|973|974|1010|1012)\\b",TexetTablet:"NaviPad|TB-772A|TM-7045|TM-7055|TM-9750|TM-7016|TM-7024|TM-7026|TM-7041|TM-7043|TM-7047|TM-8041|TM-9741|TM-9747|TM-9748|TM-9751|TM-7022|TM-7021|TM-7020|TM-7011|TM-7010|TM-7023|TM-7025|TM-7037W|TM-7038W|TM-7027W|TM-9720|TM-9725|TM-9737W|TM-1020|TM-9738W|TM-9740|TM-9743W|TB-807A|TB-771A|TB-727A|TB-725A|TB-719A|TB-823A|TB-805A|TB-723A|TB-715A|TB-707A|TB-705A|TB-709A|TB-711A|TB-890HD|TB-880HD|TB-790HD|TB-780HD|TB-770HD|TB-721HD|TB-710HD|TB-434HD|TB-860HD|TB-840HD|TB-760HD|TB-750HD|TB-740HD|TB-730HD|TB-722HD|TB-720HD|TB-700HD|TB-500HD|TB-470HD|TB-431HD|TB-430HD|TB-506|TB-504|TB-446|TB-436|TB-416|TB-146SE|TB-126SE",PlaystationTablet:"Playstation.*(Portable|Vita)",TrekstorTablet:"ST10416-1|VT10416-1|ST70408-1|ST702xx-1|ST702xx-2|ST80208|ST97216|ST70104-2|VT10416-2|ST10216-2A|SurfTab",PyleAudioTablet:"\\b(PTBL10CEU|PTBL10C|PTBL72BC|PTBL72BCEU|PTBL7CEU|PTBL7C|PTBL92BC|PTBL92BCEU|PTBL9CEU|PTBL9CUK|PTBL9C)\\b",AdvanTablet:"Android.* \\b(E3A|T3X|T5C|T5B|T3E|T3C|T3B|T1J|T1F|T2A|T1H|T1i|E1C|T1-E|T5-A|T4|E1-B|T2Ci|T1-B|T1-D|O1-A|E1-A|T1-A|T3A|T4i)\\b ",DanyTechTablet:"Genius Tab G3|Genius Tab S2|Genius Tab Q3|Genius Tab G4|Genius Tab Q4|Genius Tab G-II|Genius TAB GII|Genius TAB GIII|Genius Tab S1",GalapadTablet:"Android.*\\bG1\\b",MicromaxTablet:"Funbook|Micromax.*\\b(P250|P560|P360|P362|P600|P300|P350|P500|P275)\\b",KarbonnTablet:"Android.*\\b(A39|A37|A34|ST8|ST10|ST7|Smart Tab3|Smart Tab2)\\b",AllFineTablet:"Fine7 Genius|Fine7 Shine|Fine7 Air|Fine8 Style|Fine9 More|Fine10 Joy|Fine11 Wide",PROSCANTablet:"\\b(PEM63|PLT1023G|PLT1041|PLT1044|PLT1044G|PLT1091|PLT4311|PLT4311PL|PLT4315|PLT7030|PLT7033|PLT7033D|PLT7035|PLT7035D|PLT7044K|PLT7045K|PLT7045KB|PLT7071KG|PLT7072|PLT7223G|PLT7225G|PLT7777G|PLT7810K|PLT7849G|PLT7851G|PLT7852G|PLT8015|PLT8031|PLT8034|PLT8036|PLT8080K|PLT8082|PLT8088|PLT8223G|PLT8234G|PLT8235G|PLT8816K|PLT9011|PLT9045K|PLT9233G|PLT9735|PLT9760G|PLT9770G)\\b",YONESTablet:"BQ1078|BC1003|BC1077|RK9702|BC9730|BC9001|IT9001|BC7008|BC7010|BC708|BC728|BC7012|BC7030|BC7027|BC7026",ChangJiaTablet:"TPC7102|TPC7103|TPC7105|TPC7106|TPC7107|TPC7201|TPC7203|TPC7205|TPC7210|TPC7708|TPC7709|TPC7712|TPC7110|TPC8101|TPC8103|TPC8105|TPC8106|TPC8203|TPC8205|TPC8503|TPC9106|TPC9701|TPC97101|TPC97103|TPC97105|TPC97106|TPC97111|TPC97113|TPC97203|TPC97603|TPC97809|TPC97205|TPC10101|TPC10103|TPC10106|TPC10111|TPC10203|TPC10205|TPC10503",GUTablet:"TX-A1301|TX-M9002|Q702|kf026",PointOfViewTablet:"TAB-P506|TAB-navi-7-3G-M|TAB-P517|TAB-P-527|TAB-P701|TAB-P703|TAB-P721|TAB-P731N|TAB-P741|TAB-P825|TAB-P905|TAB-P925|TAB-PR945|TAB-PL1015|TAB-P1025|TAB-PI1045|TAB-P1325|TAB-PROTAB[0-9]+|TAB-PROTAB25|TAB-PROTAB26|TAB-PROTAB27|TAB-PROTAB26XL|TAB-PROTAB2-IPS9|TAB-PROTAB30-IPS9|TAB-PROTAB25XXL|TAB-PROTAB26-IPS10|TAB-PROTAB30-IPS10",OvermaxTablet:"OV-(SteelCore|NewBase|Basecore|Baseone|Exellen|Quattor|EduTab|Solution|ACTION|BasicTab|TeddyTab|MagicTab|Stream|TB-08|TB-09)",HCLTablet:"HCL.*Tablet|Connect-3G-2.0|Connect-2G-2.0|ME Tablet U1|ME Tablet U2|ME Tablet G1|ME Tablet X1|ME Tablet Y2|ME Tablet Sync",DPSTablet:"DPS Dream 9|DPS Dual 7",VistureTablet:"V97 HD|i75 3G|Visture V4( HD)?|Visture V5( HD)?|Visture V10",CrestaTablet:"CTP(-)?810|CTP(-)?818|CTP(-)?828|CTP(-)?838|CTP(-)?888|CTP(-)?978|CTP(-)?980|CTP(-)?987|CTP(-)?988|CTP(-)?989",MediatekTablet:"\\bMT8125|MT8389|MT8135|MT8377\\b",ConcordeTablet:"Concorde([ ]+)?Tab|ConCorde ReadMan",GoCleverTablet:"GOCLEVER TAB|A7GOCLEVER|M1042|M7841|M742|R1042BK|R1041|TAB A975|TAB A7842|TAB A741|TAB A741L|TAB M723G|TAB M721|TAB A1021|TAB I921|TAB R721|TAB I720|TAB T76|TAB R70|TAB R76.2|TAB R106|TAB R83.2|TAB M813G|TAB I721|GCTA722|TAB I70|TAB I71|TAB S73|TAB R73|TAB R74|TAB R93|TAB R75|TAB R76.1|TAB A73|TAB A93|TAB A93.2|TAB T72|TAB R83|TAB R974|TAB R973|TAB A101|TAB A103|TAB A104|TAB A104.2|R105BK|M713G|A972BK|TAB A971|TAB R974.2|TAB R104|TAB R83.3|TAB A1042",ModecomTablet:"FreeTAB 9000|FreeTAB 7.4|FreeTAB 7004|FreeTAB 7800|FreeTAB 2096|FreeTAB 7.5|FreeTAB 1014|FreeTAB 1001 |FreeTAB 8001|FreeTAB 9706|FreeTAB 9702|FreeTAB 7003|FreeTAB 7002|FreeTAB 1002|FreeTAB 7801|FreeTAB 1331|FreeTAB 1004|FreeTAB 8002|FreeTAB 8014|FreeTAB 9704|FreeTAB 1003",VoninoTablet:"\\b(Argus[ _]?S|Diamond[ _]?79HD|Emerald[ _]?78E|Luna[ _]?70C|Onyx[ _]?S|Onyx[ _]?Z|Orin[ _]?HD|Orin[ _]?S|Otis[ _]?S|SpeedStar[ _]?S|Magnet[ _]?M9|Primus[ _]?94[ _]?3G|Primus[ _]?94HD|Primus[ _]?QS|Android.*\\bQ8\\b|Sirius[ _]?EVO[ _]?QS|Sirius[ _]?QS|Spirit[ _]?S)\\b",ECSTablet:"V07OT2|TM105A|S10OT1|TR10CS1",StorexTablet:"eZee[_']?(Tab|Go)[0-9]+|TabLC7|Looney Tunes Tab",VodafoneTablet:"SmartTab([ ]+)?[0-9]+|SmartTabII10|SmartTabII7",EssentielBTablet:"Smart[ ']?TAB[ ]+?[0-9]+|Family[ ']?TAB2",RossMoorTablet:"RM-790|RM-997|RMD-878G|RMD-974R|RMT-705A|RMT-701|RME-601|RMT-501|RMT-711",iMobileTablet:"i-mobile i-note",TolinoTablet:"tolino tab [0-9.]+|tolino shine",AudioSonicTablet:"\\bC-22Q|T7-QC|T-17B|T-17P\\b",AMPETablet:"Android.* A78 ",SkkTablet:"Android.* (SKYPAD|PHOENIX|CYCLOPS)",TecnoTablet:"TECNO P9",JXDTablet:"Android.* \\b(F3000|A3300|JXD5000|JXD3000|JXD2000|JXD300B|JXD300|S5800|S7800|S602b|S5110b|S7300|S5300|S602|S603|S5100|S5110|S601|S7100a|P3000F|P3000s|P101|P200s|P1000m|P200m|P9100|P1000s|S6600b|S908|P1000|P300|S18|S6600|S9100)\\b",iJoyTablet:"Tablet (Spirit 7|Essentia|Galatea|Fusion|Onix 7|Landa|Titan|Scooby|Deox|Stella|Themis|Argon|Unique 7|Sygnus|Hexen|Finity 7|Cream|Cream X2|Jade|Neon 7|Neron 7|Kandy|Scape|Saphyr 7|Rebel|Biox|Rebel|Rebel 8GB|Myst|Draco 7|Myst|Tab7-004|Myst|Tadeo Jones|Tablet Boing|Arrow|Draco Dual Cam|Aurix|Mint|Amity|Revolution|Finity 9|Neon 9|T9w|Amity 4GB Dual Cam|Stone 4GB|Stone 8GB|Andromeda|Silken|X2|Andromeda II|Halley|Flame|Saphyr 9,7|Touch 8|Planet|Triton|Unique 10|Hexen 10|Memphis 4GB|Memphis 8GB|Onix 10)",FX2Tablet:"FX2 PAD7|FX2 PAD10",XoroTablet:"KidsPAD 701|PAD[ ]?712|PAD[ ]?714|PAD[ ]?716|PAD[ ]?717|PAD[ ]?718|PAD[ ]?720|PAD[ ]?721|PAD[ ]?722|PAD[ ]?790|PAD[ ]?792|PAD[ ]?900|PAD[ ]?9715D|PAD[ ]?9716DR|PAD[ ]?9718DR|PAD[ ]?9719QR|PAD[ ]?9720QR|TelePAD1030|Telepad1032|TelePAD730|TelePAD731|TelePAD732|TelePAD735Q|TelePAD830|TelePAD9730|TelePAD795|MegaPAD 1331|MegaPAD 1851|MegaPAD 2151",ViewsonicTablet:"ViewPad 10pi|ViewPad 10e|ViewPad 10s|ViewPad E72|ViewPad7|ViewPad E100|ViewPad 7e|ViewSonic VB733|VB100a",OdysTablet:"LOOX|XENO10|ODYS[ -](Space|EVO|Xpress|NOON)|\\bXELIO\\b|Xelio10Pro|XELIO7PHONETAB|XELIO10EXTREME|XELIOPT2|NEO_QUAD10",CaptivaTablet:"CAPTIVA PAD",IconbitTablet:"NetTAB|NT-3702|NT-3702S|NT-3702S|NT-3603P|NT-3603P|NT-0704S|NT-0704S|NT-3805C|NT-3805C|NT-0806C|NT-0806C|NT-0909T|NT-0909T|NT-0907S|NT-0907S|NT-0902S|NT-0902S",TeclastTablet:"T98 4G|\\bP80\\b|\\bX90HD\\b|X98 Air|X98 Air 3G|\\bX89\\b|P80 3G|\\bX80h\\b|P98 Air|\\bX89HD\\b|P98 3G|\\bP90HD\\b|P89 3G|X98 3G|\\bP70h\\b|P79HD 3G|G18d 3G|\\bP79HD\\b|\\bP89s\\b|\\bA88\\b|\\bP10HD\\b|\\bP19HD\\b|G18 3G|\\bP78HD\\b|\\bA78\\b|\\bP75\\b|G17s 3G|G17h 3G|\\bP85t\\b|\\bP90\\b|\\bP11\\b|\\bP98t\\b|\\bP98HD\\b|\\bG18d\\b|\\bP85s\\b|\\bP11HD\\b|\\bP88s\\b|\\bA80HD\\b|\\bA80se\\b|\\bA10h\\b|\\bP89\\b|\\bP78s\\b|\\bG18\\b|\\bP85\\b|\\bA70h\\b|\\bA70\\b|\\bG17\\b|\\bP18\\b|\\bA80s\\b|\\bA11s\\b|\\bP88HD\\b|\\bA80h\\b|\\bP76s\\b|\\bP76h\\b|\\bP98\\b|\\bA10HD\\b|\\bP78\\b|\\bP88\\b|\\bA11\\b|\\bA10t\\b|\\bP76a\\b|\\bP76t\\b|\\bP76e\\b|\\bP85HD\\b|\\bP85a\\b|\\bP86\\b|\\bP75HD\\b|\\bP76v\\b|\\bA12\\b|\\bP75a\\b|\\bA15\\b|\\bP76Ti\\b|\\bP81HD\\b|\\bA10\\b|\\bT760VE\\b|\\bT720HD\\b|\\bP76\\b|\\bP73\\b|\\bP71\\b|\\bP72\\b|\\bT720SE\\b|\\bC520Ti\\b|\\bT760\\b|\\bT720VE\\b|T720-3GE|T720-WiFi",OndaTablet:"\\b(V975i|Vi30|VX530|V701|Vi60|V701s|Vi50|V801s|V719|Vx610w|VX610W|V819i|Vi10|VX580W|Vi10|V711s|V813|V811|V820w|V820|Vi20|V711|VI30W|V712|V891w|V972|V819w|V820w|Vi60|V820w|V711|V813s|V801|V819|V975s|V801|V819|V819|V818|V811|V712|V975m|V101w|V961w|V812|V818|V971|V971s|V919|V989|V116w|V102w|V973|Vi40)\\b[\\s]+",JaytechTablet:"TPC-PA762",BlaupunktTablet:"Endeavour 800NG|Endeavour 1010",DigmaTablet:"\\b(iDx10|iDx9|iDx8|iDx7|iDxD7|iDxD8|iDsQ8|iDsQ7|iDsQ8|iDsD10|iDnD7|3TS804H|iDsQ11|iDj7|iDs10)\\b",EvolioTablet:"ARIA_Mini_wifi|Aria[ _]Mini|Evolio X10|Evolio X7|Evolio X8|\\bEvotab\\b|\\bNeura\\b",LavaTablet:"QPAD E704|\\bIvoryS\\b|E-TAB IVORY|\\bE-TAB\\b",AocTablet:"MW0811|MW0812|MW0922|MTK8382",MpmanTablet:"MP11 OCTA|MP10 OCTA|MPQC1114|MPQC1004|MPQC994|MPQC974|MPQC973|MPQC804|MPQC784|MPQC780|\\bMPG7\\b|MPDCG75|MPDCG71|MPDC1006|MP101DC|MPDC9000|MPDC905|MPDC706HD|MPDC706|MPDC705|MPDC110|MPDC100|MPDC99|MPDC97|MPDC88|MPDC8|MPDC77|MP709|MID701|MID711|MID170|MPDC703|MPQC1010",CelkonTablet:"CT695|CT888|CT[\\s]?910|CT7 Tab|CT9 Tab|CT3 Tab|CT2 Tab|CT1 Tab|C820|C720|\\bCT-1\\b",WolderTablet:"miTab \\b(DIAMOND|SPACE|BROOKLYN|NEO|FLY|MANHATTAN|FUNK|EVOLUTION|SKY|GOCAR|IRON|GENIUS|POP|MINT|EPSILON|BROADWAY|JUMP|HOP|LEGEND|NEW AGE|LINE|ADVANCE|FEEL|FOLLOW|LIKE|LINK|LIVE|THINK|FREEDOM|CHICAGO|CLEVELAND|BALTIMORE-GH|IOWA|BOSTON|SEATTLE|PHOENIX|DALLAS|IN 101|MasterChef)\\b",MiTablet:"\\bMI PAD\\b|\\bHM NOTE 1W\\b",NibiruTablet:"Nibiru M1|Nibiru Jupiter One",NexoTablet:"NEXO NOVA|NEXO 10|NEXO AVIO|NEXO FREE|NEXO GO|NEXO EVO|NEXO 3G|NEXO SMART|NEXO KIDDO|NEXO MOBI",LeaderTablet:"TBLT10Q|TBLT10I|TBL-10WDKB|TBL-10WDKBO2013|TBL-W230V2|TBL-W450|TBL-W500|SV572|TBLT7I|TBA-AC7-8G|TBLT79|TBL-8W16|TBL-10W32|TBL-10WKB|TBL-W100",UbislateTablet:"UbiSlate[\\s]?7C",PocketBookTablet:"Pocketbook",Hudl:"Hudl HT7S3|Hudl 2",TelstraTablet:"T-Hub2",GenericTablet:"Android.*\\b97D\\b|Tablet(?!.*PC)|BNTV250A|MID-WCDMA|LogicPD Zoom2|\\bA7EB\\b|CatNova8|A1_07|CT704|CT1002|\\bM721\\b|rk30sdk|\\bEVOTAB\\b|M758A|ET904|ALUMIUM10|Smartfren Tab|Endeavour 1010|Tablet-PC-4|Tagi Tab|\\bM6pro\\b|CT1020W|arc 10HD|\\bJolla\\b|\\bTP750\\b"},oss:{AndroidOS:"Android",BlackBerryOS:"blackberry|\\bBB10\\b|rim tablet os",PalmOS:"PalmOS|avantgo|blazer|elaine|hiptop|palm|plucker|xiino",SymbianOS:"Symbian|SymbOS|Series60|Series40|SYB-[0-9]+|\\bS60\\b",WindowsMobileOS:"Windows CE.*(PPC|Smartphone|Mobile|[0-9]{3}x[0-9]{3})|Window Mobile|Windows Phone [0-9.]+|WCE;",WindowsPhoneOS:"Windows Phone 10.0|Windows Phone 8.1|Windows Phone 8.0|Windows Phone OS|XBLWP7|ZuneWP7|Windows NT 6.[23]; ARM;",iOS:"\\biPhone.*Mobile|\\biPod|\\biPad",MeeGoOS:"MeeGo",MaemoOS:"Maemo",JavaOS:"J2ME/|\\bMIDP\\b|\\bCLDC\\b",webOS:"webOS|hpwOS",badaOS:"\\bBada\\b",BREWOS:"BREW"},uas:{Chrome:"\\bCrMo\\b|CriOS|Android.*Chrome/[.0-9]* (Mobile)?",Dolfin:"\\bDolfin\\b",Opera:"Opera.*Mini|Opera.*Mobi|Android.*Opera|Mobile.*OPR/[0-9.]+|Coast/[0-9.]+",Skyfire:"Skyfire",IE:"IEMobile|MSIEMobile",Firefox:"fennec|firefox.*maemo|(Mobile|Tablet).*Firefox|Firefox.*Mobile",Bolt:"bolt",TeaShark:"teashark",Blazer:"Blazer",Safari:"Version.*Mobile.*Safari|Safari.*Mobile|MobileSafari",Tizen:"Tizen",UCBrowser:"UC.*Browser|UCWEB",baiduboxapp:"baiduboxapp",baidubrowser:"baidubrowser",DiigoBrowser:"DiigoBrowser",Puffin:"Puffin",Mercury:"\\bMercury\\b",ObigoBrowser:"Obigo",NetFront:"NF-Browser",GenericBrowser:"NokiaBrowser|OviBrowser|OneBrowser|TwonkyBeamBrowser|SEMC.*Browser|FlyFlow|Minimo|NetFront|Novarra-Vision|MQQBrowser|MicroMessenger"},props:{Mobile:"Mobile/[VER]",Build:"Build/[VER]",Version:"Version/[VER]",VendorID:"VendorID/[VER]",iPad:"iPad.*CPU[a-z ]+[VER]",iPhone:"iPhone.*CPU[a-z ]+[VER]",iPod:"iPod.*CPU[a-z ]+[VER]",Kindle:"Kindle/[VER]",Chrome:["Chrome/[VER]","CriOS/[VER]","CrMo/[VER]"],Coast:["Coast/[VER]"],Dolfin:"Dolfin/[VER]",Firefox:"Firefox/[VER]",Fennec:"Fennec/[VER]",IE:["IEMobile/[VER];","IEMobile [VER]","MSIE [VER];","Trident/[0-9.]+;.*rv:[VER]"],NetFront:"NetFront/[VER]",NokiaBrowser:"NokiaBrowser/[VER]",Opera:[" OPR/[VER]","Opera Mini/[VER]","Version/[VER]"],"Opera Mini":"Opera Mini/[VER]","Opera Mobi":"Version/[VER]","UC Browser":"UC Browser[VER]",MQQBrowser:"MQQBrowser/[VER]",MicroMessenger:"MicroMessenger/[VER]",baiduboxapp:"baiduboxapp/[VER]",baidubrowser:"baidubrowser/[VER]",Iron:"Iron/[VER]",Safari:["Version/[VER]","Safari/[VER]"],Skyfire:"Skyfire/[VER]",Tizen:"Tizen/[VER]",Webkit:"webkit[ /][VER]",Gecko:"Gecko/[VER]",Trident:"Trident/[VER]",Presto:"Presto/[VER]",iOS:" \\bi?OS\\b [VER][ ;]{1}",Android:"Android [VER]",BlackBerry:["BlackBerry[\\w]+/[VER]","BlackBerry.*Version/[VER]","Version/[VER]"],BREW:"BREW [VER]",Java:"Java/[VER]","Windows Phone OS":["Windows Phone OS [VER]","Windows Phone [VER]"],"Windows Phone":"Windows Phone [VER]","Windows CE":"Windows CE/[VER]","Windows NT":"Windows NT [VER]",Symbian:["SymbianOS/[VER]","Symbian/[VER]"],webOS:["webOS/[VER]","hpwOS/[VER];"]},utils:{Bot:"Googlebot|facebookexternalhit|AdsBot-Google|Google Keyword Suggestion|Facebot|YandexBot|bingbot|ia_archiver|AhrefsBot|Ezooms|GSLFbot|WBSearchBot|Twitterbot|TweetmemeBot|Twikle|PaperLiBot|Wotbox|UnwindFetchor|Exabot|MJ12bot|YandexImages|TurnitinBot|Pingdom",MobileBot:"Googlebot-Mobile|AdsBot-Google-Mobile|YahooSeeker/M1A1-R2D2",DesktopMode:"WPDesktop",TV:"SonyDTV|HbbTV",WebKit:"(webkit)[ /]([\\w.]+)",Console:"\\b(Nintendo|Nintendo WiiU|Nintendo 3DS|PLAYSTATION|Xbox)\\b",Watch:"SM-V700"}},f.detectMobileBrowsers={fullPattern:/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i,shortPattern:/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i,tabletPattern:/android|ipad|playbook|silk/i};var g,h=Object.prototype.hasOwnProperty;return f.FALLBACK_PHONE="UnknownPhone",f.FALLBACK_TABLET="UnknownTablet",f.FALLBACK_MOBILE="UnknownMobile",g="isArray"in Array?Array.isArray:function(a){return"[object Array]"===Object.prototype.toString.call(a)},function(){var a,b,c,e,i,j,k=f.mobileDetectRules;for(a in k.props)if(h.call(k.props,a)){for(b=k.props[a],g(b)||(b=[b]),i=b.length,e=0;i>e;++e)c=b[e],j=c.indexOf("[VER]"),j>=0&&(c=c.substring(0,j)+"([\\w._\\+]+)"+c.substring(j+5)),b[e]=new RegExp(c,"i");k.props[a]=b}d(k.oss),d(k.phones),d(k.tablets),d(k.uas),d(k.utils),k.oss0={WindowsPhoneOS:k.oss.WindowsPhoneOS,WindowsMobileOS:k.oss.WindowsMobileOS}}(),
+f.findMatch=function(a,b){for(var c in a)if(h.call(a,c)&&a[c].test(b))return c;return null},f.findMatches=function(a,b){var c=[];for(var d in a)h.call(a,d)&&a[d].test(b)&&c.push(d);return c},f.getVersionStr=function(a,b){var c,d,e,g,i=f.mobileDetectRules.props;if(h.call(i,a))for(c=i[a],e=c.length,d=0;e>d;++d)if(g=c[d].exec(b),null!==g)return g[1];return null},f.getVersion=function(a,b){var c=f.getVersionStr(a,b);return c?f.prepareVersionNo(c):NaN},f.prepareVersionNo=function(a){var b;return b=a.split(/[a-z._ \/\-]/i),1===b.length&&(a=b[0]),b.length>1&&(a=b[0]+".",b.shift(),a+=b.join("")),Number(a)},f.isMobileFallback=function(a){return f.detectMobileBrowsers.fullPattern.test(a)||f.detectMobileBrowsers.shortPattern.test(a.substr(0,4))},f.isTabletFallback=function(a){return f.detectMobileBrowsers.tabletPattern.test(a)},f.prepareDetectionCache=function(a,c,d){if(a.mobile===b){var g,h,i;return(h=f.findMatch(f.mobileDetectRules.tablets,c))?(a.mobile=a.tablet=h,void(a.phone=null)):(g=f.findMatch(f.mobileDetectRules.phones,c))?(a.mobile=a.phone=g,void(a.tablet=null)):void(f.isMobileFallback(c)?(i=e.isPhoneSized(d),i===b?(a.mobile=f.FALLBACK_MOBILE,a.tablet=a.phone=null):i?(a.mobile=a.phone=f.FALLBACK_PHONE,a.tablet=null):(a.mobile=a.tablet=f.FALLBACK_TABLET,a.phone=null)):f.isTabletFallback(c)?(a.mobile=a.tablet=f.FALLBACK_TABLET,a.phone=null):a.mobile=a.tablet=a.phone=null)}},f.mobileGrade=function(a){var b=null!==a.mobile();return a.os("iOS")&&a.version("iPad")>=4.3||a.os("iOS")&&a.version("iPhone")>=3.1||a.os("iOS")&&a.version("iPod")>=3.1||a.version("Android")>2.1&&a.is("Webkit")||a.version("Windows Phone OS")>=7||a.is("BlackBerry")&&a.version("BlackBerry")>=6||a.match("Playbook.*Tablet")||a.version("webOS")>=1.4&&a.match("Palm|Pre|Pixi")||a.match("hp.*TouchPad")||a.is("Firefox")&&a.version("Firefox")>=12||a.is("Chrome")&&a.is("AndroidOS")&&a.version("Android")>=4||a.is("Skyfire")&&a.version("Skyfire")>=4.1&&a.is("AndroidOS")&&a.version("Android")>=2.3||a.is("Opera")&&a.version("Opera Mobi")>11&&a.is("AndroidOS")||a.is("MeeGoOS")||a.is("Tizen")||a.is("Dolfin")&&a.version("Bada")>=2||(a.is("UC Browser")||a.is("Dolfin"))&&a.version("Android")>=2.3||a.match("Kindle Fire")||a.is("Kindle")&&a.version("Kindle")>=3||a.is("AndroidOS")&&a.is("NookTablet")||a.version("Chrome")>=11&&!b||a.version("Safari")>=5&&!b||a.version("Firefox")>=4&&!b||a.version("MSIE")>=7&&!b||a.version("Opera")>=10&&!b?"A":a.os("iOS")&&a.version("iPad")<4.3||a.os("iOS")&&a.version("iPhone")<3.1||a.os("iOS")&&a.version("iPod")<3.1||a.is("Blackberry")&&a.version("BlackBerry")>=5&&a.version("BlackBerry")<6||a.version("Opera Mini")>=5&&a.version("Opera Mini")<=6.5&&(a.version("Android")>=2.3||a.is("iOS"))||a.match("NokiaN8|NokiaC7|N97.*Series60|Symbian/3")||a.version("Opera Mobi")>=11&&a.is("SymbianOS")?"B":(a.version("BlackBerry")<5||a.match("MSIEMobile|Windows CE.*Mobile")||a.version("Windows Mobile")<=5.2,"C")},f.detectOS=function(a){return f.findMatch(f.mobileDetectRules.oss0,a)||f.findMatch(f.mobileDetectRules.oss,a)},f.getDeviceSmallerSide=function(){return window.screen.widtha?b:f.getDeviceSmallerSide()<=a}:e.isPhoneSized=function(){},e._impl=f,e})}(function(a){if("undefined"!=typeof module&&module.exports)return function(a){module.exports=a()};if("function"==typeof define&&define.amd)return define;if("undefined"!=typeof window)return function(a){window.MobileDetect=a()};throw new Error("unknown environment")}());
+/*
+ * Foundation Responsive Library
+ * http://foundation.zurb.com
+ * Copyright 2015, ZURB
+ * Free to use under the MIT license.
+ * http://www.opensource.org/licenses/mit-license.php
+*/
+
+
+(function ($, window, document, undefined) {
+ 'use strict';
+
+ var header_helpers = function (class_array) {
+ var head = $('head');
+ head.prepend($.map(class_array, function (class_name) {
+ if (head.has('.' + class_name).length === 0) {
+ return ' ';
+ }
+ }));
+ };
+
+ header_helpers([
+ 'foundation-mq-small',
+ 'foundation-mq-small-only',
+ 'foundation-mq-medium',
+ 'foundation-mq-medium-only',
+ 'foundation-mq-large',
+ 'foundation-mq-large-only',
+ 'foundation-mq-xlarge',
+ 'foundation-mq-xlarge-only',
+ 'foundation-mq-xxlarge',
+ 'foundation-data-attribute-namespace']);
+
+ // Enable FastClick if present
+
+ $(function () {
+ if (typeof FastClick !== 'undefined') {
+ // Don't attach to body if undefined
+ if (typeof document.body !== 'undefined') {
+ FastClick.attach(document.body);
+ }
+ }
+ });
+
+ // private Fast Selector wrapper,
+ // returns jQuery object. Only use where
+ // getElementById is not available.
+ var S = function (selector, context) {
+ if (typeof selector === 'string') {
+ if (context) {
+ var cont;
+ if (context.jquery) {
+ cont = context[0];
+ if (!cont) {
+ return context;
+ }
+ } else {
+ cont = context;
+ }
+ return $(cont.querySelectorAll(selector));
+ }
+
+ return $(document.querySelectorAll(selector));
+ }
+
+ return $(selector, context);
+ };
+
+ // Namespace functions.
+
+ var attr_name = function (init) {
+ var arr = [];
+ if (!init) {
+ arr.push('data');
+ }
+ if (this.namespace.length > 0) {
+ arr.push(this.namespace);
+ }
+ arr.push(this.name);
+
+ return arr.join('-');
+ };
+
+ var add_namespace = function (str) {
+ var parts = str.split('-'),
+ i = parts.length,
+ arr = [];
+
+ while (i--) {
+ if (i !== 0) {
+ arr.push(parts[i]);
+ } else {
+ if (this.namespace.length > 0) {
+ arr.push(this.namespace, parts[i]);
+ } else {
+ arr.push(parts[i]);
+ }
+ }
+ }
+
+ return arr.reverse().join('-');
+ };
+
+ // Event binding and data-options updating.
+
+ var bindings = function (method, options) {
+ var self = this,
+ bind = function(){
+ var $this = S(this),
+ should_bind_events = !$this.data(self.attr_name(true) + '-init');
+ $this.data(self.attr_name(true) + '-init', $.extend({}, self.settings, (options || method), self.data_options($this)));
+
+ if (should_bind_events) {
+ self.events(this);
+ }
+ };
+
+ if (S(this.scope).is('[' + this.attr_name() +']')) {
+ bind.call(this.scope);
+ } else {
+ S('[' + this.attr_name() +']', this.scope).each(bind);
+ }
+ // # Patch to fix #5043 to move this *after* the if/else clause in order for Backbone and similar frameworks to have improved control over event binding and data-options updating.
+ if (typeof method === 'string') {
+ return this[method].call(this, options);
+ }
+
+ };
+
+ var single_image_loaded = function (image, callback) {
+ function loaded () {
+ callback(image[0]);
+ }
+
+ function bindLoad () {
+ this.one('load', loaded);
+
+ if (/MSIE (\d+\.\d+);/.test(navigator.userAgent)) {
+ var src = this.attr( 'src' ),
+ param = src.match( /\?/ ) ? '&' : '?';
+
+ param += 'random=' + (new Date()).getTime();
+ this.attr('src', src + param);
+ }
+ }
+
+ if (!image.attr('src')) {
+ loaded();
+ return;
+ }
+
+ if (image[0].complete || image[0].readyState === 4) {
+ loaded();
+ } else {
+ bindLoad.call(image);
+ }
+ };
+
+ /*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas, David Knight. Dual MIT/BSD license */
+
+ window.matchMedia || (window.matchMedia = function() {
+ "use strict";
+
+ // For browsers that support matchMedium api such as IE 9 and webkit
+ var styleMedia = (window.styleMedia || window.media);
+
+ // For those that don't support matchMedium
+ if (!styleMedia) {
+ var style = document.createElement('style'),
+ script = document.getElementsByTagName('script')[0],
+ info = null;
+
+ style.type = 'text/css';
+ style.id = 'matchmediajs-test';
+
+ script.parentNode.insertBefore(style, script);
+
+ // 'style.currentStyle' is used by IE <= 8 and 'window.getComputedStyle' for all other browsers
+ info = ('getComputedStyle' in window) && window.getComputedStyle(style, null) || style.currentStyle;
+
+ styleMedia = {
+ matchMedium: function(media) {
+ var text = '@media ' + media + '{ #matchmediajs-test { width: 1px; } }';
+
+ // 'style.styleSheet' is used by IE <= 8 and 'style.textContent' for all other browsers
+ if (style.styleSheet) {
+ style.styleSheet.cssText = text;
+ } else {
+ style.textContent = text;
+ }
+
+ // Test if media query is true or false
+ return info.width === '1px';
+ }
+ };
+ }
+
+ return function(media) {
+ return {
+ matches: styleMedia.matchMedium(media || 'all'),
+ media: media || 'all'
+ };
+ };
+ }());
+
+ /*
+ * jquery.requestAnimationFrame
+ * https://github.com/gnarf37/jquery-requestAnimationFrame
+ * Requires jQuery 1.8+
+ *
+ * Copyright (c) 2012 Corey Frang
+ * Licensed under the MIT license.
+ */
+
+ (function(jQuery) {
+
+
+ // requestAnimationFrame polyfill adapted from Erik Möller
+ // fixes from Paul Irish and Tino Zijdel
+ // http://paulirish.com/2011/requestanimationframe-for-smart-animating/
+ // http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
+
+ var animating,
+ lastTime = 0,
+ vendors = ['webkit', 'moz'],
+ requestAnimationFrame = window.requestAnimationFrame,
+ cancelAnimationFrame = window.cancelAnimationFrame,
+ jqueryFxAvailable = 'undefined' !== typeof jQuery.fx;
+
+ for (; lastTime < vendors.length && !requestAnimationFrame; lastTime++) {
+ requestAnimationFrame = window[ vendors[lastTime] + 'RequestAnimationFrame' ];
+ cancelAnimationFrame = cancelAnimationFrame ||
+ window[ vendors[lastTime] + 'CancelAnimationFrame' ] ||
+ window[ vendors[lastTime] + 'CancelRequestAnimationFrame' ];
+ }
+
+ function raf() {
+ if (animating) {
+ requestAnimationFrame(raf);
+
+ if (jqueryFxAvailable) {
+ jQuery.fx.tick();
+ }
+ }
+ }
+
+ if (requestAnimationFrame) {
+ // use rAF
+ window.requestAnimationFrame = requestAnimationFrame;
+ window.cancelAnimationFrame = cancelAnimationFrame;
+
+ if (jqueryFxAvailable) {
+ jQuery.fx.timer = function (timer) {
+ if (timer() && jQuery.timers.push(timer) && !animating) {
+ animating = true;
+ raf();
+ }
+ };
+
+ jQuery.fx.stop = function () {
+ animating = false;
+ };
+ }
+ } else {
+ // polyfill
+ window.requestAnimationFrame = function (callback) {
+ var currTime = new Date().getTime(),
+ timeToCall = Math.max(0, 16 - (currTime - lastTime)),
+ id = window.setTimeout(function () {
+ callback(currTime + timeToCall);
+ }, timeToCall);
+ lastTime = currTime + timeToCall;
+ return id;
+ };
+
+ window.cancelAnimationFrame = function (id) {
+ clearTimeout(id);
+ };
+
+ }
+
+ }( $ ));
+
+ function removeQuotes (string) {
+ if (typeof string === 'string' || string instanceof String) {
+ string = string.replace(/^['\\/"]+|(;\s?})+|['\\/"]+$/g, '');
+ }
+
+ return string;
+ }
+
+ function MediaQuery(selector) {
+ this.selector = selector;
+ this.query = '';
+ }
+
+ MediaQuery.prototype.toString = function () {
+ return this.query || (this.query = S(this.selector).css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''));
+ };
+
+ window.Foundation = {
+ name : 'Foundation',
+
+ version : '5.5.3',
+
+ media_queries : {
+ 'small' : new MediaQuery('.foundation-mq-small'),
+ 'small-only' : new MediaQuery('.foundation-mq-small-only'),
+ 'medium' : new MediaQuery('.foundation-mq-medium'),
+ 'medium-only' : new MediaQuery('.foundation-mq-medium-only'),
+ 'large' : new MediaQuery('.foundation-mq-large'),
+ 'large-only' : new MediaQuery('.foundation-mq-large-only'),
+ 'xlarge' : new MediaQuery('.foundation-mq-xlarge'),
+ 'xlarge-only' : new MediaQuery('.foundation-mq-xlarge-only'),
+ 'xxlarge' : new MediaQuery('.foundation-mq-xxlarge')
+ },
+
+ stylesheet : $('').appendTo('head')[0].sheet,
+
+ global : {
+ namespace : undefined
+ },
+
+ init : function (scope, libraries, method, options, response) {
+ var args = [scope, method, options, response],
+ responses = [];
+
+ // check RTL
+ this.rtl = /rtl/i.test(S('html').attr('dir'));
+
+ // set foundation global scope
+ this.scope = scope || this.scope;
+
+ this.set_namespace();
+
+ if (libraries && typeof libraries === 'string' && !/reflow/i.test(libraries)) {
+ if (this.libs.hasOwnProperty(libraries)) {
+ responses.push(this.init_lib(libraries, args));
+ }
+ } else {
+ for (var lib in this.libs) {
+ responses.push(this.init_lib(lib, libraries));
+ }
+ }
+
+ S(window).load(function () {
+ S(window)
+ .trigger('resize.fndtn.clearing')
+ .trigger('resize.fndtn.dropdown')
+ .trigger('resize.fndtn.equalizer')
+ .trigger('resize.fndtn.interchange')
+ .trigger('resize.fndtn.joyride')
+ .trigger('resize.fndtn.magellan')
+ .trigger('resize.fndtn.topbar')
+ .trigger('resize.fndtn.slider');
+ });
+
+ return scope;
+ },
+
+ init_lib : function (lib, args) {
+ if (this.libs.hasOwnProperty(lib)) {
+ this.patch(this.libs[lib]);
+
+ if (args && args.hasOwnProperty(lib)) {
+ if (typeof this.libs[lib].settings !== 'undefined') {
+ $.extend(true, this.libs[lib].settings, args[lib]);
+ } else if (typeof this.libs[lib].defaults !== 'undefined') {
+ $.extend(true, this.libs[lib].defaults, args[lib]);
+ }
+ return this.libs[lib].init.apply(this.libs[lib], [this.scope, args[lib]]);
+ }
+
+ args = args instanceof Array ? args : new Array(args);
+ return this.libs[lib].init.apply(this.libs[lib], args);
+ }
+
+ return function () {};
+ },
+
+ patch : function (lib) {
+ lib.scope = this.scope;
+ lib.namespace = this.global.namespace;
+ lib.rtl = this.rtl;
+ lib['data_options'] = this.utils.data_options;
+ lib['attr_name'] = attr_name;
+ lib['add_namespace'] = add_namespace;
+ lib['bindings'] = bindings;
+ lib['S'] = this.utils.S;
+ },
+
+ inherit : function (scope, methods) {
+ var methods_arr = methods.split(' '),
+ i = methods_arr.length;
+
+ while (i--) {
+ if (this.utils.hasOwnProperty(methods_arr[i])) {
+ scope[methods_arr[i]] = this.utils[methods_arr[i]];
+ }
+ }
+ },
+
+ set_namespace : function () {
+
+ // Description:
+ // Don't bother reading the namespace out of the meta tag
+ // if the namespace has been set globally in javascript
+ //
+ // Example:
+ // Foundation.global.namespace = 'my-namespace';
+ // or make it an empty string:
+ // Foundation.global.namespace = '';
+ //
+ //
+
+ // If the namespace has not been set (is undefined), try to read it out of the meta element.
+ // Otherwise use the globally defined namespace, even if it's empty ('')
+ var namespace = ( this.global.namespace === undefined ) ? $('.foundation-data-attribute-namespace').css('font-family') : this.global.namespace;
+
+ // Finally, if the namsepace is either undefined or false, set it to an empty string.
+ // Otherwise use the namespace value.
+ this.global.namespace = ( namespace === undefined || /false/i.test(namespace) ) ? '' : namespace;
+ },
+
+ libs : {},
+
+ // methods that can be inherited in libraries
+ utils : {
+
+ // Description:
+ // Fast Selector wrapper returns jQuery object. Only use where getElementById
+ // is not available.
+ //
+ // Arguments:
+ // Selector (String): CSS selector describing the element(s) to be
+ // returned as a jQuery object.
+ //
+ // Scope (String): CSS selector describing the area to be searched. Default
+ // is document.
+ //
+ // Returns:
+ // Element (jQuery Object): jQuery object containing elements matching the
+ // selector within the scope.
+ S : S,
+
+ // Description:
+ // Executes a function a max of once every n milliseconds
+ //
+ // Arguments:
+ // Func (Function): Function to be throttled.
+ //
+ // Delay (Integer): Function execution threshold in milliseconds.
+ //
+ // Returns:
+ // Lazy_function (Function): Function with throttling applied.
+ throttle : function (func, delay) {
+ var timer = null;
+
+ return function () {
+ var context = this, args = arguments;
+
+ if (timer == null) {
+ timer = setTimeout(function () {
+ func.apply(context, args);
+ timer = null;
+ }, delay);
+ }
+ };
+ },
+
+ // Description:
+ // Executes a function when it stops being invoked for n seconds
+ // Modified version of _.debounce() http://underscorejs.org
+ //
+ // Arguments:
+ // Func (Function): Function to be debounced.
+ //
+ // Delay (Integer): Function execution threshold in milliseconds.
+ //
+ // Immediate (Bool): Whether the function should be called at the beginning
+ // of the delay instead of the end. Default is false.
+ //
+ // Returns:
+ // Lazy_function (Function): Function with debouncing applied.
+ debounce : function (func, delay, immediate) {
+ var timeout, result;
+ return function () {
+ var context = this, args = arguments;
+ var later = function () {
+ timeout = null;
+ if (!immediate) {
+ result = func.apply(context, args);
+ }
+ };
+ var callNow = immediate && !timeout;
+ clearTimeout(timeout);
+ timeout = setTimeout(later, delay);
+ if (callNow) {
+ result = func.apply(context, args);
+ }
+ return result;
+ };
+ },
+
+ // Description:
+ // Parses data-options attribute
+ //
+ // Arguments:
+ // El (jQuery Object): Element to be parsed.
+ //
+ // Returns:
+ // Options (Javascript Object): Contents of the element's data-options
+ // attribute.
+ data_options : function (el, data_attr_name) {
+ data_attr_name = data_attr_name || 'options';
+ var opts = {}, ii, p, opts_arr,
+ data_options = function (el) {
+ var namespace = Foundation.global.namespace;
+
+ if (namespace.length > 0) {
+ return el.data(namespace + '-' + data_attr_name);
+ }
+
+ return el.data(data_attr_name);
+ };
+
+ var cached_options = data_options(el);
+
+ if (typeof cached_options === 'object') {
+ return cached_options;
+ }
+
+ opts_arr = (cached_options || ':').split(';');
+ ii = opts_arr.length;
+
+ function isNumber (o) {
+ return !isNaN (o - 0) && o !== null && o !== '' && o !== false && o !== true;
+ }
+
+ function trim (str) {
+ if (typeof str === 'string') {
+ return $.trim(str);
+ }
+ return str;
+ }
+
+ while (ii--) {
+ p = opts_arr[ii].split(':');
+ p = [p[0], p.slice(1).join(':')];
+
+ if (/true/i.test(p[1])) {
+ p[1] = true;
+ }
+ if (/false/i.test(p[1])) {
+ p[1] = false;
+ }
+ if (isNumber(p[1])) {
+ if (p[1].indexOf('.') === -1) {
+ p[1] = parseInt(p[1], 10);
+ } else {
+ p[1] = parseFloat(p[1]);
+ }
+ }
+
+ if (p.length === 2 && p[0].length > 0) {
+ opts[trim(p[0])] = trim(p[1]);
+ }
+ }
+
+ return opts;
+ },
+
+ // Description:
+ // Adds JS-recognizable media queries
+ //
+ // Arguments:
+ // Media (String): Key string for the media query to be stored as in
+ // Foundation.media_queries
+ //
+ // Class (String): Class name for the generated tag
+ register_media : function (media, media_class) {
+ if (Foundation.media_queries[media] === undefined) {
+ $('head').append(' ');
+ Foundation.media_queries[media] = removeQuotes($('.' + media_class).css('font-family'));
+ }
+ },
+
+ // Description:
+ // Add custom CSS within a JS-defined media query
+ //
+ // Arguments:
+ // Rule (String): CSS rule to be appended to the document.
+ //
+ // Media (String): Optional media query string for the CSS rule to be
+ // nested under.
+ add_custom_rule : function (rule, media) {
+ if (media === undefined && Foundation.stylesheet) {
+ Foundation.stylesheet.insertRule(rule, Foundation.stylesheet.cssRules.length);
+ } else {
+ var query = Foundation.media_queries[media];
+
+ if (query !== undefined) {
+ Foundation.stylesheet.insertRule('@media ' +
+ Foundation.media_queries[media] + '{ ' + rule + ' }', Foundation.stylesheet.cssRules.length);
+ }
+ }
+ },
+
+ // Description:
+ // Performs a callback function when an image is fully loaded
+ //
+ // Arguments:
+ // Image (jQuery Object): Image(s) to check if loaded.
+ //
+ // Callback (Function): Function to execute when image is fully loaded.
+ image_loaded : function (images, callback) {
+ var self = this,
+ unloaded = images.length;
+
+ function pictures_has_height(images) {
+ var pictures_number = images.length;
+
+ for (var i = pictures_number - 1; i >= 0; i--) {
+ if(images.attr('height') === undefined) {
+ return false;
+ };
+ };
+
+ return true;
+ }
+
+ if (unloaded === 0 || pictures_has_height(images)) {
+ callback(images);
+ }
+
+ images.each(function () {
+ single_image_loaded(self.S(this), function () {
+ unloaded -= 1;
+ if (unloaded === 0) {
+ callback(images);
+ }
+ });
+ });
+ },
+
+ // Description:
+ // Returns a random, alphanumeric string
+ //
+ // Arguments:
+ // Length (Integer): Length of string to be generated. Defaults to random
+ // integer.
+ //
+ // Returns:
+ // Rand (String): Pseudo-random, alphanumeric string.
+ random_str : function () {
+ if (!this.fidx) {
+ this.fidx = 0;
+ }
+ this.prefix = this.prefix || [(this.name || 'F'), (+new Date).toString(36)].join('-');
+
+ return this.prefix + (this.fidx++).toString(36);
+ },
+
+ // Description:
+ // Helper for window.matchMedia
+ //
+ // Arguments:
+ // mq (String): Media query
+ //
+ // Returns:
+ // (Boolean): Whether the media query passes or not
+ match : function (mq) {
+ return window.matchMedia(mq).matches;
+ },
+
+ // Description:
+ // Helpers for checking Foundation default media queries with JS
+ //
+ // Returns:
+ // (Boolean): Whether the media query passes or not
+
+ is_small_up : function () {
+ return this.match(Foundation.media_queries.small);
+ },
+
+ is_medium_up : function () {
+ return this.match(Foundation.media_queries.medium);
+ },
+
+ is_large_up : function () {
+ return this.match(Foundation.media_queries.large);
+ },
+
+ is_xlarge_up : function () {
+ return this.match(Foundation.media_queries.xlarge);
+ },
+
+ is_xxlarge_up : function () {
+ return this.match(Foundation.media_queries.xxlarge);
+ },
+
+ is_small_only : function () {
+ return !this.is_medium_up() && !this.is_large_up() && !this.is_xlarge_up() && !this.is_xxlarge_up();
+ },
+
+ is_medium_only : function () {
+ return this.is_medium_up() && !this.is_large_up() && !this.is_xlarge_up() && !this.is_xxlarge_up();
+ },
+
+ is_large_only : function () {
+ return this.is_medium_up() && this.is_large_up() && !this.is_xlarge_up() && !this.is_xxlarge_up();
+ },
+
+ is_xlarge_only : function () {
+ return this.is_medium_up() && this.is_large_up() && this.is_xlarge_up() && !this.is_xxlarge_up();
+ },
+
+ is_xxlarge_only : function () {
+ return this.is_medium_up() && this.is_large_up() && this.is_xlarge_up() && this.is_xxlarge_up();
+ }
+ }
+ };
+
+ $.fn.foundation = function () {
+ var args = Array.prototype.slice.call(arguments, 0);
+
+ return this.each(function () {
+ Foundation.init.apply(Foundation, [this].concat(args));
+ return this;
+ });
+ };
+
+}(jQuery, window, window.document));
+;(function ($, window, document, undefined) {
+ 'use strict';
+
+ var openModals = [];
+
+ Foundation.libs.reveal = {
+ name : 'reveal',
+
+ version : '5.5.3',
+
+ locked : false,
+
+ settings : {
+ animation : 'fadeAndPop',
+ animation_speed : 250,
+ close_on_background_click : true,
+ close_on_esc : true,
+ dismiss_modal_class : 'close-reveal-modal',
+ multiple_opened : false,
+ bg_class : 'reveal-modal-bg',
+ root_element : 'body',
+ open : function(){},
+ opened : function(){},
+ close : function(){},
+ closed : function(){},
+ on_ajax_error: $.noop,
+ bg : $('.reveal-modal-bg'),
+ css : {
+ open : {
+ 'opacity' : 0,
+ 'visibility' : 'visible',
+ 'display' : 'block'
+ },
+ close : {
+ 'opacity' : 1,
+ 'visibility' : 'hidden',
+ 'display' : 'none'
+ }
+ }
+ },
+
+ init : function (scope, method, options) {
+ $.extend(true, this.settings, method, options);
+ this.bindings(method, options);
+ },
+
+ events : function (scope) {
+ var self = this,
+ S = self.S;
+
+ S(this.scope)
+ .off('.reveal')
+ .on('click.fndtn.reveal', '[' + this.add_namespace('data-reveal-id') + ']:not([disabled])', function (e) {
+ e.preventDefault();
+
+ if (!self.locked) {
+ var element = S(this),
+ ajax = element.data(self.data_attr('reveal-ajax')),
+ replaceContentSel = element.data(self.data_attr('reveal-replace-content'));
+
+ self.locked = true;
+
+ if (typeof ajax === 'undefined') {
+ self.open.call(self, element);
+ } else {
+ var url = ajax === true ? element.attr('href') : ajax;
+ self.open.call(self, element, {url : url}, { replaceContentSel : replaceContentSel });
+ }
+ }
+ });
+
+ S(document)
+ .on('click.fndtn.reveal', this.close_targets(), function (e) {
+ e.preventDefault();
+ if (!self.locked) {
+ var settings = S('[' + self.attr_name() + '].open').data(self.attr_name(true) + '-init') || self.settings,
+ bg_clicked = S(e.target)[0] === S('.' + settings.bg_class)[0];
+
+ if (bg_clicked) {
+ if (settings.close_on_background_click) {
+ e.stopPropagation();
+ } else {
+ return;
+ }
+ }
+
+ self.locked = true;
+ self.close.call(self, bg_clicked ? S('[' + self.attr_name() + '].open:not(.toback)') : S(this).closest('[' + self.attr_name() + ']'));
+ }
+ });
+
+ if (S('[' + self.attr_name() + ']', this.scope).length > 0) {
+ S(this.scope)
+ // .off('.reveal')
+ .on('open.fndtn.reveal', this.settings.open)
+ .on('opened.fndtn.reveal', this.settings.opened)
+ .on('opened.fndtn.reveal', this.open_video)
+ .on('close.fndtn.reveal', this.settings.close)
+ .on('closed.fndtn.reveal', this.settings.closed)
+ .on('closed.fndtn.reveal', this.close_video);
+ } else {
+ S(this.scope)
+ // .off('.reveal')
+ .on('open.fndtn.reveal', '[' + self.attr_name() + ']', this.settings.open)
+ .on('opened.fndtn.reveal', '[' + self.attr_name() + ']', this.settings.opened)
+ .on('opened.fndtn.reveal', '[' + self.attr_name() + ']', this.open_video)
+ .on('close.fndtn.reveal', '[' + self.attr_name() + ']', this.settings.close)
+ .on('closed.fndtn.reveal', '[' + self.attr_name() + ']', this.settings.closed)
+ .on('closed.fndtn.reveal', '[' + self.attr_name() + ']', this.close_video);
+ }
+
+ return true;
+ },
+
+ // PATCH #3: turning on key up capture only when a reveal window is open
+ key_up_on : function (scope) {
+ var self = this;
+
+ // PATCH #1: fixing multiple keyup event trigger from single key press
+ self.S('body').off('keyup.fndtn.reveal').on('keyup.fndtn.reveal', function ( event ) {
+ var open_modal = self.S('[' + self.attr_name() + '].open'),
+ settings = open_modal.data(self.attr_name(true) + '-init') || self.settings ;
+ // PATCH #2: making sure that the close event can be called only while unlocked,
+ // so that multiple keyup.fndtn.reveal events don't prevent clean closing of the reveal window.
+ if ( settings && event.which === 27 && settings.close_on_esc && !self.locked) { // 27 is the keycode for the Escape key
+ self.close.call(self, open_modal);
+ }
+ });
+
+ return true;
+ },
+
+ // PATCH #3: turning on key up capture only when a reveal window is open
+ key_up_off : function (scope) {
+ this.S('body').off('keyup.fndtn.reveal');
+ return true;
+ },
+
+ open : function (target, ajax_settings) {
+ var self = this,
+ modal;
+
+ if (target) {
+ if (typeof target.selector !== 'undefined') {
+ // Find the named node; only use the first one found, since the rest of the code assumes there's only one node
+ modal = self.S('#' + target.data(self.data_attr('reveal-id'))).first();
+ } else {
+ modal = self.S(this.scope);
+
+ ajax_settings = target;
+ }
+ } else {
+ modal = self.S(this.scope);
+ }
+
+ var settings = modal.data(self.attr_name(true) + '-init');
+ settings = settings || this.settings;
+
+
+ if (modal.hasClass('open') && target !== undefined && target.attr('data-reveal-id') == modal.attr('id')) {
+ return self.close(modal);
+ }
+
+ if (!modal.hasClass('open')) {
+ var open_modal = self.S('[' + self.attr_name() + '].open');
+
+ if (typeof modal.data('css-top') === 'undefined') {
+ modal.data('css-top', parseInt(modal.css('top'), 10))
+ .data('offset', this.cache_offset(modal));
+ }
+
+ modal.attr('tabindex','0').attr('aria-hidden','false');
+
+ this.key_up_on(modal); // PATCH #3: turning on key up capture only when a reveal window is open
+
+ // Prevent namespace event from triggering twice
+ modal.on('open.fndtn.reveal', function(e) {
+ if (e.namespace !== 'fndtn.reveal') return;
+ });
+
+ modal.on('open.fndtn.reveal').trigger('open.fndtn.reveal');
+
+ if (open_modal.length < 1) {
+ this.toggle_bg(modal, true);
+ }
+
+ if (typeof ajax_settings === 'string') {
+ ajax_settings = {
+ url : ajax_settings
+ };
+ }
+
+ var openModal = function() {
+ if(open_modal.length > 0) {
+ if(settings.multiple_opened) {
+ self.to_back(open_modal);
+ } else {
+ self.hide(open_modal, settings.css.close);
+ }
+ }
+
+ // bl: add the open_modal that isn't already in the background to the openModals array
+ if(settings.multiple_opened) {
+ openModals.push(modal);
+ }
+
+ self.show(modal, settings.css.open);
+ };
+
+ if (typeof ajax_settings === 'undefined' || !ajax_settings.url) {
+ openModal();
+ } else {
+ var old_success = typeof ajax_settings.success !== 'undefined' ? ajax_settings.success : null;
+ $.extend(ajax_settings, {
+ success : function (data, textStatus, jqXHR) {
+ if ( $.isFunction(old_success) ) {
+ var result = old_success(data, textStatus, jqXHR);
+ if (typeof result == 'string') {
+ data = result;
+ }
+ }
+
+ if (typeof options !== 'undefined' && typeof options.replaceContentSel !== 'undefined') {
+ modal.find(options.replaceContentSel).html(data);
+ } else {
+ modal.html(data);
+ }
+
+ self.S(modal).foundation('section', 'reflow');
+ self.S(modal).children().foundation();
+
+ openModal();
+ }
+ });
+
+ // check for if user initalized with error callback
+ if (settings.on_ajax_error !== $.noop) {
+ $.extend(ajax_settings, {
+ error : settings.on_ajax_error
+ });
+ }
+
+ $.ajax(ajax_settings);
+ }
+ }
+ self.S(window).trigger('resize');
+ },
+
+ close : function (modal) {
+ var modal = modal && modal.length ? modal : this.S(this.scope),
+ open_modals = this.S('[' + this.attr_name() + '].open'),
+ settings = modal.data(this.attr_name(true) + '-init') || this.settings,
+ self = this;
+
+ if (open_modals.length > 0) {
+
+ modal.removeAttr('tabindex','0').attr('aria-hidden','true');
+
+ this.locked = true;
+ this.key_up_off(modal); // PATCH #3: turning on key up capture only when a reveal window is open
+
+ modal.trigger('close.fndtn.reveal');
+
+ if ((settings.multiple_opened && open_modals.length === 1) || !settings.multiple_opened || modal.length > 1) {
+ self.toggle_bg(modal, false);
+ self.to_front(modal);
+ }
+
+ if (settings.multiple_opened) {
+ var isCurrent = modal.is(':not(.toback)');
+ self.hide(modal, settings.css.close, settings);
+ if(isCurrent) {
+ // remove the last modal since it is now closed
+ openModals.pop();
+ } else {
+ // if this isn't the current modal, then find it in the array and remove it
+ openModals = $.grep(openModals, function(elt) {
+ var isThis = elt[0]===modal[0];
+ if(isThis) {
+ // since it's not currently in the front, put it in the front now that it is hidden
+ // so that if it's re-opened, it won't be .toback
+ self.to_front(modal);
+ }
+ return !isThis;
+ });
+ }
+ // finally, show the next modal in the stack, if there is one
+ if(openModals.length>0) {
+ self.to_front(openModals[openModals.length - 1]);
+ }
+ } else {
+ self.hide(open_modals, settings.css.close, settings);
+ }
+ }
+ },
+
+ close_targets : function () {
+ var base = '.' + this.settings.dismiss_modal_class;
+
+ if (this.settings.close_on_background_click) {
+ return base + ', .' + this.settings.bg_class;
+ }
+
+ return base;
+ },
+
+ toggle_bg : function (modal, state) {
+ if (this.S('.' + this.settings.bg_class).length === 0) {
+ this.settings.bg = $('
', {'class': this.settings.bg_class})
+ .appendTo('body').hide();
+ }
+
+ var visible = this.settings.bg.filter(':visible').length > 0;
+ if ( state != visible ) {
+ if ( state == undefined ? visible : !state ) {
+ this.hide(this.settings.bg);
+ } else {
+ this.show(this.settings.bg);
+ }
+ }
+ },
+
+ show : function (el, css) {
+ // is modal
+ if (css) {
+ var settings = el.data(this.attr_name(true) + '-init') || this.settings,
+ root_element = settings.root_element,
+ context = this;
+
+ if (el.parent(root_element).length === 0) {
+ var placeholder = el.wrap('
').parent();
+
+ el.on('closed.fndtn.reveal.wrapped', function () {
+ el.detach().appendTo(placeholder);
+ el.unwrap().unbind('closed.fndtn.reveal.wrapped');
+ });
+
+ el.detach().appendTo(root_element);
+ }
+
+ var animData = getAnimationData(settings.animation);
+ if (!animData.animate) {
+ this.locked = false;
+ }
+ if (animData.pop) {
+ css.top = $(window).scrollTop() - el.data('offset') + 'px';
+ var end_css = {
+ top: $(window).scrollTop() + el.data('css-top') + 'px',
+ opacity: 1
+ };
+
+ return setTimeout(function () {
+ return el
+ .css(css)
+ .animate(end_css, settings.animation_speed, 'linear', function () {
+ context.locked = false;
+ el.trigger('opened.fndtn.reveal');
+ })
+ .addClass('open');
+ }, settings.animation_speed / 2);
+ }
+
+ css.top = $(window).scrollTop() + el.data('css-top') + 'px';
+
+ if (animData.fade) {
+ var end_css = {opacity: 1};
+
+ return setTimeout(function () {
+ return el
+ .css(css)
+ .animate(end_css, settings.animation_speed, 'linear', function () {
+ context.locked = false;
+ el.trigger('opened.fndtn.reveal');
+ })
+ .addClass('open');
+ }, settings.animation_speed / 2);
+ }
+
+ return el.css(css).show().css({opacity : 1}).addClass('open').trigger('opened.fndtn.reveal');
+ }
+
+ var settings = this.settings;
+
+ // should we animate the background?
+ if (getAnimationData(settings.animation).fade) {
+ return el.fadeIn(settings.animation_speed / 2);
+ }
+
+ this.locked = false;
+
+ return el.show();
+ },
+
+ to_back : function(el) {
+ el.addClass('toback');
+ },
+
+ to_front : function(el) {
+ el.removeClass('toback');
+ },
+
+ hide : function (el, css) {
+ // is modal
+ if (css) {
+ var settings = el.data(this.attr_name(true) + '-init'),
+ context = this;
+ settings = settings || this.settings;
+
+ var animData = getAnimationData(settings.animation);
+ if (!animData.animate) {
+ this.locked = false;
+ }
+ if (animData.pop) {
+ var end_css = {
+ top: - $(window).scrollTop() - el.data('offset') + 'px',
+ opacity: 0
+ };
+
+ return setTimeout(function () {
+ return el
+ .animate(end_css, settings.animation_speed, 'linear', function () {
+ context.locked = false;
+ el.css(css).trigger('closed.fndtn.reveal');
+ })
+ .removeClass('open');
+ }, settings.animation_speed / 2);
+ }
+
+ if (animData.fade) {
+ var end_css = {opacity : 0};
+
+ return setTimeout(function () {
+ return el
+ .animate(end_css, settings.animation_speed, 'linear', function () {
+ context.locked = false;
+ el.css(css).trigger('closed.fndtn.reveal');
+ })
+ .removeClass('open');
+ }, settings.animation_speed / 2);
+ }
+
+ return el.hide().css(css).removeClass('open').trigger('closed.fndtn.reveal');
+ }
+
+ var settings = this.settings;
+
+ // should we animate the background?
+ if (getAnimationData(settings.animation).fade) {
+ return el.fadeOut(settings.animation_speed / 2);
+ }
+
+ return el.hide();
+ },
+
+ close_video : function (e) {
+ var video = $('.flex-video', e.target),
+ iframe = $('iframe', video);
+
+ if (iframe.length > 0) {
+ iframe.attr('data-src', iframe[0].src);
+ iframe.attr('src', iframe.attr('src'));
+ video.hide();
+ }
+ },
+
+ open_video : function (e) {
+ var video = $('.flex-video', e.target),
+ iframe = video.find('iframe');
+
+ if (iframe.length > 0) {
+ var data_src = iframe.attr('data-src');
+ if (typeof data_src === 'string') {
+ iframe[0].src = iframe.attr('data-src');
+ } else {
+ var src = iframe[0].src;
+ iframe[0].src = undefined;
+ iframe[0].src = src;
+ }
+ video.show();
+ }
+ },
+
+ data_attr : function (str) {
+ if (this.namespace.length > 0) {
+ return this.namespace + '-' + str;
+ }
+
+ return str;
+ },
+
+ cache_offset : function (modal) {
+ var offset = modal.show().height() + parseInt(modal.css('top'), 10) + modal.scrollY;
+
+ modal.hide();
+
+ return offset;
+ },
+
+ off : function () {
+ $(this.scope).off('.fndtn.reveal');
+ },
+
+ reflow : function () {}
+ };
+
+ /*
+ * getAnimationData('popAndFade') // {animate: true, pop: true, fade: true}
+ * getAnimationData('fade') // {animate: true, pop: false, fade: true}
+ * getAnimationData('pop') // {animate: true, pop: true, fade: false}
+ * getAnimationData('foo') // {animate: false, pop: false, fade: false}
+ * getAnimationData(null) // {animate: false, pop: false, fade: false}
+ */
+ function getAnimationData(str) {
+ var fade = /fade/i.test(str);
+ var pop = /pop/i.test(str);
+ return {
+ animate : fade || pop,
+ pop : pop,
+ fade : fade
+ };
+ }
+}(jQuery, window, window.document));
+;(function ($, window, document, undefined) {
+ 'use strict';
+
+ Foundation.libs.dropdown = {
+ name : 'dropdown',
+
+ version : '5.5.3',
+
+ settings : {
+ active_class : 'open',
+ disabled_class : 'disabled',
+ mega_class : 'mega',
+ align : 'bottom',
+ is_hover : false,
+ hover_timeout : 150,
+ opened : function () {},
+ closed : function () {}
+ },
+
+ init : function (scope, method, options) {
+ Foundation.inherit(this, 'throttle');
+
+ $.extend(true, this.settings, method, options);
+ this.bindings(method, options);
+ },
+
+ events : function (scope) {
+ var self = this,
+ S = self.S;
+
+ S(this.scope)
+ .off('.dropdown')
+ .on('click.fndtn.dropdown', '[' + this.attr_name() + ']', function (e) {
+ var settings = S(this).data(self.attr_name(true) + '-init') || self.settings;
+ if (!settings.is_hover || Modernizr.touch) {
+ e.preventDefault();
+ if (S(this).parent('[data-reveal-id]').length) {
+ e.stopPropagation();
+ }
+ self.toggle($(this));
+ }
+ })
+ .on('mouseenter.fndtn.dropdown', '[' + this.attr_name() + '], [' + this.attr_name() + '-content]', function (e) {
+ var $this = S(this),
+ dropdown,
+ target;
+
+ clearTimeout(self.timeout);
+
+ if ($this.data(self.data_attr())) {
+ dropdown = S('#' + $this.data(self.data_attr()));
+ target = $this;
+ } else {
+ dropdown = $this;
+ target = S('[' + self.attr_name() + '="' + dropdown.attr('id') + '"]');
+ }
+
+ var settings = target.data(self.attr_name(true) + '-init') || self.settings;
+
+ if (S(e.currentTarget).data(self.data_attr()) && settings.is_hover) {
+ self.closeall.call(self);
+ }
+
+ if (settings.is_hover) {
+ self.open.apply(self, [dropdown, target]);
+ }
+ })
+ .on('mouseleave.fndtn.dropdown', '[' + this.attr_name() + '], [' + this.attr_name() + '-content]', function (e) {
+ var $this = S(this);
+ var settings;
+
+ if ($this.data(self.data_attr())) {
+ settings = $this.data(self.data_attr(true) + '-init') || self.settings;
+ } else {
+ var target = S('[' + self.attr_name() + '="' + S(this).attr('id') + '"]'),
+ settings = target.data(self.attr_name(true) + '-init') || self.settings;
+ }
+
+ self.timeout = setTimeout(function () {
+ if ($this.data(self.data_attr())) {
+ if (settings.is_hover) {
+ self.close.call(self, S('#' + $this.data(self.data_attr())));
+ }
+ } else {
+ if (settings.is_hover) {
+ self.close.call(self, $this);
+ }
+ }
+ }.bind(this), settings.hover_timeout);
+ })
+ .on('click.fndtn.dropdown', function (e) {
+ var parent = S(e.target).closest('[' + self.attr_name() + '-content]');
+ var links = parent.find('a');
+
+ if (links.length > 0 && parent.attr('aria-autoclose') !== 'false') {
+ self.close.call(self, S('[' + self.attr_name() + '-content]'));
+ }
+
+ if (e.target !== document && !$.contains(document.documentElement, e.target)) {
+ return;
+ }
+
+ if (S(e.target).closest('[' + self.attr_name() + ']').length > 0) {
+ return;
+ }
+
+ if (!(S(e.target).data('revealId')) &&
+ (parent.length > 0 && (S(e.target).is('[' + self.attr_name() + '-content]') ||
+ $.contains(parent.first()[0], e.target)))) {
+ e.stopPropagation();
+ return;
+ }
+
+ self.close.call(self, S('[' + self.attr_name() + '-content]'));
+ })
+ .on('opened.fndtn.dropdown', '[' + self.attr_name() + '-content]', function () {
+ self.settings.opened.call(this);
+ })
+ .on('closed.fndtn.dropdown', '[' + self.attr_name() + '-content]', function () {
+ self.settings.closed.call(this);
+ });
+
+ S(window)
+ .off('.dropdown')
+ .on('resize.fndtn.dropdown', self.throttle(function () {
+ self.resize.call(self);
+ }, 50));
+
+ this.resize();
+ },
+
+ close : function (dropdown) {
+ var self = this;
+ dropdown.each(function (idx) {
+ var original_target = $('[' + self.attr_name() + '=' + dropdown[idx].id + ']') || $('aria-controls=' + dropdown[idx].id + ']');
+ original_target.attr('aria-expanded', 'false');
+ if (self.S(this).hasClass(self.settings.active_class)) {
+ self.S(this)
+ .css(Foundation.rtl ? 'right' : 'left', '-99999px')
+ .attr('aria-hidden', 'true')
+ .removeClass(self.settings.active_class)
+ .prev('[' + self.attr_name() + ']')
+ .removeClass(self.settings.active_class)
+ .removeData('target');
+
+ self.S(this).trigger('closed.fndtn.dropdown', [dropdown]);
+ }
+ });
+ dropdown.removeClass('f-open-' + this.attr_name(true));
+ },
+
+ closeall : function () {
+ var self = this;
+ $.each(self.S('.f-open-' + this.attr_name(true)), function () {
+ self.close.call(self, self.S(this));
+ });
+ },
+
+ open : function (dropdown, target) {
+ this
+ .css(dropdown
+ .addClass(this.settings.active_class), target);
+ dropdown.prev('[' + this.attr_name() + ']').addClass(this.settings.active_class);
+ dropdown.data('target', target.get(0)).trigger('opened.fndtn.dropdown', [dropdown, target]);
+ dropdown.attr('aria-hidden', 'false');
+ target.attr('aria-expanded', 'true');
+ dropdown.focus();
+ dropdown.addClass('f-open-' + this.attr_name(true));
+ },
+
+ data_attr : function () {
+ if (this.namespace.length > 0) {
+ return this.namespace + '-' + this.name;
+ }
+
+ return this.name;
+ },
+
+ toggle : function (target) {
+ if (target.hasClass(this.settings.disabled_class)) {
+ return;
+ }
+ var dropdown = this.S('#' + target.data(this.data_attr()));
+ if (dropdown.length === 0) {
+ // No dropdown found, not continuing
+ return;
+ }
+
+ this.close.call(this, this.S('[' + this.attr_name() + '-content]').not(dropdown));
+
+ if (dropdown.hasClass(this.settings.active_class)) {
+ this.close.call(this, dropdown);
+ if (dropdown.data('target') !== target.get(0)) {
+ this.open.call(this, dropdown, target);
+ }
+ } else {
+ this.open.call(this, dropdown, target);
+ }
+ },
+
+ resize : function () {
+ var dropdown = this.S('[' + this.attr_name() + '-content].open');
+ var target = $(dropdown.data("target"));
+
+ if (dropdown.length && target.length) {
+ this.css(dropdown, target);
+ }
+ },
+
+ css : function (dropdown, target) {
+ var left_offset = Math.max((target.width() - dropdown.width()) / 2, 8),
+ settings = target.data(this.attr_name(true) + '-init') || this.settings,
+ parentOverflow = dropdown.parent().css('overflow-y') || dropdown.parent().css('overflow');
+
+ this.clear_idx();
+
+
+
+ if (this.small()) {
+ var p = this.dirs.bottom.call(dropdown, target, settings);
+
+ dropdown.attr('style', '').removeClass('drop-left drop-right drop-top').css({
+ position : 'absolute',
+ width : '95%',
+ 'max-width' : 'none',
+ top : p.top
+ });
+
+ dropdown.css(Foundation.rtl ? 'right' : 'left', left_offset);
+ }
+ // detect if dropdown is in an overflow container
+ else if (parentOverflow !== 'visible') {
+ var offset = target[0].offsetTop + target[0].offsetHeight;
+
+ dropdown.attr('style', '').css({
+ position : 'absolute',
+ top : offset
+ });
+
+ dropdown.css(Foundation.rtl ? 'right' : 'left', left_offset);
+ }
+ else {
+
+ this.style(dropdown, target, settings);
+ }
+
+ return dropdown;
+ },
+
+ style : function (dropdown, target, settings) {
+ var css = $.extend({position : 'absolute'},
+ this.dirs[settings.align].call(dropdown, target, settings));
+
+ dropdown.attr('style', '').css(css);
+ },
+
+ // return CSS property object
+ // `this` is the dropdown
+ dirs : {
+ // Calculate target offset
+ _base : function (t, s) {
+ var o_p = this.offsetParent(),
+ o = o_p.offset(),
+ p = t.offset();
+
+ p.top -= o.top;
+ p.left -= o.left;
+
+ //set some flags on the p object to pass along
+ p.missRight = false;
+ p.missTop = false;
+ p.missLeft = false;
+ p.leftRightFlag = false;
+
+ //lets see if the panel will be off the screen
+ //get the actual width of the page and store it
+ var actualBodyWidth;
+ var windowWidth = window.innerWidth;
+
+ if (document.getElementsByClassName('row')[0]) {
+ actualBodyWidth = document.getElementsByClassName('row')[0].clientWidth;
+ } else {
+ actualBodyWidth = windowWidth;
+ }
+
+ var actualMarginWidth = (windowWidth - actualBodyWidth) / 2;
+ var actualBoundary = actualBodyWidth;
+
+ if (!this.hasClass('mega') && !s.ignore_repositioning) {
+ var outerWidth = this.outerWidth();
+ var o_left = t.offset().left;
+
+ //miss top
+ if (t.offset().top <= this.outerHeight()) {
+ p.missTop = true;
+ actualBoundary = windowWidth - actualMarginWidth;
+ p.leftRightFlag = true;
+ }
+
+ //miss right
+ if (o_left + outerWidth > o_left + actualMarginWidth && o_left - actualMarginWidth > outerWidth) {
+ p.missRight = true;
+ p.missLeft = false;
+ }
+
+ //miss left
+ if (o_left - outerWidth <= 0) {
+ p.missLeft = true;
+ p.missRight = false;
+ }
+ }
+
+ return p;
+ },
+
+ top : function (t, s) {
+ var self = Foundation.libs.dropdown,
+ p = self.dirs._base.call(this, t, s);
+
+ this.addClass('drop-top');
+
+ if (p.missTop == true) {
+ p.top = p.top + t.outerHeight() + this.outerHeight();
+ this.removeClass('drop-top');
+ }
+
+ if (p.missRight == true) {
+ p.left = p.left - this.outerWidth() + t.outerWidth();
+ }
+
+ if (t.outerWidth() < this.outerWidth() || self.small() || this.hasClass(s.mega_menu)) {
+ self.adjust_pip(this, t, s, p);
+ }
+
+ if (Foundation.rtl) {
+ return {left : p.left - this.outerWidth() + t.outerWidth(),
+ top : p.top - this.outerHeight()};
+ }
+
+ return {left : p.left, top : p.top - this.outerHeight()};
+ },
+
+ bottom : function (t, s) {
+ var self = Foundation.libs.dropdown,
+ p = self.dirs._base.call(this, t, s);
+
+ if (p.missRight == true) {
+ p.left = p.left - this.outerWidth() + t.outerWidth();
+ }
+
+ if (t.outerWidth() < this.outerWidth() || self.small() || this.hasClass(s.mega_menu)) {
+ self.adjust_pip(this, t, s, p);
+ }
+
+ if (self.rtl) {
+ return {left : p.left - this.outerWidth() + t.outerWidth(), top : p.top + t.outerHeight()};
+ }
+
+ return {left : p.left, top : p.top + t.outerHeight()};
+ },
+
+ left : function (t, s) {
+ var p = Foundation.libs.dropdown.dirs._base.call(this, t, s);
+
+ this.addClass('drop-left');
+
+ if (p.missLeft == true) {
+ p.left = p.left + this.outerWidth();
+ p.top = p.top + t.outerHeight();
+ this.removeClass('drop-left');
+ }
+
+ return {left : p.left - this.outerWidth(), top : p.top};
+ },
+
+ right : function (t, s) {
+ var p = Foundation.libs.dropdown.dirs._base.call(this, t, s);
+
+ this.addClass('drop-right');
+
+ if (p.missRight == true) {
+ p.left = p.left - this.outerWidth();
+ p.top = p.top + t.outerHeight();
+ this.removeClass('drop-right');
+ } else {
+ p.triggeredRight = true;
+ }
+
+ var self = Foundation.libs.dropdown;
+
+ if (t.outerWidth() < this.outerWidth() || self.small() || this.hasClass(s.mega_menu)) {
+ self.adjust_pip(this, t, s, p);
+ }
+
+ return {left : p.left + t.outerWidth(), top : p.top};
+ }
+ },
+
+ // Insert rule to style psuedo elements
+ adjust_pip : function (dropdown, target, settings, position) {
+ var sheet = Foundation.stylesheet,
+ pip_offset_base = 8;
+
+ if (dropdown.hasClass(settings.mega_class)) {
+ pip_offset_base = position.left + (target.outerWidth() / 2) - 8;
+ } else if (this.small()) {
+ pip_offset_base += position.left - 8;
+ }
+
+ this.rule_idx = sheet.cssRules.length;
+
+ //default
+ var sel_before = '.f-dropdown.open:before',
+ sel_after = '.f-dropdown.open:after',
+ css_before = 'left: ' + pip_offset_base + 'px;',
+ css_after = 'left: ' + (pip_offset_base - 1) + 'px;';
+
+ if (position.missRight == true) {
+ pip_offset_base = dropdown.outerWidth() - 23;
+ sel_before = '.f-dropdown.open:before',
+ sel_after = '.f-dropdown.open:after',
+ css_before = 'left: ' + pip_offset_base + 'px;',
+ css_after = 'left: ' + (pip_offset_base - 1) + 'px;';
+ }
+
+ //just a case where right is fired, but its not missing right
+ if (position.triggeredRight == true) {
+ sel_before = '.f-dropdown.open:before',
+ sel_after = '.f-dropdown.open:after',
+ css_before = 'left:-12px;',
+ css_after = 'left:-14px;';
+ }
+
+ if (sheet.insertRule) {
+ sheet.insertRule([sel_before, '{', css_before, '}'].join(' '), this.rule_idx);
+ sheet.insertRule([sel_after, '{', css_after, '}'].join(' '), this.rule_idx + 1);
+ } else {
+ sheet.addRule(sel_before, css_before, this.rule_idx);
+ sheet.addRule(sel_after, css_after, this.rule_idx + 1);
+ }
+ },
+
+ // Remove old dropdown rule index
+ clear_idx : function () {
+ var sheet = Foundation.stylesheet;
+
+ if (typeof this.rule_idx !== 'undefined') {
+ sheet.deleteRule(this.rule_idx);
+ sheet.deleteRule(this.rule_idx);
+ delete this.rule_idx;
+ }
+ },
+
+ small : function () {
+ return matchMedia(Foundation.media_queries.small).matches &&
+ !matchMedia(Foundation.media_queries.medium).matches;
+ },
+
+ off : function () {
+ this.S(this.scope).off('.fndtn.dropdown');
+ this.S('html, body').off('.fndtn.dropdown');
+ this.S(window).off('.fndtn.dropdown');
+ this.S('[data-dropdown-content]').off('.fndtn.dropdown');
+ },
+
+ reflow : function () {}
+ };
+}(jQuery, window, window.document));
+;(function ($, window, document, undefined) {
+ 'use strict';
+
+ Foundation.libs.tab = {
+ name : 'tab',
+
+ version : '5.5.3',
+
+ settings : {
+ active_class : 'active',
+ callback : function () {},
+ deep_linking : false,
+ scroll_to_content : true,
+ is_hover : false
+ },
+
+ default_tab_hashes : [],
+
+ init : function (scope, method, options) {
+ var self = this,
+ S = this.S;
+
+ // Store the default active tabs which will be referenced when the
+ // location hash is absent, as in the case of navigating the tabs and
+ // returning to the first viewing via the browser Back button.
+ S('[' + this.attr_name() + '] > .active > a', this.scope).each(function () {
+ self.default_tab_hashes.push(this.hash);
+ });
+
+ this.bindings(method, options);
+ this.handle_location_hash_change();
+ },
+
+ events : function () {
+ var self = this,
+ S = this.S;
+
+ var usual_tab_behavior = function (e, target) {
+ var settings = S(target).closest('[' + self.attr_name() + ']').data(self.attr_name(true) + '-init');
+ if (!settings.is_hover || Modernizr.touch) {
+ // if user did not pressed tab key, prevent default action
+ var keyCode = e.keyCode || e.which;
+ if (keyCode !== 9) {
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ self.toggle_active_tab(S(target).parent());
+
+ }
+ };
+
+ S(this.scope)
+ .off('.tab')
+ // Key event: focus/tab key
+ .on('keydown.fndtn.tab', '[' + this.attr_name() + '] > * > a', function(e) {
+ var keyCode = e.keyCode || e.which;
+ // if user pressed tab key
+ if (keyCode === 13 || keyCode === 32) { // enter or space
+ var el = this;
+ usual_tab_behavior(e, el);
+ }
+ })
+ // Click event: tab title
+ .on('click.fndtn.tab', '[' + this.attr_name() + '] > * > a', function(e) {
+ var el = this;
+ usual_tab_behavior(e, el);
+ })
+ // Hover event: tab title
+ .on('mouseenter.fndtn.tab', '[' + this.attr_name() + '] > * > a', function (e) {
+ var settings = S(this).closest('[' + self.attr_name() + ']').data(self.attr_name(true) + '-init');
+ if (settings.is_hover) {
+ self.toggle_active_tab(S(this).parent());
+ }
+ });
+
+ // Location hash change event
+ S(window).on('hashchange.fndtn.tab', function (e) {
+ e.preventDefault();
+ self.handle_location_hash_change();
+ });
+ },
+
+ handle_location_hash_change : function () {
+
+ var self = this,
+ S = this.S;
+
+ S('[' + this.attr_name() + ']', this.scope).each(function () {
+ var settings = S(this).data(self.attr_name(true) + '-init');
+ if (settings.deep_linking) {
+ // Match the location hash to a label
+ var hash;
+ if (settings.scroll_to_content) {
+ hash = self.scope.location.hash;
+ } else {
+ // prefix the hash to prevent anchor scrolling
+ hash = self.scope.location.hash.replace('fndtn-', '');
+ }
+ if (hash != '') {
+ // Check whether the location hash references a tab content div or
+ // another element on the page (inside or outside the tab content div)
+ var hash_element = S(hash);
+ if (hash_element.hasClass('content') && hash_element.parent().hasClass('tabs-content')) {
+ // Tab content div
+ self.toggle_active_tab($('[' + self.attr_name() + '] > * > a[href=' + hash + ']').parent());
+ } else {
+ // Not the tab content div. If inside the tab content, find the
+ // containing tab and toggle it as active.
+ var hash_tab_container_id = hash_element.closest('.content').attr('id');
+ if (hash_tab_container_id != undefined) {
+ self.toggle_active_tab($('[' + self.attr_name() + '] > * > a[href=#' + hash_tab_container_id + ']').parent(), hash);
+ }
+ }
+ } else {
+ // Reference the default tab hashes which were initialized in the init function
+ for (var ind = 0; ind < self.default_tab_hashes.length; ind++) {
+ self.toggle_active_tab($('[' + self.attr_name() + '] > * > a[href=' + self.default_tab_hashes[ind] + ']').parent());
+ }
+ }
+ }
+ });
+ },
+
+ toggle_active_tab : function (tab, location_hash) {
+ var self = this,
+ S = self.S,
+ tabs = tab.closest('[' + this.attr_name() + ']'),
+ tab_link = tab.find('a'),
+ anchor = tab.children('a').first(),
+ target_hash = '#' + anchor.attr('href').split('#')[1],
+ target = S(target_hash),
+ siblings = tab.siblings(),
+ settings = tabs.data(this.attr_name(true) + '-init'),
+ interpret_keyup_action = function (e) {
+ // Light modification of Heydon Pickering's Practical ARIA Examples: http://heydonworks.com/practical_aria_examples/js/a11y.js
+
+ // define current, previous and next (possible) tabs
+
+ var $original = $(this);
+ var $prev = $(this).parents('li').prev().children('[role="tab"]');
+ var $next = $(this).parents('li').next().children('[role="tab"]');
+ var $target;
+
+ // find the direction (prev or next)
+
+ switch (e.keyCode) {
+ case 37:
+ $target = $prev;
+ break;
+ case 39:
+ $target = $next;
+ break;
+ default:
+ $target = false
+ break;
+ }
+
+ if ($target.length) {
+ $original.attr({
+ 'tabindex' : '-1',
+ 'aria-selected' : null
+ });
+ $target.attr({
+ 'tabindex' : '0',
+ 'aria-selected' : true
+ }).focus();
+ }
+
+ // Hide panels
+
+ $('[role="tabpanel"]')
+ .attr('aria-hidden', 'true');
+
+ // Show panel which corresponds to target
+
+ $('#' + $(document.activeElement).attr('href').substring(1))
+ .attr('aria-hidden', null);
+
+ },
+ go_to_hash = function(hash) {
+ // This function allows correct behaviour of the browser's back button when deep linking is enabled. Without it
+ // the user would get continually redirected to the default hash.
+ var default_hash = settings.scroll_to_content ? self.default_tab_hashes[0] : 'fndtn-' + self.default_tab_hashes[0].replace('#', '');
+
+ if (hash !== default_hash || window.location.hash) {
+ window.location.hash = hash;
+ }
+ };
+
+ // allow usage of data-tab-content attribute instead of href
+ if (anchor.data('tab-content')) {
+ target_hash = '#' + anchor.data('tab-content').split('#')[1];
+ target = S(target_hash);
+ }
+
+ if (settings.deep_linking) {
+
+ if (settings.scroll_to_content) {
+
+ // retain current hash to scroll to content
+ go_to_hash(location_hash || target_hash);
+
+ if (location_hash == undefined || location_hash == target_hash) {
+ tab.parent()[0].scrollIntoView();
+ } else {
+ S(target_hash)[0].scrollIntoView();
+ }
+ } else {
+ // prefix the hashes so that the browser doesn't scroll down
+ if (location_hash != undefined) {
+ go_to_hash('fndtn-' + location_hash.replace('#', ''));
+ } else {
+ go_to_hash('fndtn-' + target_hash.replace('#', ''));
+ }
+ }
+ }
+
+ // WARNING: The activation and deactivation of the tab content must
+ // occur after the deep linking in order to properly refresh the browser
+ // window (notably in Chrome).
+ // Clean up multiple attr instances to done once
+ tab.addClass(settings.active_class).triggerHandler('opened');
+ tab_link.attr({'aria-selected' : 'true', tabindex : 0});
+ siblings.removeClass(settings.active_class)
+ siblings.find('a').attr({'aria-selected' : 'false'/*, tabindex : -1*/});
+ target.siblings().removeClass(settings.active_class).attr({'aria-hidden' : 'true'/*, tabindex : -1*/});
+ target.addClass(settings.active_class).attr('aria-hidden', 'false').removeAttr('tabindex');
+ settings.callback(tab);
+ target.triggerHandler('toggled', [target]);
+ tabs.triggerHandler('toggled', [tab]);
+
+ tab_link.off('keydown').on('keydown', interpret_keyup_action );
+ },
+
+ data_attr : function (str) {
+ if (this.namespace.length > 0) {
+ return this.namespace + '-' + str;
+ }
+
+ return str;
+ },
+
+ off : function () {},
+
+ reflow : function () {}
+ };
+}(jQuery, window, window.document));
+(function() {
+ $(".relative_time").each(function() {
+ var date, el;
+ el = $(this);
+ date = Date.create(el.attr('data-time'));
+ return el.text(date.relative());
+ });
+
+ $(document).ready(function() {
+ return $("#new_comment").on("ajax:success", function(e, data, status, xhr) {
+ $("#discussion_replies").append(xhr.responseText);
+ $("#discussion_replies .comment_wrapper:last-child .comment_body").css("outline-color", "#FFF3A5");
+ $("#discussion_replies .comment_wrapper:last-child .relative_time").text('Just now');
+ return $("#comment_text").val('');
+ }).on("ajax:error", function(e, xhr, status, error) {
+ return $("#new_comment_error").html("" + xhr.responseText + "
");
+ });
+ });
+
+}).call(this);
+(function() {
+ var Concept, ConceptAdmin;
+
+ Concept = (function() {
+ function Concept() {
+ this.init_audio_links();
+ }
+
+ Concept.prototype.init_audio_links = function() {
+ return $(document).on('click', '.concept_audio', function(e) {
+ var el, id;
+ e.preventDefault();
+ id = $(this).attr('data-id');
+ el = document.getElementById(id);
+ if (el) {
+ return el.play();
+ }
+ });
+ };
+
+ return Concept;
+
+ })();
+
+ ConceptAdmin = (function() {
+ function ConceptAdmin() {
+ this.init_show_link();
+ }
+
+ ConceptAdmin.prototype.init_show_link = function() {
+ return $(document).on('click', '.show_concept_admin', function(e) {
+ var id;
+ e.preventDefault();
+ id = $(this).attr('data-id') + '_admin';
+ return $('#' + id).toggle();
+ });
+ };
+
+ return ConceptAdmin;
+
+ })();
+
+ $(document).ready(function() {
+ new Concept;
+ return new ConceptAdmin;
+ });
+
+}).call(this);
+(function() {
+ this.Filters = (function() {
+ function Filters() {
+ this.type_reg = /#(word|kanji|sentence)s?/g;
+ this.pos_reg = /#(noun|verb|adjective|adverb|particle|counter)\b/g;
+ this.level_reg = /#(common|jlpt-n1|jlpt-n2|jlpt-n3|jlpt-n4|jlpt-n5)\b/g;
+ this.set_filters();
+ this.set_links();
+ }
+
+ Filters.prototype.set_filters = function() {
+ var has_level, has_pos, has_type, level, match, pos, query;
+ $('.filter').removeClass('active');
+ query = $('.keyword').val();
+ has_type = false;
+ while ((match = this.type_reg.exec(query)) !== null) {
+ has_type = true;
+ this.set_dropdown_text(match[1]);
+ switch (match[1]) {
+ case "word":
+ case "words":
+ $('.filter[data-filter=words]').addClass('active');
+ break;
+ case "sentence":
+ case "sentences":
+ $('.filter[data-filter=sentences]').addClass('active');
+ break;
+ case "kanji":
+ $('.filter[data-filter=kanji]').addClass('active');
+ }
+ }
+ has_pos = false;
+ while ((match = this.pos_reg.exec(query)) !== null) {
+ has_pos = true;
+ pos = match[1];
+ $(".filter[data-filter=" + pos + "]").addClass('active');
+ }
+ has_level = false;
+ while ((match = this.level_reg.exec(query)) !== null) {
+ has_level = true;
+ level = match[1];
+ $(".filter[data-filter=" + level + "]").addClass('active');
+ }
+ if (!has_type) {
+ $('.filter[data-filter=type-all]').addClass('active');
+ }
+ if (!has_pos) {
+ $('.filter[data-filter=pos-all]').addClass('active');
+ }
+ if (!has_level) {
+ return $('.filter[data-filter=level-all]').addClass('active');
+ }
+ };
+
+ Filters.prototype.set_dropdown_text = function(type) {
+ switch (type) {
+ case "word":
+ case "words":
+ return $('#search_dropdown_text').text('Words');
+ case "sentence":
+ case "sentences":
+ return $('#search_dropdown_text').text('Sentences');
+ case "kanji":
+ return $('#search_dropdown_text').text('Kanji');
+ case "name":
+ case "names":
+ return $('#search_dropdown_text').text('Names');
+ default:
+ return $('#search_dropdown_text').text('All');
+ }
+ };
+
+ Filters.prototype.set_links = function() {
+ return $('.filter').bind('click', (function(_this) {
+ return function(e) {
+ var el, group, query, tag, this_is_active;
+ el = $(e.target);
+ e.preventDefault();
+ query = $('.keyword').val();
+ tag = el.attr('data-filter');
+ group = el.parents('ul').attr('data-filter-group');
+ this_is_active = el.hasClass('active');
+ $("ul[data-filter-group=" + group + "] a").removeClass('active');
+ if (group === 'type') {
+ query = query.replace(/#(word|kanji|sentence|name)s?/, '');
+ if (tag === 'type-all') {
+ el.addClass('active');
+ } else {
+ if (!this_is_active || !/\#/.test(query)) {
+ el.addClass('active');
+ _this.set_dropdown_text(tag);
+ switch (tag) {
+ case 'word':
+ case 'words':
+ query = query.add(" #words");
+ break;
+ case 'sentence':
+ case 'sentences':
+ query = query.add(" #sentences");
+ break;
+ case 'kanji':
+ query = query.add(" #kanji");
+ break;
+ case 'name':
+ case 'names':
+ query = query.add(" #names");
+ }
+ }
+ }
+ }
+ if (group === 'pos') {
+ query = query.replace(/#(noun|verb|adjective|adverb|particle|counter)/, '');
+ if (tag === 'pos-all') {
+ el.addClass('active');
+ } else {
+ if (!this_is_active) {
+ el.addClass('active');
+ query = query.add(" #" + tag);
+ }
+ }
+ }
+ if (group === 'level') {
+ query = query.replace(/#(common|jlpt-n1|jlpt-n2|jlpt-n3|jlpt-n4|jlpt-n5)/, '');
+ if (tag === 'level-all') {
+ el.addClass('active');
+ } else {
+ if (!this_is_active) {
+ el.addClass('active');
+ query = query.add(" #" + tag);
+ }
+ }
+ }
+ if ($("ul[data-filter-group=" + group + "] .active").length === 0) {
+ $(".filter[data-filter=" + group + "-all]").addClass('active');
+ }
+ query = query.replace(/\s{2,}/g, ' ');
+ $('#keyword').val(query);
+ if (query.replace(/#\w+/, '').trim()) {
+ return $('#search').submit();
+ } else {
+ return $('#keyword').caret({
+ start: 0,
+ end: 0
+ }).focus();
+ }
+ };
+ })(this));
+ };
+
+ return Filters;
+
+ })();
+
+}).call(this);
+(function() {
+ var Adjective, Form, Godan, IAdjective, Ichidan, InflectionPattern, InflectionTable, Kuru, SuruDerivative, SuruOnly, Verb,
+ __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
+
+ Form = (function() {
+ function Form(inflected, furigana) {
+ this.inflected = inflected;
+ this.furigana = furigana;
+ }
+
+ return Form;
+
+ })();
+
+ Verb = (function() {
+ function Verb() {}
+
+ return Verb;
+
+ })();
+
+ Adjective = (function() {
+ function Adjective() {}
+
+ return Adjective;
+
+ })();
+
+ IAdjective = (function() {
+ function IAdjective(base) {
+ this.base = base;
+ this.part_of_speech = new Adjective;
+ }
+
+ IAdjective.prototype.jisho = function() {
+ return new Form(this.base);
+ };
+
+ IAdjective.prototype.katta = function() {
+ return new Form(this.stem() + 'かった');
+ };
+
+ IAdjective.prototype.kunai = function() {
+ return new Form(this.stem() + 'くない');
+ };
+
+ IAdjective.prototype.kunakatta = function() {
+ return new Form(this.stem() + 'くなかった');
+ };
+
+ IAdjective.prototype.stem = function() {
+ return this.base.replace(/い$/, '');
+ };
+
+ return IAdjective;
+
+ })();
+
+ Kuru = (function() {
+ function Kuru(base) {
+ this.base = base;
+ this.part_of_speech = new Verb;
+ }
+
+ Kuru.prototype.jisho = function() {
+ return new Form(this.base, 'く');
+ };
+
+ Kuru.prototype.nai = function() {
+ return new Form(this.mizenkei() + 'ない', 'こ');
+ };
+
+ Kuru.prototype.masu = function() {
+ return new Form(this.renyokei() + 'ます', 'き');
+ };
+
+ Kuru.prototype.masen = function() {
+ return new Form(this.renyokei() + 'ません', 'き');
+ };
+
+ Kuru.prototype.ta = function() {
+ return new Form(this.takei() + 'た', 'き');
+ };
+
+ Kuru.prototype.nakatta = function() {
+ return new Form(this.mizenkei() + 'なかった', 'こ');
+ };
+
+ Kuru.prototype.mashita = function() {
+ return new Form(this.renyokei() + 'ました', 'き');
+ };
+
+ Kuru.prototype.masendeshita = function() {
+ return new Form(this.renyokei() + 'ませんでした', 'き');
+ };
+
+ Kuru.prototype.te = function() {
+ return new Form(this.takei() + 'て', 'き');
+ };
+
+ Kuru.prototype.nakute = function() {
+ return new Form(this.mizenkei() + 'なくて', 'こ');
+ };
+
+ Kuru.prototype.potential = function() {
+ return new Form(this.mizenkei() + 'られる', 'こ');
+ };
+
+ Kuru.prototype.potential_negative = function() {
+ return new Form(this.mizenkei() + 'られない', 'こ');
+ };
+
+ Kuru.prototype.passive = function() {
+ return new Form(this.mizenkei() + 'られる', 'こ');
+ };
+
+ Kuru.prototype.passive_negative = function() {
+ return new Form(this.mizenkei() + 'られない', 'こ');
+ };
+
+ Kuru.prototype.causative = function() {
+ return new Form(this.mizenkei() + 'させる', 'こ');
+ };
+
+ Kuru.prototype.causative_negative = function() {
+ return new Form(this.mizenkei() + 'させない', 'こ');
+ };
+
+ Kuru.prototype.causative_passive = function() {
+ return new Form(this.mizenkei() + 'させられる', 'こ');
+ };
+
+ Kuru.prototype.causative_passive_negative = function() {
+ return new Form(this.mizenkei() + 'させられない', 'こ');
+ };
+
+ Kuru.prototype.imperative = function() {
+ return new Form(this.meireikei() + 'い', 'こ');
+ };
+
+ Kuru.prototype.imperative_negative = function() {
+ return new Form(this.base + 'な', 'く');
+ };
+
+ Kuru.prototype.renyokei = function() {
+ return this.base.replace(/る$/, '');
+ };
+
+ Kuru.prototype.mizenkei = function() {
+ return this.base.replace(/る$/, '');
+ };
+
+ Kuru.prototype.meireikei = function() {
+ return this.base.replace(/る$/, '');
+ };
+
+ Kuru.prototype.takei = function() {
+ return this.base.replace(/る$/, '');
+ };
+
+ return Kuru;
+
+ })();
+
+ SuruOnly = (function() {
+ function SuruOnly(base) {
+ this.base = base;
+ this.part_of_speech = new Verb;
+ }
+
+ SuruOnly.prototype.jisho = function() {
+ return new Form(this.base + 'る', 'す');
+ };
+
+ SuruOnly.prototype.nai = function() {
+ return new Form(this.base + 'ない', 'し');
+ };
+
+ SuruOnly.prototype.masu = function() {
+ return new Form(this.base + 'ます', 'し');
+ };
+
+ SuruOnly.prototype.masen = function() {
+ return new Form(this.base + 'ません', 'し');
+ };
+
+ SuruOnly.prototype.ta = function() {
+ return new Form(this.base + 'た', 'し');
+ };
+
+ SuruOnly.prototype.nakatta = function() {
+ return new Form(this.base + 'なかった', 'し');
+ };
+
+ SuruOnly.prototype.mashita = function() {
+ return new Form(this.base + 'ました', 'し');
+ };
+
+ SuruOnly.prototype.masendeshita = function() {
+ return new Form(this.base + 'ませんでした', 'し');
+ };
+
+ SuruOnly.prototype.te = function() {
+ return new Form(this.base + 'て', 'し');
+ };
+
+ SuruOnly.prototype.nakute = function() {
+ return new Form(this.base + 'なくて', 'し');
+ };
+
+ SuruOnly.prototype.potential = function() {
+ return new Form('できる');
+ };
+
+ SuruOnly.prototype.potential_negative = function() {
+ return new Form('できない');
+ };
+
+ SuruOnly.prototype.passive = function() {
+ return new Form(this.base + 'れる', 'さ');
+ };
+
+ SuruOnly.prototype.passive_negative = function() {
+ return new Form(this.base + 'れない', 'さ');
+ };
+
+ SuruOnly.prototype.causative = function() {
+ return new Form(this.base + 'せる', 'さ');
+ };
+
+ SuruOnly.prototype.causative_negative = function() {
+ return new Form(this.base + 'せない', 'さ');
+ };
+
+ SuruOnly.prototype.causative_passive = function() {
+ return new Form(this.base + 'せられる', 'さ');
+ };
+
+ SuruOnly.prototype.causative_passive_negative = function() {
+ return new Form(this.base + 'せられない', 'さ');
+ };
+
+ SuruOnly.prototype.imperative = function() {
+ return new Form(this.base + 'ろ', 'し');
+ };
+
+ SuruOnly.prototype.imperative_negative = function() {
+ return new Form(this.base + 'るな', 'す');
+ };
+
+ SuruOnly.prototype.renyokei = function() {
+ return 'し';
+ };
+
+ SuruOnly.prototype.mizenkei = function() {
+ return 'し';
+ };
+
+ SuruOnly.prototype.meireikei = function() {
+ return 'し';
+ };
+
+ SuruOnly.prototype.takei = function() {
+ return 'し';
+ };
+
+ return SuruOnly;
+
+ })();
+
+ SuruDerivative = (function() {
+ function SuruDerivative(base, prefix) {
+ this.base = base;
+ this.prefix = prefix;
+ this.part_of_speech = new Verb;
+ }
+
+ SuruDerivative.prototype.jisho = function() {
+ return new Form(this.base + 'する');
+ };
+
+ SuruDerivative.prototype.nai = function() {
+ return new Form(this.base + 'しない');
+ };
+
+ SuruDerivative.prototype.masu = function() {
+ return new Form(this.base + 'します');
+ };
+
+ SuruDerivative.prototype.masen = function() {
+ return new Form(this.base + 'しません');
+ };
+
+ SuruDerivative.prototype.ta = function() {
+ return new Form(this.base + 'した');
+ };
+
+ SuruDerivative.prototype.nakatta = function() {
+ return new Form(this.base + 'しなかった');
+ };
+
+ SuruDerivative.prototype.mashita = function() {
+ return new Form(this.base + 'しました');
+ };
+
+ SuruDerivative.prototype.masendeshita = function() {
+ return new Form(this.base + 'しませんでした');
+ };
+
+ SuruDerivative.prototype.te = function() {
+ return new Form(this.base + 'して');
+ };
+
+ SuruDerivative.prototype.nakute = function() {
+ return new Form(this.base + 'しなくて');
+ };
+
+ SuruDerivative.prototype.potential = function() {
+ return new Form(this.prefix + 'できる');
+ };
+
+ SuruDerivative.prototype.potential_negative = function() {
+ return new Form(this.prefix + 'できない');
+ };
+
+ SuruDerivative.prototype.passive = function() {
+ return new Form(this.base + 'される');
+ };
+
+ SuruDerivative.prototype.passive_negative = function() {
+ return new Form(this.base + 'されない');
+ };
+
+ SuruDerivative.prototype.causative = function() {
+ return new Form(this.base + 'させる');
+ };
+
+ SuruDerivative.prototype.causative_negative = function() {
+ return new Form(this.base + 'させない');
+ };
+
+ SuruDerivative.prototype.causative_passive = function() {
+ return new Form(this.base + 'させられる');
+ };
+
+ SuruDerivative.prototype.causative_passive_negative = function() {
+ return new Form(this.base + 'させられない');
+ };
+
+ SuruDerivative.prototype.imperative = function() {
+ return new Form(this.base + 'しろ');
+ };
+
+ SuruDerivative.prototype.imperative_negative = function() {
+ return new Form(this.base + 'するな');
+ };
+
+ SuruDerivative.prototype.renyokei = function() {
+ return 'し';
+ };
+
+ SuruDerivative.prototype.mizenkei = function() {
+ return 'し';
+ };
+
+ SuruDerivative.prototype.meireikei = function() {
+ return 'し';
+ };
+
+ SuruDerivative.prototype.takei = function() {
+ return 'し';
+ };
+
+ return SuruDerivative;
+
+ })();
+
+ Ichidan = (function() {
+ function Ichidan(base) {
+ this.base = base;
+ this.part_of_speech = new Verb;
+ }
+
+ Ichidan.prototype.jisho = function() {
+ return new Form(this.base);
+ };
+
+ Ichidan.prototype.nai = function() {
+ return new Form(this.mizenkei() + 'ない');
+ };
+
+ Ichidan.prototype.masu = function() {
+ return new Form(this.renyokei() + 'ます');
+ };
+
+ Ichidan.prototype.masen = function() {
+ return new Form(this.renyokei() + 'ません');
+ };
+
+ Ichidan.prototype.ta = function() {
+ return new Form(this.takei() + 'た');
+ };
+
+ Ichidan.prototype.nakatta = function() {
+ return new Form(this.mizenkei() + 'なかった');
+ };
+
+ Ichidan.prototype.mashita = function() {
+ return new Form(this.renyokei() + 'ました');
+ };
+
+ Ichidan.prototype.masendeshita = function() {
+ return new Form(this.renyokei() + 'ませんでした');
+ };
+
+ Ichidan.prototype.te = function() {
+ return new Form(this.takei() + 'て');
+ };
+
+ Ichidan.prototype.nakute = function() {
+ return new Form(this.mizenkei() + 'なくて');
+ };
+
+ Ichidan.prototype.potential = function() {
+ return new Form(this.mizenkei() + 'られる');
+ };
+
+ Ichidan.prototype.potential_negative = function() {
+ return new Form(this.mizenkei() + 'られない');
+ };
+
+ Ichidan.prototype.passive = function() {
+ return new Form(this.mizenkei() + 'られる');
+ };
+
+ Ichidan.prototype.passive_negative = function() {
+ return new Form(this.mizenkei() + 'られない');
+ };
+
+ Ichidan.prototype.causative = function() {
+ return new Form(this.mizenkei() + 'させる');
+ };
+
+ Ichidan.prototype.causative_negative = function() {
+ return new Form(this.mizenkei() + 'させない');
+ };
+
+ Ichidan.prototype.causative_passive = function() {
+ return new Form(this.mizenkei() + 'させられる');
+ };
+
+ Ichidan.prototype.causative_passive_negative = function() {
+ return new Form(this.mizenkei() + 'させられない');
+ };
+
+ Ichidan.prototype.imperative = function() {
+ return new Form(this.meireikei() + 'ろ');
+ };
+
+ Ichidan.prototype.imperative_negative = function() {
+ return new Form(this.base + 'な');
+ };
+
+ Ichidan.prototype.renyokei = function() {
+ return this.base.replace(/る$/, '');
+ };
+
+ Ichidan.prototype.mizenkei = function() {
+ return this.base.replace(/る$/, '');
+ };
+
+ Ichidan.prototype.meireikei = function() {
+ return this.base.replace(/る$/, '');
+ };
+
+ Ichidan.prototype.takei = function() {
+ return this.base.replace(/る$/, '');
+ };
+
+ return Ichidan;
+
+ })();
+
+ Godan = (function() {
+ function Godan(base, type) {
+ this.base = base;
+ this.type = type;
+ this.part_of_speech = new Verb;
+ this.v5_patterns = {
+ u: ['い', 'わ', 'え', 'っ'],
+ k: ['き', 'か', 'け', 'い'],
+ 'k-s': ['き', 'か', 'け', 'っ'],
+ g: ['ぎ', 'が', 'げ', 'い'],
+ m: ['み', 'ま', 'め', 'ん'],
+ n: ['に', 'な', 'ね', 'ん'],
+ r: ['り', 'ら', 'れ', 'っ'],
+ b: ['び', 'ば', 'べ', 'ん'],
+ s: ['し', 'さ', 'せ', 'し'],
+ t: ['ち', 'た', 'て', 'っ'],
+ z: ['じ', 'ざ', 'ぜ', 'い']
+ };
+ }
+
+ Godan.prototype.jisho = function() {
+ return new Form(this.base);
+ };
+
+ Godan.prototype.nai = function() {
+ return new Form(this.mizenkei() + 'ない');
+ };
+
+ Godan.prototype.masu = function() {
+ return new Form(this.renyokei() + 'ます');
+ };
+
+ Godan.prototype.masen = function() {
+ return new Form(this.renyokei() + 'ません');
+ };
+
+ Godan.prototype.ta = function() {
+ var _ref;
+ if ((_ref = this.type) === 'g' || _ref === 'm' || _ref === 'n' || _ref === 'b') {
+ return new Form(this.takei() + 'だ');
+ } else {
+ return new Form(this.takei() + 'た');
+ }
+ };
+
+ Godan.prototype.nakatta = function() {
+ return new Form(this.mizenkei() + 'なかった');
+ };
+
+ Godan.prototype.mashita = function() {
+ return new Form(this.renyokei() + 'ました');
+ };
+
+ Godan.prototype.masendeshita = function() {
+ return new Form(this.renyokei() + 'ませんでした');
+ };
+
+ Godan.prototype.te = function() {
+ var _ref;
+ if ((_ref = this.type) === 'g' || _ref === 'm' || _ref === 'n' || _ref === 'b') {
+ return new Form(this.takei() + 'で');
+ } else {
+ return new Form(this.takei() + 'て');
+ }
+ };
+
+ Godan.prototype.nakute = function() {
+ return new Form(this.mizenkei() + 'なくて');
+ };
+
+ Godan.prototype.potential = function() {
+ return new Form(this.meireikei() + 'る');
+ };
+
+ Godan.prototype.potential_negative = function() {
+ return new Form(this.meireikei() + 'ない');
+ };
+
+ Godan.prototype.passive = function() {
+ return new Form(this.mizenkei() + 'れる');
+ };
+
+ Godan.prototype.passive_negative = function() {
+ return new Form(this.mizenkei() + 'れない');
+ };
+
+ Godan.prototype.causative = function() {
+ return new Form(this.mizenkei() + 'せる');
+ };
+
+ Godan.prototype.causative_negative = function() {
+ return new Form(this.mizenkei() + 'せない');
+ };
+
+ Godan.prototype.causative_passive = function() {
+ return new Form(this.mizenkei() + 'せられる');
+ };
+
+ Godan.prototype.causative_passive_negative = function() {
+ return new Form(this.mizenkei() + 'せられない');
+ };
+
+ Godan.prototype.imperative = function() {
+ return new Form(this.meireikei());
+ };
+
+ Godan.prototype.imperative_negative = function() {
+ return new Form(this.base + 'な');
+ };
+
+ Godan.prototype.renyokei = function() {
+ return this.base.replace(/.$/, this.v5_patterns[this.type][0]);
+ };
+
+ Godan.prototype.mizenkei = function() {
+ return this.base.replace(/.$/, this.v5_patterns[this.type][1]);
+ };
+
+ Godan.prototype.meireikei = function() {
+ return this.base.replace(/.$/, this.v5_patterns[this.type][2]);
+ };
+
+ Godan.prototype.takei = function() {
+ return this.base.replace(/.$/, this.v5_patterns[this.type][3]);
+ };
+
+ return Godan;
+
+ })();
+
+ InflectionPattern = (function() {
+ function InflectionPattern() {}
+
+ InflectionPattern.determine = function(base, pos) {
+ var klass, pattern;
+ if ('v1' === pos) {
+ pattern = new Ichidan(base);
+ } else if (klass = pos.match(/^v5(.*)$/)) {
+ pattern = new Godan(base, klass[1]);
+ } else if ('vk' === pos) {
+ pattern = new Kuru(base);
+ } else if ('vs-i' === pos && ('する' === base || '為る' === base)) {
+ pattern = new SuruOnly(base.replace(/る$/, ''));
+ } else if ('vs-i' === pos) {
+ pattern = new SuruDerivative(base.replace(/する$/, ''), base.replace(/する$/, ''), '');
+ } else if ('adj-i' === pos) {
+ pattern = new IAdjective(base);
+ }
+ return pattern;
+ };
+
+ return InflectionPattern;
+
+ })();
+
+ InflectionTable = (function() {
+ function InflectionTable() {
+ this.show_inflection_table = __bind(this.show_inflection_table, this);
+ $(document).on('click', '.show_inflection_table', (function(_this) {
+ return function(e) {
+ e.preventDefault();
+ return _this.show_inflection_table(e.target);
+ };
+ })(this));
+ }
+
+ InflectionTable.prototype.show_inflection_table = function(target) {
+ var caption, modal, pattern, pos, word;
+ word = $(target).attr('data-word');
+ pos = $(target).attr('data-pos');
+ caption = $(target).attr('data-caption');
+ pattern = InflectionPattern.determine(word, pos);
+ modal = $('#inflection_modal');
+ modal.find('.modal_content').empty().append((function(_this) {
+ return function() {
+ var html;
+ html = " " + caption + " Affirmative Negative ";
+ if (pattern.part_of_speech instanceof Verb) {
+ html = html + _this.row("Non-past", pattern.jisho(), pattern.nai()) + _this.row("Non-past, polite", pattern.masu(), pattern.masen()) + _this.row("Past", pattern.ta(), pattern.nakatta()) + _this.row("Past, polite", pattern.mashita(), pattern.masendeshita()) + _this.row("Te-form", pattern.te(), pattern.nakute()) + _this.row("Potential", pattern.potential(), pattern.potential_negative()) + _this.row("Passive", pattern.passive(), pattern.passive_negative()) + _this.row("Causative", pattern.causative(), pattern.causative_negative()) + _this.row("Causative Passive", pattern.causative_passive(), pattern.causative_passive_negative()) + _this.row("Imperative", pattern.imperative(), pattern.imperative_negative());
+ } else if (pattern.part_of_speech instanceof Adjective) {
+ html = html + _this.row("Non-past", pattern.jisho(), pattern.kunai()) + _this.row("Past", pattern.katta(), pattern.kunakatta());
+ }
+ html = html + '
';
+ return html;
+ };
+ })(this));
+ return modal.foundation('reveal', 'open');
+ };
+
+ InflectionTable.prototype.row = function(description, positive, negative) {
+ var html, negative_furigana, positive_furigana;
+ positive_furigana = positive.furigana ? "" + positive.furigana + " " : "";
+ negative_furigana = negative.furigana ? "" + negative.furigana + " " : "";
+ html = " " + description + " " + positive_furigana + "" + positive.inflected + " " + negative_furigana + "" + negative.inflected + " ";
+ return html;
+ };
+
+ return InflectionTable;
+
+ })();
+
+ $(document).ready(function() {
+ return new InflectionTable;
+ });
+
+}).call(this);
+(function() {
+ var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
+
+ this.SearchArea = (function() {
+ var MOBILE_WIDTH, SCROLL_MARGIN;
+
+ SCROLL_MARGIN = 15;
+
+ MOBILE_WIDTH = 640;
+
+ function SearchArea(name) {
+ this.isMobile = __bind(this.isMobile, this);
+ this.body = $(document.body);
+ this.main = $('#search_main');
+ this.area = $("#" + name + "_area");
+ this.button = $("#" + name + "_button");
+ this.name = name;
+ this.button.click((function(_this) {
+ return function() {
+ if (_this.active) {
+ return _this.deactivate();
+ } else {
+ return _this.activate();
+ }
+ };
+ })(this));
+ }
+
+ SearchArea.prototype.activate = function(silent) {
+ if (!silent) {
+ this.area.trigger('area_will_activate', [this]);
+ }
+ this.active = true;
+ this.area.show();
+ window.location.hash = this.name;
+ this.scrollToSearch();
+ this.body.addClass("search_area_active " + this.name + "_area_active");
+ if (!silent) {
+ return this.area.trigger('area_activated', [this]);
+ }
+ };
+
+ SearchArea.prototype.deactivate = function(silent) {
+ if (this.active && window.location.hash === ("#" + this.name) && history.pushState) {
+ history.pushState("", document.title, window.location.pathname + window.location.search);
+ }
+ this.active = false;
+ this.area.hide();
+ this.body.removeClass("search_area_active " + this.name + "_area_active");
+ if (!silent) {
+ return this.area.trigger('area_deactivated', [this]);
+ }
+ };
+
+ SearchArea.prototype.isMobile = function() {
+ return window.innerWidth < MOBILE_WIDTH;
+ };
+
+ SearchArea.prototype.scrollToSearch = function() {
+ var top;
+ if (this.isMobile()) {
+ top = this.main[0].getBoundingClientRect().top;
+ return window.scrollTo(0, top + window.scrollY - SCROLL_MARGIN);
+ }
+ };
+
+ return SearchArea;
+
+ })();
+
+}).call(this);
+(function() {
+ var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
+ __hasProp = {}.hasOwnProperty,
+ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
+
+ this.Input = (function(_super) {
+ __extends(Input, _super);
+
+ function Input(prefix) {
+ this.handleResultsWrap = __bind(this.handleResultsWrap, this);
+ this.setResultsOverflowType = __bind(this.setResultsOverflowType, this);
+ this.handleResize = __bind(this.handleResize, this);
+ Input.__super__.constructor.call(this, prefix);
+ this.showMore = this.area.find('.show_more');
+ this.showLess = this.area.find('.show_less');
+ this.showMore.click((function(_this) {
+ return function() {
+ _this.toggleMoreResults(true);
+ return _this.area.trigger('show_more');
+ };
+ })(this));
+ this.showLess.click((function(_this) {
+ return function() {
+ _this.toggleMoreResults(false);
+ _this.area.trigger('show_less');
+ return _this.list.scrollTop(0);
+ };
+ })(this));
+ $(window).smartResize(this.handleResize.debounce(250));
+ this.setResultsOverflowType();
+ }
+
+ Input.prototype.toggleMoreResults = function(show) {
+ this.list.toggleClass('viewing_all', show);
+ this.showLess.toggle(show);
+ return this.showMore.toggle(!show);
+ };
+
+ Input.prototype.appendResult = function(character, extra_class) {
+ if (extra_class == null) {
+ extra_class = '';
+ }
+ return this.list.append("");
+ };
+
+ Input.prototype.appendLabel = function(label) {
+ return this.list.append("" + label + " ");
+ };
+
+ Input.prototype.handleResize = function() {
+ if (this.list) {
+ this.setResultsOverflowType();
+ return this.handleResultsWrap();
+ }
+ };
+
+ Input.prototype.emptyResults = function() {
+ this.list.empty().append(this.instructions);
+ this.handleResultsWrap();
+ return this.list.removeClass('showing_results');
+ };
+
+ Input.prototype.setResultsOverflowType = function() {
+ return this.resultsHorizontalScroll = this.isMobile();
+ };
+
+ Input.prototype.handleResultsWrap = function() {
+ var overflow;
+ this.list.css('width', 'auto');
+ this.list.removeClass('overflowing');
+ if (this.resultsHorizontalScroll) {
+ this.list.css('white-space', 'nowrap');
+ if (this.list[0].scrollWidth > this.list[0].clientWidth) {
+ this.list.width(Math.ceil(this.list[0].scrollWidth / 2) + 50);
+ }
+ this.list.css('white-space', 'normal');
+ } else {
+ overflow = this.list[0].scrollHeight > this.list[0].clientHeight;
+ this.showMore.toggle(overflow);
+ this.showLess.toggle(!overflow && this.list.hasClass('viewing_all'));
+ this.list.toggleClass('overflowing', overflow);
+ }
+ this.list.toggleClass('showing_results', this.list.find('.result').length > 0);
+ return this.area.trigger('results_changed');
+ };
+
+ return Input;
+
+ })(SearchArea);
+
+}).call(this);
+(function() {
+ var __hasProp = {}.hasOwnProperty,
+ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
+
+ this.HandwritingInput = (function(_super) {
+ __extends(HandwritingInput, _super);
+
+ function HandwritingInput() {
+ var panel, _i, _len, _ref;
+ HandwritingInput.__super__.constructor.call(this, 'handwriting');
+ this.url = this.area.data('url');
+ this.list = this.area.find('.list');
+ this.inputs = this.area.find('.inputs');
+ this.panels = this.area.find('.panel');
+ _ref = this.panels;
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ panel = _ref[_i];
+ this.setupPanel(panel);
+ }
+ this.setupEvents(this.area);
+ this.instructions = this.list.find('.instructions');
+ this.defaultPanelWidth = this.panels.first().width();
+ this.calculateDimensions();
+ this.deactivate();
+ }
+
+ HandwritingInput.prototype.setupEvents = function() {
+ return this.list.on('click', '.result', (function(_this) {
+ return function(evt) {
+ if (!Event.withMeta(evt)) {
+ evt.preventDefault();
+ _this.setCandidate($(evt.target).text());
+ return _this.confirmCandidate();
+ }
+ };
+ })(this));
+ };
+
+ HandwritingInput.prototype.setupPanel = function(el) {
+ var $el, backButton, canvas, panel, resetButton;
+ $el = $(el);
+ backButton = $el.find('.back').attr('disabled', 'disabled');
+ resetButton = $el.find('.reset').attr('disabled', 'disabled');
+ canvas = $el.find('canvas');
+ panel = new KanjiHandwriting(canvas[0]);
+ panel.onStrokeStart = (function(_this) {
+ return function() {
+ if (_this.currentPanel && _this.currentPanel !== panel) {
+ _this.confirmCandidate();
+ _this.currentPanel.reset();
+ }
+ _this.currentPanel = panel;
+ $el.addClass('drawing');
+ resetButton.removeAttr('disabled');
+ if (panel.strokes.length > 1) {
+ return backButton.removeAttr('disabled');
+ }
+ };
+ })(this);
+ panel.onStrokeEnd = (function(_this) {
+ return function() {
+ return _this.query(panel);
+ };
+ })(this);
+ panel.onReset = function() {
+ $el.removeClass('drawing');
+ resetButton.attr('disabled', 'disabled');
+ return backButton.attr('disabled', 'disabled');
+ };
+ backButton.click((function(_this) {
+ return function() {
+ if (panel.strokes.length > 1) {
+ panel.back();
+ _this.query(panel);
+ if (panel.strokes.length === 1) {
+ return backButton.attr('disabled', 'disabled');
+ }
+ }
+ };
+ })(this));
+ resetButton.click((function(_this) {
+ return function() {
+ panel.reset();
+ return _this.removeCandidate();
+ };
+ })(this));
+ return $el.resize((function(_this) {
+ return function(event, targetWidth) {
+ event.stopPropagation();
+ return panel.resizeToWidth(targetWidth);
+ };
+ })(this));
+ };
+
+ HandwritingInput.prototype.query = function(panel) {
+ return $.ajax({
+ url: this.url,
+ type: 'post',
+ data: {
+ sexp: panel.toSexp()
+ },
+ success: (function(_this) {
+ return function(results) {
+ return _this.setResult(results);
+ };
+ })(this)
+ });
+ };
+
+ HandwritingInput.prototype.setResult = function(results) {
+ this.list.empty();
+ results.each((function(_this) {
+ return function(kanji, i) {
+ return _this.appendResult(kanji);
+ };
+ })(this));
+ this.handleResultsWrap();
+ return this.setCandidate(results.charAt(0));
+ };
+
+ HandwritingInput.prototype.calculateDimensions = function() {
+ var targetWidth;
+ targetWidth = Math.min(this.area.outerWidth(true), this.defaultPanelWidth);
+ if (targetWidth !== this.currentPanelWidth) {
+ this.panels.trigger('resize', targetWidth);
+ this.inputs.css('max-height', this.panels.first().height());
+ return this.currentPanelWidth = targetWidth;
+ }
+ };
+
+ HandwritingInput.prototype.setCandidate = function(candidate) {
+ this.list.trigger('candidate_chosen', candidate);
+ return this.candidate = candidate;
+ };
+
+ HandwritingInput.prototype.confirmCandidate = function() {
+ if (this.candidate) {
+ this.list.trigger('candidate_confirmed');
+ }
+ return this.clearCandidate();
+ };
+
+ HandwritingInput.prototype.clearCandidate = function() {
+ if (this.currentPanel) {
+ this.currentPanel.reset();
+ }
+ return this.candidate = null;
+ };
+
+ HandwritingInput.prototype.setCharacter = function(character) {
+ return this.list.trigger('character_chosen', character);
+ };
+
+ HandwritingInput.prototype.removeCandidate = function() {
+ return this.list.trigger('candidate_removed');
+ };
+
+ return HandwritingInput;
+
+ })(Input);
+
+}).call(this);
+/*
+ * Redrawing elements that need to force a paint.
+ */
+
+(function($) {
+
+ $.fn.redraw = function() {
+
+ this.each(function() {
+ this.style.display = 'none';
+ this.offsetHeight;
+ this.style.display = 'block';
+ });
+
+ }
+
+})(jQuery);
+(function() {
+ var __hasProp = {}.hasOwnProperty,
+ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
+ __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
+
+ this.RadicalInput = (function(_super) {
+ var HEIGHT_MARGIN;
+
+ __extends(RadicalInput, _super);
+
+ HEIGHT_MARGIN = 15;
+
+ function RadicalInput() {
+ RadicalInput.__super__.constructor.call(this, 'radical');
+ this.MAX_KANJI_RESULTS = 36;
+ this.list = this.area.find('.list');
+ this.table = this.area.find('.radical_table');
+ this.radicals = this.area.find('.radical');
+ this.resetRadicalsButton = this.area.find('.reset_radicals');
+ this.allRadicals = this.area.find('.radical');
+ this.inputMethods = $('#input_methods');
+ this.instructions = this.list.find('.instructions');
+ this.toggleCombinedMode(true);
+ this.setupEvents();
+ this.reset();
+ this.radicals.addClass('available');
+ this.deactivate();
+ }
+
+ RadicalInput.prototype.setupTouch = function() {
+ if (Modernizr.touch) {
+ return this.table.touchZoomGrid({
+ selector: '.radical'
+ });
+ }
+ };
+
+ RadicalInput.prototype.setupEvents = function() {
+ this.table.on('click', '.radical', (function(_this) {
+ return function(evt) {
+ var combine, radical, radicalAlreadySelected, radicalCannotBeCombined, radk;
+ radical = $(evt.target);
+ radk = radical.data('radk') || radical.text();
+ combine = true;
+ radicalCannotBeCombined = function() {
+ return combine && !radical.hasClass('available');
+ };
+ radicalAlreadySelected = function() {
+ return __indexOf.call(_this.selected_radicals, radk) >= 0;
+ };
+ if (radicalAlreadySelected()) {
+ if (_this.selected_radicals.any(radk)) {
+ _this.selected_radicals = _this.selected_radicals.remove(radk);
+ radical.removeClass('selected');
+ if (_this.selected_radicals.length === 0) {
+ _this.reset();
+ return;
+ } else {
+ _this.getKanji();
+ return;
+ }
+ }
+ }
+ if (radicalCannotBeCombined()) {
+ return;
+ }
+ if (!combine) {
+ _this.reset();
+ }
+ _this.selected_radicals.push(radk);
+ radical.addClass('selected');
+ return _this.getKanji();
+ };
+ })(this));
+ this.list.on('click', '.result', (function(_this) {
+ return function(evt) {
+ evt.preventDefault();
+ return _this.list.trigger('character_chosen', $(evt.target).text());
+ };
+ })(this));
+ this.resetRadicalsButton.on('click', (function(_this) {
+ return function() {
+ return _this.reset();
+ };
+ })(this));
+ return this.area.on('area_activated', (function(_this) {
+ return function(evt, area) {
+ if (area.name === 'radical') {
+ return _this.setHeight();
+ }
+ };
+ })(this));
+ };
+
+ RadicalInput.prototype.reset = function() {
+ this.selected_radicals = [];
+ return this.allRadicals.removeClass('selected').addClass('available');
+ };
+
+ RadicalInput.prototype.toggleCombinedMode = function(state) {
+ this.combinedModeOn = state;
+ return this.toggleCombined(state);
+ };
+
+ RadicalInput.prototype.toggleCombined = function(show) {
+ return this.area.toggleClass('combined_mode', show);
+ };
+
+ RadicalInput.prototype.setAvailableRadicals = function(validRadicals) {
+ this.radicals.each((function(_this) {
+ return function(i, radical) {
+ var radk;
+ radical = $(radical);
+ radk = radical.data('radk') || radical.text();
+ return radical.toggleClass('available', _this.selected_radicals.none(radk) && !!validRadicals[radk]);
+ };
+ })(this));
+ return this.table.redraw();
+ };
+
+ RadicalInput.prototype.setHeight = function() {
+ var height;
+ if (this.isMobile()) {
+ height = window.innerHeight - this.table[0].getBoundingClientRect().top - HEIGHT_MARGIN - this.inputMethods[0].clientHeight;
+ return this.table.css('max-height', height);
+ } else {
+ return this.table.css('max-height', 'none');
+ }
+ };
+
+ RadicalInput.prototype.getKanji = function() {
+ return $.ajax({
+ type: 'GET',
+ url: '/radicals/' + this.selected_radicals.join(','),
+ dataType: 'json',
+ success: (function(_this) {
+ return function(data) {
+ var current_strokes;
+ current_strokes = 0;
+ if (data.kanji && data.kanji.length > 0) {
+ _this.list.empty();
+ $.each(data.kanji, function(i, kanji) {
+ if (current_strokes < kanji.strokes) {
+ _this.appendLabel(kanji.strokes);
+ current_strokes = kanji.strokes;
+ }
+ return _this.appendResult(kanji.kanji, "g" + kanji.grade);
+ });
+ } else {
+ _this.message('No kanji found with those parts');
+ }
+ _this.setAvailableRadicals(data.is_valid_radical);
+ return _this.handleResultsWrap();
+ };
+ })(this)
+ });
+ };
+
+ return RadicalInput;
+
+ })(Input);
+
+}).call(this);
+(function() {
+ var __hasProp = {}.hasOwnProperty,
+ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
+
+ this.SpeechInput = (function(_super) {
+ __extends(SpeechInput, _super);
+
+ function SpeechInput() {
+ SpeechInput.__super__.constructor.call(this, 'speech');
+ this.SpeechRecognition = Modernizr.speech;
+ this.englishButton = this.area.find('button.english');
+ this.japaneseButton = this.area.find('button.japanese');
+ this.results = this.area.find('.speech_results');
+ this.englishButton.click((function(_this) {
+ return function() {
+ _this.startRecognition();
+ return false;
+ };
+ })(this));
+ this.japaneseButton.click((function(_this) {
+ return function() {
+ _this.startRecognition('ja-JP');
+ return false;
+ };
+ })(this));
+ this.deactivate();
+ }
+
+ SpeechInput.prototype.activate = function(silent) {
+ SpeechInput.__super__.activate.call(this, silent);
+ return this.startRecognition();
+ };
+
+ SpeechInput.prototype.deactivate = function(silent) {
+ SpeechInput.__super__.deactivate.call(this, silent);
+ return this.stopRecognition();
+ };
+
+ SpeechInput.prototype.startRecognition = function(lang) {
+ if (lang == null) {
+ lang = 'en-US';
+ }
+ this.processedResults = [];
+ this.processedResultsIndex = 0;
+ this.stopRecognition();
+ this.session = new this.SpeechRecognition;
+ this.session.continuous = true;
+ this.session.interimResults = true;
+ this.session.lang = lang;
+ this.session.addEventListener('result', (function(_this) {
+ return function(event) {
+ var final, i, results, _i, _ref, _ref1;
+ results = [];
+ final = false;
+ for (i = _i = _ref = _this.processedResultsIndex, _ref1 = event.resultIndex; _ref <= _ref1 ? _i <= _ref1 : _i >= _ref1; i = _ref <= _ref1 ? ++_i : --_i) {
+ results.push(event.results[i][0].transcript);
+ final = event.results[i].isFinal;
+ }
+ return _this.processTranscript(results, final);
+ };
+ })(this));
+ return this.session.start();
+ };
+
+ SpeechInput.prototype.stopRecognition = function() {
+ if (this.session) {
+ this.session.stop();
+ }
+ return this.session = null;
+ };
+
+ SpeechInput.prototype.processTranscript = function(results, final) {
+ if (final) {
+ this.processedResults = this.processedResults.concat(results);
+ this.processedResultsIndex += results.length;
+ this.findCommands();
+ results = [];
+ }
+ this.outputResults(this.processedResults.concat(results));
+ return this.area.trigger('speech_result_added');
+ };
+
+ SpeechInput.prototype.findCommands = function() {
+ var command, split;
+ split = this.processedResults.pop().trim().split(' ');
+ command = split.last().toLowerCase();
+ switch (command) {
+ case 'back':
+ case '戻る':
+ if (split.length === 1) {
+ return this.processedResults.pop();
+ }
+ break;
+ case 'clear':
+ case 'クリア':
+ return this.processedResults = [];
+ case 'stop':
+ case 'やめる':
+ return this.stopRecognition();
+ case 'japanese':
+ case '日本語':
+ return this.startRecognition('ja-JP');
+ case 'english':
+ case '英語':
+ return this.startRecognition();
+ case 'input':
+ case '決定':
+ this.processedResults.push(split.to(-1).join(' '));
+ return this.area.trigger('speech_confirmed', [this.processedResults.join('')]);
+ case 'submit':
+ case 'search':
+ case '検索':
+ this.processedResults.push(split.to(-1).join(' '));
+ return this.area.trigger('speech_submitted', [this.processedResults.join('')]);
+ default:
+ return this.processedResults.push(split.join(' '));
+ }
+ };
+
+ SpeechInput.prototype.outputResults = function(results) {
+ return this.results.html(results.map(function(r) {
+ return "" + (r.trim()) + " ";
+ }));
+ };
+
+ SpeechInput.prototype.getVolumeLevel = function() {};
+
+ return SpeechInput;
+
+ })(Input);
+
+}).call(this);
+(function() {
+
+ var defineProperty = Object.defineProperty || function(obj, name, desc) {
+ obj[name] = desc.value;
+ };
+
+ var getOwnPropertyNames = Object.getOwnPropertyNames || function(obj) {
+ var names = [];
+ for(var key in obj) {
+ if(!obj.hasOwnProperty(key)) continue;
+ names.push(key);
+ }
+ return names;
+ };
+
+ var defineMethod = function(obj, name, method, enumerable) {
+ defineProperty(obj, name, {
+ value: method,
+ writable: true,
+ configurable: true,
+ enumerable: !!enumerable
+ });
+ };
+
+ var mixin = function(source) {
+ extend.call(this, source);
+ extend.call(this.prototype, source.prototype);
+ };
+
+ var extend = function(obj, enumerable) {
+ var names = getOwnPropertyNames(obj), i = names.length, key;
+ while(i--) {
+ key = names[i];
+ if(key && (!(key in this) || key === 'toString')) {
+ defineMethod(this, key, obj[key], enumerable !== false);
+ }
+ }
+ };
+
+ // "inherits" can be called in both the construtor of the inheriting class:
+ //
+ // var Foo = Class(function Foo() {
+ // this.inherits(new Bar());
+ // });
+ //
+ // ... or outside it:
+ //
+ // var Foo = Class(function Foo() {});
+ // Foo.inherits(Bar);
+ //
+ // The idea behind this is that often the constructor of the parent class needs
+ // to be called, and calling it outside the context of the child class' constructor
+ // often makes no sense. So, the first method makes sense when the parent's constructor
+ // needs to be called, and the second makes sense when Foo simply inherits Bar's methods.
+ // This is effectively identical to a mixin, and in fact this is not true prototypal
+ // inheritance as the properties are being mixed in as non-enumerable methods instead of
+ // actually setting the prototype object itself. This could be done through __proto__,
+ // but it's dirtier and IE could not be supported.
+ var inherits = function(parent) {
+ var target = typeof this === 'function' ? this.prototype : this;
+ if(typeof parent === 'function') {
+ extend.call(target, parent.prototype, false);
+ } else {
+ extend.call(target, parent, false);
+ extend.call(target, parent.constructor.prototype, false);
+ }
+ };
+
+ Class = function(constructor) {
+ // Don't want to define methods on Function.prototype (yet),
+ // so define them directly for now.
+ defineMethod(constructor, 'mixin', mixin, true);
+ defineMethod(constructor, 'extend', extend, true);
+ defineMethod(constructor, 'inherits', inherits);
+ defineMethod(constructor.prototype, 'extend', extend);
+ defineMethod(constructor.prototype, 'inherits', inherits);
+ return constructor;
+ };
+
+})();
+
+var Canvas = Class(function Canvas(canvas) {
+ this.setupCanvas(canvas);
+});
+
+Canvas.prototype.extend({
+
+ setupCanvas: function(canvas) {
+ var context = canvas.getContext && canvas.getContext("2d");
+ if(!canvas || !context) return;
+ this.context = context;
+ this.canvas = canvas;
+ this.pixelRatio = Math.min(2, window.devicePixelRatio || 1);
+ this.setDimensions(canvas.width, canvas.height);
+ },
+
+ setDimensions: function(w, h) {
+ var dw = w, dh = h;
+ if (this.pixelRatio > 1) {
+ dw *= this.pixelRatio;
+ dh *= this.pixelRatio;
+ }
+ this.width = this.canvas.width = dw;
+ this.height = this.canvas.height = dh;
+ this.canvas.style.width = w + 'px';
+ this.canvas.style.height = h + 'px';
+ },
+
+ clear: function() {
+ this.context.clearRect(0, 0, this.width, this.height);
+ }
+
+});
+
+
+var Point = Class(function Point(x, y) {
+ this.x = x;
+ this.y = y;
+});
+
+Point.DEGREES_IN_RADIANS = 57.2957795;
+Point.DEFAULT_SIZE = 3;
+Point.COLORS = ['red','green','blue','orange','pink','purple','gray'];
+
+Point.extend({
+
+ degToRad: function(deg) {
+ return deg / Point.DEGREES_IN_RADIANS;
+ },
+
+ radToDeg: function(rad) {
+ var deg = rad * Point.DEGREES_IN_RADIANS;
+ while(deg < 0) deg += 360;
+ return deg;
+ },
+
+ vector: function(deg, len) {
+ var rad = Point.degToRad(deg);
+ return new Point(Math.cos(rad) * len, Math.sin(rad) * len);
+ },
+
+ nextColor: function() {
+ var index = this.colorIndex;
+ if(index === undefined || index == this.COLORS.length - 1) {
+ index = 0;
+ } else {
+ index += 1;
+ }
+ this.colorIndex = index;
+ return this.COLORS[index];
+ }
+
+
+});
+
+Point.prototype.extend({
+
+ toString: function(point) {
+ return 'Point: ' + this.x + ',' + this.y;
+ },
+
+ add: function(point) {
+ return new Point(this.x + point.x, this.y + point.y);
+ },
+
+ subtract :function(point) {
+ return new Point(this.x - point.x, this.y - point.y);
+ },
+
+ multiply: function(n) {
+ return Point.vector(this.angle(), this.length() * n);
+ },
+
+ divide: function(n) {
+ return Point.vector(this.angle(), this.length() / n);
+ },
+
+ angle: function(deg) {
+ var len, rad;
+ if(deg === undefined) {
+ return Point.radToDeg(Math.atan2(this.y, this.x));
+ } else {
+ return Point.vector(deg, this.length());
+ }
+ },
+
+ rotate: function(deg) {
+ return this.angle(this.angle() + deg);
+ },
+
+ length: function(l) {
+ if(l === undefined) {
+ return Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2));
+ } else {
+ return Point.vector(this.angle(), l);
+ }
+ },
+
+ move: function(ctx) {
+ ctx.moveTo(this.x, this.y);
+ },
+
+ drawPoint: function(ctx, size) {
+ size = size || Point.DEFAULT_SIZE;
+ ctx.beginPath();
+ ctx.arc(this.x, this.y, size, 0, Math.PI * 2, true);
+ ctx.fill();
+ },
+
+ drawGuide: function(ctx, num) {
+ ctx.font = "bold 9px sans-serif";
+ ctx.fillStyle = '#000';
+ ctx.fillText(num + ' ' + Math.round(this.x) + ',' + Math.round(this.y), this.x, this.y);
+ },
+
+ draw: function(ctx, lastPoint) {
+ var inHandle, outHandle;
+ inHandle = this.inHandle ? this.inHandle : this;
+ if(lastPoint) {
+ outHandle = lastPoint.outHandle || lastPoint;
+ }
+ if(inHandle && outHandle) {
+ ctx.bezierCurveTo(outHandle.x, outHandle.y, inHandle.x, inHandle.y, this.x, this.y);
+ } else {
+ ctx.lineTo(this.x, this.y);
+ }
+ return this;
+ },
+
+ drawHandles: function(ctx) {
+ ctx.fillStyle = Point.nextColor();
+ ctx.beginPath();
+ if(this.inHandle) {
+ ctx.arc(this.inHandle.x, this.inHandle.y, 3, 0, Math.PI * 2, true);
+ }
+ if(this.outHandle) {
+ ctx.arc(this.outHandle.x, this.outHandle.y, 3, 0, Math.PI * 2, true);
+ }
+ ctx.fill()
+ }
+
+});
+
+
+var Color = Class(function Color() {
+ if(typeof arguments[0] === 'string') {
+ this.setFromHex(arguments[0]);
+ } else {
+ this.red = arguments[0] || 0;
+ this.blue = arguments[1] || 0;
+ this.green = arguments[2] || 0;
+ this.alpha = arguments[3] || 0;
+ }
+});
+
+Color.prototype.extend({
+
+ setFromHex: function(hex) {
+ hex = hex.replace(/#/, '');
+ if(hex.length < 6) {
+ hex = hex.replace(/(.)/g, '$1$1');
+ }
+ this.red = parseInt(hex.slice(0,2), 16);
+ this.green = parseInt(hex.slice(2,4), 16);
+ this.blue = parseInt(hex.slice(4,6), 16);
+ this.alpha = 1;
+ },
+
+ toString: function() {
+ return 'rgba(' + [this.red, this.green, this.blue, this.alpha].join(',') + ')';
+ },
+
+ set: function(ctx, line) {
+ if(line) {
+ ctx.strokeStyle = this.toString();
+ } else {
+ ctx.fillStyle = this.toString();
+ }
+ }
+
+});
+
+var Shadow = Class(function Shadow(color, xOffset, yOffset, blur) {
+ this.color = color;
+ this.xOffset = xOffset;
+ this.yOffset = yOffset;
+ this.blur = blur;
+});
+
+Shadow.prototype.extend({
+
+ set: function(ctx) {
+ ctx.shadowColor = this.color ? this.color.toString() : null;
+ ctx.shadowOffsetX = this.xOffset || 0;
+ ctx.shadowOffsetY = this.yOffset || 0;
+ ctx.shadowBlur = this.blur || 0;
+ }
+
+});
+
+var LineStyle = Class(function LineStyle(s, cap, miter, join) {
+ var match;
+
+ match = s.match(/(\d+\.?\d*)px/);
+ this.px = match ? match[1] : 1;
+
+ match = s.match(/(#[0-9a-f]{3,6}|rgba?\(.+\))/);
+ this.color = new Color(match ? match[1] : '#000');
+
+ this.cap = cap || 'round';
+ this.miter = miter || 'round';
+ this.join = join || 'round';
+});
+
+LineStyle.prototype.extend({
+
+ toString: function() {
+ return this.px + 'px ' + this.color.toString();
+ },
+
+ set: function(ctx) {
+ ctx.lineWidth = this.px;
+ ctx.lineCap = this.cap;
+ ctx.lineMiter = this.miter;
+ ctx.lineJoin = this.join;
+ this.color.set(ctx, true);
+ }
+
+});
+// Dependencies: jQuery
+
+(function(global) {
+
+ var Event = Class(function Events() {});
+
+ var COMMAND_KEY_CODE = 91;
+ var CTRL_KEY_CODE = 17;
+ var SHIFT_KEY_CODE = 16;
+ var ALT_KEY_CODE = 18;
+
+ Event.withMeta = function(evt) {
+ return evt.ctrlKey || evt.metaKey;
+ }
+
+ Event.isMeta = function(evt) {
+ return evt.which === CTRL_KEY_CODE || evt.which === COMMAND_KEY_CODE;
+ }
+
+ Event.Listener = {
+
+ setupListeners: function() {
+ if(!this.eventListeners) {
+ this.eventListeners = [];
+ }
+ },
+
+ listener: function(target) {
+ this.setupListeners();
+ this.eventListeners.push(target);
+ },
+
+ broadcast: function(name) {
+ var handlerName = 'on' + name;
+ var args = Array.prototype.slice.call(arguments, 1);
+ this.eventListeners.forEach(function(listener) {
+ if(listener[handlerName]) {
+ listener[handlerName].apply(listener, args);
+ }
+ }, this);
+ }
+
+ };
+
+ global.Event = Event;
+
+})(this);
+
+var ImageMap = Class(function ImageMap(container) {
+ this.id = ImageMap.getNextId();
+ this.map = $(' ').attr({
+ id: this.id,
+ name: this.id
+ });
+ this.image = $(' ').attr({
+ src: ImageMap.getTransparent(),
+ ismap: true,
+ usemap: '#' + this.id
+ }).css({
+ top: 0,
+ left: 0,
+ position: 'absolute'
+ });
+ container.append(this.map);
+ container.append(this.image);
+
+});
+
+ImageMap.extend({
+
+ getNextId: function() {
+ if(!this._id) {
+ this._id = 0;
+ }
+ this._id += 1;
+ return 'image_map_' + this._id;
+ },
+
+ getTransparent: function() {
+ // base64 encode for a 1px transparent gif
+ var pixel = 'R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
+ return 'data:image/gif;base64,' + pixel;
+ }
+
+});
+
+ImageMap.prototype.extend({
+
+ setDimensions: function(w, h) {
+ this.image.css({ width: w, height: h });
+ },
+
+ addArea: function(points, connect) {
+ var area = $(' ');
+ area.attr('coords', points.map(function(p) {
+ return p.x + ',' + p.y;
+ }).join(','));
+ area.on('mouseenter', function(evt) {
+ if(connect.onMouseEnter) {
+ connect.onMouseEnter(evt);
+ }
+ });
+ area.on('mouseleave', function(evt) {
+ if(connect.onMouseLeave) {
+ connect.onMouseLeave(evt);
+ }
+ });
+ area.on('click', function(evt) {
+ if(connect.onClick) {
+ connect.onClick(evt);
+ }
+ });
+ this.map.append(area);
+ }
+
+});
+// DERP. Trigger asset rebuild
+
+var Animation = Class(function Animation() {});
+
+Animation.mixin(Canvas);
+
+Animation.prototype.extend(Event.Listener);
+Animation.prototype.extend({
+
+ reset: function() {
+ this.relativePosition = 0;
+ this.maxPosition = this.totalFrames - 1;
+ this.completed = false;
+ this.animating = false;
+ window.cancelAnimationFrame(this.requestId);
+ },
+
+ ease: function(v, vmax) {
+ return -vmax/ 2 * (Math.cos(Math.PI * v / vmax) - 1);
+ },
+
+ getCurrentFrame: function() {
+ var easedPosition = this.ease(this.relativePosition, this.maxPosition);
+ return Math.min(1 + Math.round(easedPosition), this.totalFrames);
+ },
+
+ advance: function() {
+ if (this.relativePosition >= this.maxPosition) {
+ if (!this.completed) {
+ this.animating = false;
+ this.completed = true;
+ this.broadcast('AnimationComplete', this);
+ }
+ } else {
+ this.relativePosition += this.speed;
+ }
+ },
+
+ drawCurrentFrameAndAdvance: function() {
+ this.drawFrame(this.getCurrentFrame());
+ this.advance();
+ },
+
+ drawCurrentFrameAndRequestNext: function() {
+ var self = this;
+ this.clear();
+ this.animating = true;
+ this.drawCurrentFrameAndAdvance();
+ if(this.animating) {
+ this.requestId = window.requestAnimationFrame(function() {
+ self.drawCurrentFrameAndRequestNext();
+ });
+ }
+ },
+
+ animate: function() {
+ if(this.animating) return;
+ this.reset();
+ this.drawCurrentFrameAndRequestNext();
+ }
+});
+
+// requestAnimationFrame polyfill
+
+(function() {
+ var lastTime = 0;
+ var vendors = ['ms', 'moz', 'webkit', 'o'];
+ for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
+ window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
+ window.cancelAnimationFrame =
+ window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
+ }
+
+ if (!window.requestAnimationFrame)
+ window.requestAnimationFrame = function(callback, element) {
+ var currTime = new Date().getTime();
+ var timeToCall = Math.max(0, 16 - (currTime - lastTime));
+ var id = window.setTimeout(function() { callback(currTime + timeToCall); },
+ timeToCall);
+ lastTime = currTime + timeToCall;
+ return id;
+ };
+
+ if (!window.cancelAnimationFrame)
+ window.cancelAnimationFrame = function(id) {
+ clearTimeout(id);
+ };
+}());
+
+
+var Kanji = Class(function Kanji(svgCommands, container, options) {
+
+ options = options || {};
+
+ var canvas = container.find('canvas');
+
+ this.setupCanvas(canvas.get(0));
+ this.imageMap = new ImageMap(container.find('.stage'));
+ this.imageMap.setDimensions(150, 150);
+
+ // KanjiVG dimensions are 109x109
+ this.scale = Math.min(this.width, this.height) / 109;
+
+ // TODO: Derive this from KanjiStroke?
+ this.setBrush(options['brush']);
+
+ this.build(svgCommands);
+
+ this.setSpeed(Kanji.getSetting('speed'));
+ this.setThickness(Kanji.getSetting('thickness'));
+});
+
+Kanji.mixin(Canvas);
+Kanji.mixin(ImageMap);
+
+// TODO: This needs to be a global settings object!
+
+Kanji.HAS_LOCAL_STORAGE = !!window.localStorage;
+
+Kanji.setSetting = function(name, val) {
+ if(!Kanji.HAS_LOCAL_STORAGE) return;
+ localStorage[name] = val;
+};
+
+Kanji.getSetting = function(name) {
+ var val, num;
+ if(Kanji.HAS_LOCAL_STORAGE) {
+ val = localStorage[name];
+ num = parseFloat(val);
+ if(!isNaN(num)) {
+ val = num;
+ }
+ }
+ if(val === undefined) {
+ val = Kanji['DEFAULT_' + name.toUpperCase()];
+ }
+ return val;
+};
+
+Kanji.prototype.extend(Event.Listener);
+Kanji.prototype.extend({
+
+ setBrush: function(url) {
+ var img = new Image(), self = this;
+ img.src = url;
+ img.onload = this.onBrushLoaded.bind(this);
+ this.brush = img;
+ },
+
+ onBrushLoaded: function() {
+ if (this.drawOnBrushLoaded) {
+ this.draw();
+ }
+ },
+
+ build: function(svgCommands) {
+ this.strokes = svgCommands.map(function(c, i) {
+ var k = new KanjiStroke({
+ scale: this.scale,
+ brush: this.brush,
+ context: this.context,
+ pressurePoint: true
+ });
+ k.number = i + 1;
+ k.listener(this);
+ k.buildPointsFromSVG(c);
+ // Scale for the image map is in CSS pixels, not device pixels,
+ // so divide by the pixel ratio here.
+ this.imageMap.addArea(k.getOutline(5, this.scale / this.pixelRatio), k);
+ return k;
+ }, this);
+ },
+
+ setSpeed: function(speed) {
+ if(speed < Kanji.MIN_SPEED || speed > Kanji.MAX_SPEED) {
+ return;
+ }
+ this.strokes.forEach(function(s) {
+ s.speed = speed;
+ });
+ this.speed = speed;
+ this.delayBetweenStrokes = 250;
+ Kanji.setSetting('speed', speed);
+ },
+
+ setThickness: function(thickness) {
+ if(thickness < Kanji.MIN_THICKNESS || thickness > Kanji.MAX_THICKNESS) {
+ return;
+ }
+ thickness = thickness.round(1);
+ this.strokes.forEach(function(s) {
+ s.strokeThickness = thickness;
+ });
+ this.thickness = thickness;
+ this.draw();
+ Kanji.setSetting('thickness', thickness);
+ },
+
+ drawNumbers: function() {
+ this.clear();
+ this.draw();
+ this.strokes.forEach(function(s) {
+ s.drawNumber();
+ });
+ },
+
+ drawPoints: function() {
+ this.clear();
+ this.strokes.forEach(function(s) {
+ s.drawPoints();
+ });
+ },
+
+ draw: function(guides, handles) {
+ if (!this.brush.complete) {
+ this.drawOnBrushLoaded = true;
+ return;
+ }
+ this.clear();
+ this.strokes.forEach(function(s) {
+ s.draw(guides, handles);
+ });
+ },
+
+ animateFromStart: function() {
+ this.singleStrokeMode = false;
+ this.animatingAll = true;
+ this.strokes.forEach(function(s) {
+ s.reset();
+ });
+ this.clear();
+ clearTimeout(this.timer);
+ this.strokes[0].animate();
+ },
+
+ animateSingleStroke: function(num) {
+ this.singleStrokeMode = true;
+ this.strokes[num - 1].animate();
+ },
+
+ onAnimationComplete: function(stroke) {
+ if(this.singleStrokeMode) return;
+ // .number is equal to the array index + 1
+ var next = this.strokes[stroke.number];
+ if(next) {
+ this.timer = setTimeout(function() {
+ next.animate();
+ }, this.delayBetweenStrokes);
+ }
+ this.animatingAll = !!next;
+ },
+
+ onBeforeFrame: function(stroke) {
+ var i;
+ this.clear();
+ if(this.singleStrokeMode) {
+ // Draw all strokes except currently animating one
+ for(i = 0, len = this.strokes.length; i < len; i++) {
+ if(this.strokes[i] !== stroke) {
+ this.strokes[i].draw();
+ }
+ }
+ } else {
+ // Draw all strokes up to the current onee
+ for(i = 0, len = stroke.number - 1; i < len; i++) {
+ this.strokes[i].draw();
+ }
+ }
+ },
+
+ onStrokeEnter: function(stroke) {
+ if(this.animatingAll) return;
+ stroke.color = stroke.highlightedColor;
+ this.clear();
+ this.draw();
+ },
+
+ onStrokeLeave: function(stroke) {
+ if(this.animatingAll) return;
+ stroke.color = stroke.mainColor;
+ this.clear();
+ this.draw();
+ },
+
+ onStrokeClick: function(stroke) {
+ if(this.animatingAll) return;
+ stroke.color = stroke.mainColor;
+ this.animateSingleStroke(stroke.number);
+ }
+
+});
+
+
+Kanji.MIN_SPEED = 0.3;
+Kanji.MAX_SPEED = 3;
+
+Kanji.MIN_THICKNESS = 0.2;
+Kanji.MAX_THICKNESS = 3;
+
+Kanji.DEFAULT_SPEED = 1.5;
+Kanji.DEFAULT_THICKNESS = 1.2;
+
+var KanjiStroke = Class(function KanjiStroke(options) {
+
+ this.options = options;
+
+ this.scale = this.options.scale || 1;
+ this.context = this.options.context;
+ this.style = this.options.style;
+
+ this.points = [];
+ this.listeners = [];
+ this.speed = 1;
+
+ this.brushScale = .10 * this.scale;
+ this.brushRotation = true;
+ this.pressurePointRadius = 15 * this.scale;
+ this.pointsBisectThreshold = 1;
+ this.outlineBisectThreshold = 6 * this.scale;
+ this.strokeThickness = 0.8 * this.scale;
+
+ this.mainColor = new Color('#777');
+ this.darkColor = new Color('#777');
+ this.mainShadow = new Shadow();
+
+ this.highlightedColor = new Color('#bbb');
+ this.pressurePointColor = new Color(255, 0, 0, 0.2);
+
+ this.numberRadius = 10;
+ this.numberCircleColor = new Color('#b77');
+ this.numberCircleShadow = new Shadow(new Color(0, 0, 0, 0.2), 0, 2, 2);
+ this.numberTextColor = new Color(255,255,255,1);
+ this.numberTextShadow = new Shadow(new Color(0, 0, 0, 0.5), 0, -1, 0);
+
+ this.color = this.mainColor;
+ this.shadow = this.mainShadow;
+
+});
+
+KanjiStroke.mixin(Animation);
+
+KanjiStroke.prototype.extend(Event.Listener);
+KanjiStroke.extend({
+
+ DISTANCE_BETWEEN_POINTS: 20,
+
+ cachedThicknesses: {},
+ cachedRotations: {},
+
+ getThicknessForTime: function(codepoint, t) {
+ var map = this.cachedThicknesses[codepoint] || this.buildThicknessMap(codepoint);
+ return map[Math.round(t * 100)];
+ },
+
+ getRotationForTime: function(codepoint, t) {
+ var map = this.cachedRotations[codepoint] || this.buildRotationMap(codepoint);
+ return map[t.toFixed(2)];
+ },
+
+ buildThicknessMap: function(codepoint) {
+ var guide = KanjiStroke.LINE_GUIDES[codepoint] || KanjiStroke.LINE_GUIDES['default'];
+ var map = {};
+
+ var split = guide.split(',').map(function(l) {
+ var s = l.split(' L');
+ return [parseInt(s[0]), parseInt(s[1])];
+ });
+ split.forEach(function(set, i) {
+ var startTime = set[0];
+ if(startTime == 100) {
+ return;
+ }
+ var startThickness = set[1];
+ var next = split[i + 1];
+ var stopTime = next ? next[0] : 100;
+ var stopThickness = next ? next[1] : startThickness;
+ var dThickness = stopThickness - startThickness;
+ var dTime = stopTime - startTime;
+ var time = startTime;
+ while(time <= stopTime) {
+ map[time] = ((((time - startTime) / dTime) * dThickness) + startThickness) / 100;
+ time++;
+ }
+ });
+ this.cachedThicknesses[codepoint] = map;
+ return map;
+ },
+
+ buildRotationMap: function(codepoint) {
+ var guide = KanjiStroke.BRUSH_ROTATION[codepoint] || KanjiStroke.BRUSH_ROTATION['default'];
+ var map = {};
+
+ var split = guide.split(' ').map(function(l) {
+ var s = l.split(':');
+ return [parseFloat(s[0]), parseFloat(s[1])];
+ });
+ split.forEach(function(set, i) {
+ var startTime = set[0];
+ if(startTime == 1) {
+ return;
+ }
+ var startRotation = set[1];
+ var next = split[i + 1];
+ var stopTime = next ? next[0] : 1;
+ var stopRotation = next ? next[1] : startRotation;
+ var dRotation = stopRotation - startRotation;
+ var dTime = stopTime - startTime;
+ var time = startTime;
+ while(time <= stopTime) {
+ map[time.toFixed(2)] = Point.degToRad((((time - startTime) / dTime) * dRotation) + startRotation);
+ time += .01;
+ }
+ });
+ this.cachedRotations[codepoint] = map;
+ return map;
+ }
+
+});
+
+KanjiStroke.prototype.extend({
+
+ addPoint: function(points) {
+ this.points = this.points.concat(points);
+ return this;
+ },
+
+ buildPointsFromSVG: function(svg, ignoreLast) {
+ var self, commands, currentPoint, split;
+ if(!svg) return;
+
+ split = svg.split(':');
+ svg = split[1];
+ self = this;
+ commands = parseCommands(svg);
+
+ this.codepoint = split[0];
+
+ function parseCommands(svg) {
+ var split = svg.split(/(?=[mcls])/i), result = [], str, command, points, group;
+ // Handle repeated commands
+ for (var i = 0; i < split.length; i++) {
+ str = split[i];
+ command = str.charAt(0);
+ group = getCommandPoints(command, str.slice(1).trim());
+ for (var j = 0; j < group.length; j++) {
+ result.push({
+ command: command,
+ points: group[j]
+ });
+ }
+ }
+ return result;
+ }
+
+ // Get points for an SVG command while
+ // handling repeated command groups.
+ function getCommandPoints(command, str) {
+ var points, group = [], n;
+ points = str.split(/[, ]|(?=-)/).map(parseFloat);
+ switch(command.toLowerCase()) {
+ case 'm': n = 2; break;
+ case 'l': n = 2; break;
+ case 'c': n = 6; break;
+ case 's': n = 6; break;
+ }
+ if (points.length % n !== 0) {
+ console.warn('Unexpected number of points in SVG command');
+ }
+ do {
+ group.push(points.slice(0, n));
+ points = points.slice(n);
+ } while (points.length);
+ return group;
+ }
+
+ function moveAbsolute(p) {
+ var p = getNewPoint(p[0], p[1]);
+ self.addPoint(p);
+ currentPoint = p;
+ }
+
+ function lineRelative(points) {
+ points[0] += currentPoint.x;
+ points[1] += currentPoint.y;
+ lineAbsolute(points);
+ }
+
+ function lineAbsolute(points) {
+ var target = getNewPoint(points[0], points[1]);
+ self.addPoint(target);
+ currentPoint = target;
+ }
+
+ function shorthandCurveAbsolute(bezierPoints) {
+ var outHandle;
+ if(currentPoint.inHandle) {
+ outHandle = currentPoint.add(currentPoint.subtract(currentPoint.inHandle));
+ } else {
+ outHandle = getNewPoint(currentPoint.x, currentPoint.y);
+ }
+ bezierPoints.unshift(outHandle.y);
+ bezierPoints.unshift(outHandle.x);
+ curveAbsolute(bezierPoints);
+ }
+
+ function shorthandCurveRelative(bezierPoints) {
+ var outHandle;
+ if(currentPoint.inHandle) {
+ outHandle = currentPoint.inHandle.subtract(currentPoint).multiply(-1);
+ } else {
+ outHandle = getNewPoint(0, 0);
+ }
+ bezierPoints.unshift(outHandle.y);
+ bezierPoints.unshift(outHandle.x);
+ curveRelative(bezierPoints);
+ }
+
+ function curveRelative(bezierPoints, skipFirst) {
+ bezierPoints[0] += currentPoint.x;
+ bezierPoints[1] += currentPoint.y;
+ bezierPoints[2] += currentPoint.x;
+ bezierPoints[3] += currentPoint.y;
+ bezierPoints[4] += currentPoint.x;
+ bezierPoints[5] += currentPoint.y;
+ curveAbsolute(bezierPoints);
+ }
+
+ function curveAbsolute(bezierPoints) {
+ var target, inHandleX, inHandleY, outHandleX, outHandleY, targetX, targetY;
+ currentPoint.outHandle = getNewPoint(bezierPoints[0], bezierPoints[1]);
+ target = getNewPoint(bezierPoints[4], bezierPoints[5]);
+ target.inHandle = getNewPoint(bezierPoints[2], bezierPoints[3]);
+ self.addPoint(target);
+ currentPoint = target;
+ }
+
+ function getNewPoint(x, y) {
+ return new Point(x, y);
+ }
+
+ function runCommand(c, index, commands) {
+ if(ignoreLast && index === commands.length - 1) {
+ return;
+ }
+ switch(c.command) {
+ case 'M': moveAbsolute(c.points); break;
+ case 'm': moveAbsolute(c.points); break;
+ case 'C': curveAbsolute(c.points); break;
+ case 'c': curveRelative(c.points); break;
+ case 'S': shorthandCurveAbsolute(c.points); break;
+ case 's': shorthandCurveRelative(c.points); break;
+ case 'L': lineAbsolute(c.points); break;
+ case 'l': lineRelative(c.points); break;
+ }
+ }
+
+ commands.forEach(runCommand);
+
+ this.minimalPoints = this.removeAdjacentPoints(this.points);
+ this.points = this.bisectToThreshold(this.minimalPoints);
+ this.totalFrames = this.points.length;
+ },
+
+ removeAdjacentPoints: function(points) {
+ var currentPoint = points[0], result = [currentPoint];
+ points.from(1).forEach(function(p, i) {
+ var nextPoint = p;
+ var distance = nextPoint.subtract(currentPoint).length();
+ if (distance > this.pointsBisectThreshold) {
+ result.push(nextPoint);
+ currentPoint = nextPoint;
+ }
+ }, this);
+ return result;
+ },
+
+ inspectPoints: function(points) {
+ console.info(points.join('\n'));
+ },
+
+ bisectToThreshold: function(points) {
+
+ function splitBezier(p1, p2) {
+
+ var t = 0.5;
+
+ var x1 = p1.x;
+ var y1 = p1.y;
+ var x2 = p1.outHandle ? p1.outHandle.x : p1.x;
+ var y2 = p1.outHandle ? p1.outHandle.y : p1.y;
+ var x3 = p2.inHandle ? p2.inHandle.x : p2.x;
+ var y3 = p2.inHandle ? p2.inHandle.y : p2.y;
+ var x4 = p2.x;
+ var y4 = p2.y;
+
+ var x12 = (x2 - x1) * t + x1;
+ var y12 = (y2 - y1) * t + y1;
+
+ var x23 = (x3 - x2) * t + x2;
+ var y23 = (y3 - y2) * t + y2;
+
+ var x34 = (x4 - x3) * t + x3;
+ var y34 = (y4 - y3) * t + y3;
+
+ var x123 = (x23 - x12) * t + x12;
+ var y123 = (y23 - y12) * t + y12;
+
+ var x234 = (x34 - x23) * t + x23;
+ var y234 = (y34 - y23) * t + y23;
+
+ var x1234 = (x234 - x123) * t + x123;
+ var y1234 = (y234 - y123) * t + y123;
+
+ if(p1.outHandle) {
+ p1.outHandle = p1.add(p1.outHandle.subtract(p1).multiply(0.5));
+ }
+ if(p2.inHandle) {
+ p2.inHandle = p2.add(p2.inHandle.subtract(p2).multiply(0.5));
+ }
+ var p = new Point(x1234, y1234);
+ p.inHandle = new Point(x123, y123);
+ p.outHandle = p.add(p.subtract(p.inHandle));
+ return p;
+ }
+
+ var currentPoint, nextPoint, result = points.concat(), i = 1;
+ currentPoint = result[0];
+ var kill = 0;
+
+ while (nextPoint = result[i]) {
+ var dist = nextPoint.subtract(currentPoint).length();
+ if (dist > this.pointsBisectThreshold) {
+ result.splice(i, 0, splitBezier(currentPoint, nextPoint));
+ } else {
+ // Move on
+ currentPoint = nextPoint;
+ i++;
+ }
+
+ // REMOVE ME!
+ kill++;
+ if (kill > 1000) {
+ console.info("DEAD AT", kill);
+ break;
+ }
+ }
+
+ return result;
+
+ },
+
+ /*
+ bisectToThreshold: function(points, threshold) {
+ for(var i = 0; i < 10; i++) {
+ points = this.bisectPoints(points, threshold);
+ }
+ return points;
+ },
+
+ bisectPoints: function(points, threshold) {
+
+ var bisectedPoints = [];
+ var earmarkedPoints = {};
+
+ function splitBezier(p1, p2) {
+
+ var t = 0.5;
+
+ var x1 = p1.x;
+ var y1 = p1.y;
+ var x2 = p1.outHandle ? p1.outHandle.x : p1.x;
+ var y2 = p1.outHandle ? p1.outHandle.y : p1.y;
+ var x3 = p2.inHandle ? p2.inHandle.x : p2.x;
+ var y3 = p2.inHandle ? p2.inHandle.y : p2.y;
+ var x4 = p2.x;
+ var y4 = p2.y;
+
+ var x12 = (x2 - x1) * t + x1;
+ var y12 = (y2 - y1) * t + y1;
+
+ var x23 = (x3 - x2) * t + x2;
+ var y23 = (y3 - y2) * t + y2;
+
+ var x34 = (x4 - x3) * t + x3;
+ var y34 = (y4 - y3) * t + y3;
+
+ var x123 = (x23 - x12) * t + x12;
+ var y123 = (y23 - y12) * t + y12;
+
+ var x234 = (x34 - x23) * t + x23;
+ var y234 = (y34 - y23) * t + y23;
+
+ var x1234 = (x234 - x123) * t + x123;
+ var y1234 = (y234 - y123) * t + y123;
+
+ if(p1.outHandle) {
+ p1.outHandle = p1.add(p1.outHandle.subtract(p1).multiply(0.5));
+ }
+ if(p2.inHandle) {
+ p2.inHandle = p2.add(p2.inHandle.subtract(p2).multiply(0.5));
+ }
+ var p = new Point(x1234, y1234);
+ p.inHandle = new Point(x123, y123);
+ p.outHandle = p.add(p.subtract(p.inHandle));
+ return p;
+ }
+
+ points.forEach(function(p, i, points) {
+ var thisPoint = p;
+ var nextPoint = points.at(i + 1);
+ var distanceToNext = nextPoint.subtract(thisPoint).length();
+ if(nextPoint.inHandle && distanceToNext > threshold) {
+ earmarkedPoints[i] = true;
+ earmarkedPoints[points.length - (i + 2)] = true;
+ }
+ });
+
+ points.forEach(function(p, i, points) {
+ var thisPoint = p;
+ var nextPoint = points.at(i + 1);
+ bisectedPoints.push(thisPoint);
+ if(earmarkedPoints[i]) {
+ bisectedPoints.push(splitBezier(thisPoint, nextPoint));
+ }
+ });
+
+ return bisectedPoints;
+ },
+
+ rescale: function() {
+ this.points.forEach(this.rescalePoint, this);
+ if(this.outline) {
+ this.outline.forEach(this.rescalePoint, this);
+ }
+ },
+
+ rescalePoint: function(p) {
+ p.x = p.x * this.scale;
+ p.y = p.y * this.scale;
+ if(p.inHandle) this.rescalePoint(p.inHandle);
+ if(p.outHandle) this.rescalePoint(p.outHandle);
+ },
+
+ createOutline: function() {
+ this.outline = this.getOutline(this.strokeThickness);
+ this.outline = this.bisectToThreshold(this.outline, this.outlineBisectThreshold);
+ this.extendStrokeEdges();
+ },
+ */
+
+ getOutline: function(thickness, scale) {
+ var points = this.minimalPoints, result = [], i, len, half;
+
+ function createTopPoint(original, offset) {
+ var point = original.subtract(offset);
+ // Note that we are creating an outline (2D) from a set of points (1D), that will end
+ // up being drawn in sequential order around the outline, so we need to flip the "top"
+ // (subtracted) point's inHandle and outHandle, or they will be reversed upon final output.
+ if(original.inHandle) {
+ point.outHandle = original.inHandle.subtract(offset);
+ }
+ if(original.outHandle) {
+ point.inHandle = original.outHandle.subtract(offset);
+ }
+ return point.multiply(scale);
+ }
+
+ function createBottomPoint(original, offset) {
+ var point = original.add(offset);
+ if(original.inHandle) {
+ point.inHandle = original.inHandle.add(offset);
+ }
+ if(original.outHandle) {
+ point.outHandle = original.outHandle.add(offset);
+ }
+ return point.multiply(scale);
+ }
+
+ function pushTangentPoints(point, vector) {
+ var offset = vector.rotate(90).length((thickness || 5) * (point.thickness || 2));
+ result.unshift(createTopPoint(point, offset));
+ result.push(createBottomPoint(point, offset));
+ }
+
+ pushTangentPoints(points.first(), points.at(1).subtract(points.first()));
+
+ for(i = 1, len = points.length - 1; i < len; i++) {
+ pushTangentPoints(points.at(i), points.at(i + 1).subtract(points.at(i - 1)));
+ }
+
+ pushTangentPoints(points.last(), points.last().subtract(points.at(-2)));
+
+ half = Math.floor(points.length);
+ result = result.slice(half).concat(result.slice(0, half));
+ return result;
+ },
+
+ /*
+ calculateStrokeThickness: function() {
+
+ var i, len, p1, p2, delta, t, thickness;
+
+ for(i = 0, len = this.outline.length; i < len; i++) {
+ t = (i > len / 2 ? len - i : i) / len;
+ p1 = this.outline.at(i);
+ p2 = this.outline.at(-i);
+ delta = p2.subtract(p1);
+ thickness = KanjiStroke.getThicknessForTime(this.codepoint, t);
+
+ if(thickness > 1) {
+ this.outline[i] = p1.subtract(delta.multiply(thickness - 1));
+ } else if(thickness < 1) {
+ this.outline[i] = p1.add(delta.multiply(1 - thickness));
+ }
+ }
+ },
+
+ extendStrokeEdges: function() {
+ this.extendStrokeEdge(this.outline.at(0), this.outline.at(-1));
+ this.extendStrokeEdge(this.outline.at(this.outline.length / 2), this.outline.at((this.outline.length / 2) - 1 ));
+ },
+
+ extendStrokeEdge: function(p1, p2) {
+ p1.inHandle = this.extendHandle(p1, p1, p2);
+ p2.outHandle = this.extendHandle(p2, p1, p2);
+ },
+
+ extendHandle: function(point, p1, p2) {
+ return point.subtract(p2.subtract(p1).rotate(90).multiply(0.5));
+ },
+
+ smoothOutline: function(factor) {
+ var outline = this.outline;
+ outline.forEach(function(point, i) {
+ var nextSegment = outline.at(i + 1).subtract(point);
+ var lastSegment = point.subtract(outline.at(i - 1));
+ point.inHandle = point.subtract(nextSegment.length(lastSegment.length() / factor));
+ point.outHandle = point.add(lastSegment.length(nextSegment.length() / factor));
+ });
+ },
+ */
+
+ // Simple moving average
+
+ smoothPoints: function(factor) {
+ if(this.smoothed) {
+ return;
+ }
+ factor = factor || 5;
+ var average = [];
+ this.points.slice(0,-1).forEach(function(p) {
+ var xSum = 0, ySum = 0, n = factor;
+ average.push(new Point(p.x, p.y));
+ if(average.length > factor) {
+ // Remove first element
+ average.splice(0, 1);
+ }
+ average.forEach(function(p) {
+ xSum += p.x;
+ ySum += p.y;
+ });
+ if(average.length < factor) {
+ n = average.length;
+ }
+ p.x = xSum / n;
+ p.y = ySum / n;
+ });
+ this.smoothed = true;
+ },
+
+ onMouseEnter: function(evt) {
+ this.broadcast('StrokeEnter', this);
+ },
+
+ onMouseLeave: function(e) {
+ this.broadcast('StrokeLeave', this);
+ },
+
+ onClick: function(e) {
+ this.broadcast('StrokeClick', this);
+ },
+
+ drawFrame: function(frame) {
+ var ctx = this.context;
+ this.broadcast('BeforeFrame', this);
+ this.color.set(ctx);
+ this.shadow.set(ctx);
+ this.drawBrush(frame);
+ if(this.options.pressurePoint) {
+ this.drawPressurePoint(frame);
+ }
+ },
+
+ /* DELETE
+ drawFrameOutline: function(frame) {
+ var i, len, points, ctx = this.context;
+ this.broadcast('BeforeFrame', this);
+ this.color.set(ctx);
+ this.shadow.set(ctx);
+ points = this.outline.slice(0, frame).concat(this.outline.slice(-frame));
+ ctx.beginPath();
+ points.last().move(ctx);
+ for(i = 0, len = points.length; i < len; i++) {
+ points[i].draw(ctx, points.at(i - 1));
+ }
+ ctx.closePath();
+ ctx.fill();
+ if(this.pressurePoint) {
+ this.drawPressurePoint(frame);
+ }
+ },
+ */
+
+ drawLine: function() {
+ var self = this;
+ var ctx = this.context;
+ this.style.set(ctx);
+ ctx.beginPath();
+ this.points.forEach(function(p) {
+ if (this.scale > 1) {
+ p = p.multiply(this.scale);
+ }
+ p.draw(ctx);
+ }, this);
+ ctx.stroke();
+ },
+
+ drawPressurePoint: function(frame) {
+ var ctx = this.context, x, y;
+ var point = this.points[frame];
+ if(point) {
+ this.pressurePointColor.set(ctx);
+ ctx.beginPath();
+ x = point.x * this.scale;
+ y = point.y * this.scale;
+ ctx.arc(x, y, this.pressurePointRadius, 0, Math.PI * 2, true);
+ ctx.fill();
+ }
+ },
+
+ draw: function() {
+ this.drawBrush();
+ },
+
+ drawBrush: function(to) {
+ var ctx = this.context,
+ brush = this.options.brush,
+ w = brush.width * this.brushScale,
+ h = brush.height * this.brushScale,
+ xOffset = w / 2,
+ yOffset = h / 2;
+ if (to === undefined) {
+ to = this.points.length;
+ }
+ for(var i = 0; i < to; i++) {
+ var t = Math.round(i / this.points.length * 100) / 100;
+ var p = this.points[i];
+ var x = (p.x * this.scale) - xOffset;
+ var y = (p.y * this.scale) - yOffset;
+ ctx.save()
+ ctx.translate(x, y);
+ if (this.brushRotation) {
+ ctx.rotate(KanjiStroke.getRotationForTime(this.codepoint, t));
+ }
+ ctx.drawImage(brush, 0, 0, w, h);
+ ctx.restore();
+ }
+ },
+
+ drawPoints: function(to) {
+ var i, ctx = this.context;
+ if (to === undefined) {
+ to = this.points.length;
+ }
+ //this.darkColor.set(ctx);
+ for(i = 0; i < to; i++) {
+ this.points[i].drawPoint(ctx, 4);
+ }
+ },
+
+ drawNumber: function() {
+ var ctx = this.context, point = this.outline[this.outline.length / 2];
+ // Not exactly 0 as strokes overlap, but fallback.
+ point = this.points[3] || this.points[0];
+ var radius = this.numberRadius;
+ var fontSize = radius + 1;
+ var xOffset = fontSize / 3;
+ var yOffset = fontSize / 3;
+ ctx.moveTo(point.x, point.y);
+ this.numberCircleShadow.set(ctx);
+ this.numberCircleColor.set(ctx);
+ ctx.beginPath();
+ ctx.arc(point.x, point.y, radius, 0, Math.PI * 2, true);
+ ctx.closePath();
+ ctx.fill();
+ this.numberTextShadow.set(ctx);
+ this.numberTextColor.set(ctx);
+ ctx.font = 'bold '+ fontSize + 'px sans-serif';
+ ctx.fillText(this.number, point.x - xOffset, point.y + yOffset);
+ },
+
+ drawArrow: function() {
+ var ctx, point, first, next, vector;
+ ctx = this.context;
+ first = this.points[0];
+ next = this.points[5] || this.points[3] || this.points[1];
+ vector = next.subtract(first);
+ ctx.save();
+ ctx.translate(first.x, first.y);
+ ctx.rotate(Point.degToRad(vector.angle()));
+ ctx.translate(-first.x, -first.y);
+ ctx.beginPath();
+ ctx.fillStyle = 'rgba(255,0,0,0.3)';
+ point = first.add(new Point(0, -15)).draw(ctx);
+ point = point.add(new Point(12, 0)).draw(ctx);
+ point = point.add(new Point(0, -4)).draw(ctx);
+ point = point.add(new Point(10, 7)).draw(ctx);
+ point = point.add(new Point(-10, 7)).draw(ctx);
+ point = point.add(new Point(0, -4)).draw(ctx);
+ point = point.add(new Point(-12, 0)).draw(ctx);
+ ctx.closePath();
+ ctx.fill();
+ ctx.restore();
+ },
+
+ drawOutline: function(handles, guides) {
+ var i, points, len, ctx = this.context;
+ this.color.set(ctx);
+ points = this.outline;
+ len = points.length;
+ ctx.beginPath();
+ points.last().move(ctx);
+ for(i = 0; i < len; i++) {
+ points.at(i).draw(ctx, points.at(i - 1), i);
+ }
+ ctx.closePath();
+ ctx.fill();
+ if(guides) {
+ for(i = 0; i < len; i++) {
+ points.at(i).drawGuide(ctx, i);
+ }
+ }
+ if(handles) {
+ for(i = 0; i < len; i++) {
+ if(points[i].drawHandles) {
+ points[i].drawHandles(ctx, i / len);
+ }
+ }
+ }
+ }
+
+});
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// Keeping this down here as it makes my MacVim very slow :)
+
+KanjiStroke.extend({
+
+ BRUSH_ROTATION: {
+ 'default': '0:0 1:0',
+ // ㇑
+ '31d1': '0:0 .5:-10 1:0',
+ // ㇐
+ '31d0': '0:0 .5:-15 1:0',
+ // ㇒
+ '31d2': '0:-45 1:-55',
+ // ㇏
+ '31cf': '0:-15 .05:0 .6:-20 1:-45',
+ },
+
+ LINE_GUIDES: {
+ 'default': '0 L100',
+ // 2nd
+ '31c4': '0 L100',
+ '31d4': '0 L100',
+ // 儿 2nd
+ '31df': '0 L100',
+ // 日 2nd
+ '31d5': '0 L100,35 L150,40 L120,60 L50',
+ // 今 4th
+ '31d6': '0 L100',
+ // 乃 2nd
+ '31cc': '0 L100',
+ // 扌 3rd
+ '31c0': '0 L100',
+ // 扌 2nd
+ '31da': '0 L100',
+ // 厂 1st
+ '31d0': '0 L130,20 L100',
+ // 亻 2nd
+ '31d1': '0 L120,100 L80',
+ // 亻 1st
+ '31d2': '0 L140,100 L40',
+ // 又 1st
+ '31c7': '0 L100',
+ // 又 2nd
+ '31cf': '0 L100,80 L180,100 L80'
+ }
+
+});
+/** noUiSlider
+ ** @author: Léon Gersen
+ ** @documentation: http://refreshless.com/nouislider/
+ **/
+/*jslint browser: true, devel: true, plusplus: true, white: true, unparam: true */
+
+(function( $, undefined ){
+
+ "use strict";
+
+ if ( $.zepto && !$.fn.removeData ) {
+ throw new ReferenceError("Zepto is loaded without the data module.");
+ }
+
+ $.fn.noUiSlider = function( options ){
+
+ var namespace = '.nui'
+ // Create a shorthand for document event binding.
+ ,all = $(document)
+ // Create a map of touch and mouse actions.
+ ,actions = {
+ start: 'mousedown touchstart'
+ ,move: 'mousemove touchmove'
+ ,end: 'mouseup touchend'
+ }
+ // Make a copy of the current 'val' function.
+ ,$VAL = $.fn.val
+ // Define a set of standard HTML classes for
+ // the various structures noUiSlider uses.
+ ,clsList = [
+ 'noUi-base' // 0
+ ,'noUi-origin' // 1
+ ,'noUi-handle' // 2
+ ,'noUi-input' // 3
+ ,'noUi-active' // 4
+ ,'noUi-state-tap' // 5
+ ,'noUi-target' // 6
+ ,'-lower' // 7
+ ,'-upper' // 8
+ ,'noUi-connect' // 9
+ ,'noUi-vertical' // 10
+ ,'noUi-horizontal' // 11
+ ,'noUi-background' // 12
+ ,'noUi-z-index' // 13
+ ]
+ // Define an extendible object with base classes for the various
+ // structure elements in the slider. These can be extended by simply
+ // pushing to the array, which reduces '.addClass()' calls.
+ ,stdCls = {
+ base: [clsList[0]]
+ ,origin: [clsList[1]]
+ ,handle: [clsList[2]]
+ }
+ // This object contains some well tested functions to convert
+ // values to and from percentages. It can be a bit strange to wrap
+ // your head around the individual calls, but they'll do their job
+ // with all positive and negative input values.
+ ,percentage = {
+ to: function ( range, value ) {
+ value = range[0] < 0 ? value + Math.abs(range[0]) : value - range[0];
+ return (value * 100) / this.len(range);
+ }
+ ,from: function ( range, value ) {
+ return (value * 100) / this.len(range);
+ }
+ ,is: function ( range, value ) {
+ return ((value * this.len(range)) / 100) + range[0];
+ }
+ ,len: function ( range ) {
+ return (range[0] > range[1] ? range[0] - range[1] : range[1] - range[0]);
+ }
+ }
+ // Event handlers bound to elements to perform basic tasks.
+ ,eventHandlers = [
+ // Assign input field values to the slider,
+ // and signal unevaluated input.
+ function ( ) {
+
+ this.target.val([
+ !this.i ? this.val() : null
+ , this.i ? this.val() : null
+ ], { trusted: false });
+
+ }
+ // Shorthand for stopping propagation on an object.
+ // Calling a function prevents having to define
+ // one within other code.
+ ,function ( e ) {
+ e.stopPropagation();
+ }
+ ];
+
+ // When the browser supports MsPointerEvents,
+ // don't bind touch or mouse events. The touch events are
+ // currently only implemented by IE10, but they are stable
+ // and convenient to use. IE11 implements pointerEvents without
+ // a prefix, which breaks compatibility with the IE10 implementation.
+ if( window.navigator.pointerEnabled ) {
+ actions = {
+ start: 'pointerdown'
+ ,move: 'pointermove'
+ ,end: 'pointerup'
+ };
+ } else if ( window.navigator.msPointerEnabled ) {
+ actions = {
+ start: 'MSPointerDown'
+ ,move: 'MSPointerMove'
+ ,end: 'MSPointerUp'
+ };
+ }
+
+ // Test an array of objects, and calls them if they are a function.
+ function call ( f, scope, args ) {
+
+ // Allow the passing of an unwrapped function.
+ // Leaves other code a more comprehensible.
+ if( !$.isArray(f) ){
+ f = [f];
+ }
+
+ $.each(f,function(i,q){
+ if (typeof q === "function") {
+ q.call(scope, args);
+ }
+ });
+ }
+
+ function instance ( object ) {
+ return object instanceof $ || ( $.zepto && $.zepto.isZ ( object ) );
+ }
+
+ function fixEvent ( e ) {
+
+ // Required (in at the very least Chrome) to prevent
+ // scrolling and panning while attempting to slide.
+ // The tap event also depends on this. This doesn't
+ // seem to prevent panning in Firefox, which is an issue.
+ // Prevent-default will also stop Chrome from setting a text-cursor.
+ e.preventDefault();
+
+ // Filter the event to register the type,
+ // which can be touch, mouse or pointer. Since noUiSlider 4
+ // so longer binds touch OR mouse, but rather touch AND mouse,
+ // offset changes need to be made on an event specific basis.
+ var touch = e.type.indexOf('touch') === 0
+ ,mouse = e.type.indexOf('mouse') === 0
+ ,pointer = e.type.indexOf('pointer') === 0
+ ,x,y, event = e;
+
+ // IE10 implemented pointer events with a prefix,
+ // so we'll needs to check for those, too.
+ if ( e.type.indexOf('MSPointer') === 0 ) {
+ pointer = true;
+ }
+
+ // Get the originalEvent, if the event has been wrapped
+ // by jQuery. Zepto doesn't wrap the event.
+ if ( e.originalEvent ) {
+ e = e.originalEvent;
+ }
+
+ if ( touch ) {
+ // noUiSlider supports one movement at a time, for now.
+ // It is therefore safe to select the first 'changedTouch'.
+ x = e.changedTouches[0].pageX;
+ y = e.changedTouches[0].pageY;
+ }
+ if ( mouse || pointer ) {
+
+ // Polyfill the pageXOffset and pageYOffset
+ // variables for IE7 and IE8;
+ if( !pointer && window.pageXOffset === undefined ){
+ window.pageXOffset = document.documentElement.scrollLeft;
+ window.pageYOffset = document.documentElement.scrollTop;
+ }
+
+ x = e.clientX + window.pageXOffset;
+ y = e.clientY + window.pageYOffset;
+ }
+
+ return $.extend( event, { x:x, y:y } );
+
+ }
+
+ // Handler for attaching events trough a proxy
+ function attach ( events, target, callback, scope, noAbstraction ) {
+
+ // Add the noUiSlider namespace to all events.
+ events = events.replace( /\s/g, namespace + ' ' ) + namespace;
+
+ // The 'noAbstraction' argument can be set to prevent
+ // event checking, and instead just proxy the event to
+ // the right namespace. 'noAbstraction' can be level 1 or 2.
+ if ( noAbstraction ) {
+ if ( noAbstraction > 1 ){
+ scope = $.extend(target, scope);
+ }
+ return target.on( events, $.proxy( callback, scope ));
+ }
+
+ // Make the callback available in a lower scope
+ scope.handler = callback;
+
+ return target.on( events, $.proxy( function( e ){
+
+ // Test if there is anything that should prevent an event
+ // from being handled, such as a disabled state or an active
+ // 'tap' transition. Prevent interaction with disabled sliders.
+ if( this.target.is('[class*="noUi-state-"], [disabled]') ) {
+ return false;
+ }
+
+ // Call the event handler with the original event as argument.
+ // The handler won't know it has been passed trough this
+ // proxy, and it won't have to filter event validity, because
+ // that was done here. Since the scope can just be 'this',
+ // there is no need to use .call().
+ this.handler( fixEvent ( e ) );
+
+ }, scope ));
+ }
+
+ // Checks whether a variable is numerical.
+ function isNumeric ( a ) {
+ return !isNaN( parseFloat( a ) ) && isFinite( a );
+ }
+
+ // jQuery doesn't have a method to return a CSS value as a percentage.
+ function getPercentage( a ){
+ return parseFloat(this.style[a]);
+ }
+
+ function test ( o, set ){
+
+ // Checks whether a variable is a candidate to be a
+ // valid serialization target.
+ function ser(r){
+ return ( instance ( r ) || typeof r === 'string' || r === false );
+ }
+
+ // These tests are structured with an item for every option available.
+ // Every item contains an 'r' flag, which marks a required option, and
+ // a 't' function, which in turn takes some arguments:
+ // - the value for the option
+ // - [optional] a reference to options object
+ // - [optional] the option name
+ // The testing function returns false when an error is detected,
+ // or true when everything is OK. Every test also has an 'init'
+ // method which appends the parent object to all children.
+
+ var TESTS = {
+ /* Handles.
+ * Has default, can be 1 or 2.
+ */
+ "handles": {
+ r: true
+ ,t: function(q){
+ q = parseInt(q, 10);
+ return ( q === 1 || q === 2 );
+ }
+ }
+ /* Range.
+ * Must be an array of two numerical floats,
+ * which can't be identical.
+ */
+ ,"range": {
+ r: true
+ ,t: function(q,o,w){
+ if(q.length!==2){
+ return false;
+ }
+ // Reset the array to floats
+ q = [parseFloat(q[0]),parseFloat(q[1])];
+ // Test if those floats are numerical
+ if(!isNumeric(q[0])||!isNumeric(q[1])){
+ return false;
+ }
+ // When this test is run for range, the values can't
+ // be identical.
+ if(w==="range" && q[0] === q[1]){
+ return false;
+ }
+ // The lowest value must really be the lowest value.
+ if(q[1] 100 ? 100 : to;
+
+ // Handle the step option, or ignore it.
+ if( nui.step && !forgive ){
+ to = closest( to, percentage.from(nui.range, nui.step));
+ }
+
+ // Stop handling this call if the handle won't step to a new value.
+ if( to === handle[0].gPct(style) ) {
+ return false;
+ }
+
+ // We're done if this is the only handle,
+ // if the handle bounce is trusted to the user
+ // or on initialisation when handles isn't defined yet.
+ if( handle.siblings('.' + clsList[1]).length && !forgive && handles ){
+
+ // Otherwise, the handle should bounce,
+ // and stop at the other handle.
+ if ( handle.data('nui').number ) {
+ hLimit = handles[0][0].gPct(style) + nui.margin;
+ to = to < hLimit ? hLimit : to;
+ } else {
+ hLimit = handles[1][0].gPct(style) - nui.margin;
+ to = to > hLimit ? hLimit : to;
+ }
+
+ // Stop handling this call if the handle can't move past another.
+ if( to === handle[0].gPct(style) ) {
+ return false;
+ }
+
+ }
+
+ // Fix for the z-index issue where the lower handle gets stuck
+ // below the upper one. Since this function is called for every
+ // movement, toggleClass cannot be used.
+ if( handle.data('nui').number === 0 && to > 95 ){
+ handle.addClass(clsList[13]);
+ } else {
+ handle.removeClass(clsList[13]);
+ }
+
+ // Set handle to new location
+ handle.css( style , to + '%' );
+
+ // Write the value to the serialization object.
+ handle.data('store').val(
+ format ( percentage.is( nui.range, to ), handle.data('nui').target )
+ );
+
+ return true;
+
+ }
+
+ function store ( handle, S ) {
+
+ var i = handle.data('nui').number, scope = {
+ target: handle.data('nui').target
+ ,options: handle.data('nui').options
+ ,handle: handle
+ ,i: i
+ };
+
+ if( instance ( S.to[i] ) ) {
+
+ // Add a change event to the supplied jQuery object, which
+ // will just trigger the 'val' function on the parent. In some
+ // cases, the change event will not fire on select elements,
+ // so listen to 'blur' too.
+ attach ( 'change blur'
+ ,S.to[i]
+ ,eventHandlers[0]
+ ,scope
+ ,2 );
+
+ // Triggering the 'set' callback should not occur on the 'blur'
+ // event, so bind it only to 'change'.
+ attach ( 'change'
+ ,S.to[i]
+ ,scope.options.set
+ ,scope.target
+ ,1 );
+
+ return S.to[i];
+ }
+
+ if ( typeof S.to[i] === "string" ) {
+
+ // Append a new object to the noUiSlider base,
+ // prevent change events flowing upward.
+ return $(' ')
+ .appendTo(handle)
+ .addClass(clsList[3])
+ .change(eventHandlers[1]);
+ }
+
+ if ( S.to[i] === false ) {
+
+ // Create an object capable of handling all jQuery calls.
+ return {
+ // The value will be stored a data on the handle.
+ val : function(a) {
+ // Value function provides a getter and a setter.
+ // Can't just test for !a, as a might be 0.
+ // When no argument is provided, return the value.
+ // Otherwise, set the value.
+ if ( a === undefined ) {
+ return this.handleElement.data('nui-val');
+ }
+ this.handleElement.data('nui-val', a);
+ }
+ // The object could be mistaken for a jQuery object,
+ // make sure that doesn't trigger any errors.
+ ,hasClass: function(){
+ return false;
+ }
+ // The val function needs access to the handle.
+ ,handleElement: handle
+ };
+ }
+ }
+
+ function move( event ) {
+
+ // This function is called often, keep it light.
+
+ var base = this.base
+ ,style = base.data('style')
+ // Subtract the initial movement from the current event,
+ // while taking vertical sliders into account.
+ ,proposal = event.x - this.startEvent.x
+ ,baseSize = style === 'left' ? base.width() : base.height();
+
+ // This loop prevents a long ternary for the proposal variable.
+ if( style === 'top' ) {
+ proposal = event.y - this.startEvent.y;
+ }
+
+ proposal = this.position + ( ( proposal * 100 ) / baseSize );
+
+ setHandle( this.handle, proposal );
+
+ // Trigger the 'slide' event, pass the target so that it is 'this'.
+ call( base.data('options').slide
+ ,base.data('target') );
+
+ }
+
+ function end ( ) {
+
+ var base = this.base
+ ,handle = this.handle;
+
+ // The handle is no longer active, so remove
+ // the class.
+ handle.children().removeClass(clsList[4]);
+
+ // Unbind move and end events, to prevent
+ // them stacking up over and over;
+ all.off(actions.move);
+ all.off(actions.end);
+
+ // Some text-selection events are bound to the body.
+ $('body').off(namespace);
+
+ // Trigger the change event.
+ base.data('target').change();
+
+ // Trigger the 'end' callback.
+ call( handle.data('nui').options.set
+ ,base.data('target') );
+
+ }
+
+ function start ( event ) {
+
+ var handle = this.handle
+ ,position = handle[0].gPct( handle.data('nui').style );
+
+ handle.children().addClass(clsList[4]);
+
+ // Attach the move event handler, while
+ // passing all relevant information along.
+ attach ( actions.move, all, move, {
+ startEvent: event
+ ,position: position
+ ,base: this.base
+ ,target: this.target
+ ,handle: handle
+ });
+
+ attach ( actions.end, all, end, {
+ base: this.base
+ ,target: this.target
+ ,handle: handle
+ });
+
+ // Prevent text selection when dragging the handles.
+ // This doesn't prevent the browser defaulting to the I like cursor.
+ $('body').on(
+ 'selectstart' + namespace
+ ,function( ){ return false; }
+ );
+ }
+
+ function selfEnd( event ) {
+
+ // Stop propagation so that the tap handler doesn't interfere;
+ event.stopPropagation();
+
+ // Trigger the end handler. Supply the current scope,
+ // which contains all required information.
+ end.call( this );
+ }
+
+ function tap ( event ) {
+
+ // If the target contains an active handle, don't trigger
+ // this event. Tapping shouldn't be possible while dragging.
+ if ( this.base.find('.' + clsList[4]).length ) {
+ return;
+ }
+
+ // Getting variables from the event is not required, but
+ // shortens other expressions and is far more convenient;
+ var i, handle, hCenter, base = this.base
+ ,handles = this.handles
+ ,style = base.data('style')
+ ,eventXY = event[style === 'left' ? 'x' : 'y']
+ ,baseSize = style === 'left' ? base.width() : base.height()
+ ,offset = {
+ handles: []
+ ,base: {
+ left: base.offset().left
+ ,top: base.offset().top
+ }
+ };
+
+ // Loop handles and add data to the offset list.
+ for (i = 0; i < handles.length; i++ ) {
+ offset.handles.push({
+ left: handles[i].offset().left
+ ,top: handles[i].offset().top
+ });
+ }
+
+ // Calculate the central point between the handles;
+ hCenter = handles.length === 1 ? 0 :
+ (( offset.handles[0][style] + offset.handles[1][style] ) / 2 );
+
+ // If there is just one handle,
+ // or the lower handles in closest to the event,
+ // select the first handle. Otherwise, pick the second.
+ if ( handles.length === 1 || eventXY < hCenter ){
+ handle = handles[0];
+ } else {
+ handle = handles[1];
+ }
+
+ // Flag the slider as it is now in a transitional state.
+ // Transition takes 300 ms, so re-enable the slider afterwards.
+ base.addClass(clsList[5]);
+ setTimeout(function(){
+ base.removeClass(clsList[5]);
+ }, 300);
+
+ // Calculate the new position for the handle and
+ // trigger the movement.
+ setHandle(
+ handle
+ ,(((eventXY - offset.base[style]) * 100) / baseSize)
+ );
+
+ // Trigger the 'slide' and 'set' callbacks,
+ // pass the target so that it is 'this'.
+ call( [ handle.data('nui').options.slide
+ ,handle.data('nui').options.set ]
+ ,base.data('target') );
+
+ base.data('target').change();
+
+ }
+
+ function create ( options ) {
+
+ return this.each(function( index, target ){
+
+ // Target is the wrapper that will receive all external
+ // scripting interaction. It has no styling and serves no
+ // other function.
+ target = $(target);
+ target.addClass(clsList[6]);
+
+ // Base is the internal main 'bar'.
+ var i, style, decimals, handle
+ ,base = $('
').appendTo(target)
+ ,handles = []
+ ,cls = {
+ base: stdCls.base
+ ,origin: [
+ stdCls.origin.concat([clsList[1] + clsList[7]])
+ ,stdCls.origin.concat([clsList[1] + clsList[8]])
+ ]
+ ,handle: [
+ stdCls.handle.concat([clsList[2] + clsList[7]])
+ ,stdCls.handle.concat([clsList[2] + clsList[8]])
+ ]
+ };
+
+ // Set defaults where applicable;
+ options = $.extend({
+ handles: 2
+ ,margin: 0
+ ,orientation: "horizontal"
+ }, options) || {};
+
+ // Set a default for serialization;
+ if(!options.serialization){
+ options.serialization = {
+ to : [false, false]
+ ,resolution : 0.01
+ ,mark: '.'
+ };
+ }
+
+ // Run all options through a testing mechanism to ensure correct
+ // input. The test function will throw errors, so there is
+ // no need to capture the result of this call. It should be noted
+ // that options might get modified to be handled properly. E.g.
+ // wrapping integers in arrays.
+ test(options, target);
+
+ // I can't type serialization any more, and it doesn't compress
+ // very well, so shorten it.
+ options.S = options.serialization;
+
+ // Apply the required connection classes to the elements
+ // that need them. Some classes are made up for several segments
+ // listed in the class list, to allow easy renaming and provide
+ // a minor compression benefit.
+ if( options.connect ) {
+
+ if( options.connect === "lower" ){
+ // Add some styling classes to the base;
+ cls.base.push(clsList[9], clsList[9] + clsList[7]);
+ // When using the option 'Lower', there is only one
+ // handle, and thus only one origin.
+ cls.origin[0].push(clsList[12]);
+ } else {
+ cls.base.push(clsList[9] + clsList[8], clsList[12]);
+ cls.origin[0].push(clsList[9]);
+ }
+
+ } else {
+ cls.base.push(clsList[12]);
+ }
+
+ // Parse the syntactic sugar that is the serialization
+ // resolution option to a usable integer.
+ style = options.orientation === 'vertical' ? 'top' : 'left';
+
+ decimals = options.S.resolution.toString().split('.');
+
+ // Checking for a string "1", since the resolution needs
+ // to be cast to a string to split in on the period.
+ decimals = decimals[0] === "1" ? 0 : decimals[1].length;
+
+ // Add classes for horizontal and vertical sliders.
+ // The horizontal class is provided for completeness,
+ // as it isn't used in the default theme.
+ if( options.orientation === "vertical" ){
+ cls.base.push(clsList[10]);
+ } else {
+ cls.base.push(clsList[11]);
+ }
+
+ // Merge base classes with default;
+ base.addClass(cls.base.join(" ")).data('target', target);
+
+ // Make data accessible in functions throughout the plugin.
+ target.data({
+ base: base
+ ,mark: options.S.mark
+ ,decimals: decimals
+ });
+
+ for (i = 0; i < options.handles; i++ ) {
+
+ handle = $('').appendTo(base);
+
+ // Add all default and option-specific classes to the
+ // origins and handles.
+ handle.addClass(cls.origin[i].join(" "));
+ handle.children().addClass(cls.handle[i].join(" "));
+
+ // These events are only bound to the visual handle element,
+ // not the 'real' origin element.
+ attach ( actions.start, handle.children(), start, {
+ base: base
+ ,target: target
+ ,handle: handle
+ });
+
+ attach ( actions.end, handle.children(), selfEnd, {
+ base: base
+ ,target: target
+ ,handle: handle
+ });
+
+ // Make sure every handle has access to all primary
+ // variables. Can't uses jQuery's .data( obj ) structure
+ // here, as 'store' needs some values from the 'nui' object.
+ handle.data('nui', {
+ target: target
+ ,decimals: decimals
+ ,options: options
+ ,base: base
+ ,style: style
+ ,number: i
+ }).data('store', store (
+ handle
+ ,options.S
+ ));
+
+ // Write a function to the native DOM element, since
+ // jQuery wont let me get the current value in percentages.
+ handle[0].gPct = getPercentage;
+
+ // Make handles loop-able
+ handles.push(handle);
+
+ // Set the handle to its initial position;
+ setHandle(handle, percentage.to(options.range, options.start[i]));
+
+ }
+
+ // The base could use the handles too;
+ base.data({
+ options: options
+ ,handles: handles
+ ,style: style
+ });
+
+ // Add a reference to the handles on the target as well.
+ target.data({
+ handles: handles
+ });
+
+ // Attach the the tap event to the slider base.
+ attach ( actions.end, base, tap, {
+ base: base
+ ,target: target
+ ,handles: handles
+ });
+
+ });
+
+ }
+
+ function getValue ( ) {
+
+ var re = [];
+
+ // Loop the handles, and get the value from the input
+ // for every handle on its' own.
+ $.each( $(this).data('handles'), function( i, handle ){
+ re.push( handle.data('store').val() );
+ });
+
+ // If the slider has just one handle, return a single value.
+ // Otherwise, return an array.
+ return ( re.length === 1 ? re[0] : re );
+ }
+
+ function val ( args, modifiers ) {
+
+ // If the function is called without arguments,
+ // act as a 'getter'. Call the getValue function
+ // in the same scope as this call.
+ if( args === undefined ){
+ return getValue.call( this );
+ }
+
+ // Passing the modifiers argument is not required.
+ // The input might also be 'true', to indicate that the
+ // 'set' event should be called.
+ modifiers = modifiers === true ? { trigger: true } : ( modifiers || {} );
+
+ // If the val is to be set to a number, which is valid
+ // when using a one-handle slider, wrap it in an array.
+ if( !$.isArray(args) ){
+ args = [args];
+ }
+
+ // Setting is handled properly for each slider in the data set.
+ // Note that the val method is called on the target, which can
+ // therefore be used in the function.
+ return this.each(function( index, target ){
+
+ // Make sure 'target' is a jQuery element.
+ target = $(target);
+
+ $.each( $(this).data('handles'), function( j, handle ){
+
+ // The set request might want to ignore this handle.
+ // Test for 'undefined' too, as a two-handle slider
+ // can still be set with an integer.
+ if( args[j] === null || args[j] === undefined ) {
+ return;
+ }
+
+ // Calculate a new position for the handle.
+ var value, current
+ ,range = handle.data('nui').options.range
+ ,to = args[j], result;
+
+ // Assume the input can be trusted.
+ modifiers.trusted = true;
+
+ // Handle user facing input correction. The value is
+ // 'trusted' when a developer provides it from the 'val'
+ // method, not when it comes from an input element.
+ if ( modifiers.trusted === false || args.length === 1 ) {
+ modifiers.trusted = false;
+ }
+
+ // If one handle isn't set, the other can't move past it.
+ if ( args.length === 2 && $.inArray( null, args ) >= 0 ) {
+ modifiers.trusted = false;
+ }
+
+ // Add support for the comma (,) as a decimal symbol.
+ // Replace it by a period so it is handled properly by
+ // parseFloat. Omitting this would result in a removal
+ // of decimals. This is relevant on trusted input too,
+ // as a developer might input a comma separated string
+ // using the 'val' method.
+ if( $.type(to) === "string" ) {
+ to = to.replace(',', '.');
+ }
+
+ // Calculate the new handle position
+ to = percentage.to( range, parseFloat( to ) );
+
+ // Set handle to new location, and make sure developer
+ // input is always accepted. The 'trusted' flag indicates
+ // input that is not coming from user facing elements.
+ result = setHandle( handle, to, modifiers.trusted );
+
+ // The 'val' method allows for an external modifier,
+ // to specify a request for an 'set' event.
+ if( modifiers.trigger ) {
+ call( handle.data('nui').options.set
+ ,target );
+ }
+
+ // If the value of the input doesn't match the slider,
+ // reset it.
+ if( !result ){
+
+ // Get the 'store' object, which can be an input
+ // element or a wrapper around a 'data' call.
+ value = handle.data('store').val();
+
+ // Get the value for the current position.
+ current = percentage.is(
+ range
+ ,handle[0].gPct(handle.data('nui').style)
+ );
+
+ // Sometimes the input is changed to a value the slider
+ // has rejected. This can occur when using 'select' or
+ // 'input[type="number"]' elements. In this case,
+ // set the value back to the input.
+ if( value !== current ){
+ handle.data('store').val( format( current, target ) );
+ }
+ }
+ });
+ });
+ }
+
+ // Overwrite the native jQuery 'val' function
+ // with a simple handler. noUiSlider will use the internal
+ // value method, anything else will use the standard method.
+ $.fn.val = function(){
+ return this.hasClass(clsList[6])
+ ? val.apply(this, arguments)
+ : $VAL.apply(this, arguments);
+ };
+
+ return create.call( this, options );
+
+ };
+
+}( $ ));
+
+
+
+
+
+
+
+
+
+
+
+
+(function() {
+
+
+}).call(this);
+(function() {
+ var ListAdder;
+
+ ListAdder = (function() {
+ function ListAdder() {
+ this.init_show_link();
+ this.init_add_link();
+ this.init_remove_link();
+ }
+
+ ListAdder.prototype.init_show_link = function() {
+ return $('.show_list_adder_link').on('click', function(e) {
+ var add_to_existing, adder, form_ele, link, list, object_id, type, _i, _len;
+ e.preventDefault();
+ type = $(this).attr('data-type');
+ object_id = $(this).attr('data-object-id');
+ adder = $(this).siblings('.add_to_list_adder').first();
+ if (adder.length > 0) {
+ return adder.remove();
+ } else {
+ if (user_lists.length > 0) {
+ add_to_existing = "";
+ for (_i = 0, _len = user_lists.length; _i < _len; _i++) {
+ list = user_lists[_i];
+ link = "Add to " + list.name + " ";
+ add_to_existing = add_to_existing + link;
+ }
+ add_to_existing = add_to_existing + " ";
+ } else {
+ add_to_existing = "";
+ }
+ $(this).parent().append("\n " + add_to_existing + "\n Add to a new list:\n
\n \n
\n
");
+ form_ele = $("#create_list_" + type + "_" + object_id);
+ return form_ele.on('submit', function(e) {
+ var description, name;
+ e.preventDefault();
+ name = form_ele.children('input[name=name]').first().val();
+ description = form_ele.children('input[name=description]').first().val();
+ return $.ajax("/lists", {
+ data: {
+ 'object_id': object_id,
+ 'type': type,
+ 'list[name]': name,
+ 'list[description]': description
+ },
+ type: 'POST',
+ dataType: 'json',
+ error: function(jqXHR, textStatus, errorThrown) {
+ form_ele.parent().html("Something went wrong :/ ");
+ return console.log("Error: " + textStatus);
+ },
+ success: function(data, textStatus, jqXHR) {
+ return form_ele.parent().html("Added! ");
+ }
+ });
+ });
+ }
+ });
+ };
+
+ ListAdder.prototype.init_add_link = function() {
+ return $('.add_to_list_link').on('click', function(e) {
+ var link, list_id, object_id, type;
+ e.preventDefault();
+ type = $(this).attr('data-type');
+ object_id = $(this).attr('data-object-id');
+ list_id = $(this).attr('data-list-id');
+ link = $(this);
+ return $.ajax("/lists/" + list_id, {
+ data: {
+ add: true,
+ object_id: object_id,
+ type: type,
+ _method: 'PATCH'
+ },
+ type: 'POST',
+ dataType: 'json',
+ error: function(jqXHR, textStatus, errorThrown) {
+ link.parent().html("Something went wrong :/ ");
+ return console.log("Error: " + textStatus);
+ },
+ success: function(data, textStatus, jqXHR) {
+ return link.parent().html("Added! ");
+ }
+ });
+ });
+ };
+
+ ListAdder.prototype.init_remove_link = function() {
+ return $('.remove_from_list_link').on('click', function(e) {
+ var link, list_id, object_id, type;
+ e.preventDefault();
+ type = $(this).attr('data-type');
+ object_id = $(this).attr('data-object-id');
+ list_id = $(this).attr('data-list-id');
+ link = $(this);
+ return $.ajax("/lists/" + list_id, {
+ data: {
+ remove: true,
+ object_id: object_id,
+ type: type,
+ _method: 'PATCH'
+ },
+ type: 'POST',
+ dataType: 'json',
+ error: function(jqXHR, textStatus, errorThrown) {
+ link.parent().html("Something went wrong :/ ");
+ return console.log("Error: " + textStatus);
+ },
+ success: function(data, textStatus, jqXHR) {
+ return link.closest('.concept,.kanji_light_block').first().fadeOut(500, function() {
+ return $(this).remove();
+ });
+ }
+ });
+ });
+ };
+
+ return ListAdder;
+
+ })();
+
+ $(function() {});
+
+}).call(this);
+(function() {
+ var RadicalSetup, Representations, Searcher, Zen;
+
+ $.fn.log = function() {
+ console.log(this);
+ return this;
+ };
+
+ Searcher = (function() {
+ Searcher.search = function(query, live) {
+ $('.search').data('live', live);
+ $('.search').submit();
+ return $('#zen_bar div').html('');
+ };
+
+ Searcher.focus = function() {
+ var keyword;
+ keyword = $('.search .keyword').first();
+ keyword.focus();
+ if (keyword.select) {
+ return keyword.select();
+ }
+ };
+
+ function Searcher() {
+ if ($('#zen_bar').length > 0) {
+ $('#zen_bar').addClass('focus');
+ } else if ($('#new_discussion').length > 0) {
+ $('#discussion_title').focus();
+ } else {
+ if (!Modernizr.touch || (Modernizr.touch && !((new MobileDetect(window.navigator.userAgent)).mobile()))) {
+ Searcher.focus();
+ }
+ }
+ $('.search-form_clear-button_js').click(function(e) {
+ e.preventDefault();
+ $('.search .keyword').val("");
+ return Searcher.focus();
+ });
+ $('#search').submit(function(e) {
+ var query, token;
+ query = $('.search .keyword').val();
+ if (query.split(/\s+/).length > 5 || query.length > 50) {
+ $('.search').attr('method', 'POST');
+ token = $('meta[name="csrf-token"]').attr('content');
+ return $(this).prepend(" ");
+ } else {
+ e.preventDefault();
+ if ($('#main_results').length > 0 && $('.search').data('live')) {
+ return $.post("/search/" + (encodeURIComponent(query)), function(data) {
+ $('#main_results').replaceWith(data);
+ return window.history.pushState('', '', "/search/" + (encodeURIComponent(query)));
+ });
+ } else {
+ return window.location = "" + location.protocol + "//" + location.host + "/search/" + (encodeURIComponent(query));
+ }
+ }
+ });
+ }
+
+ return Searcher;
+
+ })();
+
+ Zen = (function() {
+ function Zen() {
+ $('#zen_bar a').on('click', function(e) {
+ var query;
+ e.preventDefault();
+ $('#zen_bar .current').each(function() {
+ return $(this).removeClass('current');
+ });
+ $(this).addClass('current');
+ query = $(this).attr('data-word');
+ return $.post("/search/\"" + (encodeURIComponent(query)) + "\"?zen=true", function(data) {
+ return $('#main_results').replaceWith(data);
+ });
+ });
+ $('.search .keyword').bind('focus', function(e) {
+ return $('#zen_bar').removeClass('focus');
+ });
+ $('#zen_bar').bind('click', function(e) {
+ return $('#zen_bar').addClass('focus');
+ });
+ $('.search .keyword').keydown(function(e) {
+ if (9 === e.keyCode && $('#ime:visible').length < 1) {
+ e.preventDefault();
+ $('#zen_bar').click();
+ return $('.search .keyword').blur();
+ }
+ });
+ $(document).keydown(function(e) {
+ if ($('#zen_bar').hasClass('focus')) {
+ switch (e.keyCode) {
+ case 37:
+ case 72:
+ case 65:
+ return $('.current').closest('li').prevAll().children('a').first().click();
+ case 39:
+ case 76:
+ case 68:
+ return $('.current').closest('li').nextAll().children('a').first().click();
+ }
+ }
+ });
+ }
+
+ return Zen;
+
+ })();
+
+ RadicalSetup = (function() {
+ function RadicalSetup() {
+ this.initialized = false;
+ $('#kanji_container a').on('click', function(e) {
+ e.preventDefault();
+ Searcher.search($(this).text(), true);
+ return $('#radical_search').remove();
+ });
+ $(document.body).click(function() {
+ return $('#radical_search').remove();
+ });
+ }
+
+ return RadicalSetup;
+
+ })();
+
+ Representations = (function() {
+ function Representations() {
+ $(document).on('mouseenter', '.representations .all .representation', function(event) {
+ var el, primary;
+ el = $(this);
+ primary = el.parents('.representations').find('.primary .representation');
+ el.siblings().removeClass('active');
+ el.addClass('active');
+ return primary.html(el.html());
+ });
+ $(document).on('mouseleave', '.representations', function(event) {
+ return $(this).find('.all .representation:first').mouseenter();
+ });
+ $(document).on('click', '.representations .all a', function(event) {
+ return event.preventDefault();
+ });
+ }
+
+ return Representations;
+
+ })();
+
+ $(document).ready(function() {
+ new Searcher;
+ new Search($('#search'));
+ new Zen;
+ new RadicalSetup;
+ new Representations;
+ return $('[title]').tooltip();
+ });
+
+}).call(this);
+
+(function($) {
+
+ $.fn.smartRange = function(options) {
+ var input, caret, range, hidden;
+
+ input = this;
+ options = options || {};
+ range = options.range || input.siblings('.range:first');
+ hidden = options.hidden || false;
+
+ function onBlur() {
+ caret = input.caret();
+ updateRange();
+ }
+
+ function onInteracted() {
+ caret = input.caret();
+ input.trigger('caret_changed');
+ range.hide();
+ }
+
+ function setRangeHidden() {
+ hidden = true;
+ range.hide();
+ }
+
+ function setRangeVisible() {
+ hidden = false;
+ updateRange();
+ }
+
+ function setupRange() {
+ range.css({
+ 'font-size': input.css('font-size'),
+ 'font-weight': input.css('font-weight')
+ });
+ caret = input.caret();
+ }
+
+ function updateRange() {
+ var val, wrapped;
+ if(caret.start === caret.end || hidden) {
+ range.hide();
+ } else {
+ val = input.val();
+ range.toggle(caret.start !== caret.end);
+ html = val.split('').map(function(l, i) {
+ var classAttr, selected;
+ selected = i >= caret.start && i < caret.end;
+ classAttr = selected ? 'selected character' : 'character';
+ return '' + l + ' ';
+ }).join('');
+ range.html(html).show();
+ }
+ }
+
+ function insert(str, select) {
+ var text = caret.replace(str);
+ caret.end = caret.start + str.length;
+ if(!select) {
+ caret.start = caret.end;
+ }
+ input.val(text);
+ if(input.is(':focus')) {
+ input.caret(caret);
+ }
+ updateRange();
+ }
+
+ function deselect() {
+ if(caret.start !== caret.end) {
+ caret.start = caret.end;
+ updateRange();
+ }
+ }
+
+ function selectAll() {
+ caret.start = 0;
+ caret.end = input.val().length;
+ updateRange();
+ }
+
+ input.on('blur', onBlur);
+ input.on('click keyup', onInteracted);
+
+ setupRange();
+
+ return {
+ insert: insert,
+ show: setRangeVisible,
+ hide: setRangeHidden,
+ deselect: deselect,
+ selectAll: selectAll
+ };
+ }
+
+})(jQuery);
+
+/*
+ * Smart Resize
+ *
+ * Mobile Safari bundled with iOS as of v6.1.3 fires an extra window resize
+ * event after scrolling the page *if* there are position: fixed elements in
+ * the page. This very simple plugin will prevent those events from being
+ * fired and also forward orientationchange events. This plugin should only
+ * be required if you are using position: fixed elements.
+ *
+ */
+
+(function($) {
+
+ $.fn.smartResize = function(handler) {
+
+ this.on('resize', function name() {
+ if(Modernizr.touch) return;
+ handler.apply(this, arguments);
+ });
+
+ this.on('orientationchange', function name() {
+ handler.apply(this, arguments);
+ });
+
+ }
+
+})(jQuery);
+
+(function($) {
+
+
+ // Elements must all be the same dimensions
+
+ var DEFAULT_SCALE = 3;
+ var DEFAULT_TIME_TO_DISAPPEAR = 100;
+ var DEFAULT_TOP_OFFSET = -80;
+ var DEFAULT_HOVER_CLASS_NAME = 'touch-hover';
+
+ var transformProperty;
+
+ function getTransformProperty() {
+ ['transform','webkitTransform'].forEach(function(prop) {
+ if(document.body.style[prop] !== undefined) {
+ transformProperty = prop;
+ }
+ });
+ }
+
+ $.fn.touchZoomGrid = function(options) {
+
+ // The grid
+ var grid;
+ // Elements
+ var elements, gridElement, currentHover, closestOffsetHidden;
+ // Dimensions
+ var elementWidth, elementHeight, gridLeftOffset, gridTopOffset, gridRightEdge, elementProtrusion;
+ // Options
+ var scale, offsetTop, canceled;
+
+ options = options || {};
+
+ elements = this.find(options.selector || '*');
+ gridElement = this;
+
+ gridRightEdge = 0;
+ scale = options.scale || DEFAULT_SCALE;
+ offsetTop = options.offsetTop || DEFAULT_TOP_OFFSET;
+
+ function getTouches(evt) {
+ return evt.originalEvent.touches;
+ }
+
+ function setTransform(el, offsetX, offsetY) {
+ var transform = 'translate('+ offsetX +'px, '+ offsetY +'px) scale('+ scale +')';
+ el.css(transformProperty, transform);
+ }
+
+ function setClosestOffsetHidden() {
+ var el = gridElement;
+ do {
+ el = el.parent();
+ } while(el[0] !== document.body && el.css('overflow') !== 'hidden');
+ closestOffsetHidden = el.offset().top;
+ }
+
+ function constrainHorizontalToGrid(x) {
+ var gridLeftProtrusion, gridRightProtrusion, offsetX = 0;
+ gridLeftProtrusion = x - elementProtrusion;
+ gridRightProtrusion = gridRightEdge - x - elementWidth - elementProtrusion;
+
+ if(gridLeftProtrusion < 0) {
+ offsetX -= gridLeftProtrusion;
+ } else if(gridRightProtrusion < 0) {
+ offsetX += gridRightProtrusion;
+ }
+ return offsetX;
+ }
+
+ function constrainVertical(y) {
+ var verticalSpace, offsetY = offsetTop;
+ verticalSpace = gridTopOffset + y - Math.max(closestOffsetHidden, window.pageYOffset);
+ if(verticalSpace + offsetY - elementProtrusion < 0) {
+ offsetY *= -1;
+ }
+ return offsetY;
+ }
+
+ function hoverOn(el, gridX, gridY) {
+ var x, y;
+ x = constrainHorizontalToGrid(gridX * elementWidth);
+ y = constrainVertical(gridY * elementHeight);
+ setTransform(el, x, y, scale);
+ el.addClass(DEFAULT_HOVER_CLASS_NAME);
+ }
+
+ function currentHoverOff() {
+ if(currentHover) {
+ currentHover.removeClass(DEFAULT_HOVER_CLASS_NAME);
+ currentHover.css(transformProperty, 'none');
+ currentHover = null;
+ }
+ }
+
+ function buildGrid() {
+ var offset;
+ grid = {};
+ offset = gridElement.offset();
+ gridLeftOffset = offset.left;
+ gridTopOffset = offset.top;
+ setClosestOffsetHidden();
+ elements.each(function() {
+ var el, height, width, offset, x, y;
+ el = $(this);
+ height = el.outerHeight(true);
+ width = el.outerWidth(true);
+ offset = el.offset();
+ x = Math.floor((offset.left - gridLeftOffset) / width);
+ y = Math.floor((offset.top - gridTopOffset) / height);
+ grid[x + ',' + y] = el;
+ elementWidth = width;
+ elementHeight = height;
+ gridRightEdge = Math.max(gridRightEdge, (x * elementWidth) + elementWidth);
+ });
+ elementProtrusion = (elementWidth / 2) * (scale - 1);
+ }
+
+ function setElementHover(evt, touches) {
+ var touches, touch, gridX, gridY, el;
+
+ if(canceled) {
+ return;
+ }
+
+ if(touches.length > 1) {
+ currentHoverOff();
+ canceled = true;
+ return;
+ }
+
+ touch = touches[0];
+ gridX = Math.floor((touch.pageX - gridLeftOffset) / elementWidth);
+ gridY = Math.floor((touch.pageY - gridTopOffset) / elementHeight);
+ el = grid[gridX + ',' + gridY];
+
+ if(el && el != currentHover) {
+ currentHoverOff();
+ hoverOn(el, gridX, gridY, touch.pageX, touch.pageY);
+ currentHover = el;
+ }
+
+ evt.preventDefault();
+ }
+
+ gridElement.on('touchstart', function(evt) {
+ var touches = getTouches(evt);
+ if(touches.length > 1) {
+ currentHoverOff();
+ } else {
+ canceled = false;
+ setElementHover(evt, touches);
+ }
+ });
+ gridElement.on('touchmove', function(evt) {
+ setElementHover(evt, getTouches(evt));
+ });
+ gridElement.on('touchend', function(evt) {
+ if(currentHover) {
+ currentHover.click();
+ setTimeout(function() {
+ currentHoverOff();
+ }, DEFAULT_TIME_TO_DISAPPEAR);
+ }
+ });
+
+ $(window).smartResize(buildGrid);
+ buildGrid();
+
+ }
+
+ getTransformProperty();
+
+
+})(jQuery);
+
+var CanvasPaint = Class(function CanvasPaint() {});
+
+CanvasPaint.prototype.extend(Event.Listener);
+CanvasPaint.prototype.extend({
+
+ setOffset: function(x, y) {
+ var rect = this.canvas.getBoundingClientRect();
+ this.startX = rect.left;
+ this.startY = rect.top;
+ },
+
+ getEventCoordinates: function(evt) {
+ var pos = {};
+ evt.preventDefault();
+ if(evt.targetTouches && evt.targetTouches[0]) {
+ pos.x = evt.targetTouches[0]['clientX'];
+ pos.y = evt.targetTouches[0]['clientY'];
+ } else if(evt.changedTouches && evt.changedTouches[0]) {
+ pos.x = evt.changedTouches[0]['clientX'];
+ pos.y = evt.changedTouches[0]['clientY'];
+ } else {
+ pos.x = evt.clientX;
+ pos.y = evt.clientY;
+ }
+ pos.x -= this.startX;
+ pos.y -= this.startY;
+ return pos;
+ },
+
+ onStart: function(evt) {
+ if(this.isMultiTouch(evt)) return;
+ this.down = true;
+ this.setOffset();
+ this.broadcast('BeforePaintStart');
+ this.broadcast('PaintStart', this.getEventCoordinates(evt));
+ },
+
+ onStop: function(evt) {
+ if(!this.down || this.isMultiTouch(evt)) return;
+ this.broadcast('PaintStop', this.getEventCoordinates(evt));
+ this.down = false;
+ },
+
+ onMove: function(evt) {
+ if(!this.down || this.isMultiTouch(evt)) return;
+ evt.preventDefault();
+ this.broadcast('Paint', this.getEventCoordinates(evt));
+ },
+
+ isMultiTouch: function(evt) {
+ return evt.targetTouches && evt.targetTouches.length > 1;
+ },
+
+ setupPaintEvents: function() {
+ if(Modernizr.touch) {
+ this.canvas.addEventListener('touchstart', this.onStart.bind(this), false);
+ this.canvas.addEventListener('touchend', this.onStop.bind(this), false);
+ this.canvas.addEventListener('touchcancel', this.onStop.bind(this), false);
+ this.canvas.addEventListener('touchmove', this.onMove.bind(this), false);
+ } else {
+ this.canvas.addEventListener('mousedown', this.onStart.bind(this), false);
+ this.canvas.addEventListener('mouseup', this.onStop.bind(this), false);
+ this.canvas.addEventListener('mouseout', this.onStop.bind(this), false);
+ this.canvas.addEventListener('mousemove', this.onMove.bind(this), false);
+ }
+ }
+
+});
+
+
+KanjiSexp = {
+
+ toSexp: function() {
+ return this.getSexpExpression('character', [
+ this.getSexpExpression('width', this.width),
+ this.getSexpExpression('height', this.height),
+ this.getSexpExpression('strokes', this.getSexpStrokes())
+ ]);
+ },
+
+ getSexpStrokes: function() {
+ return this.strokes.map(this.getSexpStroke, this);
+ },
+
+ getSexpStroke: function(stroke) {
+ return this.getSexpExpression('', this.getSexpPoints(stroke))
+ },
+
+ getSexpPoints: function(stroke) {
+ return stroke.points.map(this.getSexpPoint, this);
+ },
+
+ getSexpPoint: function(point) {
+ var ratio = this.pixelRatio || 1;
+ return this.getSexpExpression('', (point.x * ratio | 0) + ' ' + (point.y * ratio | 0));
+ },
+
+ getSexpExpression: function(name, value) {
+ if(Array.isArray(value)) {
+ value = value.join(' ');
+ }
+ return '(' + name + (name ? ' ' : '') + value + ')';
+ }
+
+};
+
+// Requires jQuery and Modernizr
+
+var KanjiHandwriting = Class(function KanjiHandwriting(el) {
+ this.setupCanvas(el);
+ this.setupPaintEvents();
+ this.strokes = [];
+ this.listener(this); // Listens to own paint events
+ this.ratio = this.width / this.height;
+ this.scale = this.pixelRatio;
+ this.lineWidth = 4 * this.scale;
+});
+
+KanjiHandwriting.mixin(CanvasPaint);
+KanjiHandwriting.mixin(Canvas);
+KanjiHandwriting.prototype.extend(KanjiSexp);
+
+KanjiHandwriting.prototype.extend({
+
+ reset: function() {
+ this.clear();
+ this.strokes = [];
+ this.broadcast('Reset');
+ },
+
+ back: function() {
+ this.strokes.pop();
+ this.draw();
+ },
+
+ draw: function() {
+ this.clear();
+ this.strokes.forEach(function(s) {
+ s.drawLine();
+ });
+ },
+
+ resizeToWidth: function(w) {
+ var ratio = w / this.width;
+ if(ratio === 1) return;
+ this.strokes.forEach(function(s) {
+ s.scale = ratio * this.scale;
+ s.rescale();
+ }, this);
+ this.setDimensions(w, Math.floor(w / this.ratio));
+ this.draw();
+ },
+
+ dimLastStroke: function() {
+ if(!this.currentStroke) return;
+ this.currentStroke.style = new LineStyle(this.lineWidth + 'px #aaa');
+ },
+
+ onPaintStart: function(pos) {
+ var stroke = new KanjiStroke({
+ scale: this.scale,
+ style: new LineStyle(this.lineWidth + 'px #000'),
+ context: this.context
+ });
+ stroke.addPoint(new Point(pos.x, pos.y));
+ this.currentStroke = stroke;
+ this.strokes.push(stroke);
+ this.broadcast('StrokeStart');
+ },
+
+ onPaint: function(pos) {
+ this.currentStroke.addPoint(new Point(pos.x, pos.y));
+ this.draw();
+ },
+
+ onPaintStop: function(pos) {
+ this.currentStroke.addPoint(new Point(pos.x, pos.y));
+ this.smoothPoints();
+ this.dimLastStroke();
+ this.draw();
+ this.broadcast('StrokeEnd');
+ },
+
+ smoothPoints: function() {
+ this.strokes.forEach(function(s) {
+ s.smoothPoints();
+ });
+ }
+
+});
+
+(function() {
+ var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
+
+ this.Search = (function() {
+ function Search(el) {
+ this.turnOffArea = __bind(this.turnOffArea, this);
+ this.turnOnArea = __bind(this.turnOnArea, this);
+ this.deactivateCurrentArea = __bind(this.deactivateCurrentArea, this);
+ this.form = el;
+ this.keyword = el.find('.keyword');
+ this.mainArea = el.find('.main');
+ this.subArea = el.find('.sub');
+ this.canSelectAll = true;
+ this.range = this.keyword.smartRange({
+ hidden: true
+ });
+ this.setupEvents();
+ this.setupComponents();
+ this.checkInputLink();
+ }
+
+ Search.prototype.setupComponents = function() {
+ this.radicalInput = new RadicalInput;
+ this.handwritingInput = new HandwritingInput;
+ this.speechInput = new SpeechInput;
+ this.filters = new Filters;
+ return this.advancedOptions = new SearchArea('advanced');
+ };
+
+ Search.prototype.setupEvents = function() {
+ $(window).smartResize(this.recalculateDimensions.debounce(250).bind(this));
+ this.form.on('character_chosen', (function(_this) {
+ return function(event, character) {
+ return _this.range.insert(character);
+ };
+ })(this));
+ this.form.on('candidate_chosen', (function(_this) {
+ return function(event, character) {
+ return _this.range.insert(character, true);
+ };
+ })(this));
+ this.form.on('candidate_confirmed', (function(_this) {
+ return function(event) {
+ return _this.range.deselect();
+ };
+ })(this));
+ this.form.on('candidate_removed', (function(_this) {
+ return function(event) {
+ return _this.range.insert('');
+ };
+ })(this));
+ this.form.on('speech_confirmed', (function(_this) {
+ return function(event, text) {
+ return _this.range.insert(text, true);
+ };
+ })(this));
+ this.form.on('speech_submitted', (function(_this) {
+ return function(event, text) {
+ _this.range.insert(text, true);
+ return _this.form.submit();
+ };
+ })(this));
+ this.form.on('speech_result_added show_more show_less results_changed', (function(_this) {
+ return function(event) {
+ return _this.setAreaHeight();
+ };
+ })(this));
+ this.form.on('area_activated', this.turnOnArea);
+ this.form.on('area_deactivated', this.turnOffArea);
+ this.form.on('area_will_activate', this.deactivateCurrentArea);
+ return this.keyword.on('caret_changed', (function(_this) {
+ return function() {
+ _this.handwritingInput.clearCandidate();
+ return _this.canSelectAll = false;
+ };
+ })(this));
+ };
+
+ Search.prototype.deactivateCurrentArea = function() {
+ if (this.currentArea) {
+ return this.currentArea.deactivate(true);
+ }
+ };
+
+ Search.prototype.turnOnRange = function() {
+ if (this.canSelectAll) {
+ this.range.selectAll();
+ }
+ return this.range.show();
+ };
+
+ Search.prototype.turnOnArea = function(event, area) {
+ this.currentArea = area;
+ this.turnOnRange();
+ this.mainArea.addClass('in');
+ return this.setAreaHeight();
+ };
+
+ Search.prototype.turnOffArea = function(event, area) {
+ this.currentArea = null;
+ this.mainArea.removeClass('in');
+ this.range.hide();
+ return this.setAreaHeight();
+ };
+
+ Search.prototype.setAreaHeight = function() {
+ if (this.currentArea) {
+ return this.subArea.height(this.currentArea.area.outerHeight());
+ } else {
+ return this.subArea.height(0);
+ }
+ };
+
+ Search.prototype.recalculateDimensions = function() {
+ this.handwritingInput.calculateDimensions();
+ this.radicalInput.setHeight();
+ return this.setAreaHeight();
+ };
+
+ Search.prototype.checkInputLink = function() {
+ var input_type;
+ input_type = window.location.hash;
+ if (input_type == null) {
+ input_type = "#" + (Object.getURLParam('input'));
+ }
+ switch (input_type) {
+ case '#radical':
+ return this.radicalInput.activate();
+ case '#handwriting':
+ return this.handwritingInput.activate();
+ case '#speech':
+ return this.speechInput.activate();
+ }
+ };
+
+ return Search;
+
+ })();
+
+}).call(this);
+(function() {
+
+
+}).call(this);
+(function() {
+
+
+}).call(this);
+var strokeOrderDiagram = function(element, svgDocument) {
+ var s = Snap(element);
+ var diagramSize = 200;
+ var coordRe = '(?:\\d+(?:\\.\\d+)?)';
+ var strokeRe = new RegExp('^[LMT]\\s*(' + coordRe + ')[,\\s](' + coordRe + ')', 'i');
+ var f = Snap(svgDocument.getElementsByTagName('svg')[0]);
+ var allPaths = f.selectAll("path");
+ var drawnPaths = [];
+ var canvasWidth = (allPaths.length * diagramSize) / 2;
+ var canvasHeight = diagramSize / 2;
+ var frameSize = diagramSize / 2;
+ var frameOffsetMatrix = new Snap.Matrix()
+ frameOffsetMatrix.translate((-frameSize / 16)+2, (-frameSize / 16)+2);
+
+ // Set drawing area
+ s.node.style.width = canvasWidth + "px";
+ s.node.style.height = canvasHeight + "px";
+ s.node.setAttribute("viewBox", "0 0 " + canvasWidth + " " + canvasHeight);
+
+ // Draw global guides
+ var boundingBoxTop = s.line(1, 1, canvasWidth-1, 1);
+ var boundingBoxLeft = s.line(1, 1, 1, canvasHeight-1);
+ var boundingBoxBottom = s.line(1, canvasHeight-1, canvasWidth-1, canvasHeight-1);
+ var horizontalGuide = s.line(0, canvasHeight/2, canvasWidth, canvasHeight/2);
+ boundingBoxTop.attr({"class": "stroke_order_diagram--bounding_box"});
+ boundingBoxLeft.attr({"class": "stroke_order_diagram--bounding_box"});
+ boundingBoxBottom.attr({"class": "stroke_order_diagram--bounding_box"});
+ horizontalGuide.attr({"class": "stroke_order_diagram--guide_line"});
+
+ // Draw strokes
+ var pathNumber = 1;
+ allPaths.forEach(function(currentPath) {
+ var moveFrameMatrix = new Snap.Matrix()
+ moveFrameMatrix.translate((frameSize * (pathNumber - 1)) - 4, -4);
+
+ // Draw frame guides
+ var verticalGuide = s.line((frameSize * pathNumber) - (frameSize / 2), 1, (frameSize * pathNumber) - (frameSize / 2), canvasHeight-1);
+ var frameBoxRight = s.line((frameSize * pathNumber) - 1, 1, (frameSize * pathNumber) - 1, canvasHeight-1);
+ verticalGuide.attr({"class": "stroke_order_diagram--guide_line"});
+ frameBoxRight.attr({"class": "stroke_order_diagram--bounding_box"});
+
+ // Draw previous strokes
+ drawnPaths.forEach(function(existingPath) {
+ var localPath = existingPath.clone();
+ localPath.transform(moveFrameMatrix);
+ localPath.attr({"class": "stroke_order_diagram--existing_path"})
+ s.append(localPath);
+ });
+
+ // Draw current stroke
+ currentPath.transform(frameOffsetMatrix);
+ currentPath.transform(moveFrameMatrix);
+ currentPath.attr({"class": "stroke_order_diagram--current_path"})
+ s.append(currentPath);
+
+ // Draw stroke start point
+ var match = strokeRe.exec(currentPath.node.getAttribute('d'));
+ var pathStartX = match[1];
+ var pathStartY = match[2];
+ var strokeStart = s.circle(pathStartX, pathStartY, 4);
+ strokeStart.attr({"class": "stroke_order_diagram--path_start"});
+ strokeStart.transform(moveFrameMatrix);
+
+ pathNumber++;
+ drawnPaths.push(currentPath.clone());
+ });
+};
+(function() {
+
+
+}).call(this);
+// This is a manifest file that'll be compiled into including all the files listed below.
+// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
+// be included in the compiled file accessible from http://example.com/assets/application.js
+// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
+// the compiled file.
+//
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+$(document).foundation();
diff --git a/src/static/img/ent/fuckingdiedisease.jpg b/src/static/img/ent/fuckingdiedisease.jpg
new file mode 100644
index 0000000..4560bd2
Binary files /dev/null and b/src/static/img/ent/fuckingdiedisease.jpg differ
diff --git a/src/views/diary-boxes.tpl b/src/views/diary-boxes.tpl
index 02cf32d..c6bda0d 100644
--- a/src/views/diary-boxes.tpl
+++ b/src/views/diary-boxes.tpl
@@ -3,14 +3,14 @@