diff --git a/package.json b/package.json index 321c1d07..7ca71315 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "jsdom": false, "jsdom/lib/jsdom/living/generated/utils": false, "source-map-support": false, - "./dist/node/window.js": false, + "./dist/node/self.js": false, "./dist/node/extend.js": false }, "keywords": [ diff --git a/src/init.js b/src/init.js index de416b51..55ae313b 100644 --- a/src/init.js +++ b/src/init.js @@ -17,14 +17,19 @@ // their shared scope. /* global document:true, window:true */ -// 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 -// In workers, and on Node.js when no Canvas is present, `window` is null (but -// `self` is defined), so we can use the validity of the local window object to -// detect a worker-like context in the library. -// Make sure `self` always points to a window object, also on Node.js. -self = self || require('./node/window.js'); +// Set up a local `window` variable valid across the full the paper.js scope, +// pointing to the native window in browsers and the one provided by JSDom in +// Node.js +// In workers and on Node.js, the global `window` variable is null. In workers, +// `self` is defined as a `WorkerGlobalScope` object, while in Node.js, `self` +// is null. +// When `self` is null (Node.js only). './node/self.js' is required to provide +// a window object through JSDom and assigned it to `self`. +// When `self.window` and therefore the local `window` is still null after that, +// we know that we are in a worker-like context. This check is used all across +// the library, see for example `View.create()`. // 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 || require('./node/self.js'); var window = self.window, document = self.document; diff --git a/src/load.js b/src/load.js index 945058e1..4d14ea3a 100644 --- a/src/load.js +++ b/src/load.js @@ -43,7 +43,7 @@ if (typeof window === 'object') { include('../node_modules/stats.js/build/stats.min.js'); include('paper.js'); } -} else { +} else if (typeof require !== 'undefined') { // Node.js based loading through Prepro.js: var prepro = require('prepro/lib/node'), // Load the default browser-based options for further amendments. diff --git a/src/node/canvas.js b/src/node/canvas.js index df4e8880..420e5c81 100644 --- a/src/node/canvas.js +++ b/src/node/canvas.js @@ -15,7 +15,7 @@ // - Various Node Canvas methods, routed through from HTMLCanvasElement: // toBuffer, pngStream, createPNGStream, jpgStream, createJPGStream -module.exports = function(window) { +module.exports = function(self) { var Canvas; try { Canvas = require('canvas'); @@ -25,14 +25,14 @@ module.exports = function(window) { // - 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; + delete self.window; console.info( - 'Unable to load Canvas module. Running in a headless context.'); + 'Canvas module not found, running in a headless context.'); return; } var idlUtils = require('jsdom/lib/jsdom/living/generated/utils'), - HTMLCanvasElement = window.HTMLCanvasElement; + HTMLCanvasElement = self.HTMLCanvasElement; // Add fake HTMLCanvasElement#type property: Object.defineProperty(HTMLCanvasElement.prototype, 'type', { diff --git a/src/node/extend.js b/src/node/extend.js index 831f93a6..1b342e7f 100644 --- a/src/node/extend.js +++ b/src/node/extend.js @@ -57,8 +57,8 @@ module.exports = function(paper) { paper.PaperScope.inject({ createCanvas: function(width, height, type) { // Do not use CanvasProvider.getCanvas(), since we may be changing - // the underlying node-canvas and don't want to release it after - // back into the pool. + // the underlying node-canvas when requesting PDF support, and don't + // want to release it after back into the pool. var canvas = paper.document.createElement('canvas'); canvas.width = width; canvas.height = height; diff --git a/src/node/self.js b/src/node/self.js new file mode 100644 index 00000000..0fa37c2c --- /dev/null +++ b/src/node/self.js @@ -0,0 +1,73 @@ +/* + * Paper.js - The Swiss Army Knife of Vector Graphics Scripting. + * http://paperjs.org/ + * + * Copyright (c) 2011 - 2016, Juerg Lehni & Jonathan Puckey + * http://scratchdisk.com/ & http://jonathanpuckey.com/ + * + * Distributed under the MIT license. See LICENSE file for details. + * + * All rights reserved. + */ + +// Node.js emulation layer of browser environment, based on jsdom with node- +// canvas integration. + +var self; + +try { + var jsdom = require('jsdom'); + + // Create our document and window objects through jsdom. + /* global document:true, window:true */ + var document = jsdom.jsdom('', { + // Use the current working directory as the document's origin, so + // requests to local files work correctly with CORS. + url: 'file://' + process.cwd() + '/', + features: { + FetchExternalResources: ['img', 'script'] + } + }); + self = document.defaultView; + + require('./canvas.js')(self); + + // Define XMLSerializer shim, to emulate browser behavior. + // Effort to bring XMLSerializer to jsdom: + // https://github.com/tmpvar/jsdom/issues/1368 + /*jshint -W082 */ + function XMLSerializer() { + } + + XMLSerializer.prototype.serializeToString = function(node) { + if (!node) + return ''; + // Fix a jsdom issue where all SVG tagNames are lowercased: + // https://github.com/tmpvar/jsdom/issues/620 + var text = node.outerHTML, + tagNames = ['linearGradient', 'radialGradient', 'clipPath', + 'textPath']; + for (var i = 0, l = tagNames.length; i < l; i++) { + var tagName = tagNames[i]; + text = text.replace( + new RegExp('(<|', { - // Use the current working directory as the document's origin, so - // requests to local files work correctly with CORS. - url: 'file://' + process.cwd() + '/', - features: { - FetchExternalResources: ['img', 'script'] - } - }), - window = document.defaultView; - -require('./canvas.js')(window); - -// Define XMLSerializer and DOMParser shims, to emulate browser behavior. -// Effort to bring this to jsdom: https://github.com/tmpvar/jsdom/issues/1368 -function XMLSerializer() { -} - -XMLSerializer.prototype.serializeToString = function(node) { - if (!node) - return ''; - var text = node.outerHTML; - // Fix a jsdom issue where all SVG tagNames are lowercased: - // https://github.com/tmpvar/jsdom/issues/620 - var tagNames = ['linearGradient', 'radialGradient', 'clipPath', 'textPath']; - for (var i = 0, l = tagNames.length; i < l; i++) { - var tagName = tagNames[i]; - text = text.replace( - new RegExp('(<|