3 * Code to convert node tree to Javascript...
5 * usage : x = (new JsRender.NodeToJs(node)).munge();
12 public class JsRender.NodeToJs : Object {
14 static uint indent = 1;
15 static string indent_str = " ";
18 Gee.ArrayList<string> doubleStringProps; // need to think if this is a good idea like this
21 Gee.ArrayList<string> els;
22 //Gee.ArrayList<string> skip;
23 Gee.HashMap<string,string> ar_props;
25 Gee.HashMap<string,string> out_props;
26 Gee.HashMap<string,string> out_listeners;
27 Gee.HashMap<string,Node> out_nodeprops;
28 Gee.ArrayList<Node> out_children;
29 Gee.HashMap<string,Gee.ArrayList<Node>> out_props_array;
30 Gee.HashMap<string,Gee.ArrayList<string>> out_props_array_plain;
38 public NodeToJs( Node node, Gee.ArrayList<string> doubleStringProps, string pad, NodeToJs? parent)
41 this.doubleStringProps = doubleStringProps;
44 this.els = new Gee.ArrayList<string>();
45 this.ar_props = new Gee.HashMap<string,string>();
48 this.out_props = new Gee.HashMap<string,string>();
49 this.out_listeners = new Gee.HashMap<string,string>();
50 this.out_nodeprops = new Gee.HashMap<string,Node>() ;
51 this.out_children = new Gee.ArrayList<Node> ();
52 this.out_props_array = new Gee.HashMap<string,Gee.ArrayList<Node>>() ;
53 this.out_props_array_plain = new Gee.HashMap<string,Gee.ArrayList<string>>() ;
57 this.cur_line = parent == null ? 0 : parent.cur_line;
59 this.top = parent == null ? this : parent.top;
68 public string munge ( )
70 //return this.mungeToString(this.node);
72 this.node.line_start = this.cur_line;
76 this.readArrayProps();
79 if (!this.node.props.has_key("* xinclude")) {
85 // no properties to output...
86 if (this.els.size < 1) {
94 var spad = this.pad.substring(0, this.pad.length-indent);
97 var str_props = gLibStringListJoin(",\n" + this.pad , this.els) ;
98 //print ("STR PROPS: " + str_props);
102 if (!this.node.props.has_key("* xinclude")) {
104 this.pad + str_props +
110 return "Roo.apply(" + this.node.props.get("* xinclude") + "._tree(), "+
112 this.pad + str_props +
118 This currently works by creating a key/value array of this.els, which is just an array of properties..
119 this is so that join() works...
123 b) output plan properties.
124 c) output listeners..
126 g) output prop_arrays..
135 public string mungeOut()
138 var spad = this.pad.substring(0, this.pad.length-indent);
140 if (this.node.props.has_key("* xinclude")) {
141 this.addLine("Roo.apply(" + this.node.props.get("* xinclude") + "._tree(), {");
147 // output the items...
148 // work out remaining items...
149 var total_nodes = this.out_props.size +
150 this.out_props_array_plain.size +
151 (this.out_listeners.size > 0 ? 1 : 0) +
152 this.out_nodeprops.size +
153 this.out_props_array.size +
154 (this.out_children.size > 0 ? 1 : 0);
159 var niter = this.out_nodeprops.map_iterator();
161 while(niter.next()) {
163 suffix = total_nodes > 0 ? "," : "";
164 this.addMultiLine(this.pad + niter.get_key() + " : " +
165 this.mungeChildNew(this.pad + indent_str, niter.get_value()) + suffix
170 var iter = this.out_props.map_iterator();
173 suffix = total_nodes > 0 ? "," : "";
174 this.addLine(this.pad + iter.get_key() + " : " + iter.get_value() + suffix);
177 // out_props_array_plain -- not used?
178 var paiter = this.out_props_array_plain.map_iterator();
180 while(paiter.next()) {
183 this.addLine(this.pad + paiter.get_key() + " : [");
184 var paliter = paiter.get_value().list_iterator();
185 while (paliter.next()) {
186 suffix = paliter.has_next() ? "," : "";
187 this.addMultiLine(this.pad + indent_str + paliter.get() + suffix);
190 suffix = total_nodes > 0 ? "," : "";
191 // this.mungeChild(this.pad + indent_str, niter.get_value())
192 this.addLine(this.pad + "]" + suffix);
200 var piter = this.out_props_array.map_iterator();
202 while(piter.next()) {
205 this.addLine(this.pad + piter.get_key() + " : [");
206 var pliter = piter.get_value().list_iterator();
207 while (pliter.next()) {
208 suffix = pliter.has_next() ? "," : "";
209 this.addMultiLine(this.pad + indent_str +
210 this.mungeChildNew(this.pad + indent_str + indent_str, pliter.get()) + suffix);
213 suffix = total_nodes > 0 ? "," : "";
215 this.addLine(this.pad + "]" + suffix);
219 if (this.out_listeners.size > 0 ) {
221 this.addLine(this.pad + "listeners : {");
222 iter = this.out_listeners.map_iterator();
223 var sz = this.out_listeners.size;
226 suffix = sz > 0 ? "," : "";
227 this.addMultiLine(this.pad + indent_str + iter.get_key() + " : " + iter.get_value() + suffix);
229 suffix = total_nodes > 0 ? "," : "";
230 this.addLine(this.pad + "}" + suffix);
234 if (this.out_children.size > 0) {
235 this.addLine(this.pad + "items : [" );
236 var cniter = this.out_children.list_iterator();
237 while (cniter.next()) {
238 suffix = cniter.has_next() ? "," : "";
239 this.addMultiLine(this.pad + indent_str +
240 this.mungeChildNew(this.pad + indent_str + indent_str, cniter.get()) + suffix
245 this.addLine(this.pad + "]");
248 if (this.node.props.has_key("* xinclude")) {
249 this.ret += spad + "})";
252 this.ret += spad + "}";
264 public void addLine(string str= "")
267 this.ret += str+ "\n";
273 public void addMultiLine(string str= "")
276 this.cur_line += str.split("\n").length;
277 //this.ret += "/*%d*/ ".printf(l) + str + "\n";
278 this.ret += str + "\n";
281 string gLibStringListJoin( string sep, Gee.ArrayList<string> ar)
284 for (var i = 0; i < ar.size; i++) {
285 ret += i>0 ? sep : "";
291 public string mungeChild(string pad , Node cnode)
293 var x = new NodeToJs(cnode, this.doubleStringProps, pad, this);
297 public string mungeChildNew(string pad , Node cnode)
299 var x = new NodeToJs(cnode, this.doubleStringProps, pad, this);
306 public void checkChildren ()
310 // look throught he chilren == looking for * prop.. -- fixme might not work..
313 if (!this.node.hasChildren()) {
318 for (var ii =0; ii< this.node.items.size; ii++) {
319 var pl = this.node.items.get(ii);
320 if (!pl.props.has_key("* prop")) {
325 //print(JSON.stringify(pl,null,4));
327 //var prop = pl['*prop'] + '';
328 //delete pl['*prop'];
329 var prop = pl.get("* prop");
330 print("got prop "+ prop + "\n");
333 if (! Regex.match_simple("\\[\\]$", prop)) {
334 // it's a standard prop..
336 // munge property..??
338 this.out_nodeprops.set(prop, pl);
340 this.els.add( prop + " : " + this.mungeChild ( this.pad + indent_str, pl));
350 var sprop = prop.replace("[]", "");
351 print("sprop is : " + sprop + "\n");
353 // it's an array type..
355 if (!this.ar_props.has_key(sprop)) {
357 this.ar_props.set(sprop, "");
358 this.out_props_array.set(sprop, new Gee.ArrayList<Node>());
360 old = this.ar_props.get(sprop);
362 var nstr = old += old.length > 0 ? ",\n" : "";
363 nstr += this.mungeChild( this.pad + indent_str + indent_str + indent_str , pl);
364 this.out_props_array.get(sprop).add( pl);
365 this.ar_props.set(sprop, nstr);
372 * Standardize this crap...
374 * standard properties (use to set)
375 * If they are long values show the dialog..
378 * bool is_xxx :: can show a pulldown.. (true/false)
380 * $ string html = string with value interpolated eg. baseURL + ".."
381 * Clutter.ActorAlign x_align (typed) -- shows pulldowns if type is ENUM?
382 * $ untypedvalue = javascript untyped value...
383 * _ string html ... = translatable..
386 * object properties (not part of the GOjbect being wrapped?
387 * # Gee.ArrayList<Xcls_fileitem> fileitems
392 * methods -- always text editor..
400 * * init -- big string?
402 * event handlers (listeners)
407 * +XXXX -- indicates it's a instance property / not glob...
408 * *XXXX -- skip writing glob property (used as classes that can be created...)
412 public void readProps()
417 if (this.node.props.has_key("$ xns")) {
418 this.out_props.set("'|xns'", this.node.props.get("$ xns") );
420 this.els.add("'|xns' : '" + this.node.props.get("$ xns") + "'");
426 func_regex = new Regex("^\\s+|\\s+$");
428 print("failed to build regex");
431 // sort the key's so they always get rendered in the same order..
433 var keys = new Gee.ArrayList<string>();
434 var piter = this.node.props.map_iterator();
435 while (piter.next() ) {
439 this.node.normalize_key(piter.get_key(), out k, out kflag, out ktype);
444 keys.sort(( a, b) => {
445 return ((string)a).collate((string)b);
446 //if (a == b) return 0;
447 //return a < b ? -1 : 1;
452 for (var i = 0; i< keys.size; i++) {
453 var key = this.node.get_key(keys.get(i));
454 print("ADD KEY %s\n", key);
459 this.node.normalize_key(key, out k, out kflag, out ktype);
462 var v = this.node.get(key);
465 //if (this.skip.contains(k) ) {
468 if ( Regex.match_simple("\\[\\]$", k)) {
469 // array .. not supported... here?
475 // skip builder stuff. prefixed with '.' .. just like unix fs..
476 if (kflag == ".") { // |. or . -- do not output..
480 // ignore '* prop'; ???
485 if (Lang.isKeyword(leftv) || Lang.isBuiltin(leftv)) {
486 left = "'" + leftv + "'";
487 } else if (Regex.match_simple("[^A-Za-z_]+",leftv)) { // not plain a-z... - quoted.
488 var val = this.node.quoteString(leftv);
490 left = "'" + val.substring(1, val.length-2).replace("'", "\\'") + "'";
496 // next.. is it a function.. or a raw string..
504 // ??? any others that are raw output..
506 // does not hapepnd with arrays..
507 if (v.length < 1) { //if (typeof(el) == 'string' && !obj[i].length) { //skip empty.
514 str = func_regex.replace(v,v.length, 0, "");
516 print("regex failed");
522 var lines = str.split("\n");
524 if (lines.length > 0) {
525 nstr = string.joinv("\n" + this.pad, lines);
526 //nstr = string.joinv("\n", lines);
528 this.out_props.set(left, nstr);
529 //print("==> " + str + "\n");
530 this.els.add(left + " : "+ nstr);
541 ktype.down() == "boolean"
543 ktype.down() == "bool"
545 ktype.down() == "number"
547 ktype.down() == "int"
548 ) { // boolean or number...?
549 this.out_props.set(left, v.down());
550 this.els.add(left + " : " + v.down() );
554 // is it a translated string?
560 //if (this.doubleStringProps.size < 1) {
561 // this.els.add(left + this.node.quoteString(v));
565 if (this.doubleStringProps.index_of(k) > -1) {
566 // then use the translated version...
568 this.els.add(left + " : _this._strings['" +
569 GLib.Checksum.compute_for_string (ChecksumType.MD5, v) +
572 this.out_props.set(left, "_this._strings['" +
573 GLib.Checksum.compute_for_string (ChecksumType.MD5, v) +
577 if (ktype.down() == "string" && k[0] == '_') {
578 this.els.add(left + " : _this._strings['" +
579 GLib.Checksum.compute_for_string (ChecksumType.MD5, v) +
582 this.out_props.set(left, " _this._strings['" +
583 GLib.Checksum.compute_for_string (ChecksumType.MD5, v) +
590 // otherwise it needs to be encapsulated.. as single quotes..
592 var vv = this.node.quoteString(v);
593 // single quote.. v.substring(1, v.length-1).replace("'", "\\'") + "'";
594 this.els.add(left + " : " + "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
595 this.out_props.set(left, "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
602 public void readArrayProps()
605 // this is not needed in the new version
606 // as array props are handled more directly..
608 // handle the childitems that are arrays.. eg. button[] = { }...
610 // note this does not handle a mix of nodes and properties with the same
614 var iter = this.ar_props.map_iterator();
615 while (iter.next()) {
616 var k = iter.get_key();
617 var right = iter.get_value();
619 string leftv = k[0] == '|' ? k.substring(1) : k;
620 if (Lang.isKeyword(leftv) || Lang.isBuiltin(leftv)) {
621 left = "'" + leftv + "'";
622 } else if (Regex.match_simple("[^A-Za-z_]+",leftv)) { // not plain a-z... - quoted.
623 var val = this.node.quoteString(leftv);
625 left = "'" + val.substring(1, val.length-2).replace("'", "\\'") + "'";
631 if (right.length > 0){
632 //if (this.out_props_array_plain.has_key(left)) {
633 // this.out_props_array_plain.set(left, new Gee.ArrayList<string>());
635 //this.out_props_array_plain.get(left).add(right);
637 this.els.add(left + " : [\n" + this.pad + indent_str + indent_str +
638 right + "\n" + this.pad + "]");
645 public void readListeners()
648 if (this.node.listeners.size < 1) {
651 // munge the listeners.
652 //print("ADDING listeners?");
657 var keys = new Gee.ArrayList<string>();
658 var piter = this.node.listeners.map_iterator();
659 while (piter.next() ) {
661 keys.add(piter.get_key());
663 keys.sort(( a, b) => {
664 return ((string)a).collate((string)b);
665 //if (a == b) return 0;
666 //return a < b ? -1 : 1;
669 var itms = "listeners : {\n";
671 for (var i = 0; i< keys.size; i++) {
672 var key = keys.get(i);
673 var val = this.node.listeners.get(key);
676 itms += i >0 ? ",\n" : "";
678 var str = val.strip();
679 var lines = str.split("\n");
680 if (lines.length > 0) {
681 //str = string.joinv("\n" + this.pad + " ", lines);
682 str = string.joinv("\n" + this.pad + indent_str + indent_str , lines);
685 itms += this.pad + indent_str + key.replace("|", "") + " : " + str;
686 this.out_listeners.set(key.replace("|", "") ,str);
690 itms += "\n" + this.pad + "}";
691 //print ( "ADD " + itms);
696 public void iterChildren()
700 // finally munge the children...
701 if (this.node.items.size < 1) {
704 var itms = "items : [\n";
706 for(var i = 0; i < this.node.items.size;i++) {
707 var ele = this.node.items.get(i);
708 if (ele.props.has_key("* prop")) {
715 itms += this.pad + indent_str +
716 this.mungeChild( this.pad + indent_str + indent_str , ele);
717 this.out_children.add(ele);
720 itms += "\n"+ this.pad + "]" + "\n";
724 // finally output listeners...
726 public void xIncludeToString()