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, ',');
195 if (this.out_listeners.size > 0 ) {
197 this.addLine(this.pad + "listeners : {", 0);
198 iter = this.orderedListenerKeys().list_iterator();
203 var v = this.out_listeners.get(k);
204 this.node.setLine(this.cur_line, "l",k); //listener
205 this.addLine(this.pad + indent_str + k + " : " + v , ',');
210 this.addLine(this.pad + "}" ,',');
214 //------- at this point it is the end of the code relating directly to the object..
216 this.node.line_end = this.cur_line;
222 var niter = this.out_nodeprops.map_iterator();
224 while(niter.next()) {
225 var addstr = this.mungeChildNew(this.pad + indent_str, niter.get_value());
226 this.node.setLine(this.cur_line, "p",niter.get_key());
227 this.addLine(this.pad + niter.get_key() + " : " + addstr, ',');
228 if (iter.get_key() == "xns") {
229 this.addLine(this.pad + "'|xns' : '" + addstr + "'", ',');
236 var piter = this.out_props_array.map_iterator();
238 while(piter.next()) {
239 this.node.setLine(this.cur_line, "p",niter.get_key());
240 this.addLine(this.pad + piter.get_key() + " : [", 0);
242 var pliter = piter.get_value().list_iterator();
243 while (pliter.next()) {
244 var addstr = this.mungeChildNew(this.pad + indent_str + indent_str, pliter.get());
245 this.addLine(this.pad + indent_str + addstr, ',');
248 this.addLine(this.pad + "]" , ',');
252 if (this.out_children.size > 0) {
253 this.addLine(this.pad + "items : [" , 0);
254 var cniter = this.out_children.list_iterator();
255 while (cniter.next()) {
256 suffix = cniter.has_next() ? "," : "";
257 var addstr = this.mungeChildNew(this.pad + indent_str + indent_str, cniter.get());
258 this.addLine(this.pad + indent_str + addstr, ',');
262 this.addLine(this.pad + "]",',');
266 if (this.node.props.has_key("* xinclude")) {
267 this.addLine(spad + "})",0);
270 this.addLine( spad + "}", 0);
273 this.node.sortLines();
280 * if we end with a ','
284 char last_line_end = '!';
287 * add a line - note we will end up with an extra line break
288 * at beginning of nodes doing this..
290 * @param str = text to add..
291 * @param line_end = 0 (just add a line break)
292 * line_end = ',' and ","
295 public void addLine(string str, char line_end)
297 if (this.last_line_end != '!') {
298 this.ret += (this.last_line_end == 0 ? "" : this.last_line_end.to_string()) + "\n";
300 this.last_line_end = line_end;
301 this.cur_line += str.split("\n").length;
305 //this.ret += "/*%d(%d-%d)*/ ".printf(this.cur_line -1, this.node.line_start,this.node.line_end) + str + "\n";
309 public void closeLine() // send this before '}' or ']' to block output of ',' ...
311 this.last_line_end = 0;
314 /* public void addMultiLine(str= "")
317 //this.ret += "/ * %d(%d-%d) * / ".printf(this.cur_line, this.node.line_start,this.node.line_end)+ str + "\n";
318 this.ret += str + "\n";
319 this.cur_line += str.split("\n").length;
322 public string mungeChildNew(string pad , Node cnode )
324 var x = new NodeToJs(cnode, this.doubleStringProps, pad, this);
331 * loop through items[] array see if any of the children have '* prop'
332 * -- which means they are a property of this node.
333 * -- ADD TO : this.opt_props_array
337 public void checkChildren ()
341 // look throught he chilren == looking for * prop.. -- fixme might not work..
344 if (!this.node.hasChildren()) {
349 for (var ii =0; ii< this.node.items.size; ii++) {
350 var pl = this.node.items.get(ii);
351 if (!pl.props.has_key("* prop")) {
356 //print(JSON.stringify(pl,null,4));
358 //var prop = pl['*prop'] + '';
359 //delete pl['*prop'];
360 var prop = pl.get("* prop");
361 //print("got prop "+ prop + "\n");
364 if (! Regex.match_simple("\\[\\]$", prop)) {
365 // it's a standard prop..
367 // munge property..??
369 this.out_nodeprops.set(prop, pl);
377 var sprop = prop.replace("[]", "");
378 //print("sprop is : " + sprop + "\n");
380 // it's an array type..
382 if (!this.out_props_array.has_key(sprop)) {
383 this.out_props_array.set(sprop, new Gee.ArrayList<Node>());
387 this.out_props_array.get(sprop).add( pl);
388 //this.ar_props.set(sprop, nstr);
395 * Standardize this crap...
397 * standard properties (use to set)
398 * If they are long values show the dialog..
401 * bool is_xxx :: can show a pulldown.. (true/false)
403 * $ string html = string with value interpolated eg. baseURL + ".."
404 * Clutter.ActorAlign x_align (typed) -- shows pulldowns if type is ENUM?
405 * $ untypedvalue = javascript untyped value...
406 * _ string html ... = translatable..
409 * object properties (not part of the GOjbect being wrapped?
410 * # Gee.ArrayList<Xcls_fileitem> fileitems
415 * methods -- always text editor..
423 * * init -- big string?
425 * event handlers (listeners)
430 * +XXXX -- indicates it's a instance property / not glob...
431 * *XXXX -- skip writing glob property (used as classes that can be created...)
435 public void readProps()
441 func_regex = new Regex("^\\s+|\\s+$");
442 } catch (RegexError e) {
443 print("failed to build regex");
446 // sort the key's so they always get rendered in the same order..
448 var keys = new Gee.ArrayList<string>();
449 var piter = this.node.props.map_iterator();
450 while (piter.next() ) {
454 this.node.normalize_key(piter.get_key(), out k, out kflag, out ktype);
459 keys.sort(( a, b) => {
460 return ((string)a).collate((string)b);
461 //if (a == b) return 0;
462 //return a < b ? -1 : 1;
465 var has_cms = this.node.has("cms-id");
467 for (var i = 0; i< keys.size; i++) {
468 var key = this.node.get_key(keys.get(i));
469 //("ADD KEY %s\n", key);
474 this.node.normalize_key(key, out k, out kflag, out ktype);
477 var v = this.node.get(key);
480 //if (this.skip.contains(k) ) {
483 if ( Regex.match_simple("\\[\\]$", k)) {
484 // array .. not supported... here?
490 // skip builder stuff. prefixed with '.' .. just like unix fs..
491 if (kflag == ".") { // |. or . -- do not output..
495 // ignore '* prop'; ???
499 // handle cms-id // html
500 if (has_cms && k == "cms-id") {
501 continue; // ignore it...
503 // html must not be a dynamic property...
504 // note - we do not translate this either...
505 if (has_cms && k == "html" && kflag != "$") {
508 this.out_props.set("html", "Pman.Cms.content(" +
509 this.node.quoteString(this.renderer.name + "::" + this.node.get("cms-id")) +
511 this.node.quoteString(v) +
520 if (Lang.isKeyword(leftv) || Lang.isBuiltin(leftv)) {
521 left = "'" + leftv + "'";
522 } else if (Regex.match_simple("[^A-Za-z_]+",leftv)) { // not plain a-z... - quoted.
523 var val = this.node.quoteString(leftv);
525 left = "'" + val.substring(1, val.length-2).replace("'", "\\'") + "'";
531 // next.. is it a function.. or a raw string..
539 // ??? any others that are raw output..
541 // does not hapepnd with arrays..
542 if (v.length < 1) { //if (typeof(el) == 'string' && !obj[i].length) { //skip empty.
549 str = func_regex.replace(v,v.length, 0, "");
551 print("regex failed");
557 var lines = str.split("\n");
559 if (lines.length > 0) {
560 nstr = string.joinv("\n" + this.pad, lines);
561 //nstr = string.joinv("\n", lines);
563 this.out_props.set(left, nstr);
569 //print("==> " + str + "\n");
570 //this.els.add(left + " : "+ nstr);
581 ktype.down() == "boolean"
583 ktype.down() == "bool"
585 ktype.down() == "number"
587 ktype.down() == "int"
588 ) { // boolean or number...?
589 this.out_props.set(left, v.down());
590 //this.els.add(left + " : " + v.down() );
594 // is it a translated string?
600 //if (this.doubleStringProps.size < 1) {
601 // this.els.add(left + this.node.quoteString(v));
605 if ((this.doubleStringProps.index_of(k) > -1) ||
606 (ktype.down() == "string" && k[0] == '_')
609 // then use the translated version...
612 (v.split("\n").length > 1 ?
613 ("\n" + this.pad + string.joinv(this.pad + "\n", v.split("\n")).replace("*/", "* - /") + "\n" + this.pad + "*/ ") :
614 (v.replace("*/", "* - /") + " */")
617 //this.els.add(left + " : _this._strings['" +
618 // GLib.Checksum.compute_for_string (ChecksumType.MD5, v) +
621 this.out_props.set(left, "_this._strings['" +
622 GLib.Checksum.compute_for_string (ChecksumType.MD5, v.strip()) +
627 // otherwise it needs to be encapsulated.. as single quotes..
629 var vv = this.node.quoteString(v);
630 // single quote.. v.substring(1, v.length-1).replace("'", "\\'") + "'";
631 //this.els.add(left + " : " + "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
632 this.out_props.set(left, "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
640 public void readListeners()
643 if (this.node.listeners.size < 1) {
646 // munge the listeners.
647 //print("ADDING listeners?");
652 var keys = new Gee.ArrayList<string>();
653 var piter = this.node.listeners.map_iterator();
654 while (piter.next() ) {
656 keys.add(piter.get_key());
658 keys.sort(( a, b) => {
659 return ((string)a).collate((string)b);
660 //if (a == b) return 0;
661 //return a < b ? -1 : 1;
665 for (var i = 0; i< keys.size; i++) {
666 var key = keys.get(i);
667 var val = this.node.listeners.get(key);
671 var str = val.strip();
672 var lines = str.split("\n");
673 if (lines.length > 0) {
674 //str = string.joinv("\n" + this.pad + " ", lines);
675 str = string.joinv("\n" + this.pad + indent_str + indent_str , lines);
678 this.out_listeners.set(key.replace("|", "") ,str);
687 public void iterChildren()
691 // finally munge the children...
692 if (this.node.items.size < 1) {
695 var itms = "items : [\n";
697 for(var i = 0; i < this.node.items.size;i++) {
698 var ele = this.node.items.get(i);
699 if (ele.props.has_key("* prop")) {
703 this.out_children.add(ele);
706 itms += "\n"+ this.pad + "]" + "\n";
707 //this.els.add(itms);
710 // finally output listeners...
712 public void xIncludeToString()