1 (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;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
2 var INFINITE, JSONToShape, LiterallyCanvas, Pencil, actions, bindEvents, createShape, math, ref, renderShapeToContext, renderShapeToSVG, renderSnapshotToImage, renderSnapshotToSVG, shapeToJSON, util,
3 bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
5 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; };
7 actions = require('./actions');
9 bindEvents = require('./bindEvents');
11 math = require('./math');
13 ref = require('./shapes'), createShape = ref.createShape, shapeToJSON = ref.shapeToJSON, JSONToShape = ref.JSONToShape;
15 renderShapeToContext = require('./canvasRenderer').renderShapeToContext;
17 renderShapeToSVG = require('./svgRenderer').renderShapeToSVG;
19 renderSnapshotToImage = require('./renderSnapshotToImage');
21 renderSnapshotToSVG = require('./renderSnapshotToSVG');
23 Pencil = require('../tools/Pencil');
25 util = require('./util');
27 INFINITE = 'infinite';
29 module.exports = LiterallyCanvas = (function() {
30 function LiterallyCanvas(arg1, arg2) {
31 this.setImageSize = bind(this.setImageSize, this);
32 var containerEl, opts;
35 if (arg1 instanceof HTMLElement) {
41 this.opts = opts || {};
43 zoomMin: opts.zoomMin || 0.2,
44 zoomMax: opts.zoomMax || 4.0,
45 zoomStep: opts.zoomStep || 0.2
48 primary: opts.primaryColor || '#000',
49 secondary: opts.secondaryColor || '#fff',
50 background: opts.backgroundColor || 'transparent'
52 this.watermarkImage = opts.watermarkImage;
53 this.watermarkScale = opts.watermarkScale || 1;
54 this.backgroundCanvas = document.createElement('canvas');
55 this.backgroundCtx = this.backgroundCanvas.getContext('2d');
56 this.canvas = document.createElement('canvas');
57 this.canvas.style['background-color'] = 'transparent';
58 this.buffer = document.createElement('canvas');
59 this.buffer.style['background-color'] = 'transparent';
60 this.ctx = this.canvas.getContext('2d');
61 this.bufferCtx = this.buffer.getContext('2d');
62 this.backingScale = util.getBackingScale(this.ctx);
63 this.backgroundShapes = opts.backgroundShapes || [];
64 this._shapesInProgress = [];
68 this.isDragging = false;
74 this.setTool(new this.opts.tools[0](this));
75 this.width = opts.imageSize.width || INFINITE;
76 this.height = opts.imageSize.height || INFINITE;
77 this.setZoom(this.scale);
79 this.loadSnapshot(opts.snapshot);
83 this.bindToElement(containerEl);
87 LiterallyCanvas.prototype.bindToElement = function(containerEl) {
89 if (this.containerEl) {
90 console.warn("Trying to bind Literally Canvas to a DOM element more than once is unsupported.");
93 this.containerEl = containerEl;
94 this._unsubscribeEvents = bindEvents(this, this.containerEl, this.opts.keyboardShortcuts);
95 this.containerEl.style['background-color'] = this.colors.background;
96 this.containerEl.appendChild(this.backgroundCanvas);
97 this.containerEl.appendChild(this.canvas);
99 repaintAll = (function(_this) {
101 _this.keepPanInImageBounds();
102 return _this.repaintAllLayers();
105 util.matchElementSize(this.containerEl, [this.backgroundCanvas, this.canvas], this.backingScale, repaintAll);
106 if (this.watermarkImage) {
107 this.watermarkImage.onload = (function(_this) {
109 return _this.repaintLayer('background');
113 if ((ref1 = this.tool) != null) {
114 ref1.didBecomeActive(this);
119 LiterallyCanvas.prototype._teardown = function() {
120 this.tool.willBecomeInactive(this);
121 if (typeof this._unsubscribeEvents === "function") {
122 this._unsubscribeEvents();
125 this.containerEl = null;
126 return this.isBound = false;
129 LiterallyCanvas.prototype.trigger = function(name, data) {
130 this.canvas.dispatchEvent(new CustomEvent(name, {
136 LiterallyCanvas.prototype.on = function(name, fn) {
138 wrapper = function(e) {
141 this.canvas.addEventListener(name, wrapper);
142 return (function(_this) {
144 return _this.canvas.removeEventListener(name, wrapper);
149 LiterallyCanvas.prototype.getRenderScale = function() {
150 return this.scale * this.backingScale;
153 LiterallyCanvas.prototype.clientCoordsToDrawingCoords = function(x, y) {
155 x: (x * this.backingScale - this.position.x) / this.getRenderScale(),
156 y: (y * this.backingScale - this.position.y) / this.getRenderScale()
160 LiterallyCanvas.prototype.drawingCoordsToClientCoords = function(x, y) {
162 x: x * this.getRenderScale() + this.position.x,
163 y: y * this.getRenderScale() + this.position.y
167 LiterallyCanvas.prototype.setImageSize = function(width, height) {
168 this.width = width || INFINITE;
169 this.height = height || INFINITE;
170 this.keepPanInImageBounds();
171 this.repaintAllLayers();
172 return this.trigger('imageSizeChange', {
178 LiterallyCanvas.prototype.setTool = function(tool) {
181 if ((ref1 = this.tool) != null) {
182 ref1.willBecomeInactive(this);
186 this.trigger('toolChange', {
190 return this.tool.didBecomeActive(this);
194 LiterallyCanvas.prototype.setShapesInProgress = function(newVal) {
195 return this._shapesInProgress = newVal;
198 LiterallyCanvas.prototype.pointerDown = function(x, y) {
200 p = this.clientCoordsToDrawingCoords(x, y);
201 if (this.tool.usesSimpleAPI) {
202 this.tool.begin(p.x, p.y, this);
203 this.isDragging = true;
204 return this.trigger("drawStart", {
208 this.isDragging = true;
209 return this.trigger("lc-pointerdown", {
219 LiterallyCanvas.prototype.pointerMove = function(x, y) {
220 return util.requestAnimationFrame((function(_this) {
223 p = _this.clientCoordsToDrawingCoords(x, y);
224 if ((ref1 = _this.tool) != null ? ref1.usesSimpleAPI : void 0) {
225 if (_this.isDragging) {
226 _this.tool["continue"](p.x, p.y, _this);
227 return _this.trigger("drawContinue", {
232 if (_this.isDragging) {
233 return _this.trigger("lc-pointerdrag", {
241 return _this.trigger("lc-pointermove", {
254 LiterallyCanvas.prototype.pointerUp = function(x, y) {
256 p = this.clientCoordsToDrawingCoords(x, y);
257 if (this.tool.usesSimpleAPI) {
258 if (this.isDragging) {
259 this.tool.end(p.x, p.y, this);
260 this.isDragging = false;
261 return this.trigger("drawEnd", {
266 this.isDragging = false;
267 return this.trigger("lc-pointerup", {
277 LiterallyCanvas.prototype.setColor = function(name, color) {
278 this.colors[name] = color;
284 this.containerEl.style.backgroundColor = this.colors.background;
285 this.repaintLayer('background');
288 this.repaintLayer('main');
291 this.repaintLayer('main');
293 this.trigger(name + "ColorChange", this.colors[name]);
294 if (name === 'background') {
295 return this.trigger("drawingChange");
299 LiterallyCanvas.prototype.getColor = function(name) {
300 return this.colors[name];
303 LiterallyCanvas.prototype.saveShape = function(shape, triggerShapeSaveEvent, previousShapeId) {
304 if (triggerShapeSaveEvent == null) {
305 triggerShapeSaveEvent = true;
307 if (previousShapeId == null) {
308 previousShapeId = null;
310 if (!previousShapeId) {
311 previousShapeId = this.shapes.length ? this.shapes[this.shapes.length - 1].id : null;
313 this.execute(new actions.AddShapeAction(this, shape, previousShapeId));
314 if (triggerShapeSaveEvent) {
315 this.trigger('shapeSave', {
317 previousShapeId: previousShapeId
320 return this.trigger('drawingChange');
323 LiterallyCanvas.prototype.pan = function(x, y) {
324 return this.setPan(this.position.x - x, this.position.y - y);
327 LiterallyCanvas.prototype.keepPanInImageBounds = function() {
328 var ref1, renderScale, x, y;
329 renderScale = this.getRenderScale();
330 ref1 = this.position, x = ref1.x, y = ref1.y;
331 if (this.width !== INFINITE) {
332 if (this.canvas.width > this.width * renderScale) {
333 x = (this.canvas.width - this.width * renderScale) / 2;
335 x = Math.max(Math.min(0, x), this.canvas.width - this.width * renderScale);
338 if (this.height !== INFINITE) {
339 if (this.canvas.height > this.height * renderScale) {
340 y = (this.canvas.height - this.height * renderScale) / 2;
342 y = Math.max(Math.min(0, y), this.canvas.height - this.height * renderScale);
345 return this.position = {
351 LiterallyCanvas.prototype.setPan = function(x, y) {
356 this.keepPanInImageBounds();
357 this.repaintAllLayers();
358 return this.trigger('pan', {
364 LiterallyCanvas.prototype.zoom = function(factor) {
366 newScale = this.scale + factor;
367 newScale = Math.max(newScale, this.config.zoomMin);
368 newScale = Math.min(newScale, this.config.zoomMax);
369 newScale = Math.round(newScale * 100) / 100;
370 return this.setZoom(newScale);
373 LiterallyCanvas.prototype.setZoom = function(scale) {
375 oldScale = this.scale;
377 this.position.x = math.scalePositionScalar(this.position.x, this.canvas.width, oldScale, this.scale);
378 this.position.y = math.scalePositionScalar(this.position.y, this.canvas.height, oldScale, this.scale);
379 this.keepPanInImageBounds();
380 this.repaintAllLayers();
381 return this.trigger('zoom', {
387 LiterallyCanvas.prototype.setWatermarkImage = function(newImage) {
388 this.watermarkImage = newImage;
389 util.addImageOnload(newImage, (function(_this) {
391 return _this.repaintLayer('background');
394 if (newImage.width) {
395 return this.repaintLayer('background');
399 LiterallyCanvas.prototype.repaintAllLayers = function() {
400 var i, key, len, ref1;
401 ref1 = ['background', 'main'];
402 for (i = 0, len = ref1.length; i < len; i++) {
404 this.repaintLayer(key);
409 LiterallyCanvas.prototype.repaintLayer = function(repaintLayerKey, dirty) {
412 dirty = repaintLayerKey === 'main';
417 switch (repaintLayerKey) {
419 this.backgroundCtx.clearRect(0, 0, this.backgroundCanvas.width, this.backgroundCanvas.height);
420 retryCallback = (function(_this) {
422 return _this.repaintLayer('background');
425 if (this.watermarkImage) {
426 this._renderWatermark(this.backgroundCtx, true, retryCallback);
428 this.draw(this.backgroundShapes, this.backgroundCtx, retryCallback);
431 retryCallback = (function(_this) {
433 return _this.repaintLayer('main', true);
437 this.buffer.width = this.canvas.width;
438 this.buffer.height = this.canvas.height;
439 this.bufferCtx.clearRect(0, 0, this.buffer.width, this.buffer.height);
440 this.draw(this.shapes, this.bufferCtx, retryCallback);
442 this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
443 if (this.canvas.width > 0 && this.canvas.height > 0) {
444 this.ctx.fillStyle = '#ccc';
445 this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
446 this.clipped(((function(_this) {
448 _this.ctx.clearRect(0, 0, _this.canvas.width, _this.canvas.height);
449 return _this.ctx.drawImage(_this.buffer, 0, 0);
451 })(this)), this.ctx);
452 this.clipped(((function(_this) {
454 return _this.transformed((function() {
455 var i, len, ref1, results, shape;
456 ref1 = _this._shapesInProgress;
458 for (i = 0, len = ref1.length; i < len; i++) {
460 results.push(renderShapeToContext(_this.ctx, shape, {
461 bufferCtx: _this.bufferCtx,
462 shouldOnlyDrawLatest: true
466 }), _this.ctx, _this.bufferCtx);
468 })(this)), this.ctx, this.bufferCtx);
471 return this.trigger('repaint', {
472 layerKey: repaintLayerKey
476 LiterallyCanvas.prototype._renderWatermark = function(ctx, worryAboutRetina, retryCallback) {
477 if (worryAboutRetina == null) {
478 worryAboutRetina = true;
480 if (!this.watermarkImage.width) {
481 this.watermarkImage.onload = retryCallback;
485 ctx.translate(ctx.canvas.width / 2, ctx.canvas.height / 2);
486 ctx.scale(this.watermarkScale, this.watermarkScale);
487 if (worryAboutRetina) {
488 ctx.scale(this.backingScale, this.backingScale);
490 ctx.drawImage(this.watermarkImage, -this.watermarkImage.width / 2, -this.watermarkImage.height / 2);
491 return ctx.restore();
494 LiterallyCanvas.prototype.drawShapeInProgress = function(shape) {
495 this.repaintLayer('main', false);
496 return this.clipped(((function(_this) {
498 return _this.transformed((function() {
499 return renderShapeToContext(_this.ctx, shape, {
500 bufferCtx: _this.bufferCtx,
501 shouldOnlyDrawLatest: true
503 }), _this.ctx, _this.bufferCtx);
505 })(this)), this.ctx, this.bufferCtx);
508 LiterallyCanvas.prototype.draw = function(shapes, ctx, retryCallback) {
510 if (!shapes.length) {
513 drawShapes = (function(_this) {
515 var i, len, results, shape;
517 for (i = 0, len = shapes.length; i < len; i++) {
519 results.push(renderShapeToContext(ctx, shape, {
520 retryCallback: retryCallback
526 return this.clipped(((function(_this) {
528 return _this.transformed(drawShapes, ctx);
533 LiterallyCanvas.prototype.clipped = function() {
534 var contexts, ctx, fn, height, i, j, len, len1, results, width, x, y;
535 fn = arguments[0], contexts = 2 <= arguments.length ? slice.call(arguments, 1) : [];
536 x = this.width === INFINITE ? 0 : this.position.x;
537 y = this.height === INFINITE ? 0 : this.position.y;
538 width = (function() {
539 switch (this.width) {
541 return this.canvas.width;
543 return this.width * this.getRenderScale();
546 height = (function() {
547 switch (this.height) {
549 return this.canvas.height;
551 return this.height * this.getRenderScale();
554 for (i = 0, len = contexts.length; i < len; i++) {
558 ctx.rect(x, y, width, height);
563 for (j = 0, len1 = contexts.length; j < len1; j++) {
565 results.push(ctx.restore());
570 LiterallyCanvas.prototype.transformed = function() {
571 var contexts, ctx, fn, i, j, len, len1, results, scale;
572 fn = arguments[0], contexts = 2 <= arguments.length ? slice.call(arguments, 1) : [];
573 for (i = 0, len = contexts.length; i < len; i++) {
576 ctx.translate(Math.floor(this.position.x), Math.floor(this.position.y));
577 scale = this.getRenderScale();
578 ctx.scale(scale, scale);
582 for (j = 0, len1 = contexts.length; j < len1; j++) {
584 results.push(ctx.restore());
589 LiterallyCanvas.prototype.clear = function(triggerClearEvent) {
590 var newShapes, oldShapes;
591 if (triggerClearEvent == null) {
592 triggerClearEvent = true;
594 oldShapes = this.shapes;
596 this.setShapesInProgress([]);
597 this.execute(new actions.ClearAction(this, oldShapes, newShapes));
598 this.repaintLayer('main');
599 if (triggerClearEvent) {
600 this.trigger('clear', null);
602 return this.trigger('drawingChange', {});
605 LiterallyCanvas.prototype.execute = function(action) {
606 this.undoStack.push(action);
608 return this.redoStack = [];
611 LiterallyCanvas.prototype.undo = function() {
613 if (!this.undoStack.length) {
616 action = this.undoStack.pop();
618 this.redoStack.push(action);
619 this.trigger('undo', {
622 return this.trigger('drawingChange', {});
625 LiterallyCanvas.prototype.redo = function() {
627 if (!this.redoStack.length) {
630 action = this.redoStack.pop();
631 this.undoStack.push(action);
633 this.trigger('redo', {
636 return this.trigger('drawingChange', {});
639 LiterallyCanvas.prototype.canUndo = function() {
640 return !!this.undoStack.length;
643 LiterallyCanvas.prototype.canRedo = function() {
644 return !!this.redoStack.length;
647 LiterallyCanvas.prototype.getPixel = function(x, y) {
649 p = this.drawingCoordsToClientCoords(x, y);
650 pixel = this.ctx.getImageData(p.x, p.y, 1, 1).data;
652 return "rgb(" + pixel[0] + ", " + pixel[1] + ", " + pixel[2] + ")";
658 LiterallyCanvas.prototype.getContentBounds = function() {
659 return util.getBoundingRect((this.shapes.concat(this.backgroundShapes)).map(function(s) {
660 return s.getBoundingRect();
661 }), this.width === INFINITE ? 0 : this.width, this.height === INFINITE ? 0 : this.height);
664 LiterallyCanvas.prototype.getDefaultImageRect = function(explicitSize, margin) {
666 if (explicitSize == null) {
672 if (margin == null) {
680 return util.getDefaultImageRect((function() {
681 var i, len, ref1, results;
682 ref1 = this.shapes.concat(this.backgroundShapes);
684 for (i = 0, len = ref1.length; i < len; i++) {
686 results.push(s.getBoundingRect(this.ctx));
689 }).call(this), explicitSize, margin);
692 LiterallyCanvas.prototype.getImage = function(opts) {
696 if (opts.includeWatermark == null) {
697 opts.includeWatermark = true;
699 if (opts.scaleDownRetina == null) {
700 opts.scaleDownRetina = true;
702 if (opts.scale == null) {
705 if (!opts.scaleDownRetina) {
706 opts.scale *= this.backingScale;
708 if (opts.includeWatermark) {
709 opts.watermarkImage = this.watermarkImage;
710 opts.watermarkScale = this.watermarkScale;
711 if (!opts.scaleDownRetina) {
712 opts.watermarkScale *= this.backingScale;
715 return renderSnapshotToImage(this.getSnapshot(), opts);
718 LiterallyCanvas.prototype.canvasForExport = function() {
719 this.repaintAllLayers();
720 return util.combineCanvases(this.backgroundCanvas, this.canvas);
723 LiterallyCanvas.prototype.canvasWithBackground = function(backgroundImageOrCanvas) {
724 return util.combineCanvases(backgroundImageOrCanvas, this.canvasForExport());
727 LiterallyCanvas.prototype.getSnapshot = function(keys) {
728 var i, k, len, ref1, shape, snapshot;
733 keys = ['shapes', 'imageSize', 'colors', 'position', 'scale', 'backgroundShapes'];
736 ref1 = ['colors', 'position', 'scale'];
737 for (i = 0, len = ref1.length; i < len; i++) {
739 if (indexOf.call(keys, k) >= 0) {
740 snapshot[k] = this[k];
743 if (indexOf.call(keys, 'shapes') >= 0) {
744 snapshot.shapes = (function() {
745 var j, len1, ref2, results;
748 for (j = 0, len1 = ref2.length; j < len1; j++) {
750 results.push(shapeToJSON(shape));
755 if (indexOf.call(keys, 'backgroundShapes') >= 0) {
756 snapshot.backgroundShapes = (function() {
757 var j, len1, ref2, results;
758 ref2 = this.backgroundShapes;
760 for (j = 0, len1 = ref2.length; j < len1; j++) {
762 results.push(shapeToJSON(shape));
767 if (indexOf.call(keys, 'imageSize') >= 0) {
768 snapshot.imageSize = {
776 LiterallyCanvas.prototype.getSnapshotJSON = function() {
777 console.warn("lc.getSnapshotJSON() is deprecated. use JSON.stringify(lc.getSnapshot()) instead.");
778 return JSON.stringify(this.getSnapshot());
781 LiterallyCanvas.prototype.getSVGString = function(opts) {
785 return renderSnapshotToSVG(this.getSnapshot(), opts);
788 LiterallyCanvas.prototype.loadSnapshot = function(snapshot) {
789 var i, j, k, len, len1, ref1, ref2, s, shape, shapeRepr;
793 if (snapshot.colors) {
794 ref1 = ['primary', 'secondary', 'background'];
795 for (i = 0, len = ref1.length; i < len; i++) {
797 this.setColor(k, snapshot.colors[k]);
800 if (snapshot.shapes) {
802 ref2 = snapshot.shapes;
803 for (j = 0, len1 = ref2.length; j < len1; j++) {
805 shape = JSONToShape(shapeRepr);
807 this.execute(new actions.AddShapeAction(this, shape));
811 if (snapshot.backgroundShapes) {
812 this.backgroundShapes = (function() {
813 var l, len2, ref3, results;
814 ref3 = snapshot.backgroundShapes;
816 for (l = 0, len2 = ref3.length; l < len2; l++) {
818 results.push(JSONToShape(s));
823 if (snapshot.imageSize) {
824 this.width = snapshot.imageSize.width;
825 this.height = snapshot.imageSize.height;
827 if (snapshot.position) {
828 this.position = snapshot.position;
830 if (snapshot.scale) {
831 this.scale = snapshot.scale;
833 this.repaintAllLayers();
834 this.trigger('snapshotLoad');
835 return this.trigger('drawingChange', {});
838 LiterallyCanvas.prototype.loadSnapshotJSON = function(str) {
839 console.warn("lc.loadSnapshotJSON() is deprecated. use lc.loadSnapshot(JSON.parse(snapshot)) instead.");
840 return this.loadSnapshot(JSON.parse(str));
843 return LiterallyCanvas;
848 },{"../tools/Pencil":24,"./actions":3,"./bindEvents":4,"./canvasRenderer":5,"./math":10,"./renderSnapshotToImage":11,"./renderSnapshotToSVG":12,"./shapes":13,"./svgRenderer":14,"./util":15}],2:[function(require,module,exports){
849 var TextRenderer, getLinesToRender, getNextLine, parseFontString;
851 require('./fontmetrics.js');
853 parseFontString = function(font) {
854 var fontFamily, fontItems, fontSize, item, j, len, maybeSize, remainingFontString;
855 fontItems = font.split(' ');
857 for (j = 0, len = fontItems.length; j < len; j++) {
859 maybeSize = parseInt(item.replace("px", ""), 10);
860 if (!isNaN(maybeSize)) {
861 fontSize = maybeSize;
865 throw "Font size not found";
867 remainingFontString = font.substring(fontItems[0].length + 1).replace('bold ', '').replace('italic ', '').replace('underline ', '');
868 fontFamily = remainingFontString;
871 fontFamily: fontFamily
875 getNextLine = function(ctx, text, forcedWidth) {
876 var doesSubstringFit, endIndex, isEndOfString, isNonWord, isWhitespace, lastGoodIndex, lastOkayIndex, nextWordStartIndex, textToHere, wasInWord;
886 isEndOfString = endIndex >= text.length;
887 isWhitespace = (!isEndOfString) && text[endIndex].match(/\s/);
888 isNonWord = isWhitespace || isEndOfString;
889 textToHere = text.substring(0, endIndex);
890 doesSubstringFit = forcedWidth ? ctx.measureTextWidth(textToHere).width <= forcedWidth : true;
891 if (doesSubstringFit) {
892 lastOkayIndex = endIndex;
894 if (isNonWord && wasInWord) {
896 if (doesSubstringFit) {
897 lastGoodIndex = endIndex;
900 wasInWord = !isWhitespace;
901 if (isEndOfString || !doesSubstringFit) {
902 if (doesSubstringFit) {
904 } else if (lastGoodIndex > 0) {
905 nextWordStartIndex = lastGoodIndex + 1;
906 while (nextWordStartIndex < text.length && text[nextWordStartIndex].match('/\s/')) {
907 nextWordStartIndex += 1;
909 return [text.substring(0, lastGoodIndex), text.substring(nextWordStartIndex)];
911 return [text.substring(0, lastOkayIndex), text.substring(lastOkayIndex)];
917 getLinesToRender = function(ctx, text, forcedWidth) {
918 var j, len, lines, nextLine, ref, ref1, remainingText, textLine, textSplitOnLines;
919 textSplitOnLines = text.split(/\r\n|\r|\n/g);
921 for (j = 0, len = textSplitOnLines.length; j < len; j++) {
922 textLine = textSplitOnLines[j];
923 ref = getNextLine(ctx, textLine, forcedWidth), nextLine = ref[0], remainingText = ref[1];
926 lines.push(nextLine);
927 ref1 = getNextLine(ctx, remainingText, forcedWidth), nextLine = ref1[0], remainingText = ref1[1];
930 lines.push(textLine);
936 TextRenderer = (function() {
937 function TextRenderer(ctx, text1, font1, forcedWidth1, forcedHeight) {
938 var fontFamily, fontSize, ref;
941 this.forcedWidth = forcedWidth1;
942 this.forcedHeight = forcedHeight;
943 ref = parseFontString(this.font), fontFamily = ref.fontFamily, fontSize = ref.fontSize;
944 ctx.font = this.font;
945 ctx.textBaseline = 'baseline';
946 this.emDashWidth = ctx.measureTextWidth('—', fontSize, fontFamily).width;
947 this.caratWidth = ctx.measureTextWidth('|', fontSize, fontFamily).width;
948 this.lines = getLinesToRender(ctx, this.text, this.forcedWidth);
949 this.metricses = this.lines.map((function(_this) {
950 return function(line) {
951 return ctx.measureText2(line || 'X', fontSize, _this.font);
955 ascent: Math.max.apply(Math, this.metricses.map(function(arg) {
960 descent: Math.max.apply(Math, this.metricses.map(function(arg) {
962 descent = arg.descent;
965 fontsize: Math.max.apply(Math, this.metricses.map(function(arg) {
967 fontsize = arg.fontsize;
970 leading: Math.max.apply(Math, this.metricses.map(function(arg) {
972 leading = arg.leading;
975 width: Math.max.apply(Math, this.metricses.map(function(arg) {
980 height: Math.max.apply(Math, this.metricses.map(function(arg) {
986 minx: Math.min.apply(Math, this.metricses.map(function(arg) {
991 miny: Math.min.apply(Math, this.metricses.map(function(arg) {
996 maxx: Math.max.apply(Math, this.metricses.map(function(arg) {
1001 maxy: Math.max.apply(Math, this.metricses.map(function(arg) {
1003 bounds = arg.bounds;
1008 this.boundingBoxWidth = Math.ceil(this.metrics.width);
1011 TextRenderer.prototype.draw = function(ctx, x, y) {
1012 var i, j, len, line, ref, results;
1013 ctx.textBaseline = 'top';
1014 ctx.font = this.font;
1018 for (j = 0, len = ref.length; j < len; j++) {
1020 ctx.fillText(line, x, y + i * this.metrics.leading);
1021 results.push(i += 1);
1026 TextRenderer.prototype.getWidth = function(isEditing) {
1027 if (isEditing == null) {
1030 if (this.forcedWidth) {
1031 return this.forcedWidth;
1034 return this.metrics.bounds.maxx + this.caratWidth;
1036 return this.metrics.bounds.maxx;
1041 TextRenderer.prototype.getHeight = function() {
1042 return this.forcedHeight || (this.metrics.leading * this.lines.length);
1045 return TextRenderer;
1049 module.exports = TextRenderer;
1052 },{"./fontmetrics.js":7}],3:[function(require,module,exports){
1053 var AddShapeAction, ClearAction;
1055 ClearAction = (function() {
1056 function ClearAction(lc1, oldShapes, newShapes1) {
1058 this.oldShapes = oldShapes;
1059 this.newShapes = newShapes1;
1062 ClearAction.prototype["do"] = function() {
1063 this.lc.shapes = this.newShapes;
1064 return this.lc.repaintLayer('main');
1067 ClearAction.prototype.undo = function() {
1068 this.lc.shapes = this.oldShapes;
1069 return this.lc.repaintLayer('main');
1076 AddShapeAction = (function() {
1077 function AddShapeAction(lc1, shape1, previousShapeId) {
1079 this.shape = shape1;
1080 this.previousShapeId = previousShapeId != null ? previousShapeId : null;
1083 AddShapeAction.prototype["do"] = function() {
1084 var found, i, len, newShapes, ref, shape;
1085 if (!this.lc.shapes.length || this.lc.shapes[this.lc.shapes.length - 1].id === this.previousShapeId || this.previousShapeId === null) {
1086 this.lc.shapes.push(this.shape);
1090 ref = this.lc.shapes;
1091 for (i = 0, len = ref.length; i < len; i++) {
1093 newShapes.push(shape);
1094 if (shape.id === this.previousShapeId) {
1095 newShapes.push(this.shape);
1100 newShapes.push(this.shape);
1102 this.lc.shapes = newShapes;
1104 return this.lc.repaintLayer('main');
1107 AddShapeAction.prototype.undo = function() {
1108 var i, len, newShapes, ref, shape;
1109 if (this.lc.shapes[this.lc.shapes.length - 1].id === this.shape.id) {
1110 this.lc.shapes.pop();
1113 ref = this.lc.shapes;
1114 for (i = 0, len = ref.length; i < len; i++) {
1116 if (shape.id !== this.shape.id) {
1117 newShapes.push(shape);
1120 lc.shapes = newShapes;
1122 return this.lc.repaintLayer('main');
1125 return AddShapeAction;
1130 ClearAction: ClearAction,
1131 AddShapeAction: AddShapeAction
1135 },{}],4:[function(require,module,exports){
1136 var bindEvents, buttonIsDown, coordsForTouchEvent, position;
1138 coordsForTouchEvent = function(el, e) {
1140 tx = e.changedTouches[0].clientX;
1141 ty = e.changedTouches[0].clientY;
1142 p = el.getBoundingClientRect();
1143 return [tx - p.left, ty - p.top];
1146 position = function(el, e) {
1148 p = el.getBoundingClientRect();
1150 left: e.clientX - p.left,
1151 top: e.clientY - p.top
1155 buttonIsDown = function(e) {
1156 if (e.buttons != null) {
1157 return e.buttons === 1;
1163 module.exports = bindEvents = function(lc, canvas, panWithKeyboard) {
1164 var listener, mouseMoveListener, mouseUpListener, touchEndListener, touchMoveListener, unsubs;
1165 if (panWithKeyboard == null) {
1166 panWithKeyboard = false;
1169 mouseMoveListener = (function(_this) {
1170 return function(e) {
1173 p = position(canvas, e);
1174 return lc.pointerMove(p.left, p.top);
1177 mouseUpListener = (function(_this) {
1178 return function(e) {
1181 canvas.onselectstart = function() {
1184 p = position(canvas, e);
1185 lc.pointerUp(p.left, p.top);
1186 document.removeEventListener('mousemove', mouseMoveListener);
1187 document.removeEventListener('mouseup', mouseUpListener);
1188 return canvas.addEventListener('mousemove', mouseMoveListener);
1191 canvas.addEventListener('mousedown', (function(_this) {
1192 return function(e) {
1194 if (e.target.tagName.toLowerCase() !== 'canvas') {
1199 canvas.onselectstart = function() {
1202 p = position(canvas, e);
1203 lc.pointerDown(p.left, p.top);
1204 canvas.removeEventListener('mousemove', mouseMoveListener);
1205 document.addEventListener('mousemove', mouseMoveListener);
1206 return document.addEventListener('mouseup', mouseUpListener);
1209 touchMoveListener = function(e) {
1211 return lc.pointerMove.apply(lc, coordsForTouchEvent(canvas, e));
1213 touchEndListener = function(e) {
1215 lc.pointerUp.apply(lc, coordsForTouchEvent(canvas, e));
1216 document.removeEventListener('touchmove', touchMoveListener);
1217 document.removeEventListener('touchend', touchEndListener);
1218 return document.removeEventListener('touchcancel', touchEndListener);
1220 canvas.addEventListener('touchstart', function(e) {
1221 if (e.target.tagName.toLowerCase() !== 'canvas') {
1225 if (e.touches.length === 1) {
1226 lc.pointerDown.apply(lc, coordsForTouchEvent(canvas, e));
1227 document.addEventListener('touchmove', touchMoveListener);
1228 document.addEventListener('touchend', touchEndListener);
1229 return document.addEventListener('touchcancel', touchEndListener);
1231 return lc.pointerMove.apply(lc, coordsForTouchEvent(canvas, e));
1234 if (panWithKeyboard) {
1235 console.warn("Keyboard panning is deprecated.");
1236 listener = function(e) {
1237 switch (e.keyCode) {
1250 return lc.repaintAllLayers();
1252 document.addEventListener('keydown', listener);
1253 unsubs.push(function() {
1254 return document.removeEventListener(listener);
1258 var f, i, len, results;
1260 for (i = 0, len = unsubs.length; i < len; i++) {
1269 },{}],5:[function(require,module,exports){
1270 var _drawRawLinePath, defineCanvasRenderer, drawErasedLinePath, drawErasedLinePathLatest, drawLinePath, drawLinePathLatest, lineEndCapShapes, noop, renderShapeToCanvas, renderShapeToContext, renderers;
1272 lineEndCapShapes = require('./lineEndCapShapes');
1276 defineCanvasRenderer = function(shapeName, drawFunc, drawLatestFunc) {
1277 return renderers[shapeName] = {
1279 drawLatestFunc: drawLatestFunc
1283 noop = function() {};
1285 renderShapeToContext = function(ctx, shape, opts) {
1290 if (opts.shouldIgnoreUnsupportedShapes == null) {
1291 opts.shouldIgnoreUnsupportedShapes = false;
1293 if (opts.retryCallback == null) {
1294 opts.retryCallback = noop;
1296 if (opts.shouldOnlyDrawLatest == null) {
1297 opts.shouldOnlyDrawLatest = false;
1299 if (opts.bufferCtx == null) {
1300 opts.bufferCtx = null;
1302 bufferCtx = opts.bufferCtx;
1303 if (renderers[shape.className]) {
1304 if (opts.shouldOnlyDrawLatest && renderers[shape.className].drawLatestFunc) {
1305 return renderers[shape.className].drawLatestFunc(ctx, bufferCtx, shape, opts.retryCallback);
1307 return renderers[shape.className].drawFunc(ctx, shape, opts.retryCallback);
1309 } else if (opts.shouldIgnoreUnsupportedShapes) {
1310 return console.warn("Can't render shape of type " + shape.className + " to canvas");
1312 throw "Can't render shape of type " + shape.className + " to canvas";
1316 renderShapeToCanvas = function(canvas, shape, opts) {
1317 return renderShapeToContext(canvas.getContext('2d'), shape, opts);
1320 defineCanvasRenderer('Rectangle', function(ctx, shape) {
1324 if (shape.strokeWidth % 2 !== 0) {
1328 ctx.fillStyle = shape.fillColor;
1329 ctx.fillRect(x, y, shape.width, shape.height);
1330 ctx.lineWidth = shape.strokeWidth;
1331 ctx.strokeStyle = shape.strokeColor;
1332 return ctx.strokeRect(x, y, shape.width, shape.height);
1335 defineCanvasRenderer('Ellipse', function(ctx, shape) {
1336 var centerX, centerY, halfHeight, halfWidth;
1338 halfWidth = Math.floor(shape.width / 2);
1339 halfHeight = Math.floor(shape.height / 2);
1340 centerX = shape.x + halfWidth;
1341 centerY = shape.y + halfHeight;
1342 ctx.translate(centerX, centerY);
1343 ctx.scale(1, Math.abs(shape.height / shape.width));
1345 ctx.arc(0, 0, Math.abs(halfWidth), 0, Math.PI * 2);
1348 ctx.fillStyle = shape.fillColor;
1350 ctx.lineWidth = shape.strokeWidth;
1351 ctx.strokeStyle = shape.strokeColor;
1352 return ctx.stroke();
1355 defineCanvasRenderer('SelectionBox', (function() {
1357 _drawHandle = function(ctx, arg, handleSize) {
1359 x = arg.x, y = arg.y;
1360 if (handleSize === 0) {
1363 ctx.fillStyle = '#fff';
1364 ctx.fillRect(x, y, handleSize, handleSize);
1365 ctx.strokeStyle = '#000';
1366 return ctx.strokeRect(x, y, handleSize, handleSize);
1368 return function(ctx, shape) {
1369 _drawHandle(ctx, shape.getTopLeftHandleRect(), shape.handleSize);
1370 _drawHandle(ctx, shape.getTopRightHandleRect(), shape.handleSize);
1371 _drawHandle(ctx, shape.getBottomLeftHandleRect(), shape.handleSize);
1372 _drawHandle(ctx, shape.getBottomRightHandleRect(), shape.handleSize);
1373 if (shape.backgroundColor) {
1374 ctx.fillStyle = shape.backgroundColor;
1375 ctx.fillRect(shape._br.x - shape.margin, shape._br.y - shape.margin, shape._br.width + shape.margin * 2, shape._br.height + shape.margin * 2);
1378 ctx.strokeStyle = '#000';
1379 ctx.setLineDash([2, 4]);
1380 ctx.strokeRect(shape._br.x - shape.margin, shape._br.y - shape.margin, shape._br.width + shape.margin * 2, shape._br.height + shape.margin * 2);
1381 return ctx.setLineDash([]);
1385 defineCanvasRenderer('Image', function(ctx, shape, retryCallback) {
1386 if (shape.image.width) {
1387 if (shape.scale === 1) {
1388 return ctx.drawImage(shape.image, shape.x, shape.y);
1390 return ctx.drawImage(shape.image, shape.x, shape.y, shape.image.width * shape.scale, shape.image.height * shape.scale);
1392 } else if (retryCallback) {
1393 return shape.image.onload = retryCallback;
1397 defineCanvasRenderer('Line', function(ctx, shape) {
1398 var arrowWidth, x1, x2, y1, y2;
1399 if (shape.x1 === shape.x2 && shape.y1 === shape.y2) {
1406 if (shape.strokeWidth % 2 !== 0) {
1412 ctx.lineWidth = shape.strokeWidth;
1413 ctx.strokeStyle = shape.color;
1414 ctx.lineCap = shape.capStyle;
1416 ctx.setLineDash(shape.dash);
1423 ctx.setLineDash([]);
1425 arrowWidth = Math.max(shape.strokeWidth * 2.2, 5);
1426 if (shape.endCapShapes[0]) {
1427 lineEndCapShapes[shape.endCapShapes[0]].drawToCanvas(ctx, x1, y1, Math.atan2(y1 - y2, x1 - x2), arrowWidth, shape.color);
1429 if (shape.endCapShapes[1]) {
1430 return lineEndCapShapes[shape.endCapShapes[1]].drawToCanvas(ctx, x2, y2, Math.atan2(y2 - y1, x2 - x1), arrowWidth, shape.color);
1434 _drawRawLinePath = function(ctx, points, close, lineCap) {
1435 var i, len, point, ref;
1436 if (close == null) {
1439 if (lineCap == null) {
1442 if (!points.length) {
1445 ctx.lineCap = lineCap;
1446 ctx.strokeStyle = points[0].color;
1447 ctx.lineWidth = points[0].size;
1449 if (points[0].size % 2 === 0) {
1450 ctx.moveTo(points[0].x, points[0].y);
1452 ctx.moveTo(points[0].x + 0.5, points[0].y + 0.5);
1454 ref = points.slice(1);
1455 for (i = 0, len = ref.length; i < len; i++) {
1457 if (points[0].size % 2 === 0) {
1458 ctx.lineTo(point.x, point.y);
1460 ctx.lineTo(point.x + 0.5, point.y + 0.5);
1464 return ctx.closePath();
1468 drawLinePath = function(ctx, shape) {
1469 _drawRawLinePath(ctx, shape.smoothedPoints);
1470 return ctx.stroke();
1473 drawLinePathLatest = function(ctx, bufferCtx, shape) {
1474 var drawEnd, drawStart, segmentStart;
1476 segmentStart = shape.smoothedPoints.length - shape.segmentSize * shape.tailSize;
1477 drawStart = segmentStart < shape.segmentSize * 2 ? 0 : segmentStart;
1478 drawEnd = segmentStart + shape.segmentSize + 1;
1479 _drawRawLinePath(bufferCtx, shape.smoothedPoints.slice(drawStart, drawEnd));
1480 return bufferCtx.stroke();
1482 _drawRawLinePath(bufferCtx, shape.smoothedPoints);
1483 return bufferCtx.stroke();
1487 defineCanvasRenderer('LinePath', drawLinePath, drawLinePathLatest);
1489 drawErasedLinePath = function(ctx, shape) {
1491 ctx.globalCompositeOperation = "destination-out";
1492 drawLinePath(ctx, shape);
1493 return ctx.restore();
1496 drawErasedLinePathLatest = function(ctx, bufferCtx, shape) {
1498 ctx.globalCompositeOperation = "destination-out";
1500 bufferCtx.globalCompositeOperation = "destination-out";
1501 drawLinePathLatest(ctx, bufferCtx, shape);
1503 return bufferCtx.restore();
1506 defineCanvasRenderer('ErasedLinePath', drawErasedLinePath, drawErasedLinePathLatest);
1508 defineCanvasRenderer('Text', function(ctx, shape) {
1509 if (!shape.renderer) {
1510 shape._makeRenderer(ctx);
1512 ctx.fillStyle = shape.color;
1513 return shape.renderer.draw(ctx, shape.x, shape.y);
1516 defineCanvasRenderer('Polygon', function(ctx, shape) {
1517 ctx.fillStyle = shape.fillColor;
1518 _drawRawLinePath(ctx, shape.points, shape.isClosed, 'butt');
1520 return ctx.stroke();
1524 defineCanvasRenderer: defineCanvasRenderer,
1525 renderShapeToCanvas: renderShapeToCanvas,
1526 renderShapeToContext: renderShapeToContext
1530 },{"./lineEndCapShapes":8}],6:[function(require,module,exports){
1534 imageURLPrefix: 'lib/img',
1535 primaryColor: 'hsla(0, 0%, 0%, 1)',
1536 secondaryColor: 'hsla(0, 0%, 100%, 1)',
1537 backgroundColor: 'transparent',
1538 strokeWidths: [1, 2, 5, 10, 20, 30],
1539 defaultStrokeWidth: 5,
1540 toolbarPosition: 'top',
1541 keyboardShortcuts: false,
1542 imageSize: { width: 'infinite', height: 'infinite' },
1543 backgroundShapes: [],
1544 watermarkImage: null,
1550 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')]
1553 },{"../tools/Ellipse":19,"../tools/Eraser":20,"../tools/Eyedropper":21,"../tools/Line":22,"../tools/Pan":23,"../tools/Pencil":24,"../tools/Polygon":25,"../tools/Rectangle":26,"../tools/Text":28}],7:[function(require,module,exports){
1557 This library rewrites the Canvas2D "measureText" function
1558 so that it returns a more complete metrics object.
1559 This library is licensed under the MIT (Expat) license,
1560 the text for which is included below.
1562 ** -----------------------------------------------------------------------------
1566 2012-01-21 - Whitespace handling added by Joe Turner
1567 (https://github.com/oampo)
1569 2015-06-08 - Various hacks added by Steve Johnson
1571 ** -----------------------------------------------------------------------------
1573 Copyright (C) 2011 by Mike "Pomax" Kamermans
1575 Permission is hereby granted, free of charge, to any person obtaining a copy
1576 of this software and associated documentation files (the "Software"), to deal
1577 in the Software without restriction, including without limitation the rights
1578 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1579 copies of the Software, and to permit persons to whom the Software is
1580 furnished to do so, subject to the following conditions:
1582 The above copyright notice and this permission notice shall be included in
1583 all copies or substantial portions of the Software.
1585 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1586 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1587 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1588 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1589 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1590 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
1594 var NAME = "FontMetrics Library";
1595 var VERSION = "1-2012.0121.1300";
1597 // if there is no getComputedStyle, this library won't work.
1598 if (!document.defaultView.getComputedStyle) {
1599 throw "ERROR: 'document.defaultView.getComputedStyle' not found. This library only works in browsers that can report computed CSS values.";
1602 // store the old text metrics function on the Canvas2D prototype
1603 CanvasRenderingContext2D.prototype.measureTextWidth = CanvasRenderingContext2D.prototype.measureText;
1606 * shortcut function for getting computed CSS values
1608 var getCSSValue = function getCSSValue(element, property) {
1609 return document.defaultView.getComputedStyle(element, null).getPropertyValue(property);
1613 var show = function show(canvas, ctx, xstart, w, h, metrics) {
1614 document.body.appendChild(canvas);
1615 ctx.strokeStyle = 'rgba(0, 0, 0, 0.5)';
1618 ctx.moveTo(xstart, 0);
1619 ctx.lineTo(xstart, h);
1624 ctx.moveTo(xstart + metrics.bounds.maxx, 0);
1625 ctx.lineTo(xstart + metrics.bounds.maxx, h);
1630 ctx.moveTo(0, h / 2 - metrics.ascent);
1631 ctx.lineTo(w, h / 2 - metrics.ascent);
1636 ctx.moveTo(0, h / 2 + metrics.descent);
1637 ctx.lineTo(w, h / 2 + metrics.descent);
1643 * The new text metrics function
1645 CanvasRenderingContext2D.prototype.measureText2 = function (textstring, fontSize, fontString) {
1646 var metrics = this.measureTextWidth(textstring),
1647 isSpace = !/\S/.test(textstring);
1648 metrics.fontsize = fontSize;
1650 // for text lead values, we meaure a multiline text container.
1651 var leadDiv = document.createElement("div");
1652 leadDiv.style.position = "absolute";
1653 leadDiv.style.opacity = 0;
1654 leadDiv.style.font = fontString;
1655 leadDiv.innerHTML = textstring + "<br/>" + textstring;
1656 document.body.appendChild(leadDiv);
1658 // make some initial guess at the text leading (using the standard TeX ratio)
1659 metrics.leading = 1.2 * fontSize;
1661 // then we try to get the real value from the browser
1662 var leadDivHeight = getCSSValue(leadDiv, "height");
1663 leadDivHeight = leadDivHeight.replace("px", "");
1664 if (leadDivHeight >= fontSize * 2) {
1665 metrics.leading = leadDivHeight / 2 | 0;
1667 document.body.removeChild(leadDiv);
1669 // if we're not dealing with white space, we can compute metrics
1671 // Have characters, so measure the text
1672 var canvas = document.createElement("canvas");
1674 canvas.width = metrics.width + padding;
1675 canvas.height = 3 * fontSize;
1676 canvas.style.opacity = 1;
1677 canvas.style.font = fontString;
1678 var ctx = canvas.getContext("2d");
1679 ctx.font = fontString;
1681 var w = canvas.width,
1685 // Set all canvas pixeldata values to 255, with all the content
1686 // data being 0. This lets us scan for data[i] != 255.
1687 ctx.fillStyle = "white";
1688 ctx.fillRect(-1, -1, w + 2, h + 2);
1689 ctx.fillStyle = "black";
1690 ctx.fillText(textstring, padding / 2, baseline);
1691 var pixelData = ctx.getImageData(0, 0, w, h).data;
1693 // canvas pixel data is w*4 by h*4, because R, G, B and A are separate,
1694 // consecutive values in the array, rather than stored as 32 bit ints.
1697 len = pixelData.length;
1699 // Finding the ascent uses a normal, forward scanline
1700 while (++i < len && pixelData[i] === 255) {}
1701 var ascent = i / w4 | 0;
1703 // Finding the descent uses a reverse scanline
1705 while (--i > 0 && pixelData[i] === 255) {}
1706 var descent = i / w4 | 0;
1708 // find the min-x coordinate
1709 for (i = 0; i < len && pixelData[i] === 255;) {
1715 var minx = i % w4 / 4 | 0;
1717 // find the max-x coordinate
1719 for (i = len - 3; i >= 0 && pixelData[i] === 255;) {
1722 i = len - 3 - step++ * 4;
1725 var maxx = i % w4 / 4 + 1 | 0;
1728 metrics.ascent = baseline - ascent;
1729 metrics.descent = descent - baseline;
1730 metrics.bounds = { minx: minx - padding / 2,
1731 maxx: maxx - padding / 2,
1733 maxy: descent - ascent };
1734 metrics.height = 1 + (descent - ascent);
1737 // if we ARE dealing with whitespace, most values will just be zero.
1739 // Only whitespace, so we can't measure the text
1741 metrics.descent = 0;
1742 metrics.bounds = { minx: 0,
1743 maxx: metrics.width, // Best guess
1752 },{}],8:[function(require,module,exports){
1754 arrow: (function() {
1756 getPoints = function(x, y, angle, width, length) {
1759 x: x + Math.cos(angle + Math.PI / 2) * width / 2,
1760 y: y + Math.sin(angle + Math.PI / 2) * width / 2
1762 x: x + Math.cos(angle) * length,
1763 y: y + Math.sin(angle) * length
1765 x: x + Math.cos(angle - Math.PI / 2) * width / 2,
1766 y: y + Math.sin(angle - Math.PI / 2) * width / 2
1771 drawToCanvas: function(ctx, x, y, angle, width, color, length) {
1773 if (length == null) {
1776 length = length || width;
1777 ctx.fillStyle = color;
1779 ctx.strokeStyle = 'transparent';
1781 points = getPoints(x, y, angle, width, length);
1782 ctx.moveTo(points[0].x, points[0].y);
1783 ctx.lineTo(points[1].x, points[1].y);
1784 ctx.lineTo(points[2].x, points[2].y);
1787 svg: function(x, y, angle, width, color, length) {
1789 if (length == null) {
1792 length = length || width;
1793 points = getPoints(x, y, angle, width, length);
1794 return "<polygon fill='" + color + "' stroke='none' points='" + (points.map(function(p) {
1795 return p.x + "," + p.y;
1803 },{}],9:[function(require,module,exports){
1804 var _, localize, strings;
1808 localize = function(localStrings) {
1809 return strings = localStrings;
1812 _ = function(string) {
1814 translation = strings[string];
1815 return translation || string;
1824 },{}],10:[function(require,module,exports){
1825 var Point, _slope, math, normals, unit, util;
1827 Point = require('./shapes').Point;
1829 util = require('./util');
1833 math.toPoly = function(line) {
1834 var i, index, len, n, point, polyLeft, polyRight;
1838 for (i = 0, len = line.length; i < len; i++) {
1840 n = normals(point, _slope(line, index));
1841 polyLeft = polyLeft.concat([n[0]]);
1842 polyRight = [n[1]].concat(polyRight);
1845 return polyLeft.concat(polyRight);
1848 _slope = function(line, index) {
1850 if (line.length < 3) {
1857 point = _slope(line, index + 1);
1858 } else if (index === line.length - 1) {
1859 point = _slope(line, index - 1);
1861 point = math.diff(line[index - 1], line[index + 1]);
1866 math.diff = function(a, b) {
1873 unit = function(vector) {
1875 length = math.len(vector);
1877 x: vector.x / length,
1878 y: vector.y / length
1882 normals = function(p, slope) {
1883 slope = unit(slope);
1884 slope.x = slope.x * p.size / 2;
1885 slope.y = slope.y * p.size / 2;
1899 math.len = function(vector) {
1900 return Math.sqrt(Math.pow(vector.x, 2) + Math.pow(vector.y, 2));
1903 math.scalePositionScalar = function(val, viewportSize, oldScale, newScale) {
1904 var newSize, oldSize;
1905 oldSize = viewportSize * oldScale;
1906 newSize = viewportSize * newScale;
1907 return val + (oldSize - newSize) / 2;
1910 module.exports = math;
1913 },{"./shapes":13,"./util":15}],11:[function(require,module,exports){
1914 var INFINITE, JSONToShape, renderWatermark, util;
1916 util = require('./util');
1918 JSONToShape = require('./shapes').JSONToShape;
1920 INFINITE = 'infinite';
1922 renderWatermark = function(ctx, image, scale) {
1927 ctx.translate(ctx.canvas.width / 2, ctx.canvas.height / 2);
1928 ctx.scale(scale, scale);
1929 ctx.drawImage(image, -image.width / 2, -image.height / 2);
1930 return ctx.restore();
1933 module.exports = function(snapshot, opts) {
1934 var allShapes, backgroundShapes, colors, imageSize, s, shapes, watermarkCanvas, watermarkCtx;
1938 if (opts.scale == null) {
1941 shapes = (function() {
1942 var i, len, ref, results;
1943 ref = snapshot.shapes;
1945 for (i = 0, len = ref.length; i < len; i++) {
1947 results.push(JSONToShape(s));
1951 backgroundShapes = [];
1952 if (snapshot.backgroundShapes) {
1953 backgroundShapes = (function() {
1954 var i, len, ref, results;
1955 ref = snapshot.backgroundShapes;
1957 for (i = 0, len = ref.length; i < len; i++) {
1959 results.push(JSONToShape(s));
1964 if (opts.margin == null) {
1972 imageSize = snapshot.imageSize || {
1976 colors = snapshot.colors || {
1977 background: 'transparent'
1979 allShapes = shapes.concat(backgroundShapes);
1980 watermarkCanvas = document.createElement('canvas');
1981 watermarkCtx = watermarkCanvas.getContext('2d');
1983 opts.rect.x -= opts.margin.left;
1984 opts.rect.y -= opts.margin.top;
1985 opts.rect.width += opts.margin.left + opts.margin.right;
1986 opts.rect.height += opts.margin.top + opts.margin.bottom;
1988 opts.rect = util.getDefaultImageRect((function() {
1989 var i, len, results;
1991 for (i = 0, len = allShapes.length; i < len; i++) {
1993 results.push(s.getBoundingRect(watermarkCtx));
1996 })(), imageSize, opts.margin);
1998 watermarkCanvas.width = opts.rect.width * opts.scale;
1999 watermarkCanvas.height = opts.rect.height * opts.scale;
2000 watermarkCtx.fillStyle = colors.background;
2001 watermarkCtx.fillRect(0, 0, watermarkCanvas.width, watermarkCanvas.height);
2002 if (!(opts.rect.width && opts.rect.height)) {
2005 if (opts.watermarkImage) {
2006 renderWatermark(watermarkCtx, opts.watermarkImage, opts.watermarkScale);
2008 return util.combineCanvases(watermarkCanvas, util.renderShapes(backgroundShapes, opts.rect, opts.scale), util.renderShapes(shapes, opts.rect, opts.scale));
2012 },{"./shapes":13,"./util":15}],12:[function(require,module,exports){
2013 var INFINITE, JSONToShape, util;
2015 util = require('./util');
2017 JSONToShape = require('./shapes').JSONToShape;
2019 INFINITE = 'infinite';
2021 module.exports = function(snapshot, opts) {
2022 var allShapes, backgroundShapes, colors, ctx, dummyCanvas, imageSize, s, shapes;
2026 shapes = (function() {
2027 var i, len, ref, results;
2028 ref = snapshot.shapes;
2030 for (i = 0, len = ref.length; i < len; i++) {
2032 results.push(JSONToShape(s));
2036 backgroundShapes = [];
2037 if (snapshot.backgroundShapes) {
2038 backgroundShapes = (function() {
2039 var i, len, ref, results;
2040 ref = snapshot.backgroundShapes;
2042 for (i = 0, len = ref.length; i < len; i++) {
2044 results.push(JSONToShape(s));
2049 if (opts.margin == null) {
2057 imageSize = snapshot.imageSize || {
2061 colors = snapshot.colors || {
2062 background: 'transparent'
2064 allShapes = shapes.concat(backgroundShapes);
2065 dummyCanvas = document.createElement('canvas');
2066 ctx = dummyCanvas.getContext('2d');
2068 opts.rect.x -= opts.margin.left;
2069 opts.rect.y -= opts.margin.top;
2070 opts.rect.width += opts.margin.left + opts.margin.right;
2071 opts.rect.height += opts.margin.top + opts.margin.bottom;
2073 opts.rect = util.getDefaultImageRect((function() {
2074 var i, len, results;
2076 for (i = 0, len = allShapes.length; i < len; i++) {
2078 results.push(s.getBoundingRect(ctx));
2081 })(), imageSize, opts.margin);
2083 return LC.renderShapesToSVG(backgroundShapes.concat(shapes), opts.rect, colors.background);
2087 },{"./shapes":13,"./util":15}],13:[function(require,module,exports){
2088 var JSONToShape, LinePath, TextRenderer, _createLinePathFromData, _doAllPointsShareStyle, _dual, _mid, _refine, bspline, createShape, defineCanvasRenderer, defineSVGRenderer, defineShape, lineEndCapShapes, linePathFuncs, ref, ref1, renderShapeToContext, renderShapeToSVG, shapeToJSON, shapes, util;
2090 util = require('./util');
2092 TextRenderer = require('./TextRenderer');
2094 lineEndCapShapes = require('./lineEndCapShapes');
2096 ref = require('./canvasRenderer'), defineCanvasRenderer = ref.defineCanvasRenderer, renderShapeToContext = ref.renderShapeToContext;
2098 ref1 = require('./svgRenderer'), defineSVGRenderer = ref1.defineSVGRenderer, renderShapeToSVG = ref1.renderShapeToSVG;
2102 defineShape = function(name, props) {
2103 var Shape, drawFunc, drawLatestFunc, k, legacyDrawFunc, legacyDrawLatestFunc, legacySVGFunc, svgFunc;
2104 Shape = function(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) {
2105 props.constructor.call(this, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p);
2108 Shape.prototype.className = name;
2109 Shape.fromJSON = props.fromJSON;
2111 legacyDrawFunc = props.draw;
2112 legacyDrawLatestFunc = props.draw || function(ctx, bufferCtx, retryCallback) {
2113 return this.draw(ctx, bufferCtx, retryCallback);
2115 drawFunc = function(ctx, shape, retryCallback) {
2116 return legacyDrawFunc.call(shape, ctx, retryCallback);
2118 drawLatestFunc = function(ctx, bufferCtx, shape, retryCallback) {
2119 return legacyDrawLatestFunc.call(shape, ctx, bufferCtx, retryCallback);
2122 if (props.drawLatest) {
2123 delete props.drawLatest;
2125 defineCanvasRenderer(name, drawFunc, drawLatestFunc);
2128 legacySVGFunc = props.toSVG;
2129 svgFunc = function(shape) {
2130 return legacySVGFunc.call(shape);
2133 defineSVGRenderer(name, svgFunc);
2135 Shape.prototype.draw = function(ctx, retryCallback) {
2136 return renderShapeToContext(ctx, this, {
2137 retryCallback: retryCallback
2140 Shape.prototype.drawLatest = function(ctx, bufferCtx, retryCallback) {
2141 return renderShapeToContext(ctx, this, {
2142 retryCallback: retryCallback,
2143 bufferCtx: bufferCtx,
2144 shouldOnlyDrawLatest: true
2147 Shape.prototype.toSVG = function() {
2148 return renderShapeToSVG(this);
2151 if (k !== 'fromJSON') {
2152 Shape.prototype[k] = props[k];
2155 shapes[name] = Shape;
2159 createShape = function(name, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) {
2161 s = new shapes[name](a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p);
2162 s.id = util.getGUID();
2166 JSONToShape = function(arg) {
2167 var className, data, id, shape;
2168 className = arg.className, data = arg.data, id = arg.id;
2169 if (className in shapes) {
2170 shape = shapes[className].fromJSON(data);
2177 console.log('Unreadable shape:', className, data);
2181 console.log("Unknown shape:", className, data);
2186 shapeToJSON = function(shape) {
2188 className: shape.className,
2189 data: shape.toJSON(),
2194 bspline = function(points, order) {
2198 return bspline(_dual(_dual(_refine(points))), order - 1);
2201 _refine = function(points) {
2202 var index, len, point, q, refined;
2203 points = [points[0]].concat(points).concat(util.last(points));
2206 for (q = 0, len = points.length; q < len; q++) {
2208 refined[index * 2] = point;
2209 if (points[index + 1]) {
2210 refined[index * 2 + 1] = _mid(point, points[index + 1]);
2217 _dual = function(points) {
2218 var dualed, index, len, point, q;
2221 for (q = 0, len = points.length; q < len; q++) {
2223 if (points[index + 1]) {
2224 dualed[index] = _mid(point, points[index + 1]);
2231 _mid = function(a, b) {
2232 return createShape('Point', {
2233 x: a.x + ((b.x - a.x) / 2),
2234 y: a.y + ((b.y - a.y) / 2),
2235 size: a.size + ((b.size - a.size) / 2),
2240 defineShape('Image', {
2241 constructor: function(args) {
2245 this.x = args.x || 0;
2246 this.y = args.y || 0;
2247 this.scale = args.scale || 1;
2248 return this.image = args.image || null;
2250 getBoundingRect: function() {
2254 width: this.image.width * this.scale,
2255 height: this.image.height * this.scale
2258 toJSON: function() {
2262 imageSrc: this.image.src,
2263 imageObject: this.image,
2267 fromJSON: function(data) {
2270 if ((ref2 = data.imageObject) != null ? ref2.width : void 0) {
2271 img = data.imageObject;
2274 img.src = data.imageSrc;
2276 return createShape('Image', {
2283 move: function(moveInfo) {
2284 if (moveInfo == null) {
2287 this.x = this.x - moveInfo.xDiff;
2288 return this.y = this.y - moveInfo.yDiff;
2290 setUpperLeft: function(upperLeft) {
2291 if (upperLeft == null) {
2294 this.x = upperLeft.x;
2295 return this.y = upperLeft.y;
2299 defineShape('Rectangle', {
2300 constructor: function(args) {
2304 this.x = args.x || 0;
2305 this.y = args.y || 0;
2306 this.width = args.width || 0;
2307 this.height = args.height || 0;
2308 this.strokeWidth = args.strokeWidth || 1;
2309 this.strokeColor = args.strokeColor || 'black';
2310 return this.fillColor = args.fillColor || 'transparent';
2312 getBoundingRect: function() {
2314 x: this.x - this.strokeWidth / 2,
2315 y: this.y - this.strokeWidth / 2,
2316 width: this.width + this.strokeWidth,
2317 height: this.height + this.strokeWidth
2320 toJSON: function() {
2325 height: this.height,
2326 strokeWidth: this.strokeWidth,
2327 strokeColor: this.strokeColor,
2328 fillColor: this.fillColor
2331 fromJSON: function(data) {
2332 return createShape('Rectangle', data);
2334 move: function(moveInfo) {
2335 if (moveInfo == null) {
2338 this.x = this.x - moveInfo.xDiff;
2339 return this.y = this.y - moveInfo.yDiff;
2341 setUpperLeft: function(upperLeft) {
2342 if (upperLeft == null) {
2345 this.x = upperLeft.x;
2346 return this.y = upperLeft.y;
2350 defineShape('Ellipse', {
2351 constructor: function(args) {
2355 this.x = args.x || 0;
2356 this.y = args.y || 0;
2357 this.width = args.width || 0;
2358 this.height = args.height || 0;
2359 this.strokeWidth = args.strokeWidth || 1;
2360 this.strokeColor = args.strokeColor || 'black';
2361 return this.fillColor = args.fillColor || 'transparent';
2363 getBoundingRect: function() {
2365 x: this.x - this.strokeWidth / 2,
2366 y: this.y - this.strokeWidth / 2,
2367 width: this.width + this.strokeWidth,
2368 height: this.height + this.strokeWidth
2371 toJSON: function() {
2376 height: this.height,
2377 strokeWidth: this.strokeWidth,
2378 strokeColor: this.strokeColor,
2379 fillColor: this.fillColor
2382 fromJSON: function(data) {
2383 return createShape('Ellipse', data);
2385 move: function(moveInfo) {
2386 if (moveInfo == null) {
2389 this.x = this.x - moveInfo.xDiff;
2390 return this.y = this.y - moveInfo.yDiff;
2392 setUpperLeft: function(upperLeft) {
2393 if (upperLeft == null) {
2396 this.x = upperLeft.x;
2397 return this.y = upperLeft.y;
2401 defineShape('Line', {
2402 constructor: function(args) {
2406 this.x1 = args.x1 || 0;
2407 this.y1 = args.y1 || 0;
2408 this.x2 = args.x2 || 0;
2409 this.y2 = args.y2 || 0;
2410 this.strokeWidth = args.strokeWidth || 1;
2411 this.color = args.color || 'black';
2412 this.capStyle = args.capStyle || 'round';
2413 this.endCapShapes = args.endCapShapes || [null, null];
2414 return this.dash = args.dash || null;
2416 getBoundingRect: function() {
2418 x: Math.min(this.x1, this.x2) - this.strokeWidth / 2,
2419 y: Math.min(this.y1, this.y2) - this.strokeWidth / 2,
2420 width: Math.abs(this.x2 - this.x1) + this.strokeWidth / 2,
2421 height: Math.abs(this.y2 - this.y1) + this.strokeWidth / 2
2424 toJSON: function() {
2430 strokeWidth: this.strokeWidth,
2432 capStyle: this.capStyle,
2434 endCapShapes: this.endCapShapes
2437 fromJSON: function(data) {
2438 return createShape('Line', data);
2440 move: function(moveInfo) {
2441 if (moveInfo == null) {
2444 this.x1 = this.x1 - moveInfo.xDiff;
2445 this.y1 = this.y1 - moveInfo.yDiff;
2446 this.x2 = this.x2 - moveInfo.xDiff;
2447 return this.y2 = this.y2 - moveInfo.yDiff;
2449 setUpperLeft: function(upperLeft) {
2450 var br, xDiff, yDiff;
2451 if (upperLeft == null) {
2454 br = this.getBoundingRect();
2455 xDiff = br.x - upperLeft.x;
2456 yDiff = br.y - upperLeft.y;
2464 _doAllPointsShareStyle = function(points) {
2465 var color, len, point, q, size;
2466 if (!points.length) {
2469 size = points[0].size;
2470 color = points[0].color;
2471 for (q = 0, len = points.length; q < len; q++) {
2473 if (!(point.size === size && point.color === color)) {
2474 console.log(size, color, point.size, point.color);
2476 if (!(point.size === size && point.color === color)) {
2483 _createLinePathFromData = function(shapeName, data) {
2484 var pointData, points, smoothedPoints, x, y;
2487 points = (function() {
2488 var len, q, ref2, results;
2491 for (q = 0, len = ref2.length; q < len; q++) {
2492 pointData = ref2[q];
2493 results.push(JSONToShape(pointData));
2497 } else if (data.pointCoordinatePairs) {
2498 points = (function() {
2499 var len, q, ref2, ref3, results;
2500 ref2 = data.pointCoordinatePairs;
2502 for (q = 0, len = ref2.length; q < len; q++) {
2503 ref3 = ref2[q], x = ref3[0], y = ref3[1];
2504 results.push(JSONToShape({
2509 size: data.pointSize,
2510 color: data.pointColor,
2518 smoothedPoints = null;
2519 if (data.smoothedPointCoordinatePairs) {
2520 smoothedPoints = (function() {
2521 var len, q, ref2, ref3, results;
2522 ref2 = data.smoothedPointCoordinatePairs;
2524 for (q = 0, len = ref2.length; q < len; q++) {
2525 ref3 = ref2[q], x = ref3[0], y = ref3[1];
2526 results.push(JSONToShape({
2531 size: data.pointSize,
2532 color: data.pointColor,
2543 return createShape(shapeName, {
2545 smoothedPoints: smoothedPoints,
2547 tailSize: data.tailSize,
2553 constructor: function(args) {
2554 var len, point, points, q, results;
2558 points = args.points || [];
2559 this.order = args.order || 3;
2560 this.tailSize = args.tailSize || 3;
2561 this.smooth = 'smooth' in args ? args.smooth : true;
2562 this.segmentSize = Math.pow(2, this.order);
2563 this.sampleSize = this.tailSize + 1;
2564 if (args.smoothedPoints) {
2565 this.points = args.points;
2566 return this.smoothedPoints = args.smoothedPoints;
2570 for (q = 0, len = points.length; q < len; q++) {
2572 results.push(this.addPoint(point));
2577 getBoundingRect: function() {
2578 return util.getBoundingRect(this.points.map(function(p) {
2580 x: p.x - p.size / 2,
2581 y: p.y - p.size / 2,
2587 toJSON: function() {
2589 if (_doAllPointsShareStyle(this.points)) {
2592 tailSize: this.tailSize,
2593 smooth: this.smooth,
2594 pointCoordinatePairs: (function() {
2595 var len, q, ref2, results;
2598 for (q = 0, len = ref2.length; q < len; q++) {
2600 results.push([point.x, point.y]);
2604 smoothedPointCoordinatePairs: (function() {
2605 var len, q, ref2, results;
2606 ref2 = this.smoothedPoints;
2608 for (q = 0, len = ref2.length; q < len; q++) {
2610 results.push([point.x, point.y]);
2614 pointSize: this.points[0].size,
2615 pointColor: this.points[0].color
2620 tailSize: this.tailSize,
2621 smooth: this.smooth,
2622 points: (function() {
2623 var len, q, ref2, results;
2626 for (q = 0, len = ref2.length; q < len; q++) {
2628 results.push(shapeToJSON(p));
2635 fromJSON: function(data) {
2636 return _createLinePathFromData('LinePath', data);
2638 addPoint: function(point) {
2639 this.points.push(point);
2641 this.smoothedPoints = this.points;
2644 if (!this.smoothedPoints || this.points.length < this.sampleSize) {
2645 return this.smoothedPoints = bspline(this.points, this.order);
2647 this.tail = util.last(bspline(util.last(this.points, this.sampleSize), this.order), this.segmentSize * this.tailSize);
2648 return this.smoothedPoints = this.smoothedPoints.slice(0, this.smoothedPoints.length - this.segmentSize * (this.tailSize - 1)).concat(this.tail);
2651 move: function(moveInfo) {
2652 var len, pt, pts, q;
2653 if (moveInfo == null) {
2659 pts = this.smoothedPoints;
2661 for (q = 0, len = pts.length; q < len; q++) {
2665 return this.points = this.smoothedPoints;
2667 setUpperLeft: function(upperLeft) {
2668 var br, xDiff, yDiff;
2669 if (upperLeft == null) {
2672 br = this.getBoundingRect();
2673 xDiff = br.x - upperLeft.x;
2674 yDiff = br.y - upperLeft.y;
2682 LinePath = defineShape('LinePath', linePathFuncs);
2684 defineShape('ErasedLinePath', {
2685 constructor: linePathFuncs.constructor,
2686 toJSON: linePathFuncs.toJSON,
2687 addPoint: linePathFuncs.addPoint,
2688 getBoundingRect: linePathFuncs.getBoundingRect,
2689 fromJSON: function(data) {
2690 return _createLinePathFromData('ErasedLinePath', data);
2694 defineShape('Point', {
2695 constructor: function(args) {
2699 this.x = args.x || 0;
2700 this.y = args.y || 0;
2701 this.size = args.size || 0;
2702 return this.color = args.color || '';
2704 getBoundingRect: function() {
2706 x: this.x - this.size / 2,
2707 y: this.y - this.size / 2,
2712 toJSON: function() {
2720 fromJSON: function(data) {
2721 return createShape('Point', data);
2723 move: function(moveInfo) {
2724 if (moveInfo == null) {
2727 this.x = this.x - moveInfo.xDiff;
2728 return this.y = this.y - moveInfo.yDiff;
2730 setUpperLeft: function(upperLeft) {
2731 if (upperLeft == null) {
2734 this.x = upperLeft.x;
2735 return this.y = upperLeft.y;
2739 defineShape('Polygon', {
2740 constructor: function(args) {
2741 var len, point, q, ref2, results;
2745 this.points = args.points;
2746 this.fillColor = args.fillColor || 'white';
2747 this.strokeColor = args.strokeColor || 'black';
2748 this.strokeWidth = args.strokeWidth;
2749 this.dash = args.dash || null;
2750 if (args.isClosed == null) {
2751 args.isClosed = true;
2753 this.isClosed = args.isClosed;
2756 for (q = 0, len = ref2.length; q < len; q++) {
2758 point.color = this.strokeColor;
2759 results.push(point.size = this.strokeWidth);
2763 addPoint: function(x, y) {
2764 return this.points.push(LC.createShape('Point', {
2769 getBoundingRect: function() {
2770 return util.getBoundingRect(this.points.map(function(p) {
2771 return p.getBoundingRect();
2774 toJSON: function() {
2776 strokeWidth: this.strokeWidth,
2777 fillColor: this.fillColor,
2778 strokeColor: this.strokeColor,
2780 isClosed: this.isClosed,
2781 pointCoordinatePairs: this.points.map(function(p) {
2786 fromJSON: function(data) {
2787 data.points = data.pointCoordinatePairs.map(function(arg) {
2789 x = arg[0], y = arg[1];
2790 return createShape('Point', {
2793 size: data.strokeWidth,
2794 color: data.strokeColor
2797 return createShape('Polygon', data);
2799 move: function(moveInfo) {
2800 var len, pt, q, ref2, results;
2801 if (moveInfo == null) {
2806 for (q = 0, len = ref2.length; q < len; q++) {
2808 results.push(pt.move(moveInfo));
2812 setUpperLeft: function(upperLeft) {
2813 var br, xDiff, yDiff;
2814 if (upperLeft == null) {
2817 br = this.getBoundingRect();
2818 xDiff = br.x - upperLeft.x;
2819 yDiff = br.y - upperLeft.y;
2827 defineShape('Text', {
2828 constructor: function(args) {
2832 this.x = args.x || 0;
2833 this.y = args.y || 0;
2834 this.v = args.v || 0;
2835 this.text = args.text || '';
2836 this.color = args.color || 'black';
2837 this.font = args.font || '18px sans-serif';
2838 this.forcedWidth = args.forcedWidth || null;
2839 return this.forcedHeight = args.forcedHeight || null;
2841 _makeRenderer: function(ctx) {
2842 ctx.lineHeight = 1.2;
2843 this.renderer = new TextRenderer(ctx, this.text, this.font, this.forcedWidth, this.forcedHeight);
2845 console.log('repairing baseline');
2847 this.x -= this.renderer.metrics.bounds.minx;
2848 return this.y -= this.renderer.metrics.leading - this.renderer.metrics.descent;
2851 setText: function(text) {
2853 return this.renderer = null;
2855 setFont: function(font) {
2857 return this.renderer = null;
2859 setPosition: function(x, y) {
2863 setSize: function(forcedWidth, forcedHeight) {
2864 this.forcedWidth = Math.max(forcedWidth, 0);
2865 this.forcedHeight = Math.max(forcedHeight, 0);
2866 return this.renderer = null;
2868 enforceMaxBoundingRect: function(lc) {
2869 var br, dx, lcBoundingRect;
2870 br = this.getBoundingRect(lc.ctx);
2872 x: -lc.position.x / lc.scale,
2873 y: -lc.position.y / lc.scale,
2874 width: lc.canvas.width / lc.scale,
2875 height: lc.canvas.height / lc.scale
2877 if (br.x + br.width > lcBoundingRect.x + lcBoundingRect.width) {
2878 dx = br.x - lcBoundingRect.x;
2879 this.forcedWidth = lcBoundingRect.width - dx - 10;
2880 return this.renderer = null;
2883 getBoundingRect: function(ctx, isEditing) {
2884 if (isEditing == null) {
2887 if (!this.renderer) {
2889 this._makeRenderer(ctx);
2891 throw "Must pass ctx if text hasn't been rendered yet";
2895 x: Math.floor(this.x),
2896 y: Math.floor(this.y),
2897 width: Math.ceil(this.renderer.getWidth(true)),
2898 height: Math.ceil(this.renderer.getHeight())
2901 toJSON: function() {
2908 forcedWidth: this.forcedWidth,
2909 forcedHeight: this.forcedHeight,
2913 fromJSON: function(data) {
2914 return createShape('Text', data);
2916 move: function(moveInfo) {
2917 if (moveInfo == null) {
2920 this.x = this.x - moveInfo.xDiff;
2921 return this.y = this.y - moveInfo.yDiff;
2923 setUpperLeft: function(upperLeft) {
2924 if (upperLeft == null) {
2927 this.x = upperLeft.x;
2928 return this.y = upperLeft.y;
2932 defineShape('SelectionBox', {
2933 constructor: function(args) {
2937 this.shape = args.shape;
2938 if (args.handleSize != null) {
2939 this.handleSize = args.handleSize;
2941 this.handleSize = 10;
2944 this.backgroundColor = args.backgroundColor || null;
2945 return this._br = this.shape.getBoundingRect(args.ctx);
2947 toJSON: function() {
2949 shape: shapeToJSON(this.shape),
2950 backgroundColor: this.backgroundColor
2953 fromJSON: function(arg) {
2954 var backgroundColor, handleSize, margin, shape;
2955 shape = arg.shape, handleSize = arg.handleSize, margin = arg.margin, backgroundColor = arg.backgroundColor;
2956 return createShape('SelectionBox', {
2957 shape: JSONToShape(shape),
2958 backgroundColor: backgroundColor
2961 getTopLeftHandleRect: function() {
2963 x: this._br.x - this.handleSize - this.margin,
2964 y: this._br.y - this.handleSize - this.margin,
2965 width: this.handleSize,
2966 height: this.handleSize
2969 getBottomLeftHandleRect: function() {
2971 x: this._br.x - this.handleSize - this.margin,
2972 y: this._br.y + this._br.height + this.margin,
2973 width: this.handleSize,
2974 height: this.handleSize
2977 getTopRightHandleRect: function() {
2979 x: this._br.x + this._br.width + this.margin,
2980 y: this._br.y - this.handleSize - this.margin,
2981 width: this.handleSize,
2982 height: this.handleSize
2985 getBottomRightHandleRect: function() {
2987 x: this._br.x + this._br.width + this.margin,
2988 y: this._br.y + this._br.height + this.margin,
2989 width: this.handleSize,
2990 height: this.handleSize
2993 getBoundingRect: function() {
2995 x: this._br.x - this.margin,
2996 y: this._br.y - this.margin,
2997 width: this._br.width + this.margin * 2,
2998 height: this._br.height + this.margin * 2
3004 defineShape: defineShape,
3005 createShape: createShape,
3006 JSONToShape: JSONToShape,
3007 shapeToJSON: shapeToJSON
3011 },{"./TextRenderer":2,"./canvasRenderer":5,"./lineEndCapShapes":8,"./svgRenderer":14,"./util":15}],14:[function(require,module,exports){
3012 var defineSVGRenderer, lineEndCapShapes, renderShapeToSVG, renderers;
3014 lineEndCapShapes = require('./lineEndCapShapes');
3018 defineSVGRenderer = function(shapeName, shapeToSVGFunc) {
3019 return renderers[shapeName] = shapeToSVGFunc;
3022 renderShapeToSVG = function(shape, opts) {
3026 if (opts.shouldIgnoreUnsupportedShapes == null) {
3027 opts.shouldIgnoreUnsupportedShapes = false;
3029 if (renderers[shape.className]) {
3030 return renderers[shape.className](shape);
3031 } else if (opts.shouldIgnoreUnsupportedShapes) {
3032 console.warn("Can't render shape of type " + shape.className + " to SVG");
3035 throw "Can't render shape of type " + shape.className + " to SVG";
3039 defineSVGRenderer('Rectangle', function(shape) {
3040 var height, width, x, x1, x2, y, y1, y2;
3043 x2 = shape.x + shape.width;
3044 y2 = shape.y + shape.height;
3045 x = Math.min(x1, x2);
3046 y = Math.min(y1, y2);
3047 width = Math.max(x1, x2) - x;
3048 height = Math.max(y1, y2) - y;
3049 if (shape.strokeWidth % 2 !== 0) {
3053 return "<rect x='" + x + "' y='" + y + "' width='" + width + "' height='" + height + "' stroke='" + shape.strokeColor + "' fill='" + shape.fillColor + "' stroke-width='" + shape.strokeWidth + "' />";
3056 defineSVGRenderer('SelectionBox', function(shape) {
3060 defineSVGRenderer('Ellipse', function(shape) {
3061 var centerX, centerY, halfHeight, halfWidth;
3062 halfWidth = Math.floor(shape.width / 2);
3063 halfHeight = Math.floor(shape.height / 2);
3064 centerX = shape.x + halfWidth;
3065 centerY = shape.y + halfHeight;
3066 return "<ellipse cx='" + centerX + "' cy='" + centerY + "' rx='" + (Math.abs(halfWidth)) + "' ry='" + (Math.abs(halfHeight)) + "' stroke='" + shape.strokeColor + "' fill='" + shape.fillColor + "' stroke-width='" + shape.strokeWidth + "' />";
3069 defineSVGRenderer('Image', function(shape) {
3070 return "<image x='" + shape.x + "' y='" + shape.y + "' width='" + (shape.image.naturalWidth * shape.scale) + "' height='" + (shape.image.naturalHeight * shape.scale) + "' xlink:href='" + shape.image.src + "' />";
3073 defineSVGRenderer('Line', function(shape) {
3074 var arrowWidth, capString, dashString, x1, x2, y1, y2;
3075 dashString = shape.dash ? "stroke-dasharray='" + (shape.dash.join(', ')) + "'" : '';
3077 arrowWidth = Math.max(shape.strokeWidth * 2.2, 5);
3082 if (shape.strokeWidth % 2 !== 0) {
3088 if (shape.endCapShapes[0]) {
3089 capString += lineEndCapShapes[shape.endCapShapes[0]].svg(x1, y1, Math.atan2(y1 - y2, x1 - x2), arrowWidth, shape.color);
3091 if (shape.endCapShapes[1]) {
3092 capString += lineEndCapShapes[shape.endCapShapes[1]].svg(x2, y2, Math.atan2(y2 - y1, x2 - x1), arrowWidth, shape.color);
3094 return "<g> <line x1='" + x1 + "' y1='" + y1 + "' x2='" + x2 + "' y2='" + y2 + "' " + dashString + " stroke-linecap='" + shape.capStyle + "' stroke='" + shape.color + " 'stroke-width='" + shape.strokeWidth + "' /> " + capString + " </g>";
3097 defineSVGRenderer('LinePath', function(shape) {
3098 return "<polyline fill='none' points='" + (shape.smoothedPoints.map(function(p) {
3100 offset = p.strokeWidth % 2 === 0 ? 0.0 : 0.5;
3101 return (p.x + offset) + "," + (p.y + offset);
3102 }).join(' ')) + "' stroke='" + shape.points[0].color + "' stroke-linecap='round' stroke-width='" + shape.points[0].size + "' />";
3105 defineSVGRenderer('ErasedLinePath', function(shape) {
3109 defineSVGRenderer('Polygon', function(shape) {
3110 if (shape.isClosed) {
3111 return "<polygon fill='" + shape.fillColor + "' points='" + (shape.points.map(function(p) {
3113 offset = p.strokeWidth % 2 === 0 ? 0.0 : 0.5;
3114 return (p.x + offset) + "," + (p.y + offset);
3115 }).join(' ')) + "' stroke='" + shape.strokeColor + "' stroke-width='" + shape.strokeWidth + "' />";
3117 return "<polyline fill='" + shape.fillColor + "' points='" + (shape.points.map(function(p) {
3119 offset = p.strokeWidth % 2 === 0 ? 0.0 : 0.5;
3120 return (p.x + offset) + "," + (p.y + offset);
3121 }).join(' ')) + "' stroke='none' /> <polyline fill='none' points='" + (shape.points.map(function(p) {
3123 offset = p.strokeWidth % 2 === 0 ? 0.0 : 0.5;
3124 return (p.x + offset) + "," + (p.y + offset);
3125 }).join(' ')) + "' stroke='" + shape.strokeColor + "' stroke-width='" + shape.strokeWidth + "' />";
3129 defineSVGRenderer('Text', function(shape) {
3130 var heightString, textSplitOnLines, widthString;
3131 widthString = shape.forcedWidth ? "width='" + shape.forcedWidth + "px'" : "";
3132 heightString = shape.forcedHeight ? "height='" + shape.forcedHeight + "px'" : "";
3133 textSplitOnLines = shape.text.split(/\r\n|\r|\n/g);
3134 if (shape.renderer) {
3135 textSplitOnLines = shape.renderer.lines;
3137 return "<text x='" + shape.x + "' y='" + shape.y + "' " + widthString + " " + heightString + " fill='" + shape.color + "' style='font: " + shape.font + ";'> " + (textSplitOnLines.map((function(_this) {
3138 return function(line, i) {
3140 dy = i === 0 ? 0 : '1.2em';
3141 return "<tspan x='" + shape.x + "' dy='" + dy + "' alignment-baseline='text-before-edge'> " + line + " </tspan>";
3143 })(this)).join('')) + " </text>";
3147 defineSVGRenderer: defineSVGRenderer,
3148 renderShapeToSVG: renderShapeToSVG
3152 },{"./lineEndCapShapes":8}],15:[function(require,module,exports){
3153 var renderShapeToContext, renderShapeToSVG, slice, util,
3156 slice = Array.prototype.slice;
3158 renderShapeToContext = require('./canvasRenderer').renderShapeToContext;
3160 renderShapeToSVG = require('./svgRenderer').renderShapeToSVG;
3163 addImageOnload: function(img, fn) {
3165 oldOnload = img.onload;
3166 img.onload = function() {
3167 if (typeof oldOnload === "function") {
3174 last: function(array, n) {
3179 return slice.call(array, Math.max(array.length - n, 0));
3181 return array[array.length - 1];
3184 classSet: function(classNameToIsPresent) {
3185 var classNames, key;
3187 for (key in classNameToIsPresent) {
3188 if (classNameToIsPresent[key]) {
3189 classNames.push(key);
3192 return classNames.join(' ');
3194 matchElementSize: function(elementToMatch, elementsToResize, scale, callback) {
3196 if (callback == null) {
3197 callback = function() {};
3199 resize = (function(_this) {
3202 for (i = 0, len = elementsToResize.length; i < len; i++) {
3203 el = elementsToResize[i];
3204 el.style.width = elementToMatch.offsetWidth + "px";
3205 el.style.height = elementToMatch.offsetHeight + "px";
3206 if (el.width != null) {
3207 el.setAttribute('width', el.offsetWidth * scale);
3208 el.setAttribute('height', el.offsetHeight * scale);
3214 elementToMatch.addEventListener('resize', resize);
3215 window.addEventListener('resize', resize);
3216 window.addEventListener('orientationchange', resize);
3219 combineCanvases: function() {
3220 var c, canvas, canvases, ctx, i, j, len, len1;
3221 canvases = 1 <= arguments.length ? slice1.call(arguments, 0) : [];
3222 c = document.createElement('canvas');
3223 c.width = canvases[0].width;
3224 c.height = canvases[0].height;
3225 for (i = 0, len = canvases.length; i < len; i++) {
3226 canvas = canvases[i];
3227 c.width = Math.max(canvas.width, c.width);
3228 c.height = Math.max(canvas.height, c.height);
3230 ctx = c.getContext('2d');
3231 for (j = 0, len1 = canvases.length; j < len1; j++) {
3232 canvas = canvases[j];
3233 ctx.drawImage(canvas, 0, 0);
3237 renderShapes: function(shapes, bounds, scale, canvas) {
3238 var ctx, i, len, shape;
3239 if (scale == null) {
3242 if (canvas == null) {
3245 canvas = canvas || document.createElement('canvas');
3246 canvas.width = bounds.width * scale;
3247 canvas.height = bounds.height * scale;
3248 ctx = canvas.getContext('2d');
3249 ctx.translate(-bounds.x * scale, -bounds.y * scale);
3250 ctx.scale(scale, scale);
3251 for (i = 0, len = shapes.length; i < len; i++) {
3253 renderShapeToContext(ctx, shape);
3257 renderShapesToSVG: function(shapes, arg, backgroundColor) {
3258 var height, width, x, y;
3259 x = arg.x, y = arg.y, width = arg.width, height = arg.height;
3260 return ("<svg xmlns='http://www.w3.org/2000/svg' width='" + width + "' height='" + height + "' viewBox='0 0 " + width + " " + height + "'> <rect width='" + width + "' height='" + height + "' x='0' y='0' fill='" + backgroundColor + "' /> <g transform='translate(" + (-x) + ", " + (-y) + ")'> " + (shapes.map(renderShapeToSVG).join('')) + " </g> </svg>").replace(/(\r\n|\n|\r)/gm, "");
3262 getBoundingRect: function(rects, width, height) {
3263 var i, len, maxX, maxY, minX, minY, rect;
3264 if (!rects.length) {
3274 maxX = rects[0].x + rects[0].width;
3275 maxY = rects[0].y + rects[0].height;
3276 for (i = 0, len = rects.length; i < len; i++) {
3278 minX = Math.floor(Math.min(rect.x, minX));
3279 minY = Math.floor(Math.min(rect.y, minY));
3280 maxX = Math.ceil(Math.max(maxX, rect.x + rect.width));
3281 maxY = Math.ceil(Math.max(maxY, rect.y + rect.height));
3283 minX = width ? 0 : minX;
3284 minY = height ? 0 : minY;
3285 maxX = width || maxX;
3286 maxY = height || maxY;
3294 getDefaultImageRect: function(shapeBoundingRects, explicitSize, margin) {
3295 var height, rect, width;
3296 if (explicitSize == null) {
3302 if (margin == null) {
3310 width = explicitSize.width, height = explicitSize.height;
3311 rect = util.getBoundingRect(shapeBoundingRects, width === 'infinite' ? 0 : width, height === 'infinite' ? 0 : height);
3312 rect.x -= margin.left;
3313 rect.y -= margin.top;
3314 rect.width += margin.left + margin.right;
3315 rect.height += margin.top + margin.bottom;
3318 getBackingScale: function(context) {
3319 if (window.devicePixelRatio == null) {
3322 if (!(window.devicePixelRatio > 1)) {
3325 return window.devicePixelRatio;
3327 requestAnimationFrame: (window.requestAnimationFrame || window.setTimeout).bind(window),
3328 getGUID: (function() {
3331 return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
3334 return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
3337 requestAnimationFrame: function(f) {
3338 if (window.webkitRequestAnimationFrame) {
3339 return window.webkitRequestAnimationFrame(f);
3341 if (window.requestAnimationFrame) {
3342 return window.requestAnimationFrame(f);
3344 if (window.mozRequestAnimationFrame) {
3345 return window.mozRequestAnimationFrame(f);
3347 return setTimeout(f, 0);
3349 cancelAnimationFrame: function(f) {
3350 if (window.webkitCancelRequestAnimationFrame) {
3351 return window.webkitCancelRequestAnimationFrame(f);
3353 if (window.webkitCancelAnimationFrame) {
3354 return window.webkitCancelAnimationFrame(f);
3356 if (window.cancelAnimationFrame) {
3357 return window.cancelAnimationFrame(f);
3359 if (window.mozCancelAnimationFrame) {
3360 return window.mozCancelAnimationFrame(f);
3362 return clearTimeout(f);
3366 module.exports = util;
3369 },{"./canvasRenderer":5,"./svgRenderer":14}],16:[function(require,module,exports){
3373 function CustomEvent(event, params) {
3374 params = params || { bubbles: false, cancelable: false, detail: undefined };
3375 var evt = document.createEvent('CustomEvent');
3376 evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
3380 CustomEvent.prototype = window.CustomEvent.prototype;
3382 window.CustomEvent = CustomEvent;
3385 },{}],17:[function(require,module,exports){
3388 var hasWarned = false;
3389 if (!CanvasRenderingContext2D.prototype.setLineDash) {
3390 CanvasRenderingContext2D.prototype.setLineDash = function () {
3393 console.warn("context2D.setLineDash is a no-op in this browser.");
3398 module.exports = null;
3400 },{}],18:[function(require,module,exports){
3401 var LiterallyCanvasModel, baseTools, canvasRenderer, conversion, defaultImageURLPrefix, defaultOptions, defaultTools, init, initWithoutGUI, localize, registerJQueryPlugin, renderSnapshotToImage, renderSnapshotToSVG, setDefaultImageURLPrefix, shapes, svgRenderer, tools, util;
3403 require('./ie_customevent');
3405 require('./ie_setLineDash');
3407 LiterallyCanvasModel = require('./core/LiterallyCanvas');
3409 defaultOptions = require('./core/defaultOptions');
3411 canvasRenderer = require('./core/canvasRenderer');
3413 svgRenderer = require('./core/svgRenderer');
3415 shapes = require('./core/shapes');
3417 util = require('./core/util');
3419 renderSnapshotToImage = require('./core/renderSnapshotToImage');
3421 renderSnapshotToSVG = require('./core/renderSnapshotToSVG');
3423 localize = require('./core/localization').localize;
3426 snapshotToShapes: function(snapshot) {
3427 var i, len, ref, results, shape;
3428 ref = snapshot.shapes;
3430 for (i = 0, len = ref.length; i < len; i++) {
3432 results.push(shapes.JSONToShape(shape));
3436 snapshotJSONToShapes: function(json) {
3437 return conversion.snapshotToShapes(JSON.parse(json));
3441 baseTools = require('./tools/base');
3444 Pencil: require('./tools/Pencil'),
3445 Eraser: require('./tools/Eraser'),
3446 Line: require('./tools/Line'),
3447 Rectangle: require('./tools/Rectangle'),
3448 Ellipse: require('./tools/Ellipse'),
3449 Text: require('./tools/Text'),
3450 Polygon: require('./tools/Polygon'),
3451 Pan: require('./tools/Pan'),
3452 Eyedropper: require('./tools/Eyedropper'),
3453 SelectShape: require('./tools/SelectShape'),
3454 Tool: baseTools.Tool,
3455 ToolWithStroke: baseTools.ToolWithStroke
3458 defaultTools = defaultOptions.tools;
3460 defaultImageURLPrefix = defaultOptions.imageURLPrefix;
3462 setDefaultImageURLPrefix = function(newDefault) {
3463 defaultImageURLPrefix = newDefault;
3464 return defaultOptions.imageURLPrefix = newDefault;
3467 init = function(el, opts) {
3468 var child, i, len, opt, ref;
3472 for (opt in defaultOptions) {
3473 if (!(opt in opts)) {
3474 opts[opt] = defaultOptions[opt];
3478 for (i = 0, len = ref.length; i < len; i++) {
3480 el.removeChild(child);
3482 return initWithoutGUI(el, opts);
3485 initWithoutGUI = function(el, opts) {
3486 var drawingViewElement, lc, originalClassName;
3487 originalClassName = el.className;
3488 if ([' ', ' '].join(el.className).indexOf(' literally ') === -1) {
3489 el.className = el.className + ' literally';
3491 el.className = el.className + ' toolbar-hidden';
3492 drawingViewElement = document.createElement('div');
3493 drawingViewElement.className = 'lc-drawing';
3494 el.appendChild(drawingViewElement);
3495 lc = new LiterallyCanvasModel(drawingViewElement, opts);
3496 lc.teardown = function() {
3497 var child, i, len, ref;
3500 for (i = 0, len = ref.length; i < len; i++) {
3502 el.removeChild(child);
3504 return el.className = originalClassName;
3506 if ('onInit' in opts) {
3512 registerJQueryPlugin = function(_$) {
3513 return _$.fn.literallycanvas = function(opts) {
3517 this.each((function(_this) {
3518 return function(ix, el) {
3519 return el.literallycanvas = init(el, opts);
3526 if (typeof window !== 'undefined') {
3531 registerJQueryPlugin(window.$);
3537 registerJQueryPlugin: registerJQueryPlugin,
3540 setDefaultImageURLPrefix: setDefaultImageURLPrefix,
3541 defaultTools: defaultTools,
3542 defineShape: shapes.defineShape,
3543 createShape: shapes.createShape,
3544 JSONToShape: shapes.JSONToShape,
3545 shapeToJSON: shapes.shapeToJSON,
3546 defineCanvasRenderer: canvasRenderer.defineCanvasRenderer,
3547 renderShapeToContext: canvasRenderer.renderShapeToContext,
3548 renderShapeToCanvas: canvasRenderer.renderShapeToCanvas,
3549 renderShapesToCanvas: util.renderShapes,
3550 defineSVGRenderer: svgRenderer.defineSVGRenderer,
3551 renderShapeToSVG: svgRenderer.renderShapeToSVG,
3552 renderShapesToSVG: util.renderShapesToSVG,
3553 snapshotToShapes: conversion.snapshotToShapes,
3554 snapshotJSONToShapes: conversion.snapshotJSONToShapes,
3555 renderSnapshotToImage: renderSnapshotToImage,
3556 renderSnapshotToSVG: renderSnapshotToSVG,
3561 },{"./core/LiterallyCanvas":1,"./core/canvasRenderer":5,"./core/defaultOptions":6,"./core/localization":9,"./core/renderSnapshotToImage":11,"./core/renderSnapshotToSVG":12,"./core/shapes":13,"./core/svgRenderer":14,"./core/util":15,"./ie_customevent":16,"./ie_setLineDash":17,"./tools/Ellipse":19,"./tools/Eraser":20,"./tools/Eyedropper":21,"./tools/Line":22,"./tools/Pan":23,"./tools/Pencil":24,"./tools/Polygon":25,"./tools/Rectangle":26,"./tools/SelectShape":27,"./tools/Text":28,"./tools/base":29}],19:[function(require,module,exports){
3562 var Ellipse, ToolWithStroke, createShape,
3563 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; },
3564 hasProp = {}.hasOwnProperty;
3566 ToolWithStroke = require('./base').ToolWithStroke;
3568 createShape = require('../core/shapes').createShape;
3570 module.exports = Ellipse = (function(superClass) {
3571 extend(Ellipse, superClass);
3573 function Ellipse() {
3574 return Ellipse.__super__.constructor.apply(this, arguments);
3577 Ellipse.prototype.name = 'Ellipse';
3579 Ellipse.prototype.iconName = 'ellipse';
3581 Ellipse.prototype.begin = function(x, y, lc) {
3582 return this.currentShape = createShape('Ellipse', {
3585 strokeWidth: this.strokeWidth,
3586 strokeColor: lc.getColor('primary'),
3587 fillColor: lc.getColor('secondary')
3591 Ellipse.prototype["continue"] = function(x, y, lc) {
3592 this.currentShape.width = x - this.currentShape.x;
3593 this.currentShape.height = y - this.currentShape.y;
3594 return lc.drawShapeInProgress(this.currentShape);
3597 Ellipse.prototype.end = function(x, y, lc) {
3598 return lc.saveShape(this.currentShape);
3606 },{"../core/shapes":13,"./base":29}],20:[function(require,module,exports){
3607 var Eraser, Pencil, createShape,
3608 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; },
3609 hasProp = {}.hasOwnProperty;
3611 Pencil = require('./Pencil');
3613 createShape = require('../core/shapes').createShape;
3615 module.exports = Eraser = (function(superClass) {
3616 extend(Eraser, superClass);
3619 return Eraser.__super__.constructor.apply(this, arguments);
3622 Eraser.prototype.name = 'Eraser';
3624 Eraser.prototype.iconName = 'eraser';
3626 Eraser.prototype.makePoint = function(x, y, lc) {
3627 return createShape('Point', {
3630 size: this.strokeWidth,
3635 Eraser.prototype.makeShape = function() {
3636 return createShape('ErasedLinePath');
3644 },{"../core/shapes":13,"./Pencil":24}],21:[function(require,module,exports){
3645 var Eyedropper, Tool, getPixel,
3646 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; },
3647 hasProp = {}.hasOwnProperty;
3649 Tool = require('./base').Tool;
3651 getPixel = function(ctx, arg) {
3653 x = arg.x, y = arg.y;
3654 pixel = ctx.getImageData(x, y, 1, 1).data;
3656 return "rgb(" + pixel[0] + ", " + pixel[1] + ", " + pixel[2] + ")";
3662 module.exports = Eyedropper = (function(superClass) {
3663 extend(Eyedropper, superClass);
3665 Eyedropper.prototype.name = 'Eyedropper';
3667 Eyedropper.prototype.iconName = 'eyedropper';
3669 Eyedropper.prototype.optionsStyle = 'stroke-or-fill';
3671 function Eyedropper(lc) {
3672 Eyedropper.__super__.constructor.call(this, lc);
3673 this.strokeOrFill = 'stroke';
3676 Eyedropper.prototype.readColor = function(x, y, lc) {
3677 var canvas, color, newColor, offset;
3678 offset = lc.getDefaultImageRect();
3679 canvas = lc.getImage();
3680 newColor = getPixel(canvas.getContext('2d'), {
3684 color = newColor || lc.getColor('background');
3685 if (this.strokeOrFill === 'stroke') {
3686 return lc.setColor('primary', newColor);
3688 return lc.setColor('secondary', newColor);
3692 Eyedropper.prototype.begin = function(x, y, lc) {
3693 return this.readColor(x, y, lc);
3696 Eyedropper.prototype["continue"] = function(x, y, lc) {
3697 return this.readColor(x, y, lc);
3705 },{"./base":29}],22:[function(require,module,exports){
3706 var Line, ToolWithStroke, createShape,
3707 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; },
3708 hasProp = {}.hasOwnProperty;
3710 ToolWithStroke = require('./base').ToolWithStroke;
3712 createShape = require('../core/shapes').createShape;
3714 module.exports = Line = (function(superClass) {
3715 extend(Line, superClass);
3718 return Line.__super__.constructor.apply(this, arguments);
3721 Line.prototype.name = 'Line';
3723 Line.prototype.iconName = 'line';
3725 Line.prototype.optionsStyle = 'line-options-and-stroke-width';
3727 Line.prototype.begin = function(x, y, lc) {
3728 return this.currentShape = createShape('Line', {
3733 strokeWidth: this.strokeWidth,
3736 case !this.isDashed:
3737 return [this.strokeWidth * 2, this.strokeWidth * 4];
3742 endCapShapes: this.hasEndArrow ? [null, 'arrow'] : null,
3743 color: lc.getColor('primary')
3747 Line.prototype["continue"] = function(x, y, lc) {
3748 this.currentShape.x2 = x;
3749 this.currentShape.y2 = y;
3750 return lc.drawShapeInProgress(this.currentShape);
3753 Line.prototype.end = function(x, y, lc) {
3754 return lc.saveShape(this.currentShape);
3762 },{"../core/shapes":13,"./base":29}],23:[function(require,module,exports){
3763 var Pan, Tool, createShape,
3764 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; },
3765 hasProp = {}.hasOwnProperty;
3767 Tool = require('./base').Tool;
3769 createShape = require('../core/shapes').createShape;
3771 module.exports = Pan = (function(superClass) {
3772 extend(Pan, superClass);
3775 return Pan.__super__.constructor.apply(this, arguments);
3778 Pan.prototype.name = 'Pan';
3780 Pan.prototype.iconName = 'pan';
3782 Pan.prototype.usesSimpleAPI = false;
3784 Pan.prototype.didBecomeActive = function(lc) {
3785 var unsubscribeFuncs;
3786 unsubscribeFuncs = [];
3787 this.unsubscribe = (function(_this) {
3789 var func, i, len, results;
3791 for (i = 0, len = unsubscribeFuncs.length; i < len; i++) {
3792 func = unsubscribeFuncs[i];
3793 results.push(func());
3798 unsubscribeFuncs.push(lc.on('lc-pointerdown', (function(_this) {
3799 return function(arg) {
3801 rawX = arg.rawX, rawY = arg.rawY;
3802 _this.oldPosition = lc.position;
3803 return _this.pointerStart = {
3809 return unsubscribeFuncs.push(lc.on('lc-pointerdrag', (function(_this) {
3810 return function(arg) {
3812 rawX = arg.rawX, rawY = arg.rawY;
3814 x: (rawX - _this.pointerStart.x) * lc.backingScale,
3815 y: (rawY - _this.pointerStart.y) * lc.backingScale
3817 return lc.setPan(_this.oldPosition.x + dp.x, _this.oldPosition.y + dp.y);
3822 Pan.prototype.willBecomeInactive = function(lc) {
3823 return this.unsubscribe();
3831 },{"../core/shapes":13,"./base":29}],24:[function(require,module,exports){
3832 var Pencil, ToolWithStroke, createShape,
3833 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; },
3834 hasProp = {}.hasOwnProperty;
3836 ToolWithStroke = require('./base').ToolWithStroke;
3838 createShape = require('../core/shapes').createShape;
3840 module.exports = Pencil = (function(superClass) {
3841 extend(Pencil, superClass);
3844 return Pencil.__super__.constructor.apply(this, arguments);
3847 Pencil.prototype.name = 'Pencil';
3849 Pencil.prototype.iconName = 'pencil';
3851 Pencil.prototype.eventTimeThreshold = 10;
3853 Pencil.prototype.begin = function(x, y, lc) {
3854 this.color = lc.getColor('primary');
3855 this.currentShape = this.makeShape();
3856 this.currentShape.addPoint(this.makePoint(x, y, lc));
3857 return this.lastEventTime = Date.now();
3860 Pencil.prototype["continue"] = function(x, y, lc) {
3862 timeDiff = Date.now() - this.lastEventTime;
3863 if (timeDiff > this.eventTimeThreshold) {
3864 this.lastEventTime += timeDiff;
3865 this.currentShape.addPoint(this.makePoint(x, y, lc));
3866 return lc.drawShapeInProgress(this.currentShape);
3870 Pencil.prototype.end = function(x, y, lc) {
3871 lc.saveShape(this.currentShape);
3872 return this.currentShape = void 0;
3875 Pencil.prototype.makePoint = function(x, y, lc) {
3876 return createShape('Point', {
3879 size: this.strokeWidth,
3884 Pencil.prototype.makeShape = function() {
3885 return createShape('LinePath');
3893 },{"../core/shapes":13,"./base":29}],25:[function(require,module,exports){
3894 var Polygon, ToolWithStroke, createShape,
3895 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; },
3896 hasProp = {}.hasOwnProperty;
3898 ToolWithStroke = require('./base').ToolWithStroke;
3900 createShape = require('../core/shapes').createShape;
3902 module.exports = Polygon = (function(superClass) {
3903 extend(Polygon, superClass);
3905 function Polygon() {
3906 return Polygon.__super__.constructor.apply(this, arguments);
3909 Polygon.prototype.name = 'Polygon';
3911 Polygon.prototype.iconName = 'polygon';
3913 Polygon.prototype.usesSimpleAPI = false;
3915 Polygon.prototype.didBecomeActive = function(lc) {
3916 var onDown, onMove, onUp, polygonCancel, polygonFinishClosed, polygonFinishOpen, polygonUnsubscribeFuncs;
3917 Polygon.__super__.didBecomeActive.call(this, lc);
3918 polygonUnsubscribeFuncs = [];
3919 this.polygonUnsubscribe = (function(_this) {
3921 var func, i, len, results;
3923 for (i = 0, len = polygonUnsubscribeFuncs.length; i < len; i++) {
3924 func = polygonUnsubscribeFuncs[i];
3925 results.push(func());
3931 this.maybePoint = null;
3932 onUp = (function(_this) {
3934 if (_this._getWillFinish()) {
3935 return _this._close(lc);
3937 lc.trigger('lc-polygon-started');
3939 _this.points.push(_this.maybePoint);
3941 _this.points = [_this.maybePoint];
3943 _this.maybePoint = {
3944 x: _this.maybePoint.x,
3945 y: _this.maybePoint.y
3947 lc.setShapesInProgress(_this._getShapes(lc));
3948 return lc.repaintLayer('main');
3951 onMove = (function(_this) {
3952 return function(arg) {
3954 x = arg.x, y = arg.y;
3955 if (_this.maybePoint) {
3956 _this.maybePoint.x = x;
3957 _this.maybePoint.y = y;
3958 lc.setShapesInProgress(_this._getShapes(lc));
3959 return lc.repaintLayer('main');
3963 onDown = (function(_this) {
3964 return function(arg) {
3966 x = arg.x, y = arg.y;
3967 _this.maybePoint = {
3971 lc.setShapesInProgress(_this._getShapes(lc));
3972 return lc.repaintLayer('main');
3975 polygonFinishOpen = (function(_this) {
3977 _this.maybePoint = {
3981 return _this._close(lc);
3984 polygonFinishClosed = (function(_this) {
3986 _this.maybePoint = _this.points[0];
3987 return _this._close(lc);
3990 polygonCancel = (function(_this) {
3992 return _this._cancel(lc);
3995 polygonUnsubscribeFuncs.push(lc.on('drawingChange', (function(_this) {
3997 return _this._cancel(lc);
4000 polygonUnsubscribeFuncs.push(lc.on('lc-pointerdown', onDown));
4001 polygonUnsubscribeFuncs.push(lc.on('lc-pointerdrag', onMove));
4002 polygonUnsubscribeFuncs.push(lc.on('lc-pointermove', onMove));
4003 polygonUnsubscribeFuncs.push(lc.on('lc-pointerup', onUp));
4004 polygonUnsubscribeFuncs.push(lc.on('lc-polygon-finishopen', polygonFinishOpen));
4005 polygonUnsubscribeFuncs.push(lc.on('lc-polygon-finishclosed', polygonFinishClosed));
4006 return polygonUnsubscribeFuncs.push(lc.on('lc-polygon-cancel', polygonCancel));
4009 Polygon.prototype.willBecomeInactive = function(lc) {
4010 Polygon.__super__.willBecomeInactive.call(this, lc);
4011 if (this.points || this.maybePoint) {
4014 return this.polygonUnsubscribe();
4017 Polygon.prototype._getArePointsClose = function(a, b) {
4018 return (Math.abs(a.x - b.x) + Math.abs(a.y - b.y)) < 10;
4021 Polygon.prototype._getWillClose = function() {
4022 if (!(this.points && this.points.length > 1)) {
4025 if (!this.maybePoint) {
4028 return this._getArePointsClose(this.points[0], this.maybePoint);
4031 Polygon.prototype._getWillFinish = function() {
4032 if (!(this.points && this.points.length > 1)) {
4035 if (!this.maybePoint) {
4038 return this._getArePointsClose(this.points[0], this.maybePoint) || this._getArePointsClose(this.points[this.points.length - 1], this.maybePoint);
4041 Polygon.prototype._cancel = function(lc) {
4042 lc.trigger('lc-polygon-stopped');
4043 this.maybePoint = null;
4045 lc.setShapesInProgress([]);
4046 return lc.repaintLayer('main');
4049 Polygon.prototype._close = function(lc) {
4050 lc.trigger('lc-polygon-stopped');
4051 lc.setShapesInProgress([]);
4052 if (this.points.length > 2) {
4053 lc.saveShape(this._getShape(lc, false));
4055 this.maybePoint = null;
4056 return this.points = null;
4059 Polygon.prototype._getShapes = function(lc, isInProgress) {
4061 if (isInProgress == null) {
4062 isInProgress = true;
4064 shape = this._getShape(lc, isInProgress);
4072 Polygon.prototype._getShape = function(lc, isInProgress) {
4074 if (isInProgress == null) {
4075 isInProgress = true;
4079 points = points.concat(this.points);
4081 if ((!isInProgress) && points.length < 3) {
4084 if (isInProgress && this.maybePoint) {
4085 points.push(this.maybePoint);
4087 if (points.length > 1) {
4088 return createShape('Polygon', {
4089 isClosed: this._getWillClose(),
4090 strokeColor: lc.getColor('primary'),
4091 fillColor: lc.getColor('secondary'),
4092 strokeWidth: this.strokeWidth,
4093 points: points.map(function(xy) {
4094 return createShape('Point', xy);
4102 Polygon.prototype.optionsStyle = 'polygon-and-stroke-width';
4109 },{"../core/shapes":13,"./base":29}],26:[function(require,module,exports){
4110 var Rectangle, ToolWithStroke, createShape,
4111 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; },
4112 hasProp = {}.hasOwnProperty;
4114 ToolWithStroke = require('./base').ToolWithStroke;
4116 createShape = require('../core/shapes').createShape;
4118 module.exports = Rectangle = (function(superClass) {
4119 extend(Rectangle, superClass);
4121 function Rectangle() {
4122 return Rectangle.__super__.constructor.apply(this, arguments);
4125 Rectangle.prototype.name = 'Rectangle';
4127 Rectangle.prototype.iconName = 'rectangle';
4129 Rectangle.prototype.begin = function(x, y, lc) {
4130 return this.currentShape = createShape('Rectangle', {
4133 strokeWidth: this.strokeWidth,
4134 strokeColor: lc.getColor('primary'),
4135 fillColor: lc.getColor('secondary')
4139 Rectangle.prototype["continue"] = function(x, y, lc) {
4140 this.currentShape.width = x - this.currentShape.x;
4141 this.currentShape.height = y - this.currentShape.y;
4142 return lc.drawShapeInProgress(this.currentShape);
4145 Rectangle.prototype.end = function(x, y, lc) {
4146 return lc.saveShape(this.currentShape);
4154 },{"../core/shapes":13,"./base":29}],27:[function(require,module,exports){
4155 var SelectShape, Tool, createShape,
4156 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; },
4157 hasProp = {}.hasOwnProperty;
4159 Tool = require('./base').Tool;
4161 createShape = require('../core/shapes').createShape;
4163 module.exports = SelectShape = (function(superClass) {
4164 extend(SelectShape, superClass);
4166 SelectShape.prototype.name = 'SelectShape';
4168 SelectShape.prototype.usesSimpleAPI = false;
4170 function SelectShape(lc) {
4171 this.selectCanvas = document.createElement('canvas');
4172 this.selectCanvas.style['background-color'] = 'transparent';
4173 this.selectCtx = this.selectCanvas.getContext('2d');
4176 SelectShape.prototype.didBecomeActive = function(lc) {
4177 var onDown, onDrag, onUp, selectShapeUnsubscribeFuncs;
4178 selectShapeUnsubscribeFuncs = [];
4179 this._selectShapeUnsubscribe = (function(_this) {
4181 var func, j, len, results;
4183 for (j = 0, len = selectShapeUnsubscribeFuncs.length; j < len; j++) {
4184 func = selectShapeUnsubscribeFuncs[j];
4185 results.push(func());
4190 onDown = (function(_this) {
4191 return function(arg) {
4192 var br, shapeIndex, x, y;
4193 x = arg.x, y = arg.y;
4194 _this.didDrag = false;
4195 shapeIndex = _this._getPixel(x, y, lc, _this.selectCtx);
4196 _this.selectedShape = lc.shapes[shapeIndex];
4197 if (_this.selectedShape != null) {
4198 lc.trigger('shapeSelected', {
4199 selectedShape: _this.selectedShape
4201 lc.setShapesInProgress([
4202 _this.selectedShape, createShape('SelectionBox', {
4203 shape: _this.selectedShape,
4207 lc.repaintLayer('main');
4208 br = _this.selectedShape.getBoundingRect();
4209 return _this.dragOffset = {
4216 onDrag = (function(_this) {
4217 return function(arg) {
4219 x = arg.x, y = arg.y;
4220 if (_this.selectedShape != null) {
4221 _this.didDrag = true;
4222 _this.selectedShape.setUpperLeft({
4223 x: x - _this.dragOffset.x,
4224 y: y - _this.dragOffset.y
4226 lc.setShapesInProgress([
4227 _this.selectedShape, createShape('SelectionBox', {
4228 shape: _this.selectedShape,
4232 return lc.repaintLayer('main');
4236 onUp = (function(_this) {
4237 return function(arg) {
4239 x = arg.x, y = arg.y;
4240 if (_this.didDrag) {
4241 _this.didDrag = false;
4242 lc.trigger('shapeMoved', {
4243 shape: _this.selectedShape
4245 lc.trigger('drawingChange', {});
4246 lc.repaintLayer('main');
4247 return _this._drawSelectCanvas(lc);
4251 selectShapeUnsubscribeFuncs.push(lc.on('lc-pointerdown', onDown));
4252 selectShapeUnsubscribeFuncs.push(lc.on('lc-pointerdrag', onDrag));
4253 selectShapeUnsubscribeFuncs.push(lc.on('lc-pointerup', onUp));
4254 return this._drawSelectCanvas(lc);
4257 SelectShape.prototype.willBecomeInactive = function(lc) {
4258 this._selectShapeUnsubscribe();
4259 return lc.setShapesInProgress([]);
4262 SelectShape.prototype._drawSelectCanvas = function(lc) {
4264 this.selectCanvas.width = lc.canvas.width;
4265 this.selectCanvas.height = lc.canvas.height;
4266 this.selectCtx.clearRect(0, 0, this.selectCanvas.width, this.selectCanvas.height);
4267 shapes = lc.shapes.map((function(_this) {
4268 return function(shape, index) {
4269 return createShape('SelectionBox', {
4272 backgroundColor: "#" + (_this._intToHex(index))
4276 return lc.draw(shapes, this.selectCtx);
4279 SelectShape.prototype._intToHex = function(i) {
4280 return ("000000" + (i.toString(16))).slice(-6);
4283 SelectShape.prototype._getPixel = function(x, y, lc, ctx) {
4285 p = lc.drawingCoordsToClientCoords(x, y);
4286 pixel = ctx.getImageData(p.x, p.y, 1, 1).data;
4288 return parseInt(this._rgbToHex(pixel[0], pixel[1], pixel[2]), 16);
4294 SelectShape.prototype._componentToHex = function(c) {
4296 hex = c.toString(16);
4297 return ("0" + hex).slice(-2);
4300 SelectShape.prototype._rgbToHex = function(r, g, b) {
4301 return "" + (this._componentToHex(r)) + (this._componentToHex(g)) + (this._componentToHex(b));
4309 },{"../core/shapes":13,"./base":29}],28:[function(require,module,exports){
4310 var Text, Tool, createShape, getIsPointInBox,
4311 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; },
4312 hasProp = {}.hasOwnProperty;
4314 Tool = require('./base').Tool;
4316 createShape = require('../core/shapes').createShape;
4318 getIsPointInBox = function(point, box) {
4319 if (point.x < box.x) {
4322 if (point.y < box.y) {
4325 if (point.x > box.x + box.width) {
4328 if (point.y > box.y + box.height) {
4334 module.exports = Text = (function(superClass) {
4335 extend(Text, superClass);
4337 Text.prototype.name = 'Text';
4339 Text.prototype.iconName = 'text';
4343 this.font = 'bold 18px sans-serif';
4344 this.currentShape = null;
4345 this.currentShapeState = null;
4346 this.initialShapeBoundingRect = null;
4347 this.dragAction = null;
4348 this.didDrag = false;
4351 Text.prototype.didBecomeActive = function(lc) {
4352 var switchAway, unsubscribeFuncs, updateInputEl;
4353 unsubscribeFuncs = [];
4354 this.unsubscribe = (function(_this) {
4356 var func, i, len, results;
4358 for (i = 0, len = unsubscribeFuncs.length; i < len; i++) {
4359 func = unsubscribeFuncs[i];
4360 results.push(func());
4365 switchAway = (function(_this) {
4367 _this._ensureNotEditing(lc);
4368 _this._clearCurrentShape(lc);
4369 return lc.repaintLayer('main');
4372 updateInputEl = (function(_this) {
4374 return _this._updateInputEl(lc);
4377 unsubscribeFuncs.push(lc.on('drawingChange', switchAway));
4378 unsubscribeFuncs.push(lc.on('zoom', updateInputEl));
4379 unsubscribeFuncs.push(lc.on('imageSizeChange', updateInputEl));
4380 unsubscribeFuncs.push(lc.on('snapshotLoad', (function(_this) {
4382 _this._clearCurrentShape(lc);
4383 return lc.repaintLayer('main');
4386 unsubscribeFuncs.push(lc.on('primaryColorChange', (function(_this) {
4387 return function(newColor) {
4388 if (!_this.currentShape) {
4391 _this.currentShape.color = newColor;
4392 _this._updateInputEl(lc);
4393 return lc.repaintLayer('main');
4396 return unsubscribeFuncs.push(lc.on('setFont', (function(_this) {
4397 return function(font) {
4398 if (!_this.currentShape) {
4402 _this.currentShape.setFont(font);
4403 _this._setShapesInProgress(lc);
4404 _this._updateInputEl(lc);
4405 return lc.repaintLayer('main');
4410 Text.prototype.willBecomeInactive = function(lc) {
4411 if (this.currentShape) {
4412 this._ensureNotEditing(lc);
4415 return this.unsubscribe();
4418 Text.prototype.setText = function(text) {
4419 return this.text = text;
4422 Text.prototype._ensureNotEditing = function(lc) {
4423 if (this.currentShapeState === 'editing') {
4424 return this._exitEditingState(lc);
4428 Text.prototype._clearCurrentShape = function(lc) {
4429 this.currentShape = null;
4430 this.initialShapeBoundingRect = null;
4431 this.currentShapeState = null;
4432 return lc.setShapesInProgress([]);
4435 Text.prototype.commit = function(lc) {
4436 if (this.currentShape.text) {
4437 lc.saveShape(this.currentShape);
4439 this._clearCurrentShape(lc);
4440 return lc.repaintLayer('main');
4443 Text.prototype._getSelectionShape = function(ctx, backgroundColor) {
4444 if (backgroundColor == null) {
4445 backgroundColor = null;
4447 return createShape('SelectionBox', {
4448 shape: this.currentShape,
4450 backgroundColor: backgroundColor
4454 Text.prototype._setShapesInProgress = function(lc) {
4455 switch (this.currentShapeState) {
4457 return lc.setShapesInProgress([this._getSelectionShape(lc.ctx), this.currentShape]);
4459 return lc.setShapesInProgress([this._getSelectionShape(lc.ctx, '#fff')]);
4461 return lc.setShapesInProgress([this.currentShape]);
4465 Text.prototype.begin = function(x, y, lc) {
4466 var br, point, selectionBox, selectionShape;
4467 this.dragAction = 'none';
4468 this.didDrag = false;
4469 if (this.currentShapeState === 'selected' || this.currentShapeState === 'editing') {
4470 br = this.currentShape.getBoundingRect(lc.ctx);
4471 selectionShape = this._getSelectionShape(lc.ctx);
4472 selectionBox = selectionShape.getBoundingRect();
4477 if (getIsPointInBox(point, br)) {
4478 this.dragAction = 'move';
4480 if (getIsPointInBox(point, selectionShape.getBottomRightHandleRect())) {
4481 this.dragAction = 'resizeBottomRight';
4483 if (getIsPointInBox(point, selectionShape.getTopLeftHandleRect())) {
4484 this.dragAction = 'resizeTopLeft';
4486 if (getIsPointInBox(point, selectionShape.getBottomLeftHandleRect())) {
4487 this.dragAction = 'resizeBottomLeft';
4489 if (getIsPointInBox(point, selectionShape.getTopRightHandleRect())) {
4490 this.dragAction = 'resizeTopRight';
4492 if (this.dragAction === 'none' && this.currentShapeState === 'editing') {
4493 this.dragAction = 'stop-editing';
4494 this._exitEditingState(lc);
4497 this.color = lc.getColor('primary');
4498 this.currentShape = createShape('Text', {
4506 this.dragAction = 'place';
4507 this.currentShapeState = 'selected';
4509 if (this.dragAction === 'none') {
4513 this.initialShapeBoundingRect = this.currentShape.getBoundingRect(lc.ctx);
4515 x: x - this.initialShapeBoundingRect.x,
4516 y: y - this.initialShapeBoundingRect.y
4518 this._setShapesInProgress(lc);
4519 return lc.repaintLayer('main');
4522 Text.prototype["continue"] = function(x, y, lc) {
4523 var br, brBottom, brRight;
4524 if (this.dragAction === 'none') {
4527 br = this.initialShapeBoundingRect;
4528 brRight = br.x + br.width;
4529 brBottom = br.y + br.height;
4530 switch (this.dragAction) {
4532 this.currentShape.x = x;
4533 this.currentShape.y = y;
4534 this.didDrag = true;
4537 this.currentShape.x = x - this.dragOffset.x;
4538 this.currentShape.y = y - this.dragOffset.y;
4539 this.didDrag = true;
4541 case 'resizeBottomRight':
4542 this.currentShape.setSize(x - (this.dragOffset.x - this.initialShapeBoundingRect.width) - br.x, y - (this.dragOffset.y - this.initialShapeBoundingRect.height) - br.y);
4544 case 'resizeTopLeft':
4545 this.currentShape.setSize(brRight - x + this.dragOffset.x, brBottom - y + this.dragOffset.y);
4546 this.currentShape.setPosition(x - this.dragOffset.x, y - this.dragOffset.y);
4548 case 'resizeBottomLeft':
4549 this.currentShape.setSize(brRight - x + this.dragOffset.x, y - (this.dragOffset.y - this.initialShapeBoundingRect.height) - br.y);
4550 this.currentShape.setPosition(x - this.dragOffset.x, this.currentShape.y);
4552 case 'resizeTopRight':
4553 this.currentShape.setSize(x - (this.dragOffset.x - this.initialShapeBoundingRect.width) - br.x, brBottom - y + this.dragOffset.y);
4554 this.currentShape.setPosition(this.currentShape.x, y - this.dragOffset.y);
4556 this._setShapesInProgress(lc);
4557 lc.repaintLayer('main');
4558 return this._updateInputEl(lc);
4561 Text.prototype.end = function(x, y, lc) {
4562 if (!this.currentShape) {
4565 this.currentShape.setSize(this.currentShape.forcedWidth, 0);
4566 if (this.currentShapeState === 'selected') {
4567 if (this.dragAction === 'place' || (this.dragAction === 'move' && !this.didDrag)) {
4568 this._enterEditingState(lc);
4571 this._setShapesInProgress(lc);
4572 lc.repaintLayer('main');
4573 return this._updateInputEl(lc);
4576 Text.prototype._enterEditingState = function(lc) {
4578 this.currentShapeState = 'editing';
4580 throw "State error";
4582 this.inputEl = document.createElement('textarea');
4583 this.inputEl.className = 'text-tool-input';
4584 this.inputEl.style.position = 'absolute';
4585 this.inputEl.style.transformOrigin = '0px 0px';
4586 this.inputEl.style.backgroundColor = 'transparent';
4587 this.inputEl.style.border = 'none';
4588 this.inputEl.style.outline = 'none';
4589 this.inputEl.style.margin = '0';
4590 this.inputEl.style.padding = '4px';
4591 this.inputEl.style.zIndex = '1000';
4592 this.inputEl.style.overflow = 'hidden';
4593 this.inputEl.style.resize = 'none';
4594 this.inputEl.value = this.currentShape.text;
4595 this.inputEl.addEventListener('mousedown', function(e) {
4596 return e.stopPropagation();
4598 this.inputEl.addEventListener('touchstart', function(e) {
4599 return e.stopPropagation();
4601 onChange = (function(_this) {
4602 return function(e) {
4603 _this.currentShape.setText(e.target.value);
4604 _this.currentShape.enforceMaxBoundingRect(lc);
4605 _this._setShapesInProgress(lc);
4606 lc.repaintLayer('main');
4607 _this._updateInputEl(lc);
4608 return e.stopPropagation();
4611 this.inputEl.addEventListener('keydown', (function(_this) {
4613 return _this._updateInputEl(lc, true);
4616 this.inputEl.addEventListener('keyup', onChange);
4617 this.inputEl.addEventListener('change', onChange);
4618 this._updateInputEl(lc);
4619 lc.containerEl.appendChild(this.inputEl);
4620 this.inputEl.focus();
4621 return this._setShapesInProgress(lc);
4624 Text.prototype._exitEditingState = function(lc) {
4625 this.currentShapeState = 'selected';
4626 lc.containerEl.removeChild(this.inputEl);
4627 this.inputEl = null;
4628 this._setShapesInProgress(lc);
4629 return lc.repaintLayer('main');
4632 Text.prototype._updateInputEl = function(lc, withMargin) {
4633 var br, transformString;
4634 if (withMargin == null) {
4637 if (!this.inputEl) {
4640 br = this.currentShape.getBoundingRect(lc.ctx, true);
4641 this.inputEl.style.font = this.currentShape.font;
4642 this.inputEl.style.color = this.currentShape.color;
4643 this.inputEl.style.left = (lc.position.x / lc.backingScale + br.x * lc.scale - 4) + "px";
4644 this.inputEl.style.top = (lc.position.y / lc.backingScale + br.y * lc.scale - 4) + "px";
4645 if (withMargin && !this.currentShape.forcedWidth) {
4646 this.inputEl.style.width = (br.width + 10 + this.currentShape.renderer.emDashWidth) + "px";
4648 this.inputEl.style.width = (br.width + 12) + "px";
4651 this.inputEl.style.height = (br.height + 10 + this.currentShape.renderer.metrics.leading) + "px";
4653 this.inputEl.style.height = (br.height + 10) + "px";
4655 transformString = "scale(" + lc.scale + ")";
4656 this.inputEl.style.transform = transformString;
4657 this.inputEl.style.webkitTransform = transformString;
4658 this.inputEl.style.MozTransform = transformString;
4659 this.inputEl.style.msTransform = transformString;
4660 return this.inputEl.style.OTransform = transformString;
4663 Text.prototype.optionsStyle = 'font';
4670 },{"../core/shapes":13,"./base":29}],29:[function(require,module,exports){
4671 var Tool, ToolWithStroke, tools,
4672 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; },
4673 hasProp = {}.hasOwnProperty;
4677 tools.Tool = Tool = (function() {
4680 Tool.prototype.name = null;
4682 Tool.prototype.iconName = null;
4684 Tool.prototype.usesSimpleAPI = true;
4686 Tool.prototype.begin = function(x, y, lc) {};
4688 Tool.prototype["continue"] = function(x, y, lc) {};
4690 Tool.prototype.end = function(x, y, lc) {};
4692 Tool.prototype.optionsStyle = null;
4694 Tool.prototype.didBecomeActive = function(lc) {};
4696 Tool.prototype.willBecomeInactive = function(lc) {};
4702 tools.ToolWithStroke = ToolWithStroke = (function(superClass) {
4703 extend(ToolWithStroke, superClass);
4705 function ToolWithStroke(lc) {
4706 this.strokeWidth = lc.opts.defaultStrokeWidth;
4709 ToolWithStroke.prototype.optionsStyle = 'stroke-width';
4711 ToolWithStroke.prototype.didBecomeActive = function(lc) {
4712 var unsubscribeFuncs;
4713 unsubscribeFuncs = [];
4714 this.unsubscribe = (function(_this) {
4716 var func, i, len, results;
4718 for (i = 0, len = unsubscribeFuncs.length; i < len; i++) {
4719 func = unsubscribeFuncs[i];
4720 results.push(func());
4725 return unsubscribeFuncs.push(lc.on('setStrokeWidth', (function(_this) {
4726 return function(strokeWidth) {
4727 _this.strokeWidth = strokeWidth;
4728 return lc.trigger('toolDidUpdateOptions');
4733 ToolWithStroke.prototype.willBecomeInactive = function(lc) {
4734 return this.unsubscribe();
4737 return ToolWithStroke;
4741 module.exports = tools;