Added literallycanvas and react libraries.
[openemr.git] / library / js / literallycanvas / js / literallycanvas-core.js
blob00696bded5b99907fd0c15dd41c8dc402b800081
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); }; },
4   slice = [].slice,
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;
33     opts = null;
34     containerEl = null;
35     if (arg1 instanceof HTMLElement) {
36       containerEl = arg1;
37       opts = arg2;
38     } else {
39       opts = arg1;
40     }
41     this.opts = opts || {};
42     this.config = {
43       zoomMin: opts.zoomMin || 0.2,
44       zoomMax: opts.zoomMax || 4.0,
45       zoomStep: opts.zoomStep || 0.2
46     };
47     this.colors = {
48       primary: opts.primaryColor || '#000',
49       secondary: opts.secondaryColor || '#fff',
50       background: opts.backgroundColor || 'transparent'
51     };
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 = [];
65     this.shapes = [];
66     this.undoStack = [];
67     this.redoStack = [];
68     this.isDragging = false;
69     this.position = {
70       x: 0,
71       y: 0
72     };
73     this.scale = 1.0;
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);
78     if (opts.snapshot) {
79       this.loadSnapshot(opts.snapshot);
80     }
81     this.isBound = false;
82     if (containerEl) {
83       this.bindToElement(containerEl);
84     }
85   }
87   LiterallyCanvas.prototype.bindToElement = function(containerEl) {
88     var ref1, repaintAll;
89     if (this.containerEl) {
90       console.warn("Trying to bind Literally Canvas to a DOM element more than once is unsupported.");
91       return;
92     }
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);
98     this.isBound = true;
99     repaintAll = (function(_this) {
100       return function() {
101         _this.keepPanInImageBounds();
102         return _this.repaintAllLayers();
103       };
104     })(this);
105     util.matchElementSize(this.containerEl, [this.backgroundCanvas, this.canvas], this.backingScale, repaintAll);
106     if (this.watermarkImage) {
107       this.watermarkImage.onload = (function(_this) {
108         return function() {
109           return _this.repaintLayer('background');
110         };
111       })(this);
112     }
113     if ((ref1 = this.tool) != null) {
114       ref1.didBecomeActive(this);
115     }
116     return repaintAll();
117   };
119   LiterallyCanvas.prototype._teardown = function() {
120     this.tool.willBecomeInactive(this);
121     if (typeof this._unsubscribeEvents === "function") {
122       this._unsubscribeEvents();
123     }
124     this.tool = null;
125     this.containerEl = null;
126     return this.isBound = false;
127   };
129   LiterallyCanvas.prototype.trigger = function(name, data) {
130     this.canvas.dispatchEvent(new CustomEvent(name, {
131       detail: data
132     }));
133     return null;
134   };
136   LiterallyCanvas.prototype.on = function(name, fn) {
137     var wrapper;
138     wrapper = function(e) {
139       return fn(e.detail);
140     };
141     this.canvas.addEventListener(name, wrapper);
142     return (function(_this) {
143       return function() {
144         return _this.canvas.removeEventListener(name, wrapper);
145       };
146     })(this);
147   };
149   LiterallyCanvas.prototype.getRenderScale = function() {
150     return this.scale * this.backingScale;
151   };
153   LiterallyCanvas.prototype.clientCoordsToDrawingCoords = function(x, y) {
154     return {
155       x: (x * this.backingScale - this.position.x) / this.getRenderScale(),
156       y: (y * this.backingScale - this.position.y) / this.getRenderScale()
157     };
158   };
160   LiterallyCanvas.prototype.drawingCoordsToClientCoords = function(x, y) {
161     return {
162       x: x * this.getRenderScale() + this.position.x,
163       y: y * this.getRenderScale() + this.position.y
164     };
165   };
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', {
173       width: this.width,
174       height: this.height
175     });
176   };
178   LiterallyCanvas.prototype.setTool = function(tool) {
179     var ref1;
180     if (this.isBound) {
181       if ((ref1 = this.tool) != null) {
182         ref1.willBecomeInactive(this);
183       }
184     }
185     this.tool = tool;
186     this.trigger('toolChange', {
187       tool: tool
188     });
189     if (this.isBound) {
190       return this.tool.didBecomeActive(this);
191     }
192   };
194   LiterallyCanvas.prototype.setShapesInProgress = function(newVal) {
195     return this._shapesInProgress = newVal;
196   };
198   LiterallyCanvas.prototype.pointerDown = function(x, y) {
199     var p;
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", {
205         tool: this.tool
206       });
207     } else {
208       this.isDragging = true;
209       return this.trigger("lc-pointerdown", {
210         tool: this.tool,
211         x: p.x,
212         y: p.y,
213         rawX: x,
214         rawY: y
215       });
216     }
217   };
219   LiterallyCanvas.prototype.pointerMove = function(x, y) {
220     return util.requestAnimationFrame((function(_this) {
221       return function() {
222         var p, ref1;
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", {
228               tool: _this.tool
229             });
230           }
231         } else {
232           if (_this.isDragging) {
233             return _this.trigger("lc-pointerdrag", {
234               tool: _this.tool,
235               x: p.x,
236               y: p.y,
237               rawX: x,
238               rawY: y
239             });
240           } else {
241             return _this.trigger("lc-pointermove", {
242               tool: _this.tool,
243               x: p.x,
244               y: p.y,
245               rawX: x,
246               rawY: y
247             });
248           }
249         }
250       };
251     })(this));
252   };
254   LiterallyCanvas.prototype.pointerUp = function(x, y) {
255     var p;
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", {
262           tool: this.tool
263         });
264       }
265     } else {
266       this.isDragging = false;
267       return this.trigger("lc-pointerup", {
268         tool: this.tool,
269         x: p.x,
270         y: p.y,
271         rawX: x,
272         rawY: y
273       });
274     }
275   };
277   LiterallyCanvas.prototype.setColor = function(name, color) {
278     this.colors[name] = color;
279     if (!this.isBound) {
280       return;
281     }
282     switch (name) {
283       case 'background':
284         this.containerEl.style.backgroundColor = this.colors.background;
285         this.repaintLayer('background');
286         break;
287       case 'primary':
288         this.repaintLayer('main');
289         break;
290       case 'secondary':
291         this.repaintLayer('main');
292     }
293     this.trigger(name + "ColorChange", this.colors[name]);
294     if (name === 'background') {
295       return this.trigger("drawingChange");
296     }
297   };
299   LiterallyCanvas.prototype.getColor = function(name) {
300     return this.colors[name];
301   };
303   LiterallyCanvas.prototype.saveShape = function(shape, triggerShapeSaveEvent, previousShapeId) {
304     if (triggerShapeSaveEvent == null) {
305       triggerShapeSaveEvent = true;
306     }
307     if (previousShapeId == null) {
308       previousShapeId = null;
309     }
310     if (!previousShapeId) {
311       previousShapeId = this.shapes.length ? this.shapes[this.shapes.length - 1].id : null;
312     }
313     this.execute(new actions.AddShapeAction(this, shape, previousShapeId));
314     if (triggerShapeSaveEvent) {
315       this.trigger('shapeSave', {
316         shape: shape,
317         previousShapeId: previousShapeId
318       });
319     }
320     return this.trigger('drawingChange');
321   };
323   LiterallyCanvas.prototype.pan = function(x, y) {
324     return this.setPan(this.position.x - x, this.position.y - y);
325   };
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;
334       } else {
335         x = Math.max(Math.min(0, x), this.canvas.width - this.width * renderScale);
336       }
337     }
338     if (this.height !== INFINITE) {
339       if (this.canvas.height > this.height * renderScale) {
340         y = (this.canvas.height - this.height * renderScale) / 2;
341       } else {
342         y = Math.max(Math.min(0, y), this.canvas.height - this.height * renderScale);
343       }
344     }
345     return this.position = {
346       x: x,
347       y: y
348     };
349   };
351   LiterallyCanvas.prototype.setPan = function(x, y) {
352     this.position = {
353       x: x,
354       y: y
355     };
356     this.keepPanInImageBounds();
357     this.repaintAllLayers();
358     return this.trigger('pan', {
359       x: this.position.x,
360       y: this.position.y
361     });
362   };
364   LiterallyCanvas.prototype.zoom = function(factor) {
365     var newScale;
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);
371   };
373   LiterallyCanvas.prototype.setZoom = function(scale) {
374     var oldScale;
375     oldScale = this.scale;
376     this.scale = 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', {
382       oldScale: oldScale,
383       newScale: this.scale
384     });
385   };
387   LiterallyCanvas.prototype.setWatermarkImage = function(newImage) {
388     this.watermarkImage = newImage;
389     util.addImageOnload(newImage, (function(_this) {
390       return function() {
391         return _this.repaintLayer('background');
392       };
393     })(this));
394     if (newImage.width) {
395       return this.repaintLayer('background');
396     }
397   };
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++) {
403       key = ref1[i];
404       this.repaintLayer(key);
405     }
406     return null;
407   };
409   LiterallyCanvas.prototype.repaintLayer = function(repaintLayerKey, dirty) {
410     var retryCallback;
411     if (dirty == null) {
412       dirty = repaintLayerKey === 'main';
413     }
414     if (!this.isBound) {
415       return;
416     }
417     switch (repaintLayerKey) {
418       case 'background':
419         this.backgroundCtx.clearRect(0, 0, this.backgroundCanvas.width, this.backgroundCanvas.height);
420         retryCallback = (function(_this) {
421           return function() {
422             return _this.repaintLayer('background');
423           };
424         })(this);
425         if (this.watermarkImage) {
426           this._renderWatermark(this.backgroundCtx, true, retryCallback);
427         }
428         this.draw(this.backgroundShapes, this.backgroundCtx, retryCallback);
429         break;
430       case 'main':
431         retryCallback = (function(_this) {
432           return function() {
433             return _this.repaintLayer('main', true);
434           };
435         })(this);
436         if (dirty) {
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);
441         }
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) {
447             return function() {
448               _this.ctx.clearRect(0, 0, _this.canvas.width, _this.canvas.height);
449               return _this.ctx.drawImage(_this.buffer, 0, 0);
450             };
451           })(this)), this.ctx);
452           this.clipped(((function(_this) {
453             return function() {
454               return _this.transformed((function() {
455                 var i, len, ref1, results, shape;
456                 ref1 = _this._shapesInProgress;
457                 results = [];
458                 for (i = 0, len = ref1.length; i < len; i++) {
459                   shape = ref1[i];
460                   results.push(renderShapeToContext(_this.ctx, shape, {
461                     bufferCtx: _this.bufferCtx,
462                     shouldOnlyDrawLatest: true
463                   }));
464                 }
465                 return results;
466               }), _this.ctx, _this.bufferCtx);
467             };
468           })(this)), this.ctx, this.bufferCtx);
469         }
470     }
471     return this.trigger('repaint', {
472       layerKey: repaintLayerKey
473     });
474   };
476   LiterallyCanvas.prototype._renderWatermark = function(ctx, worryAboutRetina, retryCallback) {
477     if (worryAboutRetina == null) {
478       worryAboutRetina = true;
479     }
480     if (!this.watermarkImage.width) {
481       this.watermarkImage.onload = retryCallback;
482       return;
483     }
484     ctx.save();
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);
489     }
490     ctx.drawImage(this.watermarkImage, -this.watermarkImage.width / 2, -this.watermarkImage.height / 2);
491     return ctx.restore();
492   };
494   LiterallyCanvas.prototype.drawShapeInProgress = function(shape) {
495     this.repaintLayer('main', false);
496     return this.clipped(((function(_this) {
497       return function() {
498         return _this.transformed((function() {
499           return renderShapeToContext(_this.ctx, shape, {
500             bufferCtx: _this.bufferCtx,
501             shouldOnlyDrawLatest: true
502           });
503         }), _this.ctx, _this.bufferCtx);
504       };
505     })(this)), this.ctx, this.bufferCtx);
506   };
508   LiterallyCanvas.prototype.draw = function(shapes, ctx, retryCallback) {
509     var drawShapes;
510     if (!shapes.length) {
511       return;
512     }
513     drawShapes = (function(_this) {
514       return function() {
515         var i, len, results, shape;
516         results = [];
517         for (i = 0, len = shapes.length; i < len; i++) {
518           shape = shapes[i];
519           results.push(renderShapeToContext(ctx, shape, {
520             retryCallback: retryCallback
521           }));
522         }
523         return results;
524       };
525     })(this);
526     return this.clipped(((function(_this) {
527       return function() {
528         return _this.transformed(drawShapes, ctx);
529       };
530     })(this)), ctx);
531   };
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) {
540         case INFINITE:
541           return this.canvas.width;
542         default:
543           return this.width * this.getRenderScale();
544       }
545     }).call(this);
546     height = (function() {
547       switch (this.height) {
548         case INFINITE:
549           return this.canvas.height;
550         default:
551           return this.height * this.getRenderScale();
552       }
553     }).call(this);
554     for (i = 0, len = contexts.length; i < len; i++) {
555       ctx = contexts[i];
556       ctx.save();
557       ctx.beginPath();
558       ctx.rect(x, y, width, height);
559       ctx.clip();
560     }
561     fn();
562     results = [];
563     for (j = 0, len1 = contexts.length; j < len1; j++) {
564       ctx = contexts[j];
565       results.push(ctx.restore());
566     }
567     return results;
568   };
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++) {
574       ctx = contexts[i];
575       ctx.save();
576       ctx.translate(Math.floor(this.position.x), Math.floor(this.position.y));
577       scale = this.getRenderScale();
578       ctx.scale(scale, scale);
579     }
580     fn();
581     results = [];
582     for (j = 0, len1 = contexts.length; j < len1; j++) {
583       ctx = contexts[j];
584       results.push(ctx.restore());
585     }
586     return results;
587   };
589   LiterallyCanvas.prototype.clear = function(triggerClearEvent) {
590     var newShapes, oldShapes;
591     if (triggerClearEvent == null) {
592       triggerClearEvent = true;
593     }
594     oldShapes = this.shapes;
595     newShapes = [];
596     this.setShapesInProgress([]);
597     this.execute(new actions.ClearAction(this, oldShapes, newShapes));
598     this.repaintLayer('main');
599     if (triggerClearEvent) {
600       this.trigger('clear', null);
601     }
602     return this.trigger('drawingChange', {});
603   };
605   LiterallyCanvas.prototype.execute = function(action) {
606     this.undoStack.push(action);
607     action["do"]();
608     return this.redoStack = [];
609   };
611   LiterallyCanvas.prototype.undo = function() {
612     var action;
613     if (!this.undoStack.length) {
614       return;
615     }
616     action = this.undoStack.pop();
617     action.undo();
618     this.redoStack.push(action);
619     this.trigger('undo', {
620       action: action
621     });
622     return this.trigger('drawingChange', {});
623   };
625   LiterallyCanvas.prototype.redo = function() {
626     var action;
627     if (!this.redoStack.length) {
628       return;
629     }
630     action = this.redoStack.pop();
631     this.undoStack.push(action);
632     action["do"]();
633     this.trigger('redo', {
634       action: action
635     });
636     return this.trigger('drawingChange', {});
637   };
639   LiterallyCanvas.prototype.canUndo = function() {
640     return !!this.undoStack.length;
641   };
643   LiterallyCanvas.prototype.canRedo = function() {
644     return !!this.redoStack.length;
645   };
647   LiterallyCanvas.prototype.getPixel = function(x, y) {
648     var p, pixel;
649     p = this.drawingCoordsToClientCoords(x, y);
650     pixel = this.ctx.getImageData(p.x, p.y, 1, 1).data;
651     if (pixel[3]) {
652       return "rgb(" + pixel[0] + ", " + pixel[1] + ", " + pixel[2] + ")";
653     } else {
654       return null;
655     }
656   };
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);
662   };
664   LiterallyCanvas.prototype.getDefaultImageRect = function(explicitSize, margin) {
665     var s;
666     if (explicitSize == null) {
667       explicitSize = {
668         width: 0,
669         height: 0
670       };
671     }
672     if (margin == null) {
673       margin = {
674         top: 0,
675         right: 0,
676         bottom: 0,
677         left: 0
678       };
679     }
680     return util.getDefaultImageRect((function() {
681       var i, len, ref1, results;
682       ref1 = this.shapes.concat(this.backgroundShapes);
683       results = [];
684       for (i = 0, len = ref1.length; i < len; i++) {
685         s = ref1[i];
686         results.push(s.getBoundingRect(this.ctx));
687       }
688       return results;
689     }).call(this), explicitSize, margin);
690   };
692   LiterallyCanvas.prototype.getImage = function(opts) {
693     if (opts == null) {
694       opts = {};
695     }
696     if (opts.includeWatermark == null) {
697       opts.includeWatermark = true;
698     }
699     if (opts.scaleDownRetina == null) {
700       opts.scaleDownRetina = true;
701     }
702     if (opts.scale == null) {
703       opts.scale = 1;
704     }
705     if (!opts.scaleDownRetina) {
706       opts.scale *= this.backingScale;
707     }
708     if (opts.includeWatermark) {
709       opts.watermarkImage = this.watermarkImage;
710       opts.watermarkScale = this.watermarkScale;
711       if (!opts.scaleDownRetina) {
712         opts.watermarkScale *= this.backingScale;
713       }
714     }
715     return renderSnapshotToImage(this.getSnapshot(), opts);
716   };
718   LiterallyCanvas.prototype.canvasForExport = function() {
719     this.repaintAllLayers();
720     return util.combineCanvases(this.backgroundCanvas, this.canvas);
721   };
723   LiterallyCanvas.prototype.canvasWithBackground = function(backgroundImageOrCanvas) {
724     return util.combineCanvases(backgroundImageOrCanvas, this.canvasForExport());
725   };
727   LiterallyCanvas.prototype.getSnapshot = function(keys) {
728     var i, k, len, ref1, shape, snapshot;
729     if (keys == null) {
730       keys = null;
731     }
732     if (keys == null) {
733       keys = ['shapes', 'imageSize', 'colors', 'position', 'scale', 'backgroundShapes'];
734     }
735     snapshot = {};
736     ref1 = ['colors', 'position', 'scale'];
737     for (i = 0, len = ref1.length; i < len; i++) {
738       k = ref1[i];
739       if (indexOf.call(keys, k) >= 0) {
740         snapshot[k] = this[k];
741       }
742     }
743     if (indexOf.call(keys, 'shapes') >= 0) {
744       snapshot.shapes = (function() {
745         var j, len1, ref2, results;
746         ref2 = this.shapes;
747         results = [];
748         for (j = 0, len1 = ref2.length; j < len1; j++) {
749           shape = ref2[j];
750           results.push(shapeToJSON(shape));
751         }
752         return results;
753       }).call(this);
754     }
755     if (indexOf.call(keys, 'backgroundShapes') >= 0) {
756       snapshot.backgroundShapes = (function() {
757         var j, len1, ref2, results;
758         ref2 = this.backgroundShapes;
759         results = [];
760         for (j = 0, len1 = ref2.length; j < len1; j++) {
761           shape = ref2[j];
762           results.push(shapeToJSON(shape));
763         }
764         return results;
765       }).call(this);
766     }
767     if (indexOf.call(keys, 'imageSize') >= 0) {
768       snapshot.imageSize = {
769         width: this.width,
770         height: this.height
771       };
772     }
773     return snapshot;
774   };
776   LiterallyCanvas.prototype.getSnapshotJSON = function() {
777     console.warn("lc.getSnapshotJSON() is deprecated. use JSON.stringify(lc.getSnapshot()) instead.");
778     return JSON.stringify(this.getSnapshot());
779   };
781   LiterallyCanvas.prototype.getSVGString = function(opts) {
782     if (opts == null) {
783       opts = {};
784     }
785     return renderSnapshotToSVG(this.getSnapshot(), opts);
786   };
788   LiterallyCanvas.prototype.loadSnapshot = function(snapshot) {
789     var i, j, k, len, len1, ref1, ref2, s, shape, shapeRepr;
790     if (!snapshot) {
791       return;
792     }
793     if (snapshot.colors) {
794       ref1 = ['primary', 'secondary', 'background'];
795       for (i = 0, len = ref1.length; i < len; i++) {
796         k = ref1[i];
797         this.setColor(k, snapshot.colors[k]);
798       }
799     }
800     if (snapshot.shapes) {
801       this.shapes = [];
802       ref2 = snapshot.shapes;
803       for (j = 0, len1 = ref2.length; j < len1; j++) {
804         shapeRepr = ref2[j];
805         shape = JSONToShape(shapeRepr);
806         if (shape) {
807           this.execute(new actions.AddShapeAction(this, shape));
808         }
809       }
810     }
811     if (snapshot.backgroundShapes) {
812       this.backgroundShapes = (function() {
813         var l, len2, ref3, results;
814         ref3 = snapshot.backgroundShapes;
815         results = [];
816         for (l = 0, len2 = ref3.length; l < len2; l++) {
817           s = ref3[l];
818           results.push(JSONToShape(s));
819         }
820         return results;
821       })();
822     }
823     if (snapshot.imageSize) {
824       this.width = snapshot.imageSize.width;
825       this.height = snapshot.imageSize.height;
826     }
827     if (snapshot.position) {
828       this.position = snapshot.position;
829     }
830     if (snapshot.scale) {
831       this.scale = snapshot.scale;
832     }
833     this.repaintAllLayers();
834     this.trigger('snapshotLoad');
835     return this.trigger('drawingChange', {});
836   };
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));
841   };
843   return LiterallyCanvas;
845 })();
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(' ');
856   fontSize = 0;
857   for (j = 0, len = fontItems.length; j < len; j++) {
858     item = fontItems[j];
859     maybeSize = parseInt(item.replace("px", ""), 10);
860     if (!isNaN(maybeSize)) {
861       fontSize = maybeSize;
862     }
863   }
864   if (!fontSize) {
865     throw "Font size not found";
866   }
867   remainingFontString = font.substring(fontItems[0].length + 1).replace('bold ', '').replace('italic ', '').replace('underline ', '');
868   fontFamily = remainingFontString;
869   return {
870     fontSize: fontSize,
871     fontFamily: fontFamily
872   };
875 getNextLine = function(ctx, text, forcedWidth) {
876   var doesSubstringFit, endIndex, isEndOfString, isNonWord, isWhitespace, lastGoodIndex, lastOkayIndex, nextWordStartIndex, textToHere, wasInWord;
877   if (!text.length) {
878     return ['', ''];
879   }
880   endIndex = 0;
881   lastGoodIndex = 0;
882   lastOkayIndex = 0;
883   wasInWord = false;
884   while (true) {
885     endIndex += 1;
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;
893     }
894     if (isNonWord && wasInWord) {
895       wasInWord = false;
896       if (doesSubstringFit) {
897         lastGoodIndex = endIndex;
898       }
899     }
900     wasInWord = !isWhitespace;
901     if (isEndOfString || !doesSubstringFit) {
902       if (doesSubstringFit) {
903         return [text, ''];
904       } else if (lastGoodIndex > 0) {
905         nextWordStartIndex = lastGoodIndex + 1;
906         while (nextWordStartIndex < text.length && text[nextWordStartIndex].match('/\s/')) {
907           nextWordStartIndex += 1;
908         }
909         return [text.substring(0, lastGoodIndex), text.substring(nextWordStartIndex)];
910       } else {
911         return [text.substring(0, lastOkayIndex), text.substring(lastOkayIndex)];
912       }
913     }
914   }
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);
920   lines = [];
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];
924     if (nextLine) {
925       while (nextLine) {
926         lines.push(nextLine);
927         ref1 = getNextLine(ctx, remainingText, forcedWidth), nextLine = ref1[0], remainingText = ref1[1];
928       }
929     } else {
930       lines.push(textLine);
931     }
932   }
933   return lines;
936 TextRenderer = (function() {
937   function TextRenderer(ctx, text1, font1, forcedWidth1, forcedHeight) {
938     var fontFamily, fontSize, ref;
939     this.text = text1;
940     this.font = font1;
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);
952       };
953     })(this));
954     this.metrics = {
955       ascent: Math.max.apply(Math, this.metricses.map(function(arg) {
956         var ascent;
957         ascent = arg.ascent;
958         return ascent;
959       })),
960       descent: Math.max.apply(Math, this.metricses.map(function(arg) {
961         var descent;
962         descent = arg.descent;
963         return descent;
964       })),
965       fontsize: Math.max.apply(Math, this.metricses.map(function(arg) {
966         var fontsize;
967         fontsize = arg.fontsize;
968         return fontsize;
969       })),
970       leading: Math.max.apply(Math, this.metricses.map(function(arg) {
971         var leading;
972         leading = arg.leading;
973         return leading;
974       })),
975       width: Math.max.apply(Math, this.metricses.map(function(arg) {
976         var width;
977         width = arg.width;
978         return width;
979       })),
980       height: Math.max.apply(Math, this.metricses.map(function(arg) {
981         var height;
982         height = arg.height;
983         return height;
984       })),
985       bounds: {
986         minx: Math.min.apply(Math, this.metricses.map(function(arg) {
987           var bounds;
988           bounds = arg.bounds;
989           return bounds.minx;
990         })),
991         miny: Math.min.apply(Math, this.metricses.map(function(arg) {
992           var bounds;
993           bounds = arg.bounds;
994           return bounds.miny;
995         })),
996         maxx: Math.max.apply(Math, this.metricses.map(function(arg) {
997           var bounds;
998           bounds = arg.bounds;
999           return bounds.maxx;
1000         })),
1001         maxy: Math.max.apply(Math, this.metricses.map(function(arg) {
1002           var bounds;
1003           bounds = arg.bounds;
1004           return bounds.maxy;
1005         }))
1006       }
1007     };
1008     this.boundingBoxWidth = Math.ceil(this.metrics.width);
1009   }
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;
1015     i = 0;
1016     ref = this.lines;
1017     results = [];
1018     for (j = 0, len = ref.length; j < len; j++) {
1019       line = ref[j];
1020       ctx.fillText(line, x, y + i * this.metrics.leading);
1021       results.push(i += 1);
1022     }
1023     return results;
1024   };
1026   TextRenderer.prototype.getWidth = function(isEditing) {
1027     if (isEditing == null) {
1028       isEditing = false;
1029     }
1030     if (this.forcedWidth) {
1031       return this.forcedWidth;
1032     } else {
1033       if (isEditing) {
1034         return this.metrics.bounds.maxx + this.caratWidth;
1035       } else {
1036         return this.metrics.bounds.maxx;
1037       }
1038     }
1039   };
1041   TextRenderer.prototype.getHeight = function() {
1042     return this.forcedHeight || (this.metrics.leading * this.lines.length);
1043   };
1045   return TextRenderer;
1047 })();
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) {
1057     this.lc = lc1;
1058     this.oldShapes = oldShapes;
1059     this.newShapes = newShapes1;
1060   }
1062   ClearAction.prototype["do"] = function() {
1063     this.lc.shapes = this.newShapes;
1064     return this.lc.repaintLayer('main');
1065   };
1067   ClearAction.prototype.undo = function() {
1068     this.lc.shapes = this.oldShapes;
1069     return this.lc.repaintLayer('main');
1070   };
1072   return ClearAction;
1074 })();
1076 AddShapeAction = (function() {
1077   function AddShapeAction(lc1, shape1, previousShapeId) {
1078     this.lc = lc1;
1079     this.shape = shape1;
1080     this.previousShapeId = previousShapeId != null ? previousShapeId : null;
1081   }
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);
1087     } else {
1088       newShapes = [];
1089       found = false;
1090       ref = this.lc.shapes;
1091       for (i = 0, len = ref.length; i < len; i++) {
1092         shape = ref[i];
1093         newShapes.push(shape);
1094         if (shape.id === this.previousShapeId) {
1095           newShapes.push(this.shape);
1096           found = true;
1097         }
1098       }
1099       if (!found) {
1100         newShapes.push(this.shape);
1101       }
1102       this.lc.shapes = newShapes;
1103     }
1104     return this.lc.repaintLayer('main');
1105   };
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();
1111     } else {
1112       newShapes = [];
1113       ref = this.lc.shapes;
1114       for (i = 0, len = ref.length; i < len; i++) {
1115         shape = ref[i];
1116         if (shape.id !== this.shape.id) {
1117           newShapes.push(shape);
1118         }
1119       }
1120       lc.shapes = newShapes;
1121     }
1122     return this.lc.repaintLayer('main');
1123   };
1125   return AddShapeAction;
1127 })();
1129 module.exports = {
1130   ClearAction: ClearAction,
1131   AddShapeAction: AddShapeAction
1135 },{}],4:[function(require,module,exports){
1136 var bindEvents, buttonIsDown, coordsForTouchEvent, position;
1138 coordsForTouchEvent = function(el, e) {
1139   var p, tx, ty;
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) {
1147   var p;
1148   p = el.getBoundingClientRect();
1149   return {
1150     left: e.clientX - p.left,
1151     top: e.clientY - p.top
1152   };
1155 buttonIsDown = function(e) {
1156   if (e.buttons != null) {
1157     return e.buttons === 1;
1158   } else {
1159     return e.which > 0;
1160   }
1163 module.exports = bindEvents = function(lc, canvas, panWithKeyboard) {
1164   var listener, mouseMoveListener, mouseUpListener, touchEndListener, touchMoveListener, unsubs;
1165   if (panWithKeyboard == null) {
1166     panWithKeyboard = false;
1167   }
1168   unsubs = [];
1169   mouseMoveListener = (function(_this) {
1170     return function(e) {
1171       var p;
1172       e.preventDefault();
1173       p = position(canvas, e);
1174       return lc.pointerMove(p.left, p.top);
1175     };
1176   })(this);
1177   mouseUpListener = (function(_this) {
1178     return function(e) {
1179       var p;
1180       e.preventDefault();
1181       canvas.onselectstart = function() {
1182         return true;
1183       };
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);
1189     };
1190   })(this);
1191   canvas.addEventListener('mousedown', (function(_this) {
1192     return function(e) {
1193       var down, p;
1194       if (e.target.tagName.toLowerCase() !== 'canvas') {
1195         return;
1196       }
1197       down = true;
1198       e.preventDefault();
1199       canvas.onselectstart = function() {
1200         return false;
1201       };
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);
1207     };
1208   })(this));
1209   touchMoveListener = function(e) {
1210     e.preventDefault();
1211     return lc.pointerMove.apply(lc, coordsForTouchEvent(canvas, e));
1212   };
1213   touchEndListener = function(e) {
1214     e.preventDefault();
1215     lc.pointerUp.apply(lc, coordsForTouchEvent(canvas, e));
1216     document.removeEventListener('touchmove', touchMoveListener);
1217     document.removeEventListener('touchend', touchEndListener);
1218     return document.removeEventListener('touchcancel', touchEndListener);
1219   };
1220   canvas.addEventListener('touchstart', function(e) {
1221     if (e.target.tagName.toLowerCase() !== 'canvas') {
1222       return;
1223     }
1224     e.preventDefault();
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);
1230     } else {
1231       return lc.pointerMove.apply(lc, coordsForTouchEvent(canvas, e));
1232     }
1233   });
1234   if (panWithKeyboard) {
1235     console.warn("Keyboard panning is deprecated.");
1236     listener = function(e) {
1237       switch (e.keyCode) {
1238         case 37:
1239           lc.pan(-10, 0);
1240           break;
1241         case 38:
1242           lc.pan(0, -10);
1243           break;
1244         case 39:
1245           lc.pan(10, 0);
1246           break;
1247         case 40:
1248           lc.pan(0, 10);
1249       }
1250       return lc.repaintAllLayers();
1251     };
1252     document.addEventListener('keydown', listener);
1253     unsubs.push(function() {
1254       return document.removeEventListener(listener);
1255     });
1256   }
1257   return function() {
1258     var f, i, len, results;
1259     results = [];
1260     for (i = 0, len = unsubs.length; i < len; i++) {
1261       f = unsubs[i];
1262       results.push(f());
1263     }
1264     return results;
1265   };
1269 },{}],5:[function(require,module,exports){
1270 var _drawRawLinePath, defineCanvasRenderer, drawErasedLinePath, drawErasedLinePathLatest, drawLinePath, drawLinePathLatest, lineEndCapShapes, noop, renderShapeToCanvas, renderShapeToContext, renderers;
1272 lineEndCapShapes = require('./lineEndCapShapes');
1274 renderers = {};
1276 defineCanvasRenderer = function(shapeName, drawFunc, drawLatestFunc) {
1277   return renderers[shapeName] = {
1278     drawFunc: drawFunc,
1279     drawLatestFunc: drawLatestFunc
1280   };
1283 noop = function() {};
1285 renderShapeToContext = function(ctx, shape, opts) {
1286   var bufferCtx;
1287   if (opts == null) {
1288     opts = {};
1289   }
1290   if (opts.shouldIgnoreUnsupportedShapes == null) {
1291     opts.shouldIgnoreUnsupportedShapes = false;
1292   }
1293   if (opts.retryCallback == null) {
1294     opts.retryCallback = noop;
1295   }
1296   if (opts.shouldOnlyDrawLatest == null) {
1297     opts.shouldOnlyDrawLatest = false;
1298   }
1299   if (opts.bufferCtx == null) {
1300     opts.bufferCtx = null;
1301   }
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);
1306     } else {
1307       return renderers[shape.className].drawFunc(ctx, shape, opts.retryCallback);
1308     }
1309   } else if (opts.shouldIgnoreUnsupportedShapes) {
1310     return console.warn("Can't render shape of type " + shape.className + " to canvas");
1311   } else {
1312     throw "Can't render shape of type " + shape.className + " to canvas";
1313   }
1316 renderShapeToCanvas = function(canvas, shape, opts) {
1317   return renderShapeToContext(canvas.getContext('2d'), shape, opts);
1320 defineCanvasRenderer('Rectangle', function(ctx, shape) {
1321   var x, y;
1322   x = shape.x;
1323   y = shape.y;
1324   if (shape.strokeWidth % 2 !== 0) {
1325     x += 0.5;
1326     y += 0.5;
1327   }
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;
1337   ctx.save();
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));
1344   ctx.beginPath();
1345   ctx.arc(0, 0, Math.abs(halfWidth), 0, Math.PI * 2);
1346   ctx.closePath();
1347   ctx.restore();
1348   ctx.fillStyle = shape.fillColor;
1349   ctx.fill();
1350   ctx.lineWidth = shape.strokeWidth;
1351   ctx.strokeStyle = shape.strokeColor;
1352   return ctx.stroke();
1355 defineCanvasRenderer('SelectionBox', (function() {
1356   var _drawHandle;
1357   _drawHandle = function(ctx, arg, handleSize) {
1358     var x, y;
1359     x = arg.x, y = arg.y;
1360     if (handleSize === 0) {
1361       return;
1362     }
1363     ctx.fillStyle = '#fff';
1364     ctx.fillRect(x, y, handleSize, handleSize);
1365     ctx.strokeStyle = '#000';
1366     return ctx.strokeRect(x, y, handleSize, handleSize);
1367   };
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);
1376     }
1377     ctx.lineWidth = 1;
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([]);
1382   };
1383 })());
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);
1389     } else {
1390       return ctx.drawImage(shape.image, shape.x, shape.y, shape.image.width * shape.scale, shape.image.height * shape.scale);
1391     }
1392   } else if (retryCallback) {
1393     return shape.image.onload = retryCallback;
1394   }
1397 defineCanvasRenderer('Line', function(ctx, shape) {
1398   var arrowWidth, x1, x2, y1, y2;
1399   if (shape.x1 === shape.x2 && shape.y1 === shape.y2) {
1400     return;
1401   }
1402   x1 = shape.x1;
1403   x2 = shape.x2;
1404   y1 = shape.y1;
1405   y2 = shape.y2;
1406   if (shape.strokeWidth % 2 !== 0) {
1407     x1 += 0.5;
1408     x2 += 0.5;
1409     y1 += 0.5;
1410     y2 += 0.5;
1411   }
1412   ctx.lineWidth = shape.strokeWidth;
1413   ctx.strokeStyle = shape.color;
1414   ctx.lineCap = shape.capStyle;
1415   if (shape.dash) {
1416     ctx.setLineDash(shape.dash);
1417   }
1418   ctx.beginPath();
1419   ctx.moveTo(x1, y1);
1420   ctx.lineTo(x2, y2);
1421   ctx.stroke();
1422   if (shape.dash) {
1423     ctx.setLineDash([]);
1424   }
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);
1428   }
1429   if (shape.endCapShapes[1]) {
1430     return lineEndCapShapes[shape.endCapShapes[1]].drawToCanvas(ctx, x2, y2, Math.atan2(y2 - y1, x2 - x1), arrowWidth, shape.color);
1431   }
1434 _drawRawLinePath = function(ctx, points, close, lineCap) {
1435   var i, len, point, ref;
1436   if (close == null) {
1437     close = false;
1438   }
1439   if (lineCap == null) {
1440     lineCap = 'round';
1441   }
1442   if (!points.length) {
1443     return;
1444   }
1445   ctx.lineCap = lineCap;
1446   ctx.strokeStyle = points[0].color;
1447   ctx.lineWidth = points[0].size;
1448   ctx.beginPath();
1449   if (points[0].size % 2 === 0) {
1450     ctx.moveTo(points[0].x, points[0].y);
1451   } else {
1452     ctx.moveTo(points[0].x + 0.5, points[0].y + 0.5);
1453   }
1454   ref = points.slice(1);
1455   for (i = 0, len = ref.length; i < len; i++) {
1456     point = ref[i];
1457     if (points[0].size % 2 === 0) {
1458       ctx.lineTo(point.x, point.y);
1459     } else {
1460       ctx.lineTo(point.x + 0.5, point.y + 0.5);
1461     }
1462   }
1463   if (close) {
1464     return ctx.closePath();
1465   }
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;
1475   if (shape.tail) {
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();
1481   } else {
1482     _drawRawLinePath(bufferCtx, shape.smoothedPoints);
1483     return bufferCtx.stroke();
1484   }
1487 defineCanvasRenderer('LinePath', drawLinePath, drawLinePathLatest);
1489 drawErasedLinePath = function(ctx, shape) {
1490   ctx.save();
1491   ctx.globalCompositeOperation = "destination-out";
1492   drawLinePath(ctx, shape);
1493   return ctx.restore();
1496 drawErasedLinePathLatest = function(ctx, bufferCtx, shape) {
1497   ctx.save();
1498   ctx.globalCompositeOperation = "destination-out";
1499   bufferCtx.save();
1500   bufferCtx.globalCompositeOperation = "destination-out";
1501   drawLinePathLatest(ctx, bufferCtx, shape);
1502   ctx.restore();
1503   return bufferCtx.restore();
1506 defineCanvasRenderer('ErasedLinePath', drawErasedLinePath, drawErasedLinePathLatest);
1508 defineCanvasRenderer('Text', function(ctx, shape) {
1509   if (!shape.renderer) {
1510     shape._makeRenderer(ctx);
1511   }
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');
1519   ctx.fill();
1520   return ctx.stroke();
1523 module.exports = {
1524   defineCanvasRenderer: defineCanvasRenderer,
1525   renderShapeToCanvas: renderShapeToCanvas,
1526   renderShapeToContext: renderShapeToContext
1530 },{"./lineEndCapShapes":8}],6:[function(require,module,exports){
1531 'use strict';
1533 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,
1545   watermarkScale: 1,
1546   zoomMin: 0.2,
1547   zoomMax: 4.0,
1548   zoomStep: 0.2,
1549   snapshot: 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){
1554 "use strict";
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 ** -----------------------------------------------------------------------------
1564   CHANGELOG:
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
1591   THE SOFTWARE.
1593 (function () {
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.";
1600   }
1602   // store the old text metrics function on the Canvas2D prototype
1603   CanvasRenderingContext2D.prototype.measureTextWidth = CanvasRenderingContext2D.prototype.measureText;
1605   /**
1606    *  shortcut function for getting computed CSS values
1607    */
1608   var getCSSValue = function getCSSValue(element, property) {
1609     return document.defaultView.getComputedStyle(element, null).getPropertyValue(property);
1610   };
1612   // debug function
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)';
1617     ctx.beginPath();
1618     ctx.moveTo(xstart, 0);
1619     ctx.lineTo(xstart, h);
1620     ctx.closePath();
1621     ctx.stroke();
1623     ctx.beginPath();
1624     ctx.moveTo(xstart + metrics.bounds.maxx, 0);
1625     ctx.lineTo(xstart + metrics.bounds.maxx, h);
1626     ctx.closePath();
1627     ctx.stroke();
1629     ctx.beginPath();
1630     ctx.moveTo(0, h / 2 - metrics.ascent);
1631     ctx.lineTo(w, h / 2 - metrics.ascent);
1632     ctx.closePath();
1633     ctx.stroke();
1635     ctx.beginPath();
1636     ctx.moveTo(0, h / 2 + metrics.descent);
1637     ctx.lineTo(w, h / 2 + metrics.descent);
1638     ctx.closePath();
1639     ctx.stroke();
1640   };
1642   /**
1643    * The new text metrics function
1644    */
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;
1666     }
1667     document.body.removeChild(leadDiv);
1669     // if we're not dealing with white space, we can compute metrics
1670     if (!isSpace) {
1671       // Have characters, so measure the text
1672       var canvas = document.createElement("canvas");
1673       var padding = 100;
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,
1682           h = canvas.height,
1683           baseline = h / 2;
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.
1695       var i = 0,
1696           w4 = w * 4,
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
1704       i = len - 1;
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;) {
1710         i += w4;
1711         if (i >= len) {
1712           i = i - len + 4;
1713         }
1714       }
1715       var minx = i % w4 / 4 | 0;
1717       // find the max-x coordinate
1718       var step = 1;
1719       for (i = len - 3; i >= 0 && pixelData[i] === 255;) {
1720         i -= w4;
1721         if (i < 0) {
1722           i = len - 3 - step++ * 4;
1723         }
1724       }
1725       var maxx = i % w4 / 4 + 1 | 0;
1727       // set font metrics
1728       metrics.ascent = baseline - ascent;
1729       metrics.descent = descent - baseline;
1730       metrics.bounds = { minx: minx - padding / 2,
1731         maxx: maxx - padding / 2,
1732         miny: 0,
1733         maxy: descent - ascent };
1734       metrics.height = 1 + (descent - ascent);
1735     }
1737     // if we ARE dealing with whitespace, most values will just be zero.
1738     else {
1739         // Only whitespace, so we can't measure the text
1740         metrics.ascent = 0;
1741         metrics.descent = 0;
1742         metrics.bounds = { minx: 0,
1743           maxx: metrics.width, // Best guess
1744           miny: 0,
1745           maxy: 0 };
1746         metrics.height = 0;
1747       }
1748     return metrics;
1749   };
1750 })();
1752 },{}],8:[function(require,module,exports){
1753 module.exports = {
1754   arrow: (function() {
1755     var getPoints;
1756     getPoints = function(x, y, angle, width, length) {
1757       return [
1758         {
1759           x: x + Math.cos(angle + Math.PI / 2) * width / 2,
1760           y: y + Math.sin(angle + Math.PI / 2) * width / 2
1761         }, {
1762           x: x + Math.cos(angle) * length,
1763           y: y + Math.sin(angle) * length
1764         }, {
1765           x: x + Math.cos(angle - Math.PI / 2) * width / 2,
1766           y: y + Math.sin(angle - Math.PI / 2) * width / 2
1767         }
1768       ];
1769     };
1770     return {
1771       drawToCanvas: function(ctx, x, y, angle, width, color, length) {
1772         var points;
1773         if (length == null) {
1774           length = 0;
1775         }
1776         length = length || width;
1777         ctx.fillStyle = color;
1778         ctx.lineWidth = 0;
1779         ctx.strokeStyle = 'transparent';
1780         ctx.beginPath();
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);
1785         return ctx.fill();
1786       },
1787       svg: function(x, y, angle, width, color, length) {
1788         var points;
1789         if (length == null) {
1790           length = 0;
1791         }
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;
1796         })) + "' />";
1797       }
1798     };
1799   })()
1803 },{}],9:[function(require,module,exports){
1804 var _, localize, strings;
1806 strings = {};
1808 localize = function(localStrings) {
1809   return strings = localStrings;
1812 _ = function(string) {
1813   var translation;
1814   translation = strings[string];
1815   return translation || string;
1818 module.exports = {
1819   localize: localize,
1820   _: _
1824 },{}],10:[function(require,module,exports){
1825 var Point, _slope, math, normals, unit, util;
1827 Point = require('./shapes').Point;
1829 util = require('./util');
1831 math = {};
1833 math.toPoly = function(line) {
1834   var i, index, len, n, point, polyLeft, polyRight;
1835   polyLeft = [];
1836   polyRight = [];
1837   index = 0;
1838   for (i = 0, len = line.length; i < len; i++) {
1839     point = line[i];
1840     n = normals(point, _slope(line, index));
1841     polyLeft = polyLeft.concat([n[0]]);
1842     polyRight = [n[1]].concat(polyRight);
1843     index += 1;
1844   }
1845   return polyLeft.concat(polyRight);
1848 _slope = function(line, index) {
1849   var point;
1850   if (line.length < 3) {
1851     point = {
1852       x: 0,
1853       y: 0
1854     };
1855   }
1856   if (index === 0) {
1857     point = _slope(line, index + 1);
1858   } else if (index === line.length - 1) {
1859     point = _slope(line, index - 1);
1860   } else {
1861     point = math.diff(line[index - 1], line[index + 1]);
1862   }
1863   return point;
1866 math.diff = function(a, b) {
1867   return {
1868     x: b.x - a.x,
1869     y: b.y - a.y
1870   };
1873 unit = function(vector) {
1874   var length;
1875   length = math.len(vector);
1876   return {
1877     x: vector.x / length,
1878     y: vector.y / length
1879   };
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;
1886   return [
1887     {
1888       x: p.x - slope.y,
1889       y: p.y + slope.x,
1890       color: p.color
1891     }, {
1892       x: p.x + slope.y,
1893       y: p.y - slope.x,
1894       color: p.color
1895     }
1896   ];
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) {
1923   if (!image.width) {
1924     return;
1925   }
1926   ctx.save();
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;
1935   if (opts == null) {
1936     opts = {};
1937   }
1938   if (opts.scale == null) {
1939     opts.scale = 1;
1940   }
1941   shapes = (function() {
1942     var i, len, ref, results;
1943     ref = snapshot.shapes;
1944     results = [];
1945     for (i = 0, len = ref.length; i < len; i++) {
1946       s = ref[i];
1947       results.push(JSONToShape(s));
1948     }
1949     return results;
1950   })();
1951   backgroundShapes = [];
1952   if (snapshot.backgroundShapes) {
1953     backgroundShapes = (function() {
1954       var i, len, ref, results;
1955       ref = snapshot.backgroundShapes;
1956       results = [];
1957       for (i = 0, len = ref.length; i < len; i++) {
1958         s = ref[i];
1959         results.push(JSONToShape(s));
1960       }
1961       return results;
1962     })();
1963   }
1964   if (opts.margin == null) {
1965     opts.margin = {
1966       top: 0,
1967       right: 0,
1968       bottom: 0,
1969       left: 0
1970     };
1971   }
1972   imageSize = snapshot.imageSize || {
1973     width: INFINITE,
1974     height: INFINITE
1975   };
1976   colors = snapshot.colors || {
1977     background: 'transparent'
1978   };
1979   allShapes = shapes.concat(backgroundShapes);
1980   watermarkCanvas = document.createElement('canvas');
1981   watermarkCtx = watermarkCanvas.getContext('2d');
1982   if (opts.rect) {
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;
1987   } else {
1988     opts.rect = util.getDefaultImageRect((function() {
1989       var i, len, results;
1990       results = [];
1991       for (i = 0, len = allShapes.length; i < len; i++) {
1992         s = allShapes[i];
1993         results.push(s.getBoundingRect(watermarkCtx));
1994       }
1995       return results;
1996     })(), imageSize, opts.margin);
1997   }
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)) {
2003     return null;
2004   }
2005   if (opts.watermarkImage) {
2006     renderWatermark(watermarkCtx, opts.watermarkImage, opts.watermarkScale);
2007   }
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;
2023   if (opts == null) {
2024     opts = {};
2025   }
2026   shapes = (function() {
2027     var i, len, ref, results;
2028     ref = snapshot.shapes;
2029     results = [];
2030     for (i = 0, len = ref.length; i < len; i++) {
2031       s = ref[i];
2032       results.push(JSONToShape(s));
2033     }
2034     return results;
2035   })();
2036   backgroundShapes = [];
2037   if (snapshot.backgroundShapes) {
2038     backgroundShapes = (function() {
2039       var i, len, ref, results;
2040       ref = snapshot.backgroundShapes;
2041       results = [];
2042       for (i = 0, len = ref.length; i < len; i++) {
2043         s = ref[i];
2044         results.push(JSONToShape(s));
2045       }
2046       return results;
2047     })();
2048   }
2049   if (opts.margin == null) {
2050     opts.margin = {
2051       top: 0,
2052       right: 0,
2053       bottom: 0,
2054       left: 0
2055     };
2056   }
2057   imageSize = snapshot.imageSize || {
2058     width: INFINITE,
2059     height: INFINITE
2060   };
2061   colors = snapshot.colors || {
2062     background: 'transparent'
2063   };
2064   allShapes = shapes.concat(backgroundShapes);
2065   dummyCanvas = document.createElement('canvas');
2066   ctx = dummyCanvas.getContext('2d');
2067   if (opts.rect) {
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;
2072   } else {
2073     opts.rect = util.getDefaultImageRect((function() {
2074       var i, len, results;
2075       results = [];
2076       for (i = 0, len = allShapes.length; i < len; i++) {
2077         s = allShapes[i];
2078         results.push(s.getBoundingRect(ctx));
2079       }
2080       return results;
2081     })(), imageSize, opts.margin);
2082   }
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;
2100 shapes = {};
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);
2106     return this;
2107   };
2108   Shape.prototype.className = name;
2109   Shape.fromJSON = props.fromJSON;
2110   if (props.draw) {
2111     legacyDrawFunc = props.draw;
2112     legacyDrawLatestFunc = props.draw || function(ctx, bufferCtx, retryCallback) {
2113       return this.draw(ctx, bufferCtx, retryCallback);
2114     };
2115     drawFunc = function(ctx, shape, retryCallback) {
2116       return legacyDrawFunc.call(shape, ctx, retryCallback);
2117     };
2118     drawLatestFunc = function(ctx, bufferCtx, shape, retryCallback) {
2119       return legacyDrawLatestFunc.call(shape, ctx, bufferCtx, retryCallback);
2120     };
2121     delete props.draw;
2122     if (props.drawLatest) {
2123       delete props.drawLatest;
2124     }
2125     defineCanvasRenderer(name, drawFunc, drawLatestFunc);
2126   }
2127   if (props.toSVG) {
2128     legacySVGFunc = props.toSVG;
2129     svgFunc = function(shape) {
2130       return legacySVGFunc.call(shape);
2131     };
2132     delete props.toSVG;
2133     defineSVGRenderer(name, svgFunc);
2134   }
2135   Shape.prototype.draw = function(ctx, retryCallback) {
2136     return renderShapeToContext(ctx, this, {
2137       retryCallback: retryCallback
2138     });
2139   };
2140   Shape.prototype.drawLatest = function(ctx, bufferCtx, retryCallback) {
2141     return renderShapeToContext(ctx, this, {
2142       retryCallback: retryCallback,
2143       bufferCtx: bufferCtx,
2144       shouldOnlyDrawLatest: true
2145     });
2146   };
2147   Shape.prototype.toSVG = function() {
2148     return renderShapeToSVG(this);
2149   };
2150   for (k in props) {
2151     if (k !== 'fromJSON') {
2152       Shape.prototype[k] = props[k];
2153     }
2154   }
2155   shapes[name] = Shape;
2156   return Shape;
2159 createShape = function(name, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) {
2160   var s;
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();
2163   return s;
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);
2171     if (shape) {
2172       if (id) {
2173         shape.id = id;
2174       }
2175       return shape;
2176     } else {
2177       console.log('Unreadable shape:', className, data);
2178       return null;
2179     }
2180   } else {
2181     console.log("Unknown shape:", className, data);
2182     return null;
2183   }
2186 shapeToJSON = function(shape) {
2187   return {
2188     className: shape.className,
2189     data: shape.toJSON(),
2190     id: shape.id
2191   };
2194 bspline = function(points, order) {
2195   if (!order) {
2196     return points;
2197   }
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));
2204   refined = [];
2205   index = 0;
2206   for (q = 0, len = points.length; q < len; q++) {
2207     point = points[q];
2208     refined[index * 2] = point;
2209     if (points[index + 1]) {
2210       refined[index * 2 + 1] = _mid(point, points[index + 1]);
2211     }
2212     index += 1;
2213   }
2214   return refined;
2217 _dual = function(points) {
2218   var dualed, index, len, point, q;
2219   dualed = [];
2220   index = 0;
2221   for (q = 0, len = points.length; q < len; q++) {
2222     point = points[q];
2223     if (points[index + 1]) {
2224       dualed[index] = _mid(point, points[index + 1]);
2225     }
2226     index += 1;
2227   }
2228   return dualed;
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),
2236     color: a.color
2237   });
2240 defineShape('Image', {
2241   constructor: function(args) {
2242     if (args == null) {
2243       args = {};
2244     }
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;
2249   },
2250   getBoundingRect: function() {
2251     return {
2252       x: this.x,
2253       y: this.y,
2254       width: this.image.width * this.scale,
2255       height: this.image.height * this.scale
2256     };
2257   },
2258   toJSON: function() {
2259     return {
2260       x: this.x,
2261       y: this.y,
2262       imageSrc: this.image.src,
2263       imageObject: this.image,
2264       scale: this.scale
2265     };
2266   },
2267   fromJSON: function(data) {
2268     var img, ref2;
2269     img = null;
2270     if ((ref2 = data.imageObject) != null ? ref2.width : void 0) {
2271       img = data.imageObject;
2272     } else {
2273       img = new Image();
2274       img.src = data.imageSrc;
2275     }
2276     return createShape('Image', {
2277       x: data.x,
2278       y: data.y,
2279       image: img,
2280       scale: data.scale
2281     });
2282   },
2283   move: function(moveInfo) {
2284     if (moveInfo == null) {
2285       moveInfo = {};
2286     }
2287     this.x = this.x - moveInfo.xDiff;
2288     return this.y = this.y - moveInfo.yDiff;
2289   },
2290   setUpperLeft: function(upperLeft) {
2291     if (upperLeft == null) {
2292       upperLeft = {};
2293     }
2294     this.x = upperLeft.x;
2295     return this.y = upperLeft.y;
2296   }
2299 defineShape('Rectangle', {
2300   constructor: function(args) {
2301     if (args == null) {
2302       args = {};
2303     }
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';
2311   },
2312   getBoundingRect: function() {
2313     return {
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
2318     };
2319   },
2320   toJSON: function() {
2321     return {
2322       x: this.x,
2323       y: this.y,
2324       width: this.width,
2325       height: this.height,
2326       strokeWidth: this.strokeWidth,
2327       strokeColor: this.strokeColor,
2328       fillColor: this.fillColor
2329     };
2330   },
2331   fromJSON: function(data) {
2332     return createShape('Rectangle', data);
2333   },
2334   move: function(moveInfo) {
2335     if (moveInfo == null) {
2336       moveInfo = {};
2337     }
2338     this.x = this.x - moveInfo.xDiff;
2339     return this.y = this.y - moveInfo.yDiff;
2340   },
2341   setUpperLeft: function(upperLeft) {
2342     if (upperLeft == null) {
2343       upperLeft = {};
2344     }
2345     this.x = upperLeft.x;
2346     return this.y = upperLeft.y;
2347   }
2350 defineShape('Ellipse', {
2351   constructor: function(args) {
2352     if (args == null) {
2353       args = {};
2354     }
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';
2362   },
2363   getBoundingRect: function() {
2364     return {
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
2369     };
2370   },
2371   toJSON: function() {
2372     return {
2373       x: this.x,
2374       y: this.y,
2375       width: this.width,
2376       height: this.height,
2377       strokeWidth: this.strokeWidth,
2378       strokeColor: this.strokeColor,
2379       fillColor: this.fillColor
2380     };
2381   },
2382   fromJSON: function(data) {
2383     return createShape('Ellipse', data);
2384   },
2385   move: function(moveInfo) {
2386     if (moveInfo == null) {
2387       moveInfo = {};
2388     }
2389     this.x = this.x - moveInfo.xDiff;
2390     return this.y = this.y - moveInfo.yDiff;
2391   },
2392   setUpperLeft: function(upperLeft) {
2393     if (upperLeft == null) {
2394       upperLeft = {};
2395     }
2396     this.x = upperLeft.x;
2397     return this.y = upperLeft.y;
2398   }
2401 defineShape('Line', {
2402   constructor: function(args) {
2403     if (args == null) {
2404       args = {};
2405     }
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;
2415   },
2416   getBoundingRect: function() {
2417     return {
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
2422     };
2423   },
2424   toJSON: function() {
2425     return {
2426       x1: this.x1,
2427       y1: this.y1,
2428       x2: this.x2,
2429       y2: this.y2,
2430       strokeWidth: this.strokeWidth,
2431       color: this.color,
2432       capStyle: this.capStyle,
2433       dash: this.dash,
2434       endCapShapes: this.endCapShapes
2435     };
2436   },
2437   fromJSON: function(data) {
2438     return createShape('Line', data);
2439   },
2440   move: function(moveInfo) {
2441     if (moveInfo == null) {
2442       moveInfo = {};
2443     }
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;
2448   },
2449   setUpperLeft: function(upperLeft) {
2450     var br, xDiff, yDiff;
2451     if (upperLeft == null) {
2452       upperLeft = {};
2453     }
2454     br = this.getBoundingRect();
2455     xDiff = br.x - upperLeft.x;
2456     yDiff = br.y - upperLeft.y;
2457     return this.move({
2458       xDiff: xDiff,
2459       yDiff: yDiff
2460     });
2461   }
2464 _doAllPointsShareStyle = function(points) {
2465   var color, len, point, q, size;
2466   if (!points.length) {
2467     return false;
2468   }
2469   size = points[0].size;
2470   color = points[0].color;
2471   for (q = 0, len = points.length; q < len; q++) {
2472     point = points[q];
2473     if (!(point.size === size && point.color === color)) {
2474       console.log(size, color, point.size, point.color);
2475     }
2476     if (!(point.size === size && point.color === color)) {
2477       return false;
2478     }
2479   }
2480   return true;
2483 _createLinePathFromData = function(shapeName, data) {
2484   var pointData, points, smoothedPoints, x, y;
2485   points = null;
2486   if (data.points) {
2487     points = (function() {
2488       var len, q, ref2, results;
2489       ref2 = data.points;
2490       results = [];
2491       for (q = 0, len = ref2.length; q < len; q++) {
2492         pointData = ref2[q];
2493         results.push(JSONToShape(pointData));
2494       }
2495       return results;
2496     })();
2497   } else if (data.pointCoordinatePairs) {
2498     points = (function() {
2499       var len, q, ref2, ref3, results;
2500       ref2 = data.pointCoordinatePairs;
2501       results = [];
2502       for (q = 0, len = ref2.length; q < len; q++) {
2503         ref3 = ref2[q], x = ref3[0], y = ref3[1];
2504         results.push(JSONToShape({
2505           className: 'Point',
2506           data: {
2507             x: x,
2508             y: y,
2509             size: data.pointSize,
2510             color: data.pointColor,
2511             smooth: data.smooth
2512           }
2513         }));
2514       }
2515       return results;
2516     })();
2517   }
2518   smoothedPoints = null;
2519   if (data.smoothedPointCoordinatePairs) {
2520     smoothedPoints = (function() {
2521       var len, q, ref2, ref3, results;
2522       ref2 = data.smoothedPointCoordinatePairs;
2523       results = [];
2524       for (q = 0, len = ref2.length; q < len; q++) {
2525         ref3 = ref2[q], x = ref3[0], y = ref3[1];
2526         results.push(JSONToShape({
2527           className: 'Point',
2528           data: {
2529             x: x,
2530             y: y,
2531             size: data.pointSize,
2532             color: data.pointColor,
2533             smooth: data.smooth
2534           }
2535         }));
2536       }
2537       return results;
2538     })();
2539   }
2540   if (!points[0]) {
2541     return null;
2542   }
2543   return createShape(shapeName, {
2544     points: points,
2545     smoothedPoints: smoothedPoints,
2546     order: data.order,
2547     tailSize: data.tailSize,
2548     smooth: data.smooth
2549   });
2552 linePathFuncs = {
2553   constructor: function(args) {
2554     var len, point, points, q, results;
2555     if (args == null) {
2556       args = {};
2557     }
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;
2567     } else {
2568       this.points = [];
2569       results = [];
2570       for (q = 0, len = points.length; q < len; q++) {
2571         point = points[q];
2572         results.push(this.addPoint(point));
2573       }
2574       return results;
2575     }
2576   },
2577   getBoundingRect: function() {
2578     return util.getBoundingRect(this.points.map(function(p) {
2579       return {
2580         x: p.x - p.size / 2,
2581         y: p.y - p.size / 2,
2582         width: p.size,
2583         height: p.size
2584       };
2585     }));
2586   },
2587   toJSON: function() {
2588     var p, point;
2589     if (_doAllPointsShareStyle(this.points)) {
2590       return {
2591         order: this.order,
2592         tailSize: this.tailSize,
2593         smooth: this.smooth,
2594         pointCoordinatePairs: (function() {
2595           var len, q, ref2, results;
2596           ref2 = this.points;
2597           results = [];
2598           for (q = 0, len = ref2.length; q < len; q++) {
2599             point = ref2[q];
2600             results.push([point.x, point.y]);
2601           }
2602           return results;
2603         }).call(this),
2604         smoothedPointCoordinatePairs: (function() {
2605           var len, q, ref2, results;
2606           ref2 = this.smoothedPoints;
2607           results = [];
2608           for (q = 0, len = ref2.length; q < len; q++) {
2609             point = ref2[q];
2610             results.push([point.x, point.y]);
2611           }
2612           return results;
2613         }).call(this),
2614         pointSize: this.points[0].size,
2615         pointColor: this.points[0].color
2616       };
2617     } else {
2618       return {
2619         order: this.order,
2620         tailSize: this.tailSize,
2621         smooth: this.smooth,
2622         points: (function() {
2623           var len, q, ref2, results;
2624           ref2 = this.points;
2625           results = [];
2626           for (q = 0, len = ref2.length; q < len; q++) {
2627             p = ref2[q];
2628             results.push(shapeToJSON(p));
2629           }
2630           return results;
2631         }).call(this)
2632       };
2633     }
2634   },
2635   fromJSON: function(data) {
2636     return _createLinePathFromData('LinePath', data);
2637   },
2638   addPoint: function(point) {
2639     this.points.push(point);
2640     if (!this.smooth) {
2641       this.smoothedPoints = this.points;
2642       return;
2643     }
2644     if (!this.smoothedPoints || this.points.length < this.sampleSize) {
2645       return this.smoothedPoints = bspline(this.points, this.order);
2646     } else {
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);
2649     }
2650   },
2651   move: function(moveInfo) {
2652     var len, pt, pts, q;
2653     if (moveInfo == null) {
2654       moveInfo = {};
2655     }
2656     if (!this.smooth) {
2657       pts = this.points;
2658     } else {
2659       pts = this.smoothedPoints;
2660     }
2661     for (q = 0, len = pts.length; q < len; q++) {
2662       pt = pts[q];
2663       pt.move(moveInfo);
2664     }
2665     return this.points = this.smoothedPoints;
2666   },
2667   setUpperLeft: function(upperLeft) {
2668     var br, xDiff, yDiff;
2669     if (upperLeft == null) {
2670       upperLeft = {};
2671     }
2672     br = this.getBoundingRect();
2673     xDiff = br.x - upperLeft.x;
2674     yDiff = br.y - upperLeft.y;
2675     return this.move({
2676       xDiff: xDiff,
2677       yDiff: yDiff
2678     });
2679   }
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);
2691   }
2694 defineShape('Point', {
2695   constructor: function(args) {
2696     if (args == null) {
2697       args = {};
2698     }
2699     this.x = args.x || 0;
2700     this.y = args.y || 0;
2701     this.size = args.size || 0;
2702     return this.color = args.color || '';
2703   },
2704   getBoundingRect: function() {
2705     return {
2706       x: this.x - this.size / 2,
2707       y: this.y - this.size / 2,
2708       width: this.size,
2709       height: this.size
2710     };
2711   },
2712   toJSON: function() {
2713     return {
2714       x: this.x,
2715       y: this.y,
2716       size: this.size,
2717       color: this.color
2718     };
2719   },
2720   fromJSON: function(data) {
2721     return createShape('Point', data);
2722   },
2723   move: function(moveInfo) {
2724     if (moveInfo == null) {
2725       moveInfo = {};
2726     }
2727     this.x = this.x - moveInfo.xDiff;
2728     return this.y = this.y - moveInfo.yDiff;
2729   },
2730   setUpperLeft: function(upperLeft) {
2731     if (upperLeft == null) {
2732       upperLeft = {};
2733     }
2734     this.x = upperLeft.x;
2735     return this.y = upperLeft.y;
2736   }
2739 defineShape('Polygon', {
2740   constructor: function(args) {
2741     var len, point, q, ref2, results;
2742     if (args == null) {
2743       args = {};
2744     }
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;
2752     }
2753     this.isClosed = args.isClosed;
2754     ref2 = this.points;
2755     results = [];
2756     for (q = 0, len = ref2.length; q < len; q++) {
2757       point = ref2[q];
2758       point.color = this.strokeColor;
2759       results.push(point.size = this.strokeWidth);
2760     }
2761     return results;
2762   },
2763   addPoint: function(x, y) {
2764     return this.points.push(LC.createShape('Point', {
2765       x: x,
2766       y: y
2767     }));
2768   },
2769   getBoundingRect: function() {
2770     return util.getBoundingRect(this.points.map(function(p) {
2771       return p.getBoundingRect();
2772     }));
2773   },
2774   toJSON: function() {
2775     return {
2776       strokeWidth: this.strokeWidth,
2777       fillColor: this.fillColor,
2778       strokeColor: this.strokeColor,
2779       dash: this.dash,
2780       isClosed: this.isClosed,
2781       pointCoordinatePairs: this.points.map(function(p) {
2782         return [p.x, p.y];
2783       })
2784     };
2785   },
2786   fromJSON: function(data) {
2787     data.points = data.pointCoordinatePairs.map(function(arg) {
2788       var x, y;
2789       x = arg[0], y = arg[1];
2790       return createShape('Point', {
2791         x: x,
2792         y: y,
2793         size: data.strokeWidth,
2794         color: data.strokeColor
2795       });
2796     });
2797     return createShape('Polygon', data);
2798   },
2799   move: function(moveInfo) {
2800     var len, pt, q, ref2, results;
2801     if (moveInfo == null) {
2802       moveInfo = {};
2803     }
2804     ref2 = this.points;
2805     results = [];
2806     for (q = 0, len = ref2.length; q < len; q++) {
2807       pt = ref2[q];
2808       results.push(pt.move(moveInfo));
2809     }
2810     return results;
2811   },
2812   setUpperLeft: function(upperLeft) {
2813     var br, xDiff, yDiff;
2814     if (upperLeft == null) {
2815       upperLeft = {};
2816     }
2817     br = this.getBoundingRect();
2818     xDiff = br.x - upperLeft.x;
2819     yDiff = br.y - upperLeft.y;
2820     return this.move({
2821       xDiff: xDiff,
2822       yDiff: yDiff
2823     });
2824   }
2827 defineShape('Text', {
2828   constructor: function(args) {
2829     if (args == null) {
2830       args = {};
2831     }
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;
2840   },
2841   _makeRenderer: function(ctx) {
2842     ctx.lineHeight = 1.2;
2843     this.renderer = new TextRenderer(ctx, this.text, this.font, this.forcedWidth, this.forcedHeight);
2844     if (this.v < 1) {
2845       console.log('repairing baseline');
2846       this.v = 1;
2847       this.x -= this.renderer.metrics.bounds.minx;
2848       return this.y -= this.renderer.metrics.leading - this.renderer.metrics.descent;
2849     }
2850   },
2851   setText: function(text) {
2852     this.text = text;
2853     return this.renderer = null;
2854   },
2855   setFont: function(font) {
2856     this.font = font;
2857     return this.renderer = null;
2858   },
2859   setPosition: function(x, y) {
2860     this.x = x;
2861     return this.y = y;
2862   },
2863   setSize: function(forcedWidth, forcedHeight) {
2864     this.forcedWidth = Math.max(forcedWidth, 0);
2865     this.forcedHeight = Math.max(forcedHeight, 0);
2866     return this.renderer = null;
2867   },
2868   enforceMaxBoundingRect: function(lc) {
2869     var br, dx, lcBoundingRect;
2870     br = this.getBoundingRect(lc.ctx);
2871     lcBoundingRect = {
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
2876     };
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;
2881     }
2882   },
2883   getBoundingRect: function(ctx, isEditing) {
2884     if (isEditing == null) {
2885       isEditing = false;
2886     }
2887     if (!this.renderer) {
2888       if (ctx) {
2889         this._makeRenderer(ctx);
2890       } else {
2891         throw "Must pass ctx if text hasn't been rendered yet";
2892       }
2893     }
2894     return {
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())
2899     };
2900   },
2901   toJSON: function() {
2902     return {
2903       x: this.x,
2904       y: this.y,
2905       text: this.text,
2906       color: this.color,
2907       font: this.font,
2908       forcedWidth: this.forcedWidth,
2909       forcedHeight: this.forcedHeight,
2910       v: this.v
2911     };
2912   },
2913   fromJSON: function(data) {
2914     return createShape('Text', data);
2915   },
2916   move: function(moveInfo) {
2917     if (moveInfo == null) {
2918       moveInfo = {};
2919     }
2920     this.x = this.x - moveInfo.xDiff;
2921     return this.y = this.y - moveInfo.yDiff;
2922   },
2923   setUpperLeft: function(upperLeft) {
2924     if (upperLeft == null) {
2925       upperLeft = {};
2926     }
2927     this.x = upperLeft.x;
2928     return this.y = upperLeft.y;
2929   }
2932 defineShape('SelectionBox', {
2933   constructor: function(args) {
2934     if (args == null) {
2935       args = {};
2936     }
2937     this.shape = args.shape;
2938     if (args.handleSize != null) {
2939       this.handleSize = args.handleSize;
2940     } else {
2941       this.handleSize = 10;
2942     }
2943     this.margin = 4;
2944     this.backgroundColor = args.backgroundColor || null;
2945     return this._br = this.shape.getBoundingRect(args.ctx);
2946   },
2947   toJSON: function() {
2948     return {
2949       shape: shapeToJSON(this.shape),
2950       backgroundColor: this.backgroundColor
2951     };
2952   },
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
2959     });
2960   },
2961   getTopLeftHandleRect: function() {
2962     return {
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
2967     };
2968   },
2969   getBottomLeftHandleRect: function() {
2970     return {
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
2975     };
2976   },
2977   getTopRightHandleRect: function() {
2978     return {
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
2983     };
2984   },
2985   getBottomRightHandleRect: function() {
2986     return {
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
2991     };
2992   },
2993   getBoundingRect: function() {
2994     return {
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
2999     };
3000   }
3003 module.exports = {
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');
3016 renderers = {};
3018 defineSVGRenderer = function(shapeName, shapeToSVGFunc) {
3019   return renderers[shapeName] = shapeToSVGFunc;
3022 renderShapeToSVG = function(shape, opts) {
3023   if (opts == null) {
3024     opts = {};
3025   }
3026   if (opts.shouldIgnoreUnsupportedShapes == null) {
3027     opts.shouldIgnoreUnsupportedShapes = false;
3028   }
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");
3033     return "";
3034   } else {
3035     throw "Can't render shape of type " + shape.className + " to SVG";
3036   }
3039 defineSVGRenderer('Rectangle', function(shape) {
3040   var height, width, x, x1, x2, y, y1, y2;
3041   x1 = shape.x;
3042   y1 = shape.y;
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) {
3050     x += 0.5;
3051     y += 0.5;
3052   }
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) {
3057   return "";
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(', ')) + "'" : '';
3076   capString = '';
3077   arrowWidth = Math.max(shape.strokeWidth * 2.2, 5);
3078   x1 = shape.x1;
3079   x2 = shape.x2;
3080   y1 = shape.y1;
3081   y2 = shape.y2;
3082   if (shape.strokeWidth % 2 !== 0) {
3083     x1 += 0.5;
3084     x2 += 0.5;
3085     y1 += 0.5;
3086     y2 += 0.5;
3087   }
3088   if (shape.endCapShapes[0]) {
3089     capString += lineEndCapShapes[shape.endCapShapes[0]].svg(x1, y1, Math.atan2(y1 - y2, x1 - x2), arrowWidth, shape.color);
3090   }
3091   if (shape.endCapShapes[1]) {
3092     capString += lineEndCapShapes[shape.endCapShapes[1]].svg(x2, y2, Math.atan2(y2 - y1, x2 - x1), arrowWidth, shape.color);
3093   }
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) {
3099     var offset;
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) {
3106   return "";
3109 defineSVGRenderer('Polygon', function(shape) {
3110   if (shape.isClosed) {
3111     return "<polygon fill='" + shape.fillColor + "' points='" + (shape.points.map(function(p) {
3112       var offset;
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 + "' />";
3116   } else {
3117     return "<polyline fill='" + shape.fillColor + "' points='" + (shape.points.map(function(p) {
3118       var offset;
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) {
3122       var offset;
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 + "' />";
3126   }
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;
3136   }
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) {
3139       var dy;
3140       dy = i === 0 ? 0 : '1.2em';
3141       return "<tspan x='" + shape.x + "' dy='" + dy + "' alignment-baseline='text-before-edge'> " + line + " </tspan>";
3142     };
3143   })(this)).join('')) + " </text>";
3146 module.exports = {
3147   defineSVGRenderer: defineSVGRenderer,
3148   renderShapeToSVG: renderShapeToSVG
3152 },{"./lineEndCapShapes":8}],15:[function(require,module,exports){
3153 var renderShapeToContext, renderShapeToSVG, slice, util,
3154   slice1 = [].slice;
3156 slice = Array.prototype.slice;
3158 renderShapeToContext = require('./canvasRenderer').renderShapeToContext;
3160 renderShapeToSVG = require('./svgRenderer').renderShapeToSVG;
3162 util = {
3163   addImageOnload: function(img, fn) {
3164     var oldOnload;
3165     oldOnload = img.onload;
3166     img.onload = function() {
3167       if (typeof oldOnload === "function") {
3168         oldOnload();
3169       }
3170       return fn();
3171     };
3172     return img;
3173   },
3174   last: function(array, n) {
3175     if (n == null) {
3176       n = null;
3177     }
3178     if (n) {
3179       return slice.call(array, Math.max(array.length - n, 0));
3180     } else {
3181       return array[array.length - 1];
3182     }
3183   },
3184   classSet: function(classNameToIsPresent) {
3185     var classNames, key;
3186     classNames = [];
3187     for (key in classNameToIsPresent) {
3188       if (classNameToIsPresent[key]) {
3189         classNames.push(key);
3190       }
3191     }
3192     return classNames.join(' ');
3193   },
3194   matchElementSize: function(elementToMatch, elementsToResize, scale, callback) {
3195     var resize;
3196     if (callback == null) {
3197       callback = function() {};
3198     }
3199     resize = (function(_this) {
3200       return function() {
3201         var el, i, len;
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);
3209           }
3210         }
3211         return callback();
3212       };
3213     })(this);
3214     elementToMatch.addEventListener('resize', resize);
3215     window.addEventListener('resize', resize);
3216     window.addEventListener('orientationchange', resize);
3217     return resize();
3218   },
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);
3229     }
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);
3234     }
3235     return c;
3236   },
3237   renderShapes: function(shapes, bounds, scale, canvas) {
3238     var ctx, i, len, shape;
3239     if (scale == null) {
3240       scale = 1;
3241     }
3242     if (canvas == null) {
3243       canvas = null;
3244     }
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++) {
3252       shape = shapes[i];
3253       renderShapeToContext(ctx, shape);
3254     }
3255     return canvas;
3256   },
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, "");
3261   },
3262   getBoundingRect: function(rects, width, height) {
3263     var i, len, maxX, maxY, minX, minY, rect;
3264     if (!rects.length) {
3265       return {
3266         x: 0,
3267         y: 0,
3268         width: 0 || width,
3269         height: 0 || height
3270       };
3271     }
3272     minX = rects[0].x;
3273     minY = rects[0].y;
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++) {
3277       rect = rects[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));
3282     }
3283     minX = width ? 0 : minX;
3284     minY = height ? 0 : minY;
3285     maxX = width || maxX;
3286     maxY = height || maxY;
3287     return {
3288       x: minX,
3289       y: minY,
3290       width: maxX - minX,
3291       height: maxY - minY
3292     };
3293   },
3294   getDefaultImageRect: function(shapeBoundingRects, explicitSize, margin) {
3295     var height, rect, width;
3296     if (explicitSize == null) {
3297       explicitSize = {
3298         width: 0,
3299         height: 0
3300       };
3301     }
3302     if (margin == null) {
3303       margin = {
3304         top: 0,
3305         right: 0,
3306         bottom: 0,
3307         left: 0
3308       };
3309     }
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;
3316     return rect;
3317   },
3318   getBackingScale: function(context) {
3319     if (window.devicePixelRatio == null) {
3320       return 1;
3321     }
3322     if (!(window.devicePixelRatio > 1)) {
3323       return 1;
3324     }
3325     return window.devicePixelRatio;
3326   },
3327   requestAnimationFrame: (window.requestAnimationFrame || window.setTimeout).bind(window),
3328   getGUID: (function() {
3329     var s4;
3330     s4 = function() {
3331       return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
3332     };
3333     return function() {
3334       return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
3335     };
3336   })(),
3337   requestAnimationFrame: function(f) {
3338     if (window.webkitRequestAnimationFrame) {
3339       return window.webkitRequestAnimationFrame(f);
3340     }
3341     if (window.requestAnimationFrame) {
3342       return window.requestAnimationFrame(f);
3343     }
3344     if (window.mozRequestAnimationFrame) {
3345       return window.mozRequestAnimationFrame(f);
3346     }
3347     return setTimeout(f, 0);
3348   },
3349   cancelAnimationFrame: function(f) {
3350     if (window.webkitCancelRequestAnimationFrame) {
3351       return window.webkitCancelRequestAnimationFrame(f);
3352     }
3353     if (window.webkitCancelAnimationFrame) {
3354       return window.webkitCancelAnimationFrame(f);
3355     }
3356     if (window.cancelAnimationFrame) {
3357       return window.cancelAnimationFrame(f);
3358     }
3359     if (window.mozCancelAnimationFrame) {
3360       return window.mozCancelAnimationFrame(f);
3361     }
3362     return clearTimeout(f);
3363   }
3366 module.exports = util;
3369 },{"./canvasRenderer":5,"./svgRenderer":14}],16:[function(require,module,exports){
3370 'use strict';
3372 (function () {
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);
3377     return evt;
3378   };
3380   CustomEvent.prototype = window.CustomEvent.prototype;
3382   window.CustomEvent = CustomEvent;
3383 })();
3385 },{}],17:[function(require,module,exports){
3386 "use strict";
3388 var hasWarned = false;
3389 if (!CanvasRenderingContext2D.prototype.setLineDash) {
3390   CanvasRenderingContext2D.prototype.setLineDash = function () {
3391     // no-op
3392     if (!hasWarned) {
3393       console.warn("context2D.setLineDash is a no-op in this browser.");
3394       hasWarned = true;
3395     }
3396   };
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;
3425 conversion = {
3426   snapshotToShapes: function(snapshot) {
3427     var i, len, ref, results, shape;
3428     ref = snapshot.shapes;
3429     results = [];
3430     for (i = 0, len = ref.length; i < len; i++) {
3431       shape = ref[i];
3432       results.push(shapes.JSONToShape(shape));
3433     }
3434     return results;
3435   },
3436   snapshotJSONToShapes: function(json) {
3437     return conversion.snapshotToShapes(JSON.parse(json));
3438   }
3441 baseTools = require('./tools/base');
3443 tools = {
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;
3469   if (opts == null) {
3470     opts = {};
3471   }
3472   for (opt in defaultOptions) {
3473     if (!(opt in opts)) {
3474       opts[opt] = defaultOptions[opt];
3475     }
3476   }
3477   ref = el.children;
3478   for (i = 0, len = ref.length; i < len; i++) {
3479     child = ref[i];
3480     el.removeChild(child);
3481   }
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';
3490   }
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;
3498     lc._teardown();
3499     ref = el.children;
3500     for (i = 0, len = ref.length; i < len; i++) {
3501       child = ref[i];
3502       el.removeChild(child);
3503     }
3504     return el.className = originalClassName;
3505   };
3506   if ('onInit' in opts) {
3507     opts.onInit(lc);
3508   }
3509   return lc;
3512 registerJQueryPlugin = function(_$) {
3513   return _$.fn.literallycanvas = function(opts) {
3514     if (opts == null) {
3515       opts = {};
3516     }
3517     this.each((function(_this) {
3518       return function(ix, el) {
3519         return el.literallycanvas = init(el, opts);
3520       };
3521     })(this));
3522     return this;
3523   };
3526 if (typeof window !== 'undefined') {
3527   window.LC = {
3528     init: init
3529   };
3530   if (window.$) {
3531     registerJQueryPlugin(window.$);
3532   }
3535 module.exports = {
3536   init: init,
3537   registerJQueryPlugin: registerJQueryPlugin,
3538   util: util,
3539   tools: tools,
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,
3557   localize: localize
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);
3575   }
3577   Ellipse.prototype.name = 'Ellipse';
3579   Ellipse.prototype.iconName = 'ellipse';
3581   Ellipse.prototype.begin = function(x, y, lc) {
3582     return this.currentShape = createShape('Ellipse', {
3583       x: x,
3584       y: y,
3585       strokeWidth: this.strokeWidth,
3586       strokeColor: lc.getColor('primary'),
3587       fillColor: lc.getColor('secondary')
3588     });
3589   };
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);
3595   };
3597   Ellipse.prototype.end = function(x, y, lc) {
3598     return lc.saveShape(this.currentShape);
3599   };
3601   return Ellipse;
3603 })(ToolWithStroke);
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);
3618   function Eraser() {
3619     return Eraser.__super__.constructor.apply(this, arguments);
3620   }
3622   Eraser.prototype.name = 'Eraser';
3624   Eraser.prototype.iconName = 'eraser';
3626   Eraser.prototype.makePoint = function(x, y, lc) {
3627     return createShape('Point', {
3628       x: x,
3629       y: y,
3630       size: this.strokeWidth,
3631       color: '#000'
3632     });
3633   };
3635   Eraser.prototype.makeShape = function() {
3636     return createShape('ErasedLinePath');
3637   };
3639   return Eraser;
3641 })(Pencil);
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) {
3652   var pixel, x, y;
3653   x = arg.x, y = arg.y;
3654   pixel = ctx.getImageData(x, y, 1, 1).data;
3655   if (pixel[3]) {
3656     return "rgb(" + pixel[0] + ", " + pixel[1] + ", " + pixel[2] + ")";
3657   } else {
3658     return null;
3659   }
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';
3674   }
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'), {
3681       x: x - offset.x,
3682       y: y - offset.y
3683     });
3684     color = newColor || lc.getColor('background');
3685     if (this.strokeOrFill === 'stroke') {
3686       return lc.setColor('primary', newColor);
3687     } else {
3688       return lc.setColor('secondary', newColor);
3689     }
3690   };
3692   Eyedropper.prototype.begin = function(x, y, lc) {
3693     return this.readColor(x, y, lc);
3694   };
3696   Eyedropper.prototype["continue"] = function(x, y, lc) {
3697     return this.readColor(x, y, lc);
3698   };
3700   return Eyedropper;
3702 })(Tool);
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);
3717   function Line() {
3718     return Line.__super__.constructor.apply(this, arguments);
3719   }
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', {
3729       x1: x,
3730       y1: y,
3731       x2: x,
3732       y2: y,
3733       strokeWidth: this.strokeWidth,
3734       dash: (function() {
3735         switch (false) {
3736           case !this.isDashed:
3737             return [this.strokeWidth * 2, this.strokeWidth * 4];
3738           default:
3739             return null;
3740         }
3741       }).call(this),
3742       endCapShapes: this.hasEndArrow ? [null, 'arrow'] : null,
3743       color: lc.getColor('primary')
3744     });
3745   };
3747   Line.prototype["continue"] = function(x, y, lc) {
3748     this.currentShape.x2 = x;
3749     this.currentShape.y2 = y;
3750     return lc.drawShapeInProgress(this.currentShape);
3751   };
3753   Line.prototype.end = function(x, y, lc) {
3754     return lc.saveShape(this.currentShape);
3755   };
3757   return Line;
3759 })(ToolWithStroke);
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);
3774   function Pan() {
3775     return Pan.__super__.constructor.apply(this, arguments);
3776   }
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) {
3788       return function() {
3789         var func, i, len, results;
3790         results = [];
3791         for (i = 0, len = unsubscribeFuncs.length; i < len; i++) {
3792           func = unsubscribeFuncs[i];
3793           results.push(func());
3794         }
3795         return results;
3796       };
3797     })(this);
3798     unsubscribeFuncs.push(lc.on('lc-pointerdown', (function(_this) {
3799       return function(arg) {
3800         var rawX, rawY;
3801         rawX = arg.rawX, rawY = arg.rawY;
3802         _this.oldPosition = lc.position;
3803         return _this.pointerStart = {
3804           x: rawX,
3805           y: rawY
3806         };
3807       };
3808     })(this)));
3809     return unsubscribeFuncs.push(lc.on('lc-pointerdrag', (function(_this) {
3810       return function(arg) {
3811         var dp, rawX, rawY;
3812         rawX = arg.rawX, rawY = arg.rawY;
3813         dp = {
3814           x: (rawX - _this.pointerStart.x) * lc.backingScale,
3815           y: (rawY - _this.pointerStart.y) * lc.backingScale
3816         };
3817         return lc.setPan(_this.oldPosition.x + dp.x, _this.oldPosition.y + dp.y);
3818       };
3819     })(this)));
3820   };
3822   Pan.prototype.willBecomeInactive = function(lc) {
3823     return this.unsubscribe();
3824   };
3826   return Pan;
3828 })(Tool);
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);
3843   function Pencil() {
3844     return Pencil.__super__.constructor.apply(this, arguments);
3845   }
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();
3858   };
3860   Pencil.prototype["continue"] = function(x, y, lc) {
3861     var timeDiff;
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);
3867     }
3868   };
3870   Pencil.prototype.end = function(x, y, lc) {
3871     lc.saveShape(this.currentShape);
3872     return this.currentShape = void 0;
3873   };
3875   Pencil.prototype.makePoint = function(x, y, lc) {
3876     return createShape('Point', {
3877       x: x,
3878       y: y,
3879       size: this.strokeWidth,
3880       color: this.color
3881     });
3882   };
3884   Pencil.prototype.makeShape = function() {
3885     return createShape('LinePath');
3886   };
3888   return Pencil;
3890 })(ToolWithStroke);
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);
3907   }
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) {
3920       return function() {
3921         var func, i, len, results;
3922         results = [];
3923         for (i = 0, len = polygonUnsubscribeFuncs.length; i < len; i++) {
3924           func = polygonUnsubscribeFuncs[i];
3925           results.push(func());
3926         }
3927         return results;
3928       };
3929     })(this);
3930     this.points = null;
3931     this.maybePoint = null;
3932     onUp = (function(_this) {
3933       return function() {
3934         if (_this._getWillFinish()) {
3935           return _this._close(lc);
3936         }
3937         lc.trigger('lc-polygon-started');
3938         if (_this.points) {
3939           _this.points.push(_this.maybePoint);
3940         } else {
3941           _this.points = [_this.maybePoint];
3942         }
3943         _this.maybePoint = {
3944           x: _this.maybePoint.x,
3945           y: _this.maybePoint.y
3946         };
3947         lc.setShapesInProgress(_this._getShapes(lc));
3948         return lc.repaintLayer('main');
3949       };
3950     })(this);
3951     onMove = (function(_this) {
3952       return function(arg) {
3953         var x, y;
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');
3960         }
3961       };
3962     })(this);
3963     onDown = (function(_this) {
3964       return function(arg) {
3965         var x, y;
3966         x = arg.x, y = arg.y;
3967         _this.maybePoint = {
3968           x: x,
3969           y: y
3970         };
3971         lc.setShapesInProgress(_this._getShapes(lc));
3972         return lc.repaintLayer('main');
3973       };
3974     })(this);
3975     polygonFinishOpen = (function(_this) {
3976       return function() {
3977         _this.maybePoint = {
3978           x: Infinity,
3979           y: Infinity
3980         };
3981         return _this._close(lc);
3982       };
3983     })(this);
3984     polygonFinishClosed = (function(_this) {
3985       return function() {
3986         _this.maybePoint = _this.points[0];
3987         return _this._close(lc);
3988       };
3989     })(this);
3990     polygonCancel = (function(_this) {
3991       return function() {
3992         return _this._cancel(lc);
3993       };
3994     })(this);
3995     polygonUnsubscribeFuncs.push(lc.on('drawingChange', (function(_this) {
3996       return function() {
3997         return _this._cancel(lc);
3998       };
3999     })(this)));
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));
4007   };
4009   Polygon.prototype.willBecomeInactive = function(lc) {
4010     Polygon.__super__.willBecomeInactive.call(this, lc);
4011     if (this.points || this.maybePoint) {
4012       this._cancel(lc);
4013     }
4014     return this.polygonUnsubscribe();
4015   };
4017   Polygon.prototype._getArePointsClose = function(a, b) {
4018     return (Math.abs(a.x - b.x) + Math.abs(a.y - b.y)) < 10;
4019   };
4021   Polygon.prototype._getWillClose = function() {
4022     if (!(this.points && this.points.length > 1)) {
4023       return false;
4024     }
4025     if (!this.maybePoint) {
4026       return false;
4027     }
4028     return this._getArePointsClose(this.points[0], this.maybePoint);
4029   };
4031   Polygon.prototype._getWillFinish = function() {
4032     if (!(this.points && this.points.length > 1)) {
4033       return false;
4034     }
4035     if (!this.maybePoint) {
4036       return false;
4037     }
4038     return this._getArePointsClose(this.points[0], this.maybePoint) || this._getArePointsClose(this.points[this.points.length - 1], this.maybePoint);
4039   };
4041   Polygon.prototype._cancel = function(lc) {
4042     lc.trigger('lc-polygon-stopped');
4043     this.maybePoint = null;
4044     this.points = null;
4045     lc.setShapesInProgress([]);
4046     return lc.repaintLayer('main');
4047   };
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));
4054     }
4055     this.maybePoint = null;
4056     return this.points = null;
4057   };
4059   Polygon.prototype._getShapes = function(lc, isInProgress) {
4060     var shape;
4061     if (isInProgress == null) {
4062       isInProgress = true;
4063     }
4064     shape = this._getShape(lc, isInProgress);
4065     if (shape) {
4066       return [shape];
4067     } else {
4068       return [];
4069     }
4070   };
4072   Polygon.prototype._getShape = function(lc, isInProgress) {
4073     var points;
4074     if (isInProgress == null) {
4075       isInProgress = true;
4076     }
4077     points = [];
4078     if (this.points) {
4079       points = points.concat(this.points);
4080     }
4081     if ((!isInProgress) && points.length < 3) {
4082       return null;
4083     }
4084     if (isInProgress && this.maybePoint) {
4085       points.push(this.maybePoint);
4086     }
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);
4095         })
4096       });
4097     } else {
4098       return null;
4099     }
4100   };
4102   Polygon.prototype.optionsStyle = 'polygon-and-stroke-width';
4104   return Polygon;
4106 })(ToolWithStroke);
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);
4123   }
4125   Rectangle.prototype.name = 'Rectangle';
4127   Rectangle.prototype.iconName = 'rectangle';
4129   Rectangle.prototype.begin = function(x, y, lc) {
4130     return this.currentShape = createShape('Rectangle', {
4131       x: x,
4132       y: y,
4133       strokeWidth: this.strokeWidth,
4134       strokeColor: lc.getColor('primary'),
4135       fillColor: lc.getColor('secondary')
4136     });
4137   };
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);
4143   };
4145   Rectangle.prototype.end = function(x, y, lc) {
4146     return lc.saveShape(this.currentShape);
4147   };
4149   return Rectangle;
4151 })(ToolWithStroke);
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');
4174   }
4176   SelectShape.prototype.didBecomeActive = function(lc) {
4177     var onDown, onDrag, onUp, selectShapeUnsubscribeFuncs;
4178     selectShapeUnsubscribeFuncs = [];
4179     this._selectShapeUnsubscribe = (function(_this) {
4180       return function() {
4181         var func, j, len, results;
4182         results = [];
4183         for (j = 0, len = selectShapeUnsubscribeFuncs.length; j < len; j++) {
4184           func = selectShapeUnsubscribeFuncs[j];
4185           results.push(func());
4186         }
4187         return results;
4188       };
4189     })(this);
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
4200           });
4201           lc.setShapesInProgress([
4202             _this.selectedShape, createShape('SelectionBox', {
4203               shape: _this.selectedShape,
4204               handleSize: 0
4205             })
4206           ]);
4207           lc.repaintLayer('main');
4208           br = _this.selectedShape.getBoundingRect();
4209           return _this.dragOffset = {
4210             x: x - br.x,
4211             y: y - br.y
4212           };
4213         }
4214       };
4215     })(this);
4216     onDrag = (function(_this) {
4217       return function(arg) {
4218         var x, y;
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
4225           });
4226           lc.setShapesInProgress([
4227             _this.selectedShape, createShape('SelectionBox', {
4228               shape: _this.selectedShape,
4229               handleSize: 0
4230             })
4231           ]);
4232           return lc.repaintLayer('main');
4233         }
4234       };
4235     })(this);
4236     onUp = (function(_this) {
4237       return function(arg) {
4238         var x, y;
4239         x = arg.x, y = arg.y;
4240         if (_this.didDrag) {
4241           _this.didDrag = false;
4242           lc.trigger('shapeMoved', {
4243             shape: _this.selectedShape
4244           });
4245           lc.trigger('drawingChange', {});
4246           lc.repaintLayer('main');
4247           return _this._drawSelectCanvas(lc);
4248         }
4249       };
4250     })(this);
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);
4255   };
4257   SelectShape.prototype.willBecomeInactive = function(lc) {
4258     this._selectShapeUnsubscribe();
4259     return lc.setShapesInProgress([]);
4260   };
4262   SelectShape.prototype._drawSelectCanvas = function(lc) {
4263     var shapes;
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', {
4270           shape: shape,
4271           handleSize: 0,
4272           backgroundColor: "#" + (_this._intToHex(index))
4273         });
4274       };
4275     })(this));
4276     return lc.draw(shapes, this.selectCtx);
4277   };
4279   SelectShape.prototype._intToHex = function(i) {
4280     return ("000000" + (i.toString(16))).slice(-6);
4281   };
4283   SelectShape.prototype._getPixel = function(x, y, lc, ctx) {
4284     var p, pixel;
4285     p = lc.drawingCoordsToClientCoords(x, y);
4286     pixel = ctx.getImageData(p.x, p.y, 1, 1).data;
4287     if (pixel[3]) {
4288       return parseInt(this._rgbToHex(pixel[0], pixel[1], pixel[2]), 16);
4289     } else {
4290       return null;
4291     }
4292   };
4294   SelectShape.prototype._componentToHex = function(c) {
4295     var hex;
4296     hex = c.toString(16);
4297     return ("0" + hex).slice(-2);
4298   };
4300   SelectShape.prototype._rgbToHex = function(r, g, b) {
4301     return "" + (this._componentToHex(r)) + (this._componentToHex(g)) + (this._componentToHex(b));
4302   };
4304   return SelectShape;
4306 })(Tool);
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) {
4320     return false;
4321   }
4322   if (point.y < box.y) {
4323     return false;
4324   }
4325   if (point.x > box.x + box.width) {
4326     return false;
4327   }
4328   if (point.y > box.y + box.height) {
4329     return false;
4330   }
4331   return true;
4334 module.exports = Text = (function(superClass) {
4335   extend(Text, superClass);
4337   Text.prototype.name = 'Text';
4339   Text.prototype.iconName = 'text';
4341   function Text() {
4342     this.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;
4349   }
4351   Text.prototype.didBecomeActive = function(lc) {
4352     var switchAway, unsubscribeFuncs, updateInputEl;
4353     unsubscribeFuncs = [];
4354     this.unsubscribe = (function(_this) {
4355       return function() {
4356         var func, i, len, results;
4357         results = [];
4358         for (i = 0, len = unsubscribeFuncs.length; i < len; i++) {
4359           func = unsubscribeFuncs[i];
4360           results.push(func());
4361         }
4362         return results;
4363       };
4364     })(this);
4365     switchAway = (function(_this) {
4366       return function() {
4367         _this._ensureNotEditing(lc);
4368         _this._clearCurrentShape(lc);
4369         return lc.repaintLayer('main');
4370       };
4371     })(this);
4372     updateInputEl = (function(_this) {
4373       return function() {
4374         return _this._updateInputEl(lc);
4375       };
4376     })(this);
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) {
4381       return function() {
4382         _this._clearCurrentShape(lc);
4383         return lc.repaintLayer('main');
4384       };
4385     })(this)));
4386     unsubscribeFuncs.push(lc.on('primaryColorChange', (function(_this) {
4387       return function(newColor) {
4388         if (!_this.currentShape) {
4389           return;
4390         }
4391         _this.currentShape.color = newColor;
4392         _this._updateInputEl(lc);
4393         return lc.repaintLayer('main');
4394       };
4395     })(this)));
4396     return unsubscribeFuncs.push(lc.on('setFont', (function(_this) {
4397       return function(font) {
4398         if (!_this.currentShape) {
4399           return;
4400         }
4401         _this.font = font;
4402         _this.currentShape.setFont(font);
4403         _this._setShapesInProgress(lc);
4404         _this._updateInputEl(lc);
4405         return lc.repaintLayer('main');
4406       };
4407     })(this)));
4408   };
4410   Text.prototype.willBecomeInactive = function(lc) {
4411     if (this.currentShape) {
4412       this._ensureNotEditing(lc);
4413       this.commit(lc);
4414     }
4415     return this.unsubscribe();
4416   };
4418   Text.prototype.setText = function(text) {
4419     return this.text = text;
4420   };
4422   Text.prototype._ensureNotEditing = function(lc) {
4423     if (this.currentShapeState === 'editing') {
4424       return this._exitEditingState(lc);
4425     }
4426   };
4428   Text.prototype._clearCurrentShape = function(lc) {
4429     this.currentShape = null;
4430     this.initialShapeBoundingRect = null;
4431     this.currentShapeState = null;
4432     return lc.setShapesInProgress([]);
4433   };
4435   Text.prototype.commit = function(lc) {
4436     if (this.currentShape.text) {
4437       lc.saveShape(this.currentShape);
4438     }
4439     this._clearCurrentShape(lc);
4440     return lc.repaintLayer('main');
4441   };
4443   Text.prototype._getSelectionShape = function(ctx, backgroundColor) {
4444     if (backgroundColor == null) {
4445       backgroundColor = null;
4446     }
4447     return createShape('SelectionBox', {
4448       shape: this.currentShape,
4449       ctx: ctx,
4450       backgroundColor: backgroundColor
4451     });
4452   };
4454   Text.prototype._setShapesInProgress = function(lc) {
4455     switch (this.currentShapeState) {
4456       case 'selected':
4457         return lc.setShapesInProgress([this._getSelectionShape(lc.ctx), this.currentShape]);
4458       case 'editing':
4459         return lc.setShapesInProgress([this._getSelectionShape(lc.ctx, '#fff')]);
4460       default:
4461         return lc.setShapesInProgress([this.currentShape]);
4462     }
4463   };
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();
4473       point = {
4474         x: x,
4475         y: y
4476       };
4477       if (getIsPointInBox(point, br)) {
4478         this.dragAction = 'move';
4479       }
4480       if (getIsPointInBox(point, selectionShape.getBottomRightHandleRect())) {
4481         this.dragAction = 'resizeBottomRight';
4482       }
4483       if (getIsPointInBox(point, selectionShape.getTopLeftHandleRect())) {
4484         this.dragAction = 'resizeTopLeft';
4485       }
4486       if (getIsPointInBox(point, selectionShape.getBottomLeftHandleRect())) {
4487         this.dragAction = 'resizeBottomLeft';
4488       }
4489       if (getIsPointInBox(point, selectionShape.getTopRightHandleRect())) {
4490         this.dragAction = 'resizeTopRight';
4491       }
4492       if (this.dragAction === 'none' && this.currentShapeState === 'editing') {
4493         this.dragAction = 'stop-editing';
4494         this._exitEditingState(lc);
4495       }
4496     } else {
4497       this.color = lc.getColor('primary');
4498       this.currentShape = createShape('Text', {
4499         x: x,
4500         y: y,
4501         text: this.text,
4502         color: this.color,
4503         font: this.font,
4504         v: 1
4505       });
4506       this.dragAction = 'place';
4507       this.currentShapeState = 'selected';
4508     }
4509     if (this.dragAction === 'none') {
4510       this.commit(lc);
4511       return;
4512     }
4513     this.initialShapeBoundingRect = this.currentShape.getBoundingRect(lc.ctx);
4514     this.dragOffset = {
4515       x: x - this.initialShapeBoundingRect.x,
4516       y: y - this.initialShapeBoundingRect.y
4517     };
4518     this._setShapesInProgress(lc);
4519     return lc.repaintLayer('main');
4520   };
4522   Text.prototype["continue"] = function(x, y, lc) {
4523     var br, brBottom, brRight;
4524     if (this.dragAction === 'none') {
4525       return;
4526     }
4527     br = this.initialShapeBoundingRect;
4528     brRight = br.x + br.width;
4529     brBottom = br.y + br.height;
4530     switch (this.dragAction) {
4531       case 'place':
4532         this.currentShape.x = x;
4533         this.currentShape.y = y;
4534         this.didDrag = true;
4535         break;
4536       case 'move':
4537         this.currentShape.x = x - this.dragOffset.x;
4538         this.currentShape.y = y - this.dragOffset.y;
4539         this.didDrag = true;
4540         break;
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);
4543         break;
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);
4547         break;
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);
4551         break;
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);
4555     }
4556     this._setShapesInProgress(lc);
4557     lc.repaintLayer('main');
4558     return this._updateInputEl(lc);
4559   };
4561   Text.prototype.end = function(x, y, lc) {
4562     if (!this.currentShape) {
4563       return;
4564     }
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);
4569       }
4570     }
4571     this._setShapesInProgress(lc);
4572     lc.repaintLayer('main');
4573     return this._updateInputEl(lc);
4574   };
4576   Text.prototype._enterEditingState = function(lc) {
4577     var onChange;
4578     this.currentShapeState = 'editing';
4579     if (this.inputEl) {
4580       throw "State error";
4581     }
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();
4597     });
4598     this.inputEl.addEventListener('touchstart', function(e) {
4599       return e.stopPropagation();
4600     });
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();
4609       };
4610     })(this);
4611     this.inputEl.addEventListener('keydown', (function(_this) {
4612       return function() {
4613         return _this._updateInputEl(lc, true);
4614       };
4615     })(this));
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);
4622   };
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');
4630   };
4632   Text.prototype._updateInputEl = function(lc, withMargin) {
4633     var br, transformString;
4634     if (withMargin == null) {
4635       withMargin = false;
4636     }
4637     if (!this.inputEl) {
4638       return;
4639     }
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";
4647     } else {
4648       this.inputEl.style.width = (br.width + 12) + "px";
4649     }
4650     if (withMargin) {
4651       this.inputEl.style.height = (br.height + 10 + this.currentShape.renderer.metrics.leading) + "px";
4652     } else {
4653       this.inputEl.style.height = (br.height + 10) + "px";
4654     }
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;
4661   };
4663   Text.prototype.optionsStyle = 'font';
4665   return Text;
4667 })(Tool);
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;
4675 tools = {};
4677 tools.Tool = Tool = (function() {
4678   function Tool() {}
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) {};
4698   return Tool;
4700 })();
4702 tools.ToolWithStroke = ToolWithStroke = (function(superClass) {
4703   extend(ToolWithStroke, superClass);
4705   function ToolWithStroke(lc) {
4706     this.strokeWidth = lc.opts.defaultStrokeWidth;
4707   }
4709   ToolWithStroke.prototype.optionsStyle = 'stroke-width';
4711   ToolWithStroke.prototype.didBecomeActive = function(lc) {
4712     var unsubscribeFuncs;
4713     unsubscribeFuncs = [];
4714     this.unsubscribe = (function(_this) {
4715       return function() {
4716         var func, i, len, results;
4717         results = [];
4718         for (i = 0, len = unsubscribeFuncs.length; i < len; i++) {
4719           func = unsubscribeFuncs[i];
4720           results.push(func());
4721         }
4722         return results;
4723       };
4724     })(this);
4725     return unsubscribeFuncs.push(lc.on('setStrokeWidth', (function(_this) {
4726       return function(strokeWidth) {
4727         _this.strokeWidth = strokeWidth;
4728         return lc.trigger('toolDidUpdateOptions');
4729       };
4730     })(this)));
4731   };
4733   ToolWithStroke.prototype.willBecomeInactive = function(lc) {
4734     return this.unsubscribe();
4735   };
4737   return ToolWithStroke;
4739 })(Tool);
4741 module.exports = tools;
4744 },{}]},{},[18])(18)