Merge branch 'refs/heads/paperscript-refactoring'

Conflicts:
	src/core/PaperScope.js
This commit is contained in:
Jürg Lehni 2014-01-03 12:34:07 +01:00
commit 57f1763abe
10 changed files with 192 additions and 166 deletions

View file

@ -1,7 +1,7 @@
require('paper');
var paper = require('./Tadpoles.pjs');
var paper = require('paper');
var scope = require('./Tadpoles.pjs')(new paper.Size(1024, 768));
paper.view.exportFrames({
scope.view.exportFrames({
amount: 400,
directory: __dirname,
onComplete: function() {

View file

@ -1,5 +1,3 @@
paper.setup(new Canvas(1024, 768));
// Adapted from Flocking Processing example by Daniel Schiffman:
// http://processing.org/learning/topics/flocking.html

View file

@ -26,7 +26,7 @@
},
"devDependencies": {
"uglify-js": "~2.3.6",
"prepro": "~0.8.0",
"prepro": "~0.8.1",
"grunt": "~0.4.1",
"grunt-contrib-uglify": "~0.2.2"
},

View file

@ -102,16 +102,9 @@ var PaperScope = Base.extend(/** @lends PaperScope# */{
/**
* The reference to the active tool.
* @name PaperScope#tool
* @type Tool
* @bean
*/
getTool: function() {
// If no tool exists yet but one is requested, produce it now on the fly
// so it can be used in PaperScript.
if (!this._tool)
this._tool = new Tool();
return this._tool;
},
/**
* The list of available tools.
@ -131,10 +124,9 @@ var PaperScope = Base.extend(/** @lends PaperScope# */{
return this;
},
evaluate: function(code) {
var res = paper.PaperScript.evaluate(code, this);
execute: function(code) {
paper.PaperScript.execute(code, this);
View.updateFocus();
return res;
},
/**
@ -166,10 +158,10 @@ var PaperScope = Base.extend(/** @lends PaperScope# */{
// Copy over all fields from this scope to the destination.
// Do not use Base.each, since we also want to enumerate over
// fields on PaperScope.prototype, e.g. all classes
for (var key in this) {
if (!/^(version|_id)/.test(key))
for (var key in this)
// Exclude all 'hidden' fields
if (!/^_/.test(key))
scope[key] = this[key];
}
},
/**
@ -228,14 +220,14 @@ var PaperScope = Base.extend(/** @lends PaperScope# */{
_id: 0,
/**
* Retrieves a PaperScope object with the given id or associated with
* the passed canvas element.
* Retrieves a PaperScope object with the given id or associated
* with the passed canvas element.
*
* @param id
*/
get: function(id) {
// If a script tag is passed, get the id from it.
if (typeof id === 'object')
if (id && id.getAttribute)
id = id.getAttribute('id');
return this._scopes[id] || null;
},

View file

@ -14,19 +14,10 @@
* @name PaperScript
* @namespace
*/
// Note that due to the use of with(), PaperScript gets compiled outside the
// 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 = (function(root) {
var Base = paper.Base,
PaperScope = paper.PaperScope,
// For local reference, for now only when setting lineNumberBase on
// Firefox.
PaperScript,
// Locally turn of exports and define for inlined acorn / esprima.
// Just declaring the local vars is enough, as they will be undefined.
exports, define,
var PaperScript = Base.exports.PaperScript = (function(root) {
// Locally turn of exports and define for inlined acorn / esprima.
// Just declaring the local vars is enough, as they will be undefined.
var exports, define,
// The scope into which the library is loaded.
scope = this;
/*#*/ if (__options.version == 'dev') {
@ -69,9 +60,9 @@ paper.PaperScope.prototype.PaperScript = (function(root) {
},
{}
);
paper.Point.inject(fields);
paper.Size.inject(fields);
paper.Color.inject(fields);
Point.inject(fields);
Size.inject(fields);
Color.inject(fields);
// Use very short name for the binary operator (_$_) as well as the
// unary operator ($_), as operations will be replaced with then.
@ -238,97 +229,117 @@ paper.PaperScope.prototype.PaperScript = (function(root) {
}
/**
* Evaluates parsed PaperScript code in the passed {@link PaperScope}
* object. It also installs handlers automatically for us.
* Executes the parsed PaperScript code in a compiled function that receives
* all properties of the passed {@link PaperScope} as arguments, to emulate
* a global scope with unaffected performance. It also installs global view
* and tool handlers automatically for you.
*
* @name PaperScript.evaluate
* @name PaperScript.execute
* @function
* @param {String} code The PaperScript code
* @param {PaperScript} scope The scope in which the code is executed
* @return {Object} the result of the code evaluation
* @param {PaperScript} scope The scope for which the code is executed
*/
function evaluate(code, scope) {
function execute(code, scope) {
// Set currently active scope.
paper = scope;
var view = scope.project && scope.project.view,
var view = scope.getView(),
// Only create a tool object if something resembling a tool handler
// definition is contained in the code.
tool = /\s+on(?:Key|Mouse)(?:Up|Down|Move|Drag)/.test(code)
? new Tool()
: null,
toolHandlers = tool ? tool._events : [],
// Compile a list of all handlers that can be defined globally
// inside the PaperScript. These are passed on to the function as
// undefined arguments, so that their name exists, rather than
// injecting a code line that defines them as variables.
// They are exported again at the end of the function.
handlers = ['onFrame', 'onResize'].concat(toolHandlers),
res;
// Define variables for potential handlers, so eval() calls below to
// fetch their values do not require try-catch around them.
// Use with() {} in order to make the scope the current 'global' scope
// instead of window.
with (scope) {
// Within this, use a function scope, so local variables to not try
// and set themselves on the scope object.
(function() {
var onActivate, onDeactivate, onEditOptions,
onMouseDown, onMouseUp, onMouseDrag, onMouseMove,
onKeyDown, onKeyUp, onFrame, onResize;
code = compile(code);
/*#*/ if (__options.environment == 'browser') {
if (root.InstallTrigger) { // Firefox
// On Firefox, all error numbers inside evaled code are
// relative to the line where the eval happened. Totally
// silly, but that's how it is. So we're calculating the
// base of lineNumbers, to remove it again from reported
// errors. Luckily, Firefox is the only browser where we can
// define the lineNumber for exceptions.
var handle = PaperScript.handleException;
if (!handle) {
handle = PaperScript.handleException = function(e) {
throw e.lineNumber >= lineNumber
? new Error(e.message, e.fileName,
e.lineNumber - lineNumber)
: e;
}
// We're using a crazy hack to detect wether the library
// is minified or not: By generating a second error on
// the 2nd line and using the difference in line numbers
// to calculate the offset to the eval, it works in both
// casees.
var lineNumber = new Error().lineNumber;
lineNumber += (new Error().lineNumber - lineNumber) * 3;
}
try {
// Add a semi-colon at the start so Firefox doesn't
// swallow empty lines and shift error messages.
res = eval(';' + code);
} catch (e) {
handle(e);
}
} else {
res = eval(code);
}
/*#*/ } else { // !__options.environment == 'browser'
res = eval(code);
/*#*/ } // !__options.environment == 'browser'
// Only look for tool handlers if something resembling their
// name is contained in the code.
if (/on(?:Key|Mouse)(?:Up|Down|Move|Drag)/.test(code)) {
Base.each(paper.Tool.prototype._events, function(key) {
var value = eval(key);
if (value) {
// Use the getTool accessor that handles auto tool
// creation for us:
scope.getTool()[key] = value;
}
});
}
if (view) {
view.setOnResize(onResize);
// Fire resize event directly, so any user
// defined resize handlers are called.
view.fire('resize', {
size: view.size,
delta: new Point()
});
if (onFrame)
view.setOnFrame(onFrame);
// Automatically update view at the end.
view.update();
}
}).call(scope);
code = compile(code);
// compile a list of paramter names for all variables that need to
// appear as globals inside the script. At the same time, also collect
// their values, so we can pass them on as arguments in the function
// call.
var params = ['_$_', '$_', 'view', 'tool'],
args = [_$_, $_ , view, tool];
// Look through all enumerable properties on the scope and expose these
// too as pseudo-globals.
for (var key in scope) {
if (!/^_/.test(key)) {
params.push(key);
args.push(scope[key]);
}
}
// Finally define the handler variable names as parameters and compose
// the string describing the properties for the returned object at the
// end of the code execution, so we can retrieve their values from the
// function call.
handlers = Base.each(handlers, function(key) {
params.push(key);
this.push(key + ': ' + key);
}, []).join(', ');
// We need an additional line that returns the handlers in one object.
code += '\nreturn { ' + handlers + ' };';
/*#*/ if (__options.environment == 'browser') {
if (root.InstallTrigger) { // Firefox
// Add a semi-colon at the start so Firefox doesn't swallow empty
// lines and shift error messages.
code = ';' + code;
// On Firefox, all error numbers inside evaled code are relative to
// the line where the eval happened. Totally silly, but that's how
// it is. So we're calculating the base of lineNumbers, to remove it
// again from reported errors. Luckily, Firefox is the only browser
// where we can define the lineNumber for exceptions.
var handle = PaperScript.handleException;
if (!handle) {
handle = PaperScript.handleException = function(e) {
throw e.lineNumber >= lineNumber
? new Error(e.message, e.fileName,
e.lineNumber - lineNumber)
: e;
};
// We're using a crazy hack to detect wether the library is
// minified or not: By generating a second error on the 2nd line
// and using the difference in line numbers to calculate the
// offset to the eval, it works in both casees.
var lineNumber = new Error().lineNumber;
lineNumber += (new Error().lineNumber - lineNumber) * 3;
}
try {
res = new Function(params, code).apply(scope, args);
// NOTE: in order for the calculation of the above lineNumber
// offset to work, we cannot add any statements before the above
// line of code, nor can we put it into a separate function.
} catch (e) {
handle(e);
}
} else {
res = new Function(params, code).apply(scope, args);
}
/*#*/ } else { // !__options.environment == 'browser'
res = new Function(params, code).apply(scope, args);
/*#*/ } // !__options.environment == 'browser'
// Now install the 'global' tool and view handlers, and we're done!
Base.each(toolHandlers, function(key) {
var value = res[key];
if (value)
tool[key] = value;
});
if (view) {
if (res.onResize)
view.setOnResize(res.onResize);
// Fire resize event directly, so any user
// defined resize handlers are called.
view.fire('resize', {
size: view.size,
delta: new Point()
});
if (res.onFrame)
view.setOnFrame(res.onFrame);
// Automatically update view at the end.
view.update();
}
return res;
}
/*#*/ if (__options.environment == 'browser') {
@ -356,12 +367,12 @@ paper.PaperScope.prototype.PaperScript = (function(root) {
if (src) {
// If we're loading from a source, request that first and
// then run later.
paper.Http.request('get', src, function(code) {
evaluate(code, scope);
Http.request('get', src, function(code) {
execute(code, scope);
});
} else {
// We can simply get the code form the script tag.
evaluate(script.innerHTML, scope);
execute(script.innerHTML, scope);
}
// Mark script as loaded now.
script.setAttribute('data-paper-ignore', true);
@ -375,12 +386,12 @@ paper.PaperScope.prototype.PaperScript = (function(root) {
// Handle it asynchronously
setTimeout(load);
} else {
paper.DomEvent.add(window, { load: load });
DomEvent.add(window, { load: load });
}
return PaperScript = {
return {
compile: compile,
evaluate: evaluate,
execute: execute,
load: load,
lineNumberBase: 0
};
@ -393,22 +404,28 @@ paper.PaperScope.prototype.PaperScript = (function(root) {
path = require('path');
require.extensions['.pjs'] = function(module, uri) {
var source = 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;
evaluate(source, scope);
module.exports = scope;
// Requiring a PaperScript on Node.js returns an initialize method which
// needs to receive a Canvas object when called and returns the
// PaperScope.
module.exports = function(canvas) {
var source = compile(fs.readFileSync(uri, 'utf8')),
scope = new PaperScope();
scope.setup(canvas);
scope.__filename = uri;
scope.__dirname = path.dirname(uri);
// Expose core methods and values
scope.require = require;
scope.console = console;
execute(source, scope);
return scope;
};
};
/*#*/ } // __options.environment == 'node'
return PaperScript = {
return {
compile: compile,
evaluate: evaluate
execute: execute
};
/*#*/ } // !__options.environment == 'browser'

View file

@ -10,16 +10,19 @@
* All rights reserved.
*/
/** @scope _global_ */ {
// DOCS: Find a way to put this description into _global_
/**
* In a PaperScript context, the global scope is populated with all
* fields of the currently active {@link PaperScope} object. In a JavaScript
* context, it only contains the {@link #paper} reference to the currently
* active {@link PaperScope} object, which also exposes all Paper classes.
*/
/**
* @name _global_
* @namespace
*
* When code is executed as PaperScript, the script's scope is populated with
* all fields of the currently active {@link PaperScope} object, which within
* the script appear to be global.
*
* In a JavaScript context, only the {@link paper} variable is added to the
* global scope, referencing the currently active {@link PaperScope} object,
* through which all properties and Paper.js classes can be accessed.
*/
/** @scope _global_ */{
/**
* A reference to the currently active {@link PaperScope} object.
@ -32,40 +35,58 @@
// DOCS: This does not work: @borrows PaperScope#version as _global_#version,
// so we're repeating documentation here form PaperScope:
/**
* {@grouptitle Global PaperScope Properties (for PaperScript)}
* {@grouptitle Global PaperScript Properties}
*
* The project for which the PaperScript is executed.
*
* Note that when working with mulitple projects, this does not necessarily
* reflect the currently active project. For this, use
* {@link PaperScope#project} instead.
*
* The currently active project.
* @name project
* @type Project
*/
/**
* The list of all open projects within the current Paper.js context.
*
* @name projects
* @type Project[]
*/
/**
* The reference to the active project's view.
* The reference to the project's view.
*
* Note that when working with mulitple projects, this does not necessarily
* reflect the view of the currently active project. For this, use
* {@link PaperScope#view} instead.
*
* @name view
* @type View
*/
/**
* The reference to the active tool.
* The reference to the tool object which is automatically created when global
* tool event handlers are defined.
*
* Note that when working with mulitple tools, this does not necessarily
* reflect the currently active tool. For this, use {@link PaperScope#tool}
* instead.
*
* @name tool
* @type Tool
*/
/**
* The list of available tools.
*
* @name tools
* @type Tool[]
*/
/**
* {@grouptitle View Event Handlers (for PaperScript)}
* A reference to the {@link View#onFrame} handler function.
* {@grouptitle PaperScript View Event Handlers}
* A global reference to the {@link View#onFrame} handler function.
*
* @name onFrame
* @property
@ -81,7 +102,7 @@
*/
/**
* {@grouptitle Mouse Event Handlers (for PaperScript)}
* {@grouptitle PaperScript Tool Event Handlers}
* A reference to the {@link Tool#onMouseDown} handler function.
* @name onMouseDown
* @property

View file

@ -138,12 +138,10 @@ var paper = new function(undefined) {
/*#*/ include('svg/SVGImport.js');
/*#*/ } // __options.svg
/*#*/ include('export.js');
return paper;
};
// include PaperScript separately outside the main paper scope, due to its use
// of with(). This also simplifies making its inclusion optional.
/*#*/ if (__options.paperscript) {
/*#*/ include('core/PaperScript.js');
/*#*/ } // __options.paperscript
/*#*/ include('export.js');
return paper;
};

View file

@ -45,7 +45,7 @@
var Tool = PaperScopeItem.extend(/** @lends Tool# */{
_class: 'Tool',
_list: 'tools',
_reference: '_tool', // PaperScope has accessor for #tool
_reference: 'tool',
_events: [ 'onActivate', 'onDeactivate', 'onEditOptions',
'onMouseDown', 'onMouseUp', 'onMouseDrag', 'onMouseMove',
'onKeyDown', 'onKeyUp' ],

View file

@ -74,7 +74,7 @@ var Key = new function() {
type = down ? 'keydown' : 'keyup',
view = View._focused,
scope = view && view.isVisible() && view._scope,
tool = scope && scope._tool,
tool = scope && scope.tool,
name;
keyMap[key] = down;
// Detect modifiers and mark them as pressed / released

View file

@ -664,7 +664,7 @@ var View = Base.extend(Callback, /** @lends View# */{
// Always first call the view's mouse handlers, as required by
// CanvasView, and then handle the active tool, if any.
view._handleEvent('mousedown', point, event);
if (tool = view._scope._tool)
if (tool = view._scope.tool)
tool._handleEvent('mousedown', point, event);
// In the end we always call update(), which only updates the view if
// anything has changed in the above calls.
@ -673,7 +673,7 @@ var View = Base.extend(Callback, /** @lends View# */{
function handleMouseMove(view, point, event) {
view._handleEvent('mousemove', point, event);
var tool = view._scope._tool;
var tool = view._scope.tool;
if (tool) {
// If there's no onMouseDrag, fire onMouseMove while dragging.
tool._handleEvent(dragging && tool.responds('mousedrag')