mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2025-01-05 20:32:00 -05:00
Finally found a better and faster alternative for this.base() calls, by setting base on the function object instead.
base can be accessed on named functions very easily, leading to another measurable speed increase. Finally all performance reasons against straps.js are eliminated!
This commit is contained in:
parent
369b329b23
commit
c533dda7b5
13 changed files with 55 additions and 79 deletions
|
@ -109,58 +109,34 @@ var Base = this.Base = new function() { // Straps scope
|
|||
// string values starting with '#'
|
||||
if (typeof val === 'string' && val[0] === '#')
|
||||
val = src[val.substring(1)] || val;
|
||||
var func = typeof val === 'function',
|
||||
var isFunc = typeof val === 'function',
|
||||
res = val,
|
||||
// Only lookup previous value if we preserve or define a
|
||||
// function that might need it for this.base(). If we're
|
||||
// defining a getter, don't lookup previous value, but look if
|
||||
// the property exists (name in dest) and store result in prev
|
||||
prev = preserve || func
|
||||
prev = preserve || isFunc
|
||||
? (val && val.get ? name in dest : dest[name]) : null;
|
||||
if ((dontCheck || val !== undefined && src.hasOwnProperty(name))
|
||||
&& (!preserve || !prev)) {
|
||||
if (func) {
|
||||
if (prev && /\bthis\.base\b/.test(val)) {
|
||||
var fromBase = base && base[name] == prev;
|
||||
res = function() {
|
||||
// Look up the base function each time if we can,
|
||||
// to reflect changes to the base class after
|
||||
// inheritance.
|
||||
var tmp = describe(this, 'base');
|
||||
define(this, 'base', { value: fromBase
|
||||
? base[name] : prev, configurable: true });
|
||||
try {
|
||||
return val.apply(this, arguments);
|
||||
} finally {
|
||||
tmp ? define(this, 'base', tmp)
|
||||
: delete this.base;
|
||||
}
|
||||
};
|
||||
// Make wrapping closure pretend to be the original
|
||||
// function on inspection
|
||||
res.toString = function() {
|
||||
return val.toString();
|
||||
};
|
||||
res.valueOf = function() {
|
||||
return val.valueOf();
|
||||
};
|
||||
}
|
||||
// Produce bean properties if getters are specified. This
|
||||
// does not produce properties for setter-only properties.
|
||||
// Just collect beans for now, and look them up in dest at
|
||||
// the end of fields injection. This ensures this.base()
|
||||
// works in beans too, and inherits setters for redefined
|
||||
// getters in subclasses. Only add getter beans if they do
|
||||
// not expect arguments. Functions that should function both
|
||||
// with optional arguments and as beans should not declare
|
||||
// the parameters and use the arguments array internally
|
||||
// instead.
|
||||
if (beans && val.length === 0
|
||||
&& (bean = name.match(/^(get|is)(([A-Z])(.*))$/)))
|
||||
beans.push([ bean[3].toLowerCase() + bean[4], bean[2] ]);
|
||||
}
|
||||
// Expose the 'super' function (meaning the one this function is
|
||||
// overriding) through #base:
|
||||
if (isFunc && prev)
|
||||
val.base = prev;
|
||||
// Produce bean properties if getters are specified. This does
|
||||
// not produce properties for setter-only properties. Just
|
||||
// collect beans for now, and look them up in dest at the end of
|
||||
// fields injection. This ensures base works for beans too, and
|
||||
// inherits setters for redefined getters in subclasses. Only
|
||||
// add getter beans if they do not expect arguments. Functions
|
||||
// that should function both with optional arguments and as
|
||||
// beans should not declare the parameters and use the arguments
|
||||
// array internally instead.
|
||||
if (isFunc && beans && val.length === 0
|
||||
&& (bean = name.match(/^(get|is)(([A-Z])(.*))$/)))
|
||||
beans.push([ bean[3].toLowerCase() + bean[4], bean[2] ]);
|
||||
// No need to look up getter if this is a function already.
|
||||
if (!res || func || !res.get)
|
||||
if (!res || isFunc || !res.get)
|
||||
res = { value: res, writable: true };
|
||||
// Only set/change configurable and enumerable if this field is
|
||||
// configurable
|
||||
|
@ -171,7 +147,7 @@ var Base = this.Base = new function() { // Straps scope
|
|||
}
|
||||
define(dest, name, res);
|
||||
}
|
||||
if (generics && func && (!preserve || !generics[name])) {
|
||||
if (generics && isFunc && (!preserve || !generics[name])) {
|
||||
generics[name] = function(bind) {
|
||||
// Do not call Array.slice generic here, as on Safari,
|
||||
// this seems to confuse scopes (calling another
|
||||
|
|
|
@ -82,11 +82,11 @@ this.Base = Base.inject(/** @lends Base# */{
|
|||
|
||||
_classes: {},
|
||||
|
||||
extend: function(src) {
|
||||
extend: function extend(src) {
|
||||
// Override Base.extend() with a version that registers classes that
|
||||
// define #_class inside the Base._classes lookup, for
|
||||
// deserialization.
|
||||
var res = this.base.apply(this, arguments);
|
||||
var res = extend.base.apply(this, arguments);
|
||||
if (src._class)
|
||||
Base._classes[src._class] = res;
|
||||
return res;
|
||||
|
|
|
@ -98,7 +98,7 @@ var Callback = {
|
|||
statics: {
|
||||
// Override inject() so that sub-classes automatically add the accessors
|
||||
// for the event handler functions (e.g. #onMouseDown) for each property
|
||||
inject: function(/* src, ... */) {
|
||||
inject: function inject(/* src, ... */) {
|
||||
for (var i = 0, l = arguments.length; i < l; i++) {
|
||||
var src = arguments[i],
|
||||
events = src._events;
|
||||
|
@ -132,7 +132,7 @@ var Callback = {
|
|||
});
|
||||
src._eventTypes = types;
|
||||
}
|
||||
this.base(src);
|
||||
inject.base.call(this, src);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -97,9 +97,8 @@ var Group = this.Group = Item.extend(/** @lends Group# */{
|
|||
this.addChildren(Array.isArray(arg) ? arg : arguments);
|
||||
},
|
||||
|
||||
_changed: function(flags) {
|
||||
// Don't use this.base() for reasons of performance.
|
||||
Item.prototype._changed.call(this, flags);
|
||||
_changed: function _changed(flags) {
|
||||
_changed.base.call(this, flags);
|
||||
if (flags & (/*#=*/ ChangeFlag.HIERARCHY | /*#=*/ ChangeFlag.CLIPPING)) {
|
||||
// Clear cached clip item whenever hierarchy changes
|
||||
delete this._clipItem;
|
||||
|
|
|
@ -26,14 +26,14 @@ var Item = this.Item = Base.extend(Callback, /** @lends Item# */{
|
|||
* Override Item.extend() to merge the subclass' _serializeFields with
|
||||
* the parent class' _serializeFields.
|
||||
*/
|
||||
extend: function(src) {
|
||||
extend: function extend(src) {
|
||||
if (src._serializeFields)
|
||||
src._serializeFields = Base.merge(
|
||||
this.prototype._serializeFields, src._serializeFields);
|
||||
// Derive the _type string from _class
|
||||
if (src._class)
|
||||
src._type = Base.hyphenate(src._class);
|
||||
return this.base.apply(this, arguments);
|
||||
return extend.base.apply(this, arguments);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -69,9 +69,9 @@ var Layer = this.Layer = Group.extend(/** @lends Layer# */{
|
|||
* Removes the layer from its project's layers list
|
||||
* or its parent's children list.
|
||||
*/
|
||||
_remove: function(notify) {
|
||||
_remove: function _remove(notify) {
|
||||
if (this._parent)
|
||||
return this.base(notify);
|
||||
return _remove.base.call(this, notify);
|
||||
if (this._index != null) {
|
||||
if (this._project.activeLayer === this)
|
||||
this._project.activeLayer = this.getNextSibling()
|
||||
|
@ -85,18 +85,18 @@ var Layer = this.Layer = Group.extend(/** @lends Layer# */{
|
|||
return false;
|
||||
},
|
||||
|
||||
getNextSibling: function() {
|
||||
return this._parent ? this.base()
|
||||
getNextSibling: function getNextSibling() {
|
||||
return this._parent ? getNextSibling.base.call(this)
|
||||
: this._project.layers[this._index + 1] || null;
|
||||
},
|
||||
|
||||
getPreviousSibling: function() {
|
||||
return this._parent ? this.base()
|
||||
getPreviousSibling: function getPreviousSibling() {
|
||||
return this._parent ? getPreviousSibling.base.call(this)
|
||||
: this._project.layers[this._index - 1] || null;
|
||||
},
|
||||
|
||||
isInserted: function() {
|
||||
return this._parent ? this.base() : this._index != null;
|
||||
isInserted: function isInserted() {
|
||||
return this._parent ? isInserted.base.call(this) : this._index != null;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -114,7 +114,7 @@ var Layer = this.Layer = Group.extend(/** @lends Layer# */{
|
|||
}
|
||||
}, new function () {
|
||||
function insert(above) {
|
||||
return function(item) {
|
||||
return function insert(item) {
|
||||
// If the item is a layer and contained within Project#layers, use
|
||||
// our own version of move().
|
||||
if (item instanceof Layer && !item._parent
|
||||
|
@ -124,7 +124,7 @@ var Layer = this.Layer = Group.extend(/** @lends Layer# */{
|
|||
this._setProject(item._project);
|
||||
return true;
|
||||
}
|
||||
return this.base(item);
|
||||
return insert.base.call(this, item);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -67,10 +67,10 @@ var Shape = this.Shape = Item.extend(/** @lends Shape# */{
|
|||
}
|
||||
},
|
||||
|
||||
_contains: function(point) {
|
||||
_contains: function _contains(point) {
|
||||
switch (this._type) {
|
||||
case 'rect':
|
||||
return this.base(point);
|
||||
return _contains.base.call(this, point);
|
||||
case 'circle':
|
||||
case 'ellipse':
|
||||
return point.divide(this._size).getLength() <= 0.5;
|
||||
|
|
|
@ -58,11 +58,11 @@ var CompoundPath = this.CompoundPath = PathItem.extend(/** @lends CompoundPath#
|
|||
this.addChildren(Array.isArray(arg) ? arg : arguments);
|
||||
},
|
||||
|
||||
insertChild: function(index, item, _preserve) {
|
||||
insertChild: function insertChild(index, item, _preserve) {
|
||||
// Only allow the insertion of paths
|
||||
if (item._type !== 'path')
|
||||
return null;
|
||||
item = this.base(index, item);
|
||||
item = insertChild.base.call(this, index, item);
|
||||
// All children except for the bottom one (first one in list) are set
|
||||
// to anti-clockwise orientation, so that they appear as holes, but
|
||||
// only if their orientation was not already specified before
|
||||
|
@ -223,8 +223,9 @@ var CompoundPath = this.CompoundPath = PathItem.extend(/** @lends CompoundPath#
|
|||
return (children.length & 1) == 1 && children;
|
||||
},
|
||||
|
||||
_hitTest: function(point, options) {
|
||||
var res = this.base(point, Base.merge(options, { fill: false }));
|
||||
_hitTest: function _hitTest(point, options) {
|
||||
var res = _hitTest.base.call(this, point,
|
||||
Base.merge(options, { fill: false }));
|
||||
if (!res && options.fill && this._style.getFillColor()) {
|
||||
res = this._contains(point);
|
||||
res = res ? new HitResult('fill', res[0]) : null;
|
||||
|
|
|
@ -788,12 +788,12 @@ var Path = this.Path = PathItem.extend(/** @lends Path# */{
|
|||
this.setSelected(selected);
|
||||
},
|
||||
|
||||
setSelected: function(selected) {
|
||||
setSelected: function setSelected(selected) {
|
||||
// Deselect all segments when path is marked as not selected
|
||||
if (!selected)
|
||||
this._selectSegments(false);
|
||||
// No need to pass true for noChildren since Path has none anyway.
|
||||
this.base(selected);
|
||||
setSelected.base.call(this, selected);
|
||||
},
|
||||
|
||||
_selectSegments: function(selected) {
|
||||
|
|
|
@ -97,8 +97,8 @@ var Project = this.Project = PaperScopeItem.extend(/** @lends Project# */{
|
|||
* Removes this project from the {@link PaperScope#projects} list, and also
|
||||
* removes its view, if one was defined.
|
||||
*/
|
||||
remove: function() {
|
||||
if (!this.base())
|
||||
remove: function remove() {
|
||||
if (!remove.base.call(this))
|
||||
return false;
|
||||
if (this.view)
|
||||
this.view.remove();
|
||||
|
|
|
@ -223,9 +223,9 @@ var Style = this.Style = Base.extend(new function() {
|
|||
}
|
||||
},
|
||||
|
||||
getLeading: function() {
|
||||
getLeading: function getLeading() {
|
||||
// Override leading to return fontSize * 1.2 by default.
|
||||
var leading = this.base();
|
||||
var leading = getLeading.base.call(this);
|
||||
return leading != null ? leading : this.getFontSize() * 1.2;
|
||||
},
|
||||
|
||||
|
|
|
@ -77,9 +77,9 @@ var TextItem = this.TextItem = Item.extend(/** @lends TextItem# */{
|
|||
* }
|
||||
*/
|
||||
|
||||
_clone: function(copy) {
|
||||
_clone: function _clone(copy) {
|
||||
copy.setContent(this._content);
|
||||
return this.base(copy);
|
||||
return _clone.base.call(this, copy);
|
||||
},
|
||||
|
||||
getContent: function() {
|
||||
|
|
|
@ -322,7 +322,7 @@ var Tool = this.Tool = PaperScopeItem.extend(/** @lends Tool# */{
|
|||
return true;
|
||||
},
|
||||
|
||||
fire: function(type, event) {
|
||||
fire: function fire(type, event) {
|
||||
// Override Callback#fire() so we can handle items marked in removeOn*()
|
||||
// calls first,.
|
||||
var sets = Tool._removeSets;
|
||||
|
@ -346,7 +346,7 @@ var Tool = this.Tool = PaperScopeItem.extend(/** @lends Tool# */{
|
|||
sets[type] = null;
|
||||
}
|
||||
}
|
||||
return this.base(type, event);
|
||||
return fire.base.call(this, type, event);
|
||||
},
|
||||
|
||||
_onHandleEvent: function(type, point, event) {
|
||||
|
|
Loading…
Reference in a new issue