Loosely couple Node.js code to canvas module.

Treat absence of canvas module like a web worker context.
Relates to 
This commit is contained in:
Jürg Lehni 2016-07-12 19:11:09 +02:00
parent 73fc111b50
commit 89c60b1a05
11 changed files with 39 additions and 23 deletions

View file

@ -47,7 +47,7 @@ buildNames.forEach(function(name) {
evaluate: ['src/constants.js'], evaluate: ['src/constants.js'],
setup: function() { 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 // Note that this would be merged in with already existing
// objects. // objects.
return { return {
__options: Object.assign({}, options, buildOptions[name]) __options: Object.assign({}, options, buildOptions[name])

View file

@ -347,7 +347,7 @@ Base.exports.PaperScript = function() {
} }
if (/^(inline|both)$/.test(sourceMaps)) { if (/^(inline|both)$/.test(sourceMaps)) {
code += "\n//# sourceMappingURL=data:application/json;base64," code += "\n//# sourceMappingURL=data:application/json;base64,"
+ window.btoa(unescape(encodeURIComponent( + self.btoa(unescape(encodeURIComponent(
JSON.stringify(map)))); JSON.stringify(map))));
} }
code += "\n//# sourceURL=" + (url || 'paperscript'); code += "\n//# sourceURL=" + (url || 'paperscript');

View file

@ -36,7 +36,7 @@ paper = new (PaperScope.inject(Base.exports, {
// - PaperScript support in require() with sourceMaps // - PaperScript support in require() with sourceMaps
// - exportFrames / exportImage on CanvasView // - exportFrames / exportImage on CanvasView
if (paper.agent.node) if (paper.agent.node)
require('./node/extend')(paper); require('./node/extend.js')(paper);
// https://github.com/umdjs/umd // https://github.com/umdjs/umd
if (typeof define === 'function' && define.amd) { if (typeof define === 'function' && define.amd) {

View file

@ -19,11 +19,12 @@
/* global document:true, window:true */ /* global document:true, window:true */
// Create a window variable valid in the paper.js scope, that points to the // Create a window variable valid in the paper.js scope, that points to the
// native window in browsers and the emulated JSDom one in node.js // native window in browsers and the emulated JSDom one in node.js
// In workers, window is null (but self is defined), so we can use the validity // In workers, and on Node.js when no Canvas is present, `window` is null (but
// of the local window object to detect a worker-like context in the library. // `self` is defined), so we can use the validity of the local window object to
var window = self ? self.window : require('./node/window'), // detect a worker-like context in the library.
document = window && window.document; // Make sure `self` always points to a window object, also on Node.js.
// Make sure 'self' always points to a window object, also on Node.js. self = self || require('./node/window.js');
// NOTE: We're not modifying the global `self` here. We receive its value passed // 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. // to the paper.js function scope, and this is the one that is modified here.
self = self || window; var window = self.window,
document = self.document;

View file

@ -379,7 +379,7 @@ var Raster = Item.extend(/** @lends Raster# */{
}, },
setSource: function(src) { setSource: function(src) {
var image = new window.Image(), var image = new self.Image(),
crossOrigin = this._crossOrigin; crossOrigin = this._crossOrigin;
if (crossOrigin) if (crossOrigin)
image.crossOrigin = crossOrigin; image.crossOrigin = crossOrigin;

View file

@ -13,7 +13,7 @@
var Http = { var Http = {
request: function(options) { request: function(options) {
// Code borrowed from Coffee Script and extended: // Code borrowed from Coffee Script and extended:
var xhr = new window.XMLHttpRequest(); var xhr = new self.XMLHttpRequest();
xhr.open((options.method || 'get').toUpperCase(), options.url, xhr.open((options.method || 'get').toUpperCase(), options.url,
Base.pick(options.async, true)); Base.pick(options.async, true));
if (options.mimeType) if (options.mimeType)

View file

@ -10,16 +10,29 @@
* All rights reserved. * All rights reserved.
*/ */
var Canvas = require('canvas'),
idlUtils = require('jsdom/lib/jsdom/living/generated/utils');
// Add some useful extensions to HTMLCanvasElement: // Add some useful extensions to HTMLCanvasElement:
// - HTMLCanvasElement#type, so we can switch to a PDF canvas // - HTMLCanvasElement#type, so we can switch to a PDF canvas
// - Various Node Canvas methods, routed through from HTMLCanvasElement: // - Various Node Canvas methods, routed through from HTMLCanvasElement:
// toBuffer, pngStream, createPNGStream, jpgStream, createJPGStream // toBuffer, pngStream, createPNGStream, jpgStream, createJPGStream
module.exports = function(window) { module.exports = function(window) {
var HTMLCanvasElement = window.HTMLCanvasElement; var Canvas;
try {
Canvas = require('canvas');
} catch(e) {
// Remove `self.window`, so we still have the global `self` reference,
// but no `window` object:
// - On the browser, this corresponds to a worker context.
// - On Node.js, it basically means the canvas is missing or not working
// which can be treated the same way.
delete window.window;
console.info(
'Unable to load Canvas module. Running in a headless context.');
return;
}
var idlUtils = require('jsdom/lib/jsdom/living/generated/utils'),
HTMLCanvasElement = window.HTMLCanvasElement;
// Add fake HTMLCanvasElement#type property: // Add fake HTMLCanvasElement#type property:
Object.defineProperty(HTMLCanvasElement.prototype, 'type', { Object.defineProperty(HTMLCanvasElement.prototype, 'type', {

View file

@ -27,7 +27,7 @@ var document = jsdom.jsdom('<html><body></body></html>', {
}), }),
window = document.defaultView; window = document.defaultView;
require('./canvas')(window); require('./canvas.js')(window);
// Define XMLSerializer and DOMParser shims, to emulate browser behavior. // Define XMLSerializer and DOMParser shims, to emulate browser behavior.
// Effort to bring this to jsdom: https://github.com/tmpvar/jsdom/issues/1368 // Effort to bring this to jsdom: https://github.com/tmpvar/jsdom/issues/1368

View file

@ -369,7 +369,7 @@ new function() {
definitions = null; definitions = null;
} }
return options.asString return options.asString
? new window.XMLSerializer().serializeToString(svg) ? new self.XMLSerializer().serializeToString(svg)
: svg; : svg;
} }

View file

@ -553,10 +553,12 @@ new function() {
// WebKit. We also get variations of quotes or no quotes, single or // WebKit. We also get variations of quotes or no quotes, single or
// double, so handle it all with one regular expression: // double, so handle it all with one regular expression:
var match = value && value.match(/\((?:["'#]*)([^"')]+)/), var match = value && value.match(/\((?:["'#]*)([^"')]+)/),
res = match && definitions[match[1] name = match && match[1],
// This is required by Firefox, which can produce absolute urls res = name && definitions[window
// for local gradients, see #1001: // This is required by Firefox, which can produce absolute
.replace(window.location.href.split('#')[0] + '#', '')]; // urls for local gradients, see #1001:
? name.replace(window.location.href.split('#')[0] + '#', '')
: name];
// Patch in support for SVG's gradientUnits="objectBoundingBox" through // Patch in support for SVG's gradientUnits="objectBoundingBox" through
// Color#_scaleToBounds // Color#_scaleToBounds
if (res && res._scaleToBounds) { if (res && res._scaleToBounds) {
@ -659,7 +661,7 @@ new function() {
function onLoad(svg) { function onLoad(svg) {
try { try {
var node = typeof svg === 'object' ? svg : new window.DOMParser() var node = typeof svg === 'object' ? svg : new self.DOMParser()
.parseFromString(svg, 'image/svg+xml'); .parseFromString(svg, 'image/svg+xml');
if (!node.nodeName) { if (!node.nodeName) {
node = null; node = null;

View file

@ -86,7 +86,7 @@ var equals = function(actual, expected, message, options) {
|| type === 'boolean' && 'Boolean' || type === 'boolean' && 'Boolean'
|| type === 'undefined' && 'Undefined' || type === 'undefined' && 'Undefined'
|| Array.isArray(expected) && 'Array' || Array.isArray(expected) && 'Array'
|| expected instanceof window.Element && 'Element' // handle DOM Elements || expected instanceof self.Element && 'Element' // handle DOM Elements
|| (cls = expected && expected._class) // check _class 2nd last || (cls = expected && expected._class) // check _class 2nd last
|| type === 'object' && 'Object'; // Object as catch-all || type === 'object' && 'Object'; // Object as catch-all
var comparator = type && comparators[type]; var comparator = type && comparators[type];