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) {
98 var spad = this.pad.substring(0, this.pad.length-indent);
101 var str_props = gLibStringListJoin(",\n" + this.pad , this.els) ;
102 //print ("STR PROPS: " + str_props);
106 if (!this.node.props.has_key("* xinclude")) {
108 this.pad + str_props +
114 return "Roo.apply(" + this.node.props.get("* xinclude") + "._tree(), "+
116 this.pad + str_props +
122 This currently works by creating a key/value array of this.els, which is just an array of properties..
123 this is so that join() works...
127 b) output plan properties.
128 c) output listeners..
130 g) output prop_arrays..
138 public Gee.ArrayList<string> orderedPropKeys() {
140 var ret = new Gee.ArrayList<string> ();
141 var niter = this.out_props.map_iterator();
142 while(niter.next()) {
143 ret.add(niter.get_key());
146 ret.sort(( a, b) => {
147 return ((string)a).collate((string)b);
148 //if (a == b) return 0;
149 //return a < b ? -1 : 1;
153 public Gee.ArrayList<string> orderedListenerKeys() {
155 var ret = new Gee.ArrayList<string> ();
156 var niter = this.out_listeners.map_iterator();
157 while(niter.next()) {
158 ret.add(niter.get_key());
161 ret.sort(( a, b) => {
162 return ((string)a).collate((string)b);
163 //if (a == b) return 0;
164 //return a < b ? -1 : 1;
170 public string mungeOut()
172 this.node.line_start = this.cur_line;
173 this.top.node.setNodeLine(this.cur_line, this.node);
174 var spad = this.pad.substring(0, this.pad.length-indent);
176 if (this.node.props.has_key("* xinclude")) {
177 this.addLine("Roo.apply(" + this.node.props.get("* xinclude") + "._tree(), {");
183 // output the items...
184 // work out remaining items...
185 var total_nodes = this.out_props.size +
186 this.out_props_array_plain.size +
187 (this.out_listeners.size > 0 ? 1 : 0) +
188 this.out_nodeprops.size +
189 this.out_props_array.size +
190 (this.out_children.size > 0 ? 1 : 0);
195 var niter = this.out_nodeprops.map_iterator();
197 while(niter.next()) {
199 suffix = total_nodes > 0 ? "," : "";
200 var l = this.pad + niter.get_key() + " : " +
201 this.mungeChildNew(this.pad + indent_str, niter.get_value()) + suffix;
202 this.addMultiLine(l);
206 var iter = this.orderedPropKeys().list_iterator();
209 suffix = total_nodes > 0 ? "," : "";
211 var v = this.out_props.get(k);
213 this.addMultiLine(this.pad + k + " : " + v + suffix);
216 // out_props_array_plain -- not used?
217 var paiter = this.out_props_array_plain.map_iterator();
219 while(paiter.next()) {
222 this.addLine(this.pad + paiter.get_key() + " : [");
223 var paliter = paiter.get_value().list_iterator();
224 while (paliter.next()) {
225 suffix = paliter.has_next() ? "," : "";
226 this.addMultiLine(this.pad + indent_str + paliter.get() + suffix);
229 suffix = total_nodes > 0 ? "," : "";
230 // this.mungeChild(this.pad + indent_str, niter.get_value())
231 this.addLine(this.pad + "]" + suffix);
239 var piter = this.out_props_array.map_iterator();
241 while(piter.next()) {
244 this.addLine(this.pad + piter.get_key() + " : [");
245 var pliter = piter.get_value().list_iterator();
246 while (pliter.next()) {
247 suffix = pliter.has_next() ? "," : "";
248 this.addMultiLine(this.pad + indent_str +
249 this.mungeChildNew(this.pad + indent_str + indent_str, pliter.get()) + suffix);
252 suffix = total_nodes > 0 ? "," : "";
254 this.addLine(this.pad + "]" + suffix);
258 if (this.out_listeners.size > 0 ) {
260 this.addLine(this.pad + "listeners : {");
261 iter = this.orderedListenerKeys().list_iterator();
263 var sz = this.out_listeners.size;
266 suffix = sz > 0 ? "," : "";
268 var v = this.out_listeners.get(k);
269 this.addMultiLine(this.pad + indent_str + k + " : " + v + suffix);
271 suffix = total_nodes > 0 ? "," : "";
272 this.addLine(this.pad + "}" + suffix);
276 if (this.out_children.size > 0) {
277 this.addLine(this.pad + "items : [" );
278 var cniter = this.out_children.list_iterator();
279 while (cniter.next()) {
280 suffix = cniter.has_next() ? "," : "";
281 this.addMultiLine(this.pad + indent_str +
282 this.mungeChildNew(this.pad + indent_str + indent_str, cniter.get()) + suffix
287 this.addLine(this.pad + "]");
290 if (this.node.props.has_key("* xinclude")) {
291 this.ret += spad + "})";
294 this.ret += spad + "}";
296 this.node.line_end = this.cur_line;
306 public void addLine(string str= "")
309 this.ret += str+ "\n";
310 //this.ret += "/*%d*/ ".printf(this.cur_line -1) + str + "\n";
315 public void addMultiLine(string str= "")
318 this.cur_line += str.split("\n").length;
319 //this.ret += "/*%d*/ ".printf(l) + str + "\n";
320 this.ret += str + "\n";
323 string gLibStringListJoin( string sep, Gee.ArrayList<string> ar)
326 for (var i = 0; i < ar.size; i++) {
327 ret += i>0 ? sep : "";
333 public string mungeChild(string pad , Node cnode)
335 var x = new NodeToJs(cnode, this.doubleStringProps, pad, this);
339 public string mungeChildNew(string pad , Node cnode )
341 var x = new NodeToJs(cnode, this.doubleStringProps, pad, this);
349 public void checkChildren ()
353 // look throught he chilren == looking for * prop.. -- fixme might not work..
356 if (!this.node.hasChildren()) {
361 for (var ii =0; ii< this.node.items.size; ii++) {
362 var pl = this.node.items.get(ii);
363 if (!pl.props.has_key("* prop")) {
368 //print(JSON.stringify(pl,null,4));
370 //var prop = pl['*prop'] + '';
371 //delete pl['*prop'];
372 var prop = pl.get("* prop");
373 print("got prop "+ prop + "\n");
376 if (! Regex.match_simple("\\[\\]$", prop)) {
377 // it's a standard prop..
379 // munge property..??
381 this.out_nodeprops.set(prop, pl);
383 this.els.add( prop + " : " + this.mungeChild ( this.pad + indent_str, pl));
393 var sprop = prop.replace("[]", "");
394 print("sprop is : " + sprop + "\n");
396 // it's an array type..
398 if (!this.ar_props.has_key(sprop)) {
400 this.ar_props.set(sprop, "");
401 this.out_props_array.set(sprop, new Gee.ArrayList<Node>());
403 old = this.ar_props.get(sprop);
405 var nstr = old += old.length > 0 ? ",\n" : "";
406 nstr += this.mungeChild( this.pad + indent_str + indent_str + indent_str , pl);
407 this.out_props_array.get(sprop).add( pl);
408 this.ar_props.set(sprop, nstr);
415 * Standardize this crap...
417 * standard properties (use to set)
418 * If they are long values show the dialog..
421 * bool is_xxx :: can show a pulldown.. (true/false)
423 * $ string html = string with value interpolated eg. baseURL + ".."
424 * Clutter.ActorAlign x_align (typed) -- shows pulldowns if type is ENUM?
425 * $ untypedvalue = javascript untyped value...
426 * _ string html ... = translatable..
429 * object properties (not part of the GOjbect being wrapped?
430 * # Gee.ArrayList<Xcls_fileitem> fileitems
435 * methods -- always text editor..
443 * * init -- big string?
445 * event handlers (listeners)
450 * +XXXX -- indicates it's a instance property / not glob...
451 * *XXXX -- skip writing glob property (used as classes that can be created...)
455 public void readProps()
460 if (this.node.props.has_key("$ xns")) {
461 this.out_props.set("'|xns'", "'" + this.node.props.get("$ xns") + "'" );
463 this.els.add("'|xns' : '" + this.node.props.get("$ xns") + "'");
469 func_regex = new Regex("^\\s+|\\s+$");
471 print("failed to build regex");
474 // sort the key's so they always get rendered in the same order..
476 var keys = new Gee.ArrayList<string>();
477 var piter = this.node.props.map_iterator();
478 while (piter.next() ) {
482 this.node.normalize_key(piter.get_key(), out k, out kflag, out ktype);
487 keys.sort(( a, b) => {
488 return ((string)a).collate((string)b);
489 //if (a == b) return 0;
490 //return a < b ? -1 : 1;
495 for (var i = 0; i< keys.size; i++) {
496 var key = this.node.get_key(keys.get(i));
497 print("ADD KEY %s\n", key);
502 this.node.normalize_key(key, out k, out kflag, out ktype);
505 var v = this.node.get(key);
508 //if (this.skip.contains(k) ) {
511 if ( Regex.match_simple("\\[\\]$", k)) {
512 // array .. not supported... here?
518 // skip builder stuff. prefixed with '.' .. just like unix fs..
519 if (kflag == ".") { // |. or . -- do not output..
523 // ignore '* prop'; ???
528 if (Lang.isKeyword(leftv) || Lang.isBuiltin(leftv)) {
529 left = "'" + leftv + "'";
530 } else if (Regex.match_simple("[^A-Za-z_]+",leftv)) { // not plain a-z... - quoted.
531 var val = this.node.quoteString(leftv);
533 left = "'" + val.substring(1, val.length-2).replace("'", "\\'") + "'";
539 // next.. is it a function.. or a raw string..
547 // ??? any others that are raw output..
549 // does not hapepnd with arrays..
550 if (v.length < 1) { //if (typeof(el) == 'string' && !obj[i].length) { //skip empty.
557 str = func_regex.replace(v,v.length, 0, "");
559 print("regex failed");
565 var lines = str.split("\n");
567 if (lines.length > 0) {
568 nstr = string.joinv("\n" + this.pad, lines);
569 //nstr = string.joinv("\n", lines);
571 this.out_props.set(left, nstr);
572 //print("==> " + str + "\n");
573 this.els.add(left + " : "+ nstr);
584 ktype.down() == "boolean"
586 ktype.down() == "bool"
588 ktype.down() == "number"
590 ktype.down() == "int"
591 ) { // boolean or number...?
592 this.out_props.set(left, v.down());
593 this.els.add(left + " : " + v.down() );
597 // is it a translated string?
603 //if (this.doubleStringProps.size < 1) {
604 // this.els.add(left + this.node.quoteString(v));
608 if ((this.doubleStringProps.index_of(k) > -1) ||
609 (ktype.down() == "string" && k[0] == '_')
612 // then use the translated version...
615 (v.split("\n").length > 1 ?
616 ("\n" + string.joinv(this.pad + "\n", v.split("\n")).replace("*/", "* - /") + "\n" + this.pad + "*/ ") :
617 (v.replace("*/", "* - /") + " */")
620 this.els.add(left + " : _this._strings['" +
621 GLib.Checksum.compute_for_string (ChecksumType.MD5, v) +
624 this.out_props.set(left, "_this._strings['" +
625 GLib.Checksum.compute_for_string (ChecksumType.MD5, v) +
630 // otherwise it needs to be encapsulated.. as single quotes..
632 var vv = this.node.quoteString(v);
633 // single quote.. v.substring(1, v.length-1).replace("'", "\\'") + "'";
634 this.els.add(left + " : " + "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
635 this.out_props.set(left, "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
642 public void readArrayProps()
645 // this is not needed in the new version
646 // as array props are handled more directly..
648 // handle the childitems that are arrays.. eg. button[] = { }...
650 // note this does not handle a mix of nodes and properties with the same
654 var iter = this.ar_props.map_iterator();
655 while (iter.next()) {
656 var k = iter.get_key();
657 var right = iter.get_value();
659 string leftv = k[0] == '|' ? k.substring(1) : k;
660 if (Lang.isKeyword(leftv) || Lang.isBuiltin(leftv)) {
661 left = "'" + leftv + "'";
662 } else if (Regex.match_simple("[^A-Za-z_]+",leftv)) { // not plain a-z... - quoted.
663 var val = this.node.quoteString(leftv);
665 left = "'" + val.substring(1, val.length-2).replace("'", "\\'") + "'";
671 if (right.length > 0){
672 //if (this.out_props_array_plain.has_key(left)) {
673 // this.out_props_array_plain.set(left, new Gee.ArrayList<string>());
675 //this.out_props_array_plain.get(left).add(right);
677 this.els.add(left + " : [\n" + this.pad + indent_str + indent_str +
678 right + "\n" + this.pad + "]");
685 public void readListeners()
688 if (this.node.listeners.size < 1) {
691 // munge the listeners.
692 //print("ADDING listeners?");
697 var keys = new Gee.ArrayList<string>();
698 var piter = this.node.listeners.map_iterator();
699 while (piter.next() ) {
701 keys.add(piter.get_key());
703 keys.sort(( a, b) => {
704 return ((string)a).collate((string)b);
705 //if (a == b) return 0;
706 //return a < b ? -1 : 1;
709 var itms = "listeners : {\n";
711 for (var i = 0; i< keys.size; i++) {
712 var key = keys.get(i);
713 var val = this.node.listeners.get(key);
716 itms += i >0 ? ",\n" : "";
718 var str = val.strip();
719 var lines = str.split("\n");
720 if (lines.length > 0) {
721 //str = string.joinv("\n" + this.pad + " ", lines);
722 str = string.joinv("\n" + this.pad + indent_str + indent_str , lines);
725 itms += this.pad + indent_str + key.replace("|", "") + " : " + str;
726 this.out_listeners.set(key.replace("|", "") ,str);
730 itms += "\n" + this.pad + "}";
731 //print ( "ADD " + itms);
736 public void iterChildren()
740 // finally munge the children...
741 if (this.node.items.size < 1) {
744 var itms = "items : [\n";
746 for(var i = 0; i < this.node.items.size;i++) {
747 var ele = this.node.items.get(i);
748 if (ele.props.has_key("* prop")) {
755 itms += this.pad + indent_str +
756 this.mungeChild( this.pad + indent_str + indent_str , ele);
757 this.out_children.add(ele);
760 itms += "\n"+ this.pad + "]" + "\n";
764 // finally output listeners...
766 public void xIncludeToString()