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)); paper.setup(new paper.Canvas(600, 600));
with (paper) { with (paper) {
console.log(Raster);
var raster = new paper.Raster(data); var raster = new paper.Raster(data);
raster.position = view.center; raster.position = view.center;
paper.view.exportFrames({ paper.view.exportFrames({

View file

@ -6,7 +6,7 @@
"Jürg Lehni <juerg@lehni.org> (http://lehni.org)", "Jürg Lehni <juerg@lehni.org> (http://lehni.org)",
"Jonathan Puckey <jonathan@studiomoniker.com> (http://studiomoniker.com)" "Jonathan Puckey <jonathan@studiomoniker.com> (http://studiomoniker.com)"
], ],
"main": "./src/node/index.js", "main": "./src/load.js",
"engines": { "engines": {
"node": ">= 0.4.0" "node": ">= 0.4.0"
}, },
@ -29,7 +29,7 @@
], ],
"devDependencies": { "devDependencies": {
"uglify-js": "~2.3.6", "uglify-js": "~2.3.6",
"prepro": "~0.6.0", "prepro": "~0.7.0",
"grunt": "~0.4.1", "grunt": "~0.4.1",
"grunt-contrib-uglify": "~0.2.2" "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 // 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 // better minification and the future use of strict mode once it makes sense
// in terms of performance. // in terms of performance.
paper.PaperScope.prototype.PaperScript = new function() { var PaperScript = paper.PaperScope.prototype.PaperScript = new function() {
/*#*/ if (options.parser == 'acorn') { /*#*/ if (options.parser == 'acorn') {
/*#*/ include('../../components/acorn/acorn.min.js'); /*#*/ include('../../components/acorn/acorn.min.js');
/*#*/ } else if (options.parser == 'esprima') { /*#*/ } else if (options.parser == 'esprima') {
@ -332,3 +332,24 @@ paper.PaperScope.prototype.PaperScript = new function() {
/*#*/ } // !options.browser /*#*/ } // !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 // First add Base and a couple of other objects that are not automatically
// exported to exports (Numerical, Key, etc), then inject all exports into // exported to exports (Numerical, Key, etc), then inject all exports into
// PaperScope, and create the initial paper object, all in one statement: // PaperScope, and create the initial paper object, all in one statement:
/*#*/ if (options.browser) {
paper = new (PaperScope.inject(Base.merge(Base.exports, { paper = new (PaperScope.inject(Base.merge(Base.exports, {
// Mark fields as enumeralbe so PaperScope.inject can pick them up // Mark fields as enumeralbe so PaperScope.inject can pick them up
enumerable: true, enumerable: true,
@ -26,3 +28,19 @@ paper = new (PaperScope.inject(Base.merge(Base.exports, {
// Support AMD (e.g. require.js) // Support AMD (e.g. require.js)
if (typeof define === 'function' && define.amd) if (typeof define === 'function' && define.amd)
define(paper); 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 // the browser, avoiding the step of having to manually preprocess it after each
// change. This is very useful during development of the library itself. // change. This is very useful during development of the library itself.
if (!window.include) { if (typeof window !== 'undefined') {
var scripts = document.getElementsByTagName('script'); // Browser based loading through PrePro:
var src = scripts[scripts.length - 1].getAttribute('src'); if (!window.include) {
// Assume that we're loading load.js from a root folder, either through var scripts = document.getElementsByTagName('script');
// dist/paper.js symbolic link, or directly through src/load.js, and match var src = scripts[scripts.length - 1].getAttribute('src');
// root as all the parts of the path that lead to that folder. // Assume that we're loading browser.js from a root folder, either
var root = src.match(/^(.*\/)\w*\//)[1]; // through dist/paper.js, or directly through src/load.js, and match
// First load the PrePro's load.js file, which provides the include() // root as all the parts of the path that lead to that folder.
// function for the browser. var root = src.match(/^(.*\/)\w*\//)[1];
document.write('<script type="text/javascript" src="' + root + 'node_modules/prepro/lib/load.js"></script>'); // First load the PrePro's browser.js file, which provides the include()
// Now that we have include(), load this file again, which will execute the // function for the browser.
// lower part of the code the 2nd time around. document.write('<script type="text/javascript" src="' + root
document.write('<script type="text/javascript" src="' + root + 'src/load.js"></script>'); + '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 { } else {
include('options.js'); // Node based loading through PrePro:
include('paper.js'); 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. * All rights reserved.
*/ */
// Define default options for compile-time preprocessing. These are also used // Define default options for browser based compile-time preprocessing.
// for building, but some values are overridden (e.g. version, stats). // These are also used for building, but some values are overridden
// (e.g. version, stats).
var options = { var options = {
parser: 'acorn', parser: 'acorn',

View file

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

View file

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