/** * mxAsyncCanvas */ /** * Extends mxAbstractCanvas2D */ function mxAsyncCanvas(htmlCanvas) { mxAbstractCanvas2D.call(this); this.htmlCanvas = htmlCanvas; htmlCanvas.images = htmlCanvas.images || []; htmlCanvas.subCanvas = htmlCanvas.subCanvas || []; }; /** * Extends mxAbstractCanvas2D */ mxUtils.extend(mxAsyncCanvas, mxAbstractCanvas2D); /** * Variable: htmlCanvas * * The canvas instance this object is obtaining async resources for */ mxAsyncCanvas.prototype.htmlCanvas = null; /** * Variable: canvasIndex * * The current index into the canvas sub-canvas array being processed */ mxAsyncCanvas.prototype.canvasIndex = 0; /** * Variable: ctx * * Holds the current canvas context */ mxAsyncCanvas.prototype.waitCounter = 0; /** * Variable: ctx * * Holds the current canvas context */ mxAsyncCanvas.prototype.onComplete = null; mxAsyncCanvas.prototype.incWaitCounter = function() { this.waitCounter++; }; mxAsyncCanvas.prototype.decWaitCounter = function() { this.waitCounter--; if (this.waitCounter == 0 && this.onComplete != null) { this.onComplete(); this.onComplete = null; } }; mxAsyncCanvas.prototype.updateFont = function() { var style = ''; if ((this.state.fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD) { style += 'bold '; } if ((this.state.fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC) { style += 'italic '; } this.ctx.font = style + this.state.fontSize + 'px ' + this.state.fontFamily; }; mxAsyncCanvas.prototype.rotate = function(theta, flipH, flipV, cx, cy) { }; mxAsyncCanvas.prototype.setAlpha = function(alpha) { this.state.alpha = alpha; }; mxAsyncCanvas.prototype.setFontColor = function(value) { this.state.fontColor = value; }; mxAsyncCanvas.prototype.setFontBackgroundColor = function(value) { if (value == mxConstants.NONE) { value = null; } this.state.fontBackgroundColor = value; }; mxAsyncCanvas.prototype.setFontBorderColor = function(value) { if (value == mxConstants.NONE) { value = null; } this.state.fontBorderColor = value; }; mxAsyncCanvas.prototype.setFontSize = function(value) { this.state.fontSize = value; }; mxAsyncCanvas.prototype.setFontFamily = function(value) { this.state.fontFamily = value; }; mxAsyncCanvas.prototype.setFontStyle = function(value) { this.state.fontStyle = value; }; mxAsyncCanvas.prototype.rect = function(x, y, w, h) {}; mxAsyncCanvas.prototype.roundrect = function(x, y, w, h, dx, dy) {}; mxAsyncCanvas.prototype.ellipse = function(x, y, w, h) {}; //Redirect can be implemented via a hook mxAsyncCanvas.prototype.rewriteImageSource = function(src) { if (src.substring(0, 7) == 'http://' || src.substring(0, 8) == 'https://') { src = '/proxy?url=' + encodeURIComponent(src); } return src; }; mxAsyncCanvas.prototype.image = function(x, y, w, h, src, aspect, flipH, flipV) { src = this.rewriteImageSource(src); var image = this.htmlCanvas.images[src]; if (image == null) { var image = new Image(); image.onload = mxUtils.bind(this, function() { this.decWaitCounter(); }); image.onerror = mxUtils.bind(this, function() { this.decWaitCounter(); // TODO null the image out? this.htmlCanvas.images[src] = null; }); this.incWaitCounter(); this.htmlCanvas.images[src] = image; image.src = src; } }; mxAsyncCanvas.prototype.fill = function() {}; mxAsyncCanvas.prototype.stroke = function() {}; mxAsyncCanvas.prototype.fillAndStroke = function() {}; mxAsyncCanvas.prototype.text = function(x, y, w, h, str, align, valign, wrap, format, overflow, clip, rotation) { if (str == null || str.length == 0) { return; } var sc = this.state.scale; if (format == 'html' && typeof html2canvas === 'function') { this.incWaitCounter(); var canvasIndex = this.canvasIndex++; html2canvas(str, { onrendered: mxUtils.bind(this, function(canvas) { this.htmlCanvas.subCanvas[canvasIndex] = canvas; this.decWaitCounter(); }), scale: sc, logging: true }); } }; mxAsyncCanvas.prototype.finish = function(handler) { // TODO: Check if waitCounter updates need a monitor. Question is // if image load-handler can be executed in parallel leading to // race conditions when updating the "shared" waitCounter. if (this.waitCounter == 0) { handler(); } else { this.onComplete = handler; } };