Improved PrePro to be able to dynamically load Node.js code too, and improve load.js to handle both environments.

Also moved PaperScript .pjs extension code to PaperScript, and DOM related Node.js code to dom/node.js
This commit is contained in:
Jürg Lehni 2013-06-27 13:49:04 -07:00
parent 94ce1f1312
commit 4f5dac8567
10 changed files with 148 additions and 139 deletions

View file

@ -6,7 +6,6 @@ var data = "data:image/jpeg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEd
paper.setup(new paper.Canvas(600, 600));
with (paper) {
console.log(Raster);
var raster = new paper.Raster(data);
raster.position = view.center;
paper.view.exportFrames({

View file

@ -6,7 +6,7 @@
"Jürg Lehni <juerg@lehni.org> (http://lehni.org)",
"Jonathan Puckey <jonathan@studiomoniker.com> (http://studiomoniker.com)"
],
"main": "./src/node/index.js",
"main": "./src/load.js",
"engines": {
"node": ">= 0.4.0"
},
@ -29,7 +29,7 @@
],
"devDependencies": {
"uglify-js": "~2.3.6",
"prepro": "~0.6.0",
"prepro": "~0.7.0",
"grunt": "~0.4.1",
"grunt-contrib-uglify": "~0.2.2"
}

View file

@ -18,7 +18,7 @@
// main paper scope, and is added to the PaperScope class. This allows for
// better minification and the future use of strict mode once it makes sense
// in terms of performance.
paper.PaperScope.prototype.PaperScript = new function() {
var PaperScript = paper.PaperScope.prototype.PaperScript = new function() {
/*#*/ if (options.parser == 'acorn') {
/*#*/ include('../../components/acorn/acorn.min.js');
/*#*/ } else if (options.parser == 'esprima') {
@ -332,3 +332,24 @@ paper.PaperScope.prototype.PaperScript = new function() {
/*#*/ } // !options.browser
};
/*#*/ if (options.node) {
// Register the .pjs extension and have it automatically compile as PaperScript
var fs = require('fs'),
path = require('path');
require.extensions['.pjs'] = function(module, uri) {
var source = PaperScript.compile(fs.readFileSync(uri, 'utf8')),
scope = new PaperScope();
scope.__filename = uri;
scope.__dirname = path.dirname(uri);
// Expose core methods and values
scope.require = require;
scope.console = console;
PaperScript.evaluate(source, scope);
module.exports = scope;
};
/*#*/ } // options.node

59
src/dom/node.js Normal file
View file

@ -0,0 +1,59 @@
/*
* Paper.js - The Swiss Army Knife of Vector Graphics Scripting.
* http://paperjs.org/
*
* Copyright (c) 2011 - 2013, Juerg Lehni & Jonathan Puckey
* http://lehni.org/ & http://jonathanpuckey.com/
*
* Distributed under the MIT license. See LICENSE file for details.
*
* All rights reserved.
*/
// Node.js emulation layer of browser based environment, based on node-canvas
// and jsdom.
// console.log(__dirname);
var jsdom = require('jsdom'),
domToHtml = require('jsdom/lib/jsdom/browser/domtohtml').domToHtml,
// Node Canvas library: https://github.com/learnboost/node-canvas
Canvas = require('canvas');
// Expose global browser variables and create a document and a window using
// jsdom, e.g. for import/exportSVG()
var document = jsdom.jsdom('<html><body></body></html>'),
window = document.createWindow(),
navigator = window.navigator,
HTMLCanvasElement = Canvas,
Image = Canvas.Image;
// Define XMLSerializer and DOMParser shims, to emulate browser behavior.
// TODO: Put this into a simple node module, with dependency on jsdom?
function XMLSerializer() {
}
XMLSerializer.prototype.serializeToString = function(node) {
var text = domToHtml(node);
// Fix a jsdom issue where all SVG tagNames are lowercased:
// https://github.com/tmpvar/jsdom/issues/620
var tagNames = ['linearGradient', 'radialGradient', 'clipPath'];
for (var i = 0, l = tagNames.length; i < l; i++) {
var tagName = tagNames[i];
text = text.replace(
new RegExp('(<|</)' + tagName.toLowerCase() + '\\b', 'g'),
function(all, start) {
return start + tagName;
});
}
return text;
};
function DOMParser() {
}
DOMParser.prototype.parseFromString = function(string, contenType) {
var div = doc.createElement('div');
div.innerHTML = string;
return div.firstChild;
};

View file

@ -13,6 +13,8 @@
// First add Base and a couple of other objects that are not automatically
// exported to exports (Numerical, Key, etc), then inject all exports into
// PaperScope, and create the initial paper object, all in one statement:
/*#*/ if (options.browser) {
paper = new (PaperScope.inject(Base.merge(Base.exports, {
// Mark fields as enumeralbe so PaperScope.inject can pick them up
enumerable: true,
@ -26,3 +28,19 @@ paper = new (PaperScope.inject(Base.merge(Base.exports, {
// Support AMD (e.g. require.js)
if (typeof define === 'function' && define.amd)
define(paper);
/*#*/ } else if (options.node) {
paper = new (PaperScope.inject(Base.merge(Base.exports, {
// Mark fields as enumeralbe so PaperScope.inject can pick them up
enumerable: true,
Base: Base,
Numerical: Numerical,
DomElement: DomElement,
// Export dom/node.js stuff too
XMLSerializer: XMLSerializer,
DOMParser: DOMParser,
Canvas: Canvas
})))();
/*#*/ } // options.node

View file

@ -14,20 +14,40 @@
// the browser, avoiding the step of having to manually preprocess it after each
// change. This is very useful during development of the library itself.
if (!window.include) {
if (typeof window !== 'undefined') {
// Browser based loading through PrePro:
if (!window.include) {
var scripts = document.getElementsByTagName('script');
var src = scripts[scripts.length - 1].getAttribute('src');
// Assume that we're loading load.js from a root folder, either through
// dist/paper.js symbolic link, or directly through src/load.js, and match
// Assume that we're loading browser.js from a root folder, either
// through dist/paper.js, or directly through src/load.js, and match
// root as all the parts of the path that lead to that folder.
var root = src.match(/^(.*\/)\w*\//)[1];
// First load the PrePro's load.js file, which provides the include()
// First load the PrePro's browser.js file, which provides the include()
// function for the browser.
document.write('<script type="text/javascript" src="' + root + 'node_modules/prepro/lib/load.js"></script>');
// Now that we have include(), load this file again, which will execute the
// lower part of the code the 2nd time around.
document.write('<script type="text/javascript" src="' + root + 'src/load.js"></script>');
} else {
document.write('<script type="text/javascript" src="' + root
+ 'node_modules/prepro/lib/browser.js"></script>');
// Now that we have include(), load this file again, which will execute
// the lower part of the code the 2nd time around.
document.write('<script type="text/javascript" src="' + root
+ 'src/load.js"></script>');
} else {
include('options.js');
include('paper.js');
}
} else {
// Node based loading through PrePro:
var prepro = require('prepro/lib/node.js');
// Include deafult browser options.
prepro.include('options.js');
// Override node specific options.
prepro.setOptions({
browser: false,
node: true,
stats: false
});
// Load Paper.js library files.
prepro.include('paper.js');
// Export the paper scope.
module.exports = prepro.context.paper;
}

View file

@ -1,118 +0,0 @@
/*
* Paper.js - The Swiss Army Knife of Vector Graphics Scripting.
* http://paperjs.org/
*
* Copyright (c) 2011 - 2013, Juerg Lehni & Jonathan Puckey
* http://lehni.org/ & http://jonathanpuckey.com/
*
* Distributed under the MIT license. See LICENSE file for details.
*
* All rights reserved.
*/
var fs = require('fs'),
vm = require('vm'),
path = require('path'),
// Node Canvas library: https://github.com/learnboost/node-canvas
Canvas = require('canvas'),
jsdom = require('jsdom'),
domToHtml = require('jsdom/lib/jsdom/browser/domtohtml').domToHtml;
// Load the options from load.js, but evaluate into local scope:
eval(fs.readFileSync(path.resolve(__dirname, '../options.js'), 'utf8'));
// Change node.js specific settings. Use 'dev' version for on-the fly
// compilation of separate files, and set to correct value after.
options.version = 'dev';
options.browser = false;
options.node = true;
options.stats = false;
// Create a document and a window using jsdom, e.g. for exportSVG()
var doc = jsdom.jsdom('<html><body></body></html>'),
win = doc.createWindow();
// Define XMLSerializer and DOMParser shims, to emulate browser behavior.
// TODO: Put this into a simple node module, with dependency on jsdom?
function XMLSerializer() {
}
XMLSerializer.prototype.serializeToString = function(node) {
var text = domToHtml(node);
// Fix a jsdom issue where all SVG tagNames are lowercased:
// https://github.com/tmpvar/jsdom/issues/620
var tagNames = ['linearGradient', 'radialGradient', 'clipPath'];
for (var i = 0, l = tagNames.length; i < l; i++) {
var tagName = tagNames[i];
text = text.replace(
new RegExp('(<|</)' + tagName.toLowerCase() + '\\b', 'g'),
function(all, start) {
return start + tagName;
});
}
return text;
};
function DOMParser() {
}
DOMParser.prototype.parseFromString = function(string, contenType) {
var div = doc.createElement('div');
div.innerHTML = string;
return div.firstChild;
};
// Create the context within which we will run the source files:
var dirname = path.resolve(__dirname, '..');
var context = vm.createContext({
// Used to load and run source files within the same context:
include: function(uri) {
var source = fs.readFileSync(path.resolve(dirname, uri), 'utf8'),
// For relative includes, we save the current directory and then
// add the uri directory to dirname:
prevDirname = dirname;
dirname = path.resolve(dirname, path.dirname(uri));
vm.runInContext(source, context, uri);
dirname = prevDirname;
},
// Expose core methods and values
__dirname: dirname,
require: require,
options: options,
// Expose node modules
fs: fs,
Canvas: Canvas,
// Expose global browser variables:
HTMLCanvasElement: Canvas,
XMLSerializer: XMLSerializer,
DOMParser: DOMParser,
Image: Canvas.Image,
window: win,
document: doc,
navigator: win.navigator,
console: console
});
// Load Paper.js library files:
context.include('paper.js');
context.PaperScope.inject({
// Expose the Canvas, XMLSerializer & DOMParser to PaperScope:
Canvas: Canvas,
XMLSerializer: XMLSerializer,
DOMParser: DOMParser,
// Also set the correct version from package.json
version: require('../../package.json').version
});
require.extensions['.pjs'] = function(module, uri) {
var source = context.PaperScript.compile(fs.readFileSync(uri, 'utf8'));
// Temporarily override __dirname and __filename
var envVars = 'var __dirname = \'' + path.dirname(uri) + '\';'
+ 'var __filename = \'' + uri + '\';';
vm.runInContext(envVars, context);
var scope = new context.PaperScope();
context.PaperScript.evaluate(source, scope);
module.exports = scope;
};
module.exports = context.paper;

View file

@ -10,8 +10,9 @@
* All rights reserved.
*/
// Define default options for compile-time preprocessing. These are also used
// for building, but some values are overridden (e.g. version, stats).
// Define default options for browser based compile-time preprocessing.
// These are also used for building, but some values are overridden
// (e.g. version, stats).
var options = {
parser: 'acorn',

View file

@ -91,8 +91,14 @@ var paper = new function() {
/*#*/ include('style/GradientStop.js');
/*#*/ include('style/Style.js');
/*#*/ if (options.node) {
/*#*/ include('dom/node.js');
/*#*/ } // options.node
/*#*/ include('dom/DomElement.js');
/*#*/ if (options.browser) {
// DomEvent doesn't make sense outside of the browser (yet)
/*#*/ include('dom/DomEvent.js');
/*#*/ } // options.browser
/*#*/ include('ui/View.js');
/*#*/ include('ui/CanvasView.js');

View file

@ -183,6 +183,9 @@ CanvasView.inject(new function() {
}
return str;
}
var fs = require('fs');
return {
// DOCS: CanvasView#exportFrames(param);
exportFrames: function(param) {