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 ; //-1 as we usuall concat onto the existin gline?
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..
134 public Gee.ArrayList<string> orderedPropKeys() {
136 var ret = new Gee.ArrayList<string> ();
137 var niter = this.out_props.map_iterator();
138 while(niter.next()) {
139 ret.add(niter.get_key());
142 ret.sort(( a, b) => {
143 return ((string)a).collate((string)b);
144 //if (a == b) return 0;
145 //return a < b ? -1 : 1;
149 public Gee.ArrayList<string> orderedListenerKeys() {
151 var ret = new Gee.ArrayList<string> ();
152 var niter = this.out_listeners.map_iterator();
153 while(niter.next()) {
154 ret.add(niter.get_key());
157 ret.sort(( a, b) => {
158 return ((string)a).collate((string)b);
159 //if (a == b) return 0;
160 //return a < b ? -1 : 1;
166 public string mungeOut()
168 this.node.line_start = this.cur_line;
169 this.top.node.setNodeLine(this.cur_line, this.node);
170 var spad = this.pad.substring(0, this.pad.length-indent);
172 if (this.node.props.has_key("* xinclude")) {
173 this.addLine("Roo.apply(" + this.node.props.get("* xinclude") + "._tree(), {");
179 // output the items...
180 // work out remaining items...
181 var total_nodes = this.out_props.size +
182 this.out_props_array_plain.size +
183 (this.out_listeners.size > 0 ? 1 : 0) +
184 this.out_nodeprops.size +
185 this.out_props_array.size +
186 (this.out_children.size > 0 ? 1 : 0);
191 var niter = this.out_nodeprops.map_iterator();
193 while(niter.next()) {
195 suffix = total_nodes > 0 ? "," : "";
196 var l = this.pad + niter.get_key() + " : " +
197 this.mungeChildNew(this.pad + indent_str, niter.get_value()) + suffix;
198 this.addMultiLine(l);
202 var iter = this.orderedPropKeys().list_iterator();
205 suffix = total_nodes > 0 ? "," : "";
207 var v = this.out_props.get(k);
209 this.addMultiLine(this.pad + k + " : " + v + suffix);
212 // out_props_array_plain -- not used?
213 var paiter = this.out_props_array_plain.map_iterator();
215 while(paiter.next()) {
218 this.addLine(this.pad + paiter.get_key() + " : [");
219 var paliter = paiter.get_value().list_iterator();
220 while (paliter.next()) {
221 suffix = paliter.has_next() ? "," : "";
222 this.addMultiLine(this.pad + indent_str + paliter.get() + suffix);
225 suffix = total_nodes > 0 ? "," : "";
226 // this.mungeChild(this.pad + indent_str, niter.get_value())
227 this.addLine(this.pad + "]" + suffix);
235 var piter = this.out_props_array.map_iterator();
237 while(piter.next()) {
240 this.addLine(this.pad + piter.get_key() + " : [");
241 var pliter = piter.get_value().list_iterator();
242 while (pliter.next()) {
243 suffix = pliter.has_next() ? "," : "";
244 this.addMultiLine(this.pad + indent_str +
245 this.mungeChildNew(this.pad + indent_str + indent_str, pliter.get()) + suffix);
248 suffix = total_nodes > 0 ? "," : "";
250 this.addLine(this.pad + "]" + suffix);
254 if (this.out_listeners.size > 0 ) {
256 this.addLine(this.pad + "listeners : {");
257 iter = this.orderedListenerKeys().list_iterator();
259 var sz = this.out_listeners.size;
262 suffix = sz > 0 ? "," : "";
264 var v = this.out_listeners.get(k);
265 this.addMultiLine(this.pad + indent_str + k + " : " + v + suffix);
267 suffix = total_nodes > 0 ? "," : "";
268 this.addLine(this.pad + "}" + suffix);
272 if (this.out_children.size > 0) {
273 this.addLine(this.pad + "items : [" );
274 var cniter = this.out_children.list_iterator();
275 while (cniter.next()) {
276 suffix = cniter.has_next() ? "," : "";
277 this.addMultiLine(this.pad + indent_str +
278 this.mungeChildNew(this.pad + indent_str + indent_str, cniter.get()) + suffix
283 this.addLine(this.pad + "]");
286 if (this.node.props.has_key("* xinclude")) {
287 this.ret += spad + "})";
290 this.ret += spad + "}";
292 this.node.line_end = this.cur_line;
302 public void addLine(string str= "")
305 this.ret += str+ "\n";
306 //this.ret += "/*%d*/ ".printf(this.cur_line -1) + str + "\n";
311 public void addMultiLine(string str= "")
314 this.cur_line += str.split("\n").length;
315 //this.ret += "/*%d*/ ".printf(l) + str + "\n";
316 this.ret += str + "\n";
319 string gLibStringListJoin( string sep, Gee.ArrayList<string> ar)
322 for (var i = 0; i < ar.size; i++) {
323 ret += i>0 ? sep : "";
329 public string mungeChild(string pad , Node cnode)
331 var x = new NodeToJs(cnode, this.doubleStringProps, pad, this);
335 public string mungeChildNew(string pad , Node cnode )
337 var x = new NodeToJs(cnode, this.doubleStringProps, pad, this);
345 public void checkChildren ()
349 // look throught he chilren == looking for * prop.. -- fixme might not work..
352 if (!this.node.hasChildren()) {
357 for (var ii =0; ii< this.node.items.size; ii++) {
358 var pl = this.node.items.get(ii);
359 if (!pl.props.has_key("* prop")) {
364 //print(JSON.stringify(pl,null,4));
366 //var prop = pl['*prop'] + '';
367 //delete pl['*prop'];
368 var prop = pl.get("* prop");
369 print("got prop "+ prop + "\n");
372 if (! Regex.match_simple("\\[\\]$", prop)) {
373 // it's a standard prop..
375 // munge property..??
377 this.out_nodeprops.set(prop, pl);
379 this.els.add( prop + " : " + this.mungeChild ( this.pad + indent_str, pl));
389 var sprop = prop.replace("[]", "");
390 print("sprop is : " + sprop + "\n");
392 // it's an array type..
394 if (!this.ar_props.has_key(sprop)) {
396 this.ar_props.set(sprop, "");
397 this.out_props_array.set(sprop, new Gee.ArrayList<Node>());
399 old = this.ar_props.get(sprop);
401 var nstr = old += old.length > 0 ? ",\n" : "";
402 nstr += this.mungeChild( this.pad + indent_str + indent_str + indent_str , pl);
403 this.out_props_array.get(sprop).add( pl);
404 this.ar_props.set(sprop, nstr);
411 * Standardize this crap...
413 * standard properties (use to set)
414 * If they are long values show the dialog..
417 * bool is_xxx :: can show a pulldown.. (true/false)
419 * $ string html = string with value interpolated eg. baseURL + ".."
420 * Clutter.ActorAlign x_align (typed) -- shows pulldowns if type is ENUM?
421 * $ untypedvalue = javascript untyped value...
422 * _ string html ... = translatable..
425 * object properties (not part of the GOjbect being wrapped?
426 * # Gee.ArrayList<Xcls_fileitem> fileitems
431 * methods -- always text editor..
439 * * init -- big string?
441 * event handlers (listeners)
446 * +XXXX -- indicates it's a instance property / not glob...
447 * *XXXX -- skip writing glob property (used as classes that can be created...)
451 public void readProps()
456 if (this.node.props.has_key("$ xns")) {
457 this.out_props.set("'|xns'", "'" + this.node.props.get("$ xns") + "'" );
459 this.els.add("'|xns' : '" + this.node.props.get("$ xns") + "'");
465 func_regex = new Regex("^\\s+|\\s+$");
467 print("failed to build regex");
470 // sort the key's so they always get rendered in the same order..
472 var keys = new Gee.ArrayList<string>();
473 var piter = this.node.props.map_iterator();
474 while (piter.next() ) {
478 this.node.normalize_key(piter.get_key(), out k, out kflag, out ktype);
483 keys.sort(( a, b) => {
484 return ((string)a).collate((string)b);
485 //if (a == b) return 0;
486 //return a < b ? -1 : 1;
491 for (var i = 0; i< keys.size; i++) {
492 var key = this.node.get_key(keys.get(i));
493 print("ADD KEY %s\n", key);
498 this.node.normalize_key(key, out k, out kflag, out ktype);
501 var v = this.node.get(key);
504 //if (this.skip.contains(k) ) {
507 if ( Regex.match_simple("\\[\\]$", k)) {
508 // array .. not supported... here?
514 // skip builder stuff. prefixed with '.' .. just like unix fs..
515 if (kflag == ".") { // |. or . -- do not output..
519 // ignore '* prop'; ???
524 if (Lang.isKeyword(leftv) || Lang.isBuiltin(leftv)) {
525 left = "'" + leftv + "'";
526 } else if (Regex.match_simple("[^A-Za-z_]+",leftv)) { // not plain a-z... - quoted.
527 var val = this.node.quoteString(leftv);
529 left = "'" + val.substring(1, val.length-2).replace("'", "\\'") + "'";
535 // next.. is it a function.. or a raw string..
543 // ??? any others that are raw output..
545 // does not hapepnd with arrays..
546 if (v.length < 1) { //if (typeof(el) == 'string' && !obj[i].length) { //skip empty.
553 str = func_regex.replace(v,v.length, 0, "");
555 print("regex failed");
561 var lines = str.split("\n");
563 if (lines.length > 0) {
564 nstr = string.joinv("\n" + this.pad, lines);
565 //nstr = string.joinv("\n", lines);
567 this.out_props.set(left, nstr);
568 //print("==> " + str + "\n");
569 this.els.add(left + " : "+ nstr);
580 ktype.down() == "boolean"
582 ktype.down() == "bool"
584 ktype.down() == "number"
586 ktype.down() == "int"
587 ) { // boolean or number...?
588 this.out_props.set(left, v.down());
589 this.els.add(left + " : " + v.down() );
593 // is it a translated string?
599 //if (this.doubleStringProps.size < 1) {
600 // this.els.add(left + this.node.quoteString(v));
604 if ((this.doubleStringProps.index_of(k) > -1) ||
605 (ktype.down() == "string" && k[0] == '_')
608 // then use the translated version...
611 (v.split("\n").length > 1 ?
612 ("\n" + string.joinv(this.pad + "\n", v.split("\n")).replace("*/", "* - /") + "\n" + this.pad + "*/ ") :
613 (v.replace("*/", "* - /") + " */")
616 this.els.add(left + " : _this._strings['" +
617 GLib.Checksum.compute_for_string (ChecksumType.MD5, v) +
620 this.out_props.set(left, "_this._strings['" +
621 GLib.Checksum.compute_for_string (ChecksumType.MD5, v) +
626 // otherwise it needs to be encapsulated.. as single quotes..
628 var vv = this.node.quoteString(v);
629 // single quote.. v.substring(1, v.length-1).replace("'", "\\'") + "'";
630 this.els.add(left + " : " + "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
631 this.out_props.set(left, "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
638 public void readArrayProps()
641 // this is not needed in the new version
642 // as array props are handled more directly..
644 // handle the childitems that are arrays.. eg. button[] = { }...
646 // note this does not handle a mix of nodes and properties with the same
650 var iter = this.ar_props.map_iterator();
651 while (iter.next()) {
652 var k = iter.get_key();
653 var right = iter.get_value();
655 string leftv = k[0] == '|' ? k.substring(1) : k;
656 if (Lang.isKeyword(leftv) || Lang.isBuiltin(leftv)) {
657 left = "'" + leftv + "'";
658 } else if (Regex.match_simple("[^A-Za-z_]+",leftv)) { // not plain a-z... - quoted.
659 var val = this.node.quoteString(leftv);
661 left = "'" + val.substring(1, val.length-2).replace("'", "\\'") + "'";
667 if (right.length > 0){
668 //if (this.out_props_array_plain.has_key(left)) {
669 // this.out_props_array_plain.set(left, new Gee.ArrayList<string>());
671 //this.out_props_array_plain.get(left).add(right);
673 this.els.add(left + " : [\n" + this.pad + indent_str + indent_str +
674 right + "\n" + this.pad + "]");
681 public void readListeners()
684 if (this.node.listeners.size < 1) {
687 // munge the listeners.
688 //print("ADDING listeners?");
693 var keys = new Gee.ArrayList<string>();
694 var piter = this.node.listeners.map_iterator();
695 while (piter.next() ) {
697 keys.add(piter.get_key());
699 keys.sort(( a, b) => {
700 return ((string)a).collate((string)b);
701 //if (a == b) return 0;
702 //return a < b ? -1 : 1;
705 var itms = "listeners : {\n";
707 for (var i = 0; i< keys.size; i++) {
708 var key = keys.get(i);
709 var val = this.node.listeners.get(key);
712 itms += i >0 ? ",\n" : "";
714 var str = val.strip();
715 var lines = str.split("\n");
716 if (lines.length > 0) {
717 //str = string.joinv("\n" + this.pad + " ", lines);
718 str = string.joinv("\n" + this.pad + indent_str + indent_str , lines);
721 itms += this.pad + indent_str + key.replace("|", "") + " : " + str;
722 this.out_listeners.set(key.replace("|", "") ,str);
726 itms += "\n" + this.pad + "}";
727 //print ( "ADD " + itms);
732 public void iterChildren()
736 // finally munge the children...
737 if (this.node.items.size < 1) {
740 var itms = "items : [\n";
742 for(var i = 0; i < this.node.items.size;i++) {
743 var ele = this.node.items.get(i);
744 if (ele.props.has_key("* prop")) {
751 itms += this.pad + indent_str +
752 this.mungeChild( this.pad + indent_str + indent_str , ele);
753 this.out_children.add(ele);
756 itms += "\n"+ this.pad + "]" + "\n";
760 // finally output listeners...
762 public void xIncludeToString()