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..
182 var iter = this.orderedPropKeys().list_iterator();
187 var v = this.out_props.get(k);
188 this.node.setLine(this.cur_line, "p",k);
189 this.addLine(this.pad + k + " : " + v + suffix, ',');
190 this.node.setLine(this.cur_line, "p","| xns" );
192 this.addLine(this.pad + "'|xns' : '" + v + "'", ',');
195 this.node.setLine(this.cur_line, "e", "");
201 if (this.out_listeners.size > 0 ) {
203 this.addLine(this.pad + "listeners : {", 0);
204 iter = this.orderedListenerKeys().list_iterator();
209 var v = this.out_listeners.get(k);
210 this.node.setLine(this.cur_line, "l",k); //listener
211 this.addLine(this.pad + indent_str + k + " : " + v , ',');
212 this.node.setLine(this.cur_line, "e", "");
216 this.addLine(this.pad + "}" ,',');
220 //------- at this point it is the end of the code relating directly to the object..
224 this.node.line_end = this.cur_line;
228 var niter = this.out_nodeprops.map_iterator();
230 while(niter.next()) {
231 var addstr = this.mungeChildNew(this.pad + indent_str, niter.get_value());
232 this.node.setLine(this.cur_line, "p",niter.get_key());
233 this.addLine(this.pad + niter.get_key() + " : " + addstr, ',');
235 this.node.setLine(this.cur_line, "e", "");
239 var piter = this.out_props_array.map_iterator();
241 while(piter.next()) {
242 this.node.setLine(this.cur_line, "p",niter.get_key());
243 this.addLine(this.pad + piter.get_key() + " : [", 0);
245 var pliter = piter.get_value().list_iterator();
246 while (pliter.next()) {
247 var addstr = this.mungeChildNew(this.pad + indent_str + indent_str, pliter.get());
248 this.addLine(this.pad + indent_str + addstr, ',');
249 this.node.setLine(this.cur_line, "e", "");
252 this.addLine(this.pad + "]" , ',');
256 if (this.out_children.size > 0) {
257 this.addLine(this.pad + "items : [" , 0);
258 var cniter = this.out_children.list_iterator();
259 while (cniter.next()) {
260 suffix = cniter.has_next() ? "," : "";
261 var addstr = this.mungeChildNew(this.pad + indent_str + indent_str, cniter.get());
262 this.addLine(this.pad + indent_str + addstr, ',');
263 this.node.setLine(this.cur_line, "e", "");
267 this.addLine(this.pad + "]",',');
269 this.node.setLine(this.cur_line, "e", "");
271 if (this.node.props.has_key("* xinclude")) {
272 this.addLine(spad + "})",0);
275 this.addLine( spad + "}", 0);
278 this.node.sortLines();
288 * if we end with a ','
292 char last_line_end = '!';
295 * add a line - note we will end up with an extra line break
296 * at beginning of nodes doing this..
298 * @param str = text to add..
299 * @param line_end = 0 (just add a line break)
300 * line_end = ',' and ","
303 public void addLine(string str, char line_end)
305 if (this.last_line_end != '!') {
306 this.ret += (this.last_line_end == 0 ? "" : this.last_line_end.to_string()) + "\n";
308 this.last_line_end = line_end;
309 this.cur_line += str.split("\n").length;
313 //this.ret += "/*%d(%d-%d)*/ ".printf(this.cur_line -1, this.node.line_start,this.node.line_end) + str + "\n";
317 public void closeLine() // send this before '}' or ']' to block output of ',' ...
319 this.last_line_end = 0;
322 /* public void addMultiLine(str= "")
325 //this.ret += "/ * %d(%d-%d) * / ".printf(this.cur_line, this.node.line_start,this.node.line_end)+ str + "\n";
326 this.ret += str + "\n";
327 this.cur_line += str.split("\n").length;
330 public string mungeChildNew(string pad , Node cnode )
332 var x = new NodeToJs(cnode, this.doubleStringProps, pad, this);
339 * loop through items[] array see if any of the children have '* prop'
340 * -- which means they are a property of this node.
341 * -- ADD TO : this.opt_props_array
345 public void checkChildren ()
349 // look throught he chilren == looking for * prop.. -- fixme might not work..
352 if (!this.node.hasChildren()) {
357 for (var ii =0; ii< this.node.items.size; ii++) {
358 var pl = this.node.items.get(ii);
359 if (!pl.props.has_key("* prop")) {
364 //print(JSON.stringify(pl,null,4));
366 //var prop = pl['*prop'] + '';
367 //delete pl['*prop'];
368 var prop = pl.get("* prop");
369 //print("got prop "+ prop + "\n");
372 if (! Regex.match_simple("\\[\\]$", prop)) {
373 // it's a standard prop..
375 // munge property..??
377 this.out_nodeprops.set(prop, pl);
385 var sprop = prop.replace("[]", "");
386 //print("sprop is : " + sprop + "\n");
388 // it's an array type..
390 if (!this.out_props_array.has_key(sprop)) {
391 this.out_props_array.set(sprop, new Gee.ArrayList<Node>());
395 this.out_props_array.get(sprop).add( pl);
396 //this.ar_props.set(sprop, nstr);
403 * Standardize this crap...
405 * standard properties (use to set)
406 * If they are long values show the dialog..
409 * bool is_xxx :: can show a pulldown.. (true/false)
411 * $ string html = string with value interpolated eg. baseURL + ".."
412 * Clutter.ActorAlign x_align (typed) -- shows pulldowns if type is ENUM?
413 * $ untypedvalue = javascript untyped value...
414 * _ string html ... = translatable..
417 * object properties (not part of the GOjbect being wrapped?
418 * # Gee.ArrayList<Xcls_fileitem> fileitems
423 * methods -- always text editor..
431 * * init -- big string?
433 * event handlers (listeners)
438 * +XXXX -- indicates it's a instance property / not glob...
439 * *XXXX -- skip writing glob property (used as classes that can be created...)
443 public void readProps()
449 func_regex = new Regex("^\\s+|\\s+$");
450 } catch (RegexError e) {
451 print("failed to build regex");
454 // sort the key's so they always get rendered in the same order..
456 var keys = new Gee.ArrayList<string>();
457 var piter = this.node.props.map_iterator();
458 while (piter.next() ) {
462 this.node.normalize_key(piter.get_key(), out k, out kflag, out ktype);
467 keys.sort(( a, b) => {
468 return ((string)a).collate((string)b);
469 //if (a == b) return 0;
470 //return a < b ? -1 : 1;
473 var has_cms = this.node.has("cms-id");
475 for (var i = 0; i< keys.size; i++) {
476 var key = this.node.get_key(keys.get(i));
477 //("ADD KEY %s\n", key);
482 this.node.normalize_key(key, out k, out kflag, out ktype);
485 var v = this.node.get(key);
488 //if (this.skip.contains(k) ) {
491 if ( Regex.match_simple("\\[\\]$", k)) {
492 // array .. not supported... here?
498 // skip builder stuff. prefixed with '.' .. just like unix fs..
499 if (kflag == ".") { // |. or . -- do not output..
503 // ignore '* prop'; ???
507 // handle cms-id // html
508 if (has_cms && k == "cms-id") {
509 continue; // ignore it...
511 // html must not be a dynamic property...
512 // note - we do not translate this either...
513 if (has_cms && k == "html" && kflag != "$") {
516 this.out_props.set("html", "Pman.Cms.content(" +
517 this.node.quoteString(this.renderer.name + "::" + this.node.get("cms-id")) +
519 this.node.quoteString(v) +
528 if (Lang.isKeyword(leftv) || Lang.isBuiltin(leftv)) {
529 left = "'" + leftv + "'";
530 } else if (Regex.match_simple("[^A-Za-z_]+",leftv)) { // not plain a-z... - quoted.
531 var val = this.node.quoteString(leftv);
533 left = "'" + val.substring(1, val.length-2).replace("'", "\\'") + "'";
539 // next.. is it a function.. or a raw string..
547 // ??? any others that are raw output..
549 // does not hapepnd with arrays..
550 if (v.length < 1) { //if (typeof(el) == 'string' && !obj[i].length) { //skip empty.
557 str = func_regex.replace(v,v.length, 0, "");
559 print("regex failed");
565 var lines = str.split("\n");
567 if (lines.length > 0) {
568 nstr = string.joinv("\n" + this.pad, lines);
569 //nstr = string.joinv("\n", lines);
571 this.out_props.set(left, nstr);
577 //print("==> " + str + "\n");
578 //this.els.add(left + " : "+ nstr);
589 ktype.down() == "boolean"
591 ktype.down() == "bool"
593 ktype.down() == "number"
595 ktype.down() == "int"
596 ) { // boolean or number...?
597 this.out_props.set(left, v.down());
598 //this.els.add(left + " : " + v.down() );
602 // is it a translated string?
608 //if (this.doubleStringProps.size < 1) {
609 // this.els.add(left + this.node.quoteString(v));
613 if ((this.doubleStringProps.index_of(k) > -1) ||
614 (ktype.down() == "string" && k[0] == '_')
617 // then use the translated version...
620 (v.split("\n").length > 1 ?
621 ("\n" + this.pad + string.joinv(this.pad + "\n", v.split("\n")).replace("*/", "* - /") + "\n" + this.pad + "*/ ") :
622 (v.replace("*/", "* - /") + " */")
625 //this.els.add(left + " : _this._strings['" +
626 // GLib.Checksum.compute_for_string (ChecksumType.MD5, v) +
629 this.out_props.set(left, "_this._strings['" +
630 GLib.Checksum.compute_for_string (ChecksumType.MD5, v.strip()) +
635 // otherwise it needs to be encapsulated.. as single quotes..
637 var vv = this.node.quoteString(v);
638 // single quote.. v.substring(1, v.length-1).replace("'", "\\'") + "'";
639 //this.els.add(left + " : " + "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
640 this.out_props.set(left, "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
648 public void readListeners()
651 if (this.node.listeners.size < 1) {
654 // munge the listeners.
655 //print("ADDING listeners?");
660 var keys = new Gee.ArrayList<string>();
661 var piter = this.node.listeners.map_iterator();
662 while (piter.next() ) {
664 keys.add(piter.get_key());
666 keys.sort(( a, b) => {
667 return ((string)a).collate((string)b);
668 //if (a == b) return 0;
669 //return a < b ? -1 : 1;
673 for (var i = 0; i< keys.size; i++) {
674 var key = keys.get(i);
675 var val = this.node.listeners.get(key);
679 var str = val.strip();
680 var lines = str.split("\n");
681 if (lines.length > 0) {
682 //str = string.joinv("\n" + this.pad + " ", lines);
683 str = string.joinv("\n" + this.pad + indent_str + indent_str , lines);
686 this.out_listeners.set(key.replace("|", "") ,str);
695 public void iterChildren()
699 // finally munge the children...
700 if (this.node.items.size < 1) {
703 var itms = "items : [\n";
705 for(var i = 0; i < this.node.items.size;i++) {
706 var ele = this.node.items.get(i);
707 if (ele.props.has_key("* prop")) {
711 this.out_children.add(ele);
714 itms += "\n"+ this.pad + "]" + "\n";
715 //this.els.add(itms);
718 // finally output listeners...
720 public void xIncludeToString()