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         //TODO USE PHYSICS
 71         if(this._running){
 72             child.onEnter();
 73             // prevent onEnterTransitionDidFinish to be called twice when a node is added in onEnter
 74             if(this._isTransitionFinished)
 75                 child.onEnterTransitionDidFinish();
 76         }
 77         if(this._cascadeColorEnabled)
 78             this._enableCascadeColor();
 79         if (this._cascadeOpacityEnabled)
 80             this._enableCascadeOpacity();
 81     },
 82 
 83     /**
 84      * Gets a child from the container with its tag
 85      * @param {Number} tag An identifier to find the child node.
 86      * @return {cc.Node} a Node object whose tag equals to the input parameter
 87      */
 88     getProtectedChildByTag: function(tag){
 89         cc.assert(tag != cc.NODE_TAG_INVALID, "Invalid tag");
 90         var locChildren = this._protectedChildren;
 91         for(var i = 0, len = locChildren.length; i < len; i++)
 92             if(locChildren.getTag() == tag)
 93                 return locChildren[i];
 94         return null;
 95     },
 96 
 97     /**
 98      * Removes a child from the container. It will also cleanup all running actions depending on the cleanup parameter.
 99      * @param {cc.Node} child  The child node which will be removed.
100      * @param {Boolean} [cleanup=true] true if all running actions and callbacks on the child node will be cleanup, false otherwise.
101      */
102     removeProtectedChild: function(child,  cleanup){
103         if(cleanup == null)
104             cleanup = true;
105          var locChildren = this._protectedChildren;
106         if(locChildren.length === 0)
107             return;
108         var idx = locChildren.indexOf(child);
109         if(idx > -1){
110              if(this._running){
111                  child.onExitTransitionDidStart();
112                  child.onExit();
113              }
114             //TODO  USE PHYSICS
115 
116             // If you don't do cleanup, the child's actions will not get removed and the
117             // its scheduledSelectors_ dict will not get released!
118             if (cleanup)
119                 child.cleanup();
120 
121             // set parent nil at the end
122             child.setParent(null);
123             locChildren.splice(idx, 1);
124         }
125     },
126 
127     /**
128      * Removes a child from the container by tag value.                                    <br/>
129      * It will also cleanup all running actions depending on the cleanup parameter
130      * @param {Number} tag
131      * @param {Boolean} [cleanup=true]
132      */
133     removeProtectedChildByTag: function(tag, cleanup){
134         cc.assert( tag != cc.NODE_TAG_INVALID, "Invalid tag");
135 
136         if(cleanup == null)
137             cleanup = true;
138 
139         var child = this.getProtectedChildByTag(tag);
140 
141         if (child == null)
142             cc.log("cocos2d: removeChildByTag(tag = %d): child not found!", tag);
143         else
144             this.removeProtectedChild(child, cleanup);
145     },
146 
147     /**
148      * Removes all children from the container with a cleanup.
149      * @see cc.ProtectedNode#removeAllProtectedChildrenWithCleanup
150      */
151     removeAllProtectedChildren: function(){
152         this.removeAllProtectedChildrenWithCleanup(true);
153     },
154 
155     /**
156      * Removes all children from the container, and do a cleanup to all running actions depending on the cleanup parameter.
157      * @param {Boolean} [cleanup=true] true if all running actions on all children nodes should be cleanup, false otherwise.
158      */
159     removeAllProtectedChildrenWithCleanup: function(cleanup){
160         if(cleanup == null)
161             cleanup = true;
162         var locChildren = this._protectedChildren;
163         // not using detachChild improves speed here
164         for (var i = 0, len = locChildren.length; i< len; i++) {
165             var child = locChildren[i];
166             // IMPORTANT:
167             //  -1st do onExit
168             //  -2nd cleanup
169             if(this._running){
170                 child.onExitTransitionDidStart();
171                 child.onExit();
172             }
173 
174             //TODO USE PHYSICS
175             if (cleanup)
176                 child.cleanup();
177             // set parent nil at the end
178             child.setParent(null);
179         }
180         locChildren.length = 0;
181     },
182 
183     /**
184      * Reorders a child according to a new z value.
185      * @param {cc.Node} child An already added child node. It MUST be already added.
186      * @param {Number} localZOrder Z order for drawing priority. Please refer to setLocalZOrder(int)
187      */
188     reorderProtectedChild: function(child, localZOrder){
189         cc.assert( child != null, "Child must be non-nil");
190         this._reorderProtectedChildDirty = true;
191         child.setOrderOfArrival(cc.s_globalOrderOfArrival++);
192         child._setLocalZOrder(localZOrder);
193     },
194 
195     /**
196      * <p>
197      *     Sorts the children array once before drawing, instead of every time when a child is added or reordered.       <br/>
198      *     This approach can improves the performance massively.                                                         <br/>
199      *     @note Don't call this manually unless a child added needs to be removed in the same frame
200      * </p>
201      */
202     sortAllProtectedChildren: function(){
203         if (this._reorderProtectedChildDirty) {
204             var _children = this._protectedChildren;
205 
206             // insertion sort
207             var len = _children.length, i, j, tmp;
208             for(i=1; i<len; i++){
209                 tmp = _children[i];
210                 j = i - 1;
211 
212                 //continue moving element downwards while zOrder is smaller or when zOrder is the same but mutatedIndex is smaller
213                 while(j >= 0){
214                     if(tmp._localZOrder < _children[j]._localZOrder){
215                         _children[j+1] = _children[j];
216                     }else if(tmp._localZOrder === _children[j]._localZOrder && tmp.arrivalOrder < _children[j].arrivalOrder){
217                         _children[j+1] = _children[j];
218                     }else
219                         break;
220                     j--;
221                 }
222                 _children[j+1] = tmp;
223             }
224 
225             //don't need to check children recursively, that's done in visit of each child
226             this._reorderProtectedChildDirty = false;
227         }
228     },
229 
230     /**
231      * transforms and draws itself, and visit its children and protected children.
232      * @override
233      * @function
234      * @param {CanvasRenderingContext2D|WebGLRenderingContext} ctx context of renderer
235      */
236     visit: null,
237 
238     _visitForCanvas: function(ctx){
239         var _t = this;
240         // quick return if not visible
241         if (!_t._visible)
242             return;
243 
244         //visit for canvas
245         var context = ctx || cc._renderContext, i, j;
246         var children = _t._children, child;
247         var locChildren = _t._children, locProtectedChildren = this._protectedChildren;
248         var childLen = locChildren.length, pLen = locProtectedChildren.length;
249         context.save();
250         _t.transform(context);
251 
252         _t.sortAllChildren();
253         _t.sortAllProtectedChildren();
254 
255         // draw children zOrder < 0
256         for (i = 0; i < childLen; i++) {
257             child = children[i];
258             if (child._localZOrder < 0)
259                 child.visit(context);
260             else
261                 break;
262         }
263         for (j = 0; j < pLen; j++) {
264             child = locProtectedChildren[j];
265             if (child._localZOrder < 0)
266                 child.visit(context);
267             else
268                 break;
269         }
270 
271         _t.draw(context);
272 
273         for (; i < childLen; i++)
274             children[i] && children[i].visit(context);
275         for (; j < pLen; j++)
276             locProtectedChildren[j] && locProtectedChildren[j].visit(context);
277 
278         this._cacheDirty = false;
279         _t.arrivalOrder = 0;
280         context.restore();
281     },
282 
283     _visitForWebGL: function(){
284         var _t = this;
285         // quick return if not visible
286         if (!_t._visible)
287             return;
288         var context = cc._renderContext, i, currentStack = cc.current_stack, j;
289 
290         //optimize performance for javascript
291         currentStack.stack.push(currentStack.top);
292         cc.kmMat4Assign(_t._stackMatrix, currentStack.top);
293         currentStack.top = _t._stackMatrix;
294 
295         var locGrid = _t.grid;
296         if (locGrid && locGrid._active)
297             locGrid.beforeDraw();
298 
299         _t.transform();
300 
301         var locChildren = _t._children, locProtectedChildren = this._protectedChildren;
302         var childLen = locChildren.length, pLen = locProtectedChildren.length;
303         _t.sortAllChildren();
304         _t.sortAllProtectedChildren();
305 
306         // draw children zOrder < 0
307         for (i = 0; i < childLen; i++) {
308             if (locChildren[i] && locChildren[i]._localZOrder < 0)
309                 locChildren[i].visit();
310             else
311                 break;
312         }
313         for(j = 0; j < pLen; j++){
314             if (locProtectedChildren[j] && locProtectedChildren[j]._localZOrder < 0)
315                 locProtectedChildren[j].visit();
316             else
317                 break;
318         }
319         _t.draw(context);
320         // draw children zOrder >= 0
321         for (; i < childLen; i++) {
322             locChildren[i] && locChildren[i].visit();
323         }
324         for (; j < pLen; j++) {
325             locProtectedChildren[j] && locProtectedChildren[j].visit();
326         }
327 
328         _t.arrivalOrder = 0;
329         if (locGrid && locGrid._active)
330             locGrid.afterDraw(_t);
331 
332         //optimize performance for javascript
333         currentStack.top = currentStack.stack.pop();
334     },
335 
336     /**
337      * Stops itself and its children and protected children's all running actions and schedulers
338      * @override
339      */
340     cleanup: function(){
341        cc.Node.prototype.cleanup.call(this);
342        var locChildren = this._protectedChildren;
343         for(var i = 0 , len = locChildren.length; i  < len; i++)
344             locChildren[i].cleanup();
345     },
346 
347     /**
348      * Calls its parent's onEnter and calls its protected children's onEnter
349      * @override
350      */
351     onEnter: function(){
352         cc.Node.prototype.onEnter.call(this);
353         var locChildren = this._protectedChildren;
354         for(var i = 0, len = locChildren.length;i< len;i++)
355             locChildren[i].onEnter();
356     },
357 
358     /**
359      *  <p>
360      *     Event callback that is invoked when the Node enters in the 'stage'.                                          <br/>
361      *     If the Node enters the 'stage' with a transition, this event is called when the transition finishes.         <br/>
362      *     If you override onEnterTransitionDidFinish, you shall call its parent's one, e.g. Node::onEnterTransitionDidFinish()
363      *  </p>
364      *  @override
365      */
366     onEnterTransitionDidFinish: function(){
367         cc.Node.prototype.onEnterTransitionDidFinish.call(this);
368         var locChildren = this._protectedChildren;
369         for(var i = 0, len = locChildren.length;i< len;i++)
370             locChildren[i].onEnterTransitionDidFinish();
371     },
372 
373     /**
374      * Calls its parent's onExit and calls its protected children's onExit
375      * @override
376      */
377     onExit:function(){
378         cc.Node.prototype.onExit.call(this);
379         var locChildren = this._protectedChildren;
380         for(var i = 0, len = locChildren.length;i< len;i++)
381             locChildren[i].onExit();
382     },
383 
384     /**
385      * <p>
386      *      Event callback that is called every time the Node leaves the 'stage'.                                      <br/>
387      *      If the Node leaves the 'stage' with a transition, this callback is called when the transition starts.
388      * </p>
389      */
390     onExitTransitionDidStart: function(){
391         cc.Node.prototype.onExitTransitionDidStart.call(this);
392         var locChildren = this._protectedChildren;
393         for(var i = 0, len = locChildren.length;i< len;i++)
394             locChildren[i].onExitTransitionDidStart();
395     },
396 
397     /**
398      * Updates itself and its protected children displayed opacity, if opacity cascade is enable, its children also update.
399      * @param {Number} parentOpacity
400      * @override
401      */
402     updateDisplayedOpacity: function(parentOpacity){
403         this._displayedOpacity = this._realOpacity * parentOpacity/255.0;
404         this._updateColor();
405 
406         var i,len, locChildren, _opacity = this._displayedOpacity;
407         if (this._cascadeOpacityEnabled){
408             locChildren = this._children;
409             for(i = 0, len = locChildren.length;i < len; i++){
410                 if(locChildren[i].updateDisplayedOpacity)
411                     locChildren[i].updateDisplayedOpacity(_opacity);
412             }
413         }
414         locChildren = this._protectedChildren;
415         for(i = 0, len = locChildren.length;i < len; i++){
416             if(locChildren[i])
417                 locChildren[i].updateDisplayedOpacity(_opacity);
418         }
419     },
420 
421     /**
422      * Updates itself and its protected children displayed color, if opacity cascade is enable, its children also update.
423      * @param {cc.Color} parentColor
424      * @override
425      */
426     updateDisplayedColor: function(parentColor){
427         var displayedColor = this._displayedColor, realColor = this._realColor;
428         displayedColor.r = realColor.r * parentColor.r/255.0;
429         displayedColor.g = realColor.g * parentColor.g/255.0;
430         displayedColor.b = realColor.b * parentColor.b/255.0;
431         this._updateColor();
432 
433         var i, len, locChildren;
434         if (this._cascadeColorEnabled){
435             locChildren = this._children;
436             for(i = 0, len = locChildren.length; i < len; i++){
437                 if(locChildren[i].updateDisplayedColor)
438                     locChildren[i].updateDisplayedColor(displayedColor);
439             }
440         }
441 
442         locChildren = this._protectedChildren;
443         for(i =0, len = locChildren.length; i < len; i++) {
444             if (locChildren[i])
445                 locChildren[i].updateDisplayedColor(displayedColor);
446         }
447     },
448 
449     _disableCascadeOpacity: function () {
450         this._displayedOpacity = this._realOpacity;
451 
452         var selChildren = this._children, i, item;
453         for (i = 0; i < selChildren.length; i++) {
454             item = selChildren[i];
455             if (item)
456                 item.updateDisplayedOpacity(255);
457         }
458 
459         selChildren = this._protectedChildren;
460         for (i = 0; i < selChildren.length; i++) {
461             item = selChildren[i];
462             if (item)
463                 item.updateDisplayedOpacity(255);
464         }
465     },
466 
467     _disableCascadeColor: function(){
468         var white = cc.color.WHITE;
469         var i, len, locChildren = this._children;
470         for(i = 0, len = locChildren.length; i < len; i++)
471             locChildren[i].updateDisplayedColor(white);
472 
473         locChildren = this._protectedChildren;
474         for(i =0, len = locChildren.length; i < len; i++)
475             locChildren[i].setColor(white);
476     }
477 });
478 
479 if (cc._renderType === cc._RENDER_TYPE_CANVAS) {
480     cc.ProtectedNode.prototype.visit =  cc.ProtectedNode.prototype._visitForCanvas;
481 }else{
482     cc.ProtectedNode.prototype.visit =  cc.ProtectedNode.prototype._visitForWebGL;
483 }
484 
485 /**
486  * create a cc.ProtectedNode object;
487  * @deprecated since v3.0, please use new cc.ProtectedNode() instead.
488  * @return cc.ProtectedNode
489  */
490 cc.ProtectedNode.create = function(){
491     return new cc.ProtectedNode();
492 };