2011-03-06 19:50:44 -05:00
|
|
|
/*
|
|
|
|
* 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.
|
2011-03-07 20:41:50 -05:00
|
|
|
* http://paperjs.org/
|
2011-03-06 19:50:44 -05:00
|
|
|
* http://scriptographer.org/
|
|
|
|
*
|
2011-03-07 20:41:50 -05:00
|
|
|
* Distributed under the MIT license. See LICENSE file for details.
|
|
|
|
*
|
2011-03-06 19:50:44 -05:00
|
|
|
* Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
|
|
|
|
* http://lehni.org/ & http://jonathanpuckey.com/
|
|
|
|
*
|
2011-03-07 20:41:50 -05:00
|
|
|
* All rights reserved.
|
2011-03-06 19:50:44 -05:00
|
|
|
*/
|
|
|
|
|
2011-03-04 08:34:31 -05:00
|
|
|
var Item = this.Item = Base.extend({
|
2011-02-11 12:36:03 -05:00
|
|
|
beans: true,
|
2011-02-13 13:51:49 -05:00
|
|
|
|
2011-02-07 13:28:09 -05:00
|
|
|
initialize: function() {
|
2011-05-16 08:33:15 -04:00
|
|
|
paper.project.activeLayer.appendTop(this);
|
|
|
|
this.setStyle(this._project.getCurrentStyle());
|
2011-02-11 12:36:03 -05:00
|
|
|
},
|
2011-03-04 15:57:16 -05:00
|
|
|
|
2011-05-07 09:57:20 -04:00
|
|
|
/**
|
|
|
|
* Private notifier that is called whenever a change occurs in this item or
|
|
|
|
* its sub-elements, such as Segments, Curves, PathStyles, etc.
|
|
|
|
*
|
|
|
|
* @param {ChangeFlags} flags describes what exactly has changed.
|
|
|
|
*/
|
2011-05-07 08:39:17 -04:00
|
|
|
_changed: function(flags) {
|
2011-05-15 19:01:06 -04:00
|
|
|
if (flags & ChangeFlags.GEOMETRY) {
|
2011-05-07 08:39:17 -04:00
|
|
|
delete this._position;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2011-05-14 17:38:27 -04:00
|
|
|
/**
|
|
|
|
* The unique id of the item.
|
|
|
|
*/
|
|
|
|
getId: function() {
|
|
|
|
if (this._id == null) {
|
|
|
|
this._id = Item._id = (Item._id || 0) + 1;
|
|
|
|
}
|
|
|
|
return this._id;
|
2011-05-15 06:32:09 -04:00
|
|
|
},
|
2011-05-15 13:12:27 -04:00
|
|
|
|
|
|
|
_removeFromNamed: function() {
|
2011-05-15 13:27:32 -04:00
|
|
|
var children = this._parent._children,
|
|
|
|
namedChildren = this._parent._namedChildren,
|
|
|
|
name = this._name,
|
|
|
|
namedArray = namedChildren[name];
|
2011-05-15 13:12:27 -04:00
|
|
|
if (children[name] = this)
|
|
|
|
delete children[name];
|
|
|
|
namedArray.splice(namedArray.indexOf(this), 1);
|
|
|
|
// If there are any items left in the named array, set
|
|
|
|
// the last of them to be this.parent.children[this.name]
|
|
|
|
if (namedArray.length) {
|
|
|
|
children[name] = namedArray[namedArray.length - 1];
|
|
|
|
} else {
|
|
|
|
// Otherwise delete the empty array
|
|
|
|
delete namedChildren[name];
|
|
|
|
}
|
|
|
|
},
|
2011-05-15 13:53:09 -04:00
|
|
|
|
2011-05-15 13:12:27 -04:00
|
|
|
/**
|
2011-05-15 13:53:09 -04:00
|
|
|
* The name of the item.
|
|
|
|
*/
|
2011-05-15 13:12:27 -04:00
|
|
|
getName: function() {
|
2011-05-15 13:27:32 -04:00
|
|
|
return this._name;
|
2011-05-15 13:12:27 -04:00
|
|
|
},
|
2011-05-15 13:53:09 -04:00
|
|
|
|
2011-05-15 13:12:27 -04:00
|
|
|
setName: function(name) {
|
2011-05-15 13:27:32 -04:00
|
|
|
var children = this._parent._children,
|
|
|
|
namedChildren = this._parent._namedChildren;
|
|
|
|
if (name != this._name) {
|
2011-05-15 13:12:27 -04:00
|
|
|
// If the item already had a name,
|
|
|
|
// remove its property from the parent's children object:
|
|
|
|
if (this._name)
|
|
|
|
this._removeFromNamed();
|
2011-05-15 13:27:32 -04:00
|
|
|
this._name = name || undefined;
|
2011-05-15 13:12:27 -04:00
|
|
|
}
|
|
|
|
if (name) {
|
2011-05-15 13:27:32 -04:00
|
|
|
(namedChildren[name] = namedChildren[name] || []).push(this);
|
2011-05-15 13:12:27 -04:00
|
|
|
children[name] = this;
|
|
|
|
} else {
|
2011-05-15 13:53:09 -04:00
|
|
|
delete children[name];
|
2011-05-15 13:12:27 -04:00
|
|
|
}
|
|
|
|
},
|
2011-05-14 17:38:27 -04:00
|
|
|
|
2011-02-12 11:43:51 -05:00
|
|
|
/**
|
2011-05-16 08:33:15 -04:00
|
|
|
* When passed a project, copies the item to the project,
|
|
|
|
* or duplicates it within the same project. When passed an item,
|
2011-02-12 11:43:51 -05:00
|
|
|
* copies the item into the specified item.
|
|
|
|
*
|
2011-05-16 08:33:15 -04:00
|
|
|
* @param project the project to copy the item to
|
2011-02-12 11:43:51 -05:00
|
|
|
* @return the new copy of the item
|
|
|
|
*/
|
2011-05-16 08:33:15 -04:00
|
|
|
copyTo: function(itemOrProject) {
|
2011-03-04 15:57:16 -05:00
|
|
|
var copy = this.clone();
|
2011-05-16 08:33:15 -04:00
|
|
|
if (itemOrProject.layers) {
|
|
|
|
itemOrProject.activeLayer.appendTop(copy);
|
2011-02-12 11:43:51 -05:00
|
|
|
} else {
|
2011-05-16 08:33:15 -04:00
|
|
|
itemOrProject.appendTop(copy);
|
2011-02-12 11:43:51 -05:00
|
|
|
}
|
|
|
|
return copy;
|
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-12 11:43:51 -05:00
|
|
|
/**
|
2011-05-16 08:33:15 -04:00
|
|
|
* Clones the item within the same project.
|
2011-02-12 11:43:51 -05:00
|
|
|
*
|
|
|
|
* @return the newly cloned item
|
|
|
|
*/
|
|
|
|
clone: function() {
|
2011-03-04 15:57:16 -05:00
|
|
|
var copy = new this.constructor();
|
|
|
|
// TODO: Copy children and other things.
|
2011-05-14 12:56:14 -04:00
|
|
|
if (this._parent) {
|
|
|
|
this._parent.appendTop(copy);
|
2011-05-07 10:40:02 -04:00
|
|
|
}
|
2011-03-04 15:57:16 -05:00
|
|
|
return copy;
|
2011-02-12 11:43:51 -05:00
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-04-17 12:46:35 -04:00
|
|
|
setSelected: function(selected) {
|
2011-05-14 13:07:10 -04:00
|
|
|
if (this._children) {
|
|
|
|
for (var i = 0, l = this._children.length; i < l; i++) {
|
|
|
|
this._children[i].setSelected(selected);
|
2011-04-17 12:46:35 -04:00
|
|
|
}
|
|
|
|
} else {
|
2011-04-28 05:05:43 -04:00
|
|
|
if ((selected = !!selected) != this._selected) {
|
2011-04-21 09:47:00 -04:00
|
|
|
// TODO: when an item is removed or moved to another
|
2011-05-16 08:33:15 -04:00
|
|
|
// project, it needs to be removed from _selectedItems
|
2011-04-21 09:47:00 -04:00
|
|
|
this._selected = selected;
|
2011-05-16 08:33:15 -04:00
|
|
|
this._project._selectItem(this, selected);
|
2011-04-21 09:47:00 -04:00
|
|
|
}
|
2011-04-17 12:46:35 -04:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2011-04-21 13:51:49 -04:00
|
|
|
isSelected: function() {
|
2011-05-14 13:07:10 -04:00
|
|
|
if (this._children) {
|
|
|
|
for (var i = 0, l = this._children.length; i < l; i++) {
|
|
|
|
if (this._children[i].isSelected()) {
|
2011-04-17 12:46:35 -04:00
|
|
|
return true;
|
2011-05-07 10:40:02 -04:00
|
|
|
}
|
2011-04-17 12:46:35 -04:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return !!this._selected;
|
|
|
|
}
|
2011-04-21 09:47:00 -04:00
|
|
|
return false;
|
|
|
|
},
|
|
|
|
|
2011-05-16 08:33:15 -04:00
|
|
|
getProject: function() {
|
|
|
|
return this._project;
|
2011-04-21 09:47:00 -04:00
|
|
|
},
|
2011-05-07 05:07:21 -04:00
|
|
|
|
2011-05-16 08:33:15 -04:00
|
|
|
_setProject: function(project) {
|
|
|
|
if (this._project != project) {
|
|
|
|
this._project = project;
|
2011-05-14 13:07:10 -04:00
|
|
|
if (this._children) {
|
|
|
|
for (var i = 0, l = this._children.length; i < l; i++) {
|
2011-05-16 08:33:15 -04:00
|
|
|
this._children[i]._setProject(project);
|
2011-05-07 10:40:02 -04:00
|
|
|
}
|
2011-04-21 09:47:00 -04:00
|
|
|
}
|
|
|
|
}
|
2011-04-17 12:46:35 -04:00
|
|
|
},
|
|
|
|
|
2011-02-24 11:42:32 -05:00
|
|
|
// TODO: isFullySelected / setFullySelected
|
2011-05-14 12:57:23 -04:00
|
|
|
// TODO: Change to getter / setters for these below that notify of changes
|
|
|
|
// through _changed()
|
2011-02-11 12:36:03 -05:00
|
|
|
/**
|
|
|
|
* Specifies whether the item is locked.
|
|
|
|
*
|
2011-05-15 10:06:10 -04:00
|
|
|
* @return true if the item is locked, false otherwise.
|
2011-02-11 12:36:03 -05:00
|
|
|
*/
|
|
|
|
locked: false,
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-11 12:36:03 -05:00
|
|
|
/**
|
|
|
|
* Specifies whether the item is visible.
|
|
|
|
*
|
2011-05-15 10:06:10 -04:00
|
|
|
* @return true if the item is visible, false otherwise.
|
2011-02-11 12:36:03 -05:00
|
|
|
*/
|
|
|
|
visible: true,
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-11 12:36:03 -05:00
|
|
|
/**
|
|
|
|
* The opacity of the item.
|
|
|
|
*
|
|
|
|
* @return the opacity of the item as a value between 0 and 1.
|
|
|
|
*/
|
|
|
|
opacity: 1,
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-25 06:46:45 -05:00
|
|
|
/**
|
|
|
|
* The blend mode of the item.
|
|
|
|
*/
|
|
|
|
blendMode: 'normal',
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-11 12:36:03 -05:00
|
|
|
/**
|
|
|
|
* Specifies whether the item defines a clip mask. This can only be set on
|
|
|
|
* paths, compound paths, and text frame objects, and only if the item is
|
|
|
|
* already contained within a clipping group.
|
|
|
|
*
|
2011-05-15 10:06:10 -04:00
|
|
|
* @return true if the item defines a clip mask, false otherwise.
|
2011-02-11 12:36:03 -05:00
|
|
|
*/
|
|
|
|
isClipMask: function() {
|
2011-02-15 06:05:39 -05:00
|
|
|
return this._clipMask;
|
2011-02-11 12:36:03 -05:00
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-11 12:36:03 -05:00
|
|
|
setClipMask: function(clipMask) {
|
2011-02-15 06:05:39 -05:00
|
|
|
this._clipMask = clipMask;
|
|
|
|
if (this._clipMask) {
|
2011-03-04 20:36:27 -05:00
|
|
|
this.setFillColor(null);
|
|
|
|
this.setStrokeColor(null);
|
2011-02-11 12:36:03 -05:00
|
|
|
}
|
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-05-14 13:07:10 -04:00
|
|
|
// TODO: get/setIsolated (print specific feature)
|
2011-02-24 11:42:32 -05:00
|
|
|
// TODO: get/setKnockout (print specific feature)
|
|
|
|
// TODO get/setAlphaIsShape
|
|
|
|
// TODO: get/setData
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-05-14 13:07:10 -04:00
|
|
|
/**
|
|
|
|
* The item that this item is contained within.
|
|
|
|
*/
|
|
|
|
getParent: function() {
|
|
|
|
return this._parent;
|
|
|
|
},
|
|
|
|
|
|
|
|
// TODO: #getLayer()
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The index of this item within the list of it's parent's children.
|
|
|
|
*/
|
|
|
|
getIndex: function() {
|
|
|
|
return this._index;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The children items contained within this item.
|
|
|
|
*/
|
|
|
|
getChildren: function() {
|
|
|
|
return this._children;
|
|
|
|
},
|
|
|
|
|
|
|
|
// TODO: #setChildren()
|
|
|
|
|
2011-02-24 13:31:07 -05:00
|
|
|
/**
|
|
|
|
* Reverses the order of this item's children
|
|
|
|
*/
|
|
|
|
reverseChildren: function() {
|
2011-05-14 13:07:10 -04:00
|
|
|
if (this._children) {
|
|
|
|
this._children.reverse();
|
2011-05-14 13:07:45 -04:00
|
|
|
for (var i = 0, l = this._children.length; i < l; i++) {
|
|
|
|
this._children[i]._index = i;
|
|
|
|
}
|
2011-05-07 05:07:21 -04:00
|
|
|
}
|
2011-02-24 13:31:07 -05:00
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-11 12:36:03 -05:00
|
|
|
/**
|
|
|
|
* The first item contained within this item.
|
|
|
|
*/
|
|
|
|
getFirstChild: function() {
|
2011-05-14 13:07:10 -04:00
|
|
|
return this._children && this._children[0] || null;
|
2011-02-11 12:36:03 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The last item contained within this item.
|
|
|
|
*/
|
|
|
|
getLastChild: function() {
|
2011-05-15 10:01:59 -04:00
|
|
|
return this._children && this._children[this._children.length - 1]
|
|
|
|
|| null;
|
2011-02-11 12:36:03 -05:00
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-11 12:36:03 -05:00
|
|
|
/**
|
|
|
|
* The next item on the same level as this item.
|
|
|
|
*/
|
|
|
|
getNextSibling: function() {
|
2011-05-14 13:07:10 -04:00
|
|
|
return this._parent && this._parent._children[this._index + 1] || null;
|
2011-02-11 12:36:03 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The previous item on the same level as this item.
|
|
|
|
*/
|
|
|
|
getPreviousSibling: function() {
|
2011-05-14 13:07:10 -04:00
|
|
|
return this._parent && this._parent._children[this._index - 1] || null;
|
2011-02-11 12:36:03 -05:00
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-11 12:36:03 -05:00
|
|
|
/**
|
|
|
|
* Removes the item from its parent's children list.
|
|
|
|
*/
|
2011-05-07 09:50:48 -04:00
|
|
|
_removeFromParent: function() {
|
2011-05-14 12:56:14 -04:00
|
|
|
if (this._parent) {
|
2011-05-15 13:12:27 -04:00
|
|
|
if (this._name)
|
|
|
|
this._removeFromNamed();
|
2011-05-14 13:11:31 -04:00
|
|
|
var res = Base.splice(this._parent._children, null, this._index, 1);
|
2011-05-15 16:42:31 -04:00
|
|
|
this._parent = null;
|
2011-05-14 13:11:31 -04:00
|
|
|
return !!res.length;
|
2011-03-04 16:34:14 -05:00
|
|
|
}
|
|
|
|
return false;
|
2011-02-11 12:36:03 -05:00
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-12 10:20:10 -05:00
|
|
|
/**
|
|
|
|
* Removes the item.
|
2011-03-03 17:45:17 -05:00
|
|
|
*/
|
2011-02-12 10:20:10 -05:00
|
|
|
remove: function() {
|
2011-04-28 06:50:53 -04:00
|
|
|
if (this.isSelected())
|
2011-04-21 09:47:00 -04:00
|
|
|
this.setSelected(false);
|
2011-05-07 09:50:48 -04:00
|
|
|
return this._removeFromParent();
|
2011-02-12 10:20:10 -05:00
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-12 11:43:51 -05:00
|
|
|
/**
|
|
|
|
* Checks if the item contains any children items.
|
|
|
|
*
|
2011-05-15 10:06:10 -04:00
|
|
|
* @return true if it has one or more children, false otherwise.
|
2011-02-12 11:43:51 -05:00
|
|
|
*/
|
|
|
|
hasChildren: function() {
|
2011-05-14 13:07:10 -04:00
|
|
|
return this._children && this._children.length > 0;
|
2011-02-12 11:43:51 -05:00
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-12 11:43:51 -05:00
|
|
|
/**
|
|
|
|
* Checks whether the item is editable.
|
|
|
|
*
|
2011-05-15 10:06:10 -04:00
|
|
|
* @return true when neither the item, nor it's parents are locked or
|
|
|
|
* hidden, false otherwise.
|
2011-02-12 11:43:51 -05:00
|
|
|
*/
|
|
|
|
isEditable: function() {
|
|
|
|
var parent = this;
|
2011-04-12 08:37:52 -04:00
|
|
|
while (parent) {
|
2011-02-13 11:26:24 -05:00
|
|
|
if (parent.hidden || parent.locked)
|
2011-02-12 11:43:51 -05:00
|
|
|
return false;
|
2011-05-14 12:56:14 -04:00
|
|
|
parent = parent._parent;
|
2011-02-12 11:43:51 -05:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-24 11:42:32 -05:00
|
|
|
/**
|
|
|
|
* Checks whether the item is valid, i.e. it hasn't been removed.
|
|
|
|
*
|
2011-05-15 10:06:10 -04:00
|
|
|
* @return true if the item is valid, false otherwise.
|
2011-02-24 11:42:32 -05:00
|
|
|
*/
|
|
|
|
// TODO: isValid / checkValid
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-24 11:42:32 -05:00
|
|
|
/**
|
|
|
|
* Checks if this item is above the specified item in the stacking order of
|
2011-05-16 08:33:15 -04:00
|
|
|
* the project.
|
2011-02-24 11:42:32 -05:00
|
|
|
*
|
|
|
|
* @param item The item to check against
|
2011-05-15 10:06:10 -04:00
|
|
|
* @return true if it is above the specified item, false otherwise.
|
2011-02-24 11:42:32 -05:00
|
|
|
*/
|
|
|
|
// TODO: isAbove
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-24 11:42:32 -05:00
|
|
|
/**
|
|
|
|
* Checks if the item is below the specified item in the stacking order of
|
2011-05-16 08:33:15 -04:00
|
|
|
* the project.
|
2011-02-24 11:42:32 -05:00
|
|
|
*
|
|
|
|
* @param item The item to check against
|
2011-05-15 10:06:10 -04:00
|
|
|
* @return true if it is below the specified item, false otherwise.
|
2011-02-24 11:42:32 -05:00
|
|
|
*/
|
|
|
|
// TODO: isBelow
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-04-11 13:42:03 -04:00
|
|
|
isParent: function(item) {
|
2011-05-14 12:56:14 -04:00
|
|
|
return this._parent == item;
|
2011-04-11 13:42:03 -04:00
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-11 12:36:03 -05:00
|
|
|
isChild: function(item) {
|
2011-05-14 12:56:14 -04:00
|
|
|
return item._parent == this;
|
2011-02-11 12:36:03 -05:00
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-11 12:36:03 -05:00
|
|
|
/**
|
|
|
|
* Checks if the item is contained within the specified item.
|
|
|
|
*
|
|
|
|
* @param item The item to check against
|
2011-05-15 10:06:10 -04:00
|
|
|
* @return true if it is inside the specified item, false otherwise.
|
2011-02-11 12:36:03 -05:00
|
|
|
*/
|
|
|
|
isDescendant: function(item) {
|
2011-04-27 10:16:05 -04:00
|
|
|
var parent = this;
|
2011-05-14 12:56:14 -04:00
|
|
|
while (parent = parent._parent) {
|
2011-02-13 11:26:24 -05:00
|
|
|
if (parent == item)
|
2011-02-12 10:41:57 -05:00
|
|
|
return true;
|
2011-02-11 12:36:03 -05:00
|
|
|
}
|
2011-02-12 10:41:57 -05:00
|
|
|
return false;
|
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-12 10:41:57 -05:00
|
|
|
/**
|
|
|
|
* Checks if the item is an ancestor of the specified item.
|
|
|
|
*
|
|
|
|
* @param item the item to check against
|
2011-05-15 10:06:10 -04:00
|
|
|
* @return true if the item is an ancestor of the specified item, false otherwise.
|
2011-02-12 10:41:57 -05:00
|
|
|
*/
|
|
|
|
isAncestor: function(item) {
|
2011-04-27 10:16:05 -04:00
|
|
|
var parent = item;
|
2011-05-14 12:56:14 -04:00
|
|
|
while (parent = parent._parent) {
|
2011-02-13 11:26:24 -05:00
|
|
|
if (parent == this)
|
2011-02-12 10:41:57 -05:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2011-02-13 20:05:16 -05:00
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-24 11:42:32 -05:00
|
|
|
/**
|
|
|
|
* Checks whether the item is grouped with the specified item.
|
|
|
|
*
|
|
|
|
* @param item
|
2011-05-15 10:06:10 -04:00
|
|
|
* @return true if the items are grouped together, false otherwise.
|
2011-02-24 11:42:32 -05:00
|
|
|
*/
|
2011-02-24 12:09:48 -05:00
|
|
|
isGroupedWith: function(item) {
|
2011-05-14 12:56:14 -04:00
|
|
|
var parent = this._parent;
|
2011-04-12 08:37:52 -04:00
|
|
|
while (parent) {
|
2011-05-14 12:56:14 -04:00
|
|
|
// Find group parents. Check for parent._parent, since don't want
|
2011-02-24 12:09:48 -05:00
|
|
|
// top level layers, because they also inherit from Group
|
2011-05-14 12:56:14 -04:00
|
|
|
if (parent._parent
|
2011-02-24 12:09:48 -05:00
|
|
|
&& (parent instanceof Group || parent instanceof CompoundPath)
|
|
|
|
&& item.isDescendant(parent))
|
|
|
|
return true;
|
|
|
|
// Keep walking up otherwise
|
2011-05-14 12:56:14 -04:00
|
|
|
parent = parent._parent;
|
2011-02-24 12:09:48 -05:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
},
|
2011-04-28 06:33:03 -04:00
|
|
|
|
|
|
|
getStrokeBounds: function() {
|
|
|
|
return this._getBounds(true);
|
|
|
|
},
|
|
|
|
|
2011-02-13 20:05:16 -05:00
|
|
|
getBounds: function() {
|
2011-04-28 06:33:03 -04:00
|
|
|
return this._getBounds(false);
|
|
|
|
},
|
|
|
|
|
|
|
|
_getBounds: function(includeStroke) {
|
2011-05-14 13:07:10 -04:00
|
|
|
var children = this._children;
|
2011-04-28 06:33:03 -04:00
|
|
|
if (children && children.length) {
|
2011-05-04 19:16:28 -04:00
|
|
|
var x1 = Infinity,
|
|
|
|
x2 = -Infinity,
|
|
|
|
y1 = x1,
|
|
|
|
y2 = x2;
|
2011-04-28 06:33:03 -04:00
|
|
|
for (var i = 0, l = children.length; i < l; i++) {
|
2011-05-08 12:17:54 -04:00
|
|
|
var child = children[i];
|
|
|
|
if (child.visible) {
|
|
|
|
var rect = includeStroke
|
|
|
|
? child.getStrokeBounds()
|
|
|
|
: child.getBounds();
|
|
|
|
x1 = Math.min(rect.x, x1);
|
|
|
|
y1 = Math.min(rect.y, y1);
|
|
|
|
x2 = Math.max(rect.x + rect.width, x2);
|
|
|
|
y2 = Math.max(rect.y + rect.height, y2);
|
|
|
|
}
|
2011-04-28 05:04:36 -04:00
|
|
|
}
|
2011-04-28 06:38:57 -04:00
|
|
|
return includeStroke
|
2011-04-28 06:39:55 -04:00
|
|
|
? Rectangle.create(x1, y1, x2 - x1, y2 - y1)
|
2011-04-28 06:38:57 -04:00
|
|
|
: LinkedRectangle.create(this, 'setBounds',
|
|
|
|
x1, y1, x2 - x1, y2 - y1);
|
2011-04-28 05:04:36 -04:00
|
|
|
}
|
|
|
|
// TODO: What to return if nothing is defined, e.g. empty Groups?
|
|
|
|
// Scriptographer behaves weirdly then too.
|
2011-02-13 20:05:16 -05:00
|
|
|
return new Rectangle();
|
|
|
|
},
|
|
|
|
|
|
|
|
setBounds: function(rect) {
|
|
|
|
rect = Rectangle.read(arguments);
|
2011-03-13 13:31:00 -04:00
|
|
|
var bounds = this.getBounds(),
|
|
|
|
matrix = new Matrix(),
|
|
|
|
center = rect.center;
|
2011-02-13 20:05:16 -05:00
|
|
|
// Read this from bottom to top:
|
|
|
|
// Translate to new center:
|
|
|
|
matrix.translate(center);
|
|
|
|
// Scale to new Size, if size changes and avoid divisions by 0:
|
|
|
|
if (rect.width != bounds.width || rect.height != bounds.height) {
|
|
|
|
matrix.scale(
|
|
|
|
bounds.width != 0 ? rect.width / bounds.width : 1,
|
|
|
|
bounds.height != 0 ? rect.height / bounds.height : 1);
|
|
|
|
}
|
|
|
|
// Translate to center:
|
|
|
|
center = bounds.center;
|
|
|
|
matrix.translate(-center.x, -center.y);
|
|
|
|
// Now execute the transformation:
|
2011-02-13 20:14:43 -05:00
|
|
|
this.transform(matrix);
|
2011-02-13 20:05:16 -05:00
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-24 11:42:32 -05:00
|
|
|
/**
|
|
|
|
* The bounding rectangle of the item including stroke width.
|
|
|
|
*/
|
|
|
|
// TODO: getStrokeBounds
|
2011-02-13 20:05:16 -05:00
|
|
|
|
2011-02-24 11:42:32 -05:00
|
|
|
/**
|
|
|
|
* The bounding rectangle of the item including stroke width and controls.
|
|
|
|
*/
|
|
|
|
// TODO: getControlBounds
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-24 11:42:32 -05:00
|
|
|
/**
|
|
|
|
* Rasterizes the item into a newly created Raster object. The item itself
|
|
|
|
* is not removed after rasterization.
|
|
|
|
*
|
|
|
|
* @param resolution the resolution of the raster in dpi {@default 72}
|
|
|
|
* @return the newly created Raster item
|
|
|
|
*/
|
2011-02-24 13:22:19 -05:00
|
|
|
rasterize: function(resolution) {
|
|
|
|
// TODO: why would we want to pass a size to rasterize? Seems to produce
|
|
|
|
// weird results on Scriptographer. Also we can't use antialiasing, since
|
2011-05-16 08:33:15 -04:00
|
|
|
// Canvas doesn't support it yet. Project colorMode is also out of the
|
2011-02-24 13:22:19 -05:00
|
|
|
// question for now.
|
2011-04-28 06:50:53 -04:00
|
|
|
var bounds = this.getStrokeBounds(),
|
|
|
|
scale = (resolution || 72) / 72,
|
|
|
|
canvas = CanvasProvider.getCanvas(bounds.getSize().multiply(scale)),
|
|
|
|
ctx = canvas.getContext('2d'),
|
|
|
|
matrix = new Matrix().scale(scale).translate(-bounds.x, -bounds.y);
|
2011-04-26 10:39:16 -04:00
|
|
|
matrix.applyToContext(ctx);
|
|
|
|
this.draw(ctx, {});
|
2011-02-24 13:22:19 -05:00
|
|
|
var raster = new Raster(canvas);
|
2011-05-07 06:25:46 -04:00
|
|
|
raster.setPosition(this.getPosition());
|
2011-02-24 13:22:19 -05:00
|
|
|
raster.scale(1 / scale);
|
|
|
|
return raster;
|
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-13 20:05:16 -05:00
|
|
|
/**
|
|
|
|
* The item's position within the art board. This is the
|
|
|
|
* {@link Rectangle#getCenter()} of the {@link Item#getBounds()} rectangle.
|
|
|
|
*/
|
|
|
|
getPosition: function() {
|
2011-05-07 08:13:19 -04:00
|
|
|
// Cache position value
|
2011-05-16 06:19:19 -04:00
|
|
|
if (!this._position) {
|
|
|
|
// Center is a LinkedPoint as well, so we can use _x and _y
|
|
|
|
var center = this.getBounds().getCenter();
|
|
|
|
this._position = LinkedPoint.create(this, 'setPosition',
|
|
|
|
center._x, center._y);
|
|
|
|
}
|
|
|
|
return this._position;
|
2011-02-13 20:05:16 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
setPosition: function(point) {
|
2011-02-13 20:17:43 -05:00
|
|
|
point = Point.read(arguments);
|
2011-05-07 08:13:19 -04:00
|
|
|
if (point)
|
2011-03-06 16:12:11 -05:00
|
|
|
this.translate(point.subtract(this.getPosition()));
|
2011-02-13 20:05:16 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param flags: Array of any of the following: 'objects', 'children',
|
|
|
|
* 'fill-gradients', 'fill-patterns', 'stroke-patterns', 'lines'.
|
|
|
|
* Default: ['objects', 'children']
|
|
|
|
*/
|
|
|
|
transform: function(matrix, flags) {
|
|
|
|
// TODO: Handle flags, add TransformFlag class and convert to bit mask
|
|
|
|
// for quicker checking
|
2011-02-16 16:35:53 -05:00
|
|
|
// TODO: Call transform on chidren only if 'children' flag is provided
|
2011-03-03 07:47:55 -05:00
|
|
|
if (this._transform)
|
|
|
|
this._transform(matrix, flags);
|
2011-05-16 06:19:19 -04:00
|
|
|
// Transform position as well. Do not modify _position directly,
|
|
|
|
// since it's a LinkedPoint and would cause recursion!
|
2011-05-07 08:13:19 -04:00
|
|
|
if (this._position)
|
2011-05-16 06:19:19 -04:00
|
|
|
matrix._transformPoint(this._position, this._position, true);
|
2011-05-14 13:07:10 -04:00
|
|
|
if (this._children) {
|
|
|
|
for (var i = 0, l = this._children.length; i < l; i++) {
|
|
|
|
var child = this._children[i];
|
2011-02-13 20:05:16 -05:00
|
|
|
child.transform(matrix, flags);
|
|
|
|
}
|
|
|
|
}
|
2011-05-07 09:18:27 -04:00
|
|
|
// PORT: Return 'this' in all chainable commands
|
2011-03-06 06:42:08 -05:00
|
|
|
return this;
|
2011-02-13 20:05:16 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
/*
|
2011-03-03 07:47:55 -05:00
|
|
|
_transform: function(matrix, flags) {
|
2011-02-13 20:05:16 -05:00
|
|
|
// The code that performs the actual transformation of content,
|
|
|
|
// if defined. Item itself does not define this.
|
|
|
|
},
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* Translates (moves) the item by the given offset point.
|
|
|
|
*
|
|
|
|
* @param delta
|
|
|
|
*/
|
|
|
|
translate: function(delta) {
|
|
|
|
var mx = new Matrix();
|
2011-05-07 08:09:04 -04:00
|
|
|
return this.transform(mx.translate.apply(mx, arguments));
|
2011-02-13 20:05:16 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Scales the item by the given values from its center point, or optionally
|
|
|
|
* by a supplied point.
|
|
|
|
*
|
|
|
|
* @param sx
|
|
|
|
* @param sy
|
|
|
|
* @param center {@default the center point of the item}
|
|
|
|
*
|
|
|
|
* @see Matrix#scale(double, double, Point center)
|
|
|
|
*/
|
|
|
|
scale: function(sx, sy /* | scale */, center) {
|
2011-02-15 18:23:40 -05:00
|
|
|
// See Matrix#scale for explanation of this:
|
2011-04-28 08:23:17 -04:00
|
|
|
if (arguments.length < 2 || typeof sy === 'object') {
|
2011-02-15 18:23:40 -05:00
|
|
|
center = sy;
|
|
|
|
sy = sx;
|
|
|
|
}
|
2011-03-06 06:42:08 -05:00
|
|
|
return this.transform(new Matrix().scale(sx, sy,
|
2011-03-06 16:12:11 -05:00
|
|
|
center || this.getPosition()));
|
2011-02-13 20:05:16 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Rotates the item by a given angle around the given point.
|
|
|
|
*
|
|
|
|
* Angles are oriented clockwise and measured in degrees by default. Read
|
|
|
|
* more about angle units and orientation in the description of the
|
|
|
|
* {@link com.scriptographer.ai.Point#getAngle()} property.
|
|
|
|
*
|
|
|
|
* @param angle the rotation angle
|
|
|
|
* @see Matrix#rotate(double, Point)
|
|
|
|
*/
|
|
|
|
rotate: function(angle, center) {
|
2011-03-06 06:42:08 -05:00
|
|
|
return this.transform(new Matrix().rotate(angle,
|
2011-03-06 16:12:11 -05:00
|
|
|
center || this.getPosition()));
|
2011-02-13 20:05:16 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Shears the item with a given amount around its center point.
|
|
|
|
*
|
|
|
|
* @param shx
|
|
|
|
* @param shy
|
|
|
|
* @see Matrix#shear(double, double)
|
|
|
|
*/
|
|
|
|
shear: function(shx, shy, center) {
|
2011-02-13 20:14:43 -05:00
|
|
|
// TODO: Add support for center back to Scriptographer too!
|
2011-02-15 18:23:40 -05:00
|
|
|
// See Matrix#scale for explanation of this:
|
2011-04-28 08:23:17 -04:00
|
|
|
if (arguments.length < 2 || typeof sy === 'object') {
|
2011-02-15 18:23:40 -05:00
|
|
|
center = shy;
|
|
|
|
shy = shx;
|
|
|
|
}
|
2011-03-06 06:42:08 -05:00
|
|
|
return this.transform(new Matrix().shear(shx, shy,
|
2011-03-06 16:12:11 -05:00
|
|
|
center || this.getPosition()));
|
2011-02-16 16:09:51 -05:00
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-16 16:09:51 -05:00
|
|
|
/**
|
|
|
|
* The path style of the item.
|
|
|
|
*/
|
|
|
|
getStyle: function() {
|
|
|
|
return this._style;
|
|
|
|
},
|
2011-03-03 07:19:43 -05:00
|
|
|
|
2011-02-16 16:09:51 -05:00
|
|
|
setStyle: function(style) {
|
2011-05-04 19:00:41 -04:00
|
|
|
this._style = PathStyle.create(this, style);
|
2011-03-03 07:19:43 -05:00
|
|
|
},
|
|
|
|
|
2011-02-24 11:42:32 -05:00
|
|
|
// TODO: toString
|
2011-03-03 07:19:43 -05:00
|
|
|
|
|
|
|
statics: {
|
2011-04-26 10:39:16 -04:00
|
|
|
drawSelectedBounds: function(bounds, ctx, matrix) {
|
2011-05-16 07:29:52 -04:00
|
|
|
var coords = matrix._transformCorners(bounds);
|
2011-04-26 10:39:16 -04:00
|
|
|
ctx.beginPath();
|
2011-04-18 12:46:39 -04:00
|
|
|
for (var i = 0; i < 8; i++)
|
2011-04-26 10:39:16 -04:00
|
|
|
ctx[i == 0 ? 'moveTo' : 'lineTo'](coords[i], coords[++i]);
|
|
|
|
ctx.closePath();
|
|
|
|
ctx.stroke();
|
2011-04-18 12:46:39 -04:00
|
|
|
for (var i = 0; i < 8; i++) {
|
2011-04-26 10:39:16 -04:00
|
|
|
ctx.beginPath();
|
2011-05-04 12:44:08 -04:00
|
|
|
ctx.rect(coords[i] - 2, coords[++i] - 2, 4, 4);
|
2011-04-26 10:39:16 -04:00
|
|
|
ctx.fill();
|
2011-04-18 12:46:39 -04:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2011-05-16 08:33:15 -04:00
|
|
|
// TODO: Implement ProjectView into the drawing
|
2011-03-03 07:19:43 -05:00
|
|
|
// TODO: Optimize temporary canvas drawing to ignore parts that are
|
|
|
|
// outside of the visible view.
|
2011-04-26 10:39:16 -04:00
|
|
|
draw: function(item, ctx, param) {
|
2011-03-03 07:19:43 -05:00
|
|
|
if (!item.visible || item.opacity == 0)
|
|
|
|
return;
|
|
|
|
|
2011-04-26 10:39:16 -04:00
|
|
|
var tempCanvas, parentCtx;
|
2011-03-03 07:19:43 -05:00
|
|
|
// If the item has a blendMode or is defining an opacity, draw it on
|
|
|
|
// a temporary canvas first and composite the canvas afterwards.
|
|
|
|
// Paths with an opacity < 1 that both define a fillColor
|
|
|
|
// and strokeColor also need to be drawn on a temporary canvas first,
|
|
|
|
// since otherwise their stroke is drawn half transparent over their
|
|
|
|
// fill.
|
|
|
|
if (item.blendMode !== 'normal'
|
2011-03-04 20:36:27 -05:00
|
|
|
|| item.opacity < 1
|
2011-05-15 14:58:29 -04:00
|
|
|
&& !(item._segments && (!item.getFillColor()
|
2011-03-04 21:40:38 -05:00
|
|
|
|| !item.getStrokeColor()))) {
|
2011-03-04 20:26:12 -05:00
|
|
|
var bounds = item.getStrokeBounds() || item.getBounds();
|
2011-03-03 07:19:43 -05:00
|
|
|
if (!bounds.width || !bounds.height)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Floor the offset and ceil the size, so we don't cut off any
|
|
|
|
// antialiased pixels when drawing onto the temporary canvas.
|
2011-04-28 06:50:53 -04:00
|
|
|
var itemOffset = bounds.getTopLeft().floor(),
|
2011-05-03 04:34:07 -04:00
|
|
|
size = bounds.getSize().ceil().add(new Size(1, 1));
|
2011-03-03 07:19:43 -05:00
|
|
|
tempCanvas = CanvasProvider.getCanvas(size);
|
|
|
|
|
|
|
|
// Save the parent context, so we can draw onto it later
|
2011-04-26 10:39:16 -04:00
|
|
|
parentCtx = ctx;
|
2011-03-03 07:19:43 -05:00
|
|
|
|
2011-04-26 10:39:16 -04:00
|
|
|
// Set ctx to the context of the temporary canvas,
|
|
|
|
// so we draw onto it, instead of the parentCtx
|
|
|
|
ctx = tempCanvas.getContext('2d');
|
|
|
|
ctx.save();
|
2011-03-03 07:19:43 -05:00
|
|
|
|
|
|
|
// Translate the context so the topLeft of the item is at (0, 0)
|
|
|
|
// on the temporary canvas.
|
2011-04-26 10:39:16 -04:00
|
|
|
ctx.translate(-itemOffset.x, -itemOffset.y);
|
2011-03-03 07:19:43 -05:00
|
|
|
}
|
2011-04-17 12:46:35 -04:00
|
|
|
var savedOffset;
|
|
|
|
if (itemOffset) {
|
|
|
|
savedOffset = param.offset;
|
|
|
|
param.offset = itemOffset;
|
|
|
|
}
|
2011-04-26 10:39:16 -04:00
|
|
|
item.draw(ctx, param);
|
2011-04-17 12:46:35 -04:00
|
|
|
if (itemOffset)
|
|
|
|
param.offset = savedOffset;
|
2011-03-03 07:19:43 -05:00
|
|
|
|
|
|
|
// If we created a temporary canvas before, composite it onto the
|
|
|
|
// parent canvas:
|
|
|
|
if (tempCanvas) {
|
|
|
|
|
|
|
|
// Restore the temporary canvas to its state before the
|
|
|
|
// translation matrix was applied above.
|
2011-04-26 10:39:16 -04:00
|
|
|
ctx.restore();
|
2011-03-03 07:19:43 -05:00
|
|
|
|
|
|
|
// If the item has a blendMode, use BlendMode#process to
|
|
|
|
// composite its canvas on the parentCanvas.
|
2011-04-28 08:23:17 -04:00
|
|
|
if (item.blendMode !== 'normal') {
|
2011-03-03 07:19:43 -05:00
|
|
|
// The pixel offset of the temporary canvas to the parent
|
|
|
|
// canvas.
|
|
|
|
var pixelOffset = itemOffset.subtract(param.offset);
|
2011-04-26 10:39:16 -04:00
|
|
|
BlendMode.process(item.blendMode, ctx, parentCtx,
|
2011-03-03 07:19:43 -05:00
|
|
|
item.opacity, pixelOffset);
|
|
|
|
} else {
|
|
|
|
// Otherwise we just need to set the globalAlpha before drawing
|
|
|
|
// the temporary canvas on the parent canvas.
|
2011-04-26 10:39:16 -04:00
|
|
|
parentCtx.save();
|
|
|
|
parentCtx.globalAlpha = item.opacity;
|
|
|
|
parentCtx.drawImage(tempCanvas,
|
2011-03-03 07:19:43 -05:00
|
|
|
itemOffset.x, itemOffset.y);
|
2011-04-26 10:39:16 -04:00
|
|
|
parentCtx.restore();
|
2011-03-03 07:19:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Return the temporary canvas, so it can be reused
|
|
|
|
CanvasProvider.returnCanvas(tempCanvas);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-03-04 20:50:56 -05:00
|
|
|
}, new function() {
|
|
|
|
|
|
|
|
function append(top) {
|
|
|
|
return function(item) {
|
2011-05-07 10:41:07 -04:00
|
|
|
item._removeFromParent();
|
2011-05-14 13:07:10 -04:00
|
|
|
if (this._children) {
|
|
|
|
Base.splice(this._children, [item], top ? undefined : 0, 0);
|
2011-05-14 12:56:14 -04:00
|
|
|
item._parent = this;
|
2011-05-16 08:33:15 -04:00
|
|
|
item._setProject(this._project);
|
2011-05-15 13:12:27 -04:00
|
|
|
if (item._name)
|
|
|
|
item.setName(item._name);
|
2011-03-04 20:50:56 -05:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2011-04-18 12:46:39 -04:00
|
|
|
};
|
2011-03-04 20:50:56 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
function move(above) {
|
|
|
|
return function(item) {
|
|
|
|
// first remove the item from its parent's children list
|
2011-05-14 12:56:14 -04:00
|
|
|
if (item._parent && this._removeFromParent()) {
|
2011-05-14 13:07:10 -04:00
|
|
|
Base.splice(item._parent._children, [this],
|
2011-05-07 11:11:05 -04:00
|
|
|
item._index + (above ? 1 : -1), 0);
|
2011-05-14 12:56:14 -04:00
|
|
|
this._parent = item._parent;
|
2011-05-16 08:33:15 -04:00
|
|
|
this._setProject(item._project);
|
2011-05-15 13:12:27 -04:00
|
|
|
if (item._name)
|
|
|
|
item.setName(item._name);
|
2011-03-04 20:50:56 -05:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2011-04-18 12:46:39 -04:00
|
|
|
};
|
2011-03-04 20:50:56 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
/**
|
2011-03-04 21:40:38 -05:00
|
|
|
* Inserts the specified item as a child of the item by appending it to
|
|
|
|
* the list of children and moving it above all other children. You can
|
|
|
|
* use this function for groups, compound paths and layers.
|
2011-05-15 10:01:59 -04:00
|
|
|
*
|
2011-03-04 20:50:56 -05:00
|
|
|
* @param item The item that will be appended as a child
|
|
|
|
*/
|
|
|
|
appendTop: append(true),
|
|
|
|
|
|
|
|
/**
|
2011-03-04 21:40:38 -05:00
|
|
|
* Inserts the specified item as a child of this item by appending it to
|
|
|
|
* the list of children and moving it below all other children. You can
|
|
|
|
* use this function for groups, compound paths and layers.
|
2011-03-04 20:50:56 -05:00
|
|
|
*
|
|
|
|
* @param item The item that will be appended as a child
|
|
|
|
*/
|
|
|
|
appendBottom: append(false),
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A link to {@link #appendTop}
|
|
|
|
*
|
|
|
|
* @deprecated use {@link #appendTop} or {@link #appendBottom} instead.
|
|
|
|
*/
|
|
|
|
appendChild: function(item) {
|
|
|
|
return this.appendTop(item);
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Moves this item above the specified item.
|
|
|
|
*
|
|
|
|
* @param item The item above which it should be moved
|
|
|
|
* @return true if it was moved, false otherwise
|
|
|
|
*/
|
|
|
|
moveAbove: move(true),
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Moves the item below the specified item.
|
2011-05-15 10:01:59 -04:00
|
|
|
*
|
2011-03-04 20:50:56 -05:00
|
|
|
* @param item the item below which it should be moved
|
|
|
|
* @return true if it was moved, false otherwise
|
|
|
|
*/
|
2011-03-05 08:51:23 -05:00
|
|
|
moveBelow: move(false)
|
2011-04-18 12:46:39 -04:00
|
|
|
};
|
2011-03-09 13:35:03 -05:00
|
|
|
}, new function() {
|
|
|
|
var sets = {
|
2011-03-19 20:11:02 -04:00
|
|
|
down: {}, drag: {}, up: {}, move: {}
|
2011-03-09 13:35:03 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
function removeAll(set) {
|
2011-05-03 03:54:13 -04:00
|
|
|
for (var id in set) {
|
2011-03-09 13:35:03 -05:00
|
|
|
var item = set[id];
|
|
|
|
item.remove();
|
2011-04-28 06:50:53 -04:00
|
|
|
for (var type in sets) {
|
2011-03-09 13:35:03 -05:00
|
|
|
var other = sets[type];
|
2011-04-17 12:46:35 -04:00
|
|
|
if (other != set && other[item.getId()])
|
2011-03-09 13:35:03 -05:00
|
|
|
delete other[item.getId()];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function installHandler(name) {
|
|
|
|
var handler = 'onMouse' + Base.capitalize(name);
|
|
|
|
// Inject a onMouse handler that performs all the behind the scene magic
|
|
|
|
// and calls the script's handler at the end, if defined.
|
|
|
|
var func = paper.tool[handler];
|
|
|
|
if (!func || !func._installed) {
|
|
|
|
var hash = {};
|
|
|
|
hash[handler] = function(event) {
|
2011-05-08 10:35:10 -04:00
|
|
|
// Always clear the drag set on mouseup
|
2011-04-28 08:23:17 -04:00
|
|
|
if (name === 'up')
|
2011-03-09 13:35:03 -05:00
|
|
|
sets.drag = {};
|
|
|
|
removeAll(sets[name]);
|
|
|
|
sets[name] = {};
|
|
|
|
// Call the script's overridden handler, if defined
|
2011-04-17 12:46:35 -04:00
|
|
|
if (this.base)
|
2011-03-09 13:35:03 -05:00
|
|
|
this.base(event);
|
2011-04-18 12:46:39 -04:00
|
|
|
};
|
2011-03-09 13:35:03 -05:00
|
|
|
paper.tool.inject(hash);
|
|
|
|
// Only install this handler once, and mark it as installed,
|
|
|
|
// to prevent repeated installing.
|
|
|
|
paper.tool[handler]._installed = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-03-19 20:11:02 -04:00
|
|
|
return Base.each(['down', 'drag', 'up', 'move'], function(name) {
|
2011-03-09 13:35:03 -05:00
|
|
|
this['removeOn' + Base.capitalize(name)] = function() {
|
|
|
|
var hash = {};
|
|
|
|
hash[name] = true;
|
|
|
|
return this.removeOn(hash);
|
|
|
|
};
|
|
|
|
}, {
|
|
|
|
removeOn: function(obj) {
|
|
|
|
for (var name in obj) {
|
|
|
|
if (obj[name]) {
|
|
|
|
sets[name][this.getId()] = this;
|
|
|
|
// Since the drag set gets cleared in up, we need to make
|
|
|
|
// sure it's installed too
|
2011-04-28 08:23:17 -04:00
|
|
|
if (name === 'drag')
|
2011-03-09 13:35:03 -05:00
|
|
|
installHandler('up');
|
|
|
|
installHandler(name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
});
|
2011-03-03 11:32:55 -05:00
|
|
|
});
|