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 + "'", ',');
235 this.node.line_end = this.cur_line;
239 var niter = this.out_nodeprops.map_iterator();
241 while(niter.next()) {
242 var addstr = this.mungeChildNew(this.pad + indent_str, niter.get_value());
243 this.node.setLine(this.cur_line, "p",niter.get_key());
244 this.addLine(this.pad + niter.get_key() + " : " + addstr, ',');
246 this.node.setLine(this.cur_line, "e", "");
250 var piter = this.out_props_array.map_iterator();
252 while(piter.next()) {
253 this.node.setLine(this.cur_line, "p",niter.get_key());
254 this.addLine(this.pad + piter.get_key() + " : [", 0);
256 var pliter = piter.get_value().list_iterator();
257 while (pliter.next()) {
258 var addstr = this.mungeChildNew(this.pad + indent_str + indent_str, pliter.get());
259 this.addLine(this.pad + indent_str + addstr, ',');
260 this.node.setLine(this.cur_line, "e", "");
263 this.addLine(this.pad + "]" , ',');
267 if (this.out_children.size > 0) {
268 this.addLine(this.pad + "items : [" , 0);
269 var cniter = this.out_children.list_iterator();
270 while (cniter.next()) {
271 suffix = cniter.has_next() ? "," : "";
272 var addstr = this.mungeChildNew(this.pad + indent_str + indent_str, cniter.get());
273 this.addLine(this.pad + indent_str + addstr, ',');
274 this.node.setLine(this.cur_line, "e", "");
278 this.addLine(this.pad + "]",',');
280 this.node.setLine(this.cur_line, "e", "");
282 if (this.node.props.has_key("* xinclude")) {
283 this.addLine(spad + "})",0);
286 this.addLine( spad + "}", 0);
289 this.node.sortLines();
299 * if we end with a ','
303 char last_line_end = '!';
306 * add a line - note we will end up with an extra line break
307 * at beginning of nodes doing this..
309 * @param str = text to add..
310 * @param line_end = 0 (just add a line break)
311 * line_end = ',' and ","
314 public void addLine(string str, char line_end)
316 if (this.last_line_end != '!') {
317 this.ret += (this.last_line_end == 0 ? "" : this.last_line_end.to_string()) + "\n";
319 this.last_line_end = line_end;
320 this.cur_line += str.split("\n").length;
324 //this.ret += "/*%d(%d-%d)*/ ".printf(this.cur_line -1, this.node.line_start,this.node.line_end) + str + "\n";
328 public void closeLine() // send this before '}' or ']' to block output of ',' ...
330 this.last_line_end = 0;
333 /* public void addMultiLine(str= "")
336 //this.ret += "/ * %d(%d-%d) * / ".printf(this.cur_line, this.node.line_start,this.node.line_end)+ str + "\n";
337 this.ret += str + "\n";
338 this.cur_line += str.split("\n").length;
341 public string mungeChildNew(string pad , Node cnode )
343 var x = new NodeToJs(cnode, this.doubleStringProps, pad, this);
350 * loop through items[] array see if any of the children have '* prop'
351 * -- which means they are a property of this node.
352 * -- ADD TO : this.opt_props_array
356 public void checkChildren ()
360 // look throught he chilren == looking for * prop.. -- fixme might not work..
363 if (!this.node.hasChildren()) {
368 for (var ii =0; ii< this.node.items.size; ii++) {
369 var pl = this.node.items.get(ii);
370 if (!pl.props.has_key("* prop")) {
375 //print(JSON.stringify(pl,null,4));
377 //var prop = pl['*prop'] + '';
378 //delete pl['*prop'];
379 var prop = pl.get("* prop");
380 //print("got prop "+ prop + "\n");
383 if (! Regex.match_simple("\\[\\]$", prop)) {
384 // it's a standard prop..
386 // munge property..??
388 this.out_nodeprops.set(prop, pl);
396 var sprop = prop.replace("[]", "");
397 //print("sprop is : " + sprop + "\n");
399 // it's an array type..
401 if (!this.out_props_array.has_key(sprop)) {
402 this.out_props_array.set(sprop, new Gee.ArrayList<Node>());
406 this.out_props_array.get(sprop).add( pl);
407 //this.ar_props.set(sprop, nstr);
414 * Standardize this crap...
416 * standard properties (use to set)
417 * If they are long values show the dialog..
420 * bool is_xxx :: can show a pulldown.. (true/false)
422 * $ string html = string with value interpolated eg. baseURL + ".."
423 * Clutter.ActorAlign x_align (typed) -- shows pulldowns if type is ENUM?
424 * $ untypedvalue = javascript untyped value...
425 * _ string html ... = translatable..
428 * object properties (not part of the GOjbect being wrapped?
429 * # Gee.ArrayList<Xcls_fileitem> fileitems
434 * methods -- always text editor..
442 * * init -- big string?
444 * event handlers (listeners)
449 * +XXXX -- indicates it's a instance property / not glob...
450 * *XXXX -- skip writing glob property (used as classes that can be created...)
454 public void readProps()
460 func_regex = new Regex("^\\s+|\\s+$");
461 } catch (RegexError e) {
462 print("failed to build regex");
465 // sort the key's so they always get rendered in the same order..
467 var keys = new Gee.ArrayList<string>();
468 var piter = this.node.props.map_iterator();
469 while (piter.next() ) {
473 this.node.normalize_key(piter.get_key(), out k, out kflag, out ktype);
478 keys.sort(( a, b) => {
479 return ((string)a).collate((string)b);
480 //if (a == b) return 0;
481 //return a < b ? -1 : 1;
484 var has_cms = this.node.has("cms-id");
486 for (var i = 0; i< keys.size; i++) {
487 var key = this.node.get_key(keys.get(i));
488 //("ADD KEY %s\n", key);
493 this.node.normalize_key(key, out k, out kflag, out ktype);
496 var v = this.node.get(key);
499 //if (this.skip.contains(k) ) {
502 if ( Regex.match_simple("\\[\\]$", k)) {
503 // array .. not supported... here?
509 // skip builder stuff. prefixed with '.' .. just like unix fs..
510 if (kflag == ".") { // |. or . -- do not output..
514 // ignore '* prop'; ???
518 // handle cms-id // html
519 if (has_cms && k == "cms-id") {
520 continue; // ignore it...
522 // html must not be a dynamic property...
523 // note - we do not translate this either...
524 if (has_cms && k == "html" && kflag != "$") {
527 this.out_props.set("html", "Pman.Cms.content(" +
528 this.node.quoteString(this.renderer.name + "::" + this.node.get("cms-id")) +
530 this.node.quoteString(v) +
539 if (Lang.isKeyword(leftv) || Lang.isBuiltin(leftv)) {
540 left = "'" + leftv + "'";
541 } else if (Regex.match_simple("[^A-Za-z_]+",leftv)) { // not plain a-z... - quoted.
542 var val = this.node.quoteString(leftv);
544 left = "'" + val.substring(1, val.length-2).replace("'", "\\'") + "'";
550 // next.. is it a function.. or a raw string..
558 // ??? any others that are raw output..
560 // does not hapepnd with arrays..
561 if (v.length < 1) { //if (typeof(el) == 'string' && !obj[i].length) { //skip empty.
568 str = func_regex.replace(v,v.length, 0, "");
570 print("regex failed");
576 var lines = str.split("\n");
578 if (lines.length > 0) {
579 nstr = string.joinv("\n" + this.pad, lines);
580 //nstr = string.joinv("\n", lines);
582 this.out_props.set(left, nstr);
588 //print("==> " + str + "\n");
589 //this.els.add(left + " : "+ nstr);
600 ktype.down() == "boolean"
602 ktype.down() == "bool"
604 ktype.down() == "number"
606 ktype.down() == "int"
607 ) { // boolean or number...?
608 this.out_props.set(left, v.down());
609 //this.els.add(left + " : " + v.down() );
613 // is it a translated string?
619 //if (this.doubleStringProps.size < 1) {
620 // this.els.add(left + this.node.quoteString(v));
624 if ((this.doubleStringProps.index_of(k) > -1) ||
625 (ktype.down() == "string" && k[0] == '_')
628 // then use the translated version...
631 (v.split("\n").length > 1 ?
632 ("\n" + this.pad + string.joinv(this.pad + "\n", v.split("\n")).replace("*/", "* - /") + "\n" + this.pad + "*/ ") :
633 (v.replace("*/", "* - /") + " */")
636 //this.els.add(left + " : _this._strings['" +
637 // GLib.Checksum.compute_for_string (ChecksumType.MD5, v) +
640 this.out_props.set(left, "_this._strings['" +
641 GLib.Checksum.compute_for_string (ChecksumType.MD5, v.strip()) +
646 // otherwise it needs to be encapsulated.. as single quotes..
648 var vv = this.node.quoteString(v);
649 // single quote.. v.substring(1, v.length-1).replace("'", "\\'") + "'";
650 //this.els.add(left + " : " + "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
651 this.out_props.set(left, "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
659 public void readListeners()
662 if (this.node.listeners.size < 1) {
665 // munge the listeners.
666 //print("ADDING listeners?");
671 var keys = new Gee.ArrayList<string>();
672 var piter = this.node.listeners.map_iterator();
673 while (piter.next() ) {
675 keys.add(piter.get_key());
677 keys.sort(( a, b) => {
678 return ((string)a).collate((string)b);
679 //if (a == b) return 0;
680 //return a < b ? -1 : 1;
684 for (var i = 0; i< keys.size; i++) {
685 var key = keys.get(i);
686 var val = this.node.listeners.get(key);
690 var str = val.strip();
691 var lines = str.split("\n");
692 if (lines.length > 0) {
693 //str = string.joinv("\n" + this.pad + " ", lines);
694 str = string.joinv("\n" + this.pad + indent_str + indent_str , lines);
697 this.out_listeners.set(key.replace("|", "") ,str);
706 public void iterChildren()
710 // finally munge the children...
711 if (this.node.items.size < 1) {
714 var itms = "items : [\n";
716 for(var i = 0; i < this.node.items.size;i++) {
717 var ele = this.node.items.get(i);
718 if (ele.props.has_key("* prop")) {
722 this.out_children.add(ele);
725 itms += "\n"+ this.pad + "]" + "\n";
726 //this.els.add(itms);
729 // finally output listeners...
731 public void xIncludeToString()