Implement transparent constant-substitution in prepro.js to shave of some extra bytes of final distribution file.

This commit is contained in:
Jürg Lehni 2012-11-05 18:11:44 -08:00
parent 43a7e6cfcc
commit 35568c15f2
18 changed files with 145 additions and 107 deletions

View file

@ -34,5 +34,5 @@ then
mkdir ../dist/
fi
./preprocess.sh $MODE ../src/paper.js ../dist/paper.js '{ "browser": true }'
#./preprocess.sh $MODE ../src/paper.js ../dist/paper-server.js '{ "server": true }'
./preprocess.sh $MODE ../src/paper.js '{ "browser": true }' ../src/constants.js ../dist/paper.js
#./preprocess.sh $MODE ../src/paper.js '{ "server": true }' ../src/constants.js ../dist/paper-server.js

View file

@ -21,42 +21,22 @@
* or compile time. Very useful for libraries that are built for distribution,
* but can be also compiled from seperate sources directly for development,
* supporting build time switches.
*
* Arguments:
* -d DEFINE_JSON -- define a json containing defintions availabe to prepro
* -i INCLUDE_JS -- include a JS file containing definitinos availabe to prepro
* -c -- strip comments
*/
// Required libs
var fs = require('fs'),
path = require('path');
// Parse arguments
var args = process.argv.slice(2),
options = {},
files = [],
strip = false;
while (args.length > 0) {
var arg = args.shift();
switch (arg) {
case '-d':
// Definitions are provided as JSON and supposed to be object literals
var def = JSON.parse(args.shift());
// Merge new definitions into options object.
for (var key in def)
options[key] = def[key];
break;
case '-c':
strip = true;
break;
default:
files.push(arg);
}
}
path = require('path'),
vm = require('vm');
// Preprocessing
var code = [],
out = [];
var code = [];
function include(base, file) {
// Compose a pathname from base and file, which is specified relatively,
@ -80,11 +60,11 @@ function include(base, file) {
}
} else {
// Perhaps we need to replace some values? Supported formats are:
// /*#=*/ options.NAME (outside comments)
// *#=* options.NAME (inside comments)
line = line.replace(/\/?\*#=\*\/?\s*options\.([\w]*)/g,
function(all, name) {
return options[name];
// /*#=*/ eval (outside comments)
// *#=* eval (inside comments)
line = line.replace(/\/?\*#=\*\/?\s*([\w.]*)/g,
function(all, val) {
return eval(val);
}
);
// Now add a statement that when evaluated writes out this code line
@ -93,16 +73,57 @@ function include(base, file) {
});
}
function parse() {
var out = [];
// Evaluate the collected code: Collects result in out, through out.push()
eval(code.join('\n'));
// Start again with a new code buffer.
code = [];
// Return the resulting lines as one string.
return out.join('\n');
}
// Parse arguments
var args = process.argv.slice(2),
options = {},
files = [],
strip = false;
while (args.length > 0) {
var arg = args.shift();
switch (arg) {
case '-d':
// Definitions are provided as JSON and supposed to be object literals
var def = JSON.parse(args.shift());
// Merge new definitions into options object.
for (var key in def)
options[key] = def[key];
break;
case '-i':
// Include code to be present at prepro time, e.g. for on-the-fly
// replacement of constants, using /*#=*/ statements.
// Luckily we can reuse the include() / parse() functionality to do so:
var file = args.shift();
if (file) {
include(path.resolve(), path.normalize(file));
eval(parse());
}
break;
case '-c':
strip = true;
break;
default:
files.push(arg);
}
}
// Include all files. Everything else happens from there, through include()
files.forEach(function(file) {
include(path.resolve(), file);
});
// Evaluate the resulting code: Calls puts() and writes the result to stdout.
eval(code.join('\n'));
// Convert the resulting lines to one string again.
var out = out.join('\n');
var out = parse();
if (strip) {
out = stripComments(out);

View file

@ -21,10 +21,7 @@
# are preserved or stripped and whitespaces are compressed.
#
# Usage:
# preprocess.sh MODE SOURCE DESTINATION ARGUMENTS
#
# ARGUMENTS:
# e.g. "-DBROWSER"
# preprocess.sh MODE SOURCE DEFINITIONS PREPRO_INCLUDE DESTINATION
#
# MODE:
# commented Preprocessed but still formated and commented
@ -34,18 +31,18 @@
VERSION=0.22
DATE=$(git log -1 --pretty=format:%ad)
COMMAND="./prepro.js -d '{ \"version\": $VERSION, \"date\": \"$DATE\" }' -d '$4' $2"
COMMAND="./prepro.js -d '{ \"version\": $VERSION, \"date\": \"$DATE\" }' -d '$3' -i '$4' $2"
case $1 in
stripped)
eval "$COMMAND -c" > $3
eval "$COMMAND -c" > $5
;;
commented)
eval $COMMAND > $3
eval $COMMAND > $5
;;
compressed)
eval $COMMAND > temp.js
../../uglifyjs/bin/uglifyjs temp.js --extra --unsafe --reserved-names "$eval,$sign" > $3
../../uglifyjs/bin/uglifyjs temp.js --extra --unsafe --reserved-names "$eval,$sign" > $5
rm temp.js
;;
esac

View file

@ -369,7 +369,7 @@ var Color = this.Color = Base.extend(new function() {
_changed: function() {
this._cssString = null;
if (this._owner)
this._owner._changed(Change.STYLE);
this._owner._changed(/*#=*/ Change.STYLE);
},
/**

View file

@ -60,7 +60,7 @@ var GradientStop = this.GradientStop = Base.extend(/** @lends GradientStop# */{
// the change, so they can notify their gradient colors, which in turn
// will notify the items they are used in:
if (this._owner)
this._owner._changed(Change.STYLE);
this._owner._changed(/*#=*/ Change.STYLE);
},
/**

18
src/constants.js Normal file
View file

@ -0,0 +1,18 @@
/*
* Paper.js
*
* This file is part of Paper.js, a JavaScript Vector Graphics Library,
* based on Scriptographer.org and designed to be largely API compatible.
* http://paperjs.org/
* http://scriptographer.org/
*
* Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
* http://lehni.org/ & http://jonathanpuckey.com/
*
* Distributed under the MIT license. See LICENSE file for details.
*
* All rights reserved.
*/
/*#*/ include('item/ChangeFlag.js');
/*#*/ include('path/SelectionState.js');

View file

@ -80,7 +80,7 @@ var Group = this.Group = Item.extend(/** @lends Group# */{
_changed: function(flags) {
// Don't use this.base() for reasons of performance.
Item.prototype._changed.call(this, flags);
if (flags & (ChangeFlag.HIERARCHY | ChangeFlag.CLIPPING)) {
if (flags & (/*#=*/ ChangeFlag.HIERARCHY | /*#=*/ ChangeFlag.CLIPPING)) {
// Clear cached clip item whenever hierarchy changes
delete this._clipItem;
}

View file

@ -126,26 +126,26 @@ var Item = this.Item = Base.extend(Callback, /** @lends Item# */{
* @param {ChangeFlag} flags describes what exactly has changed.
*/
_changed: function(flags) {
if (flags & ChangeFlag.GEOMETRY) {
if (flags & /*#=*/ ChangeFlag.GEOMETRY) {
// Clear cached bounds and position whenever geometry changes
delete this._bounds;
delete this._position;
}
if (this._parent
&& (flags & (ChangeFlag.GEOMETRY | ChangeFlag.STROKE))) {
&& (flags & (/*#=*/ ChangeFlag.GEOMETRY | /*#=*/ ChangeFlag.STROKE))) {
// Clear cached bounds of all items that this item contributes to.
// We call this on the parent, since the information is cached on
// the parent, see getBounds().
this._parent._clearBoundsCache();
}
if (flags & ChangeFlag.HIERARCHY) {
if (flags & /*#=*/ ChangeFlag.HIERARCHY) {
// Clear cached bounds of all items that this item contributes to.
// We don't call this on the parent, since we're already the parent
// of the child that modified the hierarchy (that's where these
// HIERARCHY notifications go)
this._clearBoundsCache();
}
if (flags & ChangeFlag.APPEARANCE) {
if (flags & /*#=*/ ChangeFlag.APPEARANCE) {
this._project._needsRedraw();
}
// If this item is a symbol's definition, notify it of the change too
@ -226,7 +226,7 @@ var Item = this.Item = Base.extend(Callback, /** @lends Item# */{
(namedChildren[name] = namedChildren[name] || []).push(this);
children[name] = this;
}
this._changed(ChangeFlag.ATTRIBUTE);
this._changed(/*#=*/ ChangeFlag.ATTRIBUTE);
},
/**
@ -287,7 +287,7 @@ var Item = this.Item = Base.extend(Callback, /** @lends Item# */{
this[name] = value;
// #locked does not change appearance, all others do:
this._changed(name === '_locked'
? ChangeFlag.ATTRIBUTE : Change.ATTRIBUTE);
? /*#=*/ ChangeFlag.ATTRIBUTE : /*#=*/ Change.ATTRIBUTE);
}
};
}, {}), /** @lends Item# */{
@ -426,7 +426,7 @@ var Item = this.Item = Base.extend(Callback, /** @lends Item# */{
} else if ((selected = !!selected) != this._selected) {
this._selected = selected;
this._project._updateSelection(this);
this._changed(Change.ATTRIBUTE);
this._changed(/*#=*/ Change.ATTRIBUTE);
}
},
@ -473,10 +473,10 @@ var Item = this.Item = Base.extend(Callback, /** @lends Item# */{
this.setFillColor(null);
this.setStrokeColor(null);
}
this._changed(Change.ATTRIBUTE);
this._changed(/*#=*/ Change.ATTRIBUTE);
// Tell the parent the clipping mask has changed
if (this._parent)
this._parent._changed(ChangeFlag.CLIPPING);
this._parent._changed(/*#=*/ ChangeFlag.CLIPPING);
}
},
@ -553,7 +553,7 @@ var Item = this.Item = Base.extend(Callback, /** @lends Item# */{
setMatrix: function(matrix) {
// Use Matrix#initialize to easily copy over values.
this._matrix.initialize(matrix);
this._changed(Change.GEOMETRY);
this._changed(/*#=*/ Change.GEOMETRY);
}
}, Base.each(['bounds', 'strokeBounds', 'handleBounds', 'roughBounds'],
function(name) {
@ -1146,7 +1146,7 @@ function(name) {
// kept in sync.
if (item._name)
item.setName(item._name);
this._changed(Change.HIERARCHY);
this._changed(/*#=*/ Change.HIERARCHY);
return true;
}
return false;
@ -1293,7 +1293,7 @@ function(name) {
Base.splice(this._parent._children, null, this._index, 1);
// Notify parent of changed hierarchy
if (notify)
this._parent._changed(Change.HIERARCHY);
this._parent._changed(/*#=*/ Change.HIERARCHY);
this._parent = null;
return true;
}
@ -1339,7 +1339,7 @@ function(name) {
for (var i = removed.length - 1; i >= 0; i--)
removed[i]._remove(true, false);
if (removed.length > 0)
this._changed(Change.HIERARCHY);
this._changed(/*#=*/ Change.HIERARCHY);
return removed;
},
@ -1352,7 +1352,7 @@ function(name) {
// Adjust inidces
for (var i = 0, l = this._children.length; i < l; i++)
this._children[i]._index = i;
this._changed(Change.HIERARCHY);
this._changed(/*#=*/ Change.HIERARCHY);
}
},
@ -1863,7 +1863,7 @@ function(name) {
this.apply();
// We always need to call _changed since we're caching bounds on all
// items, including Group.
this._changed(Change.GEOMETRY);
this._changed(/*#=*/ Change.GEOMETRY);
// Detect matrices that contain only translations and scaling
// and transform the cached _bounds and _position without having to
// fully recalculate each time.

View file

@ -144,7 +144,7 @@ var Raster = this.Raster = PlacedItem.extend(/** @lends Raster# */{
// to modify the Raster object. We can notify such changes ahead since
// they are only used afterwards for redrawing.
if (arguments[0])
this._changed(Change.PIXELS);
this._changed(/*#=*/ Change.PIXELS);
return this._context;
},
@ -168,7 +168,7 @@ var Raster = this.Raster = PlacedItem.extend(/** @lends Raster# */{
this._size = Size.create(canvas.width, canvas.height);
this._image = null;
this._context = null;
this._changed(Change.GEOMETRY | Change.PIXELS);
this._changed(/*#=*/ Change.GEOMETRY | /*#=*/ Change.PIXELS);
},
/**
@ -193,7 +193,7 @@ var Raster = this.Raster = PlacedItem.extend(/** @lends Raster# */{
/*#*/ } // options.server
this._canvas = null;
this._context = null;
this._changed(Change.GEOMETRY);
this._changed(/*#=*/ Change.GEOMETRY);
},
// DOCS: document Raster#getSubImage

View file

@ -47,6 +47,10 @@ var paper = new function() {
// Inline Bootstrap core (the Base class) inside the paper scope first:
/*#*/ include('../lib/bootstrap.js');
/*#*/ if (options.version == 'dev') {
/*#*/ include('constants.js');
/*#*/ } // options.version == 'dev'
/*#*/ if (options.stats) {
/*#*/ include('../lib/stats.js');
/*#*/ } // options.stats
@ -69,7 +73,6 @@ var paper = new function() {
/*#*/ include('project/Project.js');
/*#*/ include('project/Symbol.js');
/*#*/ include('item/ChangeFlag.js');
/*#*/ include('item/Item.js');
/*#*/ include('item/Group.js');
/*#*/ include('item/Layer.js');
@ -80,7 +83,6 @@ var paper = new function() {
/*#*/ include('path/Segment.js');
/*#*/ include('path/SegmentPoint.js');
/*#*/ include('path/SelectionState.js');
/*#*/ include('path/Curve.js');
/*#*/ include('path/CurveLocation.js');
/*#*/ include('path/PathItem.js');

View file

@ -65,17 +65,17 @@ var Path = this.Path = PathItem.extend(/** @lends Path# */{
_changed: function(flags) {
// Don't use base() for reasons of performance.
Item.prototype._changed.call(this, flags);
if (flags & ChangeFlag.GEOMETRY) {
if (flags & /*#=*/ ChangeFlag.GEOMETRY) {
delete this._length;
// Clockwise state becomes undefined as soon as geometry changes.
delete this._clockwise;
// Curves are no longer valid
if (this._curves != null) {
for (var i = 0, l = this._curves.length; i < l; i++) {
this._curves[i]._changed(Change.GEOMETRY);
this._curves[i]._changed(/*#=*/ Change.GEOMETRY);
}
}
} else if (flags & ChangeFlag.STROKE) {
} else if (flags & /*#=*/ ChangeFlag.STROKE) {
// TODO: We could preserve the purely geometric bounds that are not
// affected by stroke: _bounds.bounds and _bounds.handleBounds
delete this._bounds;
@ -206,7 +206,7 @@ var Path = this.Path = PathItem.extend(/** @lends Path# */{
this._curves[i = length - 1] = Curve.create(this,
this._segments[i], this._segments[0]);
}
this._changed(Change.GEOMETRY);
this._changed(/*#=*/ Change.GEOMETRY);
}
},
@ -287,7 +287,7 @@ var Path = this.Path = PathItem.extend(/** @lends Path# */{
segment._index = index + i;
// Select newly added segments if path was fully selected before
if (fullySelected)
segment._selectionState = SelectionState.POINT;
segment._selectionState = /*#=*/ SelectionState.POINT;
// If parts of this segment are selected, adjust the internal
// _selectedSegmentState now
if (segment._selectionState)
@ -317,7 +317,7 @@ var Path = this.Path = PathItem.extend(/** @lends Path# */{
curve._segment1 = segments[index + amount];
}
}
this._changed(Change.GEOMETRY);
this._changed(/*#=*/ Change.GEOMETRY);
return segs;
},
@ -600,7 +600,7 @@ var Path = this.Path = PathItem.extend(/** @lends Path# */{
if (last && this._closed && (curve = curves[curves.length - 1]))
curve._segment2 = segments[0];
}
this._changed(Change.GEOMETRY);
this._changed(/*#=*/ Change.GEOMETRY);
return removed;
},
@ -670,16 +670,16 @@ var Path = this.Path = PathItem.extend(/** @lends Path# */{
*/
isFullySelected: function() {
return this._selected && this._selectedSegmentState
== this._segments.length * SelectionState.POINT;
== this._segments.length * /*#=*/ SelectionState.POINT;
},
setFullySelected: function(selected) {
var length = this._segments.length;
this._selectedSegmentState = selected
? length * SelectionState.POINT : 0;
? length * /*#=*/ SelectionState.POINT : 0;
for (var i = 0; i < length; i++)
this._segments[i]._selectionState = selected
? SelectionState.POINT : 0;
? /*#=*/ SelectionState.POINT : 0;
// No need to pass true for noChildren since Path has none anyway.
this.setSelected(selected);
},
@ -941,7 +941,7 @@ var Path = this.Path = PathItem.extend(/** @lends Path# */{
last1.remove();
this.setClosed(true);
}
this._changed(Change.GEOMETRY);
this._changed(/*#=*/ Change.GEOMETRY);
return true;
}
return false;
@ -1352,7 +1352,7 @@ var Path = this.Path = PathItem.extend(/** @lends Path# */{
var segment = segments[i];
segment._transformCoordinates(matrix, coords, false);
var state = segment._selectionState,
selected = state & SelectionState.POINT,
selected = state & /*#=*/ SelectionState.POINT,
pX = coords[0],
pY = coords[1];
@ -1370,9 +1370,9 @@ var Path = this.Path = PathItem.extend(/** @lends Path# */{
}
}
if (selected || (state & SelectionState.HANDLE_IN))
if (selected || (state & /*#=*/ SelectionState.HANDLE_IN))
drawHandle(2);
if (selected || (state & SelectionState.HANDLE_OUT))
if (selected || (state & /*#=*/ SelectionState.HANDLE_OUT))
drawHandle(4);
// Draw a rectangle at segment.point:
ctx.save();

View file

@ -104,7 +104,7 @@ var Segment = this.Segment = Base.extend(/** @lends Segment# */{
other._changed();
}
}
this._path._changed(Change.GEOMETRY);
this._path._changed(/*#=*/ Change.GEOMETRY);
},
/**
@ -174,9 +174,9 @@ var Segment = this.Segment = Base.extend(/** @lends Segment# */{
_isSelected: function(point) {
var state = this._selectionState;
return point == this._point ? !!(state & SelectionState.POINT)
: point == this._handleIn ? !!(state & SelectionState.HANDLE_IN)
: point == this._handleOut ? !!(state & SelectionState.HANDLE_OUT)
return point == this._point ? !!(state & /*#=*/ SelectionState.POINT)
: point == this._handleIn ? !!(state & /*#=*/ SelectionState.HANDLE_IN)
: point == this._handleOut ? !!(state & /*#=*/ SelectionState.HANDLE_OUT)
: false;
},
@ -187,9 +187,9 @@ var Segment = this.Segment = Base.extend(/** @lends Segment# */{
// For performance reasons use array indices to access the various
// selection states: 0 = point, 1 = handleIn, 2 = handleOut
selection = [
!!(state & SelectionState.POINT),
!!(state & SelectionState.HANDLE_IN),
!!(state & SelectionState.HANDLE_OUT)
!!(state & /*#=*/ SelectionState.POINT),
!!(state & /*#=*/ SelectionState.HANDLE_IN),
!!(state & /*#=*/ SelectionState.HANDLE_OUT)
];
if (point == this._point) {
if (selected) {
@ -215,9 +215,9 @@ var Segment = this.Segment = Base.extend(/** @lends Segment# */{
selection[index] = selected;
}
}
this._selectionState = (selection[0] ? SelectionState.POINT : 0)
| (selection[1] ? SelectionState.HANDLE_IN : 0)
| (selection[2] ? SelectionState.HANDLE_OUT : 0);
this._selectionState = (selection[0] ? /*#=*/ SelectionState.POINT : 0)
| (selection[1] ? /*#=*/ SelectionState.HANDLE_IN : 0)
| (selection[2] ? /*#=*/ SelectionState.HANDLE_OUT : 0);
// If the selection state of the segment has changed, we need to let
// it's path know and possibly add or remove it from
// project._selectedItems
@ -225,7 +225,7 @@ var Segment = this.Segment = Base.extend(/** @lends Segment# */{
path._updateSelection(this, state, this._selectionState);
// Let path know that we changed something and the view should be
// redrawn
path._changed(Change.ATTRIBUTE);
path._changed(/*#=*/ Change.ATTRIBUTE);
}
},

View file

@ -116,7 +116,7 @@ var Symbol = this.Symbol = Base.extend(/** @lends Symbol# */{
// Move position to 0, 0, so it's centered when placed.
item.setPosition(new Point());
item._parentSymbol = this;
this._changed(Change.GEOMETRY);
this._changed(/*#=*/ Change.GEOMETRY);
},
/**

View file

@ -45,9 +45,9 @@ var CharacterStyle = this.CharacterStyle = PathStyle.extend(/** @lends Character
font: 'sans-serif'
}),
_flags: {
fontSize: Change.GEOMETRY,
leading: Change.GEOMETRY,
font: Change.GEOMETRY
fontSize: /*#=*/ Change.GEOMETRY,
leading: /*#=*/ Change.GEOMETRY,
font: /*#=*/ Change.GEOMETRY
}
/**

View file

@ -37,7 +37,7 @@ var ParagraphStyle = this.ParagraphStyle = Style.extend(/** @lends ParagraphStyl
justification: 'left'
},
_flags: {
justification: Change.GEOMETRY
justification: /*#=*/ Change.GEOMETRY
}
/**

View file

@ -54,10 +54,10 @@ var PathStyle = this.PathStyle = Style.extend(/** @lends PathStyle# */{
dashArray: []
},
_flags: {
strokeWidth: Change.STROKE,
strokeCap: Change.STROKE,
strokeJoin: Change.STROKE,
miterLimit: Change.STROKE
strokeWidth: /*#=*/ Change.STROKE,
strokeCap: /*#=*/ Change.STROKE,
strokeJoin: /*#=*/ Change.STROKE,
miterLimit: /*#=*/ Change.STROKE
}
// DOCS: why isn't the example code showing up?

View file

@ -98,7 +98,7 @@ var Style = Item.extend({
// always set, additional flags come from _flags,
// as used for STROKE:
if (this._item)
this._item._changed(flags[key] || Change.STYLE);
this._item._changed(flags[key] || /*#=*/ Change.STYLE);
}
}
return this;

View file

@ -89,7 +89,7 @@ var TextItem = this.TextItem = Item.extend(/** @lends TextItem# */{
setContent: function(content) {
this._content = '' + content;
this._lines = this._content.split(/\r\n|\n|\r/mg);
this._changed(Change.CONTENT);
this._changed(/*#=*/ Change.CONTENT);
},
isEmpty: function() {