1 // ==========================================================================
  2 // Project:   SC.Statechart - A Statechart Framework for SproutCore
  3 // Copyright: ©2010, 2011 Michael Cohen, and contributors.
  4 //            Portions @2011 Apple Inc. All rights reserved.
  5 // License:   Licensed under MIT license (see license.js)
  6 // ==========================================================================
  7 
  8 /*globals SC */
  9 
 10 /**
 11   Extends the JS Function object with the handleEvents method that
 12   will provide more advanced event handling capabilities when constructing
 13   your statechart's states.
 14 
 15   By default, when you add a method to a state, the state will react to
 16   events that matches a method's name, like so:
 17 
 18   {{{
 19 
 20     state = SC.State.extend({
 21 
 22       // Will be invoked when a event named "foo" is sent to this state
 23       foo: function(event, sender, context) { ... }
 24 
 25     })
 26 
 27   }}}
 28 
 29   In some situations, it may be advantageous to use one method that can react to
 30   multiple events instead of having multiple methods that essentially all do the
 31   same thing. In order to set a method to handle more than one event you use
 32   the handleEvents method which can be supplied a list of string and/or regular
 33   expressions. The following example demonstrates the use of handleEvents:
 34 
 35   {{{
 36 
 37     state = SC.State.extend({
 38 
 39       eventHandlerA: function(event, sender, context) {
 40 
 41       }.handleEvents('foo', 'bar'),
 42 
 43       eventHandlerB: function(event, sender, context) {
 44 
 45       }.handleEvents(/num\d/, 'decimal')
 46 
 47     })
 48 
 49   }}}
 50 
 51   Whenever events 'foo' and 'bar' are sent to the state, the method eventHandlerA
 52   will be invoked. When there is an event that matches the regular expression
 53   /num\d/ or the event is 'decimal' then eventHandlerB is invoked. In both
 54   cases, the name of the event will be supplied to the event handler.
 55 
 56   It should be noted that the use of regular expressions may impact performance
 57   since that statechart will not be able to fully optimize the event handling logic based
 58   on its use. Therefore the use of regular expression should be used sparingly.
 59 
 60   @param {(String|RegExp)...} args
 61 */
 62 Function.prototype.handleEvents = function() {
 63   this.isEventHandler = YES;
 64   this.events = arguments;
 65   return this;
 66 };
 67 
 68 /**
 69   Extends the JS Function object with the stateObserves method that will
 70   create a state observe handler on a given state object.
 71 
 72   Use a stateObserves() instead of the common observes() method when you want a
 73   state to observer changes to some property on the state itself or some other
 74   object.
 75 
 76   Any method on the state that has stateObserves is considered a state observe
 77   handler and behaves just like when you use observes() on a method, but with an
 78   important difference. When you apply stateObserves to a method on a state, those
 79   methods will be active *only* when the state is entered, otherwise those methods
 80   will be inactive. This removes the need for you having to explicitly call
 81   addObserver and removeObserver. As an example:
 82 
 83   {{{
 84 
 85     state = SC.State.extend({
 86 
 87       foo: null,
 88 
 89       user: null,
 90 
 91       observeHandlerA: function(target, key) {
 92 
 93       }.stateObserves('MyApp.someController.status'),
 94 
 95       observeHandlerB: function(target, key) {
 96 
 97       }.stateObserves('foo'),
 98 
 99       observeHandlerC: function(target, key) {
100 
101       }.stateObserves('.user.name', '.user.salary')
102 
103     })
104 
105   }}}
106 
107   Above, state has three state observe handlers: observeHandlerA, observeHandlerB, and
108   observeHandlerC. When state is entered, the state will automatically add itself as
109   an observer for all of its registered state observe handlers. Therefore when
110   foo changes, observeHandlerB will be invoked, and when MyApp.someController's status
111   changes then observeHandlerA will be invoked. The moment that state is exited then
112   the state will automatically remove itself as an observer for all of its registered
113   state observe handlers. Therefore none of the state observe handlers will be
114   invoked until the next time the state is entered.
115 
116   @param {String...} args
117 */
118 Function.prototype.stateObserves = function() {
119   this.isStateObserveHandler = YES;
120 
121   // Fast arguments access.
122   // Accessing `arguments.length` is just a Number and doesn't materialize the `arguments` object, which is costly.
123   this.args = new Array(arguments.length); // SC.A(arguments)
124   for (var i = 0, len = this.args.length; i < len; i++) { this.args[i] = arguments[i]; }
125 
126   return this;
127 };
128