Pman.Builder.JsRender.js
[Pman.Builder] / Pman.Builder.JsRender.js
1 /**
2  * This is baed on the code in the app builder..
3  *
4  *
5  * usage:
6  *
7 <pre><code>
8    render = new Pman.Builder.JsRender(
9           Pman.Builder.Tree.toJS()
10      );
11     
12     var source = render.toSource();
13 </code></pre>    
14  * 
15  * @cfg {Array} items - the array of items..
16  * @cfg {String} name - eg. Pman.Tab.XXXXyyyyy
17  
18  * @cfg {String} path - needs the full path << we only have the class name..
19  * @cfg {String} region - ?? needed anymore?
20  * @cfg {String} parent
21  * @cfg {String}  title
22  * @cfg {String}  disable? function call to disable it?
23  * @cfg {String} permname
24  * @cfg {Number} modOrder
25  *
26  */ 
27 Pman.Builder.JsRender = function(cfg) {
28     Roo.apply(this, cfg);
29 }
30
31 Pman.Builder.JsRender.prototype =  {
32     
33         
34     name : '',
35     path : '', // the file path..
36     modOrder : '001', /// sequence id that this uses.
37     region : 'center',
38     parent : '',
39     title : '', // the title on displayed when loading.
40     disable : '', // use a function to that returns false to disable this..
41     permname: '', /// permission name
42     
43     items: false,
44     
45     /**
46      * @type {Object} the properties that have to be double quoted to enable translation
47      */
48     doubleStringProps : [ 
49         'title',
50         'legend',
51         'loadingText',
52         'emptyText',
53         'qtip',
54         'value',
55         'text',
56         'emptyMsg',
57         'displayMsg'
58     ],
59     
60   
61     /**
62      * 
63      * munge JSON tree into Javascript code.
64      * 
65      * FIXME: + or / prefixes to properties hide it from renderer.
66      * FIXME: '*props' - not supported by this.. ?? - upto rendering code..
67      * FIXME: needs to understand what properties might be translatable (eg. double quotes)
68      * 
69      * @arg {object} obj the object or array to munge..
70      * @arg {boolean} isListener - is the array being sent a listener..
71      * @arg {string} pad - the padding to indent with. 
72      */
73     
74     
75     mungeToString:  function(obj, isListener, pad)
76     {
77         
78          
79         pad = pad || '    ';
80         var keys = [];
81         var isArray = false;
82         isListener = isListener || false;
83          
84         // am I munging a object or array...
85         if (obj.constructor.toString() === Array.toString()) {
86             for (var i= 0; i < obj.length; i++) {
87                 keys.push(i);
88             }
89             isArray = true;
90         } else {
91             for (var i in obj) {
92                 keys.push(i);
93             }
94         }
95         
96         
97         var els = []; 
98         var skip = [];
99         if (!isArray && 
100                 typeof(obj['|xns']) != 'undefined' &&
101                 typeof(obj['xtype']) != 'undefined'
102             ) {
103                 this.mungeXtype(obj['|xns'] + '.' + obj['xtype'], els);
104                 //els.push('xtype: '+ obj['|xns'] + '.' + obj['xtype']);
105                 skip.push('|xns','xtype');
106             }
107         
108         
109         if (!isArray && obj.items && obj.items.length) {
110             // look for props..
111             var newitems = [];
112             obj.items.forEach(function(pl) {
113                 if (typeof(pl['*prop']) == 'undefined') {
114                     newitems.push(pl);
115                     return;
116                 }
117                 
118                 //print(JSON.stringify(pl,null,4));
119                 // we have a prop...
120                 var prop = pl['*prop'] + '';
121                 delete pl['*prop'];
122                 if (!prop.match(/\[\]$/)) {
123                     // it's a standard prop..
124                     
125                     // munge property..??
126                     
127                     obj[prop] = pl;
128                     
129                     keys.push(prop);
130                     return;
131                 }
132                 prop  = prop.substring(0, prop.length -2); //strip []
133                 // it's an array type..
134                 obj[prop] = obj[prop]  || [];
135                 obj[prop].push(pl);
136               //  print("ADDNG PROP:" + prop + ' ' + keys.indexOf(prop) );
137                 if (keys.indexOf(prop) < 0) {
138                     keys.push(prop);
139                 }
140                 
141                 
142                 
143             });
144             obj.items = newitems;
145             if (!obj.items.length) {
146                 delete obj.items;
147             }
148             
149         }
150         
151          
152         
153         //if (isArray) { print(JSON.stringify(keys, null,4)); }
154         // keys is just the real keys of the object.
155         var _this = this;
156         
157         var left =  '';
158         
159         keys.forEach(function(i) {
160           
161             if (typeof(obj[i]) == 'undefined') { // empty or removed.
162                 return;
163             }
164             var el = obj[i];
165             if (!isArray && skip.indexOf(i) > -1) { // things we do not write..
166                 return;
167             }
168             if (!isArray) {
169                 // set the key to be quoted with singel quotes..
170                 var leftv = i[0] == '|' ? i.substring(1) : i;
171                 if (Lang.isKeyword(leftv) || Lang.isBuiltin(leftv)) {
172                     left = "'" + leftv + "'";
173                 } else if (leftv.match(/[^A-Z_]+/i)) { // not plain a-z... - quoted.
174                     var val = JSON.stringify(leftv);
175                     left = "'" + val.substring(1, val.length-1).replace(/'/g, "\\'") + "'";
176                 } else {
177                     left = '' + leftv;
178                 }
179                 left += ' : ';
180                 
181             }
182             
183             
184             if (isListener) {
185                 // change the lines...
186                 var str= ('' + obj[i]).replace(/^\s+|\s+$/g,""); // remove bar.
187                 var lines = str.split("\n");
188                 if (lines.length > 1) {
189                     str = lines.join("\n" + pad);
190                 }
191                 
192                 els.push(left  + str);
193                 return;
194             }
195              
196             
197             
198             //var left = isArray ? '' : (JSON.stringify(i) + " : " )
199             
200             if (i[0] == '|') {
201                 // does not hapepnd with arrays..
202                 if (typeof(el) == 'string' && !obj[i].length) { //skip empty.
203                     return;
204                 }
205                 // this needs to go...
206                 //if (typeof(el) == 'string'  && obj[i].match(new RegExp("Gtk.main" + "_quit"))) { // we can not handle this very well..
207                 //    return;
208                 //}
209                 
210                 var str= ('' + obj[i]).replace(/^\s+|\s+$/g,"");;
211                 var lines = str.split("\n");
212                 if (lines.length > 1) {
213                     str = lines.join("\n" + pad);
214                 }
215                 
216                 els.push(left + str);
217                 return;
218             }
219             
220             
221             
222             
223             if (typeof(el) == 'object') {
224                 
225                 // we can skip empty items lists and empty listeners..
226                 //if (!isArray && i == 'items' && !el.length) {
227                 //    return; 
228                 //}
229                // 
230                 var right = _this.mungeToString(el, i == 'listeners', pad + '    ');
231                 
232                 //if (!left.length && isArray) print(right);
233                 
234                 if ((typeof(right) != 'undefined') && right.length){
235                     els.push(left + right);
236                 }
237             
238                 return;
239             }
240             // standard. .
241             if (typeof(obj[i]) != 'string') {
242                 els.push(left + JSON.stringify(obj[i]));
243                 return;
244             }
245             // strings..
246             if (!_this.doubleStringProps) {
247                 els.push(left + JSON.stringify(obj[i]));
248                 return;
249             }
250             if (_this.doubleStringProps.indexOf(i) > -1) {
251                 els.push(left + JSON.stringify(obj[i]));
252                 return;
253             }
254             // single quote..
255             els.push(left + "'" + obj[i].replace(/'/g, "\\'") + "'");
256             
257
258         });
259         
260         if (!isArray && !els.length) {
261             return '';
262         }
263         //output the thing.
264         var spad = pad.substring(0, pad.length-4);
265         return (isArray ? '[' : '{') + "\n" +
266             pad  + els.join(",\n" + pad ) + 
267             "\n" + spad + (isArray ? ']' : '}');
268            
269         
270         
271     },
272     
273     
274   setNSID : function(id)
275     {
276         
277         this.items[0]['|module'] = id;
278    
279         
280     },
281     
282     
283     getType: function() {
284         return 'Roo';
285     },
286    
287    
288     /**
289      * old code had broken xtypes and used arrays differently,
290      * this code should try and clean it up..
291      * 
292      * 
293      */
294     fixItems : function(node, fixthis)
295     {
296         if (fixthis) {
297             // fix xtype.
298             var fn = this.guessName(node);
299             //print("guessname got " + fn);
300             if (fn) {
301                 var bits = fn.split('.');
302                 node.xtype = bits.pop();
303                 node['|xns'] = bits.join('.');
304                 
305             }
306             // fix array???
307              
308             
309         }
310         if (!node.items || !node.items.length) {
311             return;
312         }
313         var _this = this;
314         var aitems = [];
315         var nitems = [];
316         node.items.forEach(function(i) {
317             
318             
319             
320             _this.fixItems(i, true);
321             if (i.xtype == 'Array') {
322                 aitems.push(i);
323                 return;
324             }    
325             nitems.push(i);
326         });
327         node.items = nitems; 
328         
329         if (!aitems.length) {
330             return;
331         }
332         
333         aitems.forEach(function(i) {
334             
335             if (!i.items || !i.items.length) {
336                 return;
337             }
338             var prop = i['*prop'] + '[]';
339             // colModel to cm?
340             i.items.forEach(function(c) {
341                 c['*prop']  = prop;
342                 node.items.push(c);
343                 
344             });
345             
346             
347         });
348         
349         
350         // array handling.. 
351         
352         
353         
354         
355         
356     },
357      
358      /**
359      * convert xtype for munged output..
360      * 
361      */
362     mungeXtype : function(xtype, els)
363     {
364         var bits = xtype.split('.');
365         // assume it has lenght!
366         
367         els.push("xtype: '"+ bits.pop()+"'");
368         els.push('xns: '+ bits.join('.'));
369     },
370     
371     /**
372      * This needs to use some options on the project
373      * to determine how the file is output..
374      * 
375      * At present we are hard coding it..
376      * 
377      * 
378      */
379     toSource: function()
380     {
381         // dump the file tree back out to a string.
382         
383         // we have 2 types = dialogs and components
384         // 
385         var top = this.guessName(this.items[0]);
386         if (!top) {
387             return false;
388         }
389         if (top.match(/Dialog/)) {
390             return this.toSourceDialog();
391         }
392         return this.toSourceLayout();
393         
394         /*
395         eventually support 'classes??'
396          return this.toSourceStdClass();
397         */
398           
399     },
400    
401     outputHeader : function()
402     {
403         return [
404             "//<script type=\"text/javascript\">",
405             "",
406             "// Auto generated file - created by app.Builder.js- do not edit directly (at present!)",
407             ""
408         ].join("\n");
409         
410    
411     },
412     // a standard dialog module.
413     // fixme - this could be alot neater..
414     toSourceDialog : function() 
415     {
416         var items = JSON.parse(JSON.stringify(this.items[0]));
417         var o = this.mungeToString(items, false, '            ');   
418         return [
419             this.outputHeader(),
420             this.name + " = {",
421             "",
422             "    dialog : false,",
423             "    callback:  false,",
424             "",   
425             "    show : function(data, cb)",
426             "    {",
427             "        if (!this.dialog) {",
428             "            this.create();",
429             "        }",
430             "",
431             "        this.callback = cb;",
432             "        this.data = data;",
433             "        this.dialog.show(this.data._el);",
434             "        if (this.form) {",
435             "           this.form.reset();",
436             "           this.form.setValues(data);",
437             "           this.form.fireEvent('actioncomplete', this.form,  { type: 'setdata', data: data });",
438             "        }",
439             "",   
440             "    },",
441             "",
442             "    create : function()",
443             "    {",
444             "        var _this = this;",
445             "        this.dialog = Roo.factory(" + o +  ");",
446             "    }",
447             "};",
448             ""
449             
450          ].join("\n");
451          
452          
453          
454     },
455     
456     /**
457      * try to remove Pman.Tab.XXXXXYYYY
458      * where XXXX= the project name..
459      *
460      */
461     pathToPart : function()
462     {
463         
464         
465         var modname = this.name.split('.').pop();
466         
467         var ret = modname.match(/([A-Z][a-z]+)/g);
468         var mm = ret.shift();
469         return [ mm, ret.join('')];
470         
471         //return [ modname , npart];
472         
473         
474         
475         
476     },
477     
478     // a layout compoent 
479     toSourceLayout : function() 
480     {
481         var items = JSON.parse(JSON.stringify(this.items[0]));
482         var o = this.mungeToString(items, false, '            ');   
483          
484         var modkey = this.modOrder + '-' + this.name.replace(/[^A-Z.]+/ig, '-');
485         
486         
487         if (this.name.match(/^Pman/)) {
488             
489              
490             // old BC way we did things..
491             return [
492                 this.outputHeader(),
493                 "",
494                 "",
495                 "// register the module first",
496                 "Pman.on('beforeload', function()",
497                 "{",
498                 "    Pman.register({",
499                 "        part :  "+ JSON.stringify(this.pathToPart()) + ",", /// critical used by builder to associate modules/parts/persm
500                 "        modKey : '" +modkey+"',",
501                 "        module : " + this.name + ",",
502                 "        region : '" + this.region   +"',",
503                 "        parent : " + (this.parent ||  'false') + ",",
504                 "        name : " + JSON.stringify(this.title  || "unnamed module") + ",",
505                 "        disabled : " + (this.disabled || 'false') +", ",
506                 "        permname: '" + (this.permname|| '') +"' ",
507                 "    });",
508                 "});",
509                 "",
510                 
511                 this.name  +  " = new Roo.util.Observable({",
512                 "",
513                 "    panel : false,",
514                 "    disabled : false,",
515                 "    parentLayout:  false,",
516                 "",
517                 "    add : function(parentLayout, region)",
518                 "    {",
519                 "",
520                 "        var _this = this;", // standard avaialbe..
521                 "        this.parentLayout = parentLayout;",
522                 "",
523                 "        this.panel = parentLayout.addxtype(" + o +  ");",
524                 "        this.layout = this.panel.layout;",
525                 "",
526                 "    }",
527                 "});",
528                 ""
529                  
530                 
531              ].join("\n");
532         }
533         
534     
535         return [
536             this.outputHeader(),
537             
538             this.name  +  " = new Roo.XComponent({",
539             "    order    : '" +modkey+"',",
540             "    region   : '" + this.region   +"',",
541             "    parent   : "+ (this.parent ?  "'" + this.parent + "'" :  'false') + ",",
542             "    name     : " + JSON.stringify(this.title  || "unnamed module") + ",",
543             "    disabled : " + (this.disabled || 'false') +", ",
544             "    tree : function()",
545             "    {",
546             "        var _this = this;", // bc
547             "        var MODULE = this;", /// this looks like a better name.
548             "        return " + o + ';',
549             "    }",
550             "});",
551             ""
552              
553          ].join("\n");
554         
555     },
556         
557     guessName : function(ar) // turns the object into full name.
558     {
559          // eg. xns: Roo, xtype: XXX -> Roo.xxx
560         if (!ar) {
561             return false;
562         }
563         var ret = [];
564         ret.push(typeof( ar['|xns'] ) == 'undefined' ? 'Roo' : ar['|xns'] );
565         
566         
567         
568         if (typeof( ar['xtype'] ) == 'undefined' || !ar['xtype'].length) {
569             return false;
570         }
571         var xtype = ar['xtype'] + '';
572         if (xtype[0] == '*') { // prefixes????
573             xtype  = xtype.substring(1);
574         }
575         if (xtype.match(/^Roo/)) {
576             // already starts with roo...
577             ret = [];
578         }
579         ret.push(xtype);
580         return   ret.join('.');
581         
582          
583                         
584                              
585     },
586    
587     
588 };
589