Implement support for web-workers.

Relates to #634, closes #582
This commit is contained in:
Jürg Lehni 2016-02-02 17:30:38 +01:00
parent 74d188967c
commit 8fb7c41537
15 changed files with 272 additions and 135 deletions

53
examples/Worker/Main.html Normal file
View file

@ -0,0 +1,53 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Shapes</title>
<link rel="stylesheet" href="../css/style.css">
<script type="text/javascript" src="../../dist/paper-full.js"></script>
<script type="text/paperscript" canvas="canvas">
if (window.Worker) { // Check if Browser supports the Worker API.
var worker = new Worker("Worker.js");
// Create two paths, and send them to the worker to perform a boolean
// operation on them.
var circle = new Path.Circle({
center: [200, 200],
radius: 100,
fillColor: 'red'
});
var rectangle = new Path.Rectangle({
point: [200, 200],
size: [200, 200],
fillColor: 'blue'
});
var data = [
circle.exportJSON(),
rectangle.exportJSON()
];
console.log('Sent', data);
worker.postMessage(data);
// The worker sends the result back in a message, from which we can then
// create a new path item and siplay it.
worker.onmessage = function(event) {
var data = event.data;
if (data) {
console.log('Received', data);
var result = project.activeLayer.importJSON(data);
result.fillColor = 'yellow';
result.fillColor.alpha = 0.5;
result.position += [400, 0];
result.fullySelected = true;
}
};
}
</script>
</head>
<body>
<canvas id="canvas" resize></canvas>
</body>
</html>

14
examples/Worker/Worker.js Normal file
View file

@ -0,0 +1,14 @@
importScripts('../../dist/paper-full.js');
paper.install(this);
paper.setup([640, 480]);
onmessage = function(event) {
var data = event.data;
if (data) {
var path1 = project.importJSON(data[0]);
var path2 = project.importJSON(data[1]);
console.log(path1, path2);
var result = path1.unite(path2);
postMessage(result.exportJSON());
}
};

View file

@ -45,7 +45,7 @@
"gulp-cached": "^1.1.0", "gulp-cached": "^1.1.0",
"gulp-git-streamed": "^1.0.0", "gulp-git-streamed": "^1.0.0",
"gulp-jshint": "^2.0.0", "gulp-jshint": "^2.0.0",
"gulp-prepro": "^2.2.0", "gulp-prepro": "^2.3.0",
"gulp-qunits": "^2.0.1", "gulp-qunits": "^2.0.1",
"gulp-rename": "^1.2.2", "gulp-rename": "^1.2.2",
"gulp-shell": "^0.5.2", "gulp-shell": "^0.5.2",
@ -58,7 +58,7 @@
"jshint": "2.8.x", "jshint": "2.8.x",
"jshint-summary": "^0.4.0", "jshint-summary": "^0.4.0",
"merge-stream": "^1.0.0", "merge-stream": "^1.0.0",
"prepro": "^2.2.0", "prepro": "^2.3.0",
"qunitjs": "^1.20.0", "qunitjs": "^1.20.0",
"require-dir": "^0.3.0", "require-dir": "^0.3.0",
"resemblejs": "^2.1.0", "resemblejs": "^2.1.0",

View file

@ -229,29 +229,33 @@ var BlendMode = new function() {
// is sticky is not enough, as Chome 27 pretends for blend-modes to work, // is sticky is not enough, as Chome 27 pretends for blend-modes to work,
// but does not actually apply them. // but does not actually apply them.
var ctx = CanvasProvider.getContext(1, 1); var ctx = CanvasProvider.getContext(1, 1);
if (ctx) {
Base.each(modes, function(func, mode) { Base.each(modes, function(func, mode) {
// Blend #330000 (51) and #aa0000 (170): // Blend #330000 (51) and #aa0000 (170):
// Multiplying should lead to #220000 (34) // Multiplying should lead to #220000 (34)
// For darken we need to reverse color parameters in order to test mode.
var darken = mode === 'darken', var darken = mode === 'darken',
ok = false; ok = false;
ctx.save(); ctx.save();
// FF 3.6 throws exception when setting globalCompositeOperation to // FF 3.6 throws exception when setting globalCompositeOperation to
// unsupported values. // unsupported values.
try { try {
// For darken we need to reverse color parameters in order to
// test mode.
ctx.fillStyle = darken ? '#300' : '#a00'; ctx.fillStyle = darken ? '#300' : '#a00';
ctx.fillRect(0, 0, 1, 1); ctx.fillRect(0, 0, 1, 1);
ctx.globalCompositeOperation = mode; ctx.globalCompositeOperation = mode;
if (ctx.globalCompositeOperation === mode) { if (ctx.globalCompositeOperation === mode) {
ctx.fillStyle = darken ? '#a00' : '#300'; ctx.fillStyle = darken ? '#a00' : '#300';
ctx.fillRect(0, 0, 1, 1); ctx.fillRect(0, 0, 1, 1);
ok = ctx.getImageData(0, 0, 1, 1).data[0] !== darken ? 170 : 51; ok = ctx.getImageData(0, 0, 1, 1).data[0] !== darken
? 170 : 51;
} }
} catch (e) {} } catch (e) {}
ctx.restore(); ctx.restore();
nativeModes[mode] = ok; nativeModes[mode] = ok;
}); });
CanvasProvider.release(ctx); CanvasProvider.release(ctx);
}
this.process = function(mode, srcContext, dstContext, alpha, offset) { this.process = function(mode, srcContext, dstContext, alpha, offset) {
var srcCanvas = srcContext.canvas, var srcCanvas = srcContext.canvas,

View file

@ -16,6 +16,8 @@ var CanvasProvider = {
canvases: [], canvases: [],
getCanvas: function(width, height) { getCanvas: function(width, height) {
if (!window)
return null;
var canvas, var canvas,
clear = true; clear = true;
if (typeof width === 'object') { if (typeof width === 'object') {
@ -49,14 +51,17 @@ var CanvasProvider = {
}, },
getContext: function(width, height) { getContext: function(width, height) {
return this.getCanvas(width, height).getContext('2d'); var canvas = this.getCanvas(width, height);
return canvas ? canvas.getContext('2d') : null;
}, },
// release can receive either a canvas or a context. // release can receive either a canvas or a context.
release: function(obj) { release: function(obj) {
var canvas = obj.canvas ? obj.canvas : obj; var canvas = obj && obj.canvas ? obj.canvas : obj;
if (canvas && canvas.getContext) {
// We restore contexts on release(), see getCanvas() // We restore contexts on release(), see getCanvas()
canvas.getContext('2d').restore(); canvas.getContext('2d').restore();
this.canvases.push(canvas); this.canvases.push(canvas);
} }
}
}; };

View file

@ -64,7 +64,7 @@ var PaperScope = Base.extend(/** @lends PaperScope# */{
if (!this.support) { if (!this.support) {
// Set up paper.support, as an object containing properties that // Set up paper.support, as an object containing properties that
// describe the support of various features. // describe the support of various features.
var ctx = CanvasProvider.getContext(1, 1); var ctx = CanvasProvider.getContext(1, 1) || {};
proto.support = { proto.support = {
nativeDash: 'setLineDash' in ctx || 'mozDash' in ctx, nativeDash: 'setLineDash' in ctx || 'mozDash' in ctx,
nativeBlendModes: BlendMode.nativeModes nativeBlendModes: BlendMode.nativeModes
@ -72,7 +72,8 @@ var PaperScope = Base.extend(/** @lends PaperScope# */{
CanvasProvider.release(ctx); CanvasProvider.release(ctx);
} }
if (!this.agent) { if (!this.agent) {
var user = window.navigator.userAgent.toLowerCase(), // Use self.instead of window, to cover handle web-workers too.
var user = self.navigator.userAgent.toLowerCase(),
// Detect basic platforms, only mac internally required for now. // Detect basic platforms, only mac internally required for now.
os = (/(darwin|win|mac|linux|freebsd|sunos)/.exec(user)||[])[0], os = (/(darwin|win|mac|linux|freebsd|sunos)/.exec(user)||[])[0],
platform = os === 'darwin' ? 'mac' : os, platform = os === 'darwin' ? 'mac' : os,

View file

@ -300,7 +300,7 @@ Base.exports.PaperScript = (function() {
// -2 required to remove function header: // -2 required to remove function header:
// https://code.google.com/p/chromium/issues/detail?id=331655 // https://code.google.com/p/chromium/issues/detail?id=331655
offset -= 2; offset -= 2;
} else if (url && window.location.href.indexOf(url) === 0) { } else if (window && url && !window.location.href.indexOf(url)) {
// If the code stems from the actual html page, determine the // If the code stems from the actual html page, determine the
// offset of inlined code. // offset of inlined code.
var html = document.getElementsByTagName('html')[0].innerHTML; var html = document.getElementsByTagName('html')[0].innerHTML;
@ -440,7 +440,8 @@ Base.exports.PaperScript = (function() {
if (handlers) if (handlers)
code += '\nreturn { ' + handlers + ' };'; code += '\nreturn { ' + handlers + ' };';
var agent = paper.agent; var agent = paper.agent;
if (agent.chrome || agent.firefox && agent.versionNumber < 40) { if (document && (agent.chrome
|| agent.firefox && agent.versionNumber < 40)) {
// On older Firefox, all error numbers inside dynamically compiled // On older Firefox, all error numbers inside dynamically compiled
// code are relative to the line where the eval / compilation // code are relative to the line where the eval / compilation
// happened. To fix this issue, we're temporarily inserting a new // happened. To fix this issue, we're temporarily inserting a new
@ -536,7 +537,8 @@ Base.exports.PaperScript = (function() {
} }
function loadAll() { function loadAll() {
Base.each(document.getElementsByTagName('script'), loadScript); Base.each(document && document.getElementsByTagName('script'),
loadScript);
} }
/** /**
@ -560,14 +562,16 @@ Base.exports.PaperScript = (function() {
return script ? loadScript(script) : loadAll(); return script ? loadScript(script) : loadAll();
} }
// Catch cases where paper.js is loaded after the browser event has already if (window) {
// occurred. // Catch cases where paper.js is loaded after the browser event has
// already occurred.
if (document.readyState === 'complete') { if (document.readyState === 'complete') {
// Handle it asynchronously // Handle it asynchronously
setTimeout(loadAll); setTimeout(loadAll);
} else { } else {
DomEvent.add(window, { load: loadAll }); DomEvent.add(window, { load: loadAll });
} }
}
return { return {
compile: compile, compile: compile,

View file

@ -113,7 +113,7 @@ var DomElement = new function() {
* prefix variants. * prefix variants.
*/ */
getPrefixed: function(el, name) { getPrefixed: function(el, name) {
return handlePrefix(el, name); return el && handlePrefix(el, name);
}, },
setPrefixed: function(el, name, value) { setPrefixed: function(el, name, value) {

View file

@ -17,21 +17,28 @@
*/ */
var DomEvent = /** @lends DomEvent */{ var DomEvent = /** @lends DomEvent */{
add: function(el, events) { add: function(el, events) {
// Do not fail if el is not defined, that way we can keep the code that
// should not fail in web-workers to a minimum.
if (el) {
for (var type in events) { for (var type in events) {
var func = events[type], var func = events[type],
parts = type.split(/[\s,]+/g); parts = type.split(/[\s,]+/g);
for (var i = 0, l = parts.length; i < l; i++) for (var i = 0, l = parts.length; i < l; i++)
el.addEventListener(parts[i], func, false); el.addEventListener(parts[i], func, false);
} }
}
}, },
remove: function(el, events) { remove: function(el, events) {
// See DomEvent.add() for an explanation of this check:
if (el) {
for (var type in events) { for (var type in events) {
var func = events[type], var func = events[type],
parts = type.split(/[\s,]+/g); parts = type.split(/[\s,]+/g);
for (var i = 0, l = parts.length; i < l; i++) for (var i = 0, l = parts.length; i < l; i++)
el.removeEventListener(parts[i], func, false); el.removeEventListener(parts[i], func, false);
} }
}
}, },
getPoint: function(event) { getPoint: function(event) {

View file

@ -17,5 +17,14 @@
// their shared scope. // their shared scope.
/* global document:true, window:true */ /* global document:true, window:true */
window = window || require('./node/window'); // Use typeof self to detect both browsers and web-workers.
var document = window.document; // In workers, window will then be null, so we can use the validity of the
// window object to decide if we're in a worker-like context in the rest of
// the library.
var window = self ? self.window : require('./node/window'),
document = window && window.document;
// Make sure 'self' always points to a window object, also on Node.js.
// NOTE: We're not modifying the global `self` here. We receive its value passed
// to the paper.js function scope, and this is the one that is modified here.
self = self || window;

View file

@ -56,9 +56,9 @@ if (typeof window === 'object') {
prepro.setup(function() { prepro.setup(function() {
// Return objects to be defined in the preprocess-scope. // Return objects to be defined in the preprocess-scope.
// Note that this would be merge in with already existing objects. // Note that this would be merge in with already existing objects.
// We're defining window here since the paper-scope argument is only // We're defining self here since the paper-scope argument is only
// available in the included scripts when the library is actually built. // available in the included scripts when the library is actually built.
return { __options: options, window: null }; return { __options: options, self: undefined };
}); });
// Load constants.js, required by the on-the-fly preprocessing: // Load constants.js, required by the on-the-fly preprocessing:
prepro.include('../src/constants.js'); prepro.include('../src/constants.js');

View file

@ -32,7 +32,7 @@
// Allow the minification of the undefined variable by defining it as a local // Allow the minification of the undefined variable by defining it as a local
// parameter inside the paper scope. // parameter inside the paper scope.
var paper = function(window, undefined) { var paper = function(self, undefined) {
/*#*/ include('init.js'); /*#*/ include('init.js');
// Inline Straps.js core (the Base class) inside the paper scope first: // Inline Straps.js core (the Base class) inside the paper scope first:
/*#*/ include('../node_modules/straps/straps.js'); /*#*/ include('../node_modules/straps/straps.js');
@ -123,4 +123,4 @@ var paper = function(window, undefined) {
/*#*/ include('export.js'); /*#*/ include('export.js');
return paper; return paper;
}(this.window); }(this.self);

View file

@ -75,7 +75,7 @@ var Color = Base.extend(new function() {
var value = +components[i]; var value = +components[i];
components[i] = i < 3 ? value / 255 : value; components[i] = i < 3 ? value / 255 : value;
} }
} else { } else if (window) {
// Named // Named
var cached = colorCache[string]; var cached = colorCache[string];
if (!cached) { if (!cached) {
@ -102,6 +102,9 @@ var Color = Base.extend(new function() {
]; ];
} }
components = cached.slice(); components = cached.slice();
} else {
// Web-workers can't resolve CSS color names, for now.
components = [0, 0, 0];
} }
return components; return components;
} }

View file

@ -42,16 +42,16 @@ var CanvasView = View.extend(/** @lends CanvasView# */{
+ [].slice.call(arguments, 1)); + [].slice.call(arguments, 1));
canvas = CanvasProvider.getCanvas(size); canvas = CanvasProvider.getCanvas(size);
} }
var context = this._context = canvas.getContext('2d'); var ctx = this._context = canvas.getContext('2d');
// Save context right away, and restore in #remove(). Also restore() and // Save context right away, and restore in #remove(). Also restore() and
// save() again in _setViewSize(), to prevent accumulation of scaling. // save() again in _setElementSize() to prevent accumulation of scaling.
context.save(); ctx.save();
this._pixelRatio = 1; this._pixelRatio = 1;
if (!/^off|false$/.test(PaperScope.getAttribute(canvas, 'hidpi'))) { if (!/^off|false$/.test(PaperScope.getAttribute(canvas, 'hidpi'))) {
// Hi-DPI Canvas support based on: // Hi-DPI Canvas support based on:
// http://www.html5rocks.com/en/tutorials/canvas/hidpi/ // http://www.html5rocks.com/en/tutorials/canvas/hidpi/
var deviceRatio = window.devicePixelRatio || 1, var deviceRatio = window.devicePixelRatio || 1,
backingStoreRatio = DomElement.getPrefixed(context, backingStoreRatio = DomElement.getPrefixed(ctx,
'backingStorePixelRatio') || 1; 'backingStorePixelRatio') || 1;
this._pixelRatio = deviceRatio / backingStoreRatio; this._pixelRatio = deviceRatio / backingStoreRatio;
} }
@ -63,13 +63,13 @@ var CanvasView = View.extend(/** @lends CanvasView# */{
return remove.base.call(this); return remove.base.call(this);
}, },
_setViewSize: function _setViewSize(width, height) { _setElementSize: function _setElementSize(width, height) {
var pixelRatio = this._pixelRatio; var pixelRatio = this._pixelRatio;
// Upscale the canvas if the pixel ratio is more than 1. // Upscale the canvas if the pixel ratio is more than 1.
_setViewSize.base.call(this, width * pixelRatio, height * pixelRatio); _setElementSize.base.call(this, width * pixelRatio, height * pixelRatio);
if (pixelRatio !== 1) { if (pixelRatio !== 1) {
var element = this._element, var element = this._element,
context = this._context; ctx = this._context;
// We need to set the correct size on non-resizable canvases through // We need to set the correct size on non-resizable canvases through
// their style when HiDPI is active, as otherwise they would appear // their style when HiDPI is active, as otherwise they would appear
// too big. // too big.
@ -80,9 +80,9 @@ var CanvasView = View.extend(/** @lends CanvasView# */{
} }
// Scale the context to counter the fact that we've manually scaled // Scale the context to counter the fact that we've manually scaled
// our canvas element. // our canvas element.
context.restore(); ctx.restore();
context.save(); ctx.save();
context.scale(pixelRatio, pixelRatio); ctx.scale(pixelRatio, pixelRatio);
} }
}, },
@ -90,18 +90,13 @@ var CanvasView = View.extend(/** @lends CanvasView# */{
* Converts the provide size in any of the units allowed in the browser to * Converts the provide size in any of the units allowed in the browser to
* pixels. * pixels.
*/ */
getPixelSize: function(size) { getPixelSize: function getPixelSize(size) {
var agent = paper.agent, var agent = paper.agent,
pixels; pixels;
if (agent && agent.firefox) {
// Firefox doesn't appear to convert context.font sizes to pixels, // Firefox doesn't appear to convert context.font sizes to pixels,
// while other browsers do. Workaround: // while other browsers do. Fall-back to View#getPixelSize.
var parent = this._element.parentNode, if (agent && agent.firefox) {
temp = document.createElement('div'); pixels = getPixelSize.base.call(this, size);
temp.style.fontSize = size;
parent.appendChild(temp);
pixels = parseFloat(DomElement.getStyles(temp).fontSize);
parent.removeChild(temp);
} else { } else {
var ctx = this._context, var ctx = this._context,
prevFont = ctx.font; prevFont = ctx.font;

View file

@ -23,29 +23,6 @@ var View = Base.extend(Emitter, /** @lends View# */{
_class: 'View', _class: 'View',
initialize: function View(project, element) { initialize: function View(project, element) {
// Store reference to the currently active global paper scope, and the
// active project, which will be represented by this view
this._project = project;
this._scope = project._scope;
this._element = element;
// Sub-classes may set _pixelRatio first
if (!this._pixelRatio)
this._pixelRatio = window.devicePixelRatio || 1;
// Generate an id for this view / element if it does not have one
this._id = element.getAttribute('id');
if (this._id == null)
element.setAttribute('id', this._id = 'view-' + View._id++);
// Install event handlers
DomEvent.add(element, this._viewEvents);
// Borrowed from Hammer.js:
var none = 'none';
DomElement.setPrefixed(element.style, {
userDrag: none,
userSelect: none,
touchCallout: none,
contentZooming: none,
tapHighlightColor: 'rgba(0,0,0,0)'
});
function getSize(name) { function getSize(name) {
return element[name] || parseInt(element.getAttribute(name), 10); return element[name] || parseInt(element.getAttribute(name), 10);
@ -63,8 +40,26 @@ var View = Base.extend(Emitter, /** @lends View# */{
: size; : size;
} }
// If the element has the resize attribute, listen to resize events and var size;
// update its coordinate space accordingly if (window && element) {
// Generate an id for this view / element if it does not have one
this._id = element.getAttribute('id');
if (this._id == null)
element.setAttribute('id', this._id = 'view-' + View._id++);
// Install event handlers
DomEvent.add(element, this._viewEvents);
// Borrowed from Hammer.js:
var none = 'none';
DomElement.setPrefixed(element.style, {
userDrag: none,
userSelect: none,
touchCallout: none,
contentZooming: none,
tapHighlightColor: 'rgba(0,0,0,0)'
});
// If the element has the resize attribute, listen to resize events
// and update its coordinate space accordingly
if (PaperScope.hasAttribute(element, 'resize')) { if (PaperScope.hasAttribute(element, 'resize')) {
var that = this; var that = this;
DomEvent.add(window, this._windowEvents = { DomEvent.add(window, this._windowEvents = {
@ -73,13 +68,9 @@ var View = Base.extend(Emitter, /** @lends View# */{
} }
}); });
} }
// Set canvas size even if we just determined the size from it, since
// it might have been set to a % size, in which case it would use some size = getCanvasSize();
// default internal size (300x150 on WebKit) and scale up the pixels.
// We also need this call here for HiDPI support.
var size = this._viewSize = getCanvasSize();
this._setViewSize(size.width, size.height);
// TODO: Test this on IE:
if (PaperScope.hasAttribute(element, 'stats') if (PaperScope.hasAttribute(element, 'stats')
&& typeof Stats !== 'undefined') { && typeof Stats !== 'undefined') {
this._stats = new Stats(); this._stats = new Stats();
@ -92,6 +83,25 @@ var View = Base.extend(Emitter, /** @lends View# */{
style.top = offset.y + 'px'; style.top = offset.y + 'px';
document.body.appendChild(stats); document.body.appendChild(stats);
} }
} else {
// For web-workers: Allow calling of `paper.setup(new Size(x, y));`
size = new Size(element);
element = null;
}
// Store reference to the currently active global paper scope, and the
// active project, which will be represented by this view
this._project = project;
this._scope = project._scope;
this._element = element;
// Sub-classes may set _pixelRatio first
if (!this._pixelRatio)
this._pixelRatio = window && window.devicePixelRatio || 1;
// Set canvas size even if we just determined the size from it, since
// it might have been set to a % size, in which case it would use some
// default internal size (300x150 on WebKit) and scale up the pixels.
// We also need this call here for HiDPI support.
this._setElementSize(size.width, size.height);
this._viewSize = size;
// Keep track of views internally // Keep track of views internally
View._views.push(this); View._views.push(this);
// Link this id to our view // Link this id to our view
@ -192,14 +202,15 @@ var View = Base.extend(Emitter, /** @lends View# */{
* @function * @function
* @return {Boolean} {@true if the view was updated} * @return {Boolean} {@true if the view was updated}
*/ */
// update: function() { update: function() {
// }, },
/** /**
* Updates the view if there are changes. * Updates the view if there are changes.
* *
* @deprecated use {@link #update()} instead. * @deprecated use {@link #update()} instead.
*/ */
// NOTE: We cannot use draw: '#update'` as that would not work on CanvasView
draw: function() { draw: function() {
this.update(); this.update();
}, },
@ -369,8 +380,8 @@ var View = Base.extend(Emitter, /** @lends View# */{
delta = size.subtract(this._viewSize); delta = size.subtract(this._viewSize);
if (delta.isZero()) if (delta.isZero())
return; return;
this._setElementSize(width, height);
this._viewSize.set(width, height); this._viewSize.set(width, height);
this._setViewSize(width, height);
// Call onResize handler on any size change // Call onResize handler on any size change
this.emit('resize', { this.emit('resize', {
size: size, size: size,
@ -384,12 +395,14 @@ var View = Base.extend(Emitter, /** @lends View# */{
/** /**
* Private method, overridden in CanvasView for HiDPI support. * Private method, overridden in CanvasView for HiDPI support.
*/ */
_setViewSize: function(width, height) { _setElementSize: function(width, height) {
var element = this._element; var element = this._element;
if (element) {
if (element.width !== width) if (element.width !== width)
element.width = width; element.width = width;
if (element.height !== height) if (element.height !== height)
element.height = height; element.height = height;
}
}, },
/** /**
@ -482,6 +495,32 @@ var View = Base.extend(Emitter, /** @lends View# */{
*/ */
isInserted: function() { isInserted: function() {
return DomElement.isInserted(this._element); return DomElement.isInserted(this._element);
},
// Empty stubs of #getPixelSize() and #getTextWidth(), around so that
// web-workers don't fail. Overridden with proper functionality in
// CanvasView.
getPixelSize: function(size) {
var element = this._element,
pixels;
if (element) {
// this code is part of the Firefox workaround in CanvasView, but
// also provides a way to determine pixel-size that does not involve
// a Canvas. It still does not work in a web-worker though.
var parent = element.parentNode,
temp = document.createElement('div');
temp.style.fontSize = size;
parent.appendChild(temp);
pixels = parseFloat(DomElement.getStyles(temp).fontSize);
parent.removeChild(temp);
} else {
pixels = parseFloat(pixels);
}
return pixels;
},
getTextWidth: function(font, lines) {
return 0;
} }
}, Base.each(['rotate', 'scale', 'shear', 'skew'], function(key) { }, Base.each(['rotate', 'scale', 'shear', 'skew'], function(key) {
var rotate = key === 'rotate'; var rotate = key === 'rotate';
@ -924,15 +963,18 @@ var View = Base.extend(Emitter, /** @lends View# */{
_id: 0, _id: 0,
create: function(project, element) { create: function(project, element) {
if (typeof element === 'string') if (document && typeof element === 'string')
element = document.getElementById(element); element = document.getElementById(element);
// Factory to provide the right View subclass for a given element. // Factory to provide the right View subclass for a given element.
// Produces only CanvasViews for now: // Produces only CanvasView or View items (for workers) for now:
return new CanvasView(project, element); var ctor = window ? CanvasView : View;
return new ctor(project, element);
} }
} }
}, },
new function() { // Injection scope for event handling on the browser new function() { // Injection scope for event handling on the browser
if (!window)
return;
/** /**
* Native event handling, coordinate conversion, focus handling and * Native event handling, coordinate conversion, focus handling and
* delegation to view and tool objects. * delegation to view and tool objects.