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 this.addLine(this.pad + niter.get_key() + " : ", 0);
224 var addstr = this.mungeChildNew(this.pad + indent_str, niter.get_value());
225 this.addLine(addstr,',') ;
230 var piter = this.out_props_array.map_iterator();
232 while(piter.next()) {
234 this.addLine(this.pad + piter.get_key() + " : [", 0);
236 var pliter = piter.get_value().list_iterator();
237 while (pliter.next()) {
238 var addstr = this.mungeChildNew(this.pad + indent_str + indent_str, pliter.get());
239 this.addLine(this.pad + indent_str + addstr, ',');
242 this.addLine(this.pad + "]" , ',');
246 if (this.out_children.size > 0) {
247 this.addLine(this.pad + "items : [" , 0);
248 var cniter = this.out_children.list_iterator();
249 while (cniter.next()) {
250 suffix = cniter.has_next() ? "," : "";
251 var addstr = this.mungeChildNew(this.pad + indent_str + indent_str, cniter.get());
252 this.addLine(this.pad + indent_str + addstr, ',');
256 this.addLine(this.pad + "]",',');
260 if (this.node.props.has_key("* xinclude")) {
261 this.addLine(spad + "})",0);
264 this.addLine( spad + "}", 0);
267 this.node.sortLines();
274 * if we end with a ','
278 char last_line_end = 0;
281 * add a line - note we will end up with an extra line break
282 * at beginning of nodes doing this..
284 * @param str = text to add..
285 * @param line_end = 0 (just add a line break)
286 * line_end = ',' and ","
289 public void addLine(string str, char line_end)
291 this.ret += (this.last_line_end == 0 ? "" : this.last_line_end.to_string()) + "\n";
292 this.last_line_end = line_end;
293 this.cur_line += str.split("\n").length;
297 //this.ret += "/*%d(%d-%d)*/ ".printf(this.cur_line -1, this.node.line_start,this.node.line_end) + str + "\n";
301 public void closeLine() // send this before '}' or ']' to block output of ',' ...
303 this.last_line_end = 0;
306 /* public void addMultiLine(str= "")
309 //this.ret += "/ * %d(%d-%d) * / ".printf(this.cur_line, this.node.line_start,this.node.line_end)+ str + "\n";
310 this.ret += str + "\n";
311 this.cur_line += str.split("\n").length;
314 public string mungeChildNew(string pad , Node cnode )
316 var x = new NodeToJs(cnode, this.doubleStringProps, pad, this);
323 * loop through items[] array see if any of the children have '* prop'
324 * -- which means they are a property of this node.
325 * -- ADD TO : this.opt_props_array
329 public void checkChildren ()
333 // look throught he chilren == looking for * prop.. -- fixme might not work..
336 if (!this.node.hasChildren()) {
341 for (var ii =0; ii< this.node.items.size; ii++) {
342 var pl = this.node.items.get(ii);
343 if (!pl.props.has_key("* prop")) {
348 //print(JSON.stringify(pl,null,4));
350 //var prop = pl['*prop'] + '';
351 //delete pl['*prop'];
352 var prop = pl.get("* prop");
353 //print("got prop "+ prop + "\n");
356 if (! Regex.match_simple("\\[\\]$", prop)) {
357 // it's a standard prop..
359 // munge property..??
361 this.out_nodeprops.set(prop, pl);
369 var sprop = prop.replace("[]", "");
370 //print("sprop is : " + sprop + "\n");
372 // it's an array type..
374 if (!this.out_props_array.has_key(sprop)) {
375 this.out_props_array.set(sprop, new Gee.ArrayList<Node>());
379 this.out_props_array.get(sprop).add( pl);
380 //this.ar_props.set(sprop, nstr);
387 * Standardize this crap...
389 * standard properties (use to set)
390 * If they are long values show the dialog..
393 * bool is_xxx :: can show a pulldown.. (true/false)
395 * $ string html = string with value interpolated eg. baseURL + ".."
396 * Clutter.ActorAlign x_align (typed) -- shows pulldowns if type is ENUM?
397 * $ untypedvalue = javascript untyped value...
398 * _ string html ... = translatable..
401 * object properties (not part of the GOjbect being wrapped?
402 * # Gee.ArrayList<Xcls_fileitem> fileitems
407 * methods -- always text editor..
415 * * init -- big string?
417 * event handlers (listeners)
422 * +XXXX -- indicates it's a instance property / not glob...
423 * *XXXX -- skip writing glob property (used as classes that can be created...)
427 public void readProps()
433 func_regex = new Regex("^\\s+|\\s+$");
434 } catch (RegexError e) {
435 print("failed to build regex");
438 // sort the key's so they always get rendered in the same order..
440 var keys = new Gee.ArrayList<string>();
441 var piter = this.node.props.map_iterator();
442 while (piter.next() ) {
446 this.node.normalize_key(piter.get_key(), out k, out kflag, out ktype);
451 keys.sort(( a, b) => {
452 return ((string)a).collate((string)b);
453 //if (a == b) return 0;
454 //return a < b ? -1 : 1;
457 var has_cms = this.node.has("cms-id");
459 for (var i = 0; i< keys.size; i++) {
460 var key = this.node.get_key(keys.get(i));
461 //("ADD KEY %s\n", key);
466 this.node.normalize_key(key, out k, out kflag, out ktype);
469 var v = this.node.get(key);
472 //if (this.skip.contains(k) ) {
475 if ( Regex.match_simple("\\[\\]$", k)) {
476 // array .. not supported... here?
482 // skip builder stuff. prefixed with '.' .. just like unix fs..
483 if (kflag == ".") { // |. or . -- do not output..
487 // ignore '* prop'; ???
491 // handle cms-id // html
492 if (has_cms && k == "cms-id") {
493 continue; // ignore it...
495 // html must not be a dynamic property...
496 // note - we do not translate this either...
497 if (has_cms && k == "html" && kflag != "$") {
500 this.out_props.set("html", "Pman.Cms.content(" +
501 this.node.quoteString(this.renderer.name + "::" + this.node.get("cms-id")) +
503 this.node.quoteString(v) +
512 if (Lang.isKeyword(leftv) || Lang.isBuiltin(leftv)) {
513 left = "'" + leftv + "'";
514 } else if (Regex.match_simple("[^A-Za-z_]+",leftv)) { // not plain a-z... - quoted.
515 var val = this.node.quoteString(leftv);
517 left = "'" + val.substring(1, val.length-2).replace("'", "\\'") + "'";
523 // next.. is it a function.. or a raw string..
531 // ??? any others that are raw output..
533 // does not hapepnd with arrays..
534 if (v.length < 1) { //if (typeof(el) == 'string' && !obj[i].length) { //skip empty.
541 str = func_regex.replace(v,v.length, 0, "");
543 print("regex failed");
549 var lines = str.split("\n");
551 if (lines.length > 0) {
552 nstr = string.joinv("\n" + this.pad, lines);
553 //nstr = string.joinv("\n", lines);
555 this.out_props.set(left, nstr);
559 this.out_props.set("'|xns'", "'" + nstr + "'" );
564 //print("==> " + str + "\n");
565 //this.els.add(left + " : "+ nstr);
576 ktype.down() == "boolean"
578 ktype.down() == "bool"
580 ktype.down() == "number"
582 ktype.down() == "int"
583 ) { // boolean or number...?
584 this.out_props.set(left, v.down());
585 //this.els.add(left + " : " + v.down() );
589 // is it a translated string?
595 //if (this.doubleStringProps.size < 1) {
596 // this.els.add(left + this.node.quoteString(v));
600 if ((this.doubleStringProps.index_of(k) > -1) ||
601 (ktype.down() == "string" && k[0] == '_')
604 // then use the translated version...
607 (v.split("\n").length > 1 ?
608 ("\n" + this.pad + string.joinv(this.pad + "\n", v.split("\n")).replace("*/", "* - /") + "\n" + this.pad + "*/ ") :
609 (v.replace("*/", "* - /") + " */")
612 //this.els.add(left + " : _this._strings['" +
613 // GLib.Checksum.compute_for_string (ChecksumType.MD5, v) +
616 this.out_props.set(left, "_this._strings['" +
617 GLib.Checksum.compute_for_string (ChecksumType.MD5, v.strip()) +
622 // otherwise it needs to be encapsulated.. as single quotes..
624 var vv = this.node.quoteString(v);
625 // single quote.. v.substring(1, v.length-1).replace("'", "\\'") + "'";
626 //this.els.add(left + " : " + "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
627 this.out_props.set(left, "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
635 public void readListeners()
638 if (this.node.listeners.size < 1) {
641 // munge the listeners.
642 //print("ADDING listeners?");
647 var keys = new Gee.ArrayList<string>();
648 var piter = this.node.listeners.map_iterator();
649 while (piter.next() ) {
651 keys.add(piter.get_key());
653 keys.sort(( a, b) => {
654 return ((string)a).collate((string)b);
655 //if (a == b) return 0;
656 //return a < b ? -1 : 1;
660 for (var i = 0; i< keys.size; i++) {
661 var key = keys.get(i);
662 var val = this.node.listeners.get(key);
666 var str = val.strip();
667 var lines = str.split("\n");
668 if (lines.length > 0) {
669 //str = string.joinv("\n" + this.pad + " ", lines);
670 str = string.joinv("\n" + this.pad + indent_str + indent_str , lines);
673 this.out_listeners.set(key.replace("|", "") ,str);
682 public void iterChildren()
686 // finally munge the children...
687 if (this.node.items.size < 1) {
690 var itms = "items : [\n";
692 for(var i = 0; i < this.node.items.size;i++) {
693 var ele = this.node.items.get(i);
694 if (ele.props.has_key("* prop")) {
698 this.out_children.add(ele);
701 itms += "\n"+ this.pad + "]" + "\n";
702 //this.els.add(itms);
705 // finally output listeners...
707 public void xIncludeToString()