diff --git a/dist/kinetic-core.js b/dist/kinetic-core.js index dae70db9..3b170079 100644 --- a/dist/kinetic-core.js +++ b/dist/kinetic-core.js @@ -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); diff --git a/src/Node.js b/src/Node.js index b2ddcc92..743416c8 100644 --- a/src/Node.js +++ b/src/Node.js @@ -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 diff --git a/src/shapes/Ellipse.js b/src/shapes/Ellipse.js index bf600a92..891526fd 100644 --- a/src/shapes/Ellipse.js +++ b/src/shapes/Ellipse.js @@ -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(); }); }, diff --git a/src/shapes/Path.js b/src/shapes/Path.js index 6cf36339..f0c46f62 100644 --- a/src/shapes/Path.js +++ b/src/shapes/Path.js @@ -57,9 +57,7 @@ Kinetic.Path = Kinetic.Shape.extend({ } } this.fill(); - //console.profile(); this.stroke(); - //console.profileEnd(); }; // call super constructor this._super(config); diff --git a/src/shapes/Sprite.js b/src/shapes/Sprite.js index a12e8e36..ad70c10b 100644 --- a/src/shapes/Sprite.js +++ b/src/shapes/Sprite.js @@ -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); }); diff --git a/src/shapes/Text.js b/src/shapes/Text.js index 31ac3323..33f15bae 100644 --- a/src/shapes/Text.js +++ b/src/shapes/Text.js @@ -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(); diff --git a/tests/js/unitTests.js b/tests/js/unitTests.js index 836d3793..426ef7ec 100644 --- a/tests/js/unitTests.js +++ b/tests/js/unitTests.js @@ -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,