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) {
100 var spad = this.pad.substring(0, this.pad.length-indent);
103 var str_props = gLibStringListJoin(",\n" + this.pad , this.els) ;
104 //print ("STR PROPS: " + str_props);
108 if (!this.node.props.has_key("* xinclude")) {
110 this.pad + str_props +
116 return "Roo.apply(" + this.node.props.get("* xinclude") + "._tree(), "+
118 this.pad + str_props +
124 This currently works by creating a key/value array of this.els, which is just an array of properties..
125 this is so that join() works...
129 b) output plan properties.
130 c) output listeners..
132 g) output prop_arrays..
140 public Gee.ArrayList<string> orderedPropKeys() {
142 var ret = new Gee.ArrayList<string> ();
143 var niter = this.out_props.map_iterator();
144 while(niter.next()) {
145 ret.add(niter.get_key());
148 ret.sort(( a, b) => {
149 return ((string)a).collate((string)b);
150 //if (a == b) return 0;
151 //return a < b ? -1 : 1;
155 public Gee.ArrayList<string> orderedListenerKeys() {
157 var ret = new Gee.ArrayList<string> ();
158 var niter = this.out_listeners.map_iterator();
159 while(niter.next()) {
160 ret.add(niter.get_key());
163 ret.sort(( a, b) => {
164 return ((string)a).collate((string)b);
165 //if (a == b) return 0;
166 //return a < b ? -1 : 1;
172 public string mungeOut()
174 this.node.line_start = this.cur_line;
175 this.top.node.setNodeLine(this.cur_line, this.node);
176 var spad = this.pad.substring(0, this.pad.length-indent);
178 if (this.node.props.has_key("* xinclude")) {
179 this.addLine("Roo.apply(" + this.node.props.get("* xinclude") + "._tree(), {");
185 // output the items...
186 // work out remaining items...
187 var total_nodes = this.out_props.size +
188 this.out_props_array_plain.size +
189 (this.out_listeners.size > 0 ? 1 : 0) +
190 this.out_nodeprops.size +
191 this.out_props_array.size +
192 (this.out_children.size > 0 ? 1 : 0);
197 var niter = this.out_nodeprops.map_iterator();
199 while(niter.next()) {
201 suffix = total_nodes > 0 ? "," : "";
202 var l = this.pad + niter.get_key() + " : " +
203 this.mungeChildNew(this.pad + indent_str, niter.get_value()) + suffix;
204 this.addMultiLine(l);
208 var iter = this.orderedPropKeys().list_iterator();
211 suffix = total_nodes > 0 ? "," : "";
213 var v = this.out_props.get(k);
215 this.addMultiLine(this.pad + k + " : " + v + suffix);
218 // out_props_array_plain -- not used?
219 var paiter = this.out_props_array_plain.map_iterator();
221 while(paiter.next()) {
224 this.addLine(this.pad + paiter.get_key() + " : [");
225 var paliter = paiter.get_value().list_iterator();
226 while (paliter.next()) {
227 suffix = paliter.has_next() ? "," : "";
228 this.addMultiLine(this.pad + indent_str + paliter.get() + suffix);
231 suffix = total_nodes > 0 ? "," : "";
232 // this.mungeChild(this.pad + indent_str, niter.get_value())
233 this.addLine(this.pad + "]" + suffix);
241 var piter = this.out_props_array.map_iterator();
243 while(piter.next()) {
246 this.addLine(this.pad + piter.get_key() + " : [");
247 var pliter = piter.get_value().list_iterator();
248 while (pliter.next()) {
249 suffix = pliter.has_next() ? "," : "";
250 this.addMultiLine(this.pad + indent_str +
251 this.mungeChildNew(this.pad + indent_str + indent_str, pliter.get()) + suffix);
254 suffix = total_nodes > 0 ? "," : "";
256 this.addLine(this.pad + "]" + suffix);
260 if (this.out_listeners.size > 0 ) {
262 this.addLine(this.pad + "listeners : {");
263 iter = this.orderedListenerKeys().list_iterator();
265 var sz = this.out_listeners.size;
268 suffix = sz > 0 ? "," : "";
270 var v = this.out_listeners.get(k);
271 this.addMultiLine(this.pad + indent_str + k + " : " + v + suffix);
273 suffix = total_nodes > 0 ? "," : "";
274 this.addLine(this.pad + "}" + suffix);
278 if (this.out_children.size > 0) {
279 this.addLine(this.pad + "items : [" );
280 var cniter = this.out_children.list_iterator();
281 while (cniter.next()) {
282 suffix = cniter.has_next() ? "," : "";
283 this.addMultiLine(this.pad + indent_str +
284 this.mungeChildNew(this.pad + indent_str + indent_str, cniter.get()) + suffix
289 this.addLine(this.pad + "]");
292 if (this.node.props.has_key("* xinclude")) {
293 this.ret += spad + "})";
296 this.ret += spad + "}";
298 this.node.line_end = this.cur_line;
299 this.node.sortLines();
308 public void addLine(string str= "")
311 this.ret += str+ "\n";
312 //this.ret += "/*%d*/ ".printf(this.cur_line -1) + str + "\n";
317 public void addMultiLine(string str= "")
320 this.cur_line += str.split("\n").length;
321 //this.ret += "/*%d*/ ".printf(l) + str + "\n";
322 this.ret += str + "\n";
325 string gLibStringListJoin( string sep, Gee.ArrayList<string> ar)
328 for (var i = 0; i < ar.size; i++) {
329 ret += i>0 ? sep : "";
335 public string mungeChild(string pad , Node cnode)
337 var x = new NodeToJs(cnode, this.doubleStringProps, pad, this);
341 public string mungeChildNew(string pad , Node cnode )
343 var x = new NodeToJs(cnode, this.doubleStringProps, pad, this);
351 public void checkChildren ()
355 // look throught he chilren == looking for * prop.. -- fixme might not work..
358 if (!this.node.hasChildren()) {
363 for (var ii =0; ii< this.node.items.size; ii++) {
364 var pl = this.node.items.get(ii);
365 if (!pl.props.has_key("* prop")) {
370 //print(JSON.stringify(pl,null,4));
372 //var prop = pl['*prop'] + '';
373 //delete pl['*prop'];
374 var prop = pl.get("* prop");
375 print("got prop "+ prop + "\n");
378 if (! Regex.match_simple("\\[\\]$", prop)) {
379 // it's a standard prop..
381 // munge property..??
383 this.out_nodeprops.set(prop, pl);
385 //this.els.add( prop + " : " + this.mungeChild ( this.pad + indent_str, pl));
395 var sprop = prop.replace("[]", "");
396 print("sprop is : " + sprop + "\n");
398 // it's an array type..
400 if (!this.ar_props.has_key(sprop)) {
402 this.ar_props.set(sprop, "");
403 this.out_props_array.set(sprop, new Gee.ArrayList<Node>());
405 old = this.ar_props.get(sprop);
407 var nstr = old += old.length > 0 ? ",\n" : "";
408 nstr += this.mungeChild( this.pad + indent_str + indent_str + indent_str , pl);
409 this.out_props_array.get(sprop).add( pl);
410 this.ar_props.set(sprop, nstr);
417 * Standardize this crap...
419 * standard properties (use to set)
420 * If they are long values show the dialog..
423 * bool is_xxx :: can show a pulldown.. (true/false)
425 * $ string html = string with value interpolated eg. baseURL + ".."
426 * Clutter.ActorAlign x_align (typed) -- shows pulldowns if type is ENUM?
427 * $ untypedvalue = javascript untyped value...
428 * _ string html ... = translatable..
431 * object properties (not part of the GOjbect being wrapped?
432 * # Gee.ArrayList<Xcls_fileitem> fileitems
437 * methods -- always text editor..
445 * * init -- big string?
447 * event handlers (listeners)
452 * +XXXX -- indicates it's a instance property / not glob...
453 * *XXXX -- skip writing glob property (used as classes that can be created...)
457 public void readProps()
462 if (this.node.props.has_key("$ xns")) {
463 this.out_props.set("'|xns'", "'" + this.node.props.get("$ xns") + "'" );
465 //this.els.add("'|xns' : '" + this.node.props.get("$ xns") + "'");
471 func_regex = new Regex("^\\s+|\\s+$");
473 print("failed to build regex");
476 // sort the key's so they always get rendered in the same order..
478 var keys = new Gee.ArrayList<string>();
479 var piter = this.node.props.map_iterator();
480 while (piter.next() ) {
484 this.node.normalize_key(piter.get_key(), out k, out kflag, out ktype);
489 keys.sort(( a, b) => {
490 return ((string)a).collate((string)b);
491 //if (a == b) return 0;
492 //return a < b ? -1 : 1;
497 for (var i = 0; i< keys.size; i++) {
498 var key = this.node.get_key(keys.get(i));
499 print("ADD KEY %s\n", key);
504 this.node.normalize_key(key, out k, out kflag, out ktype);
507 var v = this.node.get(key);
510 //if (this.skip.contains(k) ) {
513 if ( Regex.match_simple("\\[\\]$", k)) {
514 // array .. not supported... here?
520 // skip builder stuff. prefixed with '.' .. just like unix fs..
521 if (kflag == ".") { // |. or . -- do not output..
525 // ignore '* prop'; ???
530 if (Lang.isKeyword(leftv) || Lang.isBuiltin(leftv)) {
531 left = "'" + leftv + "'";
532 } else if (Regex.match_simple("[^A-Za-z_]+",leftv)) { // not plain a-z... - quoted.
533 var val = this.node.quoteString(leftv);
535 left = "'" + val.substring(1, val.length-2).replace("'", "\\'") + "'";
541 // next.. is it a function.. or a raw string..
549 // ??? any others that are raw output..
551 // does not hapepnd with arrays..
552 if (v.length < 1) { //if (typeof(el) == 'string' && !obj[i].length) { //skip empty.
559 str = func_regex.replace(v,v.length, 0, "");
561 print("regex failed");
567 var lines = str.split("\n");
569 if (lines.length > 0) {
570 nstr = string.joinv("\n" + this.pad, lines);
571 //nstr = string.joinv("\n", lines);
573 this.out_props.set(left, nstr);
574 //print("==> " + str + "\n");
575 //this.els.add(left + " : "+ nstr);
586 ktype.down() == "boolean"
588 ktype.down() == "bool"
590 ktype.down() == "number"
592 ktype.down() == "int"
593 ) { // boolean or number...?
594 this.out_props.set(left, v.down());
595 //this.els.add(left + " : " + v.down() );
599 // is it a translated string?
605 //if (this.doubleStringProps.size < 1) {
606 // this.els.add(left + this.node.quoteString(v));
610 if ((this.doubleStringProps.index_of(k) > -1) ||
611 (ktype.down() == "string" && k[0] == '_')
614 // then use the translated version...
617 (v.split("\n").length > 1 ?
618 ("\n" + string.joinv(this.pad + "\n", v.split("\n")).replace("*/", "* - /") + "\n" + this.pad + "*/ ") :
619 (v.replace("*/", "* - /") + " */")
622 //this.els.add(left + " : _this._strings['" +
623 // GLib.Checksum.compute_for_string (ChecksumType.MD5, v) +
626 this.out_props.set(left, "_this._strings['" +
627 GLib.Checksum.compute_for_string (ChecksumType.MD5, v) +
632 // otherwise it needs to be encapsulated.. as single quotes..
634 var vv = this.node.quoteString(v);
635 // single quote.. v.substring(1, v.length-1).replace("'", "\\'") + "'";
636 //this.els.add(left + " : " + "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
637 this.out_props.set(left, "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
645 public void readArrayProps()
648 // this is not needed in the new version
649 // as array props are handled more directly..
651 // handle the childitems that are arrays.. eg. button[] = { }...
653 // note this does not handle a mix of nodes and properties with the same
657 var iter = this.ar_props.map_iterator();
658 while (iter.next()) {
659 var k = iter.get_key();
660 var right = iter.get_value();
662 string leftv = k[0] == '|' ? k.substring(1) : k;
663 if (Lang.isKeyword(leftv) || Lang.isBuiltin(leftv)) {
664 left = "'" + leftv + "'";
665 } else if (Regex.match_simple("[^A-Za-z_]+",leftv)) { // not plain a-z... - quoted.
666 var val = this.node.quoteString(leftv);
668 left = "'" + val.substring(1, val.length-2).replace("'", "\\'") + "'";
674 if (right.length > 0){
675 //if (this.out_props_array_plain.has_key(left)) {
676 // this.out_props_array_plain.set(left, new Gee.ArrayList<string>());
678 //this.out_props_array_plain.get(left).add(right);
680 //this.els.add(left + " : [\n" + this.pad + indent_str + indent_str +
681 // right + "\n" + this.pad + "]");
689 public void readListeners()
692 if (this.node.listeners.size < 1) {
695 // munge the listeners.
696 //print("ADDING listeners?");
701 var keys = new Gee.ArrayList<string>();
702 var piter = this.node.listeners.map_iterator();
703 while (piter.next() ) {
705 keys.add(piter.get_key());
707 keys.sort(( a, b) => {
708 return ((string)a).collate((string)b);
709 //if (a == b) return 0;
710 //return a < b ? -1 : 1;
713 var itms = "listeners : {\n";
715 for (var i = 0; i< keys.size; i++) {
716 var key = keys.get(i);
717 var val = this.node.listeners.get(key);
720 itms += i >0 ? ",\n" : "";
722 var str = val.strip();
723 var lines = str.split("\n");
724 if (lines.length > 0) {
725 //str = string.joinv("\n" + this.pad + " ", lines);
726 str = string.joinv("\n" + this.pad + indent_str + indent_str , lines);
729 itms += this.pad + indent_str + key.replace("|", "") + " : " + str;
730 this.out_listeners.set(key.replace("|", "") ,str);
734 itms += "\n" + this.pad + "}";
735 //print ( "ADD " + itms);
736 //this.els.add(itms);
740 public void iterChildren()
744 // finally munge the children...
745 if (this.node.items.size < 1) {
748 var itms = "items : [\n";
750 for(var i = 0; i < this.node.items.size;i++) {
751 var ele = this.node.items.get(i);
752 if (ele.props.has_key("* prop")) {
759 itms += this.pad + indent_str +
760 this.mungeChild( this.pad + indent_str + indent_str , ele);
761 this.out_children.add(ele);
764 itms += "\n"+ this.pad + "]" + "\n";
765 //this.els.add(itms);
768 // finally output listeners...
770 public void xIncludeToString()