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...
181 var iter = this.orderedPropKeys().list_iterator();
186 var v = this.out_props.get(k);
187 this.node.setLine(this.cur_line, "p",k); //listener
188 this.addLine(this.pad + k + " : " + v + suffix, ',');
193 if (this.out_listeners.size > 0 ) {
195 this.addLine(this.pad + "listeners : {", 0);
196 iter = this.orderedListenerKeys().list_iterator();
201 var v = this.out_listeners.get(k);
202 this.node.setLine(this.cur_line, "l",k); //listener
203 this.addLine(this.pad + indent_str + k + " : " + v , ',');
208 this.addLine(this.pad + "}" ,',');
212 //------- at this point it is the end of the code relating directly to the object..
214 this.node.line_end = this.cur_line;
220 var niter = this.out_nodeprops.map_iterator();
222 while(niter.next()) {
223 var addstr = this.mungeChildNew(this.pad + indent_str, niter.get_value());
224 this.node.setLine(this.cur_line, "p",niter.get_key());
225 this.addLine(this.pad + niter.get_key() + " : " + addstr, ',');
231 var piter = this.out_props_array.map_iterator();
233 while(piter.next()) {
234 this.node.setLine(this.cur_line, "p",niter.get_key());
235 this.addLine(this.pad + piter.get_key() + " : [", 0);
237 var pliter = piter.get_value().list_iterator();
238 while (pliter.next()) {
239 var addstr = this.mungeChildNew(this.pad + indent_str + indent_str, pliter.get());
240 this.addLine(this.pad + indent_str + addstr, ',');
243 this.addLine(this.pad + "]" , ',');
247 if (this.out_children.size > 0) {
248 this.addLine(this.pad + "items : [" , 0);
249 var cniter = this.out_children.list_iterator();
250 while (cniter.next()) {
251 suffix = cniter.has_next() ? "," : "";
252 var addstr = this.mungeChildNew(this.pad + indent_str + indent_str, cniter.get());
253 this.addLine(this.pad + indent_str + addstr, ',');
257 this.addLine(this.pad + "]",',');
261 if (this.node.props.has_key("* xinclude")) {
262 this.addLine(spad + "})",0);
265 this.addLine( spad + "}", 0);
268 this.node.sortLines();
275 * if we end with a ','
279 char last_line_end = 0;
282 * add a line - note we will end up with an extra line break
283 * at beginning of nodes doing this..
285 * @param str = text to add..
286 * @param line_end = 0 (just add a line break)
287 * line_end = ',' and ","
290 public void addLine(string str, char line_end)
292 this.ret += (this.last_line_end == 0 ? "" : this.last_line_end.to_string()) + "\n";
293 this.last_line_end = line_end;
294 this.cur_line += str.split("\n").length;
298 //this.ret += "/*%d(%d-%d)*/ ".printf(this.cur_line -1, this.node.line_start,this.node.line_end) + str + "\n";
302 public void closeLine() // send this before '}' or ']' to block output of ',' ...
304 this.last_line_end = 0;
307 /* public void addMultiLine(str= "")
310 //this.ret += "/ * %d(%d-%d) * / ".printf(this.cur_line, this.node.line_start,this.node.line_end)+ str + "\n";
311 this.ret += str + "\n";
312 this.cur_line += str.split("\n").length;
315 public string mungeChildNew(string pad , Node cnode )
317 var x = new NodeToJs(cnode, this.doubleStringProps, pad, this);
324 * loop through items[] array see if any of the children have '* prop'
325 * -- which means they are a property of this node.
326 * -- ADD TO : this.opt_props_array
330 public void checkChildren ()
334 // look throught he chilren == looking for * prop.. -- fixme might not work..
337 if (!this.node.hasChildren()) {
342 for (var ii =0; ii< this.node.items.size; ii++) {
343 var pl = this.node.items.get(ii);
344 if (!pl.props.has_key("* prop")) {
349 //print(JSON.stringify(pl,null,4));
351 //var prop = pl['*prop'] + '';
352 //delete pl['*prop'];
353 var prop = pl.get("* prop");
354 //print("got prop "+ prop + "\n");
357 if (! Regex.match_simple("\\[\\]$", prop)) {
358 // it's a standard prop..
360 // munge property..??
362 this.out_nodeprops.set(prop, pl);
370 var sprop = prop.replace("[]", "");
371 //print("sprop is : " + sprop + "\n");
373 // it's an array type..
375 if (!this.out_props_array.has_key(sprop)) {
376 this.out_props_array.set(sprop, new Gee.ArrayList<Node>());
380 this.out_props_array.get(sprop).add( pl);
381 //this.ar_props.set(sprop, nstr);
388 * Standardize this crap...
390 * standard properties (use to set)
391 * If they are long values show the dialog..
394 * bool is_xxx :: can show a pulldown.. (true/false)
396 * $ string html = string with value interpolated eg. baseURL + ".."
397 * Clutter.ActorAlign x_align (typed) -- shows pulldowns if type is ENUM?
398 * $ untypedvalue = javascript untyped value...
399 * _ string html ... = translatable..
402 * object properties (not part of the GOjbect being wrapped?
403 * # Gee.ArrayList<Xcls_fileitem> fileitems
408 * methods -- always text editor..
416 * * init -- big string?
418 * event handlers (listeners)
423 * +XXXX -- indicates it's a instance property / not glob...
424 * *XXXX -- skip writing glob property (used as classes that can be created...)
428 public void readProps()
434 func_regex = new Regex("^\\s+|\\s+$");
435 } catch (RegexError e) {
436 print("failed to build regex");
439 // sort the key's so they always get rendered in the same order..
441 var keys = new Gee.ArrayList<string>();
442 var piter = this.node.props.map_iterator();
443 while (piter.next() ) {
447 this.node.normalize_key(piter.get_key(), out k, out kflag, out ktype);
452 keys.sort(( a, b) => {
453 return ((string)a).collate((string)b);
454 //if (a == b) return 0;
455 //return a < b ? -1 : 1;
458 var has_cms = this.node.has("cms-id");
460 for (var i = 0; i< keys.size; i++) {
461 var key = this.node.get_key(keys.get(i));
462 //("ADD KEY %s\n", key);
467 this.node.normalize_key(key, out k, out kflag, out ktype);
470 var v = this.node.get(key);
473 //if (this.skip.contains(k) ) {
476 if ( Regex.match_simple("\\[\\]$", k)) {
477 // array .. not supported... here?
483 // skip builder stuff. prefixed with '.' .. just like unix fs..
484 if (kflag == ".") { // |. or . -- do not output..
488 // ignore '* prop'; ???
492 // handle cms-id // html
493 if (has_cms && k == "cms-id") {
494 continue; // ignore it...
496 // html must not be a dynamic property...
497 // note - we do not translate this either...
498 if (has_cms && k == "html" && kflag != "$") {
501 this.out_props.set("html", "Pman.Cms.content(" +
502 this.node.quoteString(this.renderer.name + "::" + this.node.get("cms-id")) +
504 this.node.quoteString(v) +
513 if (Lang.isKeyword(leftv) || Lang.isBuiltin(leftv)) {
514 left = "'" + leftv + "'";
515 } else if (Regex.match_simple("[^A-Za-z_]+",leftv)) { // not plain a-z... - quoted.
516 var val = this.node.quoteString(leftv);
518 left = "'" + val.substring(1, val.length-2).replace("'", "\\'") + "'";
524 // next.. is it a function.. or a raw string..
532 // ??? any others that are raw output..
534 // does not hapepnd with arrays..
535 if (v.length < 1) { //if (typeof(el) == 'string' && !obj[i].length) { //skip empty.
542 str = func_regex.replace(v,v.length, 0, "");
544 print("regex failed");
550 var lines = str.split("\n");
552 if (lines.length > 0) {
553 nstr = string.joinv("\n" + this.pad, lines);
554 //nstr = string.joinv("\n", lines);
556 this.out_props.set(left, nstr);
560 this.out_props.set("'|xns'", "'" + nstr + "'" );
565 //print("==> " + str + "\n");
566 //this.els.add(left + " : "+ nstr);
577 ktype.down() == "boolean"
579 ktype.down() == "bool"
581 ktype.down() == "number"
583 ktype.down() == "int"
584 ) { // boolean or number...?
585 this.out_props.set(left, v.down());
586 //this.els.add(left + " : " + v.down() );
590 // is it a translated string?
596 //if (this.doubleStringProps.size < 1) {
597 // this.els.add(left + this.node.quoteString(v));
601 if ((this.doubleStringProps.index_of(k) > -1) ||
602 (ktype.down() == "string" && k[0] == '_')
605 // then use the translated version...
608 (v.split("\n").length > 1 ?
609 ("\n" + this.pad + string.joinv(this.pad + "\n", v.split("\n")).replace("*/", "* - /") + "\n" + this.pad + "*/ ") :
610 (v.replace("*/", "* - /") + " */")
613 //this.els.add(left + " : _this._strings['" +
614 // GLib.Checksum.compute_for_string (ChecksumType.MD5, v) +
617 this.out_props.set(left, "_this._strings['" +
618 GLib.Checksum.compute_for_string (ChecksumType.MD5, v.strip()) +
623 // otherwise it needs to be encapsulated.. as single quotes..
625 var vv = this.node.quoteString(v);
626 // single quote.. v.substring(1, v.length-1).replace("'", "\\'") + "'";
627 //this.els.add(left + " : " + "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
628 this.out_props.set(left, "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
636 public void readListeners()
639 if (this.node.listeners.size < 1) {
642 // munge the listeners.
643 //print("ADDING listeners?");
648 var keys = new Gee.ArrayList<string>();
649 var piter = this.node.listeners.map_iterator();
650 while (piter.next() ) {
652 keys.add(piter.get_key());
654 keys.sort(( a, b) => {
655 return ((string)a).collate((string)b);
656 //if (a == b) return 0;
657 //return a < b ? -1 : 1;
661 for (var i = 0; i< keys.size; i++) {
662 var key = keys.get(i);
663 var val = this.node.listeners.get(key);
667 var str = val.strip();
668 var lines = str.split("\n");
669 if (lines.length > 0) {
670 //str = string.joinv("\n" + this.pad + " ", lines);
671 str = string.joinv("\n" + this.pad + indent_str + indent_str , lines);
674 this.out_listeners.set(key.replace("|", "") ,str);
683 public void iterChildren()
687 // finally munge the children...
688 if (this.node.items.size < 1) {
691 var itms = "items : [\n";
693 for(var i = 0; i < this.node.items.size;i++) {
694 var ele = this.node.items.get(i);
695 if (ele.props.has_key("* prop")) {
699 this.out_children.add(ele);
702 itms += "\n"+ this.pad + "]" + "\n";
703 //this.els.add(itms);
706 // finally output listeners...
708 public void xIncludeToString()