Merge remote branch 'origin/master'

This commit is contained in:
Jonathan Puckey 2011-05-18 16:01:36 +02:00
commit 241d98a1cf
24 changed files with 208 additions and 211 deletions

View file

@ -71,7 +71,7 @@ var GradientColor = this.GradientColor = Color.extend({
var gradient;
if (this.gradient.type === 'linear') {
gradient = ctx.createLinearGradient(this._origin.x, this._origin.y,
this.destination.x, this.destination.y);
this._destination.x, this._destination.y);
} else {
var origin = this._hilite || this._origin;
gradient = ctx.createRadialGradient(origin.x, origin.y,
@ -79,7 +79,7 @@ var GradientColor = this.GradientColor = Color.extend({
}
for (var i = 0, l = this.gradient._stops.length; i < l; i++) {
var stop = this.gradient._stops[i];
gradient.addColorStop(stop._rampPoint, stop.color.toCssString());
gradient.addColorStop(stop._rampPoint, stop._color.toCssString());
}
return gradient;
},
@ -92,12 +92,10 @@ var GradientColor = this.GradientColor = Color.extend({
* @return true if the GrayColor is the same, false otherwise.
*/
equals: function(color) {
if (color && color._colorType === this._colorType) {
return this.gradient.equals(color.gradient)
&& this._origin.equals(color._origin)
&& this._destination.equals(color._destination);
}
return false;
return color == this || color && color._colorType === this._colorType
&& this.gradient.equals(color.gradient)
&& this._origin.equals(color._origin)
&& this._destination.equals(color._destination);
},
transform: function(matrix) {

View file

@ -28,7 +28,7 @@ var GradientStop = this.GradientStop = Base.extend({
},
setRampPoint: function(rampPoint) {
this._rampPoint = rampPoint !== null ? rampPoint : 0;
this._rampPoint = rampPoint || 0;
},
getColor: function() {
@ -38,14 +38,10 @@ var GradientStop = this.GradientStop = Base.extend({
setColor: function(color) {
this._color = Color.read(arguments);
},
equals: function(stop) {
if (stop == this) {
return true;
} else if (stop instanceof GradientStop) {
return this._color.equals(stop._color)
&& rampPoint == stop.rampPoint;
}
return false;
return stop == this || stop instanceof GradientStop
&& this._color.equals(stop._color)
&& rampPoint == stop._rampPoint;
}
});

View file

@ -43,6 +43,14 @@ Base.inject({
return res;
},
initialize: function(object, values, defaults) {
if (!values)
values = defaults;
return Base.each(defaults, function(value, key) {
this[key] = values[key] || value;
}, object);
},
/**
* Utility function for adding and removing items from a list of which
* each entry keeps a reference to its index in the list in the private

View file

@ -19,27 +19,18 @@
* global paper object, which simply is a pointer to the currently active scope.
*/
var PaperScope = this.PaperScope = Base.extend({
beans: true,
initialize: function(id) {
this.project = null;
this.projects = [];
this.view = null;
this.views = [];
this.tool = null;
this.tools = [];
this.id = id;
PaperScope._scopes[id] = this;
},
/**
* A short-cut to the currently active view of the active project.
*/
getView: function() {
return this.project.activeView;
},
getViews: function() {
return this.project.views;
},
evaluate: function(code) {
return PaperScript.evaluate(code, this);
},
@ -60,9 +51,12 @@ var PaperScope = this.PaperScope = Base.extend({
},
clear: function() {
// Remove all projects and tools.
// Remove all projects, views and tools.
for (var i = this.projects.length - 1; i >= 0; i--)
this.projects[i].remove();
// This also removes the installed event handlers.
for (var i = this.views.length - 1; i >= 0; i--)
this.views[i].remove();
for (var i = this.tools.length - 1; i >= 0; i--)
this.tools[i].remove();
},

View file

@ -137,11 +137,12 @@ var PaperScript = this.PaperScript = new function() {
// so the active project is defined.
var canvas = code.getAttribute('canvas');
if (canvas = canvas && document.getElementById(canvas)) {
// Create a Project for this canvas, using the right scope
// Create an empty Project for this scope, and a view for the
// canvas, both using the right paper scope
paper = scope;
// XXX: Do not pass canvas to Project.
// Create ProjectView here instead.
new Project(canvas);
new Project();
// Activate the newly created view straight away
new View(canvas).activate();
}
if (code.src) {
// If we're loading from a source, request that first and then
@ -154,7 +155,7 @@ var PaperScript = this.PaperScript = new function() {
}
//#endif // BROWSER
var proj = scope.project,
view = proj.activeView,
view = scope.view,
// TODO: Add support for multiple tools
tool = scope.tool = /on(?:Key|Mouse)(?:Up|Down|Move|Drag)/.test(code)
&& new Tool(null, scope),

View file

@ -21,12 +21,9 @@ var Group = this.Group = Item.extend({
this.base();
this._children = [];
this._namedChildren = {};
if (items) {
for (var i = 0, l = items.length; i < l; i++) {
this.appendTop(items[i]);
}
}
this._clipped = false;
this.setChildren(!items || !Array.isArray(items)
|| typeof items[0] !== 'object' ? arguments : items);
},
/**

View file

@ -19,6 +19,7 @@ var Item = this.Item = Base.extend({
initialize: function() {
paper.project.activeLayer.appendTop(this);
this._style = PathStyle.create(this);
this.setStyle(this._project.getCurrentStyle());
},
@ -38,29 +39,10 @@ var Item = this.Item = Base.extend({
* The unique id of the item.
*/
getId: function() {
if (this._id == null) {
if (this._id == null)
this._id = Item._id = (Item._id || 0) + 1;
}
return this._id;
},
_removeFromNamed: function() {
var children = this._parent._children,
namedChildren = this._parent._namedChildren,
name = this._name,
namedArray = namedChildren[name];
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];
}
},
/**
* The name of the item.
@ -86,6 +68,47 @@ var Item = this.Item = Base.extend({
delete children[name];
}
},
_removeFromNamed: function() {
var children = this._parent._children,
namedChildren = this._parent._namedChildren,
name = this._name,
namedArray = namedChildren[name];
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];
}
},
/**
* Removes the item from its parent's children list.
*/
_removeFromParent: function() {
if (this._parent) {
if (this._name)
this._removeFromNamed();
var res = Base.splice(this._parent._children, null, this._index, 1);
this._parent = null;
return !!res.length;
}
return false;
},
/**
* Removes the item.
*/
remove: function() {
if (this.isSelected())
this.setSelected(false);
return this._removeFromParent();
},
/**
* When passed a project, copies the item to the project,
@ -238,7 +261,20 @@ var Item = this.Item = Base.extend({
return this._children;
},
// TODO: #setChildren()
setChildren: function(items) {
this.removeChildren();
for (var i = 0, l = items && items.length; i < l; i++)
this.appendTop(items[i]);
},
/**
* Checks if the item contains any children items.
*
* @return true if it has one or more children, false otherwise.
*/
hasChildren: function() {
return this._children && this._children.length > 0;
},
/**
* Reverses the order of this item's children
@ -252,6 +288,18 @@ var Item = this.Item = Base.extend({
}
},
/**
* Removes all of the item's children, if it has any
*/
removeChildren: function() {
var removed = false;
if (this._children) {
for (var i = this._children.length - 1; i >= 0; i--)
removed = this._children[i].remove() || removed;
}
return removed;
},
/**
* The first item contained within this item.
*/
@ -281,38 +329,6 @@ var Item = this.Item = Base.extend({
return this._parent && this._parent._children[this._index - 1] || null;
},
/**
* Removes the item from its parent's children list.
*/
_removeFromParent: function() {
if (this._parent) {
if (this._name)
this._removeFromNamed();
var res = Base.splice(this._parent._children, null, this._index, 1);
this._parent = null;
return !!res.length;
}
return false;
},
/**
* Removes the item.
*/
remove: function() {
if (this.isSelected())
this.setSelected(false);
return this._removeFromParent();
},
/**
* Checks if the item contains any children items.
*
* @return true if it has one or more children, false otherwise.
*/
hasChildren: function() {
return this._children && this._children.length > 0;
},
/**
* Checks whether the item is editable.
*
@ -629,7 +645,7 @@ var Item = this.Item = Base.extend({
},
setStyle: function(style) {
this._style = PathStyle.create(this, style);
this._style.initialize(style);
},
// TODO: toString
@ -649,7 +665,7 @@ var Item = this.Item = Base.extend({
}
},
// TODO: Implement ProjectView into the drawing
// TODO: Implement View into the drawing
// TODO: Optimize temporary canvas drawing to ignore parts that are
// outside of the visible view.
draw: function(item, ctx, param) {
@ -781,15 +797,6 @@ var Item = this.Item = Base.extend({
*/
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.
*

View file

@ -18,6 +18,8 @@ var Layer = this.Layer = Group.extend({
beans: true,
initialize: function() {
// TODO: Isn't there a way to call this.base() here and then move the
// layer into layers instead?
this._children = [];
this._namedChildren = {};
this._project = paper.project;

View file

@ -29,13 +29,13 @@ var PathStyle = this.PathStyle = Base.extend(new function() {
beans: true,
initialize: function(style) {
if (style) {
for (var i = 0, l = keys.length; i < l; i++) {
var key = keys[i],
value = style[key];
if (value !== undefined)
this[key] = value;
}
// Note: This relies on bean setters that get implicetly
// called when setting values on this[key].
for (var i = 0, l = style && keys.length; i < l; i++) {
var key = keys[i],
value = style[key];
if (value !== undefined)
this[key] = value;
}
},
@ -44,10 +44,9 @@ var PathStyle = this.PathStyle = Base.extend(new function() {
},
statics: {
create: function(item, other) {
create: function(item) {
var style = new PathStyle(PathStyle.dont);
style._item = item;
style.initialize(other);
return style;
}
}
@ -101,8 +100,8 @@ var PathStyle = this.PathStyle = Base.extend(new function() {
}
};
// 'this' = the Base.each() side-car = the object that is injected into
// Item above:
// 'this' = the Base.each() side-car = the object that is returned from
// Base.each and injected into Item above:
this[set] = function(value) {
this._style[set](value);
return this;

View file

@ -35,7 +35,6 @@ var sources = [
'src/basic/Matrix.js',
'src/basic/Line.js',
'src/project/ProjectView.js',
'src/project/Project.js',
'src/project/Symbol.js',
@ -73,6 +72,7 @@ var sources = [
'src/browser/DomElement.js',
'src/browser/DomEvent.js',
'src/ui/View.js',
'src/ui/Event.js',
'src/ui/KeyEvent.js',
'src/ui/Key.js',

View file

@ -52,7 +52,6 @@ var paper = new function() {
//#include "basic/Matrix.js"
//#include "basic/Line.js"
//#include "project/ProjectView.js"
//#include "project/Project.js"
//#include "project/Symbol.js"
@ -92,8 +91,8 @@ var paper = new function() {
//#include "browser/DomElement.js"
//#include "browser/DomEvent.js"
//#include "browser/Request.js"
//#include "ui/View.js"
//#include "ui/Event.js"
//#include "ui/KeyEvent.js"
//#include "ui/Key.js"

View file

@ -28,6 +28,7 @@ var CompoundPath = this.CompoundPath = PathItem.extend({
// clockwise orientation when creating a compound path, so that they
// appear as holes, but only if their orientation was not already
// specified before (= _clockwise is defined).
// TODO: This should really be handled in appendTop / Bottom, right?
if (path._clockwise === undefined)
path.setClockwise(i < l - 1);
this.appendTop(path);

View file

@ -643,8 +643,14 @@ var Path = this.Path = PathItem.extend({
};
return {
beans: true,
_setStyles: function(ctx) {
for (var i in styles) {
var style = this._style[i]();
if (style)
ctx[styles[i]] = style;
}
},
smooth: function() {
// This code is based on the work by Oleg V. Polikarpotchkin,
// http://ov-p.spaces.live.com/blog/cns!39D56F0C7A08D703!147.entry
@ -742,14 +748,6 @@ var Path = this.Path = PathItem.extend({
var segment = this._segments[0];
segment.setHandleIn(handleIn.subtract(segment._point));
}
},
_setStyles: function(ctx) {
for (var i in styles) {
var style = this[i]();
if (style)
ctx[styles[i]] = style;
}
}
};
}, new function() { // PostScript-style drawing commands

View file

@ -17,8 +17,8 @@
var Project = this.Project = Base.extend({
beans: true,
// XXX: Add arguments to define pages, but do not pass canvas here
initialize: function(canvas) {
// TODO: Add arguments to define pages
initialize: function() {
// Store reference to the currently active global paper scope:
this._scope = paper;
// Push it onto this._scope.projects and set index:
@ -27,13 +27,11 @@ var Project = this.Project = Base.extend({
// Layer and DoumentView constructors.
this.activate();
this.layers = [];
this.views = [];
this.symbols = [];
this.activeLayer = new Layer();
this.activeView = canvas ? new ProjectView(canvas) : null;
this._selectedItems = {};
this._selectedItemCount = 0;
this.setCurrentStyle(null);
this._currentStyle = PathStyle.create(null);
},
getCurrentStyle: function() {
@ -41,7 +39,7 @@ var Project = this.Project = Base.extend({
},
setCurrentStyle: function(style) {
this._currentStyle = PathStyle.create(null, style);
this._currentStyle.initialize(style);
},
activate: function() {
@ -55,9 +53,6 @@ var Project = this.Project = Base.extend({
remove: function() {
var res = Base.splice(this._scope.projects, null, this._index, 1);
this._scope = null;
// Remove all views. This also removes the installed event handlers.
for (var i = this.views.length - 1; i >= 0; i--)
this.views[i].remove();
return !!res.length;
},
@ -130,8 +125,10 @@ var Project = this.Project = Base.extend({
}
},
/**
* @deprecated
*/
redraw: function() {
for (var i = 0, l = this.views.length; i < l; i++)
this.views[i].draw();
this._scope.view.draw();
}
});

View file

@ -16,16 +16,17 @@
var CharacterStyle = this.CharacterStyle = PathStyle.extend({
initialize: function(style) {
this.fontSize = style.fontSize || 10;
this.font = style.font || 'sans-serif';
Base.initialize(this, style, {
fontSize: 10,
font: 'sans-serif'
});
this.base(style);
},
statics: {
create: function(item, other) {
create: function(item) {
var style = new CharacterStyle(CharacterStyle.dont);
style._item = item;
style.initialize(other);
return style;
}
}

View file

@ -15,21 +15,16 @@
*/
var ParagraphStyle = this.ParagraphStyle = Base.extend({
beans: true,
initialize: function(style) {
this.justification = (style && style.justification) || 'left';
},
clone: function() {
return new PathStyle(this);
Base.initialize(this, style, {
justification: 'left'
});
},
statics: {
create: function(item, other) {
var style = new ParagraphStyle(PathStyle.dont);
create: function(item) {
var style = new CharacterStyle(CharacterStyle.dont);
style._item = item;
style.initialize(other);
return style;
}
}

View file

@ -19,8 +19,7 @@ var PointText = this.PointText = TextItem.extend({
initialize: function(point) {
this.base();
point = Point.read(arguments, 0, 1);
this._point = point || new Point();
this._point = Point.read(arguments, 0);
this.matrix = new Matrix().translate(this._point);
},
@ -37,13 +36,13 @@ var PointText = this.PointText = TextItem.extend({
}
},
// TODO: position should be the center point of the bounds
// but we currently don't support bounds for PointText.
getPosition: function() {
return this._point;
},
setPosition: function(point) {
// TODO: position should be the center point of the bounds
// but we currently don't support bounds for PointText.
this.setPoint.apply(this, arguments);
},

View file

@ -19,9 +19,10 @@ var TextItem = this.TextItem = Item.extend({
initialize: function() {
this.base();
point = Point.read(arguments, 0, 1);
this.content = null;
this._characterStyle = CharacterStyle.create(this);
this.setCharacterStyle(this._project.getCurrentStyle());
this._paragraphStyle = ParagraphStyle.create(this);
this.setParagraphStyle();
},
@ -30,7 +31,7 @@ var TextItem = this.TextItem = Item.extend({
},
setCharacterStyle: function(style) {
this._characterStyle = CharacterStyle.create(this, style);
this._characterStyle.initialize(style);
},
getParagraphStyle: function() {
@ -38,6 +39,6 @@ var TextItem = this.TextItem = Item.extend({
},
setParagraphStyle: function(style) {
this._paragraphStyle = ParagraphStyle.create(this, style);
this._paragraphStyle.initialize(style);
}
});

View file

@ -65,7 +65,7 @@ var Key = this.Key = new function() {
var character = String.fromCharCode(charCode),
key = keys[keyCode] || character.toLowerCase(),
handler = down ? 'onKeyDown' : 'onKeyUp',
scope = ProjectView.focused && ProjectView.focused._scope,
scope = View.focused && View.focused._scope,
tool = scope && scope.tool;
keyMap[key] = down;
if (tool && tool[handler]) {

View file

@ -14,19 +14,17 @@
* All rights reserved.
*/
var ProjectView = this.ProjectView = Base.extend({
var View = this.View = Base.extend({
beans: true,
// TODO: Add bounds parameter that defines position within canvas?
// Find a good name for these bounds, since #bounds is already the artboard
// bounds of the visible area.
initialize: function(canvas) {
// To go with the convention of never passing project to constructors,
// in all items, associate the view with the currently active project.
this._project = paper.project;
this._scope = this._project._scope;
// Associate this view with the active paper scope.
this._scope = paper;
// Push it onto project.views and set index:
this._index = this._project.views.push(this) - 1;
this._index = this._scope.views.push(this) - 1;
// Handle canvas argument
var size;
if (canvas && canvas instanceof HTMLCanvasElement) {
@ -86,12 +84,8 @@ var ProjectView = this.ProjectView = Base.extend({
this._events = this._createEvents();
DomEvent.add(this._canvas, this._events);
// Make sure the first view is focused for keyboard input straight away
if (!ProjectView.focused)
ProjectView.focused = this;
},
getProject: function() {
return this._project;
if (!View.focused)
View.focused = this;
},
getViewBounds: function() {
@ -150,14 +144,19 @@ var ProjectView = this.ProjectView = Base.extend({
setZoom: function(zoom) {
// TODO: Clamp the view between 1/32 and 64, just like Illustrator?
var mx = new Matrix();
mx.scale(zoom / this._zoom, this._center);
this.transform(mx);
this._transform(new Matrix().scale(zoom / this._zoom, this.getCenter()));
this._zoom = zoom;
},
scrollBy: function(point) {
this.transform(new Matrix().translate(Point.read(arguments).negate()));
this._transform(new Matrix().translate(Point.read(arguments).negate()));
},
_transform: function(matrix, flags) {
this._matrix.preConcatenate(matrix);
// Force recalculation of these values next time they are requested.
this._bounds = null;
this._inverse = null;
},
draw: function() {
@ -166,46 +165,40 @@ var ProjectView = this.ProjectView = Base.extend({
// Initial tests conclude that clearing the canvas using clearRect
// is always faster than setting canvas.width = canvas.width
// http://jsperf.com/clearrect-vs-setting-width/7
var bounds = this._viewBounds;
this._context.clearRect(bounds._x, bounds._y,
var ctx =this._context,
bounds = this._viewBounds;
ctx.clearRect(bounds._x, bounds._y,
// TODO: +1... what if we have multiple views in one canvas?
bounds._width + 1, bounds._height + 1);
this._project.draw(this._context);
ctx.save();
this._matrix.applyToContext(ctx);
// Just draw the active project for now
this._scope.project.draw(ctx);
ctx.restore();
},
activate: function() {
this._project.activeView = this;
this._scope.view = this;
},
remove: function() {
var res = Base.splice(this._project.views, null, this._index, 1);
var res = Base.splice(this._scope.views, null, this._index, 1);
// Uninstall event handlers again for this view.
DomEvent.remove(this._canvas, this._events);
this._project = this._scope = this._canvas = this._events = null;
// Clearing _onFrame makes the frame handler stop automatically.
this._onFrame = null;
this._scope = this._canvas = this._events = this._onFrame = null;
return !!res.length;
},
transform: function(matrix, flags) {
this._matrix.preConcatenate(matrix);
// Force recalculation of these values next time they are requested.
this._bounds = null;
this._inverse = null;
},
_getInverse: function() {
if (!this._inverse)
this._inverse = this._matrix.createInverse();
return this._inverse;
},
// TODO: getInvalidBounds
// TODO: invalidate(rect)
// TODO: style: artwork / preview / raster / opaque / ink
// TODO: getShowGrid
// TODO: getMousePoint
// TODO: artworkToView(rect)
// TODO: Consider naming these projectToView, viewToProject
artworkToView: function(point) {
return this._matrix._transformPoint(Point.read(arguments));
},
@ -214,6 +207,12 @@ var ProjectView = this.ProjectView = Base.extend({
return this._getInverse()._transformPoint(Point.read(arguments));
},
_getInverse: function() {
if (!this._inverse)
this._inverse = this._matrix.createInverse();
return this._inverse;
},
/**
* Handler to be called whenever a view gets resized.
*/
@ -279,7 +278,7 @@ var ProjectView = this.ProjectView = Base.extend({
function mousedown(event) {
// Tell the Key class which view should receive keyboard input.
ProjectView.focused = that;
View.focused = that;
if (!(tool = that._scope.tool))
return;
curPoint = viewToArtwork(event);

View file

@ -37,10 +37,14 @@ test('clockwise', function() {
return path3.clockwise;
}, true);
new CompoundPath([
path1, path2, path3
]);
var compound = new CompoundPath(path1, path2, path3);
equals(function() {
return compound.lastChild == path3;
}, true);
equals(function() {
return compound.firstChild == path1;
}, true);
equals(function() {
return path1.clockwise;
}, true);

View file

@ -23,16 +23,17 @@ test('Group bounds', function() {
strokeWidth: 5,
strokeColor: 'black'
};
var path = new Path.Circle([150, 150], 60);
var secondPath = new Path.Circle([175, 175], 85);
var group = new Group([path, secondPath]);
compareRectangles(group.bounds, { x: 90, y: 90, width: 170, height: 170 });
compareRectangles(group.strokeBounds, { x: 87.5, y: 87.5, width: 175, height: 175 });
compareRectangles(group.bounds, { x: 90, y: 90, width: 170, height: 170 }, 'group.bounds');
compareRectangles(group.strokeBounds, { x: 87.5, y: 87.5, width: 175, height: 175 }, 'group.strokeBounds');
group.rotate(20);
compareRectangles(group.bounds, { x: 89.97681, y: 82.94095, width: 170.04639, height: 177.08224 });
compareRectangles(group.strokeBounds, { x: 87.47681, y: 80.44095, width: 175.04639, height: 182.08224 });
compareRectangles(group.bounds, { x: 89.97681, y: 82.94095, width: 170.04639, height: 177.08224 }, 'group.bounds');
compareRectangles(group.strokeBounds, { x: 87.47681, y: 80.44095, width: 175.04639, height: 182.08224 }, 'group.strokeBounds');
group.rotate(20, new Point(50, 50));
compareRectangles(group.bounds, { x: 39.70692, y: 114.99196, width: 170.00412, height: 180.22401 });
compareRectangles(group.strokeBounds, { x: 37.20692, y: 112.49196, width: 175.00412, height: 185.22401 });
compareRectangles(group.bounds, { x: 39.70692, y: 114.99196, width: 170.00412, height: 180.22401 }, 'group.bounds');
compareRectangles(group.strokeBounds, { x: 37.20692, y: 112.49196, width: 175.00412, height: 185.22401 }, 'group.strokeBounds');
});

View file

@ -42,10 +42,10 @@ test('clone()', function() {
}, true);
});
test('appendChild(item)', function() {
test('appendTop(item)', function() {
var proj = paper.project;
var path = new Path();
proj.activeLayer.appendChild(path);
proj.activeLayer.appendTop(path);
equals(function() {
return proj.activeLayer.children.length;
}, 1);
@ -55,7 +55,7 @@ test('item.parent / item.isChild / item.isParent', function() {
var proj = paper.project;
var secondDoc = new Project();
var path = new Path();
proj.activeLayer.appendChild(path);
proj.activeLayer.appendTop(path);
equals(function() {
return proj.activeLayer.children.indexOf(path) != -1;
}, true);