implemented clone method (inspired by matteo78) and added thorough unit tests

This commit is contained in:
Eric Rowell 2012-07-07 14:43:12 -07:00
parent 7f7cd24838
commit a8ab9a2533
7 changed files with 150 additions and 18 deletions

50
dist/kinetic-core.js vendored
View File

@ -454,7 +454,6 @@ Kinetic.Node = Kinetic.Class.extend({
this.setDefaultAttrs(this.defaultNodeAttrs);
this.eventListeners = {};
this.setAttrs(config);
// bind events
@ -1105,6 +1104,41 @@ Kinetic.Node = Kinetic.Class.extend({
return m;
},
/**
* clone node
* @param {Object} config used to override cloned
* attrs
*/
clone: function(obj) {
// instantiate new node
var classType = this.shapeType || this.nodeType;
var node = new Kinetic[classType](this.attrs);
/*
* copy over user listeners
*/
for(var key in this.eventListeners) {
var allListeners = this.eventListeners[key];
for(var n = 0; n < allListeners.length; n++) {
var listener = allListeners[n];
/*
* don't include kinetic namespaced listeners because
* these are generated by the constructors
*/
if(listener.name.indexOf('kinetic') < 0) {
// if listeners array doesn't exist, then create it
if(!node.eventListeners[key]) {
node.eventListeners[key] = [];
}
node.eventListeners[key].push(listener);
}
}
}
// apply attr overrides
node.setAttrs(obj);
return node;
},
_fireChangeEvent: function(attr) {
this._handleEvent(attr + 'Change', {});
},
@ -1120,7 +1154,7 @@ Kinetic.Node = Kinetic.Class.extend({
this._dragCleanup();
var go = Kinetic.Global;
var that = this;
this.on('mousedown.kinetic_initdrag touchstart.kinetic_initdrag', function(evt) {
this.on('mousedown.kinetic touchstart.kinetic', function(evt) {
that._initDrag();
});
},
@ -1141,8 +1175,8 @@ Kinetic.Node = Kinetic.Class.extend({
* remove drag and drop event listener
*/
_dragCleanup: function() {
this.off('mousedown.kinetic_initdrag');
this.off('touchstart.kinetic_initdrag');
this.off('mousedown.kinetic');
this.off('touchstart.kinetic');
},
/**
* handle node event
@ -3439,7 +3473,7 @@ Kinetic.Ellipse = Kinetic.Shape.extend({
this._convertRadius();
var that = this;
this.on('radiusChange', function() {
this.on('radiusChange.kinetic', function() {
that._convertRadius();
});
},
@ -3632,7 +3666,7 @@ Kinetic.Sprite = Kinetic.Shape.extend({
this._super(config);
var that = this;
this.on('animationChange', function() {
this.on('animationChange.kinetic', function() {
// reset index when animation changes
that.setIndex(0);
});
@ -4011,7 +4045,7 @@ Kinetic.Text = Kinetic.Shape.extend({
var that = this;
for(var n = 0; n < attrs.length; n++) {
var attr = attrs[n];
this.on(attr + 'Change', that._setTextData);
this.on(attr + 'Change.kinetic', that._setTextData);
}
that._setTextData();
@ -4470,9 +4504,7 @@ Kinetic.Path = Kinetic.Shape.extend({
}
}
this.fill();
//console.profile();
this.stroke();
//console.profileEnd();
};
// call super constructor
this._super(config);

View File

@ -33,7 +33,6 @@ Kinetic.Node = Kinetic.Class.extend({
this.setDefaultAttrs(this.defaultNodeAttrs);
this.eventListeners = {};
this.setAttrs(config);
// bind events
@ -684,6 +683,41 @@ Kinetic.Node = Kinetic.Class.extend({
return m;
},
/**
* clone node
* @param {Object} config used to override cloned
* attrs
*/
clone: function(obj) {
// instantiate new node
var classType = this.shapeType || this.nodeType;
var node = new Kinetic[classType](this.attrs);
/*
* copy over user listeners
*/
for(var key in this.eventListeners) {
var allListeners = this.eventListeners[key];
for(var n = 0; n < allListeners.length; n++) {
var listener = allListeners[n];
/*
* don't include kinetic namespaced listeners because
* these are generated by the constructors
*/
if(listener.name.indexOf('kinetic') < 0) {
// if listeners array doesn't exist, then create it
if(!node.eventListeners[key]) {
node.eventListeners[key] = [];
}
node.eventListeners[key].push(listener);
}
}
}
// apply attr overrides
node.setAttrs(obj);
return node;
},
_fireChangeEvent: function(attr) {
this._handleEvent(attr + 'Change', {});
},
@ -699,7 +733,7 @@ Kinetic.Node = Kinetic.Class.extend({
this._dragCleanup();
var go = Kinetic.Global;
var that = this;
this.on('mousedown.kinetic_initdrag touchstart.kinetic_initdrag', function(evt) {
this.on('mousedown.kinetic touchstart.kinetic', function(evt) {
that._initDrag();
});
},
@ -720,8 +754,8 @@ Kinetic.Node = Kinetic.Class.extend({
* remove drag and drop event listener
*/
_dragCleanup: function() {
this.off('mousedown.kinetic_initdrag');
this.off('touchstart.kinetic_initdrag');
this.off('mousedown.kinetic');
this.off('touchstart.kinetic');
},
/**
* handle node event

View File

@ -39,7 +39,7 @@ Kinetic.Ellipse = Kinetic.Shape.extend({
this._convertRadius();
var that = this;
this.on('radiusChange', function() {
this.on('radiusChange.kinetic', function() {
that._convertRadius();
});
},

View File

@ -57,9 +57,7 @@ Kinetic.Path = Kinetic.Shape.extend({
}
}
this.fill();
//console.profile();
this.stroke();
//console.profileEnd();
};
// call super constructor
this._super(config);

View File

@ -32,7 +32,7 @@ Kinetic.Sprite = Kinetic.Shape.extend({
this._super(config);
var that = this;
this.on('animationChange', function() {
this.on('animationChange.kinetic', function() {
// reset index when animation changes
that.setIndex(0);
});

View File

@ -99,7 +99,7 @@ Kinetic.Text = Kinetic.Shape.extend({
var that = this;
for(var n = 0; n < attrs.length; n++) {
var attr = attrs[n];
this.on(attr + 'Change', that._setTextData);
this.on(attr + 'Change.kinetic', that._setTextData);
}
that._setTextData();

View File

@ -3142,6 +3142,74 @@ Test.prototype.tests = {
test(offsetChange, 'offsetChange should have been triggered with setOffset()');
test(!shadowOffsetChange, 'offsetChange should not have been triggered with setShadow()');
},
'NODE - clone a shape': function(containerId) {
var stage = new Kinetic.Stage({
container: containerId,
width: 578,
height: 200
});
var layer = new Kinetic.Layer();
var rect = new Kinetic.Rect({
x: 50,
y: 50,
width: 200,
height: 50,
fill: 'blue',
offset: [10, 10],
shadow: {
color: 'black',
offset: [20, 20]
},
draggable: true,
name: 'myRect'
});
var clicks = [];
rect.on('click', function() {
clicks.push(this.getName());
});
var clone = rect.clone({
x: 300,
fill: 'red',
name: 'rectClone'
});
test(clone.getX() === 300, 'clone x should be 300');
test(clone.getY() === 50, 'clone y should be 50');
test(clone.getWidth() === 200, 'clone width should be 200');
test(clone.getHeight() === 50, 'clone height should be 50');
test(clone.getFill() === 'red', 'clone fill should be red');
test(rect.getShadow().color === 'black', 'rect shadow color should be black');
test(clone.getShadow().color === 'black', 'clone shadow color should be black');
clone.setShadow({
color: 'green'
});
/*
* Make sure that when we change a clone object attr that the rect object
* attr isn't updated by reference
*/
test(rect.getShadow().color === 'black', 'rect shadow color should be black');
test(clone.getShadow().color === 'green', 'clone shadow color should be green');
layer.add(rect);
layer.add(clone);
stage.add(layer);
// make sure private ids are different
test(rect._id !== clone._id, 'rect and clone ids should be different');
// test user event binding cloning
test(clicks.length === 0, 'no clicks should have been triggered yet');
rect.simulate('click');
test(clicks.toString() === 'myRect', 'only myRect should have been clicked on');
clone.simulate('click');
test(clicks.toString() === 'myRect,rectClone', 'click order should be myRect followed by rectClone');
},
'NODE - test on attr change': function(containerId) {
var stage = new Kinetic.Stage({
container: containerId,