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