dbgenerate.js
[app.Builder.js] / Observable.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11 XObject = imports.XObject.XObject;
12 /**
13  * @class Observable
14  * Base class that provides a common interface for publishing events. Subclasses are expected to
15  * to have a property "events" with all the events defined.<br>
16  * For example:
17  * <pre><code>
18  Employee = XObject.define(
19       function(name){
20         this.name = name;
21         this.addEvents({
22             "fired" : true,
23             "quit" : true
24         });
25      },
26      Observable, {});
27      
28 </code></pre>
29  * @param {Object} config properties to use (incuding events / listeners)
30  */
31  
32  
33 Observable = XObject.define(
34     function(cfg){
35         
36         cfg = cfg|| {};
37         this.addEvents(cfg.events || {});
38         if (cfg.events) {
39             delete cfg.events; // make sure
40         }
41          
42         XObject.extend(this, cfg);
43         
44         if(this.listeners){
45             this.on(this.listeners);
46             delete this.listeners;
47         }
48     },
49     Object,  
50     {
51         /** 
52          * @cfg {Object} listeners  list of events and functions to call for this object, 
53          * For example :
54          * <pre><code>
55             listeners :  { 
56                'click' : function(e) {
57                    ..... 
58                 } ,
59                 .... 
60             } 
61           </code></pre>
62          */
63             
64         
65         /**
66          * Fires the specified event with the passed parameters (minus the event name).
67          * @param {String} eventName
68          * @param {Object...} args Variable number of parameters are passed to handlers
69          * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
70          */
71         fireEvent : function(){
72             var ce = this.events[arguments[0].toLowerCase()];
73             if(typeof ce == "object"){
74                 return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
75             }else{
76                 return true;
77             }
78         },
79
80         // private - blcoks these types of signals?
81         filterOptRe : /^(?:scope|delay|buffer|single)$/,
82
83         /**
84          * Appends an event handler to this component
85          * @param {String}   eventName The type of event to listen for
86          * @param {Function} handler The method the event invokes
87          * @param {Object}   scope (optional) The scope in which to execute the handler
88          * function. The handler function's "this" context.
89          * @param {Object}   options (optional) An object containing handler configuration
90          * properties. This may contain any of the following properties:<ul>
91          * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
92          * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.NOT AVAIALBLE</li>
93          * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
94          * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link DelayedEvent} delayed NOT AVAIALBLE
95          * by the specified number of milliseconds. If the event fires again within that time, the original
96          * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
97          * </ul><br>
98          * <p>
99          * <b>Combining Options</b><br>
100          * Using the options argument, it is possible to combine different types of listeners:<br>
101          * <br>
102          * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
103             <pre><code>
104             el.on('click', this.onClick, this, {
105                 single: true,
106                 delay: 100,
107                 forumId: 4
108             });
109             </code></pre>
110          * <p>
111          * <b>Attaching multiple handlers in 1 call</b><br>
112          * The method also allows for a single argument to be passed which is a config object containing properties
113          * which specify multiple handlers.
114          * <pre><code>
115             el.on({
116                 'click': {
117                     fn: this.onClick,
118                     scope: this,
119                     delay: 100
120                 }, 
121                 'mouseover': {
122                     fn: this.onMouseOver,
123                     scope: this
124                 },
125                 'mouseout': {
126                     fn: this.onMouseOut,
127                     scope: this
128                 }
129             });
130             </code></pre>
131          * <p>
132          * Or a shorthand syntax which passes the same scope object to all handlers:
133             <pre><code>
134             el.on({
135                 'click': this.onClick,
136                 'mouseover': this.onMouseOver,
137                 'mouseout': this.onMouseOut,
138                 scope: this
139             });
140             </code></pre>
141          */
142         on : function(eventName, fn, scope, o){
143             if(typeof eventName == "object"){
144                 o = eventName;
145                 for(var e in o){
146                     if(this.filterOptRe.test(e)){
147                         continue;
148                     }
149                     if(typeof o[e] == "function"){
150                         // shared options
151                         this.on(e, o[e], o.scope,  o);
152                     }else{
153                         // individual options
154                         this.on(e, o[e].fn, o[e].scope, o[e]);
155                     }
156                 }
157                 return;
158             }
159             o = (!o || typeof o == "boolean") ? {} : o;
160             eventName = eventName.toLowerCase();
161             var ce = this.events[eventName] || true;
162             if(typeof ce == "boolean"){
163                 ce = new Event(this, eventName);
164                 this.events[eventName] = ce;
165             }
166             ce.addListener(fn, scope, o);
167         },
168
169         /**
170          * Removes a listener
171          * @param {String}   eventName     The type of event to listen for
172          * @param {Function} handler        The handler to remove
173          * @param {Object}   scope  (optional) The scope (this object) for the handler
174          */
175         un : function(eventName, fn, scope){
176             var ce = this.events[eventName.toLowerCase()];
177             if(typeof ce == "object"){
178                 ce.removeListener(fn, scope);
179             }
180         },
181
182         /**
183          * Removes all listeners for this object
184          */
185         purgeListeners : function(){
186             for(var evt in this.events){
187                 if(typeof this.events[evt] == "object"){
188                      this.events[evt].clearListeners();
189                 }
190             }
191         },
192         _combine : function(){
193             var as = arguments, l = as.length, r = [];
194             for(var i = 0; i < l; i++){
195                 var a = as[i];
196                 if(a instanceof Array){
197                     r = r.concat(a);
198                 }else if(a.length !== undefined && !a.substr){
199                     r = r.concat(Array.prototype.slice.call(a, 0));
200                 }else{
201                     r.push(a);
202                 }
203             }
204             return r;
205         },
206         
207         relayEvents : function(o, events){
208             var createHandler = function(ename){
209                 return function(){
210                     return this.fireEvent.apply(this, Event.prototype.combine(ename, 
211                             Array.prototype.slice.call(arguments, 0)));
212                 };
213             };
214             for(var i = 0, len = events.length; i < len; i++){
215                 var ename = events[i];
216                 if(!this.events[ename]){ this.events[ename] = true; };
217                 o.on(ename, createHandler(ename), this);
218             }
219         },
220
221         /**
222          * Used to define events on this Observable
223          * @param {Object} object The object with the events defined
224          */
225         addEvents : function(o){
226             if(!this.events){
227                 this.events = {};
228             }
229             XObject.extendIf(this.events, o);
230         },
231
232         /**
233          * Checks to see if this object has any listeners for a specified event
234          * @param {String} eventName The name of the event to check for
235          * @return {Boolean} True if the event is being listened for, else false
236          */
237         hasListener : function(eventName){
238             var e = this.events[eventName];
239             return typeof e == "object" && e.listeners.length > 0;
240         }
241 });
242  
243 /**
244  * Starts capture on the specified Observable. All events will be passed
245  * to the supplied function with the event name + standard signature of the event
246  * <b>before</b> the event is fired. If the supplied function returns false,
247  * the event will not fire.
248  * @param {Observable} o The Observable to capture
249  * @param {Function} fn The function to call
250  * @param {Object} scope (optional) The scope (this object) for the fn
251  * @static
252  */
253 Observable.capture = function(o, fn, scope){
254     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
255 };
256
257 /**
258  * Removes <b>all</b> added captures from the Observable.
259  * @param {Observable} o The Observable to release
260  * @static
261  */
262 Observable.releaseCapture = function(o){
263     o.fireEvent = Observable.prototype.fireEvent;
264 };
265
266  
267
268 var createSingle = function(h, e, fn, scope){
269     return function(){
270         e.removeListener(fn, scope);
271         return h.apply(scope, arguments);
272     };
273 };
274
275 // NOT SUPPORTED YET>
276 //var createBuffered = function(h, o, scope){
277 //    var task = new DelayedTask();
278 //    return function(){
279 //        task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
280 //    };
281 //};
282
283
284 //var createDelayed = function(h, o, scope){
285 //    return function(){
286 //        var args = Array.prototype.slice.call(arguments, 0);
287 //        setTimeout(function(){
288 //            h.apply(scope, args);
289 //        }, o.delay || 10);
290 //    };
291 //};
292
293
294 /**
295  * Event Object - manages a specific event.
296  * 
297  * 
298  * 
299  */
300
301
302
303 Event = XObject.define(
304     function(obj, name){
305         this.name = name;
306         this.obj = obj;
307         this.listeners = [];
308     },
309     Object, 
310     {
311         addListener : function(fn, scope, options){
312             var o = options || {};
313             scope = scope || this.obj;
314             if(!this.isListening(fn, scope)){
315                 var l = {fn: fn, scope: scope, options: o};
316                 var h = fn;
317                // if(o.delay){
318                 //       h = createDelayed(h, o, scope);
319                // }
320                 if(o.single){
321                     h = createSingle(h, this, fn, scope);
322                 }
323                 //if(o.buffer){
324                //     h = createBuffered(h, o, scope);
325                 //}
326                 l.fireFn = h;
327                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
328                     this.listeners.push(l);
329                 }else{
330                     this.listeners = this.listeners.slice(0);
331                     this.listeners.push(l);
332                 }
333             }
334         },
335
336         findListener : function(fn, scope){
337             scope = scope || this.obj;
338             var ls = this.listeners;
339             for(var i = 0, len = ls.length; i < len; i++){
340                 var l = ls[i];
341                 if(l.fn == fn && l.scope == scope){
342                     return i;
343                 }
344             }
345             return -1;
346         },
347
348         isListening : function(fn, scope){
349             return this.findListener(fn, scope) != -1;
350         },
351
352         removeListener : function(fn, scope){
353             var index;
354             if((index = this.findListener(fn, scope)) != -1){
355                 if(!this.firing){
356                     this.listeners.splice(index, 1);
357                 }else{
358                     this.listeners = this.listeners.slice(0);
359                     this.listeners.splice(index, 1);
360                 }
361                 return true;
362             }
363             return false;
364         },
365
366         clearListeners : function(){
367             this.listeners = [];
368         },
369
370         fire : function(){
371             var ls = this.listeners, scope, len = ls.length;
372             if(len > 0){
373                 this.firing = true;
374                 var args = Array.prototype.slice.call(arguments, 0);
375                 for(var i = 0; i < len; i++){
376                     var l = ls[i];
377                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
378                         this.firing = false;
379                         return false;
380                     }
381                 }
382                 this.firing = false;
383             }
384             return true;
385         }
386     }
387 );