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 //print("add str: %s\n", addstr);
245 this.node.setLine(this.cur_line, "p",niter.get_key());
246 this.addLine(this.pad + niter.get_key() + " : " + addstr, ',');
248 this.node.setLine(this.cur_line, "e", "");
252 var piter = this.out_props_array.map_iterator();
254 while(piter.next()) {
255 this.node.setLine(this.cur_line, "p",piter.get_key());
256 this.addLine(this.pad + piter.get_key() + " : [", 0);
258 var pliter = piter.get_value().list_iterator();
259 while (pliter.next()) {
260 var addstr = this.mungeChildNew(this.pad + indent_str + indent_str, pliter.get());
261 this.addLine(this.pad + indent_str + addstr, ',');
262 this.node.setLine(this.cur_line, "e", "");
265 this.addLine(this.pad + "]" , ',');
269 if (this.out_children.size > 0) {
270 this.addLine(this.pad + "items : [" , 0);
271 var cniter = this.out_children.list_iterator();
272 while (cniter.next()) {
273 suffix = cniter.has_next() ? "," : "";
274 var addstr = this.mungeChildNew(this.pad + indent_str + indent_str, cniter.get());
275 this.addLine(this.pad + indent_str + addstr, ',');
276 this.node.setLine(this.cur_line, "e", "");
280 this.addLine(this.pad + "]",',');
282 this.node.setLine(this.cur_line, "e", "");
284 if (this.node.props.has_key("* xinclude")) {
285 this.addLine(spad + "})",0);
288 this.addLine( spad + "}", 0);
291 this.node.sortLines();
301 * if we end with a ','
305 char last_line_end = '!';
308 * add a line - note we will end up with an extra line break
309 * at beginning of nodes doing this..
311 * @param str = text to add..
312 * @param line_end = 0 (just add a line break)
313 * line_end = ',' and ","
316 public void addLine(string str, char line_end)
318 if (this.last_line_end != '!') {
319 this.ret += (this.last_line_end == 0 ? "" : this.last_line_end.to_string()) + "\n";
321 this.last_line_end = line_end;
322 this.cur_line += str.split("\n").length;
326 //this.ret += "/*%d(%d-%d)*/ ".printf(this.cur_line -1, this.node.line_start,this.node.line_end) + str + "\n";
330 public void closeLine() // send this before '}' or ']' to block output of ',' ...
332 this.last_line_end = 0;
335 /* public void addMultiLine(str= "")
338 //this.ret += "/ * %d(%d-%d) * / ".printf(this.cur_line, this.node.line_start,this.node.line_end)+ str + "\n";
339 this.ret += str + "\n";
340 this.cur_line += str.split("\n").length;
343 public string mungeChildNew(string pad , Node cnode )
345 var x = new NodeToJs(cnode, this.doubleStringProps, pad, this);
352 * loop through items[] array see if any of the children have '* prop'
353 * -- which means they are a property of this node.
354 * -- ADD TO : this.opt_props_array
358 public void checkChildren ()
362 // look throught he chilren == looking for * prop.. -- fixme might not work..
365 if (!this.node.hasChildren()) {
370 for (var ii =0; ii< this.node.items.size; ii++) {
371 var pl = this.node.items.get(ii);
372 if (!pl.props.has_key("* prop")) {
377 //print(JSON.stringify(pl,null,4));
379 //var prop = pl['*prop'] + '';
380 //delete pl['*prop'];
381 var prop = pl.get("* prop");
382 //print("got prop "+ prop + "\n");
385 if (! Regex.match_simple("\\[\\]$", prop)) {
386 // it's a standard prop..
388 // munge property..??
390 this.out_nodeprops.set(prop, pl);
398 var sprop = prop.replace("[]", "");
399 //print("sprop is : " + sprop + "\n");
401 // it's an array type..
403 if (!this.out_props_array.has_key(sprop)) {
404 this.out_props_array.set(sprop, new Gee.ArrayList<Node>());
408 this.out_props_array.get(sprop).add( pl);
409 //this.ar_props.set(sprop, nstr);
416 * Standardize this crap...
418 * standard properties (use to set)
419 * If they are long values show the dialog..
422 * bool is_xxx :: can show a pulldown.. (true/false)
424 * $ string html = string with value interpolated eg. baseURL + ".."
425 * Clutter.ActorAlign x_align (typed) -- shows pulldowns if type is ENUM?
426 * $ untypedvalue = javascript untyped value...
427 * _ string html ... = translatable..
430 * object properties (not part of the GOjbect being wrapped?
431 * # Gee.ArrayList<Xcls_fileitem> fileitems
436 * methods -- always text editor..
444 * * init -- big string?
446 * event handlers (listeners)
451 * +XXXX -- indicates it's a instance property / not glob...
452 * *XXXX -- skip writing glob property (used as classes that can be created...)
456 public void readProps()
462 func_regex = new Regex("^\\s+|\\s+$");
463 } catch (RegexError e) {
464 print("failed to build regex");
467 // sort the key's so they always get rendered in the same order..
469 var keys = new Gee.ArrayList<string>();
470 var piter = this.node.props.map_iterator();
471 while (piter.next() ) {
475 this.node.normalize_key(piter.get_key(), out k, out kflag, out ktype);
480 keys.sort(( a, b) => {
481 return ((string)a).collate((string)b);
482 //if (a == b) return 0;
483 //return a < b ? -1 : 1;
486 var has_cms = this.node.has("cms-id");
488 for (var i = 0; i< keys.size; i++) {
489 var key = this.node.get_key(keys.get(i));
490 //("ADD KEY %s\n", key);
495 this.node.normalize_key(key, out k, out kflag, out ktype);
498 var v = this.node.get(key);
501 //if (this.skip.contains(k) ) {
504 if ( Regex.match_simple("\\[\\]$", k)) {
505 // array .. not supported... here?
511 // skip builder stuff. prefixed with '.' .. just like unix fs..
512 if (kflag == ".") { // |. or . -- do not output..
516 // ignore '* prop'; ???
520 // handle cms-id // html
521 if (has_cms && k == "cms-id") {
522 continue; // ignore it...
524 // html must not be a dynamic property...
525 // note - we do not translate this either...
526 if (has_cms && k == "html" && kflag != "$") {
529 this.out_props.set("html", "Pman.Cms.content(" +
530 this.node.quoteString(this.renderer.name + "::" + this.node.get("cms-id")) +
532 this.node.quoteString(v) +
541 if (Lang.isKeyword(leftv) || Lang.isBuiltin(leftv)) {
542 left = "'" + leftv + "'";
543 } else if (Regex.match_simple("[^A-Za-z_]+",leftv)) { // not plain a-z... - quoted.
544 var val = this.node.quoteString(leftv);
546 left = "'" + val.substring(1, val.length-2).replace("'", "\\'") + "'";
552 // next.. is it a function.. or a raw string..
560 // ??? any others that are raw output..
562 // does not hapepnd with arrays..
563 if (v.length < 1) { //if (typeof(el) == 'string' && !obj[i].length) { //skip empty.
570 str = func_regex.replace(v,v.length, 0, "");
572 print("regex failed");
578 var lines = str.split("\n");
580 if (lines.length > 0) {
581 nstr = string.joinv("\n" + this.pad, lines);
582 //nstr = string.joinv("\n", lines);
584 this.out_props.set(left, nstr);
590 //print("==> " + str + "\n");
591 //this.els.add(left + " : "+ nstr);
602 ktype.down() == "boolean"
604 ktype.down() == "bool"
606 ktype.down() == "number"
608 ktype.down() == "int"
609 ) { // boolean or number...?
610 this.out_props.set(left, v.down());
611 //this.els.add(left + " : " + v.down() );
615 // is it a translated string?
621 //if (this.doubleStringProps.size < 1) {
622 // this.els.add(left + this.node.quoteString(v));
626 if ((this.doubleStringProps.index_of(k) > -1) ||
627 (ktype.down() == "string" && k[0] == '_')
630 // then use the translated version...
633 (v.split("\n").length > 1 ?
634 ("\n" + this.pad + string.joinv(this.pad + "\n", v.split("\n")).replace("*/", "* - /") + "\n" + this.pad + "*/ ") :
635 (v.replace("*/", "* - /") + " */")
638 //this.els.add(left + " : _this._strings['" +
639 // GLib.Checksum.compute_for_string (ChecksumType.MD5, v) +
642 this.out_props.set(left, "_this._strings['" +
643 GLib.Checksum.compute_for_string (ChecksumType.MD5, v.strip()) +
648 // otherwise it needs to be encapsulated.. as single quotes..
650 var vv = this.node.quoteString(v);
651 // single quote.. v.substring(1, v.length-1).replace("'", "\\'") + "'";
652 //this.els.add(left + " : " + "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
653 this.out_props.set(left, "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
661 public void readListeners()
664 if (this.node.listeners.size < 1) {
667 // munge the listeners.
668 //print("ADDING listeners?");
673 var keys = new Gee.ArrayList<string>();
674 var piter = this.node.listeners.map_iterator();
675 while (piter.next() ) {
677 keys.add(piter.get_key());
679 keys.sort(( a, b) => {
680 return ((string)a).collate((string)b);
681 //if (a == b) return 0;
682 //return a < b ? -1 : 1;
686 for (var i = 0; i< keys.size; i++) {
687 var key = keys.get(i);
688 var val = this.node.listeners.get(key);
692 var str = val.strip();
693 var lines = str.split("\n");
694 if (lines.length > 0) {
695 //str = string.joinv("\n" + this.pad + " ", lines);
696 str = string.joinv("\n" + this.pad + indent_str + indent_str , lines);
699 this.out_listeners.set(key.replace("|", "") ,str);
708 public void iterChildren()
712 // finally munge the children...
713 if (this.node.items.size < 1) {
716 var itms = "items : [\n";
718 for(var i = 0; i < this.node.items.size;i++) {
719 var ele = this.node.items.get(i);
720 if (ele.props.has_key("* prop")) {
724 this.out_children.add(ele);
727 itms += "\n"+ this.pad + "]" + "\n";
728 //this.els.add(itms);
731 // finally output listeners...
733 public void xIncludeToString()