1 /****************************************************************************
  2  Copyright (c) 2010-2012 cocos2d-x.org
  3  Copyright (c) 2008-2010 Ricardo Quesada
  4  Copyright (c) 2011      Zynga Inc.
  5 
  6  http://www.cocos2d-x.org
  7 
  8  Permission is hereby granted, free of charge, to any person obtaining a copy
  9  of this software and associated documentation files (the "Software"), to deal
 10  in the Software without restriction, including without limitation the rights
 11  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 12  copies of the Software, and to permit persons to whom the Software is
 13  furnished to do so, subject to the following conditions:
 14 
 15  The above copyright notice and this permission notice shall be included in
 16  all copies or substantial portions of the Software.
 17 
 18  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 19  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 20  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 21  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 22  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 23  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 24  THE SOFTWARE.
 25  ****************************************************************************/
 26 
 27 /**
 28  * Priority level reserved for system services.
 29  * @constant
 30  * @type Number
 31  */
 32 cc.PRIORITY_SYSTEM = (-2147483647 - 1);
 33 
 34 /**
 35  * Minimum priority level for user scheduling.
 36  * @constant
 37  * @type Number
 38  */
 39 cc.PRIORITY_NON_SYSTEM = cc.PRIORITY_SYSTEM + 1;
 40 
 41 /**
 42  * Verify Array's Type
 43  * @param {Array} arr
 44  * @param {function} type
 45  * @return {Boolean}
 46  * @function
 47  */
 48 cc.ArrayVerifyType = function (arr, type) {
 49     if (arr && arr.length > 0) {
 50         for (var i = 0; i < arr.length; i++) {
 51             if (!(arr[i] instanceof  type)) {
 52                 cc.log("element type is wrong!");
 53                 return false;
 54             }
 55         }
 56     }
 57     return true;
 58 };
 59 
 60 /**
 61  * Removes object at specified index and pushes back all subsequent objects.Behaviour undefined if index outside [0, num-1].
 62  * @function
 63  * @param {Array} arr Source Array
 64  * @param {Number} index index of remove object
 65  */
 66 cc.ArrayRemoveObjectAtIndex = function (arr, index) {
 67     arr.splice(index, 1);
 68 };
 69 
 70 /**
 71  * Searches for the first occurance of object and removes it. If object is not found the function has no effect.
 72  * @function
 73  * @param {Array} arr Source Array
 74  * @param {*} delObj  remove object
 75  */
 76 cc.ArrayRemoveObject = function (arr, delObj) {
 77     for (var i = 0, l = arr.length; i < l; i++) {
 78         if (arr[i] == delObj) {
 79             arr.splice(i, 1);
 80             break;
 81         }
 82     }
 83 };
 84 
 85 /**
 86  * Removes from arr all values in minusArr. For each Value in minusArr, the first matching instance in arr will be removed.
 87  * @function
 88  * @param {Array} arr Source Array
 89  * @param {Array} minusArr minus Array
 90  */
 91 cc.ArrayRemoveArray = function (arr, minusArr) {
 92     for (var i = 0, l = minusArr.length; i < l; i++) {
 93         cc.ArrayRemoveObject(arr, minusArr[i]);
 94     }
 95 };
 96 
 97 /**
 98  * Returns index of first occurence of value, -1 if value not found.
 99  * @function
100  * @param {Array} arr Source Array
101  * @param {*} value find value
102  * @return {Number} index of first occurence of value
103  */
104 cc.ArrayGetIndexOfValue = function (arr, value) {
105     return arr.indexOf(value);
106 };
107 
108 /**
109  * append an object to array
110  * @function
111  * @param {Array} arr
112  * @param {*} addObj
113  */
114 cc.ArrayAppendObject = function (arr, addObj) {
115     arr.push(addObj);
116 };
117 
118 /**
119  * Inserts an object at index
120  * @function
121  * @param {Array} arr
122  * @param {*} addObj
123  * @param {Number} index
124  * @return {Array}
125  */
126 cc.ArrayAppendObjectToIndex = function (arr, addObj, index) {
127     arr.splice(index, 0, addObj);
128     return arr;
129 };
130 
131 /**
132  * Inserts some objects at index
133  * @function
134  * @param {Array} arr
135  * @param {Array} addObjs
136  * @param {Number} index
137  * @return {Array}
138  */
139 cc.ArrayAppendObjectsToIndex = function(arr, addObjs,index){
140     arr.splice.apply(arr, [index, 0].concat(addObjs));
141     return arr;
142 };
143 
144 /**
145  * Returns index of first occurence of object, -1 if value not found.
146  * @function
147  * @param {Array} arr Source Array
148  * @param {*} findObj find object
149  * @return {Number} index of first occurence of value
150  */
151 cc.ArrayGetIndexOfObject = function (arr, findObj) {
152     for (var i = 0, l = arr.length; i < l; i++) {
153         if (arr[i] == findObj)
154             return i;
155     }
156     return -1;
157 };
158 
159 /**
160  * Returns a Boolean value that indicates whether value is present in the array.
161  * @function
162  * @param {Array} arr
163  * @param {*} findObj
164  * @return {Boolean}
165  */
166 cc.ArrayContainsObject = function (arr, findObj) {
167     return cc.ArrayGetIndexOfObject(arr, findObj) != -1;
168 };
169 
170 /**
171  * find object from array by target
172  * @param {Array} arr source array
173  * @param {cc.ListEntry|cc.HashUpdateEntry} findInt find target
174  * @return {cc.ListEntry|cc.HashUpdateEntry}
175  */
176 cc.HASH_FIND_INT = function (arr, findInt) {
177     if (arr == null) {
178         return null;
179     }
180     for (var i = 0; i < arr.length; i++) {
181         if (arr[i].target === findInt) {
182             return arr[i];
183         }
184     }
185     return null;
186 };
187 
188 //data structures
189 /**
190  * A list double-linked list used for "updates with priority"
191  * @Class
192  * @Construct
193  * @param {cc.ListEntry} prev
194  * @param {cc.ListEntry} next
195  * @param {cc.Class} target not retained (retained by hashUpdateEntry)
196  * @param {Number} priority
197  * @param {Boolean} paused
198  * @param {Boolean} markedForDeletion selector will no longer be called and entry will be removed at end of the next tick
199  */
200 cc.ListEntry = function (prev, next, target, priority, paused, markedForDeletion) {
201     this.prev = prev;
202     this.next = next;
203     this.target = target;
204     this.priority = priority;
205     this.paused = paused;
206     this.markedForDeletion = markedForDeletion;
207 };
208 
209 /**
210  *  a update entry list
211  * @Class
212  * @Construct
213  * @param {cc.ListEntry} list Which list does it belong to ?
214  * @param {cc.ListEntry} entry entry in the list
215  * @param {cc.Class} target hash key (retained)
216  * @param {Array} hh
217  */
218 cc.HashUpdateEntry = function (list, entry, target, hh) {
219     this.list = list;
220     this.entry = entry;
221     this.target = target;
222     this.hh = hh;
223 };
224 
225 //
226 /**
227  * Hash Element used for "selectors with interval"
228  * @Class
229  * @Construct
230  * @param {Array} timers
231  * @param {cc.Class} target  hash key (retained)
232  * @param {Number} timerIndex
233  * @param {cc.Timer} currentTimer
234  * @param {Boolean} currentTimerSalvaged
235  * @param {Boolean} paused
236  * @param {Array} hh
237  */
238 cc.HashTimerEntry = function (timers, target, timerIndex, currentTimer, currentTimerSalvaged, paused, hh) {
239     this.timers = timers;
240     this.target = target;
241     this.timerIndex = timerIndex;
242     this.currentTimer = currentTimer;
243     this.currentTimerSalvaged = currentTimerSalvaged;
244     this.paused = paused;
245     this.hh = hh;
246 };
247 
248 /**
249  * Light weight timer
250  * @class
251  * @extends cc.Class
252  */
253 cc.Timer = cc.Class.extend(/** @lends cc.Timer# */{
254     _interval:0.0,
255     _selector:null,
256 
257     _target:null,
258     _elapsed:0.0,
259 
260     _runForever:false,
261     _useDelay:false,
262     _timesExecuted:0,
263     _repeat:0, //0 = once, 1 is 2 x executed
264     _delay:0,
265 
266     /**
267      * cc.Timer's Constructor
268      * Constructor
269      */
270     ctor:function () {
271     },
272 
273     /**
274      * returns interval of timer
275      * @return {Number}
276      */
277     getInterval:function () {
278         return this._interval;
279     },
280 
281     /**
282      * set interval in seconds
283      * @param {Number} interval
284      */
285     setInterval:function(interval){
286 
287     },
288 
289     /**
290      * returns selector
291      * @return {String|function}
292      */
293     getSelector:function(){
294        return this._selector;
295     },
296 
297     /**
298      * Initializes a timer with a target, a selector and an interval in seconds.
299      * @param {cc.Class} target target
300      * @param {String|function} selector Selector
301      * @param {Number} [seconds=0] second
302      * @param {Number} [repeat=cc.REPEAT_FOREVER] repeat times
303      * @param {Number} [delay=0] delay
304      * @return {Boolean} <tt>true</tt> if initialized
305      * * */
306     initWithTarget: function (target, selector, seconds, repeat, delay) {
307         this._target = target;
308         this._selector = selector;
309         this._elapsed = -1;
310         this._interval = seconds || 0;
311         this._delay = delay || 0;
312         this._useDelay = this._delay > 0;
313         this._repeat = (repeat == null) ? cc.REPEAT_FOREVER : repeat;
314         this._runForever = (this._repeat == cc.REPEAT_FOREVER);
315         return true;
316     },
317 
318     _callSelector:function(){
319         if (typeof(this._selector) == "string")
320             this._target[this._selector](this._elapsed);
321          else // if (typeof(this._selector) == "function") {
322             this._selector.call(this._target, this._elapsed);
323     },
324 
325     /**
326      * triggers the timer
327      * @param {Number} dt delta time
328      */
329     update:function (dt) {
330         if (this._elapsed == -1) {
331             this._elapsed = 0;
332             this._timesExecuted = 0;
333         } else {
334             var locTarget = this._target, locSelector = this._selector;
335             if (this._runForever && !this._useDelay) {
336                 //standard timer usage
337                 this._elapsed += dt;
338 
339                 if (this._elapsed >= this._interval) {
340                     if (locTarget && locSelector)
341                        this._callSelector();
342                     this._elapsed = 0;
343                 }
344             } else {
345                 //advanced usage
346                 this._elapsed += dt;
347                 if (this._useDelay) {
348                     if (this._elapsed >= this._delay) {
349                         if (locTarget && locSelector)
350                             this._callSelector();
351 
352                         this._elapsed = this._elapsed - this._delay;
353                         this._timesExecuted += 1;
354                         this._useDelay = false;
355                     }
356                 } else {
357                     if (this._elapsed >= this._interval) {
358                         if (locTarget && locSelector)
359                             this._callSelector();
360 
361                         this._elapsed = 0;
362                         this._timesExecuted += 1;
363                     }
364                 }
365 
366                 if (this._timesExecuted > this._repeat)
367                     cc.Director.getInstance().getScheduler().unscheduleCallbackForTarget(locTarget, locSelector);
368             }
369         }
370     }
371 });
372 
373 /**
374  * Allocates a timer with a target, a selector and an interval in seconds.
375  * @function
376  * @param {cc.Class} target
377  * @param {String|function} selector Selector
378  * @param {Number} seconds
379  * @return {cc.Timer} a cc.Timer instance
380  * */
381 cc.Timer.timerWithTarget = function (target, selector, seconds) {
382     if (arguments.length < 2){
383         throw new Error("timerWithTarget'argument can't is null");
384     }
385 
386     var timer = new cc.Timer();
387     seconds = seconds||0;
388     timer.initWithTarget(target, selector, seconds, cc.REPEAT_FOREVER, 0);
389     return timer;
390 };
391 
392 cc._sharedScheduler = null;
393 /**
394  * <p>
395  *    Scheduler is responsible of triggering the scheduled callbacks.<br/>
396  *    You should not use NSTimer. Instead use this class.<br/>
397  *    <br/>
398  *    There are 2 different types of callbacks (selectors):<br/>
399  *       - update selector: the 'update' selector will be called every frame. You can customize the priority.<br/>
400  *       - custom selector: A custom selector will be called every frame, or with a custom interval of time<br/>
401  *       <br/>
402  *    The 'custom selectors' should be avoided when possible. It is faster, and consumes less memory to use the 'update selector'. *
403  * </p>
404  * @class
405  * @extends cc.Class
406  *
407  * @example
408  * //register a schedule to scheduler
409  * cc.Director.getInstance().getScheduler().scheduleSelector(selector, this, interval, !this._isRunning);
410  */
411 cc.Scheduler = cc.Class.extend(/** @lends cc.Scheduler# */{
412     _timeScale:1.0,
413     _updatesNegList:null, // list of priority < 0
414     _updates0List:null, // list priority == 0
415     _updatesPosList:null, // list priority > 0
416     _hashForUpdates:null, // hash used to fetch quickly the list entries for pause,delete,etc
417 
418     _hashForTimers:null, //Used for "selectors with interval"
419 
420     _currentTarget:null,
421     _currentTargetSalvaged:false,
422     _updateHashLocked:false, //If true unschedule will not remove anything from a hash. Elements will only be marked for deletion.
423 
424     /**
425      * Constructor
426      */
427     ctor:function () {
428         this._timeScale = 1.0;
429 
430         this._updatesNegList = [];
431         this._updates0List = [];
432         this._updatesPosList = [];
433 
434         this._hashForUpdates = [];
435         this._hashForTimers = [];
436 
437         this._currentTarget = null;
438         this._currentTargetSalvaged = false;
439         this._updateHashLocked = false;
440     },
441 
442     //-----------------------private method----------------------
443     _removeHashElement:function (element) {
444         element.Timer = null;
445         element.target = null;
446         cc.ArrayRemoveObject(this._hashForTimers, element);
447         element = null;
448     },
449 
450     /**
451      * find Object from Array
452      * @private
453      * @param {Array} Source Array
454      * @param {cc.Class} destination object
455      * @return {cc.ListEntry} object if finded, or return null
456      */
457     _findElementFromArray:function (array, target) {
458         for (var i = 0; i < array.length; i++) {
459             if (array[i].target == target) {
460                 return array[i];
461             }
462         }
463         return null;
464     },
465 
466     _removeUpdateFromHash:function (entry) {
467         var element = this._findElementFromArray(this._hashForUpdates, entry.target);
468         if (element) {
469             //list entry
470             cc.ArrayRemoveObject(element.list, element.entry);
471             element.entry = null;
472 
473             //hash entry
474             element.target = null;
475             cc.ArrayRemoveObject(this._hashForUpdates, element);
476         }
477     },
478 
479     _priorityIn:function (ppList, target, priority, paused) {
480         var listElement = new cc.ListEntry(null, null, target, priority, paused, false);
481 
482         // empey list ?
483         if (!ppList) {
484             ppList = [];
485             ppList.push(listElement);
486         } else {
487             var added = false;
488             for (var i = 0; i < ppList.length; i++) {
489                 if (priority < ppList[i].priority) {
490                     ppList = cc.ArrayAppendObjectToIndex(ppList, listElement, i);
491                     added = true;
492                     break;
493                 }
494             }
495 
496             // Not added? priority has the higher value. Append it.
497             if (!added) {
498                 ppList.push(listElement);
499             }
500         }
501 
502         //update hash entry for quick access
503         var hashElement = new cc.HashUpdateEntry(ppList, listElement, target, null);
504         this._hashForUpdates.push(hashElement);
505 
506         return ppList;
507     },
508 
509     _appendIn:function (ppList, target, paused) {
510         var listElement = new cc.ListEntry(null, null, target, 0, paused, false);
511         ppList.push(listElement);
512 
513         //update hash entry for quicker access
514         var hashElement = new cc.HashUpdateEntry(ppList, listElement, target, null);
515         this._hashForUpdates.push(hashElement);
516     },
517 
518     //-----------------------public method-------------------------
519     /**
520      * <p>
521      *    Modifies the time of all scheduled callbacks.<br/>
522      *    You can use this property to create a 'slow motion' or 'fast forward' effect.<br/>
523      *    Default is 1.0. To create a 'slow motion' effect, use values below 1.0.<br/>
524      *    To create a 'fast forward' effect, use values higher than 1.0.<br/>
525      *    @warning It will affect EVERY scheduled selector / action.
526      * </p>
527      * @param {Number} timeScale
528      */
529     setTimeScale:function (timeScale) {
530         this._timeScale = timeScale;
531     },
532 
533     /**
534      * returns time scale of scheduler
535      * @return {Number}
536      */
537     getTimeScale:function () {
538         return this._timeScale;
539     },
540 
541     /**
542      * 'update' the scheduler. (You should NEVER call this method, unless you know what you are doing.)
543      * @param {Number} dt delta time
544      */
545     update:function (dt) {
546         this._updateHashLocked = true;
547 
548         if (this._timeScale != 1.0) {
549             dt *= this._timeScale;
550         }
551 
552         //Iterate all over the Updates selectors
553         var tmpEntry;
554         var i;
555         for (i = 0; i < this._updatesNegList.length; i++) {
556             tmpEntry = this._updatesNegList[i];
557             if ((!tmpEntry.paused) && (!tmpEntry.markedForDeletion)) {
558                 tmpEntry.target.update(dt);
559             }
560         }
561 
562         // updates with priority == 0
563         for (i = 0; i < this._updates0List.length; i++) {
564             tmpEntry = this._updates0List[i];
565             if ((!tmpEntry.paused) && (!tmpEntry.markedForDeletion)) {
566                 tmpEntry.target.update(dt);
567             }
568         }
569 
570         // updates with priority > 0
571         for (i = 0; i < this._updatesPosList.length; i++) {
572             tmpEntry = this._updatesPosList[i];
573             if ((!tmpEntry.paused) && (!tmpEntry.markedForDeletion)) {
574                 tmpEntry.target.update(dt);
575             }
576         }
577 
578         //Interate all over the custom selectors
579         var elt;
580         for (i = 0; i < this._hashForTimers.length; i++) {
581             this._currentTarget = this._hashForTimers[i];
582             elt = this._currentTarget;
583             this._currentTargetSalvaged = false;
584 
585             if (!this._currentTarget.paused) {
586                 // The 'timers' array may change while inside this loop
587                 for (elt.timerIndex = 0; elt.timerIndex < elt.timers.length; elt.timerIndex++) {
588                     elt.currentTimer = elt.timers[elt.timerIndex];
589                     elt.currentTimerSalvaged = false;
590 
591                     elt.currentTimer.update(dt);
592                     elt.currentTimer = null;
593                 }
594             }
595 
596             if ((this._currentTargetSalvaged) && (this._currentTarget.timers.length == 0)) {
597                 this._removeHashElement(this._currentTarget);
598             }
599         }
600 
601         //delete all updates that are marked for deletion
602         // updates with priority < 0
603         for (i = 0; i < this._updatesNegList.length; i++) {
604             if (this._updatesNegList[i].markedForDeletion) {
605                 this._removeUpdateFromHash(this._updatesNegList[i]);
606             }
607         }
608 
609         // updates with priority == 0
610         for (i = 0; i < this._updates0List.length; i++) {
611             if (this._updates0List[i].markedForDeletion) {
612                 this._removeUpdateFromHash(this._updates0List[i]);
613             }
614         }
615 
616         // updates with priority > 0
617         for (i = 0; i < this._updatesPosList.length; i++) {
618             if (this._updatesPosList[i].markedForDeletion) {
619                 this._removeUpdateFromHash(this._updatesPosList[i]);
620             }
621         }
622 
623         this._updateHashLocked = false;
624         this._currentTarget = null;
625     },
626 
627     /**
628      * <p>
629      *   The scheduled method will be called every 'interval' seconds.</br>
630      *   If paused is YES, then it won't be called until it is resumed.<br/>
631      *   If 'interval' is 0, it will be called every frame, but if so, it recommended to use 'scheduleUpdateForTarget:' instead.<br/>
632      *   If the callback function is already scheduled, then only the interval parameter will be updated without re-scheduling it again.<br/>
633      *   repeat let the action be repeated repeat + 1 times, use cc.REPEAT_FOREVER to let the action run continuously<br/>
634      *   delay is the amount of time the action will wait before it'll start<br/>
635      * </p>
636      * @param {cc.Class} target
637      * @param {function} callback_fn
638      * @param {Number} interval
639      * @param {Number} repeat
640      * @param {Number} delay
641      * @param {Boolean} paused
642      * @example
643      * //register a schedule to scheduler
644      * cc.Director.getInstance().getScheduler().scheduleCallbackForTarget(this, function, interval, repeat, delay, !this._isRunning );
645      */
646     scheduleCallbackForTarget:function (target, callback_fn, interval, repeat, delay, paused) {
647         cc.Assert(callback_fn, "scheduler.scheduleCallbackForTarget() Argument callback_fn must be non-NULL");
648         cc.Assert(target, "scheduler.scheduleCallbackForTarget() Argument target must be non-NULL");
649 
650         // default arguments
651         interval = interval || 0;
652         repeat = (repeat == null) ? cc.REPEAT_FOREVER : repeat;
653         delay = delay || 0;
654         paused = paused || false;
655 
656         var element = cc.HASH_FIND_INT(this._hashForTimers, target);
657 
658         if (!element) {
659             // Is this the 1st element ? Then set the pause level to all the callback_fns of this target
660             element = new cc.HashTimerEntry(null, target, 0, null, null, paused, null);
661             this._hashForTimers.push(element);
662         } else {
663             cc.Assert(element.paused == paused, "Sheduler.scheduleCallbackForTarget()");
664         }
665 
666         var timer;
667         if (element.timers == null) {
668             element.timers = [];
669         } else {
670             for (var i = 0; i < element.timers.length; i++) {
671                 timer = element.timers[i];
672                 if (callback_fn == timer._selector) {
673                     cc.log("CCSheduler#scheduleCallback. Callback already scheduled. Updating interval from:"
674                         + timer.getInterval().toFixed(4) + " to " + interval.toFixed(4));
675                     timer._interval = interval;
676                     return;
677                 }
678             }
679         }
680 
681         timer = new cc.Timer();
682         timer.initWithTarget(target, callback_fn, interval, repeat, delay);
683         element.timers.push(timer);
684     },
685 
686     /**
687      * <p>
688      *    Schedules the 'update' callback_fn for a given target with a given priority.<br/>
689      *    The 'update' callback_fn will be called every frame.<br/>
690      *    The lower the priority, the earlier it is called.
691      * </p>
692      * @param {cc.Class} target
693      * @param {Number} priority
694      * @param {Boolean} paused
695      * @example
696      * //register this object to scheduler
697      * cc.Director.getInstance().getScheduler().scheduleUpdateForTarget(this, priority, !this._isRunning );
698      */
699     scheduleUpdateForTarget:function (target, priority, paused) {
700         var hashElement = cc.HASH_FIND_INT(this._hashForUpdates, target);
701 
702         if (hashElement) {
703             if (cc.COCOS2D_DEBUG >= 1) {
704                 cc.Assert(hashElement.entry.markedForDeletion, "");
705             }
706             // TODO: check if priority has changed!
707             hashElement.entry.markedForDeletion = false;
708             return;
709         }
710 
711         // most of the updates are going to be 0, that's way there
712         // is an special list for updates with priority 0
713         if (priority == 0) {
714             this._appendIn(this._updates0List, target, paused);
715         } else if (priority < 0) {
716             this._updatesNegList = this._priorityIn(this._updatesNegList, target, priority, paused);
717         } else {
718             // priority > 0
719             this._updatesPosList = this._priorityIn(this._updatesPosList, target, priority, paused);
720         }
721     },
722 
723     /**
724      * <p>
725      *   Unschedule a callback function for a given target.<br/>
726      *   If you want to unschedule the "update", use unscheudleUpdateForTarget.
727      * </p>
728      * @param {cc.Class} target
729      * @param {function} callback_fn
730      * @example
731      * //unschedule a selector of target
732      * cc.Director.getInstance().getScheduler().unscheduleCallbackForTarget(function, this);
733      */
734     unscheduleCallbackForTarget:function (target, callback_fn) {
735         // explicity handle nil arguments when removing an object
736         if ((target == null) || (callback_fn == null)) {
737             return;
738         }
739 
740         var element = cc.HASH_FIND_INT(this._hashForTimers, target);
741         if (element != null) {
742             for (var i = 0; i < element.timers.length; i++) {
743                 var timer = element.timers[i];
744                 if (callback_fn == timer._selector) {
745                     if ((timer == element.currentTimer) && (!element.currentTimerSalvaged)) {
746                         element.currentTimerSalvaged = true;
747                     }
748                     cc.ArrayRemoveObjectAtIndex(element.timers, i);
749                     //update timerIndex in case we are in tick;, looping over the actions
750                     if (element.timerIndex >= i) {
751                         element.timerIndex--;
752                     }
753 
754                     if (element.timers.length == 0) {
755                         if (this._currentTarget == element) {
756                             this._currentTargetSalvaged = true;
757                         } else {
758 
759                             this._removeHashElement(element);
760                         }
761                     }
762                     return;
763                 }
764             }
765         }
766     },
767 
768     /**
769      * Unschedules the update callback function for a given target
770      * @param {cc.Class} target
771      * @example
772      * //unschedules the "update" method.
773      * cc.Director.getInstance().getScheduler().unscheduleUpdateForTarget(this);
774      */
775     unscheduleUpdateForTarget:function (target) {
776         if (target == null) {
777             return;
778         }
779 
780         var element = cc.HASH_FIND_INT(this._hashForUpdates, target);
781         if (element != null) {
782             if (this._updateHashLocked) {
783                 element.entry.markedForDeletion = true;
784             } else {
785                 this._removeUpdateFromHash(element.entry);
786             }
787         }
788     },
789 
790     /**
791      * Unschedules all function callbacks for a given target. This also includes the "update" callback function.
792      * @param {cc.Class} target
793      */
794     unscheduleAllCallbacksForTarget:function (target) {
795         //explicit NULL handling
796         if (target == null) {
797             return;
798         }
799 
800         var element = cc.HASH_FIND_INT(this._hashForTimers, target);
801         if (element) {
802             if ((!element.currentTimerSalvaged) && (cc.ArrayContainsObject(element.timers, element.currentTimer))) {
803                 element.currentTimerSalvaged = true;
804             }
805             element.timers.length = 0;
806 
807             if (this._currentTarget == element) {
808                 this._currentTargetSalvaged = true;
809             } else {
810                 this._removeHashElement(element);
811             }
812         }
813         // update selector
814         this.unscheduleUpdateForTarget(target);
815     },
816 
817     /**
818      *  <p>
819      *      Unschedules all function callbacks from all targets. <br/>
820      *      You should NEVER call this method, unless you know what you are doing.
821      *  </p>
822      */
823     unscheduleAllCallbacks:function () {
824         this.unscheduleAllCallbacksWithMinPriority(cc.PRIORITY_SYSTEM);
825     },
826 
827     /**
828      * <p>
829      *    Unschedules all function callbacks from all targets with a minimum priority.<br/>
830      *    You should only call this with kCCPriorityNonSystemMin or higher.
831      * </p>
832      * @param {Number} minPriority
833      */
834     unscheduleAllCallbacksWithMinPriority:function (minPriority) {
835         // Custom Selectors
836         var i;
837         for (i = 0; i < this._hashForTimers.length; i++) {
838             // element may be removed in unscheduleAllCallbacksForTarget
839             this.unscheduleAllCallbacksForTarget(this._hashForTimers[i].target);
840         }
841 
842         //updates selectors
843         if (minPriority < 0) {
844             for (i = 0; i < this._updatesNegList.length; i++) {
845                 this.unscheduleUpdateForTarget(this._updatesNegList[i].target);
846             }
847         }
848 
849         if (minPriority <= 0) {
850             for (i = 0; i < this._updates0List.length; i++) {
851                 this.unscheduleUpdateForTarget(this._updates0List[i].target);
852             }
853         }
854 
855         for (i = 0; i < this._updatesPosList.length; i++) {
856             if (this._updatesPosList[i].priority >= minPriority) {
857                 this.unscheduleUpdateForTarget(this._updatesPosList[i].target);
858             }
859         }
860     },
861 
862     /**
863      * <p>
864      *  Pause all selectors from all targets.<br/>
865      *  You should NEVER call this method, unless you know what you are doing.
866      * </p>
867      */
868     pauseAllTargets:function () {
869         return this.pauseAllTargetsWithMinPriority(cc.PRIORITY_SYSTEM);
870     },
871 
872     /**
873      * Pause all selectors from all targets with a minimum priority. <br/>
874      * You should only call this with kCCPriorityNonSystemMin or higher.
875      * @param minPriority
876      */
877     pauseAllTargetsWithMinPriority:function (minPriority) {
878         var idsWithSelectors = [];
879 
880         var i, element;
881         // Custom Selectors
882         for (i = 0; i < this._hashForTimers.length; i++) {
883             element = this._hashForTimers[i];
884             if (element) {
885                 element.paused = true;
886                 idsWithSelectors.push(element.target);
887             }
888         }
889 
890         // Updates selectors
891         if (minPriority < 0) {
892             for (i = 0; i < this._updatesNegList.length; i++) {
893                 element = this._updatesNegList[i];
894                 if (element) {
895                     element.paused = true;
896                     idsWithSelectors.push(element.target);
897                 }
898             }
899         }
900 
901         if (minPriority <= 0) {
902             for (i = 0; i < this._updates0List.length; i++) {
903                 element = this._updates0List[i];
904                 if (element) {
905                     element.paused = true;
906                     idsWithSelectors.push(element.target);
907                 }
908             }
909         }
910 
911         for (i = 0; i < this._updatesPosList.length; i++) {
912             element = this._updatesPosList[i];
913             if (element) {
914                 element.paused = true;
915                 idsWithSelectors.push(element.target);
916             }
917         }
918 
919         return idsWithSelectors;
920     },
921 
922     /**
923      * Resume selectors on a set of targets.<br/>
924      * This can be useful for undoing a call to pauseAllCallbacks.
925      * @param targetsToResume
926      */
927     resumeTargets:function (targetsToResume) {
928         if (!targetsToResume)
929             return;
930 
931         for (var i = 0; i < targetsToResume.length; i++) {
932             this.resumeTarget(targetsToResume[i]);
933         }
934     },
935 
936     /**
937      * <p>
938      *    Pauses the target.<br/>
939      *    All scheduled selectors/update for a given target won't be 'ticked' until the target is resumed.<br/>
940      *    If the target is not present, nothing happens.
941      * </p>
942      * @param {cc.Class} target
943      */
944     pauseTarget:function (target) {
945         cc.Assert(target != null, "Scheduler.pauseTarget():entry must be non nil");
946 
947         //customer selectors
948         var element = cc.HASH_FIND_INT(this._hashForTimers, target);
949         if (element) {
950             element.paused = true;
951         }
952 
953         //update selector
954         var elementUpdate = cc.HASH_FIND_INT(this._hashForUpdates, target);
955         if (elementUpdate) {
956             cc.Assert(elementUpdate.entry != null, "Scheduler.pauseTarget():entry must be non nil");
957             elementUpdate.entry.paused = true;
958         }
959     },
960 
961     /**
962      * Resumes the target.<br/>
963      * The 'target' will be unpaused, so all schedule selectors/update will be 'ticked' again.<br/>
964      * If the target is not present, nothing happens.
965      * @param {cc.Class} target
966      */
967     resumeTarget:function (target) {
968         cc.Assert(target != null, "");
969 
970         // custom selectors
971         var element = cc.HASH_FIND_INT(this._hashForTimers, target);
972 
973         if (element) {
974             element.paused = false;
975         }
976 
977         //update selector
978         var elementUpdate = cc.HASH_FIND_INT(this._hashForUpdates, target);
979 
980         if (elementUpdate) {
981             cc.Assert(elementUpdate.entry != null, "Scheduler.resumeTarget():entry must be non nil");
982             elementUpdate.entry.paused = false;
983         }
984     },
985 
986     /**
987      * Returns whether or not the target is paused
988      * @param {cc.Class} target
989      * @return {Boolean}
990      */
991     isTargetPaused:function (target) {
992         cc.Assert(target != null, "Scheduler.isTargetPaused():target must be non nil");
993 
994         // Custom selectors
995         var element = cc.HASH_FIND_INT(this._hashForTimers, target);
996         if (element) {
997             return element.paused;
998         }
999         return false;
1000     }
1001 });
1002 
1003