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);
188 this.addLine(this.pad + k + " : " + v + suffix, ',');
189 this.node.setLine(this.cur_line, "p","| xns" );
191 this.addLine(this.pad + "'|xns' : '" + v + "'", ',');
194 this.node.setLine(this.cur_line, "e", "");
200 if (this.out_listeners.size > 0 ) {
202 this.addLine(this.pad + "listeners : {", 0);
203 iter = this.orderedListenerKeys().list_iterator();
208 var v = this.out_listeners.get(k);
209 this.node.setLine(this.cur_line, "l",k); //listener
210 this.addLine(this.pad + indent_str + k + " : " + v , ',');
211 this.node.setLine(this.cur_line, "e", "");
215 this.addLine(this.pad + "}" ,',');
219 //------- at this point it is the end of the code relating directly to the object..
223 this.node.line_end = this.cur_line;
227 var niter = this.out_nodeprops.map_iterator();
229 while(niter.next()) {
230 var addstr = this.mungeChildNew(this.pad + indent_str, niter.get_value());
231 this.node.setLine(this.cur_line, "p",niter.get_key());
232 this.addLine(this.pad + niter.get_key() + " : " + addstr, ',');
234 this.node.setLine(this.cur_line, "e", "");
238 var piter = this.out_props_array.map_iterator();
240 while(piter.next()) {
241 this.node.setLine(this.cur_line, "p",niter.get_key());
242 this.addLine(this.pad + piter.get_key() + " : [", 0);
244 var pliter = piter.get_value().list_iterator();
245 while (pliter.next()) {
246 var addstr = this.mungeChildNew(this.pad + indent_str + indent_str, pliter.get());
247 this.addLine(this.pad + indent_str + addstr, ',');
248 this.node.setLine(this.cur_line, "e", "");
251 this.addLine(this.pad + "]" , ',');
255 if (this.out_children.size > 0) {
256 this.addLine(this.pad + "items : [" , 0);
257 var cniter = this.out_children.list_iterator();
258 while (cniter.next()) {
259 suffix = cniter.has_next() ? "," : "";
260 var addstr = this.mungeChildNew(this.pad + indent_str + indent_str, cniter.get());
261 this.addLine(this.pad + indent_str + addstr, ',');
262 this.node.setLine(this.cur_line, "e", "");
266 this.addLine(this.pad + "]",',');
268 this.node.setLine(this.cur_line, "e", "");
270 if (this.node.props.has_key("* xinclude")) {
271 this.addLine(spad + "})",0);
274 this.addLine( spad + "}", 0);
277 this.node.sortLines();
287 * if we end with a ','
291 char last_line_end = '!';
294 * add a line - note we will end up with an extra line break
295 * at beginning of nodes doing this..
297 * @param str = text to add..
298 * @param line_end = 0 (just add a line break)
299 * line_end = ',' and ","
302 public void addLine(string str, char line_end)
304 if (this.last_line_end != '!') {
305 this.ret += (this.last_line_end == 0 ? "" : this.last_line_end.to_string()) + "\n";
307 this.last_line_end = line_end;
308 this.cur_line += str.split("\n").length;
312 //this.ret += "/*%d(%d-%d)*/ ".printf(this.cur_line -1, this.node.line_start,this.node.line_end) + str + "\n";
316 public void closeLine() // send this before '}' or ']' to block output of ',' ...
318 this.last_line_end = 0;
321 /* public void addMultiLine(str= "")
324 //this.ret += "/ * %d(%d-%d) * / ".printf(this.cur_line, this.node.line_start,this.node.line_end)+ str + "\n";
325 this.ret += str + "\n";
326 this.cur_line += str.split("\n").length;
329 public string mungeChildNew(string pad , Node cnode )
331 var x = new NodeToJs(cnode, this.doubleStringProps, pad, this);
338 * loop through items[] array see if any of the children have '* prop'
339 * -- which means they are a property of this node.
340 * -- ADD TO : this.opt_props_array
344 public void checkChildren ()
348 // look throught he chilren == looking for * prop.. -- fixme might not work..
351 if (!this.node.hasChildren()) {
356 for (var ii =0; ii< this.node.items.size; ii++) {
357 var pl = this.node.items.get(ii);
358 if (!pl.props.has_key("* prop")) {
363 //print(JSON.stringify(pl,null,4));
365 //var prop = pl['*prop'] + '';
366 //delete pl['*prop'];
367 var prop = pl.get("* prop");
368 //print("got prop "+ prop + "\n");
371 if (! Regex.match_simple("\\[\\]$", prop)) {
372 // it's a standard prop..
374 // munge property..??
376 this.out_nodeprops.set(prop, pl);
384 var sprop = prop.replace("[]", "");
385 //print("sprop is : " + sprop + "\n");
387 // it's an array type..
389 if (!this.out_props_array.has_key(sprop)) {
390 this.out_props_array.set(sprop, new Gee.ArrayList<Node>());
394 this.out_props_array.get(sprop).add( pl);
395 //this.ar_props.set(sprop, nstr);
402 * Standardize this crap...
404 * standard properties (use to set)
405 * If they are long values show the dialog..
408 * bool is_xxx :: can show a pulldown.. (true/false)
410 * $ string html = string with value interpolated eg. baseURL + ".."
411 * Clutter.ActorAlign x_align (typed) -- shows pulldowns if type is ENUM?
412 * $ untypedvalue = javascript untyped value...
413 * _ string html ... = translatable..
416 * object properties (not part of the GOjbect being wrapped?
417 * # Gee.ArrayList<Xcls_fileitem> fileitems
422 * methods -- always text editor..
430 * * init -- big string?
432 * event handlers (listeners)
437 * +XXXX -- indicates it's a instance property / not glob...
438 * *XXXX -- skip writing glob property (used as classes that can be created...)
442 public void readProps()
448 func_regex = new Regex("^\\s+|\\s+$");
449 } catch (RegexError e) {
450 print("failed to build regex");
453 // sort the key's so they always get rendered in the same order..
455 var keys = new Gee.ArrayList<string>();
456 var piter = this.node.props.map_iterator();
457 while (piter.next() ) {
461 this.node.normalize_key(piter.get_key(), out k, out kflag, out ktype);
466 keys.sort(( a, b) => {
467 return ((string)a).collate((string)b);
468 //if (a == b) return 0;
469 //return a < b ? -1 : 1;
472 var has_cms = this.node.has("cms-id");
474 for (var i = 0; i< keys.size; i++) {
475 var key = this.node.get_key(keys.get(i));
476 //("ADD KEY %s\n", key);
481 this.node.normalize_key(key, out k, out kflag, out ktype);
484 var v = this.node.get(key);
487 //if (this.skip.contains(k) ) {
490 if ( Regex.match_simple("\\[\\]$", k)) {
491 // array .. not supported... here?
497 // skip builder stuff. prefixed with '.' .. just like unix fs..
498 if (kflag == ".") { // |. or . -- do not output..
502 // ignore '* prop'; ???
506 // handle cms-id // html
507 if (has_cms && k == "cms-id") {
508 continue; // ignore it...
510 // html must not be a dynamic property...
511 // note - we do not translate this either...
512 if (has_cms && k == "html" && kflag != "$") {
515 this.out_props.set("html", "Pman.Cms.content(" +
516 this.node.quoteString(this.renderer.name + "::" + this.node.get("cms-id")) +
518 this.node.quoteString(v) +
527 if (Lang.isKeyword(leftv) || Lang.isBuiltin(leftv)) {
528 left = "'" + leftv + "'";
529 } else if (Regex.match_simple("[^A-Za-z_]+",leftv)) { // not plain a-z... - quoted.
530 var val = this.node.quoteString(leftv);
532 left = "'" + val.substring(1, val.length-2).replace("'", "\\'") + "'";
538 // next.. is it a function.. or a raw string..
546 // ??? any others that are raw output..
548 // does not hapepnd with arrays..
549 if (v.length < 1) { //if (typeof(el) == 'string' && !obj[i].length) { //skip empty.
556 str = func_regex.replace(v,v.length, 0, "");
558 print("regex failed");
564 var lines = str.split("\n");
566 if (lines.length > 0) {
567 nstr = string.joinv("\n" + this.pad, lines);
568 //nstr = string.joinv("\n", lines);
570 this.out_props.set(left, nstr);
576 //print("==> " + str + "\n");
577 //this.els.add(left + " : "+ nstr);
588 ktype.down() == "boolean"
590 ktype.down() == "bool"
592 ktype.down() == "number"
594 ktype.down() == "int"
595 ) { // boolean or number...?
596 this.out_props.set(left, v.down());
597 //this.els.add(left + " : " + v.down() );
601 // is it a translated string?
607 //if (this.doubleStringProps.size < 1) {
608 // this.els.add(left + this.node.quoteString(v));
612 if ((this.doubleStringProps.index_of(k) > -1) ||
613 (ktype.down() == "string" && k[0] == '_')
616 // then use the translated version...
619 (v.split("\n").length > 1 ?
620 ("\n" + this.pad + string.joinv(this.pad + "\n", v.split("\n")).replace("*/", "* - /") + "\n" + this.pad + "*/ ") :
621 (v.replace("*/", "* - /") + " */")
624 //this.els.add(left + " : _this._strings['" +
625 // GLib.Checksum.compute_for_string (ChecksumType.MD5, v) +
628 this.out_props.set(left, "_this._strings['" +
629 GLib.Checksum.compute_for_string (ChecksumType.MD5, v.strip()) +
634 // otherwise it needs to be encapsulated.. as single quotes..
636 var vv = this.node.quoteString(v);
637 // single quote.. v.substring(1, v.length-1).replace("'", "\\'") + "'";
638 //this.els.add(left + " : " + "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
639 this.out_props.set(left, "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
647 public void readListeners()
650 if (this.node.listeners.size < 1) {
653 // munge the listeners.
654 //print("ADDING listeners?");
659 var keys = new Gee.ArrayList<string>();
660 var piter = this.node.listeners.map_iterator();
661 while (piter.next() ) {
663 keys.add(piter.get_key());
665 keys.sort(( a, b) => {
666 return ((string)a).collate((string)b);
667 //if (a == b) return 0;
668 //return a < b ? -1 : 1;
672 for (var i = 0; i< keys.size; i++) {
673 var key = keys.get(i);
674 var val = this.node.listeners.get(key);
678 var str = val.strip();
679 var lines = str.split("\n");
680 if (lines.length > 0) {
681 //str = string.joinv("\n" + this.pad + " ", lines);
682 str = string.joinv("\n" + this.pad + indent_str + indent_str , lines);
685 this.out_listeners.set(key.replace("|", "") ,str);
694 public void iterChildren()
698 // finally munge the children...
699 if (this.node.items.size < 1) {
702 var itms = "items : [\n";
704 for(var i = 0; i < this.node.items.size;i++) {
705 var ele = this.node.items.get(i);
706 if (ele.props.has_key("* prop")) {
710 this.out_children.add(ele);
713 itms += "\n"+ this.pad + "]" + "\n";
714 //this.els.add(itms);
717 // finally output listeners...
719 public void xIncludeToString()