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         cc.doCallback(this._selector, this._target,this._elapsed);
320     },
321 
322     /**
323      * triggers the timer
324      * @param {Number} dt delta time
325      */
326     update:function (dt) {
327         if (this._elapsed == -1) {
328             this._elapsed = 0;
329             this._timesExecuted = 0;
330         } else {
331             var locTarget = this._target, locSelector = this._selector;
332             if (this._runForever && !this._useDelay) {
333                 //standard timer usage
334                 this._elapsed += dt;
335 
336                 if (this._elapsed >= this._interval) {
337                     if (locTarget && locSelector)
338                        this._callSelector();
339                     this._elapsed = 0;
340                 }
341             } else {
342                 //advanced usage
343                 this._elapsed += dt;
344                 if (this._useDelay) {
345                     if (this._elapsed >= this._delay) {
346                         if (locTarget && locSelector)
347                             this._callSelector();
348 
349                         this._elapsed = this._elapsed - this._delay;
350                         this._timesExecuted += 1;
351                         this._useDelay = false;
352                     }
353                 } else {
354                     if (this._elapsed >= this._interval) {
355                         if (locTarget && locSelector)
356                             this._callSelector();
357 
358                         this._elapsed = 0;
359                         this._timesExecuted += 1;
360                     }
361                 }
362 
363                 if (this._timesExecuted > this._repeat)
364                     cc.Director.getInstance().getScheduler().unscheduleCallbackForTarget(locTarget, locSelector);
365             }
366         }
367     }
368 });
369 
370 /**
371  * Allocates a timer with a target, a selector and an interval in seconds.
372  * @function
373  * @param {cc.Class} target
374  * @param {String|function} selector Selector
375  * @param {Number} seconds
376  * @return {cc.Timer} a cc.Timer instance
377  * */
378 cc.Timer.timerWithTarget = function (target, selector, seconds) {
379     if (arguments.length < 2){
380         throw new Error("timerWithTarget'argument can't is null");
381     }
382 
383     var timer = new cc.Timer();
384     seconds = seconds||0;
385     timer.initWithTarget(target, selector, seconds, cc.REPEAT_FOREVER, 0);
386     return timer;
387 };
388 
389 cc._sharedScheduler = null;
390 /**
391  * <p>
392  *    Scheduler is responsible of triggering the scheduled callbacks.<br/>
393  *    You should not use NSTimer. Instead use this class.<br/>
394  *    <br/>
395  *    There are 2 different types of callbacks (selectors):<br/>
396  *       - update selector: the 'update' selector will be called every frame. You can customize the priority.<br/>
397  *       - custom selector: A custom selector will be called every frame, or with a custom interval of time<br/>
398  *       <br/>
399  *    The 'custom selectors' should be avoided when possible. It is faster, and consumes less memory to use the 'update selector'. *
400  * </p>
401  * @class
402  * @extends cc.Class
403  *
404  * @example
405  * //register a schedule to scheduler
406  * cc.Director.getInstance().getScheduler().scheduleSelector(selector, this, interval, !this._isRunning);
407  */
408 cc.Scheduler = cc.Class.extend(/** @lends cc.Scheduler# */{
409     _timeScale:1.0,
410     _updatesNegList:null, // list of priority < 0
411     _updates0List:null, // list priority == 0
412     _updatesPosList:null, // list priority > 0
413     _hashForUpdates:null, // hash used to fetch quickly the list entries for pause,delete,etc
414     _arrayForUpdates:null,
415 
416     _hashForTimers:null, //Used for "selectors with interval"
417     _arrayForTimes:null,
418 
419     _currentTarget:null,
420     _currentTargetSalvaged:false,
421     _updateHashLocked:false, //If true unschedule will not remove anything from a hash. Elements will only be marked for deletion.
422 
423     /**
424      * Constructor
425      */
426     ctor:function () {
427         this._timeScale = 1.0;
428 
429         this._updatesNegList = [];
430         this._updates0List = [];
431         this._updatesPosList = [];
432 
433         this._hashForUpdates = {};
434         this._arrayForUpdates = [];
435 
436         this._hashForTimers = {};
437         this._arrayForTimers = [];
438 
439         this._currentTarget = null;
440         this._currentTargetSalvaged = false;
441         this._updateHashLocked = false;
442     },
443 
444     //-----------------------private method----------------------
445     _removeHashElement:function (element) {
446         delete this._hashForTimers[element.target.__instanceId];
447         cc.ArrayRemoveObject(this._arrayForTimers, element);
448         element.Timer = null;
449         element.target = null;
450         element = null;
451     },
452 
453     /**
454      * find Object from Array
455      * @private
456      * @param {Array} array Array
457      * @param {cc.Class} target object
458      * @return {cc.ListEntry} object if found, or return null
459      */
460     _findElementFromArray:function (array, target) {
461         for (var i = 0; i < array.length; i++) {
462             if (array[i].target == target) {
463                 return array[i];
464             }
465         }
466         return null;
467     },
468 
469     _removeUpdateFromHash:function (entry) {
470 //        var element = this._findElementFromArray(this._hashForUpdates, entry.target);
471         var element = this._hashForUpdates[entry.target.__instanceId];
472         if (element) {
473             //list entry
474             cc.ArrayRemoveObject(element.list, element.entry);
475 
476             delete this._hashForUpdates[element.target.__instanceId];
477             cc.ArrayRemoveObject(this._arrayForUpdates, element);
478             element.entry = null;
479 
480             //hash entry
481             element.target = null;
482 //            cc.ArrayRemoveObject(this._hashForUpdates, element);
483         }
484     },
485 
486     _priorityIn:function (ppList, target, priority, paused) {
487         var listElement = new cc.ListEntry(null, null, target, priority, paused, false);
488 
489         // empey list ?
490         if (!ppList) {
491             ppList = [];
492             ppList.push(listElement);
493         } else {
494             var added = false;
495             for (var i = 0; i < ppList.length; i++) {
496                 if (priority < ppList[i].priority) {
497                     ppList = cc.ArrayAppendObjectToIndex(ppList, listElement, i);
498                     added = true;
499                     break;
500                 }
501             }
502 
503             // Not added? priority has the higher value. Append it.
504             if (!added) {
505                 ppList.push(listElement);
506             }
507         }
508 
509         //update hash entry for quick access
510         var hashElement = new cc.HashUpdateEntry(ppList, listElement, target, null);
511         this._arrayForUpdates.push(hashElement);
512         this._hashForUpdates[target.__instanceId] = hashElement;
513 //        this._hashForUpdates.push(hashElement);
514 
515         return ppList;
516     },
517 
518     _appendIn:function (ppList, target, paused) {
519         var listElement = new cc.ListEntry(null, null, target, 0, paused, false);
520         ppList.push(listElement);
521 
522         //update hash entry for quicker access
523         var hashElement = new cc.HashUpdateEntry(ppList, listElement, target, null);
524         this._arrayForUpdates.push(hashElement);
525         this._hashForUpdates[target.__instanceId] = hashElement;
526 //        this._hashForUpdates.push(hashElement);
527     },
528 
529     //-----------------------public method-------------------------
530     /**
531      * <p>
532      *    Modifies the time of all scheduled callbacks.<br/>
533      *    You can use this property to create a 'slow motion' or 'fast forward' effect.<br/>
534      *    Default is 1.0. To create a 'slow motion' effect, use values below 1.0.<br/>
535      *    To create a 'fast forward' effect, use values higher than 1.0.<br/>
536      *    @warning It will affect EVERY scheduled selector / action.
537      * </p>
538      * @param {Number} timeScale
539      */
540     setTimeScale:function (timeScale) {
541         this._timeScale = timeScale;
542     },
543 
544     /**
545      * returns time scale of scheduler
546      * @return {Number}
547      */
548     getTimeScale:function () {
549         return this._timeScale;
550     },
551 
552     /**
553      * 'update' the scheduler. (You should NEVER call this method, unless you know what you are doing.)
554      * @param {Number} dt delta time
555      */
556     update:function (dt) {
557         this._updateHashLocked = true;
558 
559         if (this._timeScale != 1.0) {
560             dt *= this._timeScale;
561         }
562 
563         //Iterate all over the Updates selectors
564         var tmpEntry;
565         var i;
566         for (i = 0; i < this._updatesNegList.length; i++) {
567             tmpEntry = this._updatesNegList[i];
568             if ((!tmpEntry.paused) && (!tmpEntry.markedForDeletion)) {
569                 tmpEntry.target.update(dt);
570             }
571         }
572 
573         // updates with priority == 0
574         for (i = 0; i < this._updates0List.length; i++) {
575             tmpEntry = this._updates0List[i];
576             if ((!tmpEntry.paused) && (!tmpEntry.markedForDeletion)) {
577                 tmpEntry.target.update(dt);
578             }
579         }
580 
581         // updates with priority > 0
582         for (i = 0; i < this._updatesPosList.length; i++) {
583             tmpEntry = this._updatesPosList[i];
584             if ((!tmpEntry.paused) && (!tmpEntry.markedForDeletion)) {
585                 tmpEntry.target.update(dt);
586             }
587         }
588 
589         //Interate all over the custom selectors
590         var elt;
591         for (i = 0; i < this._arrayForTimers.length; i++) {
592             this._currentTarget = this._arrayForTimers[i];
593             elt = this._currentTarget;
594             this._currentTargetSalvaged = false;
595 
596             if (!this._currentTarget.paused) {
597                 // The 'timers' array may change while inside this loop
598                 for (elt.timerIndex = 0; elt.timerIndex < elt.timers.length; elt.timerIndex++) {
599                     elt.currentTimer = elt.timers[elt.timerIndex];
600                     elt.currentTimerSalvaged = false;
601 
602                     elt.currentTimer.update(dt);
603                     elt.currentTimer = null;
604                 }
605             }
606 
607             if ((this._currentTargetSalvaged) && (this._currentTarget.timers.length == 0)) {
608                 this._removeHashElement(this._currentTarget);
609             }
610         }
611 
612         //delete all updates that are marked for deletion
613         // updates with priority < 0
614         for (i = 0; i < this._updatesNegList.length; i++) {
615             if (this._updatesNegList[i].markedForDeletion) {
616                 this._removeUpdateFromHash(this._updatesNegList[i]);
617             }
618         }
619 
620         // updates with priority == 0
621         for (i = 0; i < this._updates0List.length; i++) {
622             if (this._updates0List[i].markedForDeletion) {
623                 this._removeUpdateFromHash(this._updates0List[i]);
624             }
625         }
626 
627         // updates with priority > 0
628         for (i = 0; i < this._updatesPosList.length; i++) {
629             if (this._updatesPosList[i].markedForDeletion) {
630                 this._removeUpdateFromHash(this._updatesPosList[i]);
631             }
632         }
633 
634         this._updateHashLocked = false;
635         this._currentTarget = null;
636     },
637 
638     /**
639      * <p>
640      *   The scheduled method will be called every 'interval' seconds.</br>
641      *   If paused is YES, then it won't be called until it is resumed.<br/>
642      *   If 'interval' is 0, it will be called every frame, but if so, it recommended to use 'scheduleUpdateForTarget:' instead.<br/>
643      *   If the callback function is already scheduled, then only the interval parameter will be updated without re-scheduling it again.<br/>
644      *   repeat let the action be repeated repeat + 1 times, use cc.REPEAT_FOREVER to let the action run continuously<br/>
645      *   delay is the amount of time the action will wait before it'll start<br/>
646      * </p>
647      * @param {cc.Class} target
648      * @param {function} callback_fn
649      * @param {Number} interval
650      * @param {Number} repeat
651      * @param {Number} delay
652      * @param {Boolean} paused
653      * @example
654      * //register a schedule to scheduler
655      * cc.Director.getInstance().getScheduler().scheduleCallbackForTarget(this, function, interval, repeat, delay, !this._isRunning );
656      */
657     scheduleCallbackForTarget:function (target, callback_fn, interval, repeat, delay, paused) {
658         if(!callback_fn)
659             throw "cc.scheduler.scheduleCallbackForTarget(): callback_fn should be non-null.";
660 
661         if(!target)
662             throw "cc.scheduler.scheduleCallbackForTarget(): target should be non-null.";
663 
664         // default arguments
665         interval = interval || 0;
666         repeat = (repeat == null) ? cc.REPEAT_FOREVER : repeat;
667         delay = delay || 0;
668         paused = paused || false;
669 
670         var element = this._hashForTimers[target.__instanceId];
671 
672         if (!element) {
673             // Is this the 1st element ? Then set the pause level to all the callback_fns of this target
674             element = new cc.HashTimerEntry(null, target, 0, null, null, paused, null);
675             this._arrayForTimers.push(element);
676             this._hashForTimers[target.__instanceId] = element;
677         }
678 
679         var timer;
680         if (element.timers == null) {
681             element.timers = [];
682         } else {
683             for (var i = 0; i < element.timers.length; i++) {
684                 timer = element.timers[i];
685                 if (callback_fn == timer._selector) {
686                     cc.log("CCSheduler#scheduleCallback. Callback already scheduled. Updating interval from:"
687                         + timer.getInterval().toFixed(4) + " to " + interval.toFixed(4));
688                     timer._interval = interval;
689                     return;
690                 }
691             }
692         }
693 
694         timer = new cc.Timer();
695         timer.initWithTarget(target, callback_fn, interval, repeat, delay);
696         element.timers.push(timer);
697     },
698 
699     /**
700      * <p>
701      *    Schedules the 'update' callback_fn for a given target with a given priority.<br/>
702      *    The 'update' callback_fn will be called every frame.<br/>
703      *    The lower the priority, the earlier it is called.
704      * </p>
705      * @param {cc.Class} target
706      * @param {Number} priority
707      * @param {Boolean} paused
708      * @example
709      * //register this object to scheduler
710      * cc.Director.getInstance().getScheduler().scheduleUpdateForTarget(this, priority, !this._isRunning );
711      */
712     scheduleUpdateForTarget:function (target, priority, paused) {
713         var hashElement = this._hashForUpdates[target.__instanceId];
714 
715         if (hashElement) {
716             // TODO: check if priority has changed!
717             hashElement.entry.markedForDeletion = false;
718             return;
719         }
720 
721         // most of the updates are going to be 0, that's way there
722         // is an special list for updates with priority 0
723         if (priority == 0) {
724             this._appendIn(this._updates0List, target, paused);
725         } else if (priority < 0) {
726             this._updatesNegList = this._priorityIn(this._updatesNegList, target, priority, paused);
727         } else {
728             // priority > 0
729             this._updatesPosList = this._priorityIn(this._updatesPosList, target, priority, paused);
730         }
731     },
732 
733     /**
734      * <p>
735      *   Unschedule a callback function for a given target.<br/>
736      *   If you want to unschedule the "update", use unscheudleUpdateForTarget.
737      * </p>
738      * @param {cc.Class} target
739      * @param {function} callback_fn
740      * @example
741      * //unschedule a selector of target
742      * cc.Director.getInstance().getScheduler().unscheduleCallbackForTarget(function, this);
743      */
744     unscheduleCallbackForTarget:function (target, callback_fn) {
745         // explicity handle nil arguments when removing an object
746         if ((target == null) || (callback_fn == null)) {
747             return;
748         }
749 
750         var element = this._hashForTimers[target.__instanceId];
751         if (element != null) {
752             for (var i = 0; i < element.timers.length; i++) {
753                 var timer = element.timers[i];
754                 if (callback_fn == timer._selector) {
755                     if ((timer == element.currentTimer) && (!element.currentTimerSalvaged)) {
756                         element.currentTimerSalvaged = true;
757                     }
758                     cc.ArrayRemoveObjectAtIndex(element.timers, i);
759                     //update timerIndex in case we are in tick;, looping over the actions
760                     if (element.timerIndex >= i) {
761                         element.timerIndex--;
762                     }
763 
764                     if (element.timers.length == 0) {
765                         if (this._currentTarget == element) {
766                             this._currentTargetSalvaged = true;
767                         } else {
768 
769                             this._removeHashElement(element);
770                         }
771                     }
772                     return;
773                 }
774             }
775         }
776     },
777 
778     /**
779      * Unschedules the update callback function for a given target
780      * @param {cc.Class} target
781      * @example
782      * //unschedules the "update" method.
783      * cc.Director.getInstance().getScheduler().unscheduleUpdateForTarget(this);
784      */
785     unscheduleUpdateForTarget:function (target) {
786         if (target == null) {
787             return;
788         }
789 
790         var element = this._hashForUpdates[target.__instanceId];
791         if (element != null) {
792             if (this._updateHashLocked) {
793                 element.entry.markedForDeletion = true;
794             } else {
795                 this._removeUpdateFromHash(element.entry);
796             }
797         }
798     },
799 
800     /**
801      * Unschedules all function callbacks for a given target. This also includes the "update" callback function.
802      * @param {cc.Class} target
803      */
804     unscheduleAllCallbacksForTarget:function (target) {
805         //explicit NULL handling
806         if (target == null) {
807             return;
808         }
809 
810         var element = this._hashForTimers[target.__instanceId];
811         if (element) {
812             if ((!element.currentTimerSalvaged) && (cc.ArrayContainsObject(element.timers, element.currentTimer))) {
813                 element.currentTimerSalvaged = true;
814             }
815             element.timers.length = 0;
816 
817             if (this._currentTarget == element) {
818                 this._currentTargetSalvaged = true;
819             } else {
820                 this._removeHashElement(element);
821             }
822         }
823         // update selector
824         this.unscheduleUpdateForTarget(target);
825     },
826 
827     /**
828      *  <p>
829      *      Unschedules all function callbacks from all targets. <br/>
830      *      You should NEVER call this method, unless you know what you are doing.
831      *  </p>
832      */
833     unscheduleAllCallbacks:function () {
834         this.unscheduleAllCallbacksWithMinPriority(cc.PRIORITY_SYSTEM);
835     },
836 
837     /**
838      * <p>
839      *    Unschedules all function callbacks from all targets with a minimum priority.<br/>
840      *    You should only call this with kCCPriorityNonSystemMin or higher.
841      * </p>
842      * @param {Number} minPriority
843      */
844     unscheduleAllCallbacksWithMinPriority:function (minPriority) {
845         // Custom Selectors
846         var i;
847         for (i = 0; i < this._arrayForTimers.length; i++) {
848             // element may be removed in unscheduleAllCallbacksForTarget
849             this.unscheduleAllCallbacksForTarget(this._arrayForTimers[i].target);
850         }
851 
852         //updates selectors
853         if (minPriority < 0) {
854             for (i = 0; i < this._updatesNegList.length; i++) {
855                 this.unscheduleUpdateForTarget(this._updatesNegList[i].target);
856             }
857         }
858 
859         if (minPriority <= 0) {
860             for (i = 0; i < this._updates0List.length; i++) {
861                 this.unscheduleUpdateForTarget(this._updates0List[i].target);
862             }
863         }
864 
865         for (i = 0; i < this._updatesPosList.length; i++) {
866             if (this._updatesPosList[i].priority >= minPriority) {
867                 this.unscheduleUpdateForTarget(this._updatesPosList[i].target);
868             }
869         }
870     },
871 
872     /**
873      * <p>
874      *  Pause all selectors from all targets.<br/>
875      *  You should NEVER call this method, unless you know what you are doing.
876      * </p>
877      */
878     pauseAllTargets:function () {
879         return this.pauseAllTargetsWithMinPriority(cc.PRIORITY_SYSTEM);
880     },
881 
882     /**
883      * Pause all selectors from all targets with a minimum priority. <br/>
884      * You should only call this with kCCPriorityNonSystemMin or higher.
885      * @param minPriority
886      */
887     pauseAllTargetsWithMinPriority:function (minPriority) {
888         var idsWithSelectors = [];
889 
890         var i, element;
891         // Custom Selectors
892         for (i = 0; i < this._arrayForTimers.length; i++) {
893             element = this._arrayForTimers[i];
894             if (element) {
895                 element.paused = true;
896                 idsWithSelectors.push(element.target);
897             }
898         }
899 
900         // Updates selectors
901         if (minPriority < 0) {
902             for (i = 0; i < this._updatesNegList.length; i++) {
903                 element = this._updatesNegList[i];
904                 if (element) {
905                     element.paused = true;
906                     idsWithSelectors.push(element.target);
907                 }
908             }
909         }
910 
911         if (minPriority <= 0) {
912             for (i = 0; i < this._updates0List.length; i++) {
913                 element = this._updates0List[i];
914                 if (element) {
915                     element.paused = true;
916                     idsWithSelectors.push(element.target);
917                 }
918             }
919         }
920 
921         for (i = 0; i < this._updatesPosList.length; i++) {
922             element = this._updatesPosList[i];
923             if (element) {
924                 element.paused = true;
925                 idsWithSelectors.push(element.target);
926             }
927         }
928 
929         return idsWithSelectors;
930     },
931 
932     /**
933      * Resume selectors on a set of targets.<br/>
934      * This can be useful for undoing a call to pauseAllCallbacks.
935      * @param targetsToResume
936      */
937     resumeTargets:function (targetsToResume) {
938         if (!targetsToResume)
939             return;
940 
941         for (var i = 0; i < targetsToResume.length; i++) {
942             this.resumeTarget(targetsToResume[i]);
943         }
944     },
945 
946     /**
947      * <p>
948      *    Pauses the target.<br/>
949      *    All scheduled selectors/update for a given target won't be 'ticked' until the target is resumed.<br/>
950      *    If the target is not present, nothing happens.
951      * </p>
952      * @param {cc.Class} target
953      */
954     pauseTarget:function (target) {
955         if(!target)
956             throw "cc.Scheduler.pauseTarget():target should be non-null";
957 
958         //customer selectors
959         var element = this._hashForTimers[target.__instanceId];
960         if (element) {
961             element.paused = true;
962         }
963 
964         //update selector
965         var elementUpdate = this._hashForUpdates[target.__instanceId];
966         if (elementUpdate) {
967             elementUpdate.entry.paused = true;
968         }
969     },
970 
971     /**
972      * Resumes the target.<br/>
973      * The 'target' will be unpaused, so all schedule selectors/update will be 'ticked' again.<br/>
974      * If the target is not present, nothing happens.
975      * @param {cc.Class} target
976      */
977     resumeTarget:function (target) {
978         if(!target)
979             throw "cc.Scheduler.resumeTarget():target should be non-null";
980 
981         // custom selectors
982         var element = this._hashForTimers[target.__instanceId];
983 
984         if (element) {
985             element.paused = false;
986         }
987 
988         //update selector
989         var elementUpdate = this._hashForUpdates[target.__instanceId];
990 
991         if (elementUpdate) {
992             elementUpdate.entry.paused = false;
993         }
994     },
995 
996     /**
997      * Returns whether or not the target is paused
998      * @param {cc.Class} target
999      * @return {Boolean}
1000      */
1001     isTargetPaused:function (target) {
1002         if(!target)
1003             throw "cc.Scheduler.isTargetPaused():target should be non-null";
1004 
1005         // Custom selectors
1006         var element = this._hashForTimers[target.__instanceId];
1007         if (element) {
1008             return element.paused;
1009         }
1010         return false;
1011     }
1012 });
1013 
1014