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;
62 node.node_lines = new Gee.ArrayList<int>();
63 node.node_lines_map = new Gee.HashMap<int,Node>();
72 public string munge ( )
74 //return this.mungeToString(this.node);
76 this.node.line_start = this.cur_line;
80 this.readArrayProps();
83 if (!this.node.props.has_key("* xinclude")) {
89 // no properties to output...
90 if (this.els.size < 1) {
99 var spad = this.pad.substring(0, this.pad.length-indent);
102 var str_props = gLibStringListJoin(",\n" + this.pad , this.els) ;
103 //print ("STR PROPS: " + str_props);
107 if (!this.node.props.has_key("* xinclude")) {
109 this.pad + str_props +
115 return "Roo.apply(" + this.node.props.get("* xinclude") + "._tree(), "+
117 this.pad + str_props +
123 This currently works by creating a key/value array of this.els, which is just an array of properties..
124 this is so that join() works...
128 b) output plan properties.
129 c) output listeners..
131 g) output prop_arrays..
139 public Gee.ArrayList<string> orderedPropKeys() {
141 var ret = new Gee.ArrayList<string> ();
142 var niter = this.out_props.map_iterator();
143 while(niter.next()) {
144 ret.add(niter.get_key());
147 ret.sort(( a, b) => {
148 return ((string)a).collate((string)b);
149 //if (a == b) return 0;
150 //return a < b ? -1 : 1;
154 public Gee.ArrayList<string> orderedListenerKeys() {
156 var ret = new Gee.ArrayList<string> ();
157 var niter = this.out_listeners.map_iterator();
158 while(niter.next()) {
159 ret.add(niter.get_key());
162 ret.sort(( a, b) => {
163 return ((string)a).collate((string)b);
164 //if (a == b) return 0;
165 //return a < b ? -1 : 1;
171 public string mungeOut()
173 this.node.line_start = this.cur_line;
174 this.top.node.setNodeLine(this.cur_line, this.node);
175 var spad = this.pad.substring(0, this.pad.length-indent);
177 if (this.node.props.has_key("* xinclude")) {
178 this.addLine("Roo.apply(" + this.node.props.get("* xinclude") + "._tree(), {");
184 // output the items...
185 // work out remaining items...
186 var total_nodes = this.out_props.size +
187 this.out_props_array_plain.size +
188 (this.out_listeners.size > 0 ? 1 : 0) +
189 this.out_nodeprops.size +
190 this.out_props_array.size +
191 (this.out_children.size > 0 ? 1 : 0);
196 var niter = this.out_nodeprops.map_iterator();
198 while(niter.next()) {
200 suffix = total_nodes > 0 ? "," : "";
201 var l = this.pad + niter.get_key() + " : " +
202 this.mungeChildNew(this.pad + indent_str, niter.get_value()) + suffix;
203 this.addMultiLine(l);
207 var iter = this.orderedPropKeys().list_iterator();
210 suffix = total_nodes > 0 ? "," : "";
212 var v = this.out_props.get(k);
214 this.addMultiLine(this.pad + k + " : " + v + suffix);
217 // out_props_array_plain -- not used?
218 var paiter = this.out_props_array_plain.map_iterator();
220 while(paiter.next()) {
223 this.addLine(this.pad + paiter.get_key() + " : [");
224 var paliter = paiter.get_value().list_iterator();
225 while (paliter.next()) {
226 suffix = paliter.has_next() ? "," : "";
227 this.addMultiLine(this.pad + indent_str + paliter.get() + suffix);
230 suffix = total_nodes > 0 ? "," : "";
231 // this.mungeChild(this.pad + indent_str, niter.get_value())
232 this.addLine(this.pad + "]" + suffix);
240 var piter = this.out_props_array.map_iterator();
242 while(piter.next()) {
245 this.addLine(this.pad + piter.get_key() + " : [");
246 var pliter = piter.get_value().list_iterator();
247 while (pliter.next()) {
248 suffix = pliter.has_next() ? "," : "";
249 this.addMultiLine(this.pad + indent_str +
250 this.mungeChildNew(this.pad + indent_str + indent_str, pliter.get()) + suffix);
253 suffix = total_nodes > 0 ? "," : "";
255 this.addLine(this.pad + "]" + suffix);
259 if (this.out_listeners.size > 0 ) {
261 this.addLine(this.pad + "listeners : {");
262 iter = this.orderedListenerKeys().list_iterator();
264 var sz = this.out_listeners.size;
267 suffix = sz > 0 ? "," : "";
269 var v = this.out_listeners.get(k);
270 this.addMultiLine(this.pad + indent_str + k + " : " + v + suffix);
272 suffix = total_nodes > 0 ? "," : "";
273 this.addLine(this.pad + "}" + suffix);
277 if (this.out_children.size > 0) {
278 this.addLine(this.pad + "items : [" );
279 var cniter = this.out_children.list_iterator();
280 while (cniter.next()) {
281 suffix = cniter.has_next() ? "," : "";
282 this.addMultiLine(this.pad + indent_str +
283 this.mungeChildNew(this.pad + indent_str + indent_str, cniter.get()) + suffix
288 this.addLine(this.pad + "]");
291 if (this.node.props.has_key("* xinclude")) {
292 this.ret += spad + "})";
295 this.ret += spad + "}";
297 this.node.line_end = this.cur_line;
298 this.node.sortLines();
307 public void addLine(string str= "")
310 this.ret += str+ "\n";
311 //this.ret += "/*%d*/ ".printf(this.cur_line -1) + str + "\n";
316 public void addMultiLine(string str= "")
319 this.cur_line += str.split("\n").length;
320 //this.ret += "/*%d*/ ".printf(l) + str + "\n";
321 this.ret += str + "\n";
324 string gLibStringListJoin( string sep, Gee.ArrayList<string> ar)
327 for (var i = 0; i < ar.size; i++) {
328 ret += i>0 ? sep : "";
334 public string mungeChild(string pad , Node cnode)
336 var x = new NodeToJs(cnode, this.doubleStringProps, pad, this);
340 public string mungeChildNew(string pad , Node cnode )
342 var x = new NodeToJs(cnode, this.doubleStringProps, pad, this);
350 public void checkChildren ()
354 // look throught he chilren == looking for * prop.. -- fixme might not work..
357 if (!this.node.hasChildren()) {
362 for (var ii =0; ii< this.node.items.size; ii++) {
363 var pl = this.node.items.get(ii);
364 if (!pl.props.has_key("* prop")) {
369 //print(JSON.stringify(pl,null,4));
371 //var prop = pl['*prop'] + '';
372 //delete pl['*prop'];
373 var prop = pl.get("* prop");
374 print("got prop "+ prop + "\n");
377 if (! Regex.match_simple("\\[\\]$", prop)) {
378 // it's a standard prop..
380 // munge property..??
382 this.out_nodeprops.set(prop, pl);
384 this.els.add( prop + " : " + this.mungeChild ( this.pad + indent_str, pl));
394 var sprop = prop.replace("[]", "");
395 print("sprop is : " + sprop + "\n");
397 // it's an array type..
399 if (!this.ar_props.has_key(sprop)) {
401 this.ar_props.set(sprop, "");
402 this.out_props_array.set(sprop, new Gee.ArrayList<Node>());
404 old = this.ar_props.get(sprop);
406 var nstr = old += old.length > 0 ? ",\n" : "";
407 nstr += this.mungeChild( this.pad + indent_str + indent_str + indent_str , pl);
408 this.out_props_array.get(sprop).add( pl);
409 this.ar_props.set(sprop, nstr);
416 * Standardize this crap...
418 * standard properties (use to set)
419 * If they are long values show the dialog..
422 * bool is_xxx :: can show a pulldown.. (true/false)
424 * $ string html = string with value interpolated eg. baseURL + ".."
425 * Clutter.ActorAlign x_align (typed) -- shows pulldowns if type is ENUM?
426 * $ untypedvalue = javascript untyped value...
427 * _ string html ... = translatable..
430 * object properties (not part of the GOjbect being wrapped?
431 * # Gee.ArrayList<Xcls_fileitem> fileitems
436 * methods -- always text editor..
444 * * init -- big string?
446 * event handlers (listeners)
451 * +XXXX -- indicates it's a instance property / not glob...
452 * *XXXX -- skip writing glob property (used as classes that can be created...)
456 public void readProps()
461 if (this.node.props.has_key("$ xns")) {
462 this.out_props.set("'|xns'", "'" + this.node.props.get("$ xns") + "'" );
464 this.els.add("'|xns' : '" + this.node.props.get("$ xns") + "'");
470 func_regex = new Regex("^\\s+|\\s+$");
472 print("failed to build regex");
475 // sort the key's so they always get rendered in the same order..
477 var keys = new Gee.ArrayList<string>();
478 var piter = this.node.props.map_iterator();
479 while (piter.next() ) {
483 this.node.normalize_key(piter.get_key(), out k, out kflag, out ktype);
488 keys.sort(( a, b) => {
489 return ((string)a).collate((string)b);
490 //if (a == b) return 0;
491 //return a < b ? -1 : 1;
496 for (var i = 0; i< keys.size; i++) {
497 var key = this.node.get_key(keys.get(i));
498 print("ADD KEY %s\n", key);
503 this.node.normalize_key(key, out k, out kflag, out ktype);
506 var v = this.node.get(key);
509 //if (this.skip.contains(k) ) {
512 if ( Regex.match_simple("\\[\\]$", k)) {
513 // array .. not supported... here?
519 // skip builder stuff. prefixed with '.' .. just like unix fs..
520 if (kflag == ".") { // |. or . -- do not output..
524 // ignore '* prop'; ???
529 if (Lang.isKeyword(leftv) || Lang.isBuiltin(leftv)) {
530 left = "'" + leftv + "'";
531 } else if (Regex.match_simple("[^A-Za-z_]+",leftv)) { // not plain a-z... - quoted.
532 var val = this.node.quoteString(leftv);
534 left = "'" + val.substring(1, val.length-2).replace("'", "\\'") + "'";
540 // next.. is it a function.. or a raw string..
548 // ??? any others that are raw output..
550 // does not hapepnd with arrays..
551 if (v.length < 1) { //if (typeof(el) == 'string' && !obj[i].length) { //skip empty.
558 str = func_regex.replace(v,v.length, 0, "");
560 print("regex failed");
566 var lines = str.split("\n");
568 if (lines.length > 0) {
569 nstr = string.joinv("\n" + this.pad, lines);
570 //nstr = string.joinv("\n", lines);
572 this.out_props.set(left, nstr);
573 //print("==> " + str + "\n");
574 this.els.add(left + " : "+ nstr);
585 ktype.down() == "boolean"
587 ktype.down() == "bool"
589 ktype.down() == "number"
591 ktype.down() == "int"
592 ) { // boolean or number...?
593 this.out_props.set(left, v.down());
594 this.els.add(left + " : " + v.down() );
598 // is it a translated string?
604 //if (this.doubleStringProps.size < 1) {
605 // this.els.add(left + this.node.quoteString(v));
609 if ((this.doubleStringProps.index_of(k) > -1) ||
610 (ktype.down() == "string" && k[0] == '_')
613 // then use the translated version...
616 (v.split("\n").length > 1 ?
617 ("\n" + string.joinv(this.pad + "\n", v.split("\n")).replace("*/", "* - /") + "\n" + this.pad + "*/ ") :
618 (v.replace("*/", "* - /") + " */")
621 this.els.add(left + " : _this._strings['" +
622 GLib.Checksum.compute_for_string (ChecksumType.MD5, v) +
625 this.out_props.set(left, "_this._strings['" +
626 GLib.Checksum.compute_for_string (ChecksumType.MD5, v) +
631 // otherwise it needs to be encapsulated.. as single quotes..
633 var vv = this.node.quoteString(v);
634 // single quote.. v.substring(1, v.length-1).replace("'", "\\'") + "'";
635 this.els.add(left + " : " + "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
636 this.out_props.set(left, "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
643 public void readArrayProps()
646 // this is not needed in the new version
647 // as array props are handled more directly..
649 // handle the childitems that are arrays.. eg. button[] = { }...
651 // note this does not handle a mix of nodes and properties with the same
655 var iter = this.ar_props.map_iterator();
656 while (iter.next()) {
657 var k = iter.get_key();
658 var right = iter.get_value();
660 string leftv = k[0] == '|' ? k.substring(1) : k;
661 if (Lang.isKeyword(leftv) || Lang.isBuiltin(leftv)) {
662 left = "'" + leftv + "'";
663 } else if (Regex.match_simple("[^A-Za-z_]+",leftv)) { // not plain a-z... - quoted.
664 var val = this.node.quoteString(leftv);
666 left = "'" + val.substring(1, val.length-2).replace("'", "\\'") + "'";
672 if (right.length > 0){
673 //if (this.out_props_array_plain.has_key(left)) {
674 // this.out_props_array_plain.set(left, new Gee.ArrayList<string>());
676 //this.out_props_array_plain.get(left).add(right);
678 this.els.add(left + " : [\n" + this.pad + indent_str + indent_str +
679 right + "\n" + this.pad + "]");
686 public void readListeners()
689 if (this.node.listeners.size < 1) {
692 // munge the listeners.
693 //print("ADDING listeners?");
698 var keys = new Gee.ArrayList<string>();
699 var piter = this.node.listeners.map_iterator();
700 while (piter.next() ) {
702 keys.add(piter.get_key());
704 keys.sort(( a, b) => {
705 return ((string)a).collate((string)b);
706 //if (a == b) return 0;
707 //return a < b ? -1 : 1;
710 var itms = "listeners : {\n";
712 for (var i = 0; i< keys.size; i++) {
713 var key = keys.get(i);
714 var val = this.node.listeners.get(key);
717 itms += i >0 ? ",\n" : "";
719 var str = val.strip();
720 var lines = str.split("\n");
721 if (lines.length > 0) {
722 //str = string.joinv("\n" + this.pad + " ", lines);
723 str = string.joinv("\n" + this.pad + indent_str + indent_str , lines);
726 itms += this.pad + indent_str + key.replace("|", "") + " : " + str;
727 this.out_listeners.set(key.replace("|", "") ,str);
731 itms += "\n" + this.pad + "}";
732 //print ( "ADD " + itms);
737 public void iterChildren()
741 // finally munge the children...
742 if (this.node.items.size < 1) {
745 var itms = "items : [\n";
747 for(var i = 0; i < this.node.items.size;i++) {
748 var ele = this.node.items.get(i);
749 if (ele.props.has_key("* prop")) {
756 itms += this.pad + indent_str +
757 this.mungeChild( this.pad + indent_str + indent_str , ele);
758 this.out_children.add(ele);
761 itms += "\n"+ this.pad + "]" + "\n";
765 // finally output listeners...
767 public void xIncludeToString()