1 /****************************************************************************
  2  Copyright (c) 2013-2014 Chukong Technologies Inc.
  3 
  4  http://www.cocos2d-x.org
  5 
  6  Permission is hereby granted, free of charge, to any person obtaining a copy
  7  of this software and associated documentation files (the "Software"), to deal
  8  in the Software without restriction, including without limitation the rights
  9  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 10  copies of the Software, and to permit persons to whom the Software is
 11  furnished to do so, subject to the following conditions:
 12 
 13  The above copyright notice and this permission notice shall be included in
 14  all copies or substantial portions of the Software.
 15 
 16  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 17  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 18  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 19  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 20  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 21  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 22  THE SOFTWARE.
 23  ****************************************************************************/
 24 
 25 /**
 26  * A class inhert from cc.Node, use for saving some protected children in other list.
 27  * @class
 28  * @extends cc.Node
 29  */
 30 cc.ProtectedNode = cc.Node.extend(/** @lends cc.ProtectedNode# */{
 31     _protectedChildren: null,
 32     _reorderProtectedChildDirty: false,
 33 
 34     _insertProtectedChild: function(child, z){
 35         this._reorderProtectedChildDirty = true;
 36         this._protectedChildren.push(child);
 37         child._setLocalZOrder(z);
 38     },
 39 
 40     /**
 41      * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
 42      * @function
 43      */
 44     ctor: function(){
 45         cc.Node.prototype.ctor.call(this);
 46         this._protectedChildren = [];
 47     },
 48 
 49     /**
 50      * <p>
 51      *  Adds a child to the container with z order and tag                                                                         <br/>
 52      *  If the child is added to a 'running' node, then 'onEnter' and 'onEnterTransitionDidFinish' will be called immediately.     <br/>
 53      *  </p>
 54      * @param {cc.Node} child  A child node
 55      * @param {Number} [localZOrder]  Z order for drawing priority. Please refer to `setLocalZOrder(int)`
 56      * @param {Number} [tag]  An integer to identify the node easily. Please refer to `setTag(int)`
 57      */
 58     addProtectedChild: function(child, localZOrder, tag){
 59          cc.assert(child != null, "child must be non-nil");
 60          cc.assert(!child.parent, "child already added. It can't be added again");
 61 
 62         localZOrder = localZOrder || child.getLocalZOrder();
 63         if(tag)
 64             child.setTag(tag);
 65 
 66         this._insertProtectedChild(child, localZOrder);
 67         child.setParent(this);
 68         child.setOrderOfArrival(cc.s_globalOrderOfArrival);
 69 
 70         if(this._running){
 71             child.onEnter();
 72             // prevent onEnterTransitionDidFinish to be called twice when a node is added in onEnter
 73             if(this._isTransitionFinished)
 74                 child.onEnterTransitionDidFinish();
 75         }
 76         if(this._cascadeColorEnabled)
 77             this._renderCmd.setCascadeColorEnabledDirty();
 78         if (this._cascadeOpacityEnabled)
 79             this._renderCmd.setCascadeOpacityEnabledDirty();
 80     },
 81 
 82     /**
 83      * Gets a child from the container with its tag
 84      * @param {Number} tag An identifier to find the child node.
 85      * @return {cc.Node} a Node object whose tag equals to the input parameter
 86      */
 87     getProtectedChildByTag: function(tag){
 88         cc.assert(tag !== cc.NODE_TAG_INVALID, "Invalid tag");
 89         var locChildren = this._protectedChildren;
 90         for(var i = 0, len = locChildren.length; i < len; i++)
 91             if(locChildren.getTag() === tag)
 92                 return locChildren[i];
 93         return null;
 94     },
 95 
 96     /**
 97      * Removes a child from the container. It will also cleanup all running actions depending on the cleanup parameter.
 98      * @param {cc.Node} child  The child node which will be removed.
 99      * @param {Boolean} [cleanup=true] true if all running actions and callbacks on the child node will be cleanup, false otherwise.
100      */
101     removeProtectedChild: function(child,  cleanup){
102         if(cleanup == null)
103             cleanup = true;
104          var locChildren = this._protectedChildren;
105         if(locChildren.length === 0)
106             return;
107         var idx = locChildren.indexOf(child);
108         if(idx > -1){
109              if(this._running){
110                  child.onExitTransitionDidStart();
111                  child.onExit();
112              }
113 
114             // If you don't do cleanup, the child's actions will not get removed and the
115             // its scheduledSelectors_ dict will not get released!
116             if (cleanup)
117                 child.cleanup();
118 
119             // set parent nil at the end
120             child.setParent(null);
121             locChildren.splice(idx, 1);
122         }
123     },
124 
125     /**
126      * Removes a child from the container by tag value.                                    <br/>
127      * It will also cleanup all running actions depending on the cleanup parameter
128      * @param {Number} tag
129      * @param {Boolean} [cleanup=true]
130      */
131     removeProtectedChildByTag: function(tag, cleanup){
132         cc.assert( tag !== cc.NODE_TAG_INVALID, "Invalid tag");
133 
134         if(cleanup == null)
135             cleanup = true;
136 
137         var child = this.getProtectedChildByTag(tag);
138 
139         if (child == null)
140             cc.log("cocos2d: removeChildByTag(tag = %d): child not found!", tag);
141         else
142             this.removeProtectedChild(child, cleanup);
143     },
144 
145     /**
146      * Removes all children from the container with a cleanup.
147      * @see cc.ProtectedNode#removeAllProtectedChildrenWithCleanup
148      */
149     removeAllProtectedChildren: function(){
150         this.removeAllProtectedChildrenWithCleanup(true);
151     },
152 
153     /**
154      * Removes all children from the container, and do a cleanup to all running actions depending on the cleanup parameter.
155      * @param {Boolean} [cleanup=true] true if all running actions on all children nodes should be cleanup, false otherwise.
156      */
157     removeAllProtectedChildrenWithCleanup: function(cleanup){
158         if(cleanup == null)
159             cleanup = true;
160         var locChildren = this._protectedChildren;
161         // not using detachChild improves speed here
162         for (var i = 0, len = locChildren.length; i< len; i++) {
163             var child = locChildren[i];
164             // IMPORTANT:
165             //  -1st do onExit
166             //  -2nd cleanup
167             if(this._running){
168                 child.onExitTransitionDidStart();
169                 child.onExit();
170             }
171 
172             if (cleanup)
173                 child.cleanup();
174             // set parent nil at the end
175             child.setParent(null);
176         }
177         locChildren.length = 0;
178     },
179 
180     /**
181      * Reorders a child according to a new z value.
182      * @param {cc.Node} child An already added child node. It MUST be already added.
183      * @param {Number} localZOrder Z order for drawing priority. Please refer to setLocalZOrder(int)
184      */
185     reorderProtectedChild: function(child, localZOrder){
186         cc.assert( child != null, "Child must be non-nil");
187         this._reorderProtectedChildDirty = true;
188         child.setOrderOfArrival(cc.s_globalOrderOfArrival++);
189         child._setLocalZOrder(localZOrder);
190     },
191 
192     /**
193      * <p>
194      *     Sorts the children array once before drawing, instead of every time when a child is added or reordered.       <br/>
195      *     This approach can improves the performance massively.                                                         <br/>
196      *     @note Don't call this manually unless a child added needs to be removed in the same frame
197      * </p>
198      */
199     sortAllProtectedChildren: function(){
200         if (this._reorderProtectedChildDirty) {
201             var _children = this._protectedChildren;
202 
203             // insertion sort
204             var len = _children.length, i, j, tmp;
205             for(i=1; i<len; i++){
206                 tmp = _children[i];
207                 j = i - 1;
208 
209                 //continue moving element downwards while zOrder is smaller or when zOrder is the same but mutatedIndex is smaller
210                 while(j >= 0){
211                     if(tmp._localZOrder < _children[j]._localZOrder){
212                         _children[j+1] = _children[j];
213                     }else if(tmp._localZOrder === _children[j]._localZOrder && tmp.arrivalOrder < _children[j].arrivalOrder){
214                         _children[j+1] = _children[j];
215                     }else
216                         break;
217                     j--;
218                 }
219                 _children[j+1] = tmp;
220             }
221 
222             //don't need to check children recursively, that's done in visit of each child
223             this._reorderProtectedChildDirty = false;
224         }
225     },
226 
227     _changePosition: function(){},
228 
229     /**
230      * Stops itself and its children and protected children's all running actions and schedulers
231      * @override
232      */
233     cleanup: function(){
234         cc.Node.prototype.cleanup.call(this);
235         var locChildren = this._protectedChildren;
236         for(var i = 0 , len = locChildren.length; i  < len; i++)
237             locChildren[i].cleanup();
238     },
239 
240     /**
241      * Calls its parent's onEnter and calls its protected children's onEnter
242      * @override
243      */
244     onEnter: function(){
245         cc.Node.prototype.onEnter.call(this);
246         var locChildren = this._protectedChildren;
247         for(var i = 0, len = locChildren.length;i< len;i++)
248             locChildren[i].onEnter();
249     },
250 
251     /**
252      *  <p>
253      *     Event callback that is invoked when the Node enters in the 'stage'.                                          <br/>
254      *     If the Node enters the 'stage' with a transition, this event is called when the transition finishes.         <br/>
255      *     If you override onEnterTransitionDidFinish, you shall call its parent's one, e.g. Node::onEnterTransitionDidFinish()
256      *  </p>
257      *  @override
258      */
259     onEnterTransitionDidFinish: function(){
260         cc.Node.prototype.onEnterTransitionDidFinish.call(this);
261         var locChildren = this._protectedChildren;
262         for(var i = 0, len = locChildren.length;i< len;i++)
263             locChildren[i].onEnterTransitionDidFinish();
264     },
265 
266     /**
267      * Calls its parent's onExit and calls its protected children's onExit
268      * @override
269      */
270     onExit:function(){
271         cc.Node.prototype.onExit.call(this);
272         var locChildren = this._protectedChildren;
273         for(var i = 0, len = locChildren.length;i< len;i++)
274             locChildren[i].onExit();
275     },
276 
277     /**
278      * <p>
279      *      Event callback that is called every time the Node leaves the 'stage'.                                      <br/>
280      *      If the Node leaves the 'stage' with a transition, this callback is called when the transition starts.
281      * </p>
282      */
283     onExitTransitionDidStart: function(){
284         cc.Node.prototype.onExitTransitionDidStart.call(this);
285         var locChildren = this._protectedChildren;
286         for(var i = 0, len = locChildren.length;i< len;i++)
287             locChildren[i].onExitTransitionDidStart();
288     },
289 
290     _createRenderCmd: function(){
291         if(cc._renderType === cc.game.RENDER_TYPE_CANVAS)
292             return new cc.ProtectedNode.CanvasRenderCmd(this);
293         else
294             return new cc.ProtectedNode.WebGLRenderCmd(this);
295     }
296 });
297 
298 /**
299  * create a cc.ProtectedNode object;
300  * @deprecated since v3.0, please use new cc.ProtectedNode() instead.
301  * @return cc.ProtectedNode
302  */
303 cc.ProtectedNode.create = function(){
304     return new cc.ProtectedNode();
305 };