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 arr.indexOf(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         if(!callback_fn)
648             throw "cc.scheduler.scheduleCallbackForTarget(): callback_fn should be non-null.";
649 
650         if(!target)
651             throw "cc.scheduler.scheduleCallbackForTarget(): target should be non-null.";
652 
653         // default arguments
654         interval = interval || 0;
655         repeat = (repeat == null) ? cc.REPEAT_FOREVER : repeat;
656         delay = delay || 0;
657         paused = paused || false;
658 
659         var element = cc.HASH_FIND_INT(this._hashForTimers, target);
660 
661         if (!element) {
662             // Is this the 1st element ? Then set the pause level to all the callback_fns of this target
663             element = new cc.HashTimerEntry(null, target, 0, null, null, paused, null);
664             this._hashForTimers.push(element);
665         }
666 
667         var timer;
668         if (element.timers == null) {
669             element.timers = [];
670         } else {
671             for (var i = 0; i < element.timers.length; i++) {
672                 timer = element.timers[i];
673                 if (callback_fn == timer._selector) {
674                     cc.log("CCSheduler#scheduleCallback. Callback already scheduled. Updating interval from:"
675                         + timer.getInterval().toFixed(4) + " to " + interval.toFixed(4));
676                     timer._interval = interval;
677                     return;
678                 }
679             }
680         }
681 
682         timer = new cc.Timer();
683         timer.initWithTarget(target, callback_fn, interval, repeat, delay);
684         element.timers.push(timer);
685     },
686 
687     /**
688      * <p>
689      *    Schedules the 'update' callback_fn for a given target with a given priority.<br/>
690      *    The 'update' callback_fn will be called every frame.<br/>
691      *    The lower the priority, the earlier it is called.
692      * </p>
693      * @param {cc.Class} target
694      * @param {Number} priority
695      * @param {Boolean} paused
696      * @example
697      * //register this object to scheduler
698      * cc.Director.getInstance().getScheduler().scheduleUpdateForTarget(this, priority, !this._isRunning );
699      */
700     scheduleUpdateForTarget:function (target, priority, paused) {
701         var hashElement = cc.HASH_FIND_INT(this._hashForUpdates, target);
702 
703         if (hashElement) {
704             // TODO: check if priority has changed!
705             hashElement.entry.markedForDeletion = false;
706             return;
707         }
708 
709         // most of the updates are going to be 0, that's way there
710         // is an special list for updates with priority 0
711         if (priority == 0) {
712             this._appendIn(this._updates0List, target, paused);
713         } else if (priority < 0) {
714             this._updatesNegList = this._priorityIn(this._updatesNegList, target, priority, paused);
715         } else {
716             // priority > 0
717             this._updatesPosList = this._priorityIn(this._updatesPosList, target, priority, paused);
718         }
719     },
720 
721     /**
722      * <p>
723      *   Unschedule a callback function for a given target.<br/>
724      *   If you want to unschedule the "update", use unscheudleUpdateForTarget.
725      * </p>
726      * @param {cc.Class} target
727      * @param {function} callback_fn
728      * @example
729      * //unschedule a selector of target
730      * cc.Director.getInstance().getScheduler().unscheduleCallbackForTarget(function, this);
731      */
732     unscheduleCallbackForTarget:function (target, callback_fn) {
733         // explicity handle nil arguments when removing an object
734         if ((target == null) || (callback_fn == null)) {
735             return;
736         }
737 
738         var element = cc.HASH_FIND_INT(this._hashForTimers, target);
739         if (element != null) {
740             for (var i = 0; i < element.timers.length; i++) {
741                 var timer = element.timers[i];
742                 if (callback_fn == timer._selector) {
743                     if ((timer == element.currentTimer) && (!element.currentTimerSalvaged)) {
744                         element.currentTimerSalvaged = true;
745                     }
746                     cc.ArrayRemoveObjectAtIndex(element.timers, i);
747                     //update timerIndex in case we are in tick;, looping over the actions
748                     if (element.timerIndex >= i) {
749                         element.timerIndex--;
750                     }
751 
752                     if (element.timers.length == 0) {
753                         if (this._currentTarget == element) {
754                             this._currentTargetSalvaged = true;
755                         } else {
756 
757                             this._removeHashElement(element);
758                         }
759                     }
760                     return;
761                 }
762             }
763         }
764     },
765 
766     /**
767      * Unschedules the update callback function for a given target
768      * @param {cc.Class} target
769      * @example
770      * //unschedules the "update" method.
771      * cc.Director.getInstance().getScheduler().unscheduleUpdateForTarget(this);
772      */
773     unscheduleUpdateForTarget:function (target) {
774         if (target == null) {
775             return;
776         }
777 
778         var element = cc.HASH_FIND_INT(this._hashForUpdates, target);
779         if (element != null) {
780             if (this._updateHashLocked) {
781                 element.entry.markedForDeletion = true;
782             } else {
783                 this._removeUpdateFromHash(element.entry);
784             }
785         }
786     },
787 
788     /**
789      * Unschedules all function callbacks for a given target. This also includes the "update" callback function.
790      * @param {cc.Class} target
791      */
792     unscheduleAllCallbacksForTarget:function (target) {
793         //explicit NULL handling
794         if (target == null) {
795             return;
796         }
797 
798         var element = cc.HASH_FIND_INT(this._hashForTimers, target);
799         if (element) {
800             if ((!element.currentTimerSalvaged) && (cc.ArrayContainsObject(element.timers, element.currentTimer))) {
801                 element.currentTimerSalvaged = true;
802             }
803             element.timers.length = 0;
804 
805             if (this._currentTarget == element) {
806                 this._currentTargetSalvaged = true;
807             } else {
808                 this._removeHashElement(element);
809             }
810         }
811         // update selector
812         this.unscheduleUpdateForTarget(target);
813     },
814 
815     /**
816      *  <p>
817      *      Unschedules all function callbacks from all targets. <br/>
818      *      You should NEVER call this method, unless you know what you are doing.
819      *  </p>
820      */
821     unscheduleAllCallbacks:function () {
822         this.unscheduleAllCallbacksWithMinPriority(cc.PRIORITY_SYSTEM);
823     },
824 
825     /**
826      * <p>
827      *    Unschedules all function callbacks from all targets with a minimum priority.<br/>
828      *    You should only call this with kCCPriorityNonSystemMin or higher.
829      * </p>
830      * @param {Number} minPriority
831      */
832     unscheduleAllCallbacksWithMinPriority:function (minPriority) {
833         // Custom Selectors
834         var i;
835         for (i = 0; i < this._hashForTimers.length; i++) {
836             // element may be removed in unscheduleAllCallbacksForTarget
837             this.unscheduleAllCallbacksForTarget(this._hashForTimers[i].target);
838         }
839 
840         //updates selectors
841         if (minPriority < 0) {
842             for (i = 0; i < this._updatesNegList.length; i++) {
843                 this.unscheduleUpdateForTarget(this._updatesNegList[i].target);
844             }
845         }
846 
847         if (minPriority <= 0) {
848             for (i = 0; i < this._updates0List.length; i++) {
849                 this.unscheduleUpdateForTarget(this._updates0List[i].target);
850             }
851         }
852 
853         for (i = 0; i < this._updatesPosList.length; i++) {
854             if (this._updatesPosList[i].priority >= minPriority) {
855                 this.unscheduleUpdateForTarget(this._updatesPosList[i].target);
856             }
857         }
858     },
859 
860     /**
861      * <p>
862      *  Pause all selectors from all targets.<br/>
863      *  You should NEVER call this method, unless you know what you are doing.
864      * </p>
865      */
866     pauseAllTargets:function () {
867         return this.pauseAllTargetsWithMinPriority(cc.PRIORITY_SYSTEM);
868     },
869 
870     /**
871      * Pause all selectors from all targets with a minimum priority. <br/>
872      * You should only call this with kCCPriorityNonSystemMin or higher.
873      * @param minPriority
874      */
875     pauseAllTargetsWithMinPriority:function (minPriority) {
876         var idsWithSelectors = [];
877 
878         var i, element;
879         // Custom Selectors
880         for (i = 0; i < this._hashForTimers.length; i++) {
881             element = this._hashForTimers[i];
882             if (element) {
883                 element.paused = true;
884                 idsWithSelectors.push(element.target);
885             }
886         }
887 
888         // Updates selectors
889         if (minPriority < 0) {
890             for (i = 0; i < this._updatesNegList.length; i++) {
891                 element = this._updatesNegList[i];
892                 if (element) {
893                     element.paused = true;
894                     idsWithSelectors.push(element.target);
895                 }
896             }
897         }
898 
899         if (minPriority <= 0) {
900             for (i = 0; i < this._updates0List.length; i++) {
901                 element = this._updates0List[i];
902                 if (element) {
903                     element.paused = true;
904                     idsWithSelectors.push(element.target);
905                 }
906             }
907         }
908 
909         for (i = 0; i < this._updatesPosList.length; i++) {
910             element = this._updatesPosList[i];
911             if (element) {
912                 element.paused = true;
913                 idsWithSelectors.push(element.target);
914             }
915         }
916 
917         return idsWithSelectors;
918     },
919 
920     /**
921      * Resume selectors on a set of targets.<br/>
922      * This can be useful for undoing a call to pauseAllCallbacks.
923      * @param targetsToResume
924      */
925     resumeTargets:function (targetsToResume) {
926         if (!targetsToResume)
927             return;
928 
929         for (var i = 0; i < targetsToResume.length; i++) {
930             this.resumeTarget(targetsToResume[i]);
931         }
932     },
933 
934     /**
935      * <p>
936      *    Pauses the target.<br/>
937      *    All scheduled selectors/update for a given target won't be 'ticked' until the target is resumed.<br/>
938      *    If the target is not present, nothing happens.
939      * </p>
940      * @param {cc.Class} target
941      */
942     pauseTarget:function (target) {
943         if(!target)
944             throw "cc.Scheduler.pauseTarget():target should be non-null";
945 
946         //customer selectors
947         var element = cc.HASH_FIND_INT(this._hashForTimers, target);
948         if (element)
949             element.paused = true;
950 
951         //update selector
952         var elementUpdate = cc.HASH_FIND_INT(this._hashForUpdates, target);
953         if (elementUpdate && elementUpdate.entry)
954             elementUpdate.entry.paused = true;
955     },
956 
957     /**
958      * Resumes the target.<br/>
959      * The 'target' will be unpaused, so all schedule selectors/update will be 'ticked' again.<br/>
960      * If the target is not present, nothing happens.
961      * @param {cc.Class} target
962      */
963     resumeTarget:function (target) {
964         if(!target)
965             throw "cc.Scheduler.resumeTarget():target should be non-null";
966 
967         // custom selectors
968         var element = cc.HASH_FIND_INT(this._hashForTimers, target);
969 
970         if (element)
971             element.paused = false;
972 
973         //update selector
974         var elementUpdate = cc.HASH_FIND_INT(this._hashForUpdates, target);
975         if (elementUpdate && elementUpdate.entry)
976             elementUpdate.entry.paused = false;
977     },
978 
979     /**
980      * Returns whether or not the target is paused
981      * @param {cc.Class} target
982      * @return {Boolean}
983      */
984     isTargetPaused:function (target) {
985         if(!target)
986             throw "cc.Scheduler.isTargetPaused():target should be non-null";
987 
988         // Custom selectors
989         var element = cc.HASH_FIND_INT(this._hashForTimers, target);
990         if (element)
991             return element.paused;
992         return false;
993     }
994 });
995 
996