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;
50 //this.els = new Gee.ArrayList<string>();
51 //this.ar_props = new Gee.HashMap<string,string>();
55 // this is the bit that causes issues - we have to output as we go, otherwise we
56 // can not work out which line is which...
58 this.out_props = new Gee.HashMap<string,string>();
59 this.out_listeners = new Gee.HashMap<string,string>();
62 this.out_nodeprops = new Gee.HashMap<string,Node>() ;
63 this.out_children = new Gee.ArrayList<Node> ();
65 this.out_props_array = new Gee.HashMap<string,Gee.ArrayList<Node>>(); // filled in by 'checkChildren'
66 this.out_props_array_plain = new Gee.HashMap<string,Gee.ArrayList<string>>() ;
70 this.cur_line = parent == null ? 0 : parent.cur_line ; //-1 as we usuall concat onto the existin gline?
72 this.renderer = parent.renderer;
75 this.top = parent == null ? this : parent.top;
78 node.node_lines = new Gee.ArrayList<int>();
79 node.node_lines_map = new Gee.HashMap<int,Node>();
88 public string munge ( )
90 //return this.mungeToString(this.node);
95 //this.readArrayProps();
98 if (!this.node.props.has_key("* xinclude")) {
104 // no properties to output...
105 //if (this.els.size < 1) {
115 This currently works by creating a key/value array of this.els, which is just an array of properties..
116 this is so that join() works...
120 b) output plan properties.
121 c) output listeners..
123 g) output prop_arrays..
131 public Gee.ArrayList<string> orderedPropKeys() {
133 var ret = new Gee.ArrayList<string> ();
134 var niter = this.out_props.map_iterator();
135 while(niter.next()) {
136 ret.add(niter.get_key());
139 ret.sort(( a, b) => {
140 return ((string)a).collate((string)b);
141 //if (a == b) return 0;
142 //return a < b ? -1 : 1;
146 public Gee.ArrayList<string> orderedListenerKeys() {
148 var ret = new Gee.ArrayList<string> ();
149 var niter = this.out_listeners.map_iterator();
150 while(niter.next()) {
151 ret.add(niter.get_key());
154 ret.sort(( a, b) => {
155 return ((string)a).collate((string)b);
156 //if (a == b) return 0;
157 //return a < b ? -1 : 1;
163 public string mungeOut()
165 this.node.line_start = this.cur_line;
166 this.top.node.setNodeLine(this.cur_line, this.node);
167 var spad = this.pad.substring(0, this.pad.length-indent);
169 if (this.node.props.has_key("* xinclude")) {
170 this.addLine("Roo.apply(" + this.node.props.get("* xinclude") + "._tree(), {",0 );
173 this.addLine("{", 0);
176 // output the items...
177 // work out remaining items...
179 // output xns / xtype first..
180 if (this.out_props.has_key("xtype")) {
181 var v = this.out_props.get("xtype");
182 this.node.setLine(this.cur_line, "p","xtype");
183 this.addLine(this.pad + "xtype" + " : " + v + suffix, ',');
187 var iter = this.orderedPropKeys().list_iterator();
192 if (k == "xns" || k == "xtype") {
196 var v = this.out_props.get(k);
197 this.node.setLine(this.cur_line, "p",k);
198 this.addLine(this.pad + k + " : " + v + suffix, ',');
200 this.node.setLine(this.cur_line, "e", "");
206 if (this.out_listeners.size > 0 ) {
208 this.addLine(this.pad + "listeners : {", 0);
209 iter = this.orderedListenerKeys().list_iterator();
214 var v = this.out_listeners.get(k);
215 this.node.setLine(this.cur_line, "l",k); //listener
216 this.addLine(this.pad + indent_str + k + " : " + v , ',');
217 this.node.setLine(this.cur_line, "e", "");
221 this.addLine(this.pad + "}" ,',');
225 //------- at this point it is the end of the code relating directly to the object..
227 if (this.out_props.has_key("xns")) {
228 var v = this.out_props.get("xns");
229 this.node.setLine(this.cur_line, "p","xns");
230 this.addLine(this.pad + "xns" + " : " + v + suffix, ',');
231 this.node.setLine(this.cur_line, "p","| xns");
232 this.addLine(this.pad + "'|xns' : '" + v + "'", ',');
233 this.node.setLine(this.cur_line, "e", "");
236 this.node.line_end = this.cur_line;
240 var niter = this.out_nodeprops.map_iterator();
242 while(niter.next()) {
243 var addstr = this.mungeChildNew(this.pad + indent_str, niter.get_value());
244 this.node.setLine(this.cur_line, "p",niter.get_key());
245 this.addLine(this.pad + niter.get_key() + " : " + addstr, ',');
247 this.node.setLine(this.cur_line, "e", "");
251 var piter = this.out_props_array.map_iterator();
253 while(piter.next()) {
254 this.node.setLine(this.cur_line, "p",niter.get_key());
255 this.addLine(this.pad + piter.get_key() + " : [", 0);
257 var pliter = piter.get_value().list_iterator();
258 while (pliter.next()) {
259 var addstr = this.mungeChildNew(this.pad + indent_str + indent_str, pliter.get());
260 this.addLine(this.pad + indent_str + addstr, ',');
261 this.node.setLine(this.cur_line, "e", "");
264 this.addLine(this.pad + "]" , ',');
268 if (this.out_children.size > 0) {
269 this.addLine(this.pad + "items : [" , 0);
270 var cniter = this.out_children.list_iterator();
271 while (cniter.next()) {
272 suffix = cniter.has_next() ? "," : "";
273 var addstr = this.mungeChildNew(this.pad + indent_str + indent_str, cniter.get());
274 this.addLine(this.pad + indent_str + addstr, ',');
275 this.node.setLine(this.cur_line, "e", "");
279 this.addLine(this.pad + "]",',');
281 this.node.setLine(this.cur_line, "e", "");
283 if (this.node.props.has_key("* xinclude")) {
284 this.addLine(spad + "})",0);
287 this.addLine( spad + "}", 0);
290 this.node.sortLines();
300 * if we end with a ','
304 char last_line_end = '!';
307 * add a line - note we will end up with an extra line break
308 * at beginning of nodes doing this..
310 * @param str = text to add..
311 * @param line_end = 0 (just add a line break)
312 * line_end = ',' and ","
315 public void addLine(string str, char line_end)
317 if (this.last_line_end != '!') {
318 this.ret += (this.last_line_end == 0 ? "" : this.last_line_end.to_string()) + "\n";
320 this.last_line_end = line_end;
321 this.cur_line += str.split("\n").length;
325 //this.ret += "/*%d(%d-%d)*/ ".printf(this.cur_line -1, this.node.line_start,this.node.line_end) + str + "\n";
329 public void closeLine() // send this before '}' or ']' to block output of ',' ...
331 this.last_line_end = 0;
334 /* public void addMultiLine(str= "")
337 //this.ret += "/ * %d(%d-%d) * / ".printf(this.cur_line, this.node.line_start,this.node.line_end)+ str + "\n";
338 this.ret += str + "\n";
339 this.cur_line += str.split("\n").length;
342 public string mungeChildNew(string pad , Node cnode )
344 var x = new NodeToJs(cnode, this.doubleStringProps, pad, this);
351 * loop through items[] array see if any of the children have '* prop'
352 * -- which means they are a property of this node.
353 * -- ADD TO : this.opt_props_array
357 public void checkChildren ()
361 // look throught he chilren == looking for * prop.. -- fixme might not work..
364 if (!this.node.hasChildren()) {
369 for (var ii =0; ii< this.node.items.size; ii++) {
370 var pl = this.node.items.get(ii);
371 if (!pl.props.has_key("* prop")) {
376 //print(JSON.stringify(pl,null,4));
378 //var prop = pl['*prop'] + '';
379 //delete pl['*prop'];
380 var prop = pl.get("* prop");
381 //print("got prop "+ prop + "\n");
384 if (! Regex.match_simple("\\[\\]$", prop)) {
385 // it's a standard prop..
387 // munge property..??
389 this.out_nodeprops.set(prop, pl);
397 var sprop = prop.replace("[]", "");
398 //print("sprop is : " + sprop + "\n");
400 // it's an array type..
402 if (!this.out_props_array.has_key(sprop)) {
403 this.out_props_array.set(sprop, new Gee.ArrayList<Node>());
407 this.out_props_array.get(sprop).add( pl);
408 //this.ar_props.set(sprop, nstr);
415 * Standardize this crap...
417 * standard properties (use to set)
418 * If they are long values show the dialog..
421 * bool is_xxx :: can show a pulldown.. (true/false)
423 * $ string html = string with value interpolated eg. baseURL + ".."
424 * Clutter.ActorAlign x_align (typed) -- shows pulldowns if type is ENUM?
425 * $ untypedvalue = javascript untyped value...
426 * _ string html ... = translatable..
429 * object properties (not part of the GOjbect being wrapped?
430 * # Gee.ArrayList<Xcls_fileitem> fileitems
435 * methods -- always text editor..
443 * * init -- big string?
445 * event handlers (listeners)
450 * +XXXX -- indicates it's a instance property / not glob...
451 * *XXXX -- skip writing glob property (used as classes that can be created...)
455 public void readProps()
461 func_regex = new Regex("^\\s+|\\s+$");
462 } catch (RegexError e) {
463 print("failed to build regex");
466 // sort the key's so they always get rendered in the same order..
468 var keys = new Gee.ArrayList<string>();
469 var piter = this.node.props.map_iterator();
470 while (piter.next() ) {
474 this.node.normalize_key(piter.get_key(), out k, out kflag, out ktype);
479 keys.sort(( a, b) => {
480 return ((string)a).collate((string)b);
481 //if (a == b) return 0;
482 //return a < b ? -1 : 1;
485 var has_cms = this.node.has("cms-id");
487 for (var i = 0; i< keys.size; i++) {
488 var key = this.node.get_key(keys.get(i));
489 //("ADD KEY %s\n", key);
494 this.node.normalize_key(key, out k, out kflag, out ktype);
497 var v = this.node.get(key);
500 //if (this.skip.contains(k) ) {
503 if ( Regex.match_simple("\\[\\]$", k)) {
504 // array .. not supported... here?
510 // skip builder stuff. prefixed with '.' .. just like unix fs..
511 if (kflag == ".") { // |. or . -- do not output..
515 // ignore '* prop'; ???
519 // handle cms-id // html
520 if (has_cms && k == "cms-id") {
521 continue; // ignore it...
523 // html must not be a dynamic property...
524 // note - we do not translate this either...
525 if (has_cms && k == "html" && kflag != "$") {
528 this.out_props.set("html", "Pman.Cms.content(" +
529 this.node.quoteString(this.renderer.name + "::" + this.node.get("cms-id")) +
531 this.node.quoteString(v) +
540 if (Lang.isKeyword(leftv) || Lang.isBuiltin(leftv)) {
541 left = "'" + leftv + "'";
542 } else if (Regex.match_simple("[^A-Za-z_]+",leftv)) { // not plain a-z... - quoted.
543 var val = this.node.quoteString(leftv);
545 left = "'" + val.substring(1, val.length-2).replace("'", "\\'") + "'";
551 // next.. is it a function.. or a raw string..
559 // ??? any others that are raw output..
561 // does not hapepnd with arrays..
562 if (v.length < 1) { //if (typeof(el) == 'string' && !obj[i].length) { //skip empty.
569 str = func_regex.replace(v,v.length, 0, "");
571 print("regex failed");
577 var lines = str.split("\n");
579 if (lines.length > 0) {
580 nstr = string.joinv("\n" + this.pad, lines);
581 //nstr = string.joinv("\n", lines);
583 this.out_props.set(left, nstr);
589 //print("==> " + str + "\n");
590 //this.els.add(left + " : "+ nstr);
601 ktype.down() == "boolean"
603 ktype.down() == "bool"
605 ktype.down() == "number"
607 ktype.down() == "int"
608 ) { // boolean or number...?
609 this.out_props.set(left, v.down());
610 //this.els.add(left + " : " + v.down() );
614 // is it a translated string?
620 //if (this.doubleStringProps.size < 1) {
621 // this.els.add(left + this.node.quoteString(v));
625 if ((this.doubleStringProps.index_of(k) > -1) ||
626 (ktype.down() == "string" && k[0] == '_')
629 // then use the translated version...
632 (v.split("\n").length > 1 ?
633 ("\n" + this.pad + string.joinv(this.pad + "\n", v.split("\n")).replace("*/", "* - /") + "\n" + this.pad + "*/ ") :
634 (v.replace("*/", "* - /") + " */")
637 //this.els.add(left + " : _this._strings['" +
638 // GLib.Checksum.compute_for_string (ChecksumType.MD5, v) +
641 this.out_props.set(left, "_this._strings['" +
642 GLib.Checksum.compute_for_string (ChecksumType.MD5, v.strip()) +
647 // otherwise it needs to be encapsulated.. as single quotes..
649 var vv = this.node.quoteString(v);
650 // single quote.. v.substring(1, v.length-1).replace("'", "\\'") + "'";
651 //this.els.add(left + " : " + "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
652 this.out_props.set(left, "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
660 public void readListeners()
663 if (this.node.listeners.size < 1) {
666 // munge the listeners.
667 //print("ADDING listeners?");
672 var keys = new Gee.ArrayList<string>();
673 var piter = this.node.listeners.map_iterator();
674 while (piter.next() ) {
676 keys.add(piter.get_key());
678 keys.sort(( a, b) => {
679 return ((string)a).collate((string)b);
680 //if (a == b) return 0;
681 //return a < b ? -1 : 1;
685 for (var i = 0; i< keys.size; i++) {
686 var key = keys.get(i);
687 var val = this.node.listeners.get(key);
691 var str = val.strip();
692 var lines = str.split("\n");
693 if (lines.length > 0) {
694 //str = string.joinv("\n" + this.pad + " ", lines);
695 str = string.joinv("\n" + this.pad + indent_str + indent_str , lines);
698 this.out_listeners.set(key.replace("|", "") ,str);
707 public void iterChildren()
711 // finally munge the children...
712 if (this.node.items.size < 1) {
715 var itms = "items : [\n";
717 for(var i = 0; i < this.node.items.size;i++) {
718 var ele = this.node.items.get(i);
719 if (ele.props.has_key("* prop")) {
723 this.out_children.add(ele);
726 itms += "\n"+ this.pad + "]" + "\n";
727 //this.els.add(itms);
730 // finally output listeners...
732 public void xIncludeToString()