generate gjs documentation, run under gjs
[gnome.introspection-doc-generator] / XObject.js
1 //<script type="text/javascript">
2 /*
3  * Copyright(c) 2010 Alan Knowles <alan@akbkhome.com>
4  * Licence - LGPL
5  */
6  
7 /**
8  * XObject
9  * Yet another attempt to create a usable object construction library for seed..
10  * 
11  * Why is this useful?
12  * A) It turns rather messy code into a tree structure, making it easy to find code relating to 
13  *    an interface element
14  * B) In theory it should be gjs/Seed compatible..
15  * C) It provides getElementById style lookups for elements.
16  * D) It provides classic OO constructors for Javascript (extend/define)
17  * E) It does not modify any buildin prototypes.. 
18  *
19  * Extend this.. to use it's wonderful features..
20  * 
21  * normal usage:
22  * XObject = imports.XObject.XObject;
23  * 
24  * Xyz = new XObject({
25  *     xtype: Gtk.Window,
26  *     id : 'window',
27  *     items : [
28  *     
29  *     ]
30  *  });
31  *  Xyz.init(); // create and show.
32  * 
33  * 
34  * 
35  * @arg xtype     {String|Function} constructor or string.
36  * @arg id        {String}  (optional) id for registry
37  * @arg xns       {String|Object}   (optional) namespace eg. Gtk or 'Gtk' - used with xtype.
38  * @arg items     {Array}   (optional) list of child elements which will be constructed.. using XObject
39  * @arg listeners {Object}   (optional) map Gobject signals to functions
40  * @arg pack      {Function|String|Array}   (optional) how this object gets added to it's parent
41  * @arg el        {Object}   (optional) premade GObject
42  * 
43  *  --- needs a xdebug option!
44  * 
45  * 
46  * He's some questions.
47  * - should we generate ID's for all elements? (if so we probably need to garbage collect)
48  * - should we have a special property to use as the constructor / gobject.properties rather
49  *   than sending all basic types to this?
50  * 
51  * 
52  */
53
54 function XObject (cfg) {
55     // first apply cfg if set.
56     this.config = cfg;
57     if (cfg.init) {
58         this.init = cfg.init; // override!
59     }
60 }
61
62 XObject.prototype = {
63     /**
64      * @property el {GObject} the Gtk / etc. element.
65      */
66     el : false, 
67     /*
68      * @property items {Array} list of sub elements
69      */
70     /**
71      * @property parent {XObject} parent Element
72      */
73      
74      /**
75      * @property config {Object} the construction configuration.
76      */
77      /**
78       * @method init
79       * Initializes the Element (el) hooks up all the listeners
80       * and packs the children.
81       * you can override this, in child objects, then 
82       * do this to do thi initaliztion.
83       * 
84       * XObject.prototype.init.call(this); 
85       * 
86       */ 
87     init : function()
88     {
89         var cfg = this.config;
90     
91         print("new xobj?"  + XObject.keys(cfg).join(','));
92         //print(cfg);
93         o =  {};
94         
95         cfg.items = cfg.items || [];
96         
97         XObject.extend(o, cfg); // copy everything into o.
98         
99         o.pack = typeof(o.pack) == 'undefined' ? 'add' : o.pack;
100         
101         XObject.extend(this, o);
102
103         // remove items.
104         
105         this.listeners = this.listeners || {}; 
106         this.items = [];
107         
108         // remove objects/functions from o, so they can be sent to the contructor.
109         for (var i in o) {
110             if ((typeof(o[i]) == 'object') || 
111                 (typeof(o[i]) == 'function') || 
112                 i == 'pack'   ||
113                 i == 'id'     ||
114                 i == 'xtype'  ||
115                 i == 'xdebug' ||
116                 i == 'xns'
117             ) {
118                 delete o[i];
119             }
120         }
121         
122         // do we need to call 'beforeInit here?'
123          
124         // handle include?
125         //if ((this.xtype == 'Include')) {
126         //    o = this.pre_registry[cls];
127         //}
128         var isSeed = typeof(Seed) != 'undefined';
129          
130         // xtype= Gtk.Menu ?? what about c_new stuff?
131         print(this.xtype);
132         if (typeof(this.xtype) == 'function') {
133             print("func?"  + XObject.keys(o).join(','));
134             this.el = this.el ||   this.xtype(o);
135         }
136         if (typeof(this.xtype) == 'object') {
137             print("obj?"  + XObject.keys(o).join(','));
138             this.el = this.el ||  new this.xtype(o);
139         }
140         //print(this.el);
141         if (!this.el && o.xns) {
142             
143             var NS = imports.gi[o.xns];
144             if (!NS) {
145                 Seed.print('Invalid xns: ' + o.xns);
146             }
147             constructor = NS[o.xtype];
148             if (!constructor) {
149                 Seed.print('Invalid xtype: ' + o.xns + '.' + o.xtype);
150             }
151             this.el  =   isSeed ? new constructor(o) : new constructor();
152             
153         }
154         // always overlay props..
155         for (var i in o) {
156             this.el[i] = o[i];
157         }
158         // register it!
159         //if (o.xnsid  && o.id) {
160          //   XObject.registry = XObject.registry || { };
161          //   XObject.registry[o.xnsid] = XObject.registry[o.xnsid] || {}; 
162          //   XObject.registry[o.xnsid][o.id] = this;
163         //}
164         
165         cfg.items.forEach(this.addItem, this);
166         
167         for (var i in this.listeners) {
168             this.addListener(i, this.listeners[i]);
169         }
170         // delete this.listeners ?
171         
172         
173         // do we need to call 'init here?'
174     },
175       
176      
177      /**
178       * @method addItem
179       * Adds an item to the object using a new XObject
180       * uses pack property to determine how to add it.
181       * @arg cfg {Object} same as XObject constructor.
182       */
183     addItem : function(o) {
184
185         var item = (o.constructor == XObject) ? o : new XObject(o);
186         item.init();
187         item.parent = this;
188         this.items.push(item);
189         
190         if (item.pack===false) {  // no 
191             return;
192         }
193
194         if (typeof(item.pack) == 'function') {
195             // parent, child
196             item.pack.apply(o, [ o , o.items[i] ]);
197             item.parent = this;
198             return;
199         }
200
201         var args = [];
202         var pack_m  = false;
203         if (typeof(item.pack) == 'string') {
204             pack_m = item.pack;
205         } else {
206             pack_m = item.pack.shift();
207             args = item.pack;
208         }
209         
210         // handle error.
211         if (pack_m && typeof(this.el[pack_m]) == 'undefined') {
212             Seed.print('pack method not available : ' + this.xtype + '.' +  pack_m);
213             return;
214         }
215         
216         
217         //Seed.print('Pack ' + this.el + '.'+ pack_m + '(' + item.el + ')');
218
219         args.unshift(item.el);
220         print('[' + args.join(',') +']');
221         //Seed.print('args: ' + args.length);
222         if (pack_m) {
223             this.el[pack_m].apply(this.el, args);
224         }
225     },
226
227     /**
228      * @method addListener
229      * Connects a method to a signal. (gjs/Seed aware)
230      *
231      * @arg sig  {String} name of signal
232      * @arg fn  {Function} handler.
233      */
234     addListener  : function(sig, fn) 
235     {
236  
237         Seed.print("Add signal " + sig);
238  
239         var _li = XObject.createDelegate(fn,this);
240         // private listeners that are not copied to GTk.
241         
242         if (typeof(Seed) != 'undefined') {
243           //   Seed.print(typeof(_li));
244             this.el.signal[sig].connect(_li);
245         } else {
246             this.el.connect( sig, _li);
247         }
248     },
249
250     /**
251      * @method get
252      * Finds an object in the child elements using xid of object.
253      * prefix with '.' to look up the tree.. multiple '..' to look further up..
254      *
255      * @arg name  {String} name of signal
256      * @return   {XObject|false} the object if found.
257      */
258     get : function(xid)
259     {
260         var ret=  false;
261         if (xid[0] == '.') {
262             return this.parent.get(xid.substring(1));
263         }
264
265         this.items.forEach(function(ch) {
266             if (ch.id == xid) {
267                 ret = ch;
268                 return true;
269             } else
270                 return false;
271         });
272
273         if (ret) {
274             return ret;
275         }
276
277         // iterate children.
278         this.items.forEach(function(ch) {
279             ret = ch.get(xid);
280             if (ret) {
281                 return true;
282             } else
283                 return false;
284         });
285
286         return ret;
287     }
288
289
290
291 /**
292  * Copies all the properties of config to obj.
293  *
294  * Pretty much the same as JQuery/Prototype..
295  * @param {Object} obj The receiver of the properties
296  * @param {Object} config The source of the properties
297  * @param {Object} defaults A different object that will also be applied for default values
298  * @return {Object} returns obj
299  * @member XObject extend
300  */
301 XObject.extend = function(o, c, defaults){
302     if(defaults){
303         // no "this" reference for friendly out of scope calls
304         XObject.extend(o, defaults);
305     }
306     if(o && c && typeof c == 'object'){
307         for(var p in c){
308             o[p] = c[p];
309         }
310     }
311     return o;
312 };
313
314 XObject.extend(XObject,
315 {
316     /**
317      * Copies all the properties of config to obj, if the do not exist.
318      * @param {Object} obj The receiver of the properties
319      * @param {Object} config The source of the properties
320      * @return {Object} returns obj
321      * @member Object extendIf
322      */
323     extendIf : function(o, c){
324
325         if(!o || !c || typeof c != 'object'){
326             return o;
327         }
328
329         for(var p in c){
330             if (typeof(o[p]) != 'undefined') {
331                 continue;
332             }
333             o[p] = c[p];
334         }
335
336         return o;
337     },
338
339     /**
340      * Extends one class with another class and optionally overrides members with the passed literal. This class
341      * also adds the function "override()" to the class that can be used to override
342      * members on an instance.
343      *
344      * usage:
345      * MyObject = Object.define(
346      *     function(...) {
347      *          ....
348      *     },
349      *     parentClass, // or Object
350      *     {
351      *        ... methods and properties.
352      *     }
353      * });
354      *
355      * @param {Function} constructor The class inheriting the functionality
356      * @param {Object} superclass The class being extended
357      * @param {Object} overrides (optional) A literal with members
358      * @return {Function} constructor (eg. class
359      * @method define
360      */
361     define : function(){
362         // inline overrides
363         var io = function(o){
364             for(var m in o){
365                 this[m] = o[m];
366             }
367         };
368
369         return function(sb, sp, overrides) {
370             if (typeof(sp) == 'undefined') {
371                 // error condition - try and dump..
372                 throw "Missing superclass: when applying: " + sb
373             }
374
375             var F = function(){}, sbp, spp = sp.prototype;
376             F.prototype = spp;
377             sbp = sb.prototype = new F();
378             sbp.constructor=sb;
379             sb.superclass=spp;
380
381             // extends Object.
382             if(spp.constructor == Object.prototype.constructor){
383                 spp.constructor=sp;
384             }
385             
386             sb.override = function(o){
387                 Object.extend(sb.prototype, o);
388             };
389             sbp.override = io;
390             XObject.extend(sb.prototype, overrides);
391             return sb;
392         };
393     }(),
394
395     /**
396      * returns a list of keys of the object.
397      * @param {Object} obj object to inspect
398      * @return {Array} returns list of kyes
399      * @member XObject keys
400      */
401     keys : function(o)
402     {
403         var ret = [];
404         for(var i in o) {
405             ret.push(i);
406         }
407         return ret;
408     },
409       
410     /**
411      * @member XObject createDelegate
412      * creates a delage metdhod
413      * @param {Function} method to wrap
414      * @param {Object} scope 
415      * @param {Array} args to add
416      * @param {Boolean|Number} append arguments or replace after N arguments.
417      * @return {Function} returns the delegate
418      * 
419      * Usage: 
420      *  function a() .... 
421      * 
422      * x = XObject.createDelegate(a, this);
423      * 
424      */
425     createDelegate : function(method, obj, args, appendArgs){
426         
427         return function() {
428             var callArgs = args || arguments;
429             if(appendArgs === true){
430                 callArgs = Array.prototype.slice.call(arguments, 0);
431                 callArgs = callArgs.concat(args);
432             }else if(typeof appendArgs == "number"){
433                 callArgs = Array.prototype.slice.call(arguments, 0);   // copy arguments first
434                     var applyArgs = [appendArgs, 0].concat(args);      // create method call params
435                     Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
436                 }
437                 return method.apply(obj || window, callArgs);
438             };
439     }
440 });