3 * Code to convert node tree to Javascript...
5 * usage : x = (new JsRender.NodeToJs(node)).munge();
8 * We are changing this to output as we go.
9 * However... since line-endings on properties have ',' (not ;) like vala.
10 * we have to be a bit smarter about how to output.
19 public class JsRender.NodeToJs : Object {
21 static uint indent = 1;
22 static string indent_str = " ";
27 Gee.ArrayList<string> doubleStringProps; // need to think if this is a good idea like this
29 public JsRender renderer;
31 Gee.HashMap<string,string> out_props;
32 Gee.HashMap<string,string> out_listeners;
33 Gee.HashMap<string,Node> out_nodeprops;
34 Gee.ArrayList<Node> out_children;
35 Gee.HashMap<string,Gee.ArrayList<Node>> out_props_array;
36 Gee.HashMap<string,Gee.ArrayList<string>> out_props_array_plain;
44 public NodeToJs( Node node, Gee.ArrayList<string> doubleStringProps, string pad, NodeToJs? parent)
47 this.doubleStringProps = doubleStringProps;
49 this.node.node_pad = pad;
51 //this.els = new Gee.ArrayList<string>();
52 //this.ar_props = new Gee.HashMap<string,string>();
56 // this is the bit that causes issues - we have to output as we go, otherwise we
57 // can not work out which line is which...
59 this.out_props = new Gee.HashMap<string,string>();
60 this.out_listeners = new Gee.HashMap<string,string>();
63 this.out_nodeprops = new Gee.HashMap<string,Node>() ;
64 this.out_children = new Gee.ArrayList<Node> ();
66 this.out_props_array = new Gee.HashMap<string,Gee.ArrayList<Node>>(); // filled in by 'checkChildren'
67 this.out_props_array_plain = new Gee.HashMap<string,Gee.ArrayList<string>>() ;
71 this.cur_line = parent == null ? 0 : parent.cur_line ; //-1 as we usuall concat onto the existin gline?
73 this.renderer = parent.renderer;
76 this.top = parent == null ? this : parent.top;
79 node.node_lines = new Gee.ArrayList<int>();
80 node.node_lines_map = new Gee.HashMap<int,Node>();
89 public string munge ( )
91 //return this.mungeToString(this.node);
96 //this.readArrayProps();
99 if (!this.node.props.has_key("* xinclude")) {
105 // no properties to output...
106 //if (this.els.size < 1) {
116 This currently works by creating a key/value array of this.els, which is just an array of properties..
117 this is so that join() works...
121 b) output plan properties.
122 c) output listeners..
124 g) output prop_arrays..
132 public Gee.ArrayList<string> orderedPropKeys() {
134 var ret = new Gee.ArrayList<string> ();
135 var niter = this.out_props.map_iterator();
136 while(niter.next()) {
137 ret.add(niter.get_key());
140 ret.sort(( a, b) => {
141 return ((string)a).collate((string)b);
142 //if (a == b) return 0;
143 //return a < b ? -1 : 1;
147 public Gee.ArrayList<string> orderedListenerKeys() {
149 var ret = new Gee.ArrayList<string> ();
150 var niter = this.out_listeners.map_iterator();
151 while(niter.next()) {
152 ret.add(niter.get_key());
155 ret.sort(( a, b) => {
156 return ((string)a).collate((string)b);
157 //if (a == b) return 0;
158 //return a < b ? -1 : 1;
164 public string mungeOut()
166 this.node.line_start = this.cur_line;
167 this.top.node.setNodeLine(this.cur_line, this.node);
168 var spad = this.pad.substring(0, this.pad.length-indent);
170 if (this.node.props.has_key("* xinclude")) {
171 this.addLine("Roo.apply(" + this.node.props.get("* xinclude").val + "._tree(), {",0 );
174 this.addLine("{", 0);
177 // output the items...
178 // work out remaining items...
180 // output xns / xtype first..
181 if (this.out_props.has_key("xtype")) {
182 var v = this.out_props.get("xtype");
184 this.node.setLine(this.cur_line, "p","xtype");
185 this.addLine(this.pad + "xtype" + " : " + v + suffix, ',');
189 var iter = this.orderedPropKeys().list_iterator();
194 if (k == "xns" || k == "xtype") {
198 var v = this.out_props.get(k);
199 this.node.setLine(this.cur_line, "p",k);
201 this.addLine(this.pad + k + " : " + v + suffix, ',');
203 this.node.setLine(this.cur_line, "e", k);
209 if (this.out_listeners.size > 0 ) {
211 this.addLine(this.pad + "listeners : {", 0);
212 iter = this.orderedListenerKeys().list_iterator();
217 var v = this.out_listeners.get(k);
219 this.node.setLine(this.cur_line, "l",k); //listener
220 this.addLine(this.pad + indent_str + k + " : " + v , ',');
222 this.node.setLine(this.cur_line, "x", k);
226 this.addLine(this.pad + "}" ,',');
230 //------- at this point it is the end of the code relating directly to the object..
232 if (this.out_props.has_key("xns")) {
233 var v = this.out_props.get("xns");
235 this.node.setLine(this.cur_line, "p","xns");
236 this.addLine(this.pad + "xns" + " : " + v + suffix, ',');
237 this.node.setLine(this.cur_line, "p","| xns");
238 this.addLine(this.pad + "'|xns' : '" + v + "'", ',');
239 this.node.setLine(this.cur_line, "e", "xns");
243 this.node.line_end = this.cur_line;
247 var niter = this.out_nodeprops.map_iterator();
249 while(niter.next()) {
251 //print("add str: %s\n", addstr);
252 this.node.setLine(this.cur_line, "p",niter.get_key());
254 var addstr = this.mungeChildNew(this.pad + indent_str, niter.get_value());
255 this.addLine(this.pad + niter.get_key() + " : " + addstr, ',');
257 this.node.setLine(this.cur_line, "e", "");
261 var piter = this.out_props_array.map_iterator();
263 while(piter.next()) {
265 this.node.setLine(this.cur_line, "p",piter.get_key());
266 this.addLine(this.pad + piter.get_key() + " : [", 0);
268 var pliter = piter.get_value().list_iterator();
269 while (pliter.next()) {
270 var addstr = this.mungeChildNew(this.pad + indent_str + indent_str, pliter.get());
271 this.addLine(this.pad + indent_str + addstr, ',');
272 this.node.setLine(this.cur_line, "e", "");
276 this.addLine(this.pad + "]" , ',');
281 if (this.out_children.size > 0) {
282 this.addLine(this.pad + "items : [" , 0);
283 var cniter = this.out_children.list_iterator();
284 while (cniter.next()) {
285 suffix = cniter.has_next() ? "," : "";
286 var addstr = this.mungeChildNew(this.pad + indent_str + indent_str, cniter.get());
287 this.addLine(this.pad + indent_str + addstr, ',');
288 this.node.setLine(this.cur_line, "e", "");
292 this.addLine(this.pad + "]",',');
294 this.node.setLine(this.cur_line, "e", "");
296 if (this.node.props.has_key("* xinclude")) {
297 this.addLine(spad + "})",0);
300 this.addLine( spad + "}", 0);
303 this.node.sortLines();
313 * if we end with a ','
317 char last_line_end = '!';
320 * add a line - note we will end up with an extra line break
321 * at beginning of nodes doing this..
323 * @param str = text to add..
324 * @param line_end = 0 (just add a line break)
325 * line_end = ',' and ","
328 public void addLine(string str, char line_end)
330 if (this.last_line_end != '!') {
331 this.ret += (this.last_line_end == 0 ? "" : this.last_line_end.to_string()) + "\n";
333 this.last_line_end = line_end;
334 this.cur_line += str.split("\n").length;
338 //this.ret += "/*%d(%d-%d)*/ ".printf(this.cur_line -1, this.node.line_start,this.node.line_end) + str + "\n";
342 public void closeLine() // send this before '}' or ']' to block output of ',' ...
344 this.last_line_end = 0;
347 /* public void addMultiLine(str= "")
350 //this.ret += "/ * %d(%d-%d) * / ".printf(this.cur_line, this.node.line_start,this.node.line_end)+ str + "\n";
351 this.ret += str + "\n";
352 this.cur_line += str.split("\n").length;
355 public string mungeChildNew(string pad , Node cnode )
357 var x = new NodeToJs(cnode, this.doubleStringProps, pad, this);
364 * loop through items[] array see if any of the children have '* prop'
365 * -- which means they are a property of this node.
366 * -- ADD TO : this.opt_props_array
370 public void checkChildren ()
374 // look throught he chilren == looking for * prop.. -- fixme might not work..
377 if (!this.node.hasChildren()) {
381 var items = this.node.readItems();
382 for (var ii =0; ii< items.size; ii++) {
383 var pl = items.get(ii);
384 if (!pl.props.has_key("* prop")) {
389 //print(JSON.stringify(pl,null,4));
391 //var prop = pl['*prop'] + '';
392 //delete pl['*prop'];
393 var prop = pl.get("* prop");
394 //print("got prop "+ prop + "\n");
397 if (! Regex.match_simple("\\[\\]$", prop)) {
398 // it's a standard prop..
400 // munge property..??
402 this.out_nodeprops.set(prop, pl);
410 var sprop = prop.replace("[]", "");
411 //print("sprop is : " + sprop + "\n");
413 // it's an array type..
415 if (!this.out_props_array.has_key(sprop)) {
416 this.out_props_array.set(sprop, new Gee.ArrayList<Node>());
420 this.out_props_array.get(sprop).add( pl);
421 //this.ar_props.set(sprop, nstr);
428 * Standardize this crap...
430 * standard properties (use to set)
431 * If they are long values show the dialog..
434 * bool is_xxx :: can show a pulldown.. (true/false)
436 * $ string html = string with value interpolated eg. baseURL + ".."
437 * Clutter.ActorAlign x_align (typed) -- shows pulldowns if type is ENUM?
438 * $ untypedvalue = javascript untyped value...
439 * _ string html ... = translatable..
442 * object properties (not part of the GOjbect being wrapped?
443 * # Gee.ArrayList<Xcls_fileitem> fileitems
448 * methods -- always text editor..
456 * * init -- big string?
458 * event handlers (listeners)
463 * +XXXX -- indicates it's a instance property / not glob...
464 * *XXXX -- skip writing glob property (used as classes that can be created...)
468 public void readProps()
474 func_regex = new Regex("^\\s+|\\s+$");
475 } catch (RegexError e) {
476 print("failed to build regex");
479 // sort the key's so they always get rendered in the same order..
481 var keys = new Gee.ArrayList<string>();
482 var piter = this.node.props.map_iterator();
483 while (piter.next() ) {
486 keys.add( piter.get_key()); // since are keys are nice and clean now..
489 keys.sort(( a, b) => {
490 return ((string)a).collate((string)b);
491 //if (a == b) return 0;
492 //return a < b ? -1 : 1;
495 var has_cms = this.node.has("cms-id");
497 for (var i = 0; i< keys.size; i++) {
498 var prop = this.node.get_prop(keys.get(i));
499 //("ADD KEY %s\n", key);
501 var ktype = prop.rtype;
502 var kflag = prop.ptype;
506 //if (this.skip.contains(k) ) {
509 if ( Regex.match_simple("\\[\\]$", k)) {
510 // array .. not supported... here?
515 // skip builder stuff. prefixed with '.' .. just like unix fs..
516 //if (kflag == ".") { // |. or . -- do not output..
519 if (kflag == NodePropType.SPECIAL) {
520 // ignore '* prop'; ???
524 // handle cms-id // html
525 if (has_cms && k == "cms-id") {
526 continue; // ignore it...
528 // html must not be a dynamic property...
529 // note - we do not translate this either...
530 if (has_cms && k == "html" && kflag != NodePropType.RAW) {
533 this.out_props.set("html", "Pman.Cms.content(" +
534 this.node.quoteString(this.renderer.name + "::" + this.node.get("cms-id")) +
536 this.node.quoteString(v) +
545 if (Lang.isKeyword(leftv) || Lang.isBuiltin(leftv)) {
546 left = "'" + leftv + "'";
547 } else if (Regex.match_simple("[^A-Za-z_]+",leftv)) { // not plain a-z... - quoted.
548 var val = this.node.quoteString(leftv);
550 left = "'" + val.substring(1, val.length-2).replace("'", "\\'") + "'";
556 // next.. is it a function.. or a raw string..
558 kflag == NodePropType.METHOD
560 kflag == NodePropType.RAW
562 ktype == "function" // ??? why woudl return type be function? << messed up..
564 // ??? any others that are raw output..
566 // does not hapepnd with arrays..
567 if (v.length < 1) { //if (typeof(el) == 'string' && !obj[i].length) { //skip empty.
574 str = func_regex.replace(v,v.length, 0, "");
576 print("regex failed");
582 var lines = str.split("\n");
584 if (lines.length > 0) {
585 nstr = string.joinv("\n" + this.pad, lines);
586 //nstr = string.joinv("\n", lines);
588 this.out_props.set(left, nstr);
594 //print("==> " + str + "\n");
595 //this.els.add(left + " : "+ nstr);
606 ktype.down() == "boolean"
608 ktype.down() == "bool"
610 ktype.down() == "number"
612 ktype.down() == "int"
613 ) { // boolean or number...?
614 this.out_props.set(left, v.down());
615 //this.els.add(left + " : " + v.down() );
619 // is it a translated string?
625 // doubleStringProps is a list of keys like 'name' 'title' etc.. that we know can be translated..
627 if ((this.doubleStringProps.index_of(k) > -1) ||
628 (ktype.down() == "string" && k[0] == '_') // strings starting with '_'
631 // then use the translated version...
634 (v.split("\n").length > 1 ?
635 ("\n" + this.pad + string.joinv(this.pad + "\n", v.split("\n")).replace("*/", "* - /") + "\n" + this.pad + "*/ ") :
636 (v.replace("*/", "* - /") + " */")
639 //this.els.add(left + " : _this._strings['" +
640 // GLib.Checksum.compute_for_string (ChecksumType.MD5, v) +
644 // string is stored in Roo.vala
645 var kname = GLib.Checksum.compute_for_string (ChecksumType.MD5, v.strip());
647 this.out_props.set(left, "_this._strings['" + kname + "']" + com);
651 // otherwise it needs to be encapsulated.. as single quotes..
653 var vv = this.node.quoteString(v);
654 // single quote.. v.substring(1, v.length-1).replace("'", "\\'") + "'";
655 //this.els.add(left + " : " + "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
656 this.out_props.set(left, "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
664 public void readListeners()
667 if (this.node.listeners.size < 1) {
670 // munge the listeners.
671 //print("ADDING listeners?");
676 var keys = new Gee.ArrayList<string>();
677 var piter = this.node.listeners.map_iterator();
678 while (piter.next() ) {
680 keys.add(piter.get_key());
682 keys.sort(( a, b) => {
683 return ((string)a).collate((string)b);
684 //if (a == b) return 0;
685 //return a < b ? -1 : 1;
689 for (var i = 0; i< keys.size; i++) {
690 var key = keys.get(i);
691 var val = this.node.listeners.get(key).val;
695 var str = val.strip();
696 var lines = str.split("\n");
697 if (lines.length > 0) {
698 //str = string.joinv("\n" + this.pad + " ", lines);
699 str = string.joinv("\n" + this.pad + indent_str + indent_str , lines);
702 this.out_listeners.set(key.replace("|", "") ,str);
711 public void iterChildren()
714 var items = this.node.readItems();
715 // finally munge the children...
716 if (items.size < 1) {
719 var itms = "items : [\n";
721 for(var i = 0; i < items.size;i++) {
722 var ele = items.get(i);
723 if (ele.props.has_key("* prop")) {
727 this.out_children.add(ele);
730 itms += "\n"+ this.pad + "]" + "\n";
731 //this.els.add(itms);
734 // finally output listeners...
736 public void xIncludeToString()