Commit 1610dd67 authored by Aurélio A. Heckert's avatar Aurélio A. Heckert

first release

parents
Pipeline #5649087 passed with stage
in 55 seconds
pages:
stage: deploy
script:
- mkdir public
- cp test.html public/index.html
- cp -r jsboard.js imgs public
artifacts:
paths:
- public
only:
- master
jsBoard -- a class room white board for free hand draw in the web.
Copyright (C) 2017, Aurélio A. Heckert
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="20"
height="20"
viewBox="0 0 20 20"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="gear.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="22.725"
inkscape:cx="13.817382"
inkscape:cy="9.9119912"
inkscape:document-units="px"
inkscape:current-layer="svg2"
showgrid="true"
units="px"
inkscape:window-width="1366"
inkscape:window-height="704"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid3344"
originx="0"
originy="0" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Camada 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-1032.3622)" />
<g
id="g4216"
style="opacity:0.4">
<rect
y="8"
x="0"
height="4"
width="4"
id="rect3344"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.4723337;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.4723337;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect3346"
width="4"
height="4"
x="16"
y="8" />
<rect
transform="matrix(0,1,-1,0,0,0)"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.4723337;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect3360"
width="4"
height="4"
x="1.2246468e-15"
y="-12" />
<rect
transform="matrix(0,1,-1,0,0,0)"
y="-12"
x="16"
height="4"
width="4"
id="rect3362"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.4723337;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<rect
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.4723337;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect3364"
width="4"
height="4"
x="4.1421356"
y="-2" />
<rect
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)"
y="-2"
x="20.142136"
height="4"
width="4"
id="rect3366"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.4723337;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<rect
y="-16.142136"
x="-10"
height="4"
width="4"
id="rect3368"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.4723337;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
transform="matrix(-0.70710678,0.70710678,-0.70710678,-0.70710678,0,0)" />
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.4723337;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect3370"
width="4"
height="4"
x="6"
y="-16.142136"
transform="matrix(-0.70710678,0.70710678,-0.70710678,-0.70710678,0,0)" />
<circle
r="5"
cy="10"
cx="10"
id="path3376"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
</g>
</svg>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="40"
height="20"
viewBox="0 0 39.999999 20"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="locker.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="22.725"
inkscape:cx="20"
inkscape:cy="10"
inkscape:document-units="px"
inkscape:current-layer="svg2"
showgrid="true"
units="px"
inkscape:window-width="1366"
inkscape:window-height="704"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid3344"
originx="0"
originy="0" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<rect
y="9"
x="28"
height="11"
width="12"
id="rect4201"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect4199"
width="12"
height="11"
x="8"
y="9" />
<g
inkscape:label="Camada 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-1032.3622)" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 10,8 0,-3 c 0,-5 8,-5 8,0 l 0,3"
id="path4154"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<circle
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="path4164"
cx="14"
cy="13"
r="2" />
<path
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
id="path4193"
d="m 22,8 0,-3 c 0,-5 8,-5 8,0 l 0,3"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<circle
r="2"
cy="13"
cx="34"
id="circle4197"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect4211"
width="2"
height="4"
x="13"
y="14" />
<rect
y="14"
x="33"
height="4"
width="2"
id="rect4213"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
</svg>
/*
* jsBoard -- a class room white board for free hand draw in the web.
* Copyright (C) 2017, Aurélio A. Heckert
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
!function(t,r){var o,e,s,i,n,l,u,h,a,c,p;return e=function(){var t,r,o,e;for(e=Error().stack.split("\n"),t=0,r=e.length;t<r&&(o=e[t],!/\/jsboard\.js/.test(o));t++);return o.replace(/.*((https?|file):\/\/.*\/)[^\/]*$/,"$1")}(),n=function(t,o){return r("<"+t+"></"+t+">").appendTo(o)},o=function(){function t(t,o){var e;if(null==t&&(t="body"),null==o&&(o={}),"string"==typeof t&&(t=r(t)),null!=t.jquery&&(t=t[0]),null==t)throw Error("No parent defined.");this._w=parseInt(o.width||600),this._h=parseInt(o.height||400),this._brushSize=o.brushSize||30,this._fillColor=o.fillColor||"#080",e=Math.random().toString(16).split(".")[1].toUpperCase(),this.baseEl=n("div",t).attr({id:"jsBoard-"+e})[0],this._canvas=n("canvas",this.baseEl),this._canvas.on("mousedown",function(t){return function(r){return h(t,r)}}(this)),r(window).on("mouseup",function(t){return function(r){return c(t,r)}}(this)),this._canvas.on("mousemove",function(t){return function(r){return a(t,r)}}(this)),this._ctx=this._canvas[0].getContext("2d"),r(window).on("resize",function(t){return function(r){return p(t)}}(this)),r(document).on("webkitfullscreenchange mozfullscreenchange fullscreenchange",function(t){return function(r){return s(t)}}(this)),r(document).on("webkitfullscreenerror mozfullscreenerror fullscreenerror",function(t){return function(r){return i(t)}}(this)),this._mouseBt=[],this._ctrl=n("div",this.baseEl).addClass("jsBoard-ctrl"),l(this),n("style","html>head").text(u(this)),this.resize({width:this._w,height:this._h}),this.updateBrush()}return t.prototype.toggleCtrlLock=function(){return r(this.baseEl).toggleClass("ctrl-locked")},t.prototype.fullScreen=function(t){var r,o;return t?(console.log("fullScreen ON!"),null==(r=this.baseEl).requestFullscreen&&(r.requestFullscreen=this.baseEl.mozRequestFullScreen),null==(o=this.baseEl).requestFullscreen&&(o.requestFullscreen=this.baseEl.webkitRequestFullscreen),null!=this.baseEl.requestFullscreen?(this._isFullScreen=!0,this.baseEl.requestFullscreen()):this._isFullScreen=!1):(console.log("fullScreen OFF"),this._isFullScreen=!1,this.resize(this._lastOnPageSize))},t.prototype.resize=function(t){var r;return r=this._ctx.getImageData(0,0,this._w,this._h),this._w=t.width||this._w,this._h=t.height||this._h,this._isFullScreen||(this._lastOnPageSize={width:this._w,height:this._h}),this.baseEl.style.width=this._w+"px",this.baseEl.style.height=this._h+"px",this._canvas.attr({width:this._w,height:this._h}),this._ctx.putImageData(r,0,0),this.updateBrush()},t.prototype.setFillColor=function(t){return this._fillColor=t,this.updateBrush()},t.prototype.incBrush=function(t){return this._brushSize+=t,isNaN(this._brushSize)&&(this._brushSize=50),this._brushSize>100&&(this._brushSize=100),this._brushSize<1&&(this._brushSize=1),this._brushSize=Math.round(this._brushSize),this.updateBrush()},t.prototype.updateBrush=function(){var t;return t=Math.round((this._brushSize+1)/2),this._brushCtrlPoint.css({width:t+"px",height:t+"px",left:(50-t)/2+"px",top:(50-t)/2+"px",background:this._fillColor}),this._brushCtrlInfo.text(this._brushSize+"px"),this._ctx.lineWidth=this._brushSize,this._ctx.lineJoin="round",this._ctx.lineCap="round"},t.prototype.moveTo=function(t){return this._currentPosition=t,this._ctx.beginPath(),this._ctx.moveTo(this._currentPosition.x,this._currentPosition.y)},t.prototype.lineTo=function(t){return this._ctx.lineTo(t.x,t.y),this._ctx.strokeStyle=this._fillColor,this._ctx.stroke(),this._ctx.closePath(),this._ctx.moveTo(t.x,t.y)},t.prototype.drawLine=function(t){return this.moveTo({x:t.x1,y:t.y1}),this.lineTo({x:t.x2,y:t.y2})},t}(),p=function(t){var o;return t._isFullScreen&&(o=r(document),t.resize({width:o.width(),height:o.height()})),t._pageOffset=r(t.baseEl).offset()},s=function(t){var r;if(r=document.fullscreen||document.mozFullScreen||document.webkitIsFullScreen,console.log("fullscreenChange",t._isFullScreen,r),t._isFullScreen&&!r)return t.fullScreen(!1)},i=function(t){if(t._isFullScreen)return t.fullScreen(!1)},h=function(t,r){var o,e,s;if(e=t._pageOffset,s=e.top,o=e.left,t._mouseBt[r.button]=!0,t._mouseBt[0])return t.moveTo({x:r.pageX-o,y:r.pageY-s})},c=function(t,r){var o,e,s;return e=t._pageOffset,s=e.top,o=e.left,t._mouseBt[0]&&t.lineTo({x:r.pageX-o,y:r.pageY-s}),t._mouseBt[r.button]=!1},a=function(t,r){var o,e,s;if(e=t._pageOffset,s=e.top,o=e.left,t._mouseBt[0])return t.lineTo({x:r.pageX-o,y:r.pageY-s})},l=function(t){var r,o,e,s,i,l,u;if(null==t)throw Error("Undefined jsBoard");for(n("locker",t._ctrl).on("click",function(){return t.toggleCtrlLock()}),t._brushCtrl=n("brush",t._ctrl),t._brushCtrlPoint=n("point",t._brushCtrl),t._brushCtrlInfo=n("span",t._brushCtrl),n("more",t._brushCtrl).text("+").on("click",function(){return t.incBrush(4)}),n("less",t._brushCtrl).text("-").on("click",function(){return t.incBrush(-4)}),r=function(r){var o;return o="hsl("+30*r+",100%,25%)",n("p",t._ctrl).addClass("jsBoard-palete-line1 jsBoard-palete-index"+r).css({"background-color":o}).on("click",function(){return t.setFillColor(o)})},o=function(r){var o;return o="hsl("+30*r+",100%,50%)",n("p",t._ctrl).addClass("jsBoard-palete-line2 jsBoard-palete-index"+r).css({"background-color":o}).on("click",function(){return t.setFillColor(o)})},e=function(r){var o;return o="hsl("+30*r+",100%,75%)",n("p",t._ctrl).addClass("jsBoard-palete-line3 jsBoard-palete-index"+r).css({"background-color":o}).on("click",function(){return t.setFillColor(o)})},s=i=0;i<=11;s=++i)r(s),o(s),e(s);for(u=[],s=l=0;l<=6;s=++l)u.push(function(r){var o;return o="hsl(0,0%,"+Math.round(100*r/6)+"%)",n("p",t._ctrl).addClass("jsBoard-palete-line4 jsBoard-palete-index"+r).css({"background-color":o}).on("click",function(){return t.setFillColor(o)})}(s));return u},u=function(t){var r,o,s,i,n,l,u,h,a;if(null==t)throw Error("Undefined jsBoard");for(o=t.baseEl.id,i={"":{background:"#FFF",position:"relative",overflow:"hidden","box-shadow":"0 0 20px rgba(0,0,0,0.4)"},".jsBoard-ctrl":{position:"absolute",right:0,bottom:0,background:"rgba(200,200,200,0.5) url("+e+"imgs/gear.svg) 80% 80% no-repeat",border:"1px solid rgba(0,0,0,0.4)","border-right":"none","border-bottom":"none","border-radius":"100% 0 0 0",width:"30px",height:"30px",transition:"1s"},".jsBoard-ctrl:hover, &.ctrl-locked .jsBoard-ctrl":{width:"220px",height:"220px",transition:"0.5s","background-position":"110% 110%"},".jsBoard-ctrl locker":{display:"block",width:"20px",height:"20px",position:"absolute",right:"-20px",bottom:"3px",background:"url("+e+"imgs/locker.svg) 100%",opacity:.4,cursor:"pointer",transition:"right 1s"},"&.ctrl-locked .jsBoard-ctrl locker":{"background-position":0},".jsBoard-ctrl:hover locker, &.ctrl-locked .jsBoard-ctrl locker":{right:"3px",bottom:"3px",transition:"right 0.5s"},".jsBoard-ctrl brush":{display:"block",width:"50px",height:"50px",position:"absolute",left:"50px",bottom:"-10px",opacity:0,transition:"0.8s"},".jsBoard-ctrl:hover brush, &.ctrl-locked .jsBoard-ctrl brush":{left:"130px",bottom:"6px",opacity:1,transition:"0.3s"},".jsBoard-ctrl brush point":{position:"absolute",left:0,botom:0,display:"block",width:"30px",height:"30px",border:"1px solid rgba(0,0,0,0.2)","border-radius":"50% 50% 50% 0",transition:"background 0.5s"},".jsBoard-ctrl brush span":{position:"absolute",right:"-7px",bottom:"1px",font:"13px sans-serif",color:"#000","text-shadow":"0 0 2px #EEE, 0 0 2px #EEE, 0 0 2px #EEE, 0 0 2px #EEE","white-space":"nowrap"},".jsBoard-ctrl brush:before":{content:'""',position:"absolute",left:"15px",top:"-9px",border:"1px solid rgba(0,0,0,0.2)",display:"block",width:"80px",height:"10px","border-radius":"50%",background:"#A74",transform:"rotate(-45deg)"},".jsBoard-ctrl brush more, .jsBoard-ctrl brush less":{position:"absolute",font:"1px sans-serif",color:"transparent",opacity:.5,"border-radius":"10px",display:"block",width:"30px",height:"30px",cursor:"pointer"},".jsBoard-ctrl brush:hover more, .jsBoard-ctrl brush:hover less":{background:"rgba(255,255,255,0.8)"},".jsBoard-ctrl brush more:before, .jsBoard-ctrl brush less:before":{content:'""',background:"#000","border-radius":"3px",display:"block",width:"24px",height:"6px",position:"absolute",top:"12px",left:"3px"},".jsBoard-ctrl brush more:after":{content:'""',background:"#000","border-radius":"3px",display:"block",width:"6px",height:"24px",position:"absolute",top:"3px",left:"12px"},".jsBoard-ctrl brush more":{right:"-1px",bottom:"44px"},".jsBoard-ctrl brush less":{right:"-27px",bottom:"18px"},".jsBoard-ctrl p":{position:"absolute",display:"block",width:"20px",height:"20px",margin:0,right:"-20px",bottom:"-20px",border:"1px solid rgba(0,0,0,0.2)","border-radius":"50%",cursor:"pointer",transition:"1s"},".jsBoard-ctrl:hover p, &.ctrl-locked .jsBoard-ctrl p":{transition:"0.5s"}},u=function(t,r){return".jsBoard-ctrl:hover X, &.ctrl-locked .jsBoard-ctrl X".replace(/X/g,".jsBoard-palete-index"+t+".jsBoard-palete-line"+r)},n=l=0;l<=11;n=++l)i[u(n,1)]={right:Math.round(180*Math.cos(.143*n))+"px",bottom:Math.round(180*Math.sin(.143*n))+"px",width:"28px",height:"28px"},i[u(n,2)]={right:Math.round(153*Math.cos(.143*n))+"px",bottom:Math.round(153*Math.sin(.143*n))+"px",width:"26px",height:"26px"},i[u(n,3)]={right:Math.round(128*Math.cos(.143*n))+"px",bottom:Math.round(128*Math.sin(.143*n))+"px",width:"24px",height:"24px"},i[u(n,4)]={right:Math.round(105*Math.cos(.261*n))+"px",bottom:Math.round(105*Math.sin(.261*n))+"px"};return function(){var t;t=[];for(h in i)s=i[h],h=h.replace(/(^|,)/g,"$1 #"+o+" ").replace(/\s*&/g,""),t.push(h+" {"+function(){var t;t=[];for(r in s)a=s[r],t.push(r+":"+a+";");return t}().join("\n")+"}");return t}().join("\n\n")},t.buildJsBoard=function(t,r,e){var s,i;null==t&&(t="body"),null==r&&(r={}),null==e&&(e=function(){});try{return s=new o(t,r),setTimeout(function(){return p(s),e(null,s)},500),s}catch(t){return i=t,e(i)}}}(window,jQuery);
{
"name": "jsboard",
"version": "0.0.1",
"description": "a class room white board for free hand draw in the web.",
"main": "jsboard.js",
"scripts": {
"build": "(echo \"/*\n$(sed 's/^/ */' LICENSE.txt)\n */\"; coffee -pbo . src/jsboard.coffee | uglifyjs -cm) > jsboard.js",
"watch": "coffee -wcbo . src/jsboard.coffee",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "http://gitlab.com/aurium/jsboard"
},
"keywords": [
"board",
"class",
"teacher",
"blackboard",
"whiteboard",
"freehand",
"draw"
],
"author": "Aurélio A. Heckert <[email protected]>",
"license": "AGPL-3.0",
"dependencies": {
"coffee-script": "^1.12.2",
"uglify-js": "^2.7.5"
}
}
do (exports=window, $=jQuery)->
__dirname = do ->
for line in Error().stack.split('\n')
break if /\/jsboard\.js/.test line
line.replace /.*((https?|file):\/\/.*\/)[^\/]*$/, '$1'
# Create an HTML element and append it to some other element
mk = (tag, parent)-> $("<#{tag}></#{tag}>").appendTo(parent)
class JsBoard
constructor: (parent='body', config={})->
parent = $(parent) if 'string' is typeof parent
parent = parent[0] if parent.jquery?
throw Error 'No parent defined.' unless parent?
@_w = parseInt config.width or 600
@_h = parseInt config.height or 400
@_brushSize = config.brushSize or 30
@_fillColor = config.fillColor or '#080'
idNum = Math.random().toString(16).split('.')[1].toUpperCase()
@baseEl = mk('div', parent).attr(id: 'jsBoard-'+idNum)[0]
@_canvas = mk('canvas', @baseEl) #.attr width: @_w, height: @_h
@_canvas.on 'mousedown', (ev)=> mouseDown this, ev
$(window).on 'mouseup', (ev)=> mouseUp this, ev
@_canvas.on 'mousemove', (ev)=> mouseMove this, ev
@_ctx = @_canvas[0].getContext '2d'
$(window).on 'resize', (ev)=> windowResize this
$(document).on 'webkitfullscreenchange mozfullscreenchange fullscreenchange', (ev)=> fullscreenChange this
$(document).on 'webkitfullscreenerror mozfullscreenerror fullscreenerror', (ev)=> fullscreenError this
@_mouseBt = []
@_ctrl = mk('div', @baseEl).addClass 'jsBoard-ctrl'
mkCtrl this
mk('style', 'html>head').text mkStileSheet this
@resize width: @_w, height: @_h
do @updateBrush
toggleCtrlLock: -> $(@baseEl).toggleClass 'ctrl-locked'
fullScreen: (doIt)->
if doIt
console.log 'fullScreen ON!'
@baseEl.requestFullscreen ?= @baseEl.mozRequestFullScreen
@baseEl.requestFullscreen ?= @baseEl.webkitRequestFullscreen
if @baseEl.requestFullscreen?
@_isFullScreen = true
@baseEl.requestFullscreen()
else
@_isFullScreen = false
else
console.log 'fullScreen OFF'
@_isFullScreen = false
@resize @_lastOnPageSize
resize: (size)->
imgData = @_ctx.getImageData 0, 0, @_w, @_h
@_w = size.width or @_w
@_h = size.height or @_h
@_lastOnPageSize = width: @_w, height: @_h unless @_isFullScreen
@baseEl.style.width = @_w + 'px'
@baseEl.style.height = @_h + 'px'
@_canvas.attr width: @_w, height: @_h
@_ctx.putImageData imgData, 0, 0
do @updateBrush
setFillColor: (@_fillColor)-> do @updateBrush
incBrush: (inc)->
@_brushSize += inc
@_brushSize = 50 if isNaN @_brushSize
@_brushSize = 100 if @_brushSize > 100
@_brushSize = 1 if @_brushSize < 1
@_brushSize = Math.round @_brushSize
do @updateBrush
updateBrush: ->
displaySize = Math.round (@_brushSize+1)/2
@_brushCtrlPoint.css
width: displaySize + 'px'
height: displaySize + 'px'
left: (50-displaySize)/2 + 'px'
top: (50-displaySize)/2 + 'px'
background: @_fillColor
@_brushCtrlInfo.text @_brushSize + 'px'
@_ctx.lineWidth = @_brushSize
@_ctx.lineJoin = 'round'
@_ctx.lineCap = 'round'
moveTo: (@_currentPosition)->
@_ctx.beginPath()
@_ctx.moveTo @_currentPosition.x, @_currentPosition.y
lineTo: (point)->
@_ctx.lineTo point.x, point.y
@_ctx.strokeStyle = @_fillColor
@_ctx.stroke()
@_ctx.closePath()
@_ctx.moveTo point.x, point.y
drawLine: (points)->
@moveTo x:points.x1, y:points.y1
@lineTo x:points.x2, y:points.y2
windowResize = (board)->
if board._isFullScreen
doc = $(document)
board.resize width: doc.width(), height: doc.height()
board._pageOffset = $(board.baseEl).offset()
fullscreenChange = (board)->
docIsFullScreen = document.fullscreen or document.mozFullScreen or document.webkitIsFullScreen
console.log 'fullscreenChange', board._isFullScreen, docIsFullScreen
if board._isFullScreen and not docIsFullScreen
board.fullScreen false
fullscreenError = (board)->
if board._isFullScreen
board.fullScreen false
mouseDown = (board, ev)->
{top, left} = board._pageOffset
board._mouseBt[ev.button] = true
board.moveTo x:ev.pageX-left, y:ev.pageY-top if board._mouseBt[0]
mouseUp = (board, ev)->
{top, left} = board._pageOffset
board.lineTo x:ev.pageX-left, y:ev.pageY-top if board._mouseBt[0]
board._mouseBt[ev.button] = false
mouseMove = (board, ev)->
{top, left} = board._pageOffset
board.lineTo x:ev.pageX-left, y:ev.pageY-top if board._mouseBt[0]
mkCtrl = (board)->
throw Error 'Undefined jsBoard' unless board?
mk('locker', board._ctrl).on 'click', -> do board.toggleCtrlLock
board._brushCtrl = mk('brush', board._ctrl)
board._brushCtrlPoint = mk('point', board._brushCtrl)
board._brushCtrlInfo = mk('span', board._brushCtrl)
mk('more', board._brushCtrl).text('+').on 'click', -> board.incBrush 4
mk('less', board._brushCtrl).text('-').on 'click', -> board.incBrush -4
# make a palete:
for i in [0..11]
do (i=i)->
color = "hsl(#{i*30},100%,25%)"
mk('p', board._ctrl)
.addClass 'jsBoard-palete-line1 jsBoard-palete-index'+i
.css 'background-color': color
.on 'click', -> board.setFillColor color
do (i=i)->
color = "hsl(#{i*30},100%,50%)"
mk('p', board._ctrl)
.addClass 'jsBoard-palete-line2 jsBoard-palete-index'+i
.css 'background-color': color
.on 'click', -> board.setFillColor color
do (i=i)->
color = "hsl(#{i*30},100%,75%)"
mk('p', board._ctrl)
.addClass 'jsBoard-palete-line3 jsBoard-palete-index'+i
.css 'background-color': color
.on 'click', -> board.setFillColor color
for i in [0..6]
do (i=i)->
color = "hsl(0,0%,#{Math.round 100*(i)/6}%)"
mk('p', board._ctrl)
.addClass 'jsBoard-palete-line4 jsBoard-palete-index'+i
.css 'background-color': color
.on 'click', -> board.setFillColor color
mkStileSheet = (board)->
throw Error 'Undefined jsBoard' unless board?
baseId = board.baseEl.id
css = {
'':
background: '#FFF'
position: 'relative'
overflow: 'hidden'
'box-shadow': '0 0 20px rgba(0,0,0,0.4)'
'.jsBoard-ctrl':
position: 'absolute'
right: 0
bottom: 0
background: "rgba(200,200,200,0.5) url(#{__dirname}imgs/gear.svg) 80% 80% no-repeat"
border: '1px solid rgba(0,0,0,0.4)'
'border-right': 'none'
'border-bottom': 'none'
'border-radius': '100% 0 0 0'
width: '30px'
height: '30px'
transition: '1s'
".jsBoard-ctrl:hover, &.ctrl-locked .jsBoard-ctrl":
width: '220px'
height: '220px'
transition: '0.5s'
'background-position': '110% 110%'
'.jsBoard-ctrl locker':
display: 'block'
width: '20px'
height: '20px'
position: 'absolute'
right: '-20px'
bottom: '3px'
background: "url(#{__dirname}imgs/locker.svg) 100%"
opacity: 0.4
cursor: 'pointer'
transition: 'right 1s'
"&.ctrl-locked .jsBoard-ctrl locker":
'background-position': 0
".jsBoard-ctrl:hover locker, &.ctrl-locked .jsBoard-ctrl locker":
right: '3px'
bottom: '3px'
transition: 'right 0.5s'
'.jsBoard-ctrl brush':
display: 'block'
width: '50px'
height: '50px'
position: 'absolute'
left: '50px'
bottom: '-10px'
opacity: 0
transition: '0.8s'
'.jsBoard-ctrl:hover brush, &.ctrl-locked .jsBoard-ctrl brush':
left: '130px'