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
22 Gee.HashMap<string,string> out_props;
23 Gee.HashMap<string,string> out_listeners;
24 Gee.HashMap<string,Node> out_nodeprops;
25 Gee.ArrayList<Node> out_children;
26 Gee.HashMap<string,Gee.ArrayList<Node>> out_props_array;
27 Gee.HashMap<string,Gee.ArrayList<string>> out_props_array_plain;
35 public NodeToJs( Node node, Gee.ArrayList<string> doubleStringProps, string pad, NodeToJs? parent)
38 this.doubleStringProps = doubleStringProps;
41 //this.els = new Gee.ArrayList<string>();
42 //this.ar_props = new Gee.HashMap<string,string>();
45 this.out_props = new Gee.HashMap<string,string>();
46 this.out_listeners = new Gee.HashMap<string,string>();
47 this.out_nodeprops = new Gee.HashMap<string,Node>() ;
48 this.out_children = new Gee.ArrayList<Node> ();
49 this.out_props_array = new Gee.HashMap<string,Gee.ArrayList<Node>>() ;
50 this.out_props_array_plain = new Gee.HashMap<string,Gee.ArrayList<string>>() ;
54 this.cur_line = parent == null ? 0 : parent.cur_line ; //-1 as we usuall concat onto the existin gline?
56 this.top = parent == null ? this : parent.top;
59 node.node_lines = new Gee.ArrayList<int>();
60 node.node_lines_map = new Gee.HashMap<int,Node>();
69 public string munge ( )
71 //return this.mungeToString(this.node);
73 this.node.line_start = this.cur_line;
77 //this.readArrayProps();
80 if (!this.node.props.has_key("* xinclude")) {
86 // no properties to output...
87 //if (this.els.size < 1) {
97 This currently works by creating a key/value array of this.els, which is just an array of properties..
98 this is so that join() works...
102 b) output plan properties.
103 c) output listeners..
105 g) output prop_arrays..
113 public Gee.ArrayList<string> orderedPropKeys() {
115 var ret = new Gee.ArrayList<string> ();
116 var niter = this.out_props.map_iterator();
117 while(niter.next()) {
118 ret.add(niter.get_key());
121 ret.sort(( a, b) => {
122 return ((string)a).collate((string)b);
123 //if (a == b) return 0;
124 //return a < b ? -1 : 1;
128 public Gee.ArrayList<string> orderedListenerKeys() {
130 var ret = new Gee.ArrayList<string> ();
131 var niter = this.out_listeners.map_iterator();
132 while(niter.next()) {
133 ret.add(niter.get_key());
136 ret.sort(( a, b) => {
137 return ((string)a).collate((string)b);
138 //if (a == b) return 0;
139 //return a < b ? -1 : 1;
145 public string mungeOut()
147 this.node.line_start = this.cur_line;
148 this.top.node.setNodeLine(this.cur_line, this.node);
149 var spad = this.pad.substring(0, this.pad.length-indent);
151 if (this.node.props.has_key("* xinclude")) {
152 this.addLine("Roo.apply(" + this.node.props.get("* xinclude") + "._tree(), {");
158 // output the items...
159 // work out remaining items...
160 var total_nodes = this.out_props.size +
161 this.out_props_array_plain.size +
162 (this.out_listeners.size > 0 ? 1 : 0) +
163 this.out_nodeprops.size +
164 this.out_props_array.size +
165 (this.out_children.size > 0 ? 1 : 0);
170 var iter = this.orderedPropKeys().list_iterator();
173 suffix = total_nodes > 0 ? "," : "";
175 var v = this.out_props.get(k);
177 this.addMultiLine(this.pad + k + " : " + v + suffix);
182 if (this.out_listeners.size > 0 ) {
184 this.addLine(this.pad + "listeners : {");
185 iter = this.orderedListenerKeys().list_iterator();
187 var sz = this.out_listeners.size;
190 suffix = sz > 0 ? "," : "";
192 var v = this.out_listeners.get(k);
193 this.addMultiLine(this.pad + indent_str + k + " : " + v + suffix);
195 suffix = total_nodes > 0 ? "," : "";
196 this.addLine(this.pad + "}" + suffix);
200 //------- at this point it is the end of the code relating directly to the object..
202 this.node.line_end = this.cur_line;
205 var niter = this.out_nodeprops.map_iterator();
207 while(niter.next()) {
209 suffix = total_nodes > 0 ? "," : "";
210 var l = this.pad + niter.get_key() + " : " +
211 this.mungeChildNew(this.pad + indent_str, niter.get_value()) + suffix;
212 this.addMultiLine(l);
216 var piter = this.out_props_array.map_iterator();
218 while(piter.next()) {
221 this.addLine(this.pad + piter.get_key() + " : [");
222 var pliter = piter.get_value().list_iterator();
223 while (pliter.next()) {
224 suffix = pliter.has_next() ? "," : "";
225 this.addMultiLine(this.pad + indent_str +
226 this.mungeChildNew(this.pad + indent_str + indent_str, pliter.get()) + suffix);
229 suffix = total_nodes > 0 ? "," : "";
231 this.addLine(this.pad + "]" + suffix);
235 if (this.out_children.size > 0) {
236 this.addLine(this.pad + "items : [" );
237 var cniter = this.out_children.list_iterator();
238 while (cniter.next()) {
239 suffix = cniter.has_next() ? "," : "";
240 this.addMultiLine(this.pad + indent_str +
241 this.mungeChildNew(this.pad + indent_str + indent_str, cniter.get()) + suffix
246 this.addLine(this.pad + "]");
249 if (this.node.props.has_key("* xinclude")) {
250 this.ret += spad + "})";
253 this.ret += spad + "}";
256 this.node.sortLines();
265 public void addLine(string str= "")
268 this.ret += str+ "\n";
269 //this.ret += "/*%d*/ ".printf(this.cur_line -1) + str + "\n";
274 public void addMultiLine(string str= "")
277 this.cur_line += str.split("\n").length;
278 //this.ret += "/*%d*/ ".printf(l) + str + "\n";
279 this.ret += str + "\n";
282 string gLibStringListJoin( string sep, Gee.ArrayList<string> ar)
285 for (var i = 0; i < ar.size; i++) {
286 ret += i>0 ? sep : "";
292 public string mungeChild(string pad , Node cnode)
294 var x = new NodeToJs(cnode, this.doubleStringProps, pad, this);
298 public string mungeChildNew(string pad , Node cnode )
300 var x = new NodeToJs(cnode, this.doubleStringProps, pad, this);
308 public void checkChildren ()
312 // look throught he chilren == looking for * prop.. -- fixme might not work..
315 if (!this.node.hasChildren()) {
320 for (var ii =0; ii< this.node.items.size; ii++) {
321 var pl = this.node.items.get(ii);
322 if (!pl.props.has_key("* prop")) {
327 //print(JSON.stringify(pl,null,4));
329 //var prop = pl['*prop'] + '';
330 //delete pl['*prop'];
331 var prop = pl.get("* prop");
332 print("got prop "+ prop + "\n");
335 if (! Regex.match_simple("\\[\\]$", prop)) {
336 // it's a standard prop..
338 // munge property..??
340 this.out_nodeprops.set(prop, pl);
342 //this.els.add( prop + " : " + this.mungeChild ( this.pad + indent_str, pl));
352 var sprop = prop.replace("[]", "");
353 print("sprop is : " + sprop + "\n");
355 // it's an array type..
357 if (!this.out_props_array.has_key(sprop)) {
358 this.out_props_array.set(sprop, new Gee.ArrayList<Node>());
362 if (!this.ar_props.has_key(sprop)) {
364 this.ar_props.set(sprop, "");
365 this.out_props_array.set(sprop, new Gee.ArrayList<Node>());
367 old = this.ar_props.get(sprop);
369 var nstr = old += old.length > 0 ? ",\n" : "";
370 nstr += this.mungeChild( this.pad + indent_str + indent_str + indent_str , pl);
372 this.out_props_array.get(sprop).add( pl);
373 //this.ar_props.set(sprop, nstr);
380 * Standardize this crap...
382 * standard properties (use to set)
383 * If they are long values show the dialog..
386 * bool is_xxx :: can show a pulldown.. (true/false)
388 * $ string html = string with value interpolated eg. baseURL + ".."
389 * Clutter.ActorAlign x_align (typed) -- shows pulldowns if type is ENUM?
390 * $ untypedvalue = javascript untyped value...
391 * _ string html ... = translatable..
394 * object properties (not part of the GOjbect being wrapped?
395 * # Gee.ArrayList<Xcls_fileitem> fileitems
400 * methods -- always text editor..
408 * * init -- big string?
410 * event handlers (listeners)
415 * +XXXX -- indicates it's a instance property / not glob...
416 * *XXXX -- skip writing glob property (used as classes that can be created...)
420 public void readProps()
425 if (this.node.props.has_key("$ xns")) {
426 this.out_props.set("'|xns'", "'" + this.node.props.get("$ xns") + "'" );
428 //this.els.add("'|xns' : '" + this.node.props.get("$ xns") + "'");
434 func_regex = new Regex("^\\s+|\\s+$");
436 print("failed to build regex");
439 // sort the key's so they always get rendered in the same order..
441 var keys = new Gee.ArrayList<string>();
442 var piter = this.node.props.map_iterator();
443 while (piter.next() ) {
447 this.node.normalize_key(piter.get_key(), out k, out kflag, out ktype);
452 keys.sort(( a, b) => {
453 return ((string)a).collate((string)b);
454 //if (a == b) return 0;
455 //return a < b ? -1 : 1;
460 for (var i = 0; i< keys.size; i++) {
461 var key = this.node.get_key(keys.get(i));
462 print("ADD KEY %s\n", key);
467 this.node.normalize_key(key, out k, out kflag, out ktype);
470 var v = this.node.get(key);
473 //if (this.skip.contains(k) ) {
476 if ( Regex.match_simple("\\[\\]$", k)) {
477 // array .. not supported... here?
483 // skip builder stuff. prefixed with '.' .. just like unix fs..
484 if (kflag == ".") { // |. or . -- do not output..
488 // ignore '* prop'; ???
493 if (Lang.isKeyword(leftv) || Lang.isBuiltin(leftv)) {
494 left = "'" + leftv + "'";
495 } else if (Regex.match_simple("[^A-Za-z_]+",leftv)) { // not plain a-z... - quoted.
496 var val = this.node.quoteString(leftv);
498 left = "'" + val.substring(1, val.length-2).replace("'", "\\'") + "'";
504 // next.. is it a function.. or a raw string..
512 // ??? any others that are raw output..
514 // does not hapepnd with arrays..
515 if (v.length < 1) { //if (typeof(el) == 'string' && !obj[i].length) { //skip empty.
522 str = func_regex.replace(v,v.length, 0, "");
524 print("regex failed");
530 var lines = str.split("\n");
532 if (lines.length > 0) {
533 nstr = string.joinv("\n" + this.pad, lines);
534 //nstr = string.joinv("\n", lines);
536 this.out_props.set(left, nstr);
537 //print("==> " + str + "\n");
538 //this.els.add(left + " : "+ nstr);
549 ktype.down() == "boolean"
551 ktype.down() == "bool"
553 ktype.down() == "number"
555 ktype.down() == "int"
556 ) { // boolean or number...?
557 this.out_props.set(left, v.down());
558 //this.els.add(left + " : " + v.down() );
562 // is it a translated string?
568 //if (this.doubleStringProps.size < 1) {
569 // this.els.add(left + this.node.quoteString(v));
573 if ((this.doubleStringProps.index_of(k) > -1) ||
574 (ktype.down() == "string" && k[0] == '_')
577 // then use the translated version...
580 (v.split("\n").length > 1 ?
581 ("\n" + this.pad + string.joinv(this.pad + "\n", v.split("\n")).replace("*/", "* - /") + "\n" + this.pad + "*/ ") :
582 (v.replace("*/", "* - /") + " */")
585 //this.els.add(left + " : _this._strings['" +
586 // GLib.Checksum.compute_for_string (ChecksumType.MD5, v) +
589 this.out_props.set(left, "_this._strings['" +
590 GLib.Checksum.compute_for_string (ChecksumType.MD5, v) +
595 // otherwise it needs to be encapsulated.. as single quotes..
597 var vv = this.node.quoteString(v);
598 // single quote.. v.substring(1, v.length-1).replace("'", "\\'") + "'";
599 //this.els.add(left + " : " + "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
600 this.out_props.set(left, "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
608 public void readArrayProps()
611 // this is not needed in the new version
612 // as array props are handled more directly..
614 // handle the childitems that are arrays.. eg. button[] = { }...
616 // note this does not handle a mix of nodes and properties with the same
620 var iter = this.ar_props.map_iterator();
621 while (iter.next()) {
622 var k = iter.get_key();
623 var right = iter.get_value();
625 string leftv = k[0] == '|' ? k.substring(1) : k;
626 if (Lang.isKeyword(leftv) || Lang.isBuiltin(leftv)) {
627 left = "'" + leftv + "'";
628 } else if (Regex.match_simple("[^A-Za-z_]+",leftv)) { // not plain a-z... - quoted.
629 var val = this.node.quoteString(leftv);
631 left = "'" + val.substring(1, val.length-2).replace("'", "\\'") + "'";
637 if (right.length > 0){
638 //if (this.out_props_array_plain.has_key(left)) {
639 // this.out_props_array_plain.set(left, new Gee.ArrayList<string>());
641 //this.out_props_array_plain.get(left).add(right);
643 //this.els.add(left + " : [\n" + this.pad + indent_str + indent_str +
644 // right + "\n" + this.pad + "]");
652 public void readListeners()
655 if (this.node.listeners.size < 1) {
658 // munge the listeners.
659 //print("ADDING listeners?");
664 var keys = new Gee.ArrayList<string>();
665 var piter = this.node.listeners.map_iterator();
666 while (piter.next() ) {
668 keys.add(piter.get_key());
670 keys.sort(( a, b) => {
671 return ((string)a).collate((string)b);
672 //if (a == b) return 0;
673 //return a < b ? -1 : 1;
676 var itms = "listeners : {\n";
678 for (var i = 0; i< keys.size; i++) {
679 var key = keys.get(i);
680 var val = this.node.listeners.get(key);
683 itms += i >0 ? ",\n" : "";
685 var str = val.strip();
686 var lines = str.split("\n");
687 if (lines.length > 0) {
688 //str = string.joinv("\n" + this.pad + " ", lines);
689 str = string.joinv("\n" + this.pad + indent_str + indent_str , lines);
692 itms += this.pad + indent_str + key.replace("|", "") + " : " + str;
693 this.out_listeners.set(key.replace("|", "") ,str);
697 itms += "\n" + this.pad + "}";
698 //print ( "ADD " + itms);
699 //this.els.add(itms);
703 public void iterChildren()
707 // finally munge the children...
708 if (this.node.items.size < 1) {
711 var itms = "items : [\n";
713 for(var i = 0; i < this.node.items.size;i++) {
714 var ele = this.node.items.get(i);
715 if (ele.props.has_key("* prop")) {
722 itms += this.pad + indent_str +
723 this.mungeChild( this.pad + indent_str + indent_str , ele);
725 this.out_children.add(ele);
728 itms += "\n"+ this.pad + "]" + "\n";
729 //this.els.add(itms);
732 // finally output listeners...
734 public void xIncludeToString()