(function() { var SPACE = '', TRANSFORM_CHANGE_STR = [ 'xChange.kinetic', 'yChange.kinetic', 'scaleXChange.kinetic', 'scaleYChange.kinetic', 'skewXChange.kinetic', 'skewYChange.kinetic', 'rotationChange.kinetic', 'offsetXChange.kinetic', 'offsetYChange.kinetic' ].join(SPACE); Kinetic.Util.addMethods(Kinetic.Container, { __init: function(config) { this.children = new Kinetic.Collection(); Kinetic.Node.call(this, config); this.on(TRANSFORM_CHANGE_STR, function() { var stage = this.getStage(); if (stage) { stage._enableNestedTransforms = true; } }); }, /** * returns a {@link Kinetic.Collection} of direct descendant nodes * @method * @memberof Kinetic.Container.prototype */ getChildren: function() { return this.children; }, /** * determine if node has children * @method * @memberof Kinetic.Container.prototype */ hasChildren: function() { return this.getChildren().length > 0; }, /** * remove all children * @method * @memberof Kinetic.Container.prototype */ removeChildren: function() { var children = this.children, child; while(children.length > 0) { child = children[0]; if (child.hasChildren()) { child.removeChildren(); } child.remove(); } return this; }, /** * destroy all children * @method * @memberof Kinetic.Container.prototype */ destroyChildren: function() { var children = this.children; while(children.length > 0) { children[0].destroy(); } return this; }, /** * add node to container * @method * @memberof Kinetic.Container.prototype * @param {Node} child */ add: function(child) { var children = this.children; this._validateAdd(child); child.index = children.length; child.parent = this; children.push(child); this._fire('add', { child: child }); // chainable return this; }, destroy: function() { // destroy children if (this.hasChildren()) { this.destroyChildren(); } // then destroy self Kinetic.Node.prototype.destroy.call(this); }, /** * return a {@link Kinetic.Collection} of nodes that match the selector. Use '#' for id selections * and '.' for name selections. You can also select by type or class name. Pass multiple selectors * separated by a space. * @method * @memberof Kinetic.Container.prototype * @param {String} selector * @example * // select node with id foo
* var node = stage.find('#foo');

* * // select nodes with name bar inside layer
* var nodes = layer.find('.bar');

* * // select all groups inside layer
* var nodes = layer.find('Group');

* * // select all rectangles inside layer
* var nodes = layer.find('Rect');

* * // select node with an id of foo or a name of bar inside layer
* var nodes = layer.find('#foo, .bar'); */ find: function(selector) { var retArr = [], selectorArr = selector.replace(/ /g, '').split(','), len = selectorArr.length, n, i, sel, arr, node, children, clen; for (n = 0; n < len; n++) { sel = selectorArr[n]; // id selector if(sel.charAt(0) === '#') { node = this._getNodeById(sel.slice(1)); if(node) { retArr.push(node); } } // name selector else if(sel.charAt(0) === '.') { arr = this._getNodesByName(sel.slice(1)); retArr = retArr.concat(arr); } // unrecognized selector, pass to children else { children = this.getChildren(); clen = children.length; for(i = 0; i < clen; i++) { retArr = retArr.concat(children[i]._get(sel)); } } } return Kinetic.Collection.toCollection(retArr); }, _getNodeById: function(key) { var node = Kinetic.ids[key]; if(node !== undefined && this.isAncestorOf(node)) { return node; } return null; }, _getNodesByName: function(key) { var arr = Kinetic.names[key] || []; return this._getDescendants(arr); }, _get: function(selector) { var retArr = Kinetic.Node.prototype._get.call(this, selector); var children = this.getChildren(); var len = children.length; for(var n = 0; n < len; n++) { retArr = retArr.concat(children[n]._get(selector)); } return retArr; }, // extenders toObject: function() { var obj = Kinetic.Node.prototype.toObject.call(this); obj.children = []; var children = this.getChildren(); var len = children.length; for(var n = 0; n < len; n++) { var child = children[n]; obj.children.push(child.toObject()); } return obj; }, _getDescendants: function(arr) { var retArr = []; var len = arr.length; for(var n = 0; n < len; n++) { var node = arr[n]; if(this.isAncestorOf(node)) { retArr.push(node); } } return retArr; }, /** * determine if node is an ancestor * of descendant * @method * @memberof Kinetic.Container.prototype * @param {Kinetic.Node} node */ isAncestorOf: function(node) { var parent = node.getParent(); while(parent) { if(parent._id === this._id) { return true; } parent = parent.getParent(); } return false; }, clone: function(obj) { // call super method var node = Kinetic.Node.prototype.clone.call(this, obj); this.getChildren().each(function(no) { node.add(no.clone()); }); return node; }, /** * get all shapes that intersect a point. Note: because this method must clear a temporary * canvas and redraw every shape inside the container, it should only be used for special sitations * because it performs very poorly. Please use the {@link Kinetic.Stage#getIntersection} method if at all possible * because it performs much better * @method * @memberof Kinetic.Container.prototype * @param {Object} pos * @param {Number} pos.x * @param {Number} pos.y * @returns {Array} array of shapes */ getAllIntersections: function(pos) { var arr = []; this.find('Shape').each(function(shape) { if(shape.isVisible() && shape.intersects(pos)) { arr.push(shape); } }); return arr; }, _setChildrenIndices: function() { this.children.each(function(child, n) { child.index = n; }); }, drawScene: function(canvas) { var layer = this.getLayer(), clip = this.getClipWidth() && this.getClipHeight(), children, n, len; if (!canvas && layer) { canvas = layer.getCanvas(); } if(this.isVisible()) { if (clip) { canvas.getContext()._clip(this); } else { this._drawChildren(canvas); } } return this; }, _drawChildren: function(canvas) { this.children.each(function(child) { child.drawScene(canvas); }); }, drawHit: function() { var hasClip = this.getClipWidth() && this.getClipHeight() && this.nodeType !== 'Stage', n = 0, len = 0, children = [], hitCanvas; if(this.shouldDrawHit()) { if (hasClip) { hitCanvas = this.getLayer().hitCanvas; hitCanvas.getContext()._clip(this); } children = this.children; len = children.length; for(n = 0; n < len; n++) { children[n].drawHit(); } if (hasClip) { hitCanvas.getContext()._context.restore(); } } return this; } }); Kinetic.Util.extend(Kinetic.Container, Kinetic.Node); // deprecated methods Kinetic.Container.prototype.get = Kinetic.Container.prototype.find; // add getters setters Kinetic.Factory.addBoxGetterSetter(Kinetic.Container, 'clip'); /** * set clip * @method * @name setClip * @memberof Kinetic.Container.prototype * @param {Object} clip {x:x, y:y, width:width, height:height} * @example * // set clip x, y, width and height
* image.setClip({
* x: 20,
* y: 20,
* width: 20,
* height: 20
* }); */ /** * get clip * @name getClip * @method * @memberof Kinetic.Container.prototype * @returns {Object} */ Kinetic.Factory.addGetterSetter(Kinetic.Container, 'clipX', 0); /** * set clip x * @method * @name setClipX * @memberof Kinetic.Container.prototype * @param {Number} x */ /** * get clip x * @name getClipX * @method * @memberof Kinetic.Container.prototype * @returns {Number} */ Kinetic.Factory.addGetterSetter(Kinetic.Container, 'clipY', 0); /** * set clip y * @name setClipY * @method * @memberof Kinetic.Container.prototype * @param {Number} y */ /** * get clip y * @name getClipY * @method * @memberof Kinetic.Container.prototype * @returns {Number} */ Kinetic.Factory.addGetterSetter(Kinetic.Container, 'clipWidth', 0); /** * set clip width * @name setClipWidth * @method * @memberof Kinetic.Container.prototype * @param {Number} width */ /** * get clip width * @name getClipWidth * @method * @memberof Kinetic.Container.prototype * @returns {Number} */ Kinetic.Factory.addGetterSetter(Kinetic.Container, 'clipHeight', 0); /** * set clip height * @name setClipHeight * @method * @memberof Kinetic.Container.prototype * @param {Number} height */ /** * get clip height * @name getClipHeight * @method * @memberof Kinetic.Container.prototype * @returns {Number} */ })();