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..
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()
169 var spad = this.pad.substring(0, this.pad.length-indent);
171 if (this.node.props.has_key("* xinclude")) {
172 this.addLine("Roo.apply(" + this.node.props.get("* xinclude") + "._tree(), {");
178 // output the items...
179 // work out remaining items...
180 var total_nodes = this.out_props.size +
181 this.out_props_array_plain.size +
182 (this.out_listeners.size > 0 ? 1 : 0) +
183 this.out_nodeprops.size +
184 this.out_props_array.size +
185 (this.out_children.size > 0 ? 1 : 0);
190 var niter = this.out_nodeprops.map_iterator();
192 while(niter.next()) {
194 suffix = total_nodes > 0 ? "," : "";
195 this.addMultiLine(this.pad + niter.get_key() + " : " +
196 this.mungeChildNew(this.pad + indent_str, niter.get_value()) + suffix
201 var iter = this.orderedPropKeys().list_iterator();
204 suffix = total_nodes > 0 ? "," : "";
206 var v = this.out_props.get(k);
208 this.addLine(this.pad + k + " : " + v + suffix);
211 // out_props_array_plain -- not used?
212 var paiter = this.out_props_array_plain.map_iterator();
214 while(paiter.next()) {
217 this.addLine(this.pad + paiter.get_key() + " : [");
218 var paliter = paiter.get_value().list_iterator();
219 while (paliter.next()) {
220 suffix = paliter.has_next() ? "," : "";
221 this.addMultiLine(this.pad + indent_str + paliter.get() + suffix);
224 suffix = total_nodes > 0 ? "," : "";
225 // this.mungeChild(this.pad + indent_str, niter.get_value())
226 this.addLine(this.pad + "]" + suffix);
234 var piter = this.out_props_array.map_iterator();
236 while(piter.next()) {
239 this.addLine(this.pad + piter.get_key() + " : [");
240 var pliter = piter.get_value().list_iterator();
241 while (pliter.next()) {
242 suffix = pliter.has_next() ? "," : "";
243 this.addMultiLine(this.pad + indent_str +
244 this.mungeChildNew(this.pad + indent_str + indent_str, pliter.get()) + suffix);
247 suffix = total_nodes > 0 ? "," : "";
249 this.addLine(this.pad + "]" + suffix);
253 if (this.out_listeners.size > 0 ) {
255 this.addLine(this.pad + "listeners : {");
256 iter = this.orderedListenerKeys().list_iterator();
258 var sz = this.out_listeners.size;
261 suffix = sz > 0 ? "," : "";
263 var v = this.out_listeners.get(k);
264 this.addMultiLine(this.pad + indent_str + k + " : " + v + suffix);
266 suffix = total_nodes > 0 ? "," : "";
267 this.addLine(this.pad + "}" + suffix);
271 if (this.out_children.size > 0) {
272 this.addLine(this.pad + "items : [" );
273 var cniter = this.out_children.list_iterator();
274 while (cniter.next()) {
275 suffix = cniter.has_next() ? "," : "";
276 this.addMultiLine(this.pad + indent_str +
277 this.mungeChildNew(this.pad + indent_str + indent_str, cniter.get()) + suffix
282 this.addLine(this.pad + "]");
285 if (this.node.props.has_key("* xinclude")) {
286 this.ret += spad + "})";
289 this.ret += spad + "}";
301 public void addLine(string str= "")
304 this.ret += str+ "\n";
310 public void addMultiLine(string str= "")
313 this.cur_line += str.split("\n").length;
314 //this.ret += "/*%d*/ ".printf(l) + str + "\n";
315 this.ret += str + "\n";
318 string gLibStringListJoin( string sep, Gee.ArrayList<string> ar)
321 for (var i = 0; i < ar.size; i++) {
322 ret += i>0 ? sep : "";
328 public string mungeChild(string pad , Node cnode)
330 var x = new NodeToJs(cnode, this.doubleStringProps, pad, this);
334 public string mungeChildNew(string pad , Node cnode)
336 var x = new NodeToJs(cnode, this.doubleStringProps, pad, this);
343 public void checkChildren ()
347 // look throught he chilren == looking for * prop.. -- fixme might not work..
350 if (!this.node.hasChildren()) {
355 for (var ii =0; ii< this.node.items.size; ii++) {
356 var pl = this.node.items.get(ii);
357 if (!pl.props.has_key("* prop")) {
362 //print(JSON.stringify(pl,null,4));
364 //var prop = pl['*prop'] + '';
365 //delete pl['*prop'];
366 var prop = pl.get("* prop");
367 print("got prop "+ prop + "\n");
370 if (! Regex.match_simple("\\[\\]$", prop)) {
371 // it's a standard prop..
373 // munge property..??
375 this.out_nodeprops.set(prop, pl);
377 this.els.add( prop + " : " + this.mungeChild ( this.pad + indent_str, pl));
387 var sprop = prop.replace("[]", "");
388 print("sprop is : " + sprop + "\n");
390 // it's an array type..
392 if (!this.ar_props.has_key(sprop)) {
394 this.ar_props.set(sprop, "");
395 this.out_props_array.set(sprop, new Gee.ArrayList<Node>());
397 old = this.ar_props.get(sprop);
399 var nstr = old += old.length > 0 ? ",\n" : "";
400 nstr += this.mungeChild( this.pad + indent_str + indent_str + indent_str , pl);
401 this.out_props_array.get(sprop).add( pl);
402 this.ar_props.set(sprop, nstr);
409 * Standardize this crap...
411 * standard properties (use to set)
412 * If they are long values show the dialog..
415 * bool is_xxx :: can show a pulldown.. (true/false)
417 * $ string html = string with value interpolated eg. baseURL + ".."
418 * Clutter.ActorAlign x_align (typed) -- shows pulldowns if type is ENUM?
419 * $ untypedvalue = javascript untyped value...
420 * _ string html ... = translatable..
423 * object properties (not part of the GOjbect being wrapped?
424 * # Gee.ArrayList<Xcls_fileitem> fileitems
429 * methods -- always text editor..
437 * * init -- big string?
439 * event handlers (listeners)
444 * +XXXX -- indicates it's a instance property / not glob...
445 * *XXXX -- skip writing glob property (used as classes that can be created...)
449 public void readProps()
454 if (this.node.props.has_key("$ xns")) {
455 this.out_props.set("'|xns'", "'" + this.node.props.get("$ xns") + "'" );
457 this.els.add("'|xns' : '" + this.node.props.get("$ xns") + "'");
463 func_regex = new Regex("^\\s+|\\s+$");
465 print("failed to build regex");
468 // sort the key's so they always get rendered in the same order..
470 var keys = new Gee.ArrayList<string>();
471 var piter = this.node.props.map_iterator();
472 while (piter.next() ) {
476 this.node.normalize_key(piter.get_key(), out k, out kflag, out ktype);
481 keys.sort(( a, b) => {
482 return ((string)a).collate((string)b);
483 //if (a == b) return 0;
484 //return a < b ? -1 : 1;
489 for (var i = 0; i< keys.size; i++) {
490 var key = this.node.get_key(keys.get(i));
491 print("ADD KEY %s\n", key);
496 this.node.normalize_key(key, out k, out kflag, out ktype);
499 var v = this.node.get(key);
502 //if (this.skip.contains(k) ) {
505 if ( Regex.match_simple("\\[\\]$", k)) {
506 // array .. not supported... here?
512 // skip builder stuff. prefixed with '.' .. just like unix fs..
513 if (kflag == ".") { // |. or . -- do not output..
517 // ignore '* prop'; ???
522 if (Lang.isKeyword(leftv) || Lang.isBuiltin(leftv)) {
523 left = "'" + leftv + "'";
524 } else if (Regex.match_simple("[^A-Za-z_]+",leftv)) { // not plain a-z... - quoted.
525 var val = this.node.quoteString(leftv);
527 left = "'" + val.substring(1, val.length-2).replace("'", "\\'") + "'";
533 // next.. is it a function.. or a raw string..
541 // ??? any others that are raw output..
543 // does not hapepnd with arrays..
544 if (v.length < 1) { //if (typeof(el) == 'string' && !obj[i].length) { //skip empty.
551 str = func_regex.replace(v,v.length, 0, "");
553 print("regex failed");
559 var lines = str.split("\n");
561 if (lines.length > 0) {
562 nstr = string.joinv("\n" + this.pad, lines);
563 //nstr = string.joinv("\n", lines);
565 this.out_props.set(left, nstr);
566 //print("==> " + str + "\n");
567 this.els.add(left + " : "+ nstr);
578 ktype.down() == "boolean"
580 ktype.down() == "bool"
582 ktype.down() == "number"
584 ktype.down() == "int"
585 ) { // boolean or number...?
586 this.out_props.set(left, v.down());
587 this.els.add(left + " : " + v.down() );
591 // is it a translated string?
597 //if (this.doubleStringProps.size < 1) {
598 // this.els.add(left + this.node.quoteString(v));
602 if ((this.doubleStringProps.index_of(k) > -1) ||
603 (ktype.down() == "string" && k[0] == '_')
606 // then use the translated version...
609 (v.split("\n").length > 1 ?
610 ("\n" + string.joinv(this.pad + "\n", v.split("\n")).replace("*/", "* - /") + "\n" + this.pad + "*/ ") :
611 (v.replace("*/", "* - /") + " */")
614 this.els.add(left + " : _this._strings['" +
615 GLib.Checksum.compute_for_string (ChecksumType.MD5, v) +
618 this.out_props.set(left, "_this._strings['" +
619 GLib.Checksum.compute_for_string (ChecksumType.MD5, v) +
624 // otherwise it needs to be encapsulated.. as single quotes..
626 var vv = this.node.quoteString(v);
627 // single quote.. v.substring(1, v.length-1).replace("'", "\\'") + "'";
628 this.els.add(left + " : " + "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
629 this.out_props.set(left, "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
636 public void readArrayProps()
639 // this is not needed in the new version
640 // as array props are handled more directly..
642 // handle the childitems that are arrays.. eg. button[] = { }...
644 // note this does not handle a mix of nodes and properties with the same
648 var iter = this.ar_props.map_iterator();
649 while (iter.next()) {
650 var k = iter.get_key();
651 var right = iter.get_value();
653 string leftv = k[0] == '|' ? k.substring(1) : k;
654 if (Lang.isKeyword(leftv) || Lang.isBuiltin(leftv)) {
655 left = "'" + leftv + "'";
656 } else if (Regex.match_simple("[^A-Za-z_]+",leftv)) { // not plain a-z... - quoted.
657 var val = this.node.quoteString(leftv);
659 left = "'" + val.substring(1, val.length-2).replace("'", "\\'") + "'";
665 if (right.length > 0){
666 //if (this.out_props_array_plain.has_key(left)) {
667 // this.out_props_array_plain.set(left, new Gee.ArrayList<string>());
669 //this.out_props_array_plain.get(left).add(right);
671 this.els.add(left + " : [\n" + this.pad + indent_str + indent_str +
672 right + "\n" + this.pad + "]");
679 public void readListeners()
682 if (this.node.listeners.size < 1) {
685 // munge the listeners.
686 //print("ADDING listeners?");
691 var keys = new Gee.ArrayList<string>();
692 var piter = this.node.listeners.map_iterator();
693 while (piter.next() ) {
695 keys.add(piter.get_key());
697 keys.sort(( a, b) => {
698 return ((string)a).collate((string)b);
699 //if (a == b) return 0;
700 //return a < b ? -1 : 1;
703 var itms = "listeners : {\n";
705 for (var i = 0; i< keys.size; i++) {
706 var key = keys.get(i);
707 var val = this.node.listeners.get(key);
710 itms += i >0 ? ",\n" : "";
712 var str = val.strip();
713 var lines = str.split("\n");
714 if (lines.length > 0) {
715 //str = string.joinv("\n" + this.pad + " ", lines);
716 str = string.joinv("\n" + this.pad + indent_str + indent_str , lines);
719 itms += this.pad + indent_str + key.replace("|", "") + " : " + str;
720 this.out_listeners.set(key.replace("|", "") ,str);
724 itms += "\n" + this.pad + "}";
725 //print ( "ADD " + itms);
730 public void iterChildren()
734 // finally munge the children...
735 if (this.node.items.size < 1) {
738 var itms = "items : [\n";
740 for(var i = 0; i < this.node.items.size;i++) {
741 var ele = this.node.items.get(i);
742 if (ele.props.has_key("* prop")) {
749 itms += this.pad + indent_str +
750 this.mungeChild( this.pad + indent_str + indent_str , ele);
751 this.out_children.add(ele);
754 itms += "\n"+ this.pad + "]" + "\n";
758 // finally output listeners...
760 public void xIncludeToString()