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.out_props_array.has_key(sprop)) {
401 this.out_props_array.set(sprop, new Gee.ArrayList<Node>());
405 if (!this.ar_props.has_key(sprop)) {
407 this.ar_props.set(sprop, "");
408 this.out_props_array.set(sprop, new Gee.ArrayList<Node>());
410 old = this.ar_props.get(sprop);
412 var nstr = old += old.length > 0 ? ",\n" : "";
413 nstr += this.mungeChild( this.pad + indent_str + indent_str + indent_str , pl);
415 this.out_props_array.get(sprop).add( pl);
416 //this.ar_props.set(sprop, nstr);
423 * Standardize this crap...
425 * standard properties (use to set)
426 * If they are long values show the dialog..
429 * bool is_xxx :: can show a pulldown.. (true/false)
431 * $ string html = string with value interpolated eg. baseURL + ".."
432 * Clutter.ActorAlign x_align (typed) -- shows pulldowns if type is ENUM?
433 * $ untypedvalue = javascript untyped value...
434 * _ string html ... = translatable..
437 * object properties (not part of the GOjbect being wrapped?
438 * # Gee.ArrayList<Xcls_fileitem> fileitems
443 * methods -- always text editor..
451 * * init -- big string?
453 * event handlers (listeners)
458 * +XXXX -- indicates it's a instance property / not glob...
459 * *XXXX -- skip writing glob property (used as classes that can be created...)
463 public void readProps()
468 if (this.node.props.has_key("$ xns")) {
469 this.out_props.set("'|xns'", "'" + this.node.props.get("$ xns") + "'" );
471 //this.els.add("'|xns' : '" + this.node.props.get("$ xns") + "'");
477 func_regex = new Regex("^\\s+|\\s+$");
479 print("failed to build regex");
482 // sort the key's so they always get rendered in the same order..
484 var keys = new Gee.ArrayList<string>();
485 var piter = this.node.props.map_iterator();
486 while (piter.next() ) {
490 this.node.normalize_key(piter.get_key(), out k, out kflag, out ktype);
495 keys.sort(( a, b) => {
496 return ((string)a).collate((string)b);
497 //if (a == b) return 0;
498 //return a < b ? -1 : 1;
503 for (var i = 0; i< keys.size; i++) {
504 var key = this.node.get_key(keys.get(i));
505 print("ADD KEY %s\n", key);
510 this.node.normalize_key(key, out k, out kflag, out ktype);
513 var v = this.node.get(key);
516 //if (this.skip.contains(k) ) {
519 if ( Regex.match_simple("\\[\\]$", k)) {
520 // array .. not supported... here?
526 // skip builder stuff. prefixed with '.' .. just like unix fs..
527 if (kflag == ".") { // |. or . -- do not output..
531 // ignore '* prop'; ???
536 if (Lang.isKeyword(leftv) || Lang.isBuiltin(leftv)) {
537 left = "'" + leftv + "'";
538 } else if (Regex.match_simple("[^A-Za-z_]+",leftv)) { // not plain a-z... - quoted.
539 var val = this.node.quoteString(leftv);
541 left = "'" + val.substring(1, val.length-2).replace("'", "\\'") + "'";
547 // next.. is it a function.. or a raw string..
555 // ??? any others that are raw output..
557 // does not hapepnd with arrays..
558 if (v.length < 1) { //if (typeof(el) == 'string' && !obj[i].length) { //skip empty.
565 str = func_regex.replace(v,v.length, 0, "");
567 print("regex failed");
573 var lines = str.split("\n");
575 if (lines.length > 0) {
576 nstr = string.joinv("\n" + this.pad, lines);
577 //nstr = string.joinv("\n", lines);
579 this.out_props.set(left, nstr);
580 //print("==> " + str + "\n");
581 //this.els.add(left + " : "+ nstr);
592 ktype.down() == "boolean"
594 ktype.down() == "bool"
596 ktype.down() == "number"
598 ktype.down() == "int"
599 ) { // boolean or number...?
600 this.out_props.set(left, v.down());
601 //this.els.add(left + " : " + v.down() );
605 // is it a translated string?
611 //if (this.doubleStringProps.size < 1) {
612 // this.els.add(left + this.node.quoteString(v));
616 if ((this.doubleStringProps.index_of(k) > -1) ||
617 (ktype.down() == "string" && k[0] == '_')
620 // then use the translated version...
623 (v.split("\n").length > 1 ?
624 ("\n" + string.joinv(this.pad + "\n", v.split("\n")).replace("*/", "* - /") + "\n" + this.pad + "*/ ") :
625 (v.replace("*/", "* - /") + " */")
628 //this.els.add(left + " : _this._strings['" +
629 // GLib.Checksum.compute_for_string (ChecksumType.MD5, v) +
632 this.out_props.set(left, "_this._strings['" +
633 GLib.Checksum.compute_for_string (ChecksumType.MD5, v) +
638 // otherwise it needs to be encapsulated.. as single quotes..
640 var vv = this.node.quoteString(v);
641 // single quote.. v.substring(1, v.length-1).replace("'", "\\'") + "'";
642 //this.els.add(left + " : " + "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
643 this.out_props.set(left, "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
651 public void readArrayProps()
654 // this is not needed in the new version
655 // as array props are handled more directly..
657 // handle the childitems that are arrays.. eg. button[] = { }...
659 // note this does not handle a mix of nodes and properties with the same
663 var iter = this.ar_props.map_iterator();
664 while (iter.next()) {
665 var k = iter.get_key();
666 var right = iter.get_value();
668 string leftv = k[0] == '|' ? k.substring(1) : k;
669 if (Lang.isKeyword(leftv) || Lang.isBuiltin(leftv)) {
670 left = "'" + leftv + "'";
671 } else if (Regex.match_simple("[^A-Za-z_]+",leftv)) { // not plain a-z... - quoted.
672 var val = this.node.quoteString(leftv);
674 left = "'" + val.substring(1, val.length-2).replace("'", "\\'") + "'";
680 if (right.length > 0){
681 //if (this.out_props_array_plain.has_key(left)) {
682 // this.out_props_array_plain.set(left, new Gee.ArrayList<string>());
684 //this.out_props_array_plain.get(left).add(right);
686 //this.els.add(left + " : [\n" + this.pad + indent_str + indent_str +
687 // right + "\n" + this.pad + "]");
695 public void readListeners()
698 if (this.node.listeners.size < 1) {
701 // munge the listeners.
702 //print("ADDING listeners?");
707 var keys = new Gee.ArrayList<string>();
708 var piter = this.node.listeners.map_iterator();
709 while (piter.next() ) {
711 keys.add(piter.get_key());
713 keys.sort(( a, b) => {
714 return ((string)a).collate((string)b);
715 //if (a == b) return 0;
716 //return a < b ? -1 : 1;
719 var itms = "listeners : {\n";
721 for (var i = 0; i< keys.size; i++) {
722 var key = keys.get(i);
723 var val = this.node.listeners.get(key);
726 itms += i >0 ? ",\n" : "";
728 var str = val.strip();
729 var lines = str.split("\n");
730 if (lines.length > 0) {
731 //str = string.joinv("\n" + this.pad + " ", lines);
732 str = string.joinv("\n" + this.pad + indent_str + indent_str , lines);
735 itms += this.pad + indent_str + key.replace("|", "") + " : " + str;
736 this.out_listeners.set(key.replace("|", "") ,str);
740 itms += "\n" + this.pad + "}";
741 //print ( "ADD " + itms);
742 //this.els.add(itms);
746 public void iterChildren()
750 // finally munge the children...
751 if (this.node.items.size < 1) {
754 var itms = "items : [\n";
756 for(var i = 0; i < this.node.items.size;i++) {
757 var ele = this.node.items.get(i);
758 if (ele.props.has_key("* prop")) {
765 itms += this.pad + indent_str +
766 this.mungeChild( this.pad + indent_str + indent_str , ele);
767 this.out_children.add(ele);
770 itms += "\n"+ this.pad + "]" + "\n";
771 //this.els.add(itms);
774 // finally output listeners...
776 public void xIncludeToString()