From c3f44c58add2a34e88e52313d6b3c9761feda8f4 Mon Sep 17 00:00:00 2001 From: Celestora Date: Wed, 13 Oct 2021 20:50:16 +0300 Subject: [PATCH] Wall: WIP: Add graffiti attachment (via Photos) --- .../templates/components/textArea.xml | 4 + Web/Presenters/templates/components/wall.xml | 7 +- Web/static/css/dialog.css | 4 +- Web/static/css/style.css | 25 + Web/static/img/logo_watermark.gif | Bin 0 -> 5186 bytes Web/static/js/al_wall.js | 40 +- Web/static/js/messagebox.js | 5 +- Web/static/js/package.json | 5 + Web/static/js/vnd_literallycanvas.js | 6434 +++++++++++++++++ Web/static/js/yarn.lock | 143 + 10 files changed, 6661 insertions(+), 6 deletions(-) create mode 100644 Web/static/img/logo_watermark.gif create mode 100644 Web/static/js/vnd_literallycanvas.js diff --git a/Web/Presenters/templates/components/textArea.xml b/Web/Presenters/templates/components/textArea.xml index eedef0c2..b6a5c3df 100644 --- a/Web/Presenters/templates/components/textArea.xml +++ b/Web/Presenters/templates/components/textArea.xml @@ -44,6 +44,10 @@ {_attach_photo} + + + Нарисовать граффити + diff --git a/Web/Presenters/templates/components/wall.xml b/Web/Presenters/templates/components/wall.xml index b9eda187..74cef4bf 100644 --- a/Web/Presenters/templates/components/wall.xml +++ b/Web/Presenters/templates/components/wall.xml @@ -8,7 +8,7 @@
- {include "../components/textArea.xml", route => "/wall$owner/makePost"} + {include "../components/textArea.xml", route => "/wall$owner/makePost", graffiti => true}
@@ -25,3 +25,8 @@
+ +{script "js/node_modules/react/dist/react-with-addons.min.js"} +{script "js/node_modules/react-dom/dist/react-dom.min.js"} +{script "js/vnd_literallycanvas.js"} +{css "js/node_modules/literallycanvas/lib/css/literallycanvas.css"} diff --git a/Web/static/css/dialog.css b/Web/static/css/dialog.css index d49247fd..465b1e0b 100644 --- a/Web/static/css/dialog.css +++ b/Web/static/css/dialog.css @@ -23,13 +23,13 @@ body.dimmed > .dimmer { margin: auto; margin-top: 10px; margin-bottom: 10px; - width: 400px; + width: calc(100% - 20px); background-color: #fff; border: 1px solid #505050; } .ovk-diag-head, .ovk-diag-body, .ovk-diag-action { - width: 400px; + width: 100%; box-sizing: border-box; } diff --git a/Web/static/css/style.css b/Web/static/css/style.css index 5a6b5d0a..d97a4fc5 100644 --- a/Web/static/css/style.css +++ b/Web/static/css/style.css @@ -1398,4 +1398,29 @@ body.scrolled .toTop:hover { #wallAttachmentMenu > a:hover { background-color: #f0f0f0; +} + +#ovkDraw { + border: 1px solid #757575; +} + +#ovkDraw .lc-drawing.with-gui { + background-color: white !important; +} + +#ovkDraw .literally { + border-radius: 0; +} + +#ovkDraw .literally .lc-picker, .literally .lc-options.horz-toolbar { + background-color: #f7f7f7; + border-color: rgba(0, 0, 0, 0.15); +} + +#ovkDraw .literally .lc-picker .toolbar-button.selected:not(.disabled), #ovkDraw .literally .horz-toolbar .square-toolbar-button.selected:not(.disabled) { + background-color: #cdcdcd; +} + +#ovkDraw .literally .lc-picker .toolbar-button:hover:not(.disabled), #ovkDraw .literally .horz-toolbar .square-toolbar-button:hover:not(.disabled) { + border-color: #cdcdcd; } \ No newline at end of file diff --git a/Web/static/img/logo_watermark.gif b/Web/static/img/logo_watermark.gif new file mode 100644 index 0000000000000000000000000000000000000000..f2a93f92c875d255d25e478240d12f0b2e2beebb GIT binary patch literal 5186 zcmXYxc|28J)W=VQ@kygO^UO7*gKJ6=*O)RKGi5qvCG%95a9wkTI5Nw<9AwHRG8Hl$ zmElVD$U`BbQcu!&UeEi!`=9;UYkfX@t@Zu=_BJ*pX==H*?g;?C0spr^0Du7i0RRjD zaDV^+1P~y=0099A7(l=Q2ml}ufM5VZ00;vh93TS#83f2MKt=#E29R+81ppKTP#8cF z0L1_l2bciB1OX-tFcE->0Ze@NZU6@X90qU%z%c;dJpdp8f&dHx2nb-iIgkK=1P~;^ zAOQgh7)ZcD2mm1vgkTUtKnMdN93%rE83f5NNJc<129j|Q1wa%8Q5ZxK5XC?g2bloK z1VJVYG7*r8flM640T2g490qX!-x?PGUr5C&ivKwtpdZ3`v2#3i4Oa@^x43iO8jTfpHARcMzDs{sY!Cxr3_vgd+Z6*N02l$p2rx!KFam}Va0~)42*e;5gAfeDFbK!U z07eEeGK`TCjErGq976#N1u+!HPy|CU48<`ffH6Ug31dtIV`3N+$8Z3{K@5j69Kmo5 z!}0%&u&c%{lHI;`le<6hY%dXa0tiA08R#RGK`ZE zoQ&aQ97h2h1#uL{Q3OXZ9K~@afHOgy3FAxzXJR<>zhb*a+f{oP`R?5RTm1ihzq^Tl z-@gX{egTUAh5maJ06PwR;}9}wt{Awnzi>0%PUWRu@&RFkz@01NL)4>6+J+-7)sM5q z&ZY^Ow%!@dle0lxMq6vf3&jhKoJpX^qZrP@dF=id_w%I(qFGBFkI*KoP4X06$L`fl z(}kpuLJl`q9@RO7!aXST{@iWD$~0kfM#GCX-%QR%d-YX_adPowoH;RgJ~jBw(z)@D zriDK8zQSKGPx!bs$Mnez|HZh%>~QM$6s@(fu~@G`23`&p#f&x^4gawcghEpF3Yv)DQMu|6L_Iz#X6EQZGlb_`yo05(= zS$mmut@!9~B;c}eo=?D)s%9Y3ujpagX4LMWnap0`j_Z zel>+Y*UYqDe-*IGAD{%k-52x+k1z?2rRMgZUG>YwuJ#IcU1JA(pKVl7Vi|Kkm7Pws ze6W(w9W+$d`x=vZC<08K7Aco_O7lt0O~MXk>o!x`UliO45ZYRon_a`m@YjNw-KGa);f)u2qx z)X7ql80m6gs7clC=p^jbmcReWkoY;2_x-z+GaseBO#286+j5kFRgI}nwF%B<4Gwb(ypIS~ z6Vv~<=sv;~e$G)uW6x>(>>nPEQutSllMYN^@}1zhyGo)%zM5twaKYw5|AbIjh8ucFa7Zy&W}o<77P2N0(WjMD9}-r{`n_W5^$VpHtM#%NLLt;0USBf--p;KqJ)fO2K5cYJ z-1*#7wsg&2$)5bv5e*WDh=R{3>R*FG!{2HcnOQLsRVWHQONS@wHm? zfNX&gb5i!=ewrt1hY#M$)b@-@{9*Yp#-~`NM#y&Fg4w)5nnzCx3!M^{6_20vi<%=LmKwmp+w4zFSpsG zf1O z(!z?uR|(V4pAAfHHSp`K&vL4nirEVkagi>Qx}BhfyDA1pL_)Wmj(aV3b<$3^2l$A; z91rxjz>aOegm8RmcEK_u$`Jlno(o$W9kQOGSmwH^Q=d^Iv`=s?;vW5b&>#4fXOb z)EB(>XqCh%;gp$2RyCW4q=N#StJZTIr_K%9DL6N;yqw(D8;Pu+TiM&TJ>GmWa6uyw zjM1v=W+hT*id?agrS+0H1}*h^gGV&u#nX0MSJu(7Pbkl`Q6u$;p3CXew)V%biSLb; z6&}nnJ(^xno24G)Xg`7_B6R>gL@-; z)IxCAR?qX%Nu}4{)O_4#tL&>kpE)$0cLAUO{8mPr{-IGTrMZ`?5fk98AD$d*z8s|e z+~X&of_CekYk01tDhq4iqN*e3YFv9wob@B?Hd0I$#pdTWB={v{N-e&4H71HJbj#Rq zCqJf?a=D1%4(2(xRLXBGl21gCEtq~cH7=h>gq$c5&NPv2KL5|2rn)8$R9OdZSr>mb zFKVmH{yUL_Q`gVS*~Lf+8QA+ip{ZZ14k@ua`elD2|5VtQ8Y>0g;6c`S-ZoX;v*lQy$~^QVJkE?3ogjJNILKxU>B_s5J6mLF zeN@@hS9Z^RpFiFbC6H};)Hzm`6#Ult0XCmx`|Q!P8vG^!C84~n_e5IRJq~LoxywaM z&Sc{=E1}Ag?jag(`d&7`O0ak*Z-9VbbZ4%0XFAV!axaB)u2OjBv1u5O{_SNg?^%C= zwSv8o(Zt%MvEZt4T>_2aKD!&Wt8r{ryx`-ULjvtp@r-z8Byvd zEmCB!q;(Si?D5O4_=HAgV00vbBK;yVC8M?ABkZ3tdS+E7y>H6lpUTwVk8d`SOdcT- z8lAC|Nz^g+j0GCJOa!~t&w4Qoba%3f_oVQVlvN77ZHc^BZ#tJnrF?U6b>P1wg4|BX zx7nhf50t$%k61s8ba>AXmN5+c;S@q->XuxZ=5Uc@l`2+g&HmNri{`Zs_M&B}v-nVo zyOpthl3LjrVwO%=cs$j&K+ADuCFiwaNdapte8i`yE#W>fF_cyrM^}oY9_=9(Z3#GE zJa$ate$_Y+<*tBSE)>A!AG=T)`(`Qm@D0-D9^}(8Ei)M;Go?a=?RLdrgtC5;#bd9B zsdto$B8&4pf8W>L$xoELRjMGMx)GjGQk1_==e>I`Gi5ufupqVayNykZ(&xl0T=Idc z#l&~CWXIxL^KU4meK&Jx*N-}dcfb)9umo3riKtA>5i}wDG4b=s*aVJ>o^Y{v8M(`B zRI5Y}rm^T~w9NC`(sAQ3B&no^eh%Lew{{XGu3H&vNj9Y_N^Dox-z(D`q2T4(Ib&+v z#Voe23Bh)Jp;KpTq_k=WsNrX%X@c?zyn=auK1W~r*YO73R)saa{$*%Sr@ag(Dp#B+ zI7<0_BY48mFaM^Kil+XYx80#(6@I7KPTt1qI{p+6C*7Clcw|nh{RjhBEBTO1$hX@c ziyQAM5P6KUW!bB-=W3i^rB0%hqxb8kqE%0H`b9|AuXmJs_{Tc(HR)(qAJ~^zSl8ku z7bz~Am{i^L2MK$gBy!;;di?#(HIC@hi}Yyjbl%jnY*Ym#kW_sv*y&eWaJUL#|!sA7JnB!kWOIy=K{ zJTGK5ebuAVZ`^8aZ=LnroynB47wttHJ53D!Bc0N>p|#SCXrDumPk<<3`Mf!PAjHSZa9_uI@%dyDC&W0VnvlE?d<%L&>2QPn&MB%hak z@-Lo!>dKZ1;X!_aS0#!0gpxaTRaI+Yi5l02IIQk3Htm!1R2i^bazw7)$;ZD^!-`&+ zRmXC#DReB&*bj1=TLoMbi>;HfyKXc?BE3+Zcpq8Q+VF)V`;@QTiJ#RD1uY$EVS5WN z8r0Ja;m(Am3P)$%7H*TD{FgHd6ntOA)%;a)@UOFfei$zrGA8J0pLHbj0tj{8rRRV4 zE+^*rj?l&>BclVf-|X}h*=NjAuW;^*aob<6K`k;CAJFGdlT4JI8?5U0GmnafMGlz2 zX-7)TE(q%OaSq#Xm`0gw)t`A~VVc~!$XUr~PGwfSjbFHR&yRW5 z@;F_JD);d;7trmDDd@Y9+rMdBEd457+M-oEO{sNnR<3{4B{U?dzSn%G12;jEA~iC8 zq8a-h4}0)ci5A-^ha|kGGwOFFh`f+UGWTQ8{$|gS{p_K|sl{VN(TYxAnb9hXvDP|O z5w+-^?&(oM)8e2J;qB_vtv@OS?X!r1jdmNO4VX9@+f5HOIKdJ8g><{5yPh#O*7dje zaipg~{i;%iJ+EV=C!x1H4r4AdRJ_|&becm_`X-{sM|3Bq^~i{bU;xJjZEub`KFbs?q_lOO@{uy`DYsq+Kmz^F}dlDZ=dnTPs&6-nVt", ["Сохранить", "Отменить"], [function() { + canvas.getImage({includeWatermark: false}).toBlob(blob => { + let fName = "Graffiti-" + Math.ceil(performance.now()).toString() + ".jpeg"; + let image = new File([blob], fName, {type: "image/jpeg", lastModified: new Date().getTime()}); + let trans = new DataTransfer(); + trans.items.add(image); + + let fileSelect = document.querySelector("input[name='_pic_attachment']"); + fileSelect.files = trans.files; + + u(fileSelect).trigger("change"); + u("#write textarea").trigger("focusin"); + }, "image/jpeg", 0.92); + + canvas.teardown(); + }, function() { + canvas.teardown(); + }]); + + let watermarkImage = new Image(); + watermarkImage.src = "/assets/packages/static/openvk/img/logo_watermark.gif"; + + msgbox.attr("style", "width: 750px;"); + canvas = LC.init(document.querySelector("#ovkDraw"), { + backgroundColor: "#fff", + imageURLPrefix: "/assets/packages/static/openvk/js/node_modules/literallycanvas/lib/img", + watermarkImage: watermarkImage, + imageSize: { + width: 640, + height: 480 + } + }); +} + u("#wall-post-input").on("paste", function(e) { if(e.clipboardData.files.length === 1) { var input = u("input[name=_pic_attachment]").nodes[0]; input.files = e.clipboardData.files; - Reflect.apply(handleUpload, input, []); + u(fileSelect).trigger("change"); } }); @@ -72,4 +108,4 @@ u("#wall-post-input").on("input", function(e) { // textArea.style.height = (newHeight > originalHeight ? (newHeight + boost) : originalHeight) + "px"; }); -u("input[name=_pic_attachment]").on("change", handleUpload); \ No newline at end of file +u("input[name=_pic_attachment]").on("change", handleUpload); diff --git a/Web/static/js/messagebox.js b/Web/static/js/messagebox.js index 1071cded..45791fd3 100644 --- a/Web/static/js/messagebox.js +++ b/Web/static/js/messagebox.js @@ -24,10 +24,13 @@ function MessageBox(title, body, buttons, callbacks) { }; Reflect.apply(callbacks[callback], { - closeDialog: () => __closeDialog() + closeDialog: () => __closeDialog(), + $dialog: () => u(".ovk-diag-cont") }, [e]); __closeDialog(); }); }); + + return u(".ovk-diag-cont"); } diff --git a/Web/static/js/package.json b/Web/static/js/package.json index 47f7023b..6426ac6f 100644 --- a/Web/static/js/package.json +++ b/Web/static/js/package.json @@ -1,11 +1,16 @@ { "dependencies": { "@atlassian/aui": "^8.5.1", + "create-react-class": "^15.7.0", "jquery": "^2.1.0", "knockout": "^3.5.1", "ky": "^0.19.0", + "literallycanvas": "^0.5.2", "monaco-editor": "^0.20.0", "plotly.js-dist": "^1.52.3", + "react": "15.1", + "react-dom": "15.1", + "react-dom-factories": "^1.0.2", "requirejs": "^2.3.6", "umbrellajs": "^3.1.0" } diff --git a/Web/static/js/vnd_literallycanvas.js b/Web/static/js/vnd_literallycanvas.js new file mode 100644 index 00000000..bc53231e --- /dev/null +++ b/Web/static/js/vnd_literallycanvas.js @@ -0,0 +1,6434 @@ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.LC = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;ofoo; + * } + * }); + * + * Note: This only checks shallow equality for props and state. If these contain + * complex data structures this mixin may have false-negatives for deeper + * differences. Only mixin to components which have simple props and state, or + * use `forceUpdate()` when you know deep data structures have changed. + */ +var ReactComponentWithPureRenderMixin = { + shouldComponentUpdate: function (nextProps, nextState) { + return shallowCompare(this, nextProps, nextState); + } +}; + +module.exports = ReactComponentWithPureRenderMixin; +},{"./shallowCompare":4}],4:[function(require,module,exports){ +/** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * +* @providesModule shallowCompare +*/ + +'use strict'; + +var shallowEqual = require('fbjs/lib/shallowEqual'); + +/** + * Does a shallow comparison for props and state. + * See ReactComponentWithPureRenderMixin + */ +function shallowCompare(instance, nextProps, nextState) { + return !shallowEqual(instance.props, nextProps) || !shallowEqual(instance.state, nextState); +} + +module.exports = shallowCompare; +},{"fbjs/lib/shallowEqual":1}],5:[function(require,module,exports){ +var INFINITE, JSONToShape, LiterallyCanvas, Pencil, actions, bindEvents, createShape, math, ref, renderShapeToContext, renderShapeToSVG, renderSnapshotToImage, renderSnapshotToSVG, shapeToJSON, util, + bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, + slice = [].slice, + 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; }; + +actions = require('./actions'); + +bindEvents = require('./bindEvents'); + +math = require('./math'); + +ref = require('./shapes'), createShape = ref.createShape, shapeToJSON = ref.shapeToJSON, JSONToShape = ref.JSONToShape; + +renderShapeToContext = require('./canvasRenderer').renderShapeToContext; + +renderShapeToSVG = require('./svgRenderer').renderShapeToSVG; + +renderSnapshotToImage = require('./renderSnapshotToImage'); + +renderSnapshotToSVG = require('./renderSnapshotToSVG'); + +Pencil = require('../tools/Pencil'); + +util = require('./util'); + +INFINITE = 'infinite'; + +module.exports = LiterallyCanvas = (function() { + function LiterallyCanvas(arg1, arg2) { + this.setImageSize = bind(this.setImageSize, this); + var containerEl, opts; + opts = null; + containerEl = null; + if (arg1 instanceof HTMLElement) { + containerEl = arg1; + opts = arg2; + } else { + opts = arg1; + } + this.opts = opts || {}; + this.config = { + zoomMin: opts.zoomMin || 0.2, + zoomMax: opts.zoomMax || 4.0, + zoomStep: opts.zoomStep || 0.2 + }; + this.colors = { + primary: opts.primaryColor || '#000', + secondary: opts.secondaryColor || '#fff', + background: opts.backgroundColor || 'transparent' + }; + this.watermarkImage = opts.watermarkImage; + this.watermarkScale = opts.watermarkScale || 1; + this.backgroundCanvas = document.createElement('canvas'); + this.backgroundCtx = this.backgroundCanvas.getContext('2d'); + this.canvas = document.createElement('canvas'); + this.canvas.style['background-color'] = 'transparent'; + this.buffer = document.createElement('canvas'); + this.buffer.style['background-color'] = 'transparent'; + this.ctx = this.canvas.getContext('2d'); + this.bufferCtx = this.buffer.getContext('2d'); + this.backingScale = util.getBackingScale(this.ctx); + this.backgroundShapes = opts.backgroundShapes || []; + this._shapesInProgress = []; + this.shapes = []; + this.undoStack = []; + this.redoStack = []; + this.isDragging = false; + this.position = { + x: 0, + y: 0 + }; + this.scale = 1.0; + this.setTool(new this.opts.tools[0](this)); + this.width = opts.imageSize.width || INFINITE; + this.height = opts.imageSize.height || INFINITE; + this.setZoom(this.scale); + if (opts.snapshot) { + this.loadSnapshot(opts.snapshot); + } + this.isBound = false; + if (containerEl) { + this.bindToElement(containerEl); + } + this.respondToSizeChange = function() {}; + } + + LiterallyCanvas.prototype.bindToElement = function(containerEl) { + var ref1, repaintAll; + if (this.containerEl) { + console.warn("Trying to bind Literally Canvas to a DOM element more than once is unsupported."); + return; + } + this.containerEl = containerEl; + this._unsubscribeEvents = bindEvents(this, this.containerEl, this.opts.keyboardShortcuts); + this.containerEl.style['background-color'] = this.colors.background; + this.containerEl.appendChild(this.backgroundCanvas); + this.containerEl.appendChild(this.canvas); + this.isBound = true; + repaintAll = (function(_this) { + return function() { + _this.keepPanInImageBounds(); + return _this.repaintAllLayers(); + }; + })(this); + this.respondToSizeChange = util.matchElementSize(this.containerEl, [this.backgroundCanvas, this.canvas], this.backingScale, repaintAll); + if (this.watermarkImage) { + this.watermarkImage.onload = (function(_this) { + return function() { + return _this.repaintLayer('background'); + }; + })(this); + } + if ((ref1 = this.tool) != null) { + ref1.didBecomeActive(this); + } + return repaintAll(); + }; + + LiterallyCanvas.prototype._teardown = function() { + var ref1; + if ((ref1 = this.tool) != null) { + ref1.willBecomeInactive(this); + } + if (typeof this._unsubscribeEvents === "function") { + this._unsubscribeEvents(); + } + this.tool = null; + this.containerEl = null; + return this.isBound = false; + }; + + LiterallyCanvas.prototype.trigger = function(name, data) { + this.canvas.dispatchEvent(new CustomEvent(name, { + detail: data + })); + return null; + }; + + LiterallyCanvas.prototype.on = function(name, fn) { + var wrapper; + wrapper = function(e) { + return fn(e.detail); + }; + this.canvas.addEventListener(name, wrapper); + return (function(_this) { + return function() { + return _this.canvas.removeEventListener(name, wrapper); + }; + })(this); + }; + + LiterallyCanvas.prototype.getRenderScale = function() { + return this.scale * this.backingScale; + }; + + LiterallyCanvas.prototype.clientCoordsToDrawingCoords = function(x, y) { + return { + x: (x * this.backingScale - this.position.x) / this.getRenderScale(), + y: (y * this.backingScale - this.position.y) / this.getRenderScale() + }; + }; + + LiterallyCanvas.prototype.drawingCoordsToClientCoords = function(x, y) { + return { + x: x * this.getRenderScale() + this.position.x, + y: y * this.getRenderScale() + this.position.y + }; + }; + + LiterallyCanvas.prototype.setImageSize = function(width, height) { + this.width = width || INFINITE; + this.height = height || INFINITE; + this.keepPanInImageBounds(); + this.repaintAllLayers(); + return this.trigger('imageSizeChange', { + width: this.width, + height: this.height + }); + }; + + LiterallyCanvas.prototype.setTool = function(tool) { + var ref1; + if (this.isBound) { + if ((ref1 = this.tool) != null) { + ref1.willBecomeInactive(this); + } + } + this.tool = tool; + this.trigger('toolChange', { + tool: tool + }); + if (this.isBound) { + return this.tool.didBecomeActive(this); + } + }; + + LiterallyCanvas.prototype.setShapesInProgress = function(newVal) { + return this._shapesInProgress = newVal; + }; + + LiterallyCanvas.prototype.pointerDown = function(x, y) { + var p; + p = this.clientCoordsToDrawingCoords(x, y); + if (this.tool.usesSimpleAPI) { + this.tool.begin(p.x, p.y, this); + this.isDragging = true; + return this.trigger("drawStart", { + tool: this.tool + }); + } else { + this.isDragging = true; + return this.trigger("lc-pointerdown", { + tool: this.tool, + x: p.x, + y: p.y, + rawX: x, + rawY: y + }); + } + }; + + LiterallyCanvas.prototype.pointerMove = function(x, y) { + return util.requestAnimationFrame((function(_this) { + return function() { + var p, ref1; + p = _this.clientCoordsToDrawingCoords(x, y); + if ((ref1 = _this.tool) != null ? ref1.usesSimpleAPI : void 0) { + if (_this.isDragging) { + _this.tool["continue"](p.x, p.y, _this); + return _this.trigger("drawContinue", { + tool: _this.tool + }); + } + } else { + if (_this.isDragging) { + return _this.trigger("lc-pointerdrag", { + tool: _this.tool, + x: p.x, + y: p.y, + rawX: x, + rawY: y + }); + } else { + return _this.trigger("lc-pointermove", { + tool: _this.tool, + x: p.x, + y: p.y, + rawX: x, + rawY: y + }); + } + } + }; + })(this)); + }; + + LiterallyCanvas.prototype.pointerUp = function(x, y) { + var p; + p = this.clientCoordsToDrawingCoords(x, y); + if (this.tool.usesSimpleAPI) { + if (this.isDragging) { + this.tool.end(p.x, p.y, this); + this.isDragging = false; + return this.trigger("drawEnd", { + tool: this.tool + }); + } + } else { + this.isDragging = false; + return this.trigger("lc-pointerup", { + tool: this.tool, + x: p.x, + y: p.y, + rawX: x, + rawY: y + }); + } + }; + + LiterallyCanvas.prototype.setColor = function(name, color) { + this.colors[name] = color; + if (!this.isBound) { + return; + } + switch (name) { + case 'background': + this.containerEl.style.backgroundColor = this.colors.background; + this.repaintLayer('background'); + break; + case 'primary': + this.repaintLayer('main'); + break; + case 'secondary': + this.repaintLayer('main'); + } + this.trigger(name + "ColorChange", this.colors[name]); + if (name === 'background') { + return this.trigger("drawingChange"); + } + }; + + LiterallyCanvas.prototype.getColor = function(name) { + return this.colors[name]; + }; + + LiterallyCanvas.prototype.saveShape = function(shape, triggerShapeSaveEvent, previousShapeId) { + if (triggerShapeSaveEvent == null) { + triggerShapeSaveEvent = true; + } + if (previousShapeId == null) { + previousShapeId = null; + } + if (!previousShapeId) { + previousShapeId = this.shapes.length ? this.shapes[this.shapes.length - 1].id : null; + } + this.execute(new actions.AddShapeAction(this, shape, previousShapeId)); + if (triggerShapeSaveEvent) { + this.trigger('shapeSave', { + shape: shape, + previousShapeId: previousShapeId + }); + } + return this.trigger('drawingChange'); + }; + + LiterallyCanvas.prototype.pan = function(x, y) { + return this.setPan(this.position.x - x, this.position.y - y); + }; + + LiterallyCanvas.prototype.keepPanInImageBounds = function() { + var ref1, renderScale, x, y; + renderScale = this.getRenderScale(); + ref1 = this.position, x = ref1.x, y = ref1.y; + if (this.width !== INFINITE) { + if (this.canvas.width > this.width * renderScale) { + x = (this.canvas.width - this.width * renderScale) / 2; + } else { + x = Math.max(Math.min(0, x), this.canvas.width - this.width * renderScale); + } + } + if (this.height !== INFINITE) { + if (this.canvas.height > this.height * renderScale) { + y = (this.canvas.height - this.height * renderScale) / 2; + } else { + y = Math.max(Math.min(0, y), this.canvas.height - this.height * renderScale); + } + } + return this.position = { + x: x, + y: y + }; + }; + + LiterallyCanvas.prototype.setPan = function(x, y) { + this.position = { + x: x, + y: y + }; + this.keepPanInImageBounds(); + this.repaintAllLayers(); + return this.trigger('pan', { + x: this.position.x, + y: this.position.y + }); + }; + + LiterallyCanvas.prototype.zoom = function(factor) { + var newScale; + newScale = this.scale + factor; + newScale = Math.max(newScale, this.config.zoomMin); + newScale = Math.min(newScale, this.config.zoomMax); + newScale = Math.round(newScale * 100) / 100; + return this.setZoom(newScale); + }; + + LiterallyCanvas.prototype.setZoom = function(scale) { + var oldScale; + oldScale = this.scale; + this.scale = scale; + this.position.x = math.scalePositionScalar(this.position.x, this.canvas.width, oldScale, this.scale); + this.position.y = math.scalePositionScalar(this.position.y, this.canvas.height, oldScale, this.scale); + this.keepPanInImageBounds(); + this.repaintAllLayers(); + return this.trigger('zoom', { + oldScale: oldScale, + newScale: this.scale + }); + }; + + LiterallyCanvas.prototype.setWatermarkImage = function(newImage) { + this.watermarkImage = newImage; + util.addImageOnload(newImage, (function(_this) { + return function() { + return _this.repaintLayer('background'); + }; + })(this)); + if (newImage.width) { + return this.repaintLayer('background'); + } + }; + + LiterallyCanvas.prototype.repaintAllLayers = function() { + var i, key, len, ref1; + ref1 = ['background', 'main']; + for (i = 0, len = ref1.length; i < len; i++) { + key = ref1[i]; + this.repaintLayer(key); + } + return null; + }; + + LiterallyCanvas.prototype.repaintLayer = function(repaintLayerKey, dirty) { + var retryCallback; + if (dirty == null) { + dirty = repaintLayerKey === 'main'; + } + if (!this.isBound) { + return; + } + switch (repaintLayerKey) { + case 'background': + this.backgroundCtx.clearRect(0, 0, this.backgroundCanvas.width, this.backgroundCanvas.height); + retryCallback = (function(_this) { + return function() { + return _this.repaintLayer('background'); + }; + })(this); + if (this.watermarkImage) { + this._renderWatermark(this.backgroundCtx, true, retryCallback); + } + this.draw(this.backgroundShapes, this.backgroundCtx, retryCallback); + break; + case 'main': + retryCallback = (function(_this) { + return function() { + return _this.repaintLayer('main', true); + }; + })(this); + if (dirty) { + this.buffer.width = this.canvas.width; + this.buffer.height = this.canvas.height; + this.bufferCtx.clearRect(0, 0, this.buffer.width, this.buffer.height); + this.draw(this.shapes, this.bufferCtx, retryCallback); + } + this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); + if (this.canvas.width > 0 && this.canvas.height > 0) { + this.ctx.fillStyle = '#ccc'; + this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); + this.clipped(((function(_this) { + return function() { + _this.ctx.clearRect(0, 0, _this.canvas.width, _this.canvas.height); + return _this.ctx.drawImage(_this.buffer, 0, 0); + }; + })(this)), this.ctx); + this.clipped(((function(_this) { + return function() { + return _this.transformed((function() { + var i, len, ref1, results, shape; + ref1 = _this._shapesInProgress; + results = []; + for (i = 0, len = ref1.length; i < len; i++) { + shape = ref1[i]; + results.push(renderShapeToContext(_this.ctx, shape, { + bufferCtx: _this.bufferCtx, + shouldOnlyDrawLatest: true + })); + } + return results; + }), _this.ctx, _this.bufferCtx); + }; + })(this)), this.ctx, this.bufferCtx); + } + } + return this.trigger('repaint', { + layerKey: repaintLayerKey + }); + }; + + LiterallyCanvas.prototype._renderWatermark = function(ctx, worryAboutRetina, retryCallback) { + if (worryAboutRetina == null) { + worryAboutRetina = true; + } + if (!this.watermarkImage.width) { + this.watermarkImage.onload = retryCallback; + return; + } + ctx.save(); + ctx.translate(ctx.canvas.width / 2, ctx.canvas.height / 2); + ctx.scale(this.watermarkScale, this.watermarkScale); + if (worryAboutRetina) { + ctx.scale(this.backingScale, this.backingScale); + } + ctx.drawImage(this.watermarkImage, -this.watermarkImage.width / 2, -this.watermarkImage.height / 2); + return ctx.restore(); + }; + + LiterallyCanvas.prototype.drawShapeInProgress = function(shape) { + this.repaintLayer('main', false); + return this.clipped(((function(_this) { + return function() { + return _this.transformed((function() { + return renderShapeToContext(_this.ctx, shape, { + bufferCtx: _this.bufferCtx, + shouldOnlyDrawLatest: true + }); + }), _this.ctx, _this.bufferCtx); + }; + })(this)), this.ctx, this.bufferCtx); + }; + + LiterallyCanvas.prototype.draw = function(shapes, ctx, retryCallback) { + var drawShapes; + if (!shapes.length) { + return; + } + drawShapes = (function(_this) { + return function() { + var i, len, results, shape; + results = []; + for (i = 0, len = shapes.length; i < len; i++) { + shape = shapes[i]; + results.push(renderShapeToContext(ctx, shape, { + retryCallback: retryCallback + })); + } + return results; + }; + })(this); + return this.clipped(((function(_this) { + return function() { + return _this.transformed(drawShapes, ctx); + }; + })(this)), ctx); + }; + + LiterallyCanvas.prototype.clipped = function() { + var contexts, ctx, fn, height, i, j, len, len1, results, width, x, y; + fn = arguments[0], contexts = 2 <= arguments.length ? slice.call(arguments, 1) : []; + x = this.width === INFINITE ? 0 : this.position.x; + y = this.height === INFINITE ? 0 : this.position.y; + width = (function() { + switch (this.width) { + case INFINITE: + return this.canvas.width; + default: + return this.width * this.getRenderScale(); + } + }).call(this); + height = (function() { + switch (this.height) { + case INFINITE: + return this.canvas.height; + default: + return this.height * this.getRenderScale(); + } + }).call(this); + for (i = 0, len = contexts.length; i < len; i++) { + ctx = contexts[i]; + ctx.save(); + ctx.beginPath(); + ctx.rect(x, y, width, height); + ctx.clip(); + } + fn(); + results = []; + for (j = 0, len1 = contexts.length; j < len1; j++) { + ctx = contexts[j]; + results.push(ctx.restore()); + } + return results; + }; + + LiterallyCanvas.prototype.transformed = function() { + var contexts, ctx, fn, i, j, len, len1, results, scale; + fn = arguments[0], contexts = 2 <= arguments.length ? slice.call(arguments, 1) : []; + for (i = 0, len = contexts.length; i < len; i++) { + ctx = contexts[i]; + ctx.save(); + ctx.translate(Math.floor(this.position.x), Math.floor(this.position.y)); + scale = this.getRenderScale(); + ctx.scale(scale, scale); + } + fn(); + results = []; + for (j = 0, len1 = contexts.length; j < len1; j++) { + ctx = contexts[j]; + results.push(ctx.restore()); + } + return results; + }; + + LiterallyCanvas.prototype.clear = function(triggerClearEvent) { + var newShapes, oldShapes; + if (triggerClearEvent == null) { + triggerClearEvent = true; + } + oldShapes = this.shapes; + newShapes = []; + this.setShapesInProgress([]); + this.execute(new actions.ClearAction(this, oldShapes, newShapes)); + this.repaintLayer('main'); + if (triggerClearEvent) { + this.trigger('clear', null); + } + return this.trigger('drawingChange', {}); + }; + + LiterallyCanvas.prototype.execute = function(action) { + this.undoStack.push(action); + action["do"](); + return this.redoStack = []; + }; + + LiterallyCanvas.prototype.undo = function() { + var action; + if (!this.undoStack.length) { + return; + } + action = this.undoStack.pop(); + action.undo(); + this.redoStack.push(action); + this.trigger('undo', { + action: action + }); + return this.trigger('drawingChange', {}); + }; + + LiterallyCanvas.prototype.redo = function() { + var action; + if (!this.redoStack.length) { + return; + } + action = this.redoStack.pop(); + this.undoStack.push(action); + action["do"](); + this.trigger('redo', { + action: action + }); + return this.trigger('drawingChange', {}); + }; + + LiterallyCanvas.prototype.canUndo = function() { + return !!this.undoStack.length; + }; + + LiterallyCanvas.prototype.canRedo = function() { + return !!this.redoStack.length; + }; + + LiterallyCanvas.prototype.getPixel = function(x, y) { + var p, pixel; + p = this.drawingCoordsToClientCoords(x, y); + pixel = this.ctx.getImageData(p.x, p.y, 1, 1).data; + if (pixel[3]) { + return "rgb(" + pixel[0] + ", " + pixel[1] + ", " + pixel[2] + ")"; + } else { + return null; + } + }; + + LiterallyCanvas.prototype.getContentBounds = function() { + return util.getBoundingRect((this.shapes.concat(this.backgroundShapes)).map(function(s) { + return s.getBoundingRect(); + }), this.width === INFINITE ? 0 : this.width, this.height === INFINITE ? 0 : this.height); + }; + + LiterallyCanvas.prototype.getDefaultImageRect = function(explicitSize, margin) { + var s; + if (explicitSize == null) { + explicitSize = { + width: 0, + height: 0 + }; + } + if (margin == null) { + margin = { + top: 0, + right: 0, + bottom: 0, + left: 0 + }; + } + return util.getDefaultImageRect((function() { + var i, len, ref1, results; + ref1 = this.shapes.concat(this.backgroundShapes); + results = []; + for (i = 0, len = ref1.length; i < len; i++) { + s = ref1[i]; + results.push(s.getBoundingRect(this.ctx)); + } + return results; + }).call(this), explicitSize, margin); + }; + + LiterallyCanvas.prototype.getImage = function(opts) { + if (opts == null) { + opts = {}; + } + if (opts.includeWatermark == null) { + opts.includeWatermark = true; + } + if (opts.scaleDownRetina == null) { + opts.scaleDownRetina = true; + } + if (opts.scale == null) { + opts.scale = 1; + } + if (!opts.scaleDownRetina) { + opts.scale *= this.backingScale; + } + if (opts.includeWatermark) { + opts.watermarkImage = this.watermarkImage; + opts.watermarkScale = this.watermarkScale; + if (!opts.scaleDownRetina) { + opts.watermarkScale *= this.backingScale; + } + } + return renderSnapshotToImage(this.getSnapshot(), opts); + }; + + LiterallyCanvas.prototype.canvasForExport = function() { + this.repaintAllLayers(); + return util.combineCanvases(this.backgroundCanvas, this.canvas); + }; + + LiterallyCanvas.prototype.canvasWithBackground = function(backgroundImageOrCanvas) { + return util.combineCanvases(backgroundImageOrCanvas, this.canvasForExport()); + }; + + LiterallyCanvas.prototype.getSnapshot = function(keys) { + var i, k, len, ref1, shape, snapshot; + if (keys == null) { + keys = null; + } + if (keys == null) { + keys = ['shapes', 'imageSize', 'colors', 'position', 'scale', 'backgroundShapes']; + } + snapshot = {}; + ref1 = ['colors', 'position', 'scale']; + for (i = 0, len = ref1.length; i < len; i++) { + k = ref1[i]; + if (indexOf.call(keys, k) >= 0) { + snapshot[k] = this[k]; + } + } + if (indexOf.call(keys, 'shapes') >= 0) { + snapshot.shapes = (function() { + var j, len1, ref2, results; + ref2 = this.shapes; + results = []; + for (j = 0, len1 = ref2.length; j < len1; j++) { + shape = ref2[j]; + results.push(shapeToJSON(shape)); + } + return results; + }).call(this); + } + if (indexOf.call(keys, 'backgroundShapes') >= 0) { + snapshot.backgroundShapes = (function() { + var j, len1, ref2, results; + ref2 = this.backgroundShapes; + results = []; + for (j = 0, len1 = ref2.length; j < len1; j++) { + shape = ref2[j]; + results.push(shapeToJSON(shape)); + } + return results; + }).call(this); + } + if (indexOf.call(keys, 'imageSize') >= 0) { + snapshot.imageSize = { + width: this.width, + height: this.height + }; + } + return snapshot; + }; + + LiterallyCanvas.prototype.getSnapshotJSON = function() { + console.warn("lc.getSnapshotJSON() is deprecated. use JSON.stringify(lc.getSnapshot()) instead."); + return JSON.stringify(this.getSnapshot()); + }; + + LiterallyCanvas.prototype.getSVGString = function(opts) { + if (opts == null) { + opts = {}; + } + return renderSnapshotToSVG(this.getSnapshot(), opts); + }; + + LiterallyCanvas.prototype.loadSnapshot = function(snapshot) { + var i, j, k, len, len1, ref1, ref2, s, shape, shapeRepr; + if (!snapshot) { + return; + } + if (snapshot.colors) { + ref1 = ['primary', 'secondary', 'background']; + for (i = 0, len = ref1.length; i < len; i++) { + k = ref1[i]; + this.setColor(k, snapshot.colors[k]); + } + } + if (snapshot.shapes) { + this.shapes = []; + ref2 = snapshot.shapes; + for (j = 0, len1 = ref2.length; j < len1; j++) { + shapeRepr = ref2[j]; + shape = JSONToShape(shapeRepr); + if (shape) { + this.execute(new actions.AddShapeAction(this, shape)); + } + } + } + if (snapshot.backgroundShapes) { + this.backgroundShapes = (function() { + var l, len2, ref3, results; + ref3 = snapshot.backgroundShapes; + results = []; + for (l = 0, len2 = ref3.length; l < len2; l++) { + s = ref3[l]; + results.push(JSONToShape(s)); + } + return results; + })(); + } + if (snapshot.imageSize) { + this.width = snapshot.imageSize.width; + this.height = snapshot.imageSize.height; + } + if (snapshot.position) { + this.position = snapshot.position; + } + if (snapshot.scale) { + this.scale = snapshot.scale; + } + this.repaintAllLayers(); + this.trigger('snapshotLoad'); + return this.trigger('drawingChange', {}); + }; + + LiterallyCanvas.prototype.loadSnapshotJSON = function(str) { + console.warn("lc.loadSnapshotJSON() is deprecated. use lc.loadSnapshot(JSON.parse(snapshot)) instead."); + return this.loadSnapshot(JSON.parse(str)); + }; + + return LiterallyCanvas; + +})(); + + +},{"../tools/Pencil":48,"./actions":7,"./bindEvents":8,"./canvasRenderer":9,"./math":14,"./renderSnapshotToImage":15,"./renderSnapshotToSVG":16,"./shapes":17,"./svgRenderer":18,"./util":19}],6:[function(require,module,exports){ +var TextRenderer, getLinesToRender, getNextLine, parseFontString; + +require('./fontmetrics.js'); + +parseFontString = function(font) { + var fontFamily, fontItems, fontSize, item, j, len, maybeSize, remainingFontString; + fontItems = font.split(' '); + fontSize = 0; + for (j = 0, len = fontItems.length; j < len; j++) { + item = fontItems[j]; + maybeSize = parseInt(item.replace("px", ""), 10); + if (!isNaN(maybeSize)) { + fontSize = maybeSize; + } + } + if (!fontSize) { + throw "Font size not found"; + } + remainingFontString = font.substring(fontItems[0].length + 1).replace('bold ', '').replace('italic ', '').replace('underline ', ''); + fontFamily = remainingFontString; + return { + fontSize: fontSize, + fontFamily: fontFamily + }; +}; + +getNextLine = function(ctx, text, forcedWidth) { + var doesSubstringFit, endIndex, isEndOfString, isNonWord, isWhitespace, lastGoodIndex, lastOkayIndex, nextWordStartIndex, textToHere, wasInWord; + if (!text.length) { + return ['', '']; + } + endIndex = 0; + lastGoodIndex = 0; + lastOkayIndex = 0; + wasInWord = false; + while (true) { + endIndex += 1; + isEndOfString = endIndex >= text.length; + isWhitespace = (!isEndOfString) && text[endIndex].match(/\s/); + isNonWord = isWhitespace || isEndOfString; + textToHere = text.substring(0, endIndex); + doesSubstringFit = forcedWidth ? ctx.measureTextWidth(textToHere).width <= forcedWidth : true; + if (doesSubstringFit) { + lastOkayIndex = endIndex; + } + if (isNonWord && wasInWord) { + wasInWord = false; + if (doesSubstringFit) { + lastGoodIndex = endIndex; + } + } + wasInWord = !isWhitespace; + if (isEndOfString || !doesSubstringFit) { + if (doesSubstringFit) { + return [text, '']; + } else if (lastGoodIndex > 0) { + nextWordStartIndex = lastGoodIndex + 1; + while (nextWordStartIndex < text.length && text[nextWordStartIndex].match('/\s/')) { + nextWordStartIndex += 1; + } + return [text.substring(0, lastGoodIndex), text.substring(nextWordStartIndex)]; + } else { + return [text.substring(0, lastOkayIndex), text.substring(lastOkayIndex)]; + } + } + } +}; + +getLinesToRender = function(ctx, text, forcedWidth) { + var j, len, lines, nextLine, ref, ref1, remainingText, textLine, textSplitOnLines; + textSplitOnLines = text.split(/\r\n|\r|\n/g); + lines = []; + for (j = 0, len = textSplitOnLines.length; j < len; j++) { + textLine = textSplitOnLines[j]; + ref = getNextLine(ctx, textLine, forcedWidth), nextLine = ref[0], remainingText = ref[1]; + if (nextLine) { + while (nextLine) { + lines.push(nextLine); + ref1 = getNextLine(ctx, remainingText, forcedWidth), nextLine = ref1[0], remainingText = ref1[1]; + } + } else { + lines.push(textLine); + } + } + return lines; +}; + +TextRenderer = (function() { + function TextRenderer(ctx, text1, font1, forcedWidth1, forcedHeight) { + var fontFamily, fontSize, ref; + this.text = text1; + this.font = font1; + this.forcedWidth = forcedWidth1; + this.forcedHeight = forcedHeight; + ref = parseFontString(this.font), fontFamily = ref.fontFamily, fontSize = ref.fontSize; + ctx.font = this.font; + ctx.textBaseline = 'baseline'; + this.emDashWidth = ctx.measureTextWidth('—', fontSize, fontFamily).width; + this.caratWidth = ctx.measureTextWidth('|', fontSize, fontFamily).width; + this.lines = getLinesToRender(ctx, this.text, this.forcedWidth); + this.metricses = this.lines.map((function(_this) { + return function(line) { + return ctx.measureText2(line || 'X', fontSize, _this.font); + }; + })(this)); + this.metrics = { + ascent: Math.max.apply(Math, this.metricses.map(function(arg) { + var ascent; + ascent = arg.ascent; + return ascent; + })), + descent: Math.max.apply(Math, this.metricses.map(function(arg) { + var descent; + descent = arg.descent; + return descent; + })), + fontsize: Math.max.apply(Math, this.metricses.map(function(arg) { + var fontsize; + fontsize = arg.fontsize; + return fontsize; + })), + leading: Math.max.apply(Math, this.metricses.map(function(arg) { + var leading; + leading = arg.leading; + return leading; + })), + width: Math.max.apply(Math, this.metricses.map(function(arg) { + var width; + width = arg.width; + return width; + })), + height: Math.max.apply(Math, this.metricses.map(function(arg) { + var height; + height = arg.height; + return height; + })), + bounds: { + minx: Math.min.apply(Math, this.metricses.map(function(arg) { + var bounds; + bounds = arg.bounds; + return bounds.minx; + })), + miny: Math.min.apply(Math, this.metricses.map(function(arg) { + var bounds; + bounds = arg.bounds; + return bounds.miny; + })), + maxx: Math.max.apply(Math, this.metricses.map(function(arg) { + var bounds; + bounds = arg.bounds; + return bounds.maxx; + })), + maxy: Math.max.apply(Math, this.metricses.map(function(arg) { + var bounds; + bounds = arg.bounds; + return bounds.maxy; + })) + } + }; + this.boundingBoxWidth = Math.ceil(this.metrics.width); + } + + TextRenderer.prototype.draw = function(ctx, x, y) { + var i, j, len, line, ref, results; + ctx.textBaseline = 'top'; + ctx.font = this.font; + i = 0; + ref = this.lines; + results = []; + for (j = 0, len = ref.length; j < len; j++) { + line = ref[j]; + ctx.fillText(line, x, y + i * this.metrics.leading); + results.push(i += 1); + } + return results; + }; + + TextRenderer.prototype.getWidth = function(isEditing) { + if (isEditing == null) { + isEditing = false; + } + if (this.forcedWidth) { + return this.forcedWidth; + } else { + if (isEditing) { + return this.metrics.bounds.maxx + this.caratWidth; + } else { + return this.metrics.bounds.maxx; + } + } + }; + + TextRenderer.prototype.getHeight = function() { + return this.forcedHeight || (this.metrics.leading * this.lines.length); + }; + + return TextRenderer; + +})(); + +module.exports = TextRenderer; + + +},{"./fontmetrics.js":11}],7:[function(require,module,exports){ +var AddShapeAction, ClearAction; + +ClearAction = (function() { + function ClearAction(lc1, oldShapes, newShapes1) { + this.lc = lc1; + this.oldShapes = oldShapes; + this.newShapes = newShapes1; + } + + ClearAction.prototype["do"] = function() { + this.lc.shapes = this.newShapes; + return this.lc.repaintLayer('main'); + }; + + ClearAction.prototype.undo = function() { + this.lc.shapes = this.oldShapes; + return this.lc.repaintLayer('main'); + }; + + return ClearAction; + +})(); + +AddShapeAction = (function() { + function AddShapeAction(lc1, shape1, previousShapeId) { + this.lc = lc1; + this.shape = shape1; + this.previousShapeId = previousShapeId != null ? previousShapeId : null; + } + + AddShapeAction.prototype["do"] = function() { + var found, i, len, newShapes, ref, shape; + if (!this.lc.shapes.length || this.lc.shapes[this.lc.shapes.length - 1].id === this.previousShapeId || this.previousShapeId === null) { + this.lc.shapes.push(this.shape); + } else { + newShapes = []; + found = false; + ref = this.lc.shapes; + for (i = 0, len = ref.length; i < len; i++) { + shape = ref[i]; + newShapes.push(shape); + if (shape.id === this.previousShapeId) { + newShapes.push(this.shape); + found = true; + } + } + if (!found) { + newShapes.push(this.shape); + } + this.lc.shapes = newShapes; + } + return this.lc.repaintLayer('main'); + }; + + AddShapeAction.prototype.undo = function() { + var i, len, newShapes, ref, shape; + if (this.lc.shapes[this.lc.shapes.length - 1].id === this.shape.id) { + this.lc.shapes.pop(); + } else { + newShapes = []; + ref = this.lc.shapes; + for (i = 0, len = ref.length; i < len; i++) { + shape = ref[i]; + if (shape.id !== this.shape.id) { + newShapes.push(shape); + } + } + lc.shapes = newShapes; + } + return this.lc.repaintLayer('main'); + }; + + return AddShapeAction; + +})(); + +module.exports = { + ClearAction: ClearAction, + AddShapeAction: AddShapeAction +}; + + +},{}],8:[function(require,module,exports){ +var bindEvents, buttonIsDown, coordsForTouchEvent, position; + +coordsForTouchEvent = function(el, e) { + var p, tx, ty; + tx = e.changedTouches[0].clientX; + ty = e.changedTouches[0].clientY; + p = el.getBoundingClientRect(); + return [tx - p.left, ty - p.top]; +}; + +position = function(el, e) { + var p; + p = el.getBoundingClientRect(); + return { + left: e.clientX - p.left, + top: e.clientY - p.top + }; +}; + +buttonIsDown = function(e) { + if (e.buttons != null) { + return e.buttons === 1; + } else { + return e.which > 0; + } +}; + +module.exports = bindEvents = function(lc, canvas, panWithKeyboard) { + var listener, mouseMoveListener, mouseUpListener, touchEndListener, touchMoveListener, unsubs; + if (panWithKeyboard == null) { + panWithKeyboard = false; + } + unsubs = []; + mouseMoveListener = (function(_this) { + return function(e) { + var p; + e.preventDefault(); + p = position(canvas, e); + return lc.pointerMove(p.left, p.top); + }; + })(this); + mouseUpListener = (function(_this) { + return function(e) { + var p; + e.preventDefault(); + canvas.onselectstart = function() { + return true; + }; + p = position(canvas, e); + lc.pointerUp(p.left, p.top); + document.removeEventListener('mousemove', mouseMoveListener); + document.removeEventListener('mouseup', mouseUpListener); + return canvas.addEventListener('mousemove', mouseMoveListener); + }; + })(this); + canvas.addEventListener('mousedown', (function(_this) { + return function(e) { + var down, p; + if (e.target.tagName.toLowerCase() !== 'canvas') { + return; + } + down = true; + e.preventDefault(); + canvas.onselectstart = function() { + return false; + }; + p = position(canvas, e); + lc.pointerDown(p.left, p.top); + canvas.removeEventListener('mousemove', mouseMoveListener); + document.addEventListener('mousemove', mouseMoveListener); + return document.addEventListener('mouseup', mouseUpListener); + }; + })(this)); + touchMoveListener = function(e) { + e.preventDefault(); + return lc.pointerMove.apply(lc, coordsForTouchEvent(canvas, e)); + }; + touchEndListener = function(e) { + e.preventDefault(); + lc.pointerUp.apply(lc, coordsForTouchEvent(canvas, e)); + document.removeEventListener('touchmove', touchMoveListener); + document.removeEventListener('touchend', touchEndListener); + return document.removeEventListener('touchcancel', touchEndListener); + }; + canvas.addEventListener('touchstart', function(e) { + if (e.target.tagName.toLowerCase() !== 'canvas') { + return; + } + e.preventDefault(); + if (e.touches.length === 1) { + lc.pointerDown.apply(lc, coordsForTouchEvent(canvas, e)); + document.addEventListener('touchmove', touchMoveListener); + document.addEventListener('touchend', touchEndListener); + return document.addEventListener('touchcancel', touchEndListener); + } else { + return lc.pointerMove.apply(lc, coordsForTouchEvent(canvas, e)); + } + }); + if (panWithKeyboard) { + console.warn("Keyboard panning is deprecated."); + listener = function(e) { + switch (e.keyCode) { + case 37: + lc.pan(-10, 0); + break; + case 38: + lc.pan(0, -10); + break; + case 39: + lc.pan(10, 0); + break; + case 40: + lc.pan(0, 10); + } + return lc.repaintAllLayers(); + }; + document.addEventListener('keydown', listener); + unsubs.push(function() { + return document.removeEventListener(listener); + }); + } + return function() { + var f, i, len, results; + results = []; + for (i = 0, len = unsubs.length; i < len; i++) { + f = unsubs[i]; + results.push(f()); + } + return results; + }; +}; + + +},{}],9:[function(require,module,exports){ +var _drawRawLinePath, defineCanvasRenderer, drawErasedLinePath, drawErasedLinePathLatest, drawLinePath, drawLinePathLatest, lineEndCapShapes, noop, renderShapeToCanvas, renderShapeToContext, renderers; + +lineEndCapShapes = require('./lineEndCapShapes'); + +renderers = {}; + +defineCanvasRenderer = function(shapeName, drawFunc, drawLatestFunc) { + return renderers[shapeName] = { + drawFunc: drawFunc, + drawLatestFunc: drawLatestFunc + }; +}; + +noop = function() {}; + +renderShapeToContext = function(ctx, shape, opts) { + var bufferCtx; + if (opts == null) { + opts = {}; + } + if (opts.shouldIgnoreUnsupportedShapes == null) { + opts.shouldIgnoreUnsupportedShapes = false; + } + if (opts.retryCallback == null) { + opts.retryCallback = noop; + } + if (opts.shouldOnlyDrawLatest == null) { + opts.shouldOnlyDrawLatest = false; + } + if (opts.bufferCtx == null) { + opts.bufferCtx = null; + } + bufferCtx = opts.bufferCtx; + if (renderers[shape.className]) { + if (opts.shouldOnlyDrawLatest && renderers[shape.className].drawLatestFunc) { + return renderers[shape.className].drawLatestFunc(ctx, bufferCtx, shape, opts.retryCallback); + } else { + return renderers[shape.className].drawFunc(ctx, shape, opts.retryCallback); + } + } else if (opts.shouldIgnoreUnsupportedShapes) { + return console.warn("Can't render shape of type " + shape.className + " to canvas"); + } else { + throw "Can't render shape of type " + shape.className + " to canvas"; + } +}; + +renderShapeToCanvas = function(canvas, shape, opts) { + return renderShapeToContext(canvas.getContext('2d'), shape, opts); +}; + +defineCanvasRenderer('Rectangle', function(ctx, shape) { + var x, y; + x = shape.x; + y = shape.y; + if (shape.strokeWidth % 2 !== 0) { + x += 0.5; + y += 0.5; + } + ctx.fillStyle = shape.fillColor; + ctx.fillRect(x, y, shape.width, shape.height); + ctx.lineWidth = shape.strokeWidth; + ctx.strokeStyle = shape.strokeColor; + return ctx.strokeRect(x, y, shape.width, shape.height); +}); + +defineCanvasRenderer('Ellipse', function(ctx, shape) { + var centerX, centerY, halfHeight, halfWidth; + ctx.save(); + halfWidth = Math.floor(shape.width / 2); + halfHeight = Math.floor(shape.height / 2); + centerX = shape.x + halfWidth; + centerY = shape.y + halfHeight; + ctx.translate(centerX, centerY); + ctx.scale(1, Math.abs(shape.height / shape.width)); + ctx.beginPath(); + ctx.arc(0, 0, Math.abs(halfWidth), 0, Math.PI * 2); + ctx.closePath(); + ctx.restore(); + ctx.fillStyle = shape.fillColor; + ctx.fill(); + ctx.lineWidth = shape.strokeWidth; + ctx.strokeStyle = shape.strokeColor; + return ctx.stroke(); +}); + +defineCanvasRenderer('SelectionBox', (function() { + var _drawHandle; + _drawHandle = function(ctx, arg, handleSize) { + var x, y; + x = arg.x, y = arg.y; + if (handleSize === 0) { + return; + } + ctx.fillStyle = '#fff'; + ctx.fillRect(x, y, handleSize, handleSize); + ctx.strokeStyle = '#000'; + return ctx.strokeRect(x, y, handleSize, handleSize); + }; + return function(ctx, shape) { + _drawHandle(ctx, shape.getTopLeftHandleRect(), shape.handleSize); + _drawHandle(ctx, shape.getTopRightHandleRect(), shape.handleSize); + _drawHandle(ctx, shape.getBottomLeftHandleRect(), shape.handleSize); + _drawHandle(ctx, shape.getBottomRightHandleRect(), shape.handleSize); + if (shape.backgroundColor) { + ctx.fillStyle = shape.backgroundColor; + ctx.fillRect(shape._br.x - shape.margin, shape._br.y - shape.margin, shape._br.width + shape.margin * 2, shape._br.height + shape.margin * 2); + } + ctx.lineWidth = 1; + ctx.strokeStyle = '#000'; + ctx.setLineDash([2, 4]); + ctx.strokeRect(shape._br.x - shape.margin, shape._br.y - shape.margin, shape._br.width + shape.margin * 2, shape._br.height + shape.margin * 2); + return ctx.setLineDash([]); + }; +})()); + +defineCanvasRenderer('Image', function(ctx, shape, retryCallback) { + if (shape.image.width) { + if (shape.scale === 1) { + return ctx.drawImage(shape.image, shape.x, shape.y); + } else { + return ctx.drawImage(shape.image, shape.x, shape.y, shape.image.width * shape.scale, shape.image.height * shape.scale); + } + } else if (retryCallback) { + return shape.image.onload = retryCallback; + } +}); + +defineCanvasRenderer('Line', function(ctx, shape) { + var arrowWidth, x1, x2, y1, y2; + if (shape.x1 === shape.x2 && shape.y1 === shape.y2) { + return; + } + x1 = shape.x1; + x2 = shape.x2; + y1 = shape.y1; + y2 = shape.y2; + if (shape.strokeWidth % 2 !== 0) { + x1 += 0.5; + x2 += 0.5; + y1 += 0.5; + y2 += 0.5; + } + ctx.lineWidth = shape.strokeWidth; + ctx.strokeStyle = shape.color; + ctx.lineCap = shape.capStyle; + if (shape.dash) { + ctx.setLineDash(shape.dash); + } + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.stroke(); + if (shape.dash) { + ctx.setLineDash([]); + } + arrowWidth = Math.max(shape.strokeWidth * 2.2, 5); + if (shape.endCapShapes[0]) { + lineEndCapShapes[shape.endCapShapes[0]].drawToCanvas(ctx, x1, y1, Math.atan2(y1 - y2, x1 - x2), arrowWidth, shape.color); + } + if (shape.endCapShapes[1]) { + return lineEndCapShapes[shape.endCapShapes[1]].drawToCanvas(ctx, x2, y2, Math.atan2(y2 - y1, x2 - x1), arrowWidth, shape.color); + } +}); + +_drawRawLinePath = function(ctx, points, close, lineCap) { + var i, len, point, ref; + if (close == null) { + close = false; + } + if (lineCap == null) { + lineCap = 'round'; + } + if (!points.length) { + return; + } + ctx.lineCap = lineCap; + ctx.strokeStyle = points[0].color; + ctx.lineWidth = points[0].size; + ctx.beginPath(); + if (points[0].size % 2 === 0) { + ctx.moveTo(points[0].x, points[0].y); + } else { + ctx.moveTo(points[0].x + 0.5, points[0].y + 0.5); + } + ref = points.slice(1); + for (i = 0, len = ref.length; i < len; i++) { + point = ref[i]; + if (points[0].size % 2 === 0) { + ctx.lineTo(point.x, point.y); + } else { + ctx.lineTo(point.x + 0.5, point.y + 0.5); + } + } + if (close) { + return ctx.closePath(); + } +}; + +drawLinePath = function(ctx, shape) { + _drawRawLinePath(ctx, shape.smoothedPoints); + return ctx.stroke(); +}; + +drawLinePathLatest = function(ctx, bufferCtx, shape) { + var drawEnd, drawStart, segmentStart; + if (shape.tail) { + segmentStart = shape.smoothedPoints.length - shape.segmentSize * shape.tailSize; + drawStart = segmentStart < shape.segmentSize * 2 ? 0 : segmentStart; + drawEnd = segmentStart + shape.segmentSize + 1; + _drawRawLinePath(bufferCtx, shape.smoothedPoints.slice(drawStart, drawEnd)); + return bufferCtx.stroke(); + } else { + _drawRawLinePath(bufferCtx, shape.smoothedPoints); + return bufferCtx.stroke(); + } +}; + +defineCanvasRenderer('LinePath', drawLinePath, drawLinePathLatest); + +drawErasedLinePath = function(ctx, shape) { + ctx.save(); + ctx.globalCompositeOperation = "destination-out"; + drawLinePath(ctx, shape); + return ctx.restore(); +}; + +drawErasedLinePathLatest = function(ctx, bufferCtx, shape) { + ctx.save(); + ctx.globalCompositeOperation = "destination-out"; + bufferCtx.save(); + bufferCtx.globalCompositeOperation = "destination-out"; + drawLinePathLatest(ctx, bufferCtx, shape); + ctx.restore(); + return bufferCtx.restore(); +}; + +defineCanvasRenderer('ErasedLinePath', drawErasedLinePath, drawErasedLinePathLatest); + +defineCanvasRenderer('Text', function(ctx, shape) { + if (!shape.renderer) { + shape._makeRenderer(ctx); + } + ctx.fillStyle = shape.color; + return shape.renderer.draw(ctx, shape.x, shape.y); +}); + +defineCanvasRenderer('Polygon', function(ctx, shape) { + ctx.fillStyle = shape.fillColor; + _drawRawLinePath(ctx, shape.points, shape.isClosed, 'butt'); + ctx.fill(); + return ctx.stroke(); +}); + +module.exports = { + defineCanvasRenderer: defineCanvasRenderer, + renderShapeToCanvas: renderShapeToCanvas, + renderShapeToContext: renderShapeToContext +}; + + +},{"./lineEndCapShapes":12}],10:[function(require,module,exports){ +'use strict'; + +module.exports = { + imageURLPrefix: 'lib/img', + primaryColor: 'hsla(0, 0%, 0%, 1)', + secondaryColor: 'hsla(0, 0%, 100%, 1)', + backgroundColor: 'transparent', + strokeWidths: [1, 2, 5, 10, 20, 30], + defaultStrokeWidth: 5, + toolbarPosition: 'top', + keyboardShortcuts: false, + imageSize: { width: 'infinite', height: 'infinite' }, + backgroundShapes: [], + watermarkImage: null, + watermarkScale: 1, + zoomMin: 0.2, + zoomMax: 4.0, + zoomStep: 0.2, + snapshot: null, + onInit: function onInit() {}, + tools: [require('../tools/Pencil'), require('../tools/Eraser'), require('../tools/Line'), require('../tools/Rectangle'), require('../tools/Ellipse'), require('../tools/Text'), require('../tools/Polygon'), require('../tools/Pan'), require('../tools/Eyedropper')] +}; + +},{"../tools/Ellipse":43,"../tools/Eraser":44,"../tools/Eyedropper":45,"../tools/Line":46,"../tools/Pan":47,"../tools/Pencil":48,"../tools/Polygon":49,"../tools/Rectangle":50,"../tools/Text":52}],11:[function(require,module,exports){ +"use strict"; + +/** + This library rewrites the Canvas2D "measureText" function + so that it returns a more complete metrics object. + This library is licensed under the MIT (Expat) license, + the text for which is included below. + +** ----------------------------------------------------------------------------- + + CHANGELOG: + + 2012-01-21 - Whitespace handling added by Joe Turner + (https://github.com/oampo) + + 2015-06-08 - Various hacks added by Steve Johnson + +** ----------------------------------------------------------------------------- + + Copyright (C) 2011 by Mike "Pomax" Kamermans + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +**/ +(function () { + var NAME = "FontMetrics Library"; + var VERSION = "1-2012.0121.1300"; + + // if there is no getComputedStyle, this library won't work. + if (!document.defaultView.getComputedStyle) { + throw "ERROR: 'document.defaultView.getComputedStyle' not found. This library only works in browsers that can report computed CSS values."; + } + + // store the old text metrics function on the Canvas2D prototype + CanvasRenderingContext2D.prototype.measureTextWidth = CanvasRenderingContext2D.prototype.measureText; + + /** + * shortcut function for getting computed CSS values + */ + var getCSSValue = function getCSSValue(element, property) { + return document.defaultView.getComputedStyle(element, null).getPropertyValue(property); + }; + + // debug function + var show = function show(canvas, ctx, xstart, w, h, metrics) { + document.body.appendChild(canvas); + ctx.strokeStyle = 'rgba(0, 0, 0, 0.5)'; + + ctx.beginPath(); + ctx.moveTo(xstart, 0); + ctx.lineTo(xstart, h); + ctx.closePath(); + ctx.stroke(); + + ctx.beginPath(); + ctx.moveTo(xstart + metrics.bounds.maxx, 0); + ctx.lineTo(xstart + metrics.bounds.maxx, h); + ctx.closePath(); + ctx.stroke(); + + ctx.beginPath(); + ctx.moveTo(0, h / 2 - metrics.ascent); + ctx.lineTo(w, h / 2 - metrics.ascent); + ctx.closePath(); + ctx.stroke(); + + ctx.beginPath(); + ctx.moveTo(0, h / 2 + metrics.descent); + ctx.lineTo(w, h / 2 + metrics.descent); + ctx.closePath(); + ctx.stroke(); + }; + + /** + * The new text metrics function + */ + CanvasRenderingContext2D.prototype.measureText2 = function (textstring, fontSize, fontString) { + var metrics = this.measureTextWidth(textstring), + isSpace = !/\S/.test(textstring); + metrics.fontsize = fontSize; + + // for text lead values, we meaure a multiline text container. + var leadDiv = document.createElement("div"); + leadDiv.style.position = "absolute"; + leadDiv.style.opacity = 0; + leadDiv.style.font = fontString; + leadDiv.innerHTML = textstring + "
" + textstring; + document.body.appendChild(leadDiv); + + // make some initial guess at the text leading (using the standard TeX ratio) + metrics.leading = 1.2 * fontSize; + + // then we try to get the real value from the browser + var leadDivHeight = getCSSValue(leadDiv, "height"); + leadDivHeight = leadDivHeight.replace("px", ""); + if (leadDivHeight >= fontSize * 2) { + metrics.leading = leadDivHeight / 2 | 0; + } + document.body.removeChild(leadDiv); + + // if we're not dealing with white space, we can compute metrics + if (!isSpace) { + // Have characters, so measure the text + var canvas = document.createElement("canvas"); + var padding = 100; + canvas.width = metrics.width + padding; + canvas.height = 3 * fontSize; + canvas.style.opacity = 1; + canvas.style.font = fontString; + var ctx = canvas.getContext("2d"); + ctx.font = fontString; + + var w = canvas.width, + h = canvas.height, + baseline = h / 2; + + // Set all canvas pixeldata values to 255, with all the content + // data being 0. This lets us scan for data[i] != 255. + ctx.fillStyle = "white"; + ctx.fillRect(-1, -1, w + 2, h + 2); + ctx.fillStyle = "black"; + ctx.fillText(textstring, padding / 2, baseline); + var pixelData = ctx.getImageData(0, 0, w, h).data; + + // canvas pixel data is w*4 by h*4, because R, G, B and A are separate, + // consecutive values in the array, rather than stored as 32 bit ints. + var i = 0, + w4 = w * 4, + len = pixelData.length; + + // Finding the ascent uses a normal, forward scanline + while (++i < len && pixelData[i] === 255) {} + var ascent = i / w4 | 0; + + // Finding the descent uses a reverse scanline + i = len - 1; + while (--i > 0 && pixelData[i] === 255) {} + var descent = i / w4 | 0; + + // find the min-x coordinate + for (i = 0; i < len && pixelData[i] === 255;) { + i += w4; + if (i >= len) { + i = i - len + 4; + } + } + var minx = i % w4 / 4 | 0; + + // find the max-x coordinate + var step = 1; + for (i = len - 3; i >= 0 && pixelData[i] === 255;) { + i -= w4; + if (i < 0) { + i = len - 3 - step++ * 4; + } + } + var maxx = i % w4 / 4 + 1 | 0; + + // set font metrics + metrics.ascent = baseline - ascent; + metrics.descent = descent - baseline; + metrics.bounds = { minx: minx - padding / 2, + maxx: maxx - padding / 2, + miny: 0, + maxy: descent - ascent }; + metrics.height = 1 + (descent - ascent); + } + + // if we ARE dealing with whitespace, most values will just be zero. + else { + // Only whitespace, so we can't measure the text + metrics.ascent = 0; + metrics.descent = 0; + metrics.bounds = { minx: 0, + maxx: metrics.width, // Best guess + miny: 0, + maxy: 0 }; + metrics.height = 0; + } + return metrics; + }; +})(); + +},{}],12:[function(require,module,exports){ +module.exports = { + arrow: (function() { + var getPoints; + getPoints = function(x, y, angle, width, length) { + return [ + { + x: x + Math.cos(angle + Math.PI / 2) * width / 2, + y: y + Math.sin(angle + Math.PI / 2) * width / 2 + }, { + x: x + Math.cos(angle) * length, + y: y + Math.sin(angle) * length + }, { + x: x + Math.cos(angle - Math.PI / 2) * width / 2, + y: y + Math.sin(angle - Math.PI / 2) * width / 2 + } + ]; + }; + return { + drawToCanvas: function(ctx, x, y, angle, width, color, length) { + var points; + if (length == null) { + length = 0; + } + length = length || width; + ctx.fillStyle = color; + ctx.lineWidth = 0; + ctx.strokeStyle = 'transparent'; + ctx.beginPath(); + points = getPoints(x, y, angle, width, length); + ctx.moveTo(points[0].x, points[0].y); + ctx.lineTo(points[1].x, points[1].y); + ctx.lineTo(points[2].x, points[2].y); + return ctx.fill(); + }, + svg: function(x, y, angle, width, color, length) { + var points; + if (length == null) { + length = 0; + } + length = length || width; + points = getPoints(x, y, angle, width, length); + return ""; + } + }; + })() +}; + + +},{}],13:[function(require,module,exports){ +var _, localize, strings; + +strings = {}; + +localize = function(localStrings) { + return strings = localStrings; +}; + +_ = function(string) { + var translation; + translation = strings[string]; + return translation || string; +}; + +module.exports = { + localize: localize, + _: _ +}; + + +},{}],14:[function(require,module,exports){ +var Point, _slope, math, normals, unit, util; + +Point = require('./shapes').Point; + +util = require('./util'); + +math = {}; + +math.toPoly = function(line) { + var i, index, len, n, point, polyLeft, polyRight; + polyLeft = []; + polyRight = []; + index = 0; + for (i = 0, len = line.length; i < len; i++) { + point = line[i]; + n = normals(point, _slope(line, index)); + polyLeft = polyLeft.concat([n[0]]); + polyRight = [n[1]].concat(polyRight); + index += 1; + } + return polyLeft.concat(polyRight); +}; + +_slope = function(line, index) { + var point; + if (line.length < 3) { + point = { + x: 0, + y: 0 + }; + } + if (index === 0) { + point = _slope(line, index + 1); + } else if (index === line.length - 1) { + point = _slope(line, index - 1); + } else { + point = math.diff(line[index - 1], line[index + 1]); + } + return point; +}; + +math.diff = function(a, b) { + return { + x: b.x - a.x, + y: b.y - a.y + }; +}; + +unit = function(vector) { + var length; + length = math.len(vector); + return { + x: vector.x / length, + y: vector.y / length + }; +}; + +normals = function(p, slope) { + slope = unit(slope); + slope.x = slope.x * p.size / 2; + slope.y = slope.y * p.size / 2; + return [ + { + x: p.x - slope.y, + y: p.y + slope.x, + color: p.color + }, { + x: p.x + slope.y, + y: p.y - slope.x, + color: p.color + } + ]; +}; + +math.len = function(vector) { + return Math.sqrt(Math.pow(vector.x, 2) + Math.pow(vector.y, 2)); +}; + +math.scalePositionScalar = function(val, viewportSize, oldScale, newScale) { + var newSize, oldSize; + oldSize = viewportSize * oldScale; + newSize = viewportSize * newScale; + return val + (oldSize - newSize) / 2; +}; + +module.exports = math; + + +},{"./shapes":17,"./util":19}],15:[function(require,module,exports){ +var INFINITE, JSONToShape, renderWatermark, util; + +util = require('./util'); + +JSONToShape = require('./shapes').JSONToShape; + +INFINITE = 'infinite'; + +renderWatermark = function(ctx, image, scale) { + if (!image.width) { + return; + } + ctx.save(); + ctx.translate(ctx.canvas.width / 2, ctx.canvas.height / 2); + ctx.scale(scale, scale); + ctx.drawImage(image, -image.width / 2, -image.height / 2); + return ctx.restore(); +}; + +module.exports = function(snapshot, opts) { + var allShapes, backgroundShapes, colors, imageSize, s, shapes, watermarkCanvas, watermarkCtx; + if (opts == null) { + opts = {}; + } + if (opts.scale == null) { + opts.scale = 1; + } + shapes = (function() { + var i, len, ref, results; + ref = snapshot.shapes; + results = []; + for (i = 0, len = ref.length; i < len; i++) { + s = ref[i]; + results.push(JSONToShape(s)); + } + return results; + })(); + backgroundShapes = []; + if (snapshot.backgroundShapes) { + backgroundShapes = (function() { + var i, len, ref, results; + ref = snapshot.backgroundShapes; + results = []; + for (i = 0, len = ref.length; i < len; i++) { + s = ref[i]; + results.push(JSONToShape(s)); + } + return results; + })(); + } + if (opts.margin == null) { + opts.margin = { + top: 0, + right: 0, + bottom: 0, + left: 0 + }; + } + imageSize = snapshot.imageSize || { + width: INFINITE, + height: INFINITE + }; + colors = snapshot.colors || { + background: 'transparent' + }; + allShapes = shapes.concat(backgroundShapes); + watermarkCanvas = document.createElement('canvas'); + watermarkCtx = watermarkCanvas.getContext('2d'); + if (opts.rect) { + opts.rect.x -= opts.margin.left; + opts.rect.y -= opts.margin.top; + opts.rect.width += opts.margin.left + opts.margin.right; + opts.rect.height += opts.margin.top + opts.margin.bottom; + } else { + opts.rect = util.getDefaultImageRect((function() { + var i, len, results; + results = []; + for (i = 0, len = allShapes.length; i < len; i++) { + s = allShapes[i]; + results.push(s.getBoundingRect(watermarkCtx)); + } + return results; + })(), imageSize, opts.margin); + } + watermarkCanvas.width = opts.rect.width * opts.scale; + watermarkCanvas.height = opts.rect.height * opts.scale; + watermarkCtx.fillStyle = colors.background; + watermarkCtx.fillRect(0, 0, watermarkCanvas.width, watermarkCanvas.height); + if (!(opts.rect.width && opts.rect.height)) { + return null; + } + if (opts.watermarkImage) { + renderWatermark(watermarkCtx, opts.watermarkImage, opts.watermarkScale); + } + return util.combineCanvases(watermarkCanvas, util.renderShapes(backgroundShapes, opts.rect, opts.scale), util.renderShapes(shapes, opts.rect, opts.scale)); +}; + + +},{"./shapes":17,"./util":19}],16:[function(require,module,exports){ +var INFINITE, JSONToShape, util; + +util = require('./util'); + +JSONToShape = require('./shapes').JSONToShape; + +INFINITE = 'infinite'; + +module.exports = function(snapshot, opts) { + var allShapes, backgroundShapes, colors, ctx, dummyCanvas, imageSize, s, shapes; + if (opts == null) { + opts = {}; + } + shapes = (function() { + var i, len, ref, results; + ref = snapshot.shapes; + results = []; + for (i = 0, len = ref.length; i < len; i++) { + s = ref[i]; + results.push(JSONToShape(s)); + } + return results; + })(); + backgroundShapes = []; + if (snapshot.backgroundShapes) { + backgroundShapes = (function() { + var i, len, ref, results; + ref = snapshot.backgroundShapes; + results = []; + for (i = 0, len = ref.length; i < len; i++) { + s = ref[i]; + results.push(JSONToShape(s)); + } + return results; + })(); + } + if (opts.margin == null) { + opts.margin = { + top: 0, + right: 0, + bottom: 0, + left: 0 + }; + } + imageSize = snapshot.imageSize || { + width: INFINITE, + height: INFINITE + }; + colors = snapshot.colors || { + background: 'transparent' + }; + allShapes = shapes.concat(backgroundShapes); + dummyCanvas = document.createElement('canvas'); + ctx = dummyCanvas.getContext('2d'); + if (opts.rect) { + opts.rect.x -= opts.margin.left; + opts.rect.y -= opts.margin.top; + opts.rect.width += opts.margin.left + opts.margin.right; + opts.rect.height += opts.margin.top + opts.margin.bottom; + } else { + opts.rect = util.getDefaultImageRect((function() { + var i, len, results; + results = []; + for (i = 0, len = allShapes.length; i < len; i++) { + s = allShapes[i]; + results.push(s.getBoundingRect(ctx)); + } + return results; + })(), imageSize, opts.margin); + } + return LC.renderShapesToSVG(backgroundShapes.concat(shapes), opts.rect, colors.background); +}; + + +},{"./shapes":17,"./util":19}],17:[function(require,module,exports){ +var JSONToShape, LinePath, TextRenderer, _createLinePathFromData, _doAllPointsShareStyle, _dual, _mid, _refine, bspline, createShape, defineCanvasRenderer, defineSVGRenderer, defineShape, lineEndCapShapes, linePathFuncs, ref, ref1, renderShapeToContext, renderShapeToSVG, shapeToJSON, shapes, util; + +util = require('./util'); + +TextRenderer = require('./TextRenderer'); + +lineEndCapShapes = require('./lineEndCapShapes'); + +ref = require('./canvasRenderer'), defineCanvasRenderer = ref.defineCanvasRenderer, renderShapeToContext = ref.renderShapeToContext; + +ref1 = require('./svgRenderer'), defineSVGRenderer = ref1.defineSVGRenderer, renderShapeToSVG = ref1.renderShapeToSVG; + +shapes = {}; + +defineShape = function(name, props) { + var Shape, drawFunc, drawLatestFunc, k, legacyDrawFunc, legacyDrawLatestFunc, legacySVGFunc, svgFunc; + Shape = function(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) { + props.constructor.call(this, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p); + return this; + }; + Shape.prototype.className = name; + Shape.fromJSON = props.fromJSON; + if (props.draw) { + legacyDrawFunc = props.draw; + legacyDrawLatestFunc = props.draw || function(ctx, bufferCtx, retryCallback) { + return this.draw(ctx, bufferCtx, retryCallback); + }; + drawFunc = function(ctx, shape, retryCallback) { + return legacyDrawFunc.call(shape, ctx, retryCallback); + }; + drawLatestFunc = function(ctx, bufferCtx, shape, retryCallback) { + return legacyDrawLatestFunc.call(shape, ctx, bufferCtx, retryCallback); + }; + delete props.draw; + if (props.drawLatest) { + delete props.drawLatest; + } + defineCanvasRenderer(name, drawFunc, drawLatestFunc); + } + if (props.toSVG) { + legacySVGFunc = props.toSVG; + svgFunc = function(shape) { + return legacySVGFunc.call(shape); + }; + delete props.toSVG; + defineSVGRenderer(name, svgFunc); + } + Shape.prototype.draw = function(ctx, retryCallback) { + return renderShapeToContext(ctx, this, { + retryCallback: retryCallback + }); + }; + Shape.prototype.drawLatest = function(ctx, bufferCtx, retryCallback) { + return renderShapeToContext(ctx, this, { + retryCallback: retryCallback, + bufferCtx: bufferCtx, + shouldOnlyDrawLatest: true + }); + }; + Shape.prototype.toSVG = function() { + return renderShapeToSVG(this); + }; + for (k in props) { + if (k !== 'fromJSON') { + Shape.prototype[k] = props[k]; + } + } + shapes[name] = Shape; + return Shape; +}; + +createShape = function(name, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) { + var s; + s = new shapes[name](a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p); + s.id = util.getGUID(); + return s; +}; + +JSONToShape = function(arg) { + var className, data, id, shape; + className = arg.className, data = arg.data, id = arg.id; + if (className in shapes) { + shape = shapes[className].fromJSON(data); + if (shape) { + if (id) { + shape.id = id; + } + return shape; + } else { + console.log('Unreadable shape:', className, data); + return null; + } + } else { + console.log("Unknown shape:", className, data); + return null; + } +}; + +shapeToJSON = function(shape) { + return { + className: shape.className, + data: shape.toJSON(), + id: shape.id + }; +}; + +bspline = function(points, order) { + if (!order) { + return points; + } + return bspline(_dual(_dual(_refine(points))), order - 1); +}; + +_refine = function(points) { + var index, len, point, q, refined; + points = [points[0]].concat(points).concat(util.last(points)); + refined = []; + index = 0; + for (q = 0, len = points.length; q < len; q++) { + point = points[q]; + refined[index * 2] = point; + if (points[index + 1]) { + refined[index * 2 + 1] = _mid(point, points[index + 1]); + } + index += 1; + } + return refined; +}; + +_dual = function(points) { + var dualed, index, len, point, q; + dualed = []; + index = 0; + for (q = 0, len = points.length; q < len; q++) { + point = points[q]; + if (points[index + 1]) { + dualed[index] = _mid(point, points[index + 1]); + } + index += 1; + } + return dualed; +}; + +_mid = function(a, b) { + return createShape('Point', { + x: a.x + ((b.x - a.x) / 2), + y: a.y + ((b.y - a.y) / 2), + size: a.size + ((b.size - a.size) / 2), + color: a.color + }); +}; + +defineShape('Image', { + constructor: function(args) { + if (args == null) { + args = {}; + } + this.x = args.x || 0; + this.y = args.y || 0; + this.scale = args.scale || 1; + return this.image = args.image || null; + }, + getBoundingRect: function() { + return { + x: this.x, + y: this.y, + width: this.image.width * this.scale, + height: this.image.height * this.scale + }; + }, + toJSON: function() { + return { + x: this.x, + y: this.y, + imageSrc: this.image.src, + imageObject: this.image, + scale: this.scale + }; + }, + fromJSON: function(data) { + var img, ref2; + img = null; + if ((ref2 = data.imageObject) != null ? ref2.width : void 0) { + img = data.imageObject; + } else { + img = new Image(); + img.src = data.imageSrc; + } + return createShape('Image', { + x: data.x, + y: data.y, + image: img, + scale: data.scale + }); + }, + move: function(moveInfo) { + if (moveInfo == null) { + moveInfo = {}; + } + this.x = this.x - moveInfo.xDiff; + return this.y = this.y - moveInfo.yDiff; + }, + setUpperLeft: function(upperLeft) { + if (upperLeft == null) { + upperLeft = {}; + } + this.x = upperLeft.x; + return this.y = upperLeft.y; + } +}); + +defineShape('Rectangle', { + constructor: function(args) { + if (args == null) { + args = {}; + } + this.x = args.x || 0; + this.y = args.y || 0; + this.width = args.width || 0; + this.height = args.height || 0; + this.strokeWidth = args.strokeWidth || 1; + this.strokeColor = args.strokeColor || 'black'; + return this.fillColor = args.fillColor || 'transparent'; + }, + getBoundingRect: function() { + return { + x: this.x - this.strokeWidth / 2, + y: this.y - this.strokeWidth / 2, + width: this.width + this.strokeWidth, + height: this.height + this.strokeWidth + }; + }, + toJSON: function() { + return { + x: this.x, + y: this.y, + width: this.width, + height: this.height, + strokeWidth: this.strokeWidth, + strokeColor: this.strokeColor, + fillColor: this.fillColor + }; + }, + fromJSON: function(data) { + return createShape('Rectangle', data); + }, + move: function(moveInfo) { + if (moveInfo == null) { + moveInfo = {}; + } + this.x = this.x - moveInfo.xDiff; + return this.y = this.y - moveInfo.yDiff; + }, + setUpperLeft: function(upperLeft) { + if (upperLeft == null) { + upperLeft = {}; + } + this.x = upperLeft.x; + return this.y = upperLeft.y; + } +}); + +defineShape('Ellipse', { + constructor: function(args) { + if (args == null) { + args = {}; + } + this.x = args.x || 0; + this.y = args.y || 0; + this.width = args.width || 0; + this.height = args.height || 0; + this.strokeWidth = args.strokeWidth || 1; + this.strokeColor = args.strokeColor || 'black'; + return this.fillColor = args.fillColor || 'transparent'; + }, + getBoundingRect: function() { + return { + x: this.x - this.strokeWidth / 2, + y: this.y - this.strokeWidth / 2, + width: this.width + this.strokeWidth, + height: this.height + this.strokeWidth + }; + }, + toJSON: function() { + return { + x: this.x, + y: this.y, + width: this.width, + height: this.height, + strokeWidth: this.strokeWidth, + strokeColor: this.strokeColor, + fillColor: this.fillColor + }; + }, + fromJSON: function(data) { + return createShape('Ellipse', data); + }, + move: function(moveInfo) { + if (moveInfo == null) { + moveInfo = {}; + } + this.x = this.x - moveInfo.xDiff; + return this.y = this.y - moveInfo.yDiff; + }, + setUpperLeft: function(upperLeft) { + if (upperLeft == null) { + upperLeft = {}; + } + this.x = upperLeft.x; + return this.y = upperLeft.y; + } +}); + +defineShape('Line', { + constructor: function(args) { + if (args == null) { + args = {}; + } + this.x1 = args.x1 || 0; + this.y1 = args.y1 || 0; + this.x2 = args.x2 || 0; + this.y2 = args.y2 || 0; + this.strokeWidth = args.strokeWidth || 1; + this.color = args.color || 'black'; + this.capStyle = args.capStyle || 'round'; + this.endCapShapes = args.endCapShapes || [null, null]; + return this.dash = args.dash || null; + }, + getBoundingRect: function() { + return { + x: Math.min(this.x1, this.x2) - this.strokeWidth / 2, + y: Math.min(this.y1, this.y2) - this.strokeWidth / 2, + width: Math.abs(this.x2 - this.x1) + this.strokeWidth / 2, + height: Math.abs(this.y2 - this.y1) + this.strokeWidth / 2 + }; + }, + toJSON: function() { + return { + x1: this.x1, + y1: this.y1, + x2: this.x2, + y2: this.y2, + strokeWidth: this.strokeWidth, + color: this.color, + capStyle: this.capStyle, + dash: this.dash, + endCapShapes: this.endCapShapes + }; + }, + fromJSON: function(data) { + return createShape('Line', data); + }, + move: function(moveInfo) { + if (moveInfo == null) { + moveInfo = {}; + } + this.x1 = this.x1 - moveInfo.xDiff; + this.y1 = this.y1 - moveInfo.yDiff; + this.x2 = this.x2 - moveInfo.xDiff; + return this.y2 = this.y2 - moveInfo.yDiff; + }, + setUpperLeft: function(upperLeft) { + var br, xDiff, yDiff; + if (upperLeft == null) { + upperLeft = {}; + } + br = this.getBoundingRect(); + xDiff = br.x - upperLeft.x; + yDiff = br.y - upperLeft.y; + return this.move({ + xDiff: xDiff, + yDiff: yDiff + }); + } +}); + +_doAllPointsShareStyle = function(points) { + var color, len, point, q, size; + if (!points.length) { + return false; + } + size = points[0].size; + color = points[0].color; + for (q = 0, len = points.length; q < len; q++) { + point = points[q]; + if (!(point.size === size && point.color === color)) { + console.log(size, color, point.size, point.color); + } + if (!(point.size === size && point.color === color)) { + return false; + } + } + return true; +}; + +_createLinePathFromData = function(shapeName, data) { + var pointData, points, smoothedPoints, x, y; + points = null; + if (data.points) { + points = (function() { + var len, q, ref2, results; + ref2 = data.points; + results = []; + for (q = 0, len = ref2.length; q < len; q++) { + pointData = ref2[q]; + results.push(JSONToShape(pointData)); + } + return results; + })(); + } else if (data.pointCoordinatePairs) { + points = (function() { + var len, q, ref2, ref3, results; + ref2 = data.pointCoordinatePairs; + results = []; + for (q = 0, len = ref2.length; q < len; q++) { + ref3 = ref2[q], x = ref3[0], y = ref3[1]; + results.push(JSONToShape({ + className: 'Point', + data: { + x: x, + y: y, + size: data.pointSize, + color: data.pointColor, + smooth: data.smooth + } + })); + } + return results; + })(); + } + smoothedPoints = null; + if (data.smoothedPointCoordinatePairs) { + smoothedPoints = (function() { + var len, q, ref2, ref3, results; + ref2 = data.smoothedPointCoordinatePairs; + results = []; + for (q = 0, len = ref2.length; q < len; q++) { + ref3 = ref2[q], x = ref3[0], y = ref3[1]; + results.push(JSONToShape({ + className: 'Point', + data: { + x: x, + y: y, + size: data.pointSize, + color: data.pointColor, + smooth: data.smooth + } + })); + } + return results; + })(); + } + if (!points[0]) { + return null; + } + return createShape(shapeName, { + points: points, + smoothedPoints: smoothedPoints, + order: data.order, + tailSize: data.tailSize, + smooth: data.smooth + }); +}; + +linePathFuncs = { + constructor: function(args) { + var len, point, points, q, results; + if (args == null) { + args = {}; + } + points = args.points || []; + this.order = args.order || 3; + this.tailSize = args.tailSize || 3; + this.smooth = 'smooth' in args ? args.smooth : true; + this.segmentSize = Math.pow(2, this.order); + this.sampleSize = this.tailSize + 1; + if (args.smoothedPoints) { + this.points = args.points; + return this.smoothedPoints = args.smoothedPoints; + } else { + this.points = []; + results = []; + for (q = 0, len = points.length; q < len; q++) { + point = points[q]; + results.push(this.addPoint(point)); + } + return results; + } + }, + getBoundingRect: function() { + return util.getBoundingRect(this.points.map(function(p) { + return { + x: p.x - p.size / 2, + y: p.y - p.size / 2, + width: p.size, + height: p.size + }; + })); + }, + toJSON: function() { + var p, point; + if (_doAllPointsShareStyle(this.points)) { + return { + order: this.order, + tailSize: this.tailSize, + smooth: this.smooth, + pointCoordinatePairs: (function() { + var len, q, ref2, results; + ref2 = this.points; + results = []; + for (q = 0, len = ref2.length; q < len; q++) { + point = ref2[q]; + results.push([point.x, point.y]); + } + return results; + }).call(this), + smoothedPointCoordinatePairs: (function() { + var len, q, ref2, results; + ref2 = this.smoothedPoints; + results = []; + for (q = 0, len = ref2.length; q < len; q++) { + point = ref2[q]; + results.push([point.x, point.y]); + } + return results; + }).call(this), + pointSize: this.points[0].size, + pointColor: this.points[0].color + }; + } else { + return { + order: this.order, + tailSize: this.tailSize, + smooth: this.smooth, + points: (function() { + var len, q, ref2, results; + ref2 = this.points; + results = []; + for (q = 0, len = ref2.length; q < len; q++) { + p = ref2[q]; + results.push(shapeToJSON(p)); + } + return results; + }).call(this) + }; + } + }, + fromJSON: function(data) { + return _createLinePathFromData('LinePath', data); + }, + addPoint: function(point) { + this.points.push(point); + if (!this.smooth) { + this.smoothedPoints = this.points; + return; + } + if (!this.smoothedPoints || this.points.length < this.sampleSize) { + return this.smoothedPoints = bspline(this.points, this.order); + } else { + this.tail = util.last(bspline(util.last(this.points, this.sampleSize), this.order), this.segmentSize * this.tailSize); + return this.smoothedPoints = this.smoothedPoints.slice(0, this.smoothedPoints.length - this.segmentSize * (this.tailSize - 1)).concat(this.tail); + } + }, + move: function(moveInfo) { + var len, pt, pts, q; + if (moveInfo == null) { + moveInfo = {}; + } + if (!this.smooth) { + pts = this.points; + } else { + pts = this.smoothedPoints; + } + for (q = 0, len = pts.length; q < len; q++) { + pt = pts[q]; + pt.move(moveInfo); + } + return this.points = this.smoothedPoints; + }, + setUpperLeft: function(upperLeft) { + var br, xDiff, yDiff; + if (upperLeft == null) { + upperLeft = {}; + } + br = this.getBoundingRect(); + xDiff = br.x - upperLeft.x; + yDiff = br.y - upperLeft.y; + return this.move({ + xDiff: xDiff, + yDiff: yDiff + }); + } +}; + +LinePath = defineShape('LinePath', linePathFuncs); + +defineShape('ErasedLinePath', { + constructor: linePathFuncs.constructor, + toJSON: linePathFuncs.toJSON, + addPoint: linePathFuncs.addPoint, + getBoundingRect: linePathFuncs.getBoundingRect, + fromJSON: function(data) { + return _createLinePathFromData('ErasedLinePath', data); + } +}); + +defineShape('Point', { + constructor: function(args) { + if (args == null) { + args = {}; + } + this.x = args.x || 0; + this.y = args.y || 0; + this.size = args.size || 0; + return this.color = args.color || ''; + }, + getBoundingRect: function() { + return { + x: this.x - this.size / 2, + y: this.y - this.size / 2, + width: this.size, + height: this.size + }; + }, + toJSON: function() { + return { + x: this.x, + y: this.y, + size: this.size, + color: this.color + }; + }, + fromJSON: function(data) { + return createShape('Point', data); + }, + move: function(moveInfo) { + if (moveInfo == null) { + moveInfo = {}; + } + this.x = this.x - moveInfo.xDiff; + return this.y = this.y - moveInfo.yDiff; + }, + setUpperLeft: function(upperLeft) { + if (upperLeft == null) { + upperLeft = {}; + } + this.x = upperLeft.x; + return this.y = upperLeft.y; + } +}); + +defineShape('Polygon', { + constructor: function(args) { + var len, point, q, ref2, results; + if (args == null) { + args = {}; + } + this.points = args.points; + this.fillColor = args.fillColor || 'white'; + this.strokeColor = args.strokeColor || 'black'; + this.strokeWidth = args.strokeWidth; + this.dash = args.dash || null; + if (args.isClosed == null) { + args.isClosed = true; + } + this.isClosed = args.isClosed; + ref2 = this.points; + results = []; + for (q = 0, len = ref2.length; q < len; q++) { + point = ref2[q]; + point.color = this.strokeColor; + results.push(point.size = this.strokeWidth); + } + return results; + }, + addPoint: function(x, y) { + return this.points.push(LC.createShape('Point', { + x: x, + y: y + })); + }, + getBoundingRect: function() { + return util.getBoundingRect(this.points.map(function(p) { + return p.getBoundingRect(); + })); + }, + toJSON: function() { + return { + strokeWidth: this.strokeWidth, + fillColor: this.fillColor, + strokeColor: this.strokeColor, + dash: this.dash, + isClosed: this.isClosed, + pointCoordinatePairs: this.points.map(function(p) { + return [p.x, p.y]; + }) + }; + }, + fromJSON: function(data) { + data.points = data.pointCoordinatePairs.map(function(arg) { + var x, y; + x = arg[0], y = arg[1]; + return createShape('Point', { + x: x, + y: y, + size: data.strokeWidth, + color: data.strokeColor + }); + }); + return createShape('Polygon', data); + }, + move: function(moveInfo) { + var len, pt, q, ref2, results; + if (moveInfo == null) { + moveInfo = {}; + } + ref2 = this.points; + results = []; + for (q = 0, len = ref2.length; q < len; q++) { + pt = ref2[q]; + results.push(pt.move(moveInfo)); + } + return results; + }, + setUpperLeft: function(upperLeft) { + var br, xDiff, yDiff; + if (upperLeft == null) { + upperLeft = {}; + } + br = this.getBoundingRect(); + xDiff = br.x - upperLeft.x; + yDiff = br.y - upperLeft.y; + return this.move({ + xDiff: xDiff, + yDiff: yDiff + }); + } +}); + +defineShape('Text', { + constructor: function(args) { + if (args == null) { + args = {}; + } + this.x = args.x || 0; + this.y = args.y || 0; + this.v = args.v || 0; + this.text = args.text || ''; + this.color = args.color || 'black'; + this.font = args.font || '18px sans-serif'; + this.forcedWidth = args.forcedWidth || null; + return this.forcedHeight = args.forcedHeight || null; + }, + _makeRenderer: function(ctx) { + ctx.lineHeight = 1.2; + this.renderer = new TextRenderer(ctx, this.text, this.font, this.forcedWidth, this.forcedHeight); + if (this.v < 1) { + console.log('repairing baseline'); + this.v = 1; + this.x -= this.renderer.metrics.bounds.minx; + return this.y -= this.renderer.metrics.leading - this.renderer.metrics.descent; + } + }, + setText: function(text) { + this.text = text; + return this.renderer = null; + }, + setFont: function(font) { + this.font = font; + return this.renderer = null; + }, + setPosition: function(x, y) { + this.x = x; + return this.y = y; + }, + setSize: function(forcedWidth, forcedHeight) { + this.forcedWidth = Math.max(forcedWidth, 0); + this.forcedHeight = Math.max(forcedHeight, 0); + return this.renderer = null; + }, + enforceMaxBoundingRect: function(lc) { + var br, dx, lcBoundingRect; + br = this.getBoundingRect(lc.ctx); + lcBoundingRect = { + x: -lc.position.x / lc.scale, + y: -lc.position.y / lc.scale, + width: lc.canvas.width / lc.scale, + height: lc.canvas.height / lc.scale + }; + if (br.x + br.width > lcBoundingRect.x + lcBoundingRect.width) { + dx = br.x - lcBoundingRect.x; + this.forcedWidth = lcBoundingRect.width - dx - 10; + return this.renderer = null; + } + }, + getBoundingRect: function(ctx, isEditing) { + if (isEditing == null) { + isEditing = false; + } + if (!this.renderer) { + if (ctx) { + this._makeRenderer(ctx); + } else { + throw "Must pass ctx if text hasn't been rendered yet"; + } + } + return { + x: Math.floor(this.x), + y: Math.floor(this.y), + width: Math.ceil(this.renderer.getWidth(true)), + height: Math.ceil(this.renderer.getHeight()) + }; + }, + toJSON: function() { + return { + x: this.x, + y: this.y, + text: this.text, + color: this.color, + font: this.font, + forcedWidth: this.forcedWidth, + forcedHeight: this.forcedHeight, + v: this.v + }; + }, + fromJSON: function(data) { + return createShape('Text', data); + }, + move: function(moveInfo) { + if (moveInfo == null) { + moveInfo = {}; + } + this.x = this.x - moveInfo.xDiff; + return this.y = this.y - moveInfo.yDiff; + }, + setUpperLeft: function(upperLeft) { + if (upperLeft == null) { + upperLeft = {}; + } + this.x = upperLeft.x; + return this.y = upperLeft.y; + } +}); + +defineShape('SelectionBox', { + constructor: function(args) { + if (args == null) { + args = {}; + } + this.shape = args.shape; + if (args.handleSize != null) { + this.handleSize = args.handleSize; + } else { + this.handleSize = 10; + } + this.margin = 4; + this.backgroundColor = args.backgroundColor || null; + return this._br = this.shape.getBoundingRect(args.ctx); + }, + toJSON: function() { + return { + shape: shapeToJSON(this.shape), + backgroundColor: this.backgroundColor + }; + }, + fromJSON: function(arg) { + var backgroundColor, handleSize, margin, shape; + shape = arg.shape, handleSize = arg.handleSize, margin = arg.margin, backgroundColor = arg.backgroundColor; + return createShape('SelectionBox', { + shape: JSONToShape(shape), + backgroundColor: backgroundColor + }); + }, + getTopLeftHandleRect: function() { + return { + x: this._br.x - this.handleSize - this.margin, + y: this._br.y - this.handleSize - this.margin, + width: this.handleSize, + height: this.handleSize + }; + }, + getBottomLeftHandleRect: function() { + return { + x: this._br.x - this.handleSize - this.margin, + y: this._br.y + this._br.height + this.margin, + width: this.handleSize, + height: this.handleSize + }; + }, + getTopRightHandleRect: function() { + return { + x: this._br.x + this._br.width + this.margin, + y: this._br.y - this.handleSize - this.margin, + width: this.handleSize, + height: this.handleSize + }; + }, + getBottomRightHandleRect: function() { + return { + x: this._br.x + this._br.width + this.margin, + y: this._br.y + this._br.height + this.margin, + width: this.handleSize, + height: this.handleSize + }; + }, + getBoundingRect: function() { + return { + x: this._br.x - this.margin, + y: this._br.y - this.margin, + width: this._br.width + this.margin * 2, + height: this._br.height + this.margin * 2 + }; + } +}); + +module.exports = { + defineShape: defineShape, + createShape: createShape, + JSONToShape: JSONToShape, + shapeToJSON: shapeToJSON +}; + + +},{"./TextRenderer":6,"./canvasRenderer":9,"./lineEndCapShapes":12,"./svgRenderer":18,"./util":19}],18:[function(require,module,exports){ +var defineSVGRenderer, lineEndCapShapes, renderShapeToSVG, renderers; + +lineEndCapShapes = require('./lineEndCapShapes'); + +renderers = {}; + +defineSVGRenderer = function(shapeName, shapeToSVGFunc) { + return renderers[shapeName] = shapeToSVGFunc; +}; + +renderShapeToSVG = function(shape, opts) { + if (opts == null) { + opts = {}; + } + if (opts.shouldIgnoreUnsupportedShapes == null) { + opts.shouldIgnoreUnsupportedShapes = false; + } + if (renderers[shape.className]) { + return renderers[shape.className](shape); + } else if (opts.shouldIgnoreUnsupportedShapes) { + console.warn("Can't render shape of type " + shape.className + " to SVG"); + return ""; + } else { + throw "Can't render shape of type " + shape.className + " to SVG"; + } +}; + +defineSVGRenderer('Rectangle', function(shape) { + var height, width, x, x1, x2, y, y1, y2; + x1 = shape.x; + y1 = shape.y; + x2 = shape.x + shape.width; + y2 = shape.y + shape.height; + x = Math.min(x1, x2); + y = Math.min(y1, y2); + width = Math.max(x1, x2) - x; + height = Math.max(y1, y2) - y; + if (shape.strokeWidth % 2 !== 0) { + x += 0.5; + y += 0.5; + } + return ""; +}); + +defineSVGRenderer('SelectionBox', function(shape) { + return ""; +}); + +defineSVGRenderer('Ellipse', function(shape) { + var centerX, centerY, halfHeight, halfWidth; + halfWidth = Math.floor(shape.width / 2); + halfHeight = Math.floor(shape.height / 2); + centerX = shape.x + halfWidth; + centerY = shape.y + halfHeight; + return ""; +}); + +defineSVGRenderer('Image', function(shape) { + return ""; +}); + +defineSVGRenderer('Line', function(shape) { + var arrowWidth, capString, dashString, x1, x2, y1, y2; + dashString = shape.dash ? "stroke-dasharray='" + (shape.dash.join(', ')) + "'" : ''; + capString = ''; + arrowWidth = Math.max(shape.strokeWidth * 2.2, 5); + x1 = shape.x1; + x2 = shape.x2; + y1 = shape.y1; + y2 = shape.y2; + if (shape.strokeWidth % 2 !== 0) { + x1 += 0.5; + x2 += 0.5; + y1 += 0.5; + y2 += 0.5; + } + if (shape.endCapShapes[0]) { + capString += lineEndCapShapes[shape.endCapShapes[0]].svg(x1, y1, Math.atan2(y1 - y2, x1 - x2), arrowWidth, shape.color); + } + if (shape.endCapShapes[1]) { + capString += lineEndCapShapes[shape.endCapShapes[1]].svg(x2, y2, Math.atan2(y2 - y1, x2 - x1), arrowWidth, shape.color); + } + return " " + capString + " "; +}); + +defineSVGRenderer('LinePath', function(shape) { + return ""; +}); + +defineSVGRenderer('ErasedLinePath', function(shape) { + return ""; +}); + +defineSVGRenderer('Polygon', function(shape) { + if (shape.isClosed) { + return ""; + } else { + return " "; + } +}); + +defineSVGRenderer('Text', function(shape) { + var heightString, textSplitOnLines, widthString; + widthString = shape.forcedWidth ? "width='" + shape.forcedWidth + "px'" : ""; + heightString = shape.forcedHeight ? "height='" + shape.forcedHeight + "px'" : ""; + textSplitOnLines = shape.text.split(/\r\n|\r|\n/g); + if (shape.renderer) { + textSplitOnLines = shape.renderer.lines; + } + return " " + (textSplitOnLines.map((function(_this) { + return function(line, i) { + var dy; + dy = i === 0 ? 0 : '1.2em'; + return " " + line + " "; + }; + })(this)).join('')) + " "; +}); + +module.exports = { + defineSVGRenderer: defineSVGRenderer, + renderShapeToSVG: renderShapeToSVG +}; + + +},{"./lineEndCapShapes":12}],19:[function(require,module,exports){ +var renderShapeToContext, renderShapeToSVG, slice, util, + slice1 = [].slice; + +slice = Array.prototype.slice; + +renderShapeToContext = require('./canvasRenderer').renderShapeToContext; + +renderShapeToSVG = require('./svgRenderer').renderShapeToSVG; + +util = { + addImageOnload: function(img, fn) { + var oldOnload; + oldOnload = img.onload; + img.onload = function() { + if (typeof oldOnload === "function") { + oldOnload(); + } + return fn(); + }; + return img; + }, + last: function(array, n) { + if (n == null) { + n = null; + } + if (n) { + return slice.call(array, Math.max(array.length - n, 0)); + } else { + return array[array.length - 1]; + } + }, + classSet: function(classNameToIsPresent) { + var classNames, key; + classNames = []; + for (key in classNameToIsPresent) { + if (classNameToIsPresent[key]) { + classNames.push(key); + } + } + return classNames.join(' '); + }, + matchElementSize: function(elementToMatch, elementsToResize, scale, callback) { + var resize; + if (callback == null) { + callback = function() {}; + } + resize = (function(_this) { + return function() { + var el, i, len; + for (i = 0, len = elementsToResize.length; i < len; i++) { + el = elementsToResize[i]; + el.style.width = elementToMatch.offsetWidth + "px"; + el.style.height = elementToMatch.offsetHeight + "px"; + if (el.width != null) { + el.setAttribute('width', el.offsetWidth * scale); + el.setAttribute('height', el.offsetHeight * scale); + } + } + return callback(); + }; + })(this); + elementToMatch.addEventListener('resize', resize); + window.addEventListener('resize', resize); + window.addEventListener('orientationchange', resize); + resize(); + return resize; + }, + combineCanvases: function() { + var c, canvas, canvases, ctx, i, j, len, len1; + canvases = 1 <= arguments.length ? slice1.call(arguments, 0) : []; + c = document.createElement('canvas'); + c.width = canvases[0].width; + c.height = canvases[0].height; + for (i = 0, len = canvases.length; i < len; i++) { + canvas = canvases[i]; + c.width = Math.max(canvas.width, c.width); + c.height = Math.max(canvas.height, c.height); + } + ctx = c.getContext('2d'); + for (j = 0, len1 = canvases.length; j < len1; j++) { + canvas = canvases[j]; + ctx.drawImage(canvas, 0, 0); + } + return c; + }, + renderShapes: function(shapes, bounds, scale, canvas) { + var ctx, i, len, shape; + if (scale == null) { + scale = 1; + } + if (canvas == null) { + canvas = null; + } + canvas = canvas || document.createElement('canvas'); + canvas.width = bounds.width * scale; + canvas.height = bounds.height * scale; + ctx = canvas.getContext('2d'); + ctx.translate(-bounds.x * scale, -bounds.y * scale); + ctx.scale(scale, scale); + for (i = 0, len = shapes.length; i < len; i++) { + shape = shapes[i]; + renderShapeToContext(ctx, shape); + } + return canvas; + }, + renderShapesToSVG: function(shapes, arg, backgroundColor) { + var height, width, x, y; + x = arg.x, y = arg.y, width = arg.width, height = arg.height; + return (" " + (shapes.map(renderShapeToSVG).join('')) + " ").replace(/(\r\n|\n|\r)/gm, ""); + }, + getBoundingRect: function(rects, width, height) { + var i, len, maxX, maxY, minX, minY, rect; + if (!rects.length) { + return { + x: 0, + y: 0, + width: 0 || width, + height: 0 || height + }; + } + minX = rects[0].x; + minY = rects[0].y; + maxX = rects[0].x + rects[0].width; + maxY = rects[0].y + rects[0].height; + for (i = 0, len = rects.length; i < len; i++) { + rect = rects[i]; + minX = Math.floor(Math.min(rect.x, minX)); + minY = Math.floor(Math.min(rect.y, minY)); + maxX = Math.ceil(Math.max(maxX, rect.x + rect.width)); + maxY = Math.ceil(Math.max(maxY, rect.y + rect.height)); + } + minX = width ? 0 : minX; + minY = height ? 0 : minY; + maxX = width || maxX; + maxY = height || maxY; + return { + x: minX, + y: minY, + width: maxX - minX, + height: maxY - minY + }; + }, + getDefaultImageRect: function(shapeBoundingRects, explicitSize, margin) { + var height, rect, width; + if (explicitSize == null) { + explicitSize = { + width: 0, + height: 0 + }; + } + if (margin == null) { + margin = { + top: 0, + right: 0, + bottom: 0, + left: 0 + }; + } + width = explicitSize.width, height = explicitSize.height; + rect = util.getBoundingRect(shapeBoundingRects, width === 'infinite' ? 0 : width, height === 'infinite' ? 0 : height); + rect.x -= margin.left; + rect.y -= margin.top; + rect.width += margin.left + margin.right; + rect.height += margin.top + margin.bottom; + return rect; + }, + getBackingScale: function(context) { + if (window.devicePixelRatio == null) { + return 1; + } + if (!(window.devicePixelRatio > 1)) { + return 1; + } + return window.devicePixelRatio; + }, + requestAnimationFrame: (window.requestAnimationFrame || window.setTimeout).bind(window), + getGUID: (function() { + var s4; + s4 = function() { + return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1); + }; + return function() { + return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); + }; + })(), + requestAnimationFrame: function(f) { + if (window.requestAnimationFrame) { + return window.requestAnimationFrame(f); + } + if (window.webkitRequestAnimationFrame) { + return window.webkitRequestAnimationFrame(f); + } + if (window.mozRequestAnimationFrame) { + return window.mozRequestAnimationFrame(f); + } + return setTimeout(f, 0); + }, + cancelAnimationFrame: function(f) { + if (window.cancelAnimationFrame) { + return window.cancelAnimationFrame(f); + } + if (window.webkitCancelRequestAnimationFrame) { + return window.webkitCancelRequestAnimationFrame(f); + } + if (window.webkitCancelAnimationFrame) { + return window.webkitCancelAnimationFrame(f); + } + if (window.mozCancelAnimationFrame) { + return window.mozCancelAnimationFrame(f); + } + return clearTimeout(f); + } +}; + +module.exports = util; + + +},{"./canvasRenderer":9,"./svgRenderer":18}],20:[function(require,module,exports){ +'use strict'; + +(function () { + function CustomEvent(event, params) { + params = params || { bubbles: false, cancelable: false, detail: undefined }; + var evt = document.createEvent('CustomEvent'); + evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail); + return evt; + }; + + CustomEvent.prototype = window.CustomEvent.prototype; + + window.CustomEvent = CustomEvent; +})(); + +},{}],21:[function(require,module,exports){ +"use strict"; + +var hasWarned = false; +if (!CanvasRenderingContext2D.prototype.setLineDash) { + CanvasRenderingContext2D.prototype.setLineDash = function () { + // no-op + if (!hasWarned) { + console.warn("context2D.setLineDash is a no-op in this browser."); + hasWarned = true; + } + }; +} +module.exports = null; + +},{}],22:[function(require,module,exports){ +var LiterallyCanvasModel, LiterallyCanvasReactComponent, baseTools, canvasRenderer, conversion, defaultImageURLPrefix, defaultOptions, defaultTools, defineOptionsStyle, init, initReactDOM, initWithoutGUI, localize, registerJQueryPlugin, renderSnapshotToImage, renderSnapshotToSVG, setDefaultImageURLPrefix, shapes, svgRenderer, tools, util; + +require('./ie_customevent'); + +require('./ie_setLineDash'); + +LiterallyCanvasModel = require('./core/LiterallyCanvas'); + +defaultOptions = require('./core/defaultOptions'); + +canvasRenderer = require('./core/canvasRenderer'); + +svgRenderer = require('./core/svgRenderer'); + +shapes = require('./core/shapes'); + +util = require('./core/util'); + +renderSnapshotToImage = require('./core/renderSnapshotToImage'); + +renderSnapshotToSVG = require('./core/renderSnapshotToSVG'); + +localize = require('./core/localization').localize; + +LiterallyCanvasReactComponent = require('./reactGUI/LiterallyCanvas'); + +initReactDOM = require('./reactGUI/initDOM'); + +require('./optionsStyles/font'); + +require('./optionsStyles/stroke-width'); + +require('./optionsStyles/line-options-and-stroke-width'); + +require('./optionsStyles/polygon-and-stroke-width'); + +require('./optionsStyles/stroke-or-fill'); + +require('./optionsStyles/null'); + +defineOptionsStyle = require('./optionsStyles/optionsStyles').defineOptionsStyle; + +conversion = { + snapshotToShapes: function(snapshot) { + var i, len, ref, results, shape; + ref = snapshot.shapes; + results = []; + for (i = 0, len = ref.length; i < len; i++) { + shape = ref[i]; + results.push(shapes.JSONToShape(shape)); + } + return results; + }, + snapshotJSONToShapes: function(json) { + return conversion.snapshotToShapes(JSON.parse(json)); + } +}; + +baseTools = require('./tools/base'); + +tools = { + Pencil: require('./tools/Pencil'), + Eraser: require('./tools/Eraser'), + Line: require('./tools/Line'), + Rectangle: require('./tools/Rectangle'), + Ellipse: require('./tools/Ellipse'), + Text: require('./tools/Text'), + Polygon: require('./tools/Polygon'), + Pan: require('./tools/Pan'), + Eyedropper: require('./tools/Eyedropper'), + SelectShape: require('./tools/SelectShape'), + Tool: baseTools.Tool, + ToolWithStroke: baseTools.ToolWithStroke +}; + +defaultTools = defaultOptions.tools; + +defaultImageURLPrefix = defaultOptions.imageURLPrefix; + +setDefaultImageURLPrefix = function(newDefault) { + defaultImageURLPrefix = newDefault; + return defaultOptions.imageURLPrefix = newDefault; +}; + +init = function(el, opts) { + var child, i, len, opt, ref; + if (opts == null) { + opts = {}; + } + for (opt in defaultOptions) { + if (!(opt in opts)) { + opts[opt] = defaultOptions[opt]; + } + } + ref = el.children; + for (i = 0, len = ref.length; i < len; i++) { + child = ref[i]; + el.removeChild(child); + } + return require('./reactGUI/initDOM')(el, opts); +}; + +initWithoutGUI = function(el, opts) { + var drawingViewElement, lc, originalClassName; + originalClassName = el.className; + if ([' ', ' '].join(el.className).indexOf(' literally ') === -1) { + el.className = el.className + ' literally'; + } + el.className = el.className + ' toolbar-hidden'; + drawingViewElement = document.createElement('div'); + drawingViewElement.className = 'lc-drawing'; + el.appendChild(drawingViewElement); + lc = new LiterallyCanvasModel(drawingViewElement, opts); + lc.teardown = function() { + var child, i, len, ref; + lc._teardown(); + ref = el.children; + for (i = 0, len = ref.length; i < len; i++) { + child = ref[i]; + el.removeChild(child); + } + return el.className = originalClassName; + }; + if ('onInit' in opts) { + opts.onInit(lc); + } + return lc; +}; + +registerJQueryPlugin = function(_$) { + return _$.fn.literallycanvas = function(opts) { + if (opts == null) { + opts = {}; + } + this.each((function(_this) { + return function(ix, el) { + return el.literallycanvas = init(el, opts); + }; + })(this)); + return this; + }; +}; + +if (typeof window !== 'undefined') { + window.LC = { + init: init + }; + if (window.$) { + registerJQueryPlugin(window.$); + } +} + +module.exports = { + init: init, + registerJQueryPlugin: registerJQueryPlugin, + util: util, + tools: tools, + setDefaultImageURLPrefix: setDefaultImageURLPrefix, + defaultTools: defaultTools, + defineOptionsStyle: defineOptionsStyle, + LiterallyCanvasReactComponent: LiterallyCanvasReactComponent, + defineShape: shapes.defineShape, + createShape: shapes.createShape, + JSONToShape: shapes.JSONToShape, + shapeToJSON: shapes.shapeToJSON, + defineCanvasRenderer: canvasRenderer.defineCanvasRenderer, + renderShapeToContext: canvasRenderer.renderShapeToContext, + renderShapeToCanvas: canvasRenderer.renderShapeToCanvas, + renderShapesToCanvas: util.renderShapes, + defineSVGRenderer: svgRenderer.defineSVGRenderer, + renderShapeToSVG: svgRenderer.renderShapeToSVG, + renderShapesToSVG: util.renderShapesToSVG, + snapshotToShapes: conversion.snapshotToShapes, + snapshotJSONToShapes: conversion.snapshotJSONToShapes, + renderSnapshotToImage: renderSnapshotToImage, + renderSnapshotToSVG: renderSnapshotToSVG, + localize: localize +}; + + +},{"./core/LiterallyCanvas":5,"./core/canvasRenderer":9,"./core/defaultOptions":10,"./core/localization":13,"./core/renderSnapshotToImage":15,"./core/renderSnapshotToSVG":16,"./core/shapes":17,"./core/svgRenderer":18,"./core/util":19,"./ie_customevent":20,"./ie_setLineDash":21,"./optionsStyles/font":23,"./optionsStyles/line-options-and-stroke-width":24,"./optionsStyles/null":25,"./optionsStyles/optionsStyles":26,"./optionsStyles/polygon-and-stroke-width":27,"./optionsStyles/stroke-or-fill":28,"./optionsStyles/stroke-width":29,"./reactGUI/LiterallyCanvas":32,"./reactGUI/initDOM":42,"./tools/Ellipse":43,"./tools/Eraser":44,"./tools/Eyedropper":45,"./tools/Line":46,"./tools/Pan":47,"./tools/Pencil":48,"./tools/Polygon":49,"./tools/Rectangle":50,"./tools/SelectShape":51,"./tools/Text":52,"./tools/base":53}],23:[function(require,module,exports){ +var ALL_FONTS, FONT_NAME_TO_VALUE, MONOSPACE_FONTS, OTHER_FONTS, React, SANS_SERIF_FONTS, SERIF_FONTS, _, defineOptionsStyle, i, j, l, len, len1, len2, len3, m, name, ref, ref1, ref2, ref3, value; + +React = require('../reactGUI/React-shim'); + +defineOptionsStyle = require('./optionsStyles').defineOptionsStyle; + +_ = require('../core/localization')._; + +SANS_SERIF_FONTS = [['Arial', 'Arial,"Helvetica Neue",Helvetica,sans-serif'], ['Arial Black', '"Arial Black","Arial Bold",Gadget,sans-serif'], ['Arial Narrow', '"Arial Narrow",Arial,sans-serif'], ['Gill Sans', '"Gill Sans","Gill Sans MT",Calibri,sans-serif'], ['Helvetica', '"Helvetica Neue",Helvetica,Arial,sans-serif'], ['Impact', 'Impact,Haettenschweiler,"Franklin Gothic Bold",Charcoal,"Helvetica Inserat","Bitstream Vera Sans Bold","Arial Black",sans-serif'], ['Tahoma', 'Tahoma,Verdana,Segoe,sans-serif'], ['Trebuchet MS', '"Trebuchet MS","Lucida Grande","Lucida Sans Unicode","Lucida Sans",Tahoma,sans-serif'], ['Verdana', 'Verdana,Geneva,sans-serif']].map(function(arg) { + var name, value; + name = arg[0], value = arg[1]; + return { + name: _(name), + value: value + }; +}); + +SERIF_FONTS = [['Baskerville', 'Baskerville,"Baskerville Old Face","Hoefler Text",Garamond,"Times New Roman",serif'], ['Garamond', 'Garamond,Baskerville,"Baskerville Old Face","Hoefler Text","Times New Roman",serif'], ['Georgia', 'Georgia,Times,"Times New Roman",serif'], ['Hoefler Text', '"Hoefler Text","Baskerville Old Face",Garamond,"Times New Roman",serif'], ['Lucida Bright', '"Lucida Bright",Georgia,serif'], ['Palatino', 'Palatino,"Palatino Linotype","Palatino LT STD","Book Antiqua",Georgia,serif'], ['Times New Roman', 'TimesNewRoman,"Times New Roman",Times,Baskerville,Georgia,serif']].map(function(arg) { + var name, value; + name = arg[0], value = arg[1]; + return { + name: _(name), + value: value + }; +}); + +MONOSPACE_FONTS = [['Consolas/Monaco', 'Consolas,monaco,"Lucida Console",monospace'], ['Courier New', '"Courier New",Courier,"Lucida Sans Typewriter","Lucida Typewriter",monospace'], ['Lucida Sans Typewriter', '"Lucida Sans Typewriter","Lucida Console",monaco,"Bitstream Vera Sans Mono",monospace']].map(function(arg) { + var name, value; + name = arg[0], value = arg[1]; + return { + name: _(name), + value: value + }; +}); + +OTHER_FONTS = [['Copperplate', 'Copperplate,"Copperplate Gothic Light",fantasy'], ['Papyrus', 'Papyrus,fantasy'], ['Script', '"Brush Script MT",cursive']].map(function(arg) { + var name, value; + name = arg[0], value = arg[1]; + return { + name: _(name), + value: value + }; +}); + +ALL_FONTS = [[_('Sans Serif'), SANS_SERIF_FONTS], [_('Serif'), SERIF_FONTS], [_('Monospace'), MONOSPACE_FONTS], [_('Other'), OTHER_FONTS]]; + +FONT_NAME_TO_VALUE = {}; + +for (i = 0, len = SANS_SERIF_FONTS.length; i < len; i++) { + ref = SANS_SERIF_FONTS[i], name = ref.name, value = ref.value; + FONT_NAME_TO_VALUE[name] = value; +} + +for (j = 0, len1 = SERIF_FONTS.length; j < len1; j++) { + ref1 = SERIF_FONTS[j], name = ref1.name, value = ref1.value; + FONT_NAME_TO_VALUE[name] = value; +} + +for (l = 0, len2 = MONOSPACE_FONTS.length; l < len2; l++) { + ref2 = MONOSPACE_FONTS[l], name = ref2.name, value = ref2.value; + FONT_NAME_TO_VALUE[name] = value; +} + +for (m = 0, len3 = OTHER_FONTS.length; m < len3; m++) { + ref3 = OTHER_FONTS[m], name = ref3.name, value = ref3.value; + FONT_NAME_TO_VALUE[name] = value; +} + +defineOptionsStyle('font', React.createClass({ + displayName: 'FontOptions', + getInitialState: function() { + return { + isItalic: false, + isBold: false, + fontName: 'Helvetica', + fontSizeIndex: 4 + }; + }, + getFontSizes: function() { + return [9, 10, 12, 14, 18, 24, 36, 48, 64, 72, 96, 144, 288]; + }, + updateTool: function(newState) { + var fontSize, items, k; + if (newState == null) { + newState = {}; + } + for (k in this.state) { + if (!(k in newState)) { + newState[k] = this.state[k]; + } + } + fontSize = this.getFontSizes()[newState.fontSizeIndex]; + items = []; + if (newState.isItalic) { + items.push('italic'); + } + if (newState.isBold) { + items.push('bold'); + } + items.push(fontSize + "px"); + items.push(FONT_NAME_TO_VALUE[newState.fontName]); + this.props.lc.tool.font = items.join(' '); + return this.props.lc.trigger('setFont', items.join(' ')); + }, + handleFontSize: function(event) { + var newState; + newState = { + fontSizeIndex: event.target.value + }; + this.setState(newState); + return this.updateTool(newState); + }, + handleFontFamily: function(event) { + var newState; + newState = { + fontName: event.target.selectedOptions[0].innerHTML + }; + this.setState(newState); + return this.updateTool(newState); + }, + handleItalic: function(event) { + var newState; + newState = { + isItalic: !this.state.isItalic + }; + this.setState(newState); + return this.updateTool(newState); + }, + handleBold: function(event) { + var newState; + newState = { + isBold: !this.state.isBold + }; + this.setState(newState); + return this.updateTool(newState); + }, + componentDidMount: function() { + return this.updateTool(); + }, + render: function() { + var br, div, input, label, lc, optgroup, option, ref4, select, span; + lc = this.props.lc; + ref4 = React.DOM, div = ref4.div, input = ref4.input, select = ref4.select, option = ref4.option, br = ref4.br, label = ref4.label, span = ref4.span, optgroup = ref4.optgroup; + return div({ + className: 'lc-font-settings' + }, select({ + value: this.state.fontSizeIndex, + onChange: this.handleFontSize + }, this.getFontSizes().map((function(_this) { + return function(size, ix) { + return option({ + value: ix, + key: ix + }, size + "px"); + }; + })(this))), select({ + value: this.state.fontName, + onChange: this.handleFontFamily + }, ALL_FONTS.map((function(_this) { + return function(arg) { + var fonts, label; + label = arg[0], fonts = arg[1]; + return optgroup({ + key: label, + label: label + }, fonts.map(function(family, ix) { + return option({ + value: family.name, + key: ix + }, family.name); + })); + }; + })(this))), span({}, label({ + htmlFor: 'italic' + }, _("italic")), input({ + type: 'checkbox', + id: 'italic', + checked: this.state.isItalic, + onChange: this.handleItalic + })), span({}, label({ + htmlFor: 'bold' + }, _("bold")), input({ + type: 'checkbox', + id: 'bold', + checked: this.state.isBold, + onChange: this.handleBold + }))); + } +})); + +module.exports = {}; + + +},{"../core/localization":13,"../reactGUI/React-shim":35,"./optionsStyles":26}],24:[function(require,module,exports){ +var React, StrokeWidthPicker, classSet, createSetStateOnEventMixin, defineOptionsStyle; + +React = require('../reactGUI/React-shim'); + +defineOptionsStyle = require('./optionsStyles').defineOptionsStyle; + +StrokeWidthPicker = React.createFactory(require('../reactGUI/StrokeWidthPicker')); + +createSetStateOnEventMixin = require('../reactGUI/createSetStateOnEventMixin'); + +classSet = require('../core/util').classSet; + +defineOptionsStyle('line-options-and-stroke-width', React.createClass({ + displayName: 'LineOptionsAndStrokeWidth', + getState: function() { + return { + strokeWidth: this.props.tool.strokeWidth, + isDashed: this.props.tool.isDashed, + hasEndArrow: this.props.tool.hasEndArrow + }; + }, + getInitialState: function() { + return this.getState(); + }, + mixins: [createSetStateOnEventMixin('toolChange')], + render: function() { + var arrowButtonClass, dashButtonClass, div, img, li, ref, style, toggleIsDashed, togglehasEndArrow, ul; + ref = React.DOM, div = ref.div, ul = ref.ul, li = ref.li, img = ref.img; + toggleIsDashed = (function(_this) { + return function() { + _this.props.tool.isDashed = !_this.props.tool.isDashed; + return _this.setState(_this.getState()); + }; + })(this); + togglehasEndArrow = (function(_this) { + return function() { + _this.props.tool.hasEndArrow = !_this.props.tool.hasEndArrow; + return _this.setState(_this.getState()); + }; + })(this); + dashButtonClass = classSet({ + 'square-toolbar-button': true, + 'selected': this.state.isDashed + }); + arrowButtonClass = classSet({ + 'square-toolbar-button': true, + 'selected': this.state.hasEndArrow + }); + style = { + float: 'left', + margin: 1 + }; + return div({}, div({ + className: dashButtonClass, + onClick: toggleIsDashed, + style: style + }, img({ + src: this.props.imageURLPrefix + "/dashed-line.png" + })), div({ + className: arrowButtonClass, + onClick: togglehasEndArrow, + style: style + }, img({ + src: this.props.imageURLPrefix + "/line-with-arrow.png" + })), StrokeWidthPicker({ + tool: this.props.tool, + lc: this.props.lc + })); + } +})); + +module.exports = {}; + + +},{"../core/util":19,"../reactGUI/React-shim":35,"../reactGUI/StrokeWidthPicker":37,"../reactGUI/createSetStateOnEventMixin":40,"./optionsStyles":26}],25:[function(require,module,exports){ +var React, defineOptionsStyle; + +React = require('../reactGUI/React-shim'); + +defineOptionsStyle = require('./optionsStyles').defineOptionsStyle; + +defineOptionsStyle('null', React.createClass({ + displayName: 'NoOptions', + render: function() { + return React.DOM.div(); + } +})); + +module.exports = {}; + + +},{"../reactGUI/React-shim":35,"./optionsStyles":26}],26:[function(require,module,exports){ +var React, defineOptionsStyle, optionsStyles; + +React = require('../reactGUI/React-shim'); + +optionsStyles = {}; + +defineOptionsStyle = function(name, style) { + return optionsStyles[name] = React.createFactory(style); +}; + +module.exports = { + optionsStyles: optionsStyles, + defineOptionsStyle: defineOptionsStyle +}; + + +},{"../reactGUI/React-shim":35}],27:[function(require,module,exports){ +var React, StrokeWidthPicker, createSetStateOnEventMixin, defineOptionsStyle; + +React = require('../reactGUI/React-shim'); + +defineOptionsStyle = require('./optionsStyles').defineOptionsStyle; + +StrokeWidthPicker = React.createFactory(require('../reactGUI/StrokeWidthPicker')); + +createSetStateOnEventMixin = require('../reactGUI/createSetStateOnEventMixin'); + +defineOptionsStyle('polygon-and-stroke-width', React.createClass({ + displayName: 'PolygonAndStrokeWidth', + getState: function() { + return { + strokeWidth: this.props.tool.strokeWidth, + inProgress: false + }; + }, + getInitialState: function() { + return this.getState(); + }, + mixins: [createSetStateOnEventMixin('toolChange')], + componentDidMount: function() { + var hidePolygonTools, showPolygonTools, unsubscribeFuncs; + unsubscribeFuncs = []; + this.unsubscribe = (function(_this) { + return function() { + var func, i, len, results; + results = []; + for (i = 0, len = unsubscribeFuncs.length; i < len; i++) { + func = unsubscribeFuncs[i]; + results.push(func()); + } + return results; + }; + })(this); + showPolygonTools = (function(_this) { + return function() { + if (!_this.state.inProgress) { + return _this.setState({ + inProgress: true + }); + } + }; + })(this); + hidePolygonTools = (function(_this) { + return function() { + return _this.setState({ + inProgress: false + }); + }; + })(this); + unsubscribeFuncs.push(this.props.lc.on('lc-polygon-started', showPolygonTools)); + return unsubscribeFuncs.push(this.props.lc.on('lc-polygon-stopped', hidePolygonTools)); + }, + componentWillUnmount: function() { + return this.unsubscribe(); + }, + render: function() { + var div, img, lc, polygonCancel, polygonFinishClosed, polygonFinishOpen, polygonToolStyle, ref; + lc = this.props.lc; + ref = React.DOM, div = ref.div, img = ref.img; + polygonFinishOpen = (function(_this) { + return function() { + return lc.trigger('lc-polygon-finishopen'); + }; + })(this); + polygonFinishClosed = (function(_this) { + return function() { + return lc.trigger('lc-polygon-finishclosed'); + }; + })(this); + polygonCancel = (function(_this) { + return function() { + return lc.trigger('lc-polygon-cancel'); + }; + })(this); + polygonToolStyle = {}; + if (!this.state.inProgress) { + polygonToolStyle = { + display: 'none' + }; + } + return div({}, div({ + className: 'polygon-toolbar horz-toolbar', + style: polygonToolStyle + }, div({ + className: 'square-toolbar-button', + onClick: polygonFinishOpen + }, img({ + src: this.props.imageURLPrefix + "/polygon-open.png" + })), div({ + className: 'square-toolbar-button', + onClick: polygonFinishClosed + }, img({ + src: this.props.imageURLPrefix + "/polygon-closed.png" + })), div({ + className: 'square-toolbar-button', + onClick: polygonCancel + }, img({ + src: this.props.imageURLPrefix + "/polygon-cancel.png" + }))), div({}, StrokeWidthPicker({ + tool: this.props.tool, + lc: this.props.lc + }))); + } +})); + +module.exports = {}; + + +},{"../reactGUI/React-shim":35,"../reactGUI/StrokeWidthPicker":37,"../reactGUI/createSetStateOnEventMixin":40,"./optionsStyles":26}],28:[function(require,module,exports){ +'use strict'; + +var React = require('../reactGUI/React-shim'); + +var _require = require('./optionsStyles'); + +var defineOptionsStyle = _require.defineOptionsStyle; + +var createSetStateOnEventMixin = require('../reactGUI/createSetStateOnEventMixin'); +var _ = require('../core/localization')._; + +defineOptionsStyle('stroke-or-fill', React.createClass({ + displayName: 'StrokeOrFillPicker', + getState: function getState() { + return { strokeOrFill: 'stroke' }; + }, + getInitialState: function getInitialState() { + return this.getState(); + }, + mixins: [createSetStateOnEventMixin('toolChange')], + + onChange: function onChange(e) { + if (e.target.id == 'stroke-or-fill-stroke') { + this.props.lc.tool.strokeOrFill = 'stroke'; + } else { + this.props.lc.tool.strokeOrFill = 'fill'; + } + this.setState(this.getState()); + }, + + render: function render() { + var lc = this.props.lc; + + return React.createElement( + 'form', + null, + React.createElement( + 'span', + null, + ' ', + _('Color to change:'), + ' ' + ), + React.createElement( + 'span', + null, + React.createElement('input', { type: 'radio', name: 'stroke-or-fill', value: 'stroke', + id: 'stroke-or-fill-stroke', onChange: this.onChange, + checked: lc.tool.strokeOrFill == 'stroke' }), + React.createElement( + 'label', + { htmlFor: 'stroke-or-fill-stroke', className: 'label' }, + ' ', + _("stroke") + ) + ), + React.createElement( + 'span', + null, + React.createElement('input', { type: 'radio', name: 'stroke-or-fill', value: 'fill', + id: 'stroke-or-fill-fill', onChange: this.onChange, + checked: lc.tool.strokeOrFill == 'fill' }), + React.createElement( + 'label', + { htmlFor: 'stroke-or-fill-fill', className: 'label' }, + ' ', + _("fill") + ) + ) + ); + } +})); + +module.exports = {}; + +},{"../core/localization":13,"../reactGUI/React-shim":35,"../reactGUI/createSetStateOnEventMixin":40,"./optionsStyles":26}],29:[function(require,module,exports){ +var StrokeWidthPicker, defineOptionsStyle; + +defineOptionsStyle = require('./optionsStyles').defineOptionsStyle; + +StrokeWidthPicker = require('../reactGUI/StrokeWidthPicker'); + +defineOptionsStyle('stroke-width', StrokeWidthPicker); + +module.exports = {}; + + +},{"../reactGUI/StrokeWidthPicker":37,"./optionsStyles":26}],30:[function(require,module,exports){ +var ClearButton, React, _, classSet, createSetStateOnEventMixin; + +React = require('./React-shim'); + +createSetStateOnEventMixin = require('./createSetStateOnEventMixin'); + +_ = require('../core/localization')._; + +classSet = require('../core/util').classSet; + +ClearButton = React.createClass({ + displayName: 'ClearButton', + getState: function() { + return { + isEnabled: this.props.lc.canUndo() + }; + }, + getInitialState: function() { + return this.getState(); + }, + mixins: [createSetStateOnEventMixin('drawingChange')], + render: function() { + var className, div, lc, onClick; + div = React.DOM.div; + lc = this.props.lc; + className = classSet({ + 'lc-clear': true, + 'toolbar-button': true, + 'fat-button': true, + 'disabled': !this.state.isEnabled + }); + onClick = lc.canUndo() ? ((function(_this) { + return function() { + return lc.clear(); + }; + })(this)) : function() {}; + return div({ + className: className, + onClick: onClick + }, _('Clear')); + } +}); + +module.exports = ClearButton; + + +},{"../core/localization":13,"../core/util":19,"./React-shim":35,"./createSetStateOnEventMixin":40}],31:[function(require,module,exports){ +var ColorGrid, ColorWell, PureRenderMixin, React, _, cancelAnimationFrame, classSet, getHSLAString, getHSLString, parseHSLAString, ref, requestAnimationFrame; + +React = require('./React-shim'); + +PureRenderMixin = require('react-addons-pure-render-mixin'); + +ref = require('../core/util'), classSet = ref.classSet, requestAnimationFrame = ref.requestAnimationFrame, cancelAnimationFrame = ref.cancelAnimationFrame; + +_ = require('../core/localization')._; + +parseHSLAString = function(s) { + var components, firstParen, insideParens, lastParen; + if (s === 'transparent') { + return { + hue: 0, + sat: 0, + light: 0, + alpha: 0 + }; + } + if ((s != null ? s.substring(0, 4) : void 0) !== 'hsla') { + return null; + } + firstParen = s.indexOf('('); + lastParen = s.indexOf(')'); + insideParens = s.substring(firstParen + 1, lastParen - firstParen + 4); + components = (function() { + var j, len, ref1, results; + ref1 = insideParens.split(','); + results = []; + for (j = 0, len = ref1.length; j < len; j++) { + s = ref1[j]; + results.push(s.trim()); + } + return results; + })(); + return { + hue: parseInt(components[0], 10), + sat: parseInt(components[1].substring(0, components[1].length - 1), 10), + light: parseInt(components[2].substring(0, components[2].length - 1), 10), + alpha: parseFloat(components[3]) + }; +}; + +getHSLAString = function(arg) { + var alpha, hue, light, sat; + hue = arg.hue, sat = arg.sat, light = arg.light, alpha = arg.alpha; + return "hsla(" + hue + ", " + sat + "%, " + light + "%, " + alpha + ")"; +}; + +getHSLString = function(arg) { + var hue, light, sat; + hue = arg.hue, sat = arg.sat, light = arg.light; + return "hsl(" + hue + ", " + sat + "%, " + light + "%)"; +}; + +ColorGrid = React.createFactory(React.createClass({ + displayName: 'ColorGrid', + mixins: [PureRenderMixin], + render: function() { + var div; + div = React.DOM.div; + return div({}, this.props.rows.map((function(_this) { + return function(row, ix) { + return div({ + className: 'color-row', + key: ix, + style: { + width: 20 * row.length + } + }, row.map(function(cellColor, ix2) { + var alpha, className, colorString, colorStringNoAlpha, hue, light, sat, update; + hue = cellColor.hue, sat = cellColor.sat, light = cellColor.light, alpha = cellColor.alpha; + colorString = getHSLAString(cellColor); + colorStringNoAlpha = "hsl(" + hue + ", " + sat + "%, " + light + "%)"; + className = classSet({ + 'color-cell': true, + 'selected': _this.props.selectedColor === colorString + }); + update = function(e) { + _this.props.onChange(cellColor, colorString); + e.stopPropagation(); + return e.preventDefault(); + }; + return div({ + className: className, + onTouchStart: update, + onTouchMove: update, + onClick: update, + style: { + backgroundColor: colorStringNoAlpha + }, + key: ix2 + }); + })); + }; + })(this))); + } +})); + +ColorWell = React.createClass({ + displayName: 'ColorWell', + mixins: [PureRenderMixin], + getInitialState: function() { + var colorString, hsla; + colorString = this.props.lc.colors[this.props.colorName]; + hsla = parseHSLAString(colorString); + if (hsla == null) { + hsla = {}; + } + if (hsla.alpha == null) { + hsla.alpha = 1; + } + if (hsla.sat == null) { + hsla.sat = 100; + } + if (hsla.hue == null) { + hsla.hue = 0; + } + if (hsla.light == null) { + hsla.light = 50; + } + return { + colorString: colorString, + alpha: hsla.alpha, + sat: hsla.sat === 0 ? 100 : hsla.sat, + isPickerVisible: false, + hsla: hsla + }; + }, + componentDidMount: function() { + return this.unsubscribe = this.props.lc.on(this.props.colorName + "ColorChange", (function(_this) { + return function() { + var colorString; + colorString = _this.props.lc.colors[_this.props.colorName]; + _this.setState({ + colorString: colorString + }); + return _this.setHSLAFromColorString(colorString); + }; + })(this)); + }, + componentWillUnmount: function() { + return this.unsubscribe(); + }, + setHSLAFromColorString: function(c) { + var hsla; + hsla = parseHSLAString(c); + if (hsla) { + return this.setState({ + hsla: hsla, + alpha: hsla.alpha, + sat: hsla.sat + }); + } else { + return this.setState({ + hsla: null, + alpha: 1, + sat: 100 + }); + } + }, + closePicker: function() { + return this.setState({ + isPickerVisible: false + }); + }, + togglePicker: function() { + var isPickerVisible, shouldResetSat; + isPickerVisible = !this.state.isPickerVisible; + shouldResetSat = isPickerVisible && this.state.sat === 0; + this.setHSLAFromColorString(this.state.colorString); + return this.setState({ + isPickerVisible: isPickerVisible, + sat: shouldResetSat ? 100 : this.state.sat + }); + }, + setColor: function(c) { + this.setState({ + colorString: c + }); + this.setHSLAFromColorString(c); + return this.props.lc.setColor(this.props.colorName, c); + }, + setAlpha: function(alpha) { + var hsla; + this.setState({ + alpha: alpha + }); + if (this.state.hsla) { + hsla = this.state.hsla; + hsla.alpha = alpha; + this.setState({ + hsla: hsla + }); + return this.setColor(getHSLAString(hsla)); + } + }, + setSat: function(sat) { + var hsla; + this.setState({ + sat: sat + }); + if (isNaN(sat)) { + throw "SAT"; + } + if (this.state.hsla) { + hsla = this.state.hsla; + hsla.sat = sat; + this.setState({ + hsla: hsla + }); + return this.setColor(getHSLAString(hsla)); + } + }, + render: function() { + var br, div, label, ref1; + ref1 = React.DOM, div = ref1.div, label = ref1.label, br = ref1.br; + return div({ + className: classSet({ + 'color-well': true, + 'open': this.state.isPickerVisible + }), + onMouseLeave: this.closePicker, + style: { + float: 'left', + textAlign: 'center' + } + }, label({ + float: 'left' + }, this.props.label), br({}), div({ + className: classSet({ + 'color-well-color-container': true, + 'selected': this.state.isPickerVisible + }), + style: { + backgroundColor: 'white' + }, + onClick: this.togglePicker + }, div({ + className: 'color-well-checker color-well-checker-top-left' + }), div({ + className: 'color-well-checker color-well-checker-bottom-right', + style: { + left: '50%', + top: '50%' + } + }), div({ + className: 'color-well-color', + style: { + backgroundColor: this.state.colorString + } + }, " ")), this.renderPicker()); + }, + renderPicker: function() { + var div, hue, i, input, j, label, len, onSelectColor, ref1, ref2, renderColor, renderLabel, rows; + ref1 = React.DOM, div = ref1.div, label = ref1.label, input = ref1.input; + if (!this.state.isPickerVisible) { + return null; + } + renderLabel = (function(_this) { + return function(text) { + return div({ + className: 'color-row label', + key: text, + style: { + lineHeight: '20px', + height: 16 + } + }, text); + }; + })(this); + renderColor = (function(_this) { + return function() { + var checkerboardURL; + checkerboardURL = _this.props.lc.opts.imageURLPrefix + "/checkerboard-8x8.png"; + return div({ + className: 'color-row', + key: "color", + style: { + position: 'relative', + backgroundImage: "url(" + checkerboardURL + ")", + backgroundRepeat: 'repeat', + height: 24 + } + }, div({ + style: { + position: 'absolute', + top: 0, + right: 0, + bottom: 0, + left: 0, + backgroundColor: _this.state.colorString + } + })); + }; + })(this); + rows = []; + rows.push((function() { + var j, results; + results = []; + for (i = j = 0; j <= 100; i = j += 10) { + results.push({ + hue: 0, + sat: 0, + light: i, + alpha: this.state.alpha + }); + } + return results; + }).call(this)); + ref2 = [0, 30, 60, 90, 120, 150, 180, 210, 240, 270, 300, 330]; + for (j = 0, len = ref2.length; j < len; j++) { + hue = ref2[j]; + rows.push((function() { + var k, results; + results = []; + for (i = k = 10; k <= 90; i = k += 8) { + results.push({ + hue: hue, + sat: this.state.sat, + light: i, + alpha: this.state.alpha + }); + } + return results; + }).call(this)); + } + onSelectColor = (function(_this) { + return function(hsla, s) { + return _this.setColor(s); + }; + })(this); + return div({ + className: 'color-picker-popup' + }, renderColor(), renderLabel(_("alpha")), input({ + type: 'range', + min: 0, + max: 1, + step: 0.01, + value: this.state.alpha, + onChange: (function(_this) { + return function(e) { + return _this.setAlpha(parseFloat(e.target.value)); + }; + })(this) + }), renderLabel(_("saturation")), input({ + type: 'range', + min: 0, + max: 100, + value: this.state.sat, + max: 100, + onChange: (function(_this) { + return function(e) { + return _this.setSat(parseInt(e.target.value, 10)); + }; + })(this) + }), ColorGrid({ + rows: rows, + selectedColor: this.state.colorString, + onChange: onSelectColor + })); + } +}); + +module.exports = ColorWell; + + +},{"../core/localization":13,"../core/util":19,"./React-shim":35,"react-addons-pure-render-mixin":2}],32:[function(require,module,exports){ +'use strict'; + +var React = require('../reactGUI/React-shim'); + +var _require = require('../reactGUI/ReactDOM-shim'); + +var findDOMNode = _require.findDOMNode; + +var _require2 = require('../core/util'); + +var classSet = _require2.classSet; + +var Picker = require('./Picker'); +var Options = require('./Options'); +var createToolButton = require('./createToolButton'); +var LiterallyCanvasModel = require('../core/LiterallyCanvas'); +var defaultOptions = require('../core/defaultOptions'); + +require('../optionsStyles/font'); +require('../optionsStyles/stroke-width'); +require('../optionsStyles/line-options-and-stroke-width'); +require('../optionsStyles/polygon-and-stroke-width'); +require('../optionsStyles/null'); + +var CanvasContainer = React.createClass({ + displayName: 'CanvasContainer', + shouldComponentUpdate: function shouldComponentUpdate() { + // Avoid React trying to control this DOM + return false; + }, + render: function render() { + return React.createElement('div', { key: 'literallycanvas', className: 'lc-drawing with-gui' }); + } +}); + +var LiterallyCanvas = React.createClass({ + displayName: 'LiterallyCanvas', + + getDefaultProps: function getDefaultProps() { + return defaultOptions; + }, + bindToModel: function bindToModel() { + var canvasContainerEl = findDOMNode(this.canvas); + var opts = this.props; + this.lc.bindToElement(canvasContainerEl); + + if (typeof this.lc.opts.onInit === 'function') { + this.lc.opts.onInit(this.lc); + } + }, + componentWillMount: function componentWillMount() { + var _this = this; + + if (this.lc) return; + + if (this.props.lc) { + this.lc = this.props.lc; + } else { + this.lc = new LiterallyCanvasModel(this.props); + } + + this.toolButtonComponents = this.lc.opts.tools.map(function (ToolClass) { + return createToolButton(new ToolClass(_this.lc)); + }); + }, + componentDidMount: function componentDidMount() { + if (!this.lc.isBound) { + this.bindToModel(); + } + }, + componentWillUnmount: function componentWillUnmount() { + if (this.lc) { + this.lc._teardown(); + } + }, + render: function render() { + var _this2 = this; + + var lc = this.lc; + var toolButtonComponents = this.toolButtonComponents; + var props = this.props; + var _lc$opts = this.lc.opts; + var imageURLPrefix = _lc$opts.imageURLPrefix; + var toolbarPosition = _lc$opts.toolbarPosition; + + + var pickerProps = { lc: lc, toolButtonComponents: toolButtonComponents, imageURLPrefix: imageURLPrefix }; + var topOrBottomClassName = classSet({ + 'toolbar-at-top': toolbarPosition === 'top', + 'toolbar-at-bottom': toolbarPosition === 'bottom', + 'toolbar-hidden': toolbarPosition === 'hidden' + }); + return React.createElement( + 'div', + { className: 'literally ' + topOrBottomClassName }, + React.createElement(CanvasContainer, { ref: function ref(item) { + return _this2.canvas = item; + } }), + React.createElement(Picker, pickerProps), + React.createElement(Options, { lc: lc, imageURLPrefix: imageURLPrefix }) + ); + } +}); + +module.exports = LiterallyCanvas; + +},{"../core/LiterallyCanvas":5,"../core/defaultOptions":10,"../core/util":19,"../optionsStyles/font":23,"../optionsStyles/line-options-and-stroke-width":24,"../optionsStyles/null":25,"../optionsStyles/polygon-and-stroke-width":27,"../optionsStyles/stroke-width":29,"../reactGUI/React-shim":35,"../reactGUI/ReactDOM-shim":36,"./Options":33,"./Picker":34,"./createToolButton":41}],33:[function(require,module,exports){ +var Options, React, createSetStateOnEventMixin, optionsStyles; + +React = require('./React-shim'); + +createSetStateOnEventMixin = require('./createSetStateOnEventMixin'); + +optionsStyles = require('../optionsStyles/optionsStyles').optionsStyles; + +Options = React.createClass({ + displayName: 'Options', + getState: function() { + var ref; + return { + style: (ref = this.props.lc.tool) != null ? ref.optionsStyle : void 0, + tool: this.props.lc.tool + }; + }, + getInitialState: function() { + return this.getState(); + }, + mixins: [createSetStateOnEventMixin('toolChange')], + renderBody: function() { + var style; + style = "" + this.state.style; + return optionsStyles[style] && optionsStyles[style]({ + lc: this.props.lc, + tool: this.state.tool, + imageURLPrefix: this.props.imageURLPrefix + }); + }, + render: function() { + var div; + div = React.DOM.div; + return div({ + className: 'lc-options horz-toolbar' + }, this.renderBody()); + } +}); + +module.exports = Options; + + +},{"../optionsStyles/optionsStyles":26,"./React-shim":35,"./createSetStateOnEventMixin":40}],34:[function(require,module,exports){ +var ClearButton, ColorPickers, ColorWell, Picker, React, UndoRedoButtons, ZoomButtons, _; + +React = require('./React-shim'); + +ClearButton = React.createFactory(require('./ClearButton')); + +UndoRedoButtons = React.createFactory(require('./UndoRedoButtons')); + +ZoomButtons = React.createFactory(require('./ZoomButtons')); + +_ = require('../core/localization')._; + +ColorWell = React.createFactory(require('./ColorWell')); + +ColorPickers = React.createFactory(React.createClass({ + displayName: 'ColorPickers', + render: function() { + var div, lc; + lc = this.props.lc; + div = React.DOM.div; + return div({ + className: 'lc-color-pickers' + }, ColorWell({ + lc: lc, + colorName: 'primary', + label: _('stroke') + }), ColorWell({ + lc: lc, + colorName: 'secondary', + label: _('fill') + }), ColorWell({ + lc: lc, + colorName: 'background', + label: _('bg') + })); + } +})); + +Picker = React.createClass({ + displayName: 'Picker', + getInitialState: function() { + return { + selectedToolIndex: 0 + }; + }, + renderBody: function() { + var div, imageURLPrefix, lc, ref, toolButtonComponents; + div = React.DOM.div; + ref = this.props, toolButtonComponents = ref.toolButtonComponents, lc = ref.lc, imageURLPrefix = ref.imageURLPrefix; + return div({ + className: 'lc-picker-contents' + }, toolButtonComponents.map((function(_this) { + return function(component, ix) { + return component({ + lc: lc, + imageURLPrefix: imageURLPrefix, + key: ix, + isSelected: ix === _this.state.selectedToolIndex, + onSelect: function(tool) { + lc.setTool(tool); + return _this.setState({ + selectedToolIndex: ix + }); + } + }); + }; + })(this)), toolButtonComponents.length % 2 !== 0 ? div({ + className: 'toolbar-button thin-button disabled' + }) : void 0, div({ + style: { + position: 'absolute', + bottom: 0, + left: 0, + right: 0 + } + }, ColorPickers({ + lc: this.props.lc + }), UndoRedoButtons({ + lc: lc, + imageURLPrefix: imageURLPrefix + }), ZoomButtons({ + lc: lc, + imageURLPrefix: imageURLPrefix + }), ClearButton({ + lc: lc + }))); + }, + render: function() { + var div; + div = React.DOM.div; + return div({ + className: 'lc-picker' + }, this.renderBody()); + } +}); + +module.exports = Picker; + + +},{"../core/localization":13,"./ClearButton":30,"./ColorWell":31,"./React-shim":35,"./UndoRedoButtons":38,"./ZoomButtons":39}],35:[function(require,module,exports){ +var React, error; + +try { + React = require('react'); +} catch (error) { + React = window.React; +} + +if (React == null) { + throw "Can't find React"; +} + +module.exports = React; + + +},{"react":"react"}],36:[function(require,module,exports){ +var ReactDOM, error, error1; + +try { + ReactDOM = require('react-dom'); +} catch (error) { + ReactDOM = window.ReactDOM; +} + +if (ReactDOM == null) { + try { + ReactDOM = require('react'); + } catch (error1) { + ReactDOM = window.React; + } +} + +if (ReactDOM == null) { + throw "Can't find ReactDOM"; +} + +module.exports = ReactDOM; + + +},{"react":"react","react-dom":"react-dom"}],37:[function(require,module,exports){ +var React, classSet, createSetStateOnEventMixin; + +React = require('./React-shim'); + +createSetStateOnEventMixin = require('../reactGUI/createSetStateOnEventMixin'); + +classSet = require('../core/util').classSet; + +module.exports = React.createClass({ + displayName: 'StrokeWidthPicker', + getState: function(tool) { + if (tool == null) { + tool = this.props.tool; + } + return { + strokeWidth: tool.strokeWidth + }; + }, + getInitialState: function() { + return this.getState(); + }, + mixins: [createSetStateOnEventMixin('toolDidUpdateOptions')], + componentWillReceiveProps: function(props) { + return this.setState(this.getState(props.tool)); + }, + render: function() { + var circle, div, li, ref, strokeWidths, svg, ul; + ref = React.DOM, ul = ref.ul, li = ref.li, svg = ref.svg, circle = ref.circle, div = ref.div; + strokeWidths = this.props.lc.opts.strokeWidths; + return div({}, strokeWidths.map((function(_this) { + return function(strokeWidth, ix) { + var buttonClassName, buttonSize; + buttonClassName = classSet({ + 'square-toolbar-button': true, + 'selected': strokeWidth === _this.state.strokeWidth + }); + buttonSize = 28; + return div({ + key: strokeWidth + }, div({ + className: buttonClassName, + onClick: function() { + return _this.props.lc.trigger('setStrokeWidth', strokeWidth); + } + }, svg({ + width: buttonSize - 2, + height: buttonSize - 2, + viewPort: "0 0 " + strokeWidth + " " + strokeWidth, + version: "1.1", + xmlns: "http://www.w3.org/2000/svg" + }, circle({ + cx: Math.ceil(buttonSize / 2 - 1), + cy: Math.ceil(buttonSize / 2 - 1), + r: strokeWidth / 2 + })))); + }; + })(this))); + } +}); + + +},{"../core/util":19,"../reactGUI/createSetStateOnEventMixin":40,"./React-shim":35}],38:[function(require,module,exports){ +var React, RedoButton, UndoButton, UndoRedoButtons, classSet, createSetStateOnEventMixin, createUndoRedoButtonComponent; + +React = require('./React-shim'); + +createSetStateOnEventMixin = require('./createSetStateOnEventMixin'); + +classSet = require('../core/util').classSet; + +createUndoRedoButtonComponent = function(undoOrRedo) { + return React.createClass({ + displayName: undoOrRedo === 'undo' ? 'UndoButton' : 'RedoButton', + getState: function() { + return { + isEnabled: (function() { + switch (false) { + case undoOrRedo !== 'undo': + return this.props.lc.canUndo(); + case undoOrRedo !== 'redo': + return this.props.lc.canRedo(); + } + }).call(this) + }; + }, + getInitialState: function() { + return this.getState(); + }, + mixins: [createSetStateOnEventMixin('drawingChange')], + render: function() { + var className, div, imageURLPrefix, img, lc, onClick, ref, ref1, src, style, title; + ref = React.DOM, div = ref.div, img = ref.img; + ref1 = this.props, lc = ref1.lc, imageURLPrefix = ref1.imageURLPrefix; + title = undoOrRedo === 'undo' ? 'Undo' : 'Redo'; + className = ("lc-" + undoOrRedo + " ") + classSet({ + 'toolbar-button': true, + 'thin-button': true, + 'disabled': !this.state.isEnabled + }); + onClick = (function() { + switch (false) { + case !!this.state.isEnabled: + return function() {}; + case undoOrRedo !== 'undo': + return function() { + return lc.undo(); + }; + case undoOrRedo !== 'redo': + return function() { + return lc.redo(); + }; + } + }).call(this); + src = imageURLPrefix + "/" + undoOrRedo + ".png"; + style = { + backgroundImage: "url(" + src + ")" + }; + return div({ + className: className, + onClick: onClick, + title: title, + style: style + }); + } + }); +}; + +UndoButton = React.createFactory(createUndoRedoButtonComponent('undo')); + +RedoButton = React.createFactory(createUndoRedoButtonComponent('redo')); + +UndoRedoButtons = React.createClass({ + displayName: 'UndoRedoButtons', + render: function() { + var div; + div = React.DOM.div; + return div({ + className: 'lc-undo-redo' + }, UndoButton(this.props), RedoButton(this.props)); + } +}); + +module.exports = UndoRedoButtons; + + +},{"../core/util":19,"./React-shim":35,"./createSetStateOnEventMixin":40}],39:[function(require,module,exports){ +var React, ZoomButtons, ZoomInButton, ZoomOutButton, classSet, createSetStateOnEventMixin, createZoomButtonComponent; + +React = require('./React-shim'); + +createSetStateOnEventMixin = require('./createSetStateOnEventMixin'); + +classSet = require('../core/util').classSet; + +createZoomButtonComponent = function(inOrOut) { + return React.createClass({ + displayName: inOrOut === 'in' ? 'ZoomInButton' : 'ZoomOutButton', + getState: function() { + return { + isEnabled: (function() { + switch (false) { + case inOrOut !== 'in': + return this.props.lc.scale < this.props.lc.config.zoomMax; + case inOrOut !== 'out': + return this.props.lc.scale > this.props.lc.config.zoomMin; + } + }).call(this) + }; + }, + getInitialState: function() { + return this.getState(); + }, + mixins: [createSetStateOnEventMixin('zoom')], + render: function() { + var className, div, imageURLPrefix, img, lc, onClick, ref, ref1, src, style, title; + ref = React.DOM, div = ref.div, img = ref.img; + ref1 = this.props, lc = ref1.lc, imageURLPrefix = ref1.imageURLPrefix; + title = inOrOut === 'in' ? 'Zoom in' : 'Zoom out'; + className = ("lc-zoom-" + inOrOut + " ") + classSet({ + 'toolbar-button': true, + 'thin-button': true, + 'disabled': !this.state.isEnabled + }); + onClick = (function() { + switch (false) { + case !!this.state.isEnabled: + return function() {}; + case inOrOut !== 'in': + return function() { + return lc.zoom(lc.config.zoomStep); + }; + case inOrOut !== 'out': + return function() { + return lc.zoom(-lc.config.zoomStep); + }; + } + }).call(this); + src = imageURLPrefix + "/zoom-" + inOrOut + ".png"; + style = { + backgroundImage: "url(" + src + ")" + }; + return div({ + className: className, + onClick: onClick, + title: title, + style: style + }); + } + }); +}; + +ZoomOutButton = React.createFactory(createZoomButtonComponent('out')); + +ZoomInButton = React.createFactory(createZoomButtonComponent('in')); + +ZoomButtons = React.createClass({ + displayName: 'ZoomButtons', + render: function() { + var div; + div = React.DOM.div; + return div({ + className: 'lc-zoom' + }, ZoomOutButton(this.props), ZoomInButton(this.props)); + } +}); + +module.exports = ZoomButtons; + + +},{"../core/util":19,"./React-shim":35,"./createSetStateOnEventMixin":40}],40:[function(require,module,exports){ +var React, createSetStateOnEventMixin; + +React = require('./React-shim'); + +module.exports = createSetStateOnEventMixin = function(eventName) { + return { + componentDidMount: function() { + return this.unsubscribe = this.props.lc.on(eventName, (function(_this) { + return function() { + return _this.setState(_this.getState()); + }; + })(this)); + }, + componentWillUnmount: function() { + return this.unsubscribe(); + } + }; +}; + + +},{"./React-shim":35}],41:[function(require,module,exports){ +var React, _, classSet, createToolButton; + +React = require('./React-shim'); + +classSet = require('../core/util').classSet; + +_ = require('../core/localization')._; + +createToolButton = function(tool) { + var displayName, imageName; + displayName = tool.name; + imageName = tool.iconName; + return React.createFactory(React.createClass({ + displayName: displayName, + getDefaultProps: function() { + return { + isSelected: false, + lc: null + }; + }, + componentWillMount: function() { + if (this.props.isSelected) { + return this.props.lc.setTool(tool); + } + }, + render: function() { + var className, div, imageURLPrefix, img, isSelected, onSelect, ref, ref1, src; + ref = React.DOM, div = ref.div, img = ref.img; + ref1 = this.props, imageURLPrefix = ref1.imageURLPrefix, isSelected = ref1.isSelected, onSelect = ref1.onSelect; + className = classSet({ + 'lc-pick-tool': true, + 'toolbar-button': true, + 'thin-button': true, + 'selected': isSelected + }); + src = imageURLPrefix + "/" + imageName + ".png"; + return div({ + className: className, + style: { + 'backgroundImage': "url(" + src + ")" + }, + onClick: (function() { + return onSelect(tool); + }), + title: _(displayName) + }); + } + })); +}; + +module.exports = createToolButton; + + +},{"../core/localization":13,"../core/util":19,"./React-shim":35}],42:[function(require,module,exports){ +'use strict'; + +var React = require('./React-shim'); +var ReactDOM = require('./ReactDOM-shim'); +var LiterallyCanvasModel = require('../core/LiterallyCanvas'); +var LiterallyCanvasReactComponent = require('./LiterallyCanvas'); + +function init(el, opts) { + var originalClassName = el.className; + var lc = new LiterallyCanvasModel(opts); + ReactDOM.render(React.createElement(LiterallyCanvasReactComponent, { lc: lc }), el); + lc.teardown = function () { + lc._teardown(); + for (var i = 0; i < el.children.length; i++) { + el.removeChild(el.children[i]); + } + el.className = originalClassName; + }; + return lc; +} + +module.exports = init; + +},{"../core/LiterallyCanvas":5,"./LiterallyCanvas":32,"./React-shim":35,"./ReactDOM-shim":36}],43:[function(require,module,exports){ +var Ellipse, ToolWithStroke, createShape, + extend = 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; }, + hasProp = {}.hasOwnProperty; + +ToolWithStroke = require('./base').ToolWithStroke; + +createShape = require('../core/shapes').createShape; + +module.exports = Ellipse = (function(superClass) { + extend(Ellipse, superClass); + + function Ellipse() { + return Ellipse.__super__.constructor.apply(this, arguments); + } + + Ellipse.prototype.name = 'Ellipse'; + + Ellipse.prototype.iconName = 'ellipse'; + + Ellipse.prototype.begin = function(x, y, lc) { + return this.currentShape = createShape('Ellipse', { + x: x, + y: y, + strokeWidth: this.strokeWidth, + strokeColor: lc.getColor('primary'), + fillColor: lc.getColor('secondary') + }); + }; + + Ellipse.prototype["continue"] = function(x, y, lc) { + this.currentShape.width = x - this.currentShape.x; + this.currentShape.height = y - this.currentShape.y; + return lc.drawShapeInProgress(this.currentShape); + }; + + Ellipse.prototype.end = function(x, y, lc) { + return lc.saveShape(this.currentShape); + }; + + return Ellipse; + +})(ToolWithStroke); + + +},{"../core/shapes":17,"./base":53}],44:[function(require,module,exports){ +var Eraser, Pencil, createShape, + extend = 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; }, + hasProp = {}.hasOwnProperty; + +Pencil = require('./Pencil'); + +createShape = require('../core/shapes').createShape; + +module.exports = Eraser = (function(superClass) { + extend(Eraser, superClass); + + function Eraser() { + return Eraser.__super__.constructor.apply(this, arguments); + } + + Eraser.prototype.name = 'Eraser'; + + Eraser.prototype.iconName = 'eraser'; + + Eraser.prototype.makePoint = function(x, y, lc) { + return createShape('Point', { + x: x, + y: y, + size: this.strokeWidth, + color: '#000' + }); + }; + + Eraser.prototype.makeShape = function() { + return createShape('ErasedLinePath'); + }; + + return Eraser; + +})(Pencil); + + +},{"../core/shapes":17,"./Pencil":48}],45:[function(require,module,exports){ +var Eyedropper, Tool, getPixel, + extend = 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; }, + hasProp = {}.hasOwnProperty; + +Tool = require('./base').Tool; + +getPixel = function(ctx, arg) { + var pixel, x, y; + x = arg.x, y = arg.y; + pixel = ctx.getImageData(x, y, 1, 1).data; + if (pixel[3]) { + return "rgb(" + pixel[0] + ", " + pixel[1] + ", " + pixel[2] + ")"; + } else { + return null; + } +}; + +module.exports = Eyedropper = (function(superClass) { + extend(Eyedropper, superClass); + + Eyedropper.prototype.name = 'Eyedropper'; + + Eyedropper.prototype.iconName = 'eyedropper'; + + Eyedropper.prototype.optionsStyle = 'stroke-or-fill'; + + function Eyedropper(lc) { + Eyedropper.__super__.constructor.call(this, lc); + this.strokeOrFill = 'stroke'; + } + + Eyedropper.prototype.readColor = function(x, y, lc) { + var canvas, color, newColor, offset; + offset = lc.getDefaultImageRect(); + canvas = lc.getImage(); + newColor = getPixel(canvas.getContext('2d'), { + x: x - offset.x, + y: y - offset.y + }); + color = newColor || lc.getColor('background'); + if (this.strokeOrFill === 'stroke') { + return lc.setColor('primary', newColor); + } else { + return lc.setColor('secondary', newColor); + } + }; + + Eyedropper.prototype.begin = function(x, y, lc) { + return this.readColor(x, y, lc); + }; + + Eyedropper.prototype["continue"] = function(x, y, lc) { + return this.readColor(x, y, lc); + }; + + return Eyedropper; + +})(Tool); + + +},{"./base":53}],46:[function(require,module,exports){ +var Line, ToolWithStroke, createShape, + extend = 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; }, + hasProp = {}.hasOwnProperty; + +ToolWithStroke = require('./base').ToolWithStroke; + +createShape = require('../core/shapes').createShape; + +module.exports = Line = (function(superClass) { + extend(Line, superClass); + + function Line() { + return Line.__super__.constructor.apply(this, arguments); + } + + Line.prototype.name = 'Line'; + + Line.prototype.iconName = 'line'; + + Line.prototype.optionsStyle = 'line-options-and-stroke-width'; + + Line.prototype.begin = function(x, y, lc) { + return this.currentShape = createShape('Line', { + x1: x, + y1: y, + x2: x, + y2: y, + strokeWidth: this.strokeWidth, + dash: (function() { + switch (false) { + case !this.isDashed: + return [this.strokeWidth * 2, this.strokeWidth * 4]; + default: + return null; + } + }).call(this), + endCapShapes: this.hasEndArrow ? [null, 'arrow'] : null, + color: lc.getColor('primary') + }); + }; + + Line.prototype["continue"] = function(x, y, lc) { + this.currentShape.x2 = x; + this.currentShape.y2 = y; + return lc.drawShapeInProgress(this.currentShape); + }; + + Line.prototype.end = function(x, y, lc) { + return lc.saveShape(this.currentShape); + }; + + return Line; + +})(ToolWithStroke); + + +},{"../core/shapes":17,"./base":53}],47:[function(require,module,exports){ +var Pan, Tool, createShape, + extend = 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; }, + hasProp = {}.hasOwnProperty; + +Tool = require('./base').Tool; + +createShape = require('../core/shapes').createShape; + +module.exports = Pan = (function(superClass) { + extend(Pan, superClass); + + function Pan() { + return Pan.__super__.constructor.apply(this, arguments); + } + + Pan.prototype.name = 'Pan'; + + Pan.prototype.iconName = 'pan'; + + Pan.prototype.usesSimpleAPI = false; + + Pan.prototype.didBecomeActive = function(lc) { + var unsubscribeFuncs; + unsubscribeFuncs = []; + this.unsubscribe = (function(_this) { + return function() { + var func, i, len, results; + results = []; + for (i = 0, len = unsubscribeFuncs.length; i < len; i++) { + func = unsubscribeFuncs[i]; + results.push(func()); + } + return results; + }; + })(this); + unsubscribeFuncs.push(lc.on('lc-pointerdown', (function(_this) { + return function(arg) { + var rawX, rawY; + rawX = arg.rawX, rawY = arg.rawY; + _this.oldPosition = lc.position; + return _this.pointerStart = { + x: rawX, + y: rawY + }; + }; + })(this))); + return unsubscribeFuncs.push(lc.on('lc-pointerdrag', (function(_this) { + return function(arg) { + var dp, rawX, rawY; + rawX = arg.rawX, rawY = arg.rawY; + dp = { + x: (rawX - _this.pointerStart.x) * lc.backingScale, + y: (rawY - _this.pointerStart.y) * lc.backingScale + }; + return lc.setPan(_this.oldPosition.x + dp.x, _this.oldPosition.y + dp.y); + }; + })(this))); + }; + + Pan.prototype.willBecomeInactive = function(lc) { + return this.unsubscribe(); + }; + + return Pan; + +})(Tool); + + +},{"../core/shapes":17,"./base":53}],48:[function(require,module,exports){ +var Pencil, ToolWithStroke, createShape, + extend = 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; }, + hasProp = {}.hasOwnProperty; + +ToolWithStroke = require('./base').ToolWithStroke; + +createShape = require('../core/shapes').createShape; + +module.exports = Pencil = (function(superClass) { + extend(Pencil, superClass); + + function Pencil() { + return Pencil.__super__.constructor.apply(this, arguments); + } + + Pencil.prototype.name = 'Pencil'; + + Pencil.prototype.iconName = 'pencil'; + + Pencil.prototype.eventTimeThreshold = 10; + + Pencil.prototype.begin = function(x, y, lc) { + this.color = lc.getColor('primary'); + this.currentShape = this.makeShape(); + this.currentShape.addPoint(this.makePoint(x, y, lc)); + return this.lastEventTime = Date.now(); + }; + + Pencil.prototype["continue"] = function(x, y, lc) { + var timeDiff; + timeDiff = Date.now() - this.lastEventTime; + if (timeDiff > this.eventTimeThreshold) { + this.lastEventTime += timeDiff; + this.currentShape.addPoint(this.makePoint(x, y, lc)); + return lc.drawShapeInProgress(this.currentShape); + } + }; + + Pencil.prototype.end = function(x, y, lc) { + lc.saveShape(this.currentShape); + return this.currentShape = void 0; + }; + + Pencil.prototype.makePoint = function(x, y, lc) { + return createShape('Point', { + x: x, + y: y, + size: this.strokeWidth, + color: this.color + }); + }; + + Pencil.prototype.makeShape = function() { + return createShape('LinePath'); + }; + + return Pencil; + +})(ToolWithStroke); + + +},{"../core/shapes":17,"./base":53}],49:[function(require,module,exports){ +var Polygon, ToolWithStroke, createShape, + extend = 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; }, + hasProp = {}.hasOwnProperty; + +ToolWithStroke = require('./base').ToolWithStroke; + +createShape = require('../core/shapes').createShape; + +module.exports = Polygon = (function(superClass) { + extend(Polygon, superClass); + + function Polygon() { + return Polygon.__super__.constructor.apply(this, arguments); + } + + Polygon.prototype.name = 'Polygon'; + + Polygon.prototype.iconName = 'polygon'; + + Polygon.prototype.usesSimpleAPI = false; + + Polygon.prototype.didBecomeActive = function(lc) { + var onDown, onMove, onUp, polygonCancel, polygonFinishClosed, polygonFinishOpen, polygonUnsubscribeFuncs; + Polygon.__super__.didBecomeActive.call(this, lc); + polygonUnsubscribeFuncs = []; + this.polygonUnsubscribe = (function(_this) { + return function() { + var func, i, len, results; + results = []; + for (i = 0, len = polygonUnsubscribeFuncs.length; i < len; i++) { + func = polygonUnsubscribeFuncs[i]; + results.push(func()); + } + return results; + }; + })(this); + this.points = null; + this.maybePoint = null; + onUp = (function(_this) { + return function() { + if (_this._getWillFinish()) { + return _this._close(lc); + } + lc.trigger('lc-polygon-started'); + if (_this.points) { + _this.points.push(_this.maybePoint); + } else { + _this.points = [_this.maybePoint]; + } + _this.maybePoint = { + x: _this.maybePoint.x, + y: _this.maybePoint.y + }; + lc.setShapesInProgress(_this._getShapes(lc)); + return lc.repaintLayer('main'); + }; + })(this); + onMove = (function(_this) { + return function(arg) { + var x, y; + x = arg.x, y = arg.y; + if (_this.maybePoint) { + _this.maybePoint.x = x; + _this.maybePoint.y = y; + lc.setShapesInProgress(_this._getShapes(lc)); + return lc.repaintLayer('main'); + } + }; + })(this); + onDown = (function(_this) { + return function(arg) { + var x, y; + x = arg.x, y = arg.y; + _this.maybePoint = { + x: x, + y: y + }; + lc.setShapesInProgress(_this._getShapes(lc)); + return lc.repaintLayer('main'); + }; + })(this); + polygonFinishOpen = (function(_this) { + return function() { + _this.maybePoint = { + x: Infinity, + y: Infinity + }; + return _this._close(lc); + }; + })(this); + polygonFinishClosed = (function(_this) { + return function() { + _this.maybePoint = _this.points[0]; + return _this._close(lc); + }; + })(this); + polygonCancel = (function(_this) { + return function() { + return _this._cancel(lc); + }; + })(this); + polygonUnsubscribeFuncs.push(lc.on('drawingChange', (function(_this) { + return function() { + return _this._cancel(lc); + }; + })(this))); + polygonUnsubscribeFuncs.push(lc.on('lc-pointerdown', onDown)); + polygonUnsubscribeFuncs.push(lc.on('lc-pointerdrag', onMove)); + polygonUnsubscribeFuncs.push(lc.on('lc-pointermove', onMove)); + polygonUnsubscribeFuncs.push(lc.on('lc-pointerup', onUp)); + polygonUnsubscribeFuncs.push(lc.on('lc-polygon-finishopen', polygonFinishOpen)); + polygonUnsubscribeFuncs.push(lc.on('lc-polygon-finishclosed', polygonFinishClosed)); + return polygonUnsubscribeFuncs.push(lc.on('lc-polygon-cancel', polygonCancel)); + }; + + Polygon.prototype.willBecomeInactive = function(lc) { + Polygon.__super__.willBecomeInactive.call(this, lc); + if (this.points || this.maybePoint) { + this._cancel(lc); + } + return this.polygonUnsubscribe(); + }; + + Polygon.prototype._getArePointsClose = function(a, b) { + return (Math.abs(a.x - b.x) + Math.abs(a.y - b.y)) < 10; + }; + + Polygon.prototype._getWillClose = function() { + if (!(this.points && this.points.length > 1)) { + return false; + } + if (!this.maybePoint) { + return false; + } + return this._getArePointsClose(this.points[0], this.maybePoint); + }; + + Polygon.prototype._getWillFinish = function() { + if (!(this.points && this.points.length > 1)) { + return false; + } + if (!this.maybePoint) { + return false; + } + return this._getArePointsClose(this.points[0], this.maybePoint) || this._getArePointsClose(this.points[this.points.length - 1], this.maybePoint); + }; + + Polygon.prototype._cancel = function(lc) { + lc.trigger('lc-polygon-stopped'); + this.maybePoint = null; + this.points = null; + lc.setShapesInProgress([]); + return lc.repaintLayer('main'); + }; + + Polygon.prototype._close = function(lc) { + lc.trigger('lc-polygon-stopped'); + lc.setShapesInProgress([]); + if (this.points.length > 2) { + lc.saveShape(this._getShape(lc, false)); + } + this.maybePoint = null; + return this.points = null; + }; + + Polygon.prototype._getShapes = function(lc, isInProgress) { + var shape; + if (isInProgress == null) { + isInProgress = true; + } + shape = this._getShape(lc, isInProgress); + if (shape) { + return [shape]; + } else { + return []; + } + }; + + Polygon.prototype._getShape = function(lc, isInProgress) { + var points; + if (isInProgress == null) { + isInProgress = true; + } + points = []; + if (this.points) { + points = points.concat(this.points); + } + if ((!isInProgress) && points.length < 3) { + return null; + } + if (isInProgress && this.maybePoint) { + points.push(this.maybePoint); + } + if (points.length > 1) { + return createShape('Polygon', { + isClosed: this._getWillClose(), + strokeColor: lc.getColor('primary'), + fillColor: lc.getColor('secondary'), + strokeWidth: this.strokeWidth, + points: points.map(function(xy) { + return createShape('Point', xy); + }) + }); + } else { + return null; + } + }; + + Polygon.prototype.optionsStyle = 'polygon-and-stroke-width'; + + return Polygon; + +})(ToolWithStroke); + + +},{"../core/shapes":17,"./base":53}],50:[function(require,module,exports){ +var Rectangle, ToolWithStroke, createShape, + extend = 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; }, + hasProp = {}.hasOwnProperty; + +ToolWithStroke = require('./base').ToolWithStroke; + +createShape = require('../core/shapes').createShape; + +module.exports = Rectangle = (function(superClass) { + extend(Rectangle, superClass); + + function Rectangle() { + return Rectangle.__super__.constructor.apply(this, arguments); + } + + Rectangle.prototype.name = 'Rectangle'; + + Rectangle.prototype.iconName = 'rectangle'; + + Rectangle.prototype.begin = function(x, y, lc) { + return this.currentShape = createShape('Rectangle', { + x: x, + y: y, + strokeWidth: this.strokeWidth, + strokeColor: lc.getColor('primary'), + fillColor: lc.getColor('secondary') + }); + }; + + Rectangle.prototype["continue"] = function(x, y, lc) { + this.currentShape.width = x - this.currentShape.x; + this.currentShape.height = y - this.currentShape.y; + return lc.drawShapeInProgress(this.currentShape); + }; + + Rectangle.prototype.end = function(x, y, lc) { + return lc.saveShape(this.currentShape); + }; + + return Rectangle; + +})(ToolWithStroke); + + +},{"../core/shapes":17,"./base":53}],51:[function(require,module,exports){ +var SelectShape, Tool, createShape, + extend = 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; }, + hasProp = {}.hasOwnProperty; + +Tool = require('./base').Tool; + +createShape = require('../core/shapes').createShape; + +module.exports = SelectShape = (function(superClass) { + extend(SelectShape, superClass); + + SelectShape.prototype.name = 'SelectShape'; + + SelectShape.prototype.usesSimpleAPI = false; + + function SelectShape(lc) { + this.selectCanvas = document.createElement('canvas'); + this.selectCanvas.style['background-color'] = 'transparent'; + this.selectCtx = this.selectCanvas.getContext('2d'); + } + + SelectShape.prototype.didBecomeActive = function(lc) { + var onDown, onDrag, onUp, selectShapeUnsubscribeFuncs; + selectShapeUnsubscribeFuncs = []; + this._selectShapeUnsubscribe = (function(_this) { + return function() { + var func, j, len, results; + results = []; + for (j = 0, len = selectShapeUnsubscribeFuncs.length; j < len; j++) { + func = selectShapeUnsubscribeFuncs[j]; + results.push(func()); + } + return results; + }; + })(this); + onDown = (function(_this) { + return function(arg) { + var br, shapeIndex, x, y; + x = arg.x, y = arg.y; + _this.didDrag = false; + shapeIndex = _this._getPixel(x, y, lc, _this.selectCtx); + _this.selectedShape = lc.shapes[shapeIndex]; + if (_this.selectedShape != null) { + lc.trigger('shapeSelected', { + selectedShape: _this.selectedShape + }); + lc.setShapesInProgress([ + _this.selectedShape, createShape('SelectionBox', { + shape: _this.selectedShape, + handleSize: 0 + }) + ]); + lc.repaintLayer('main'); + br = _this.selectedShape.getBoundingRect(); + return _this.dragOffset = { + x: x - br.x, + y: y - br.y + }; + } + }; + })(this); + onDrag = (function(_this) { + return function(arg) { + var x, y; + x = arg.x, y = arg.y; + if (_this.selectedShape != null) { + _this.didDrag = true; + _this.selectedShape.setUpperLeft({ + x: x - _this.dragOffset.x, + y: y - _this.dragOffset.y + }); + lc.setShapesInProgress([ + _this.selectedShape, createShape('SelectionBox', { + shape: _this.selectedShape, + handleSize: 0 + }) + ]); + return lc.repaintLayer('main'); + } + }; + })(this); + onUp = (function(_this) { + return function(arg) { + var x, y; + x = arg.x, y = arg.y; + if (_this.didDrag) { + _this.didDrag = false; + lc.trigger('shapeMoved', { + shape: _this.selectedShape + }); + lc.trigger('drawingChange', {}); + lc.repaintLayer('main'); + return _this._drawSelectCanvas(lc); + } + }; + })(this); + selectShapeUnsubscribeFuncs.push(lc.on('lc-pointerdown', onDown)); + selectShapeUnsubscribeFuncs.push(lc.on('lc-pointerdrag', onDrag)); + selectShapeUnsubscribeFuncs.push(lc.on('lc-pointerup', onUp)); + return this._drawSelectCanvas(lc); + }; + + SelectShape.prototype.willBecomeInactive = function(lc) { + this._selectShapeUnsubscribe(); + return lc.setShapesInProgress([]); + }; + + SelectShape.prototype._drawSelectCanvas = function(lc) { + var shapes; + this.selectCanvas.width = lc.canvas.width; + this.selectCanvas.height = lc.canvas.height; + this.selectCtx.clearRect(0, 0, this.selectCanvas.width, this.selectCanvas.height); + shapes = lc.shapes.map((function(_this) { + return function(shape, index) { + return createShape('SelectionBox', { + shape: shape, + handleSize: 0, + backgroundColor: "#" + (_this._intToHex(index)) + }); + }; + })(this)); + return lc.draw(shapes, this.selectCtx); + }; + + SelectShape.prototype._intToHex = function(i) { + return ("000000" + (i.toString(16))).slice(-6); + }; + + SelectShape.prototype._getPixel = function(x, y, lc, ctx) { + var p, pixel; + p = lc.drawingCoordsToClientCoords(x, y); + pixel = ctx.getImageData(p.x, p.y, 1, 1).data; + if (pixel[3]) { + return parseInt(this._rgbToHex(pixel[0], pixel[1], pixel[2]), 16); + } else { + return null; + } + }; + + SelectShape.prototype._componentToHex = function(c) { + var hex; + hex = c.toString(16); + return ("0" + hex).slice(-2); + }; + + SelectShape.prototype._rgbToHex = function(r, g, b) { + return "" + (this._componentToHex(r)) + (this._componentToHex(g)) + (this._componentToHex(b)); + }; + + return SelectShape; + +})(Tool); + + +},{"../core/shapes":17,"./base":53}],52:[function(require,module,exports){ +var Text, Tool, createShape, getIsPointInBox, + extend = 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; }, + hasProp = {}.hasOwnProperty; + +Tool = require('./base').Tool; + +createShape = require('../core/shapes').createShape; + +getIsPointInBox = function(point, box) { + if (point.x < box.x) { + return false; + } + if (point.y < box.y) { + return false; + } + if (point.x > box.x + box.width) { + return false; + } + if (point.y > box.y + box.height) { + return false; + } + return true; +}; + +module.exports = Text = (function(superClass) { + extend(Text, superClass); + + Text.prototype.name = 'Text'; + + Text.prototype.iconName = 'text'; + + function Text() { + this.text = ''; + this.font = 'bold 18px sans-serif'; + this.currentShape = null; + this.currentShapeState = null; + this.initialShapeBoundingRect = null; + this.dragAction = null; + this.didDrag = false; + } + + Text.prototype.didBecomeActive = function(lc) { + var switchAway, unsubscribeFuncs, updateInputEl; + unsubscribeFuncs = []; + this.unsubscribe = (function(_this) { + return function() { + var func, i, len, results; + results = []; + for (i = 0, len = unsubscribeFuncs.length; i < len; i++) { + func = unsubscribeFuncs[i]; + results.push(func()); + } + return results; + }; + })(this); + switchAway = (function(_this) { + return function() { + _this._ensureNotEditing(lc); + _this._clearCurrentShape(lc); + return lc.repaintLayer('main'); + }; + })(this); + updateInputEl = (function(_this) { + return function() { + return _this._updateInputEl(lc); + }; + })(this); + unsubscribeFuncs.push(lc.on('drawingChange', switchAway)); + unsubscribeFuncs.push(lc.on('zoom', updateInputEl)); + unsubscribeFuncs.push(lc.on('imageSizeChange', updateInputEl)); + unsubscribeFuncs.push(lc.on('snapshotLoad', (function(_this) { + return function() { + _this._clearCurrentShape(lc); + return lc.repaintLayer('main'); + }; + })(this))); + unsubscribeFuncs.push(lc.on('primaryColorChange', (function(_this) { + return function(newColor) { + if (!_this.currentShape) { + return; + } + _this.currentShape.color = newColor; + _this._updateInputEl(lc); + return lc.repaintLayer('main'); + }; + })(this))); + return unsubscribeFuncs.push(lc.on('setFont', (function(_this) { + return function(font) { + if (!_this.currentShape) { + return; + } + _this.font = font; + _this.currentShape.setFont(font); + _this._setShapesInProgress(lc); + _this._updateInputEl(lc); + return lc.repaintLayer('main'); + }; + })(this))); + }; + + Text.prototype.willBecomeInactive = function(lc) { + if (this.currentShape) { + this._ensureNotEditing(lc); + this.commit(lc); + } + return this.unsubscribe(); + }; + + Text.prototype.setText = function(text) { + return this.text = text; + }; + + Text.prototype._ensureNotEditing = function(lc) { + if (this.currentShapeState === 'editing') { + return this._exitEditingState(lc); + } + }; + + Text.prototype._clearCurrentShape = function(lc) { + this.currentShape = null; + this.initialShapeBoundingRect = null; + this.currentShapeState = null; + return lc.setShapesInProgress([]); + }; + + Text.prototype.commit = function(lc) { + if (this.currentShape.text) { + lc.saveShape(this.currentShape); + } + this._clearCurrentShape(lc); + return lc.repaintLayer('main'); + }; + + Text.prototype._getSelectionShape = function(ctx, backgroundColor) { + if (backgroundColor == null) { + backgroundColor = null; + } + return createShape('SelectionBox', { + shape: this.currentShape, + ctx: ctx, + backgroundColor: backgroundColor + }); + }; + + Text.prototype._setShapesInProgress = function(lc) { + switch (this.currentShapeState) { + case 'selected': + return lc.setShapesInProgress([this._getSelectionShape(lc.ctx), this.currentShape]); + case 'editing': + return lc.setShapesInProgress([this._getSelectionShape(lc.ctx, '#fff')]); + default: + return lc.setShapesInProgress([this.currentShape]); + } + }; + + Text.prototype.begin = function(x, y, lc) { + var br, point, selectionBox, selectionShape; + this.dragAction = 'none'; + this.didDrag = false; + if (this.currentShapeState === 'selected' || this.currentShapeState === 'editing') { + br = this.currentShape.getBoundingRect(lc.ctx); + selectionShape = this._getSelectionShape(lc.ctx); + selectionBox = selectionShape.getBoundingRect(); + point = { + x: x, + y: y + }; + if (getIsPointInBox(point, br)) { + this.dragAction = 'move'; + } + if (getIsPointInBox(point, selectionShape.getBottomRightHandleRect())) { + this.dragAction = 'resizeBottomRight'; + } + if (getIsPointInBox(point, selectionShape.getTopLeftHandleRect())) { + this.dragAction = 'resizeTopLeft'; + } + if (getIsPointInBox(point, selectionShape.getBottomLeftHandleRect())) { + this.dragAction = 'resizeBottomLeft'; + } + if (getIsPointInBox(point, selectionShape.getTopRightHandleRect())) { + this.dragAction = 'resizeTopRight'; + } + if (this.dragAction === 'none' && this.currentShapeState === 'editing') { + this.dragAction = 'stop-editing'; + this._exitEditingState(lc); + } + } else { + this.color = lc.getColor('primary'); + this.currentShape = createShape('Text', { + x: x, + y: y, + text: this.text, + color: this.color, + font: this.font, + v: 1 + }); + this.dragAction = 'place'; + this.currentShapeState = 'selected'; + } + if (this.dragAction === 'none') { + this.commit(lc); + return; + } + this.initialShapeBoundingRect = this.currentShape.getBoundingRect(lc.ctx); + this.dragOffset = { + x: x - this.initialShapeBoundingRect.x, + y: y - this.initialShapeBoundingRect.y + }; + this._setShapesInProgress(lc); + return lc.repaintLayer('main'); + }; + + Text.prototype["continue"] = function(x, y, lc) { + var br, brBottom, brRight; + if (this.dragAction === 'none') { + return; + } + br = this.initialShapeBoundingRect; + brRight = br.x + br.width; + brBottom = br.y + br.height; + switch (this.dragAction) { + case 'place': + this.currentShape.x = x; + this.currentShape.y = y; + this.didDrag = true; + break; + case 'move': + this.currentShape.x = x - this.dragOffset.x; + this.currentShape.y = y - this.dragOffset.y; + this.didDrag = true; + break; + case 'resizeBottomRight': + this.currentShape.setSize(x - (this.dragOffset.x - this.initialShapeBoundingRect.width) - br.x, y - (this.dragOffset.y - this.initialShapeBoundingRect.height) - br.y); + break; + case 'resizeTopLeft': + this.currentShape.setSize(brRight - x + this.dragOffset.x, brBottom - y + this.dragOffset.y); + this.currentShape.setPosition(x - this.dragOffset.x, y - this.dragOffset.y); + break; + case 'resizeBottomLeft': + this.currentShape.setSize(brRight - x + this.dragOffset.x, y - (this.dragOffset.y - this.initialShapeBoundingRect.height) - br.y); + this.currentShape.setPosition(x - this.dragOffset.x, this.currentShape.y); + break; + case 'resizeTopRight': + this.currentShape.setSize(x - (this.dragOffset.x - this.initialShapeBoundingRect.width) - br.x, brBottom - y + this.dragOffset.y); + this.currentShape.setPosition(this.currentShape.x, y - this.dragOffset.y); + } + this._setShapesInProgress(lc); + lc.repaintLayer('main'); + return this._updateInputEl(lc); + }; + + Text.prototype.end = function(x, y, lc) { + if (!this.currentShape) { + return; + } + this.currentShape.setSize(this.currentShape.forcedWidth, 0); + if (this.currentShapeState === 'selected') { + if (this.dragAction === 'place' || (this.dragAction === 'move' && !this.didDrag)) { + this._enterEditingState(lc); + } + } + this._setShapesInProgress(lc); + lc.repaintLayer('main'); + return this._updateInputEl(lc); + }; + + Text.prototype._enterEditingState = function(lc) { + var onChange; + this.currentShapeState = 'editing'; + if (this.inputEl) { + throw "State error"; + } + this.inputEl = document.createElement('textarea'); + this.inputEl.className = 'text-tool-input'; + this.inputEl.style.position = 'absolute'; + this.inputEl.style.transformOrigin = '0px 0px'; + this.inputEl.style.backgroundColor = 'transparent'; + this.inputEl.style.border = 'none'; + this.inputEl.style.outline = 'none'; + this.inputEl.style.margin = '0'; + this.inputEl.style.padding = '4px'; + this.inputEl.style.zIndex = '1000'; + this.inputEl.style.overflow = 'hidden'; + this.inputEl.style.resize = 'none'; + this.inputEl.value = this.currentShape.text; + this.inputEl.addEventListener('mousedown', function(e) { + return e.stopPropagation(); + }); + this.inputEl.addEventListener('touchstart', function(e) { + return e.stopPropagation(); + }); + onChange = (function(_this) { + return function(e) { + _this.currentShape.setText(e.target.value); + _this.currentShape.enforceMaxBoundingRect(lc); + _this._setShapesInProgress(lc); + lc.repaintLayer('main'); + _this._updateInputEl(lc); + return e.stopPropagation(); + }; + })(this); + this.inputEl.addEventListener('keydown', (function(_this) { + return function() { + return _this._updateInputEl(lc, true); + }; + })(this)); + this.inputEl.addEventListener('keyup', onChange); + this.inputEl.addEventListener('change', onChange); + this._updateInputEl(lc); + lc.containerEl.appendChild(this.inputEl); + this.inputEl.focus(); + return this._setShapesInProgress(lc); + }; + + Text.prototype._exitEditingState = function(lc) { + this.currentShapeState = 'selected'; + lc.containerEl.removeChild(this.inputEl); + this.inputEl = null; + this._setShapesInProgress(lc); + return lc.repaintLayer('main'); + }; + + Text.prototype._updateInputEl = function(lc, withMargin) { + var br, transformString; + if (withMargin == null) { + withMargin = false; + } + if (!this.inputEl) { + return; + } + br = this.currentShape.getBoundingRect(lc.ctx, true); + this.inputEl.style.font = this.currentShape.font; + this.inputEl.style.color = this.currentShape.color; + this.inputEl.style.left = (lc.position.x / lc.backingScale + br.x * lc.scale - 4) + "px"; + this.inputEl.style.top = (lc.position.y / lc.backingScale + br.y * lc.scale - 4) + "px"; + if (withMargin && !this.currentShape.forcedWidth) { + this.inputEl.style.width = (br.width + 10 + this.currentShape.renderer.emDashWidth) + "px"; + } else { + this.inputEl.style.width = (br.width + 12) + "px"; + } + if (withMargin) { + this.inputEl.style.height = (br.height + 10 + this.currentShape.renderer.metrics.leading) + "px"; + } else { + this.inputEl.style.height = (br.height + 10) + "px"; + } + transformString = "scale(" + lc.scale + ")"; + this.inputEl.style.transform = transformString; + this.inputEl.style.webkitTransform = transformString; + this.inputEl.style.MozTransform = transformString; + this.inputEl.style.msTransform = transformString; + return this.inputEl.style.OTransform = transformString; + }; + + Text.prototype.optionsStyle = 'font'; + + return Text; + +})(Tool); + + +},{"../core/shapes":17,"./base":53}],53:[function(require,module,exports){ +var Tool, ToolWithStroke, tools, + extend = 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; }, + hasProp = {}.hasOwnProperty; + +tools = {}; + +tools.Tool = Tool = (function() { + function Tool() {} + + Tool.prototype.name = null; + + Tool.prototype.iconName = null; + + Tool.prototype.usesSimpleAPI = true; + + Tool.prototype.begin = function(x, y, lc) {}; + + Tool.prototype["continue"] = function(x, y, lc) {}; + + Tool.prototype.end = function(x, y, lc) {}; + + Tool.prototype.optionsStyle = null; + + Tool.prototype.didBecomeActive = function(lc) {}; + + Tool.prototype.willBecomeInactive = function(lc) {}; + + return Tool; + +})(); + +tools.ToolWithStroke = ToolWithStroke = (function(superClass) { + extend(ToolWithStroke, superClass); + + function ToolWithStroke(lc) { + this.strokeWidth = lc.opts.defaultStrokeWidth; + } + + ToolWithStroke.prototype.optionsStyle = 'stroke-width'; + + ToolWithStroke.prototype.didBecomeActive = function(lc) { + var unsubscribeFuncs; + unsubscribeFuncs = []; + this.unsubscribe = (function(_this) { + return function() { + var func, i, len, results; + results = []; + for (i = 0, len = unsubscribeFuncs.length; i < len; i++) { + func = unsubscribeFuncs[i]; + results.push(func()); + } + return results; + }; + })(this); + return unsubscribeFuncs.push(lc.on('setStrokeWidth', (function(_this) { + return function(strokeWidth) { + _this.strokeWidth = strokeWidth; + return lc.trigger('toolDidUpdateOptions'); + }; + })(this))); + }; + + ToolWithStroke.prototype.willBecomeInactive = function(lc) { + return this.unsubscribe(); + }; + + return ToolWithStroke; + +})(Tool); + +module.exports = tools; + + +},{}]},{},[22])(22) +}); \ No newline at end of file diff --git a/Web/static/js/yarn.lock b/Web/static/js/yarn.lock index 183961a4..38e9db0d 100644 --- a/Web/static/js/yarn.lock +++ b/Web/static/js/yarn.lock @@ -29,6 +29,11 @@ resolved "https://registry.yarnpkg.com/@atlassian/tipsy/-/tipsy-1.3.2.tgz#ab759d461670d712425b2dac7573b79575a10502" integrity sha512-H7qWMs66bztELt2QpOCLYDU9ZM3VZfE0knbRHHLBukH7v9dMkIS5ZwqcGREjWnVt0KNETaBeXxj0FD88TEOGVw== +asap@~2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= + backbone@^1.3.3: version "1.4.0" resolved "https://registry.yarnpkg.com/backbone/-/backbone-1.4.0.tgz#54db4de9df7c3811c3f032f34749a4cd27f3bd12" @@ -36,16 +41,69 @@ backbone@^1.3.3: dependencies: underscore ">=1.8.3" +core-js@^1.0.0: + version "1.2.7" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" + integrity sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY= + +create-react-class@^15.7.0: + version "15.7.0" + resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.7.0.tgz#7499d7ca2e69bb51d13faf59bd04f0c65a1d6c1e" + integrity sha512-QZv4sFWG9S5RUvkTYWbflxeZX+JG7Cz0Tn33rQBJ+WFQTqTfUTjMjiv9tnfXazjsO5r0KhPs+AqCjyrQX6h2ng== + dependencies: + loose-envify "^1.3.1" + object-assign "^4.1.1" + css.escape@1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.0.tgz#95984d7887ce4ca90684e813966f42d1ef87ecea" integrity sha1-lZhNeIfOTKkGhOgTlm9C0e+H7Oo= +encoding@^0.1.11: + version "0.1.13" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" + integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== + dependencies: + iconv-lite "^0.6.2" + fancy-file-input@~2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/fancy-file-input/-/fancy-file-input-2.0.4.tgz#698c216482e07649a827681c4db3054fddc9a32b" integrity sha512-l+J0WwDl4nM/zMJ/C8qleYnXMUJKsLng7c5uWH/miAiHoTvPDtEoLW1tmVO6Cy2O8i/1VfA+2YOwg/Q3+kgO6w== +fbjs@^0.8.0: + version "0.8.17" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd" + integrity sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90= + dependencies: + core-js "^1.0.0" + isomorphic-fetch "^2.1.1" + loose-envify "^1.0.0" + object-assign "^4.1.0" + promise "^7.1.1" + setimmediate "^1.0.5" + ua-parser-js "^0.7.18" + +iconv-lite@^0.6.2: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +is-stream@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + +isomorphic-fetch@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" + integrity sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk= + dependencies: + node-fetch "^1.0.1" + whatwg-fetch ">=0.10.0" + jquery-ui@^1.12.1: version "1.12.1" resolved "https://registry.yarnpkg.com/jquery-ui/-/jquery-ui-1.12.1.tgz#bcb4045c8dd0539c134bc1488cdd3e768a7a9e51" @@ -56,6 +114,11 @@ jquery@^2.1.0: resolved "https://registry.yarnpkg.com/jquery/-/jquery-2.2.4.tgz#2c89d6889b5eac522a7eea32c14521559c6cbf02" integrity sha1-LInWiJterFIqfuoywUUhVZxsvwI= +"js-tokens@^3.0.0 || ^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + knockout@^3.5.1: version "3.5.1" resolved "https://registry.yarnpkg.com/knockout/-/knockout-3.5.1.tgz#62c81e81843bea2008fd23c575edd9ca978e75cf" @@ -66,11 +129,38 @@ ky@^0.19.0: resolved "https://registry.yarnpkg.com/ky/-/ky-0.19.0.tgz#d6ad117e89efe2d85a1c2e91462d48ca1cda1f7a" integrity sha512-RkDgbg5ahMv1MjHfJI2WJA2+Qbxq0iNSLWhreYiCHeHry9Q12sedCnP5KYGPt7sydDvsyH+8UcG6Kanq5mpsyw== +literallycanvas@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/literallycanvas/-/literallycanvas-0.5.2.tgz#7d4800a8d9c4b38a593e91695d52466689586abd" + integrity sha512-SPxZ0DfzUWOXBPnsFD/Y1OulGKpv33IKGB+DCoUkNKQsc38kRDy1mGjbWbs/JAAyIGM1C9Y0CYI23SQmtXR5aw== + dependencies: + react-addons-pure-render-mixin "^15.1" + +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + monaco-editor@^0.20.0: version "0.20.0" resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.20.0.tgz#5d5009343a550124426cb4d965a4d27a348b4dea" integrity sha512-hkvf4EtPJRMQlPC3UbMoRs0vTAFAYdzFQ+gpMb8A+9znae1c43q8Mab9iVsgTcg/4PNiLGGn3SlDIa8uvK1FIQ== +node-fetch@^1.0.1: + version "1.7.3" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" + integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ== + dependencies: + encoding "^0.1.11" + is-stream "^1.0.1" + +object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + plotly.js-dist@^1.52.3: version "1.52.3" resolved "https://registry.yarnpkg.com/plotly.js-dist/-/plotly.js-dist-1.52.3.tgz#4c16c6da6adab6cdba169087b5005bdddbf10834" @@ -81,11 +171,54 @@ popper.js@^1.14.5: resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b" integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ== +promise@^7.1.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" + integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== + dependencies: + asap "~2.0.3" + +react-addons-pure-render-mixin@^15.1: + version "15.6.3" + resolved "https://registry.yarnpkg.com/react-addons-pure-render-mixin/-/react-addons-pure-render-mixin-15.6.3.tgz#5dc73af0fa32186dbc4887f20667b46d3f9caed7" + integrity sha512-e7F2OsLiyYGr9SHWHGlI/FfHRh+kbYx0hNfdN5zivHIf4vzeno7gsRJKXg71E35CpUCnre+JfM6UgWWgsvJBzA== + dependencies: + object-assign "^4.1.0" + +react-dom-factories@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/react-dom-factories/-/react-dom-factories-1.0.2.tgz#eb7705c4db36fb501b3aa38ff759616aa0ff96e0" + integrity sha1-63cFxNs2+1AbOqOP91lhaqD/luA= + +react-dom@15.1: + version "15.1.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.1.0.tgz#d0c2b24c8b47a41a2b9ec766662d4e686f353153" + integrity sha1-0MKyTItHpBornsdmZi1OaG81MVM= + +react@15.1: + version "15.1.0" + resolved "https://registry.yarnpkg.com/react/-/react-15.1.0.tgz#5f7a9f085a00509898efd2b24cb12ea1dfaf8b40" + integrity sha1-X3qfCFoAUJiY79KyTLEuod+vi0A= + dependencies: + fbjs "^0.8.0" + loose-envify "^1.1.0" + object-assign "^4.1.0" + requirejs@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/requirejs/-/requirejs-2.3.6.tgz#e5093d9601c2829251258c0b9445d4d19fa9e7c9" integrity sha512-ipEzlWQe6RK3jkzikgCupiTbTvm4S0/CAU5GlgptkN5SO6F3u0UD0K18wy6ErDqiCyP4J4YYe1HuAShvsxePLg== +"safer-buffer@>= 2.1.2 < 3.0.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= + skatejs-template-html@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/skatejs-template-html/-/skatejs-template-html-0.0.0.tgz#e990c1a7d4b58b7305ffcc3338939bf402023df7" @@ -101,6 +234,11 @@ trim-extra-html-whitespace@1.3.0: resolved "https://registry.yarnpkg.com/trim-extra-html-whitespace/-/trim-extra-html-whitespace-1.3.0.tgz#b47efb0d1a5f2a56a85cc45cea525651e93404cf" integrity sha1-tH77DRpfKlaoXMRc6lJWUek0BM8= +ua-parser-js@^0.7.18: + version "0.7.28" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.28.tgz#8ba04e653f35ce210239c64661685bf9121dec31" + integrity sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g== + umbrellajs@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/umbrellajs/-/umbrellajs-3.1.0.tgz#a4e6f0f6381f9d93110b5eee962e0e0864b10bd0" @@ -110,3 +248,8 @@ underscore@>=1.8.3, underscore@^1.9.1: version "1.10.2" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.10.2.tgz#73d6aa3668f3188e4adb0f1943bd12cfd7efaaaf" integrity sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg== + +whatwg-fetch@>=0.10.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c" + integrity sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==