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, ',');
233 var piter = this.out_props_array.map_iterator();
235 while(piter.next()) {
236 this.node.setLine(this.cur_line, "p",niter.get_key());
237 this.addLine(this.pad + piter.get_key() + " : [", 0);
239 var pliter = piter.get_value().list_iterator();
240 while (pliter.next()) {
241 var addstr = this.mungeChildNew(this.pad + indent_str + indent_str, pliter.get());
242 this.addLine(this.pad + indent_str + addstr, ',');
245 this.addLine(this.pad + "]" , ',');
249 if (this.out_children.size > 0) {
250 this.addLine(this.pad + "items : [" , 0);
251 var cniter = this.out_children.list_iterator();
252 while (cniter.next()) {
253 suffix = cniter.has_next() ? "," : "";
254 var addstr = this.mungeChildNew(this.pad + indent_str + indent_str, cniter.get());
255 this.addLine(this.pad + indent_str + addstr, ',');
259 this.addLine(this.pad + "]",',');
263 if (this.node.props.has_key("* xinclude")) {
264 this.addLine(spad + "})",0);
267 this.addLine( spad + "}", 0);
270 this.node.sortLines();
277 * if we end with a ','
281 char last_line_end = '!';
284 * add a line - note we will end up with an extra line break
285 * at beginning of nodes doing this..
287 * @param str = text to add..
288 * @param line_end = 0 (just add a line break)
289 * line_end = ',' and ","
292 public void addLine(string str, char line_end)
294 if (this.last_line_end != '!') {
295 this.ret += (this.last_line_end == 0 ? "" : this.last_line_end.to_string()) + "\n";
297 this.last_line_end = line_end;
298 this.cur_line += str.split("\n").length;
302 //this.ret += "/*%d(%d-%d)*/ ".printf(this.cur_line -1, this.node.line_start,this.node.line_end) + str + "\n";
306 public void closeLine() // send this before '}' or ']' to block output of ',' ...
308 this.last_line_end = 0;
311 /* public void addMultiLine(str= "")
314 //this.ret += "/ * %d(%d-%d) * / ".printf(this.cur_line, this.node.line_start,this.node.line_end)+ str + "\n";
315 this.ret += str + "\n";
316 this.cur_line += str.split("\n").length;
319 public string mungeChildNew(string pad , Node cnode )
321 var x = new NodeToJs(cnode, this.doubleStringProps, pad, this);
328 * loop through items[] array see if any of the children have '* prop'
329 * -- which means they are a property of this node.
330 * -- ADD TO : this.opt_props_array
334 public void checkChildren ()
338 // look throught he chilren == looking for * prop.. -- fixme might not work..
341 if (!this.node.hasChildren()) {
346 for (var ii =0; ii< this.node.items.size; ii++) {
347 var pl = this.node.items.get(ii);
348 if (!pl.props.has_key("* prop")) {
353 //print(JSON.stringify(pl,null,4));
355 //var prop = pl['*prop'] + '';
356 //delete pl['*prop'];
357 var prop = pl.get("* prop");
358 //print("got prop "+ prop + "\n");
361 if (! Regex.match_simple("\\[\\]$", prop)) {
362 // it's a standard prop..
364 // munge property..??
366 this.out_nodeprops.set(prop, pl);
374 var sprop = prop.replace("[]", "");
375 //print("sprop is : " + sprop + "\n");
377 // it's an array type..
379 if (!this.out_props_array.has_key(sprop)) {
380 this.out_props_array.set(sprop, new Gee.ArrayList<Node>());
384 this.out_props_array.get(sprop).add( pl);
385 //this.ar_props.set(sprop, nstr);
392 * Standardize this crap...
394 * standard properties (use to set)
395 * If they are long values show the dialog..
398 * bool is_xxx :: can show a pulldown.. (true/false)
400 * $ string html = string with value interpolated eg. baseURL + ".."
401 * Clutter.ActorAlign x_align (typed) -- shows pulldowns if type is ENUM?
402 * $ untypedvalue = javascript untyped value...
403 * _ string html ... = translatable..
406 * object properties (not part of the GOjbect being wrapped?
407 * # Gee.ArrayList<Xcls_fileitem> fileitems
412 * methods -- always text editor..
420 * * init -- big string?
422 * event handlers (listeners)
427 * +XXXX -- indicates it's a instance property / not glob...
428 * *XXXX -- skip writing glob property (used as classes that can be created...)
432 public void readProps()
438 func_regex = new Regex("^\\s+|\\s+$");
439 } catch (RegexError e) {
440 print("failed to build regex");
443 // sort the key's so they always get rendered in the same order..
445 var keys = new Gee.ArrayList<string>();
446 var piter = this.node.props.map_iterator();
447 while (piter.next() ) {
451 this.node.normalize_key(piter.get_key(), out k, out kflag, out ktype);
456 keys.sort(( a, b) => {
457 return ((string)a).collate((string)b);
458 //if (a == b) return 0;
459 //return a < b ? -1 : 1;
462 var has_cms = this.node.has("cms-id");
464 for (var i = 0; i< keys.size; i++) {
465 var key = this.node.get_key(keys.get(i));
466 //("ADD KEY %s\n", key);
471 this.node.normalize_key(key, out k, out kflag, out ktype);
474 var v = this.node.get(key);
477 //if (this.skip.contains(k) ) {
480 if ( Regex.match_simple("\\[\\]$", k)) {
481 // array .. not supported... here?
487 // skip builder stuff. prefixed with '.' .. just like unix fs..
488 if (kflag == ".") { // |. or . -- do not output..
492 // ignore '* prop'; ???
496 // handle cms-id // html
497 if (has_cms && k == "cms-id") {
498 continue; // ignore it...
500 // html must not be a dynamic property...
501 // note - we do not translate this either...
502 if (has_cms && k == "html" && kflag != "$") {
505 this.out_props.set("html", "Pman.Cms.content(" +
506 this.node.quoteString(this.renderer.name + "::" + this.node.get("cms-id")) +
508 this.node.quoteString(v) +
517 if (Lang.isKeyword(leftv) || Lang.isBuiltin(leftv)) {
518 left = "'" + leftv + "'";
519 } else if (Regex.match_simple("[^A-Za-z_]+",leftv)) { // not plain a-z... - quoted.
520 var val = this.node.quoteString(leftv);
522 left = "'" + val.substring(1, val.length-2).replace("'", "\\'") + "'";
528 // next.. is it a function.. or a raw string..
536 // ??? any others that are raw output..
538 // does not hapepnd with arrays..
539 if (v.length < 1) { //if (typeof(el) == 'string' && !obj[i].length) { //skip empty.
546 str = func_regex.replace(v,v.length, 0, "");
548 print("regex failed");
554 var lines = str.split("\n");
556 if (lines.length > 0) {
557 nstr = string.joinv("\n" + this.pad, lines);
558 //nstr = string.joinv("\n", lines);
560 this.out_props.set(left, nstr);
566 //print("==> " + str + "\n");
567 //this.els.add(left + " : "+ nstr);
578 ktype.down() == "boolean"
580 ktype.down() == "bool"
582 ktype.down() == "number"
584 ktype.down() == "int"
585 ) { // boolean or number...?
586 this.out_props.set(left, v.down());
587 //this.els.add(left + " : " + v.down() );
591 // is it a translated string?
597 //if (this.doubleStringProps.size < 1) {
598 // this.els.add(left + this.node.quoteString(v));
602 if ((this.doubleStringProps.index_of(k) > -1) ||
603 (ktype.down() == "string" && k[0] == '_')
606 // then use the translated version...
609 (v.split("\n").length > 1 ?
610 ("\n" + this.pad + string.joinv(this.pad + "\n", v.split("\n")).replace("*/", "* - /") + "\n" + this.pad + "*/ ") :
611 (v.replace("*/", "* - /") + " */")
614 //this.els.add(left + " : _this._strings['" +
615 // GLib.Checksum.compute_for_string (ChecksumType.MD5, v) +
618 this.out_props.set(left, "_this._strings['" +
619 GLib.Checksum.compute_for_string (ChecksumType.MD5, v.strip()) +
624 // otherwise it needs to be encapsulated.. as single quotes..
626 var vv = this.node.quoteString(v);
627 // single quote.. v.substring(1, v.length-1).replace("'", "\\'") + "'";
628 //this.els.add(left + " : " + "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
629 this.out_props.set(left, "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
637 public void readListeners()
640 if (this.node.listeners.size < 1) {
643 // munge the listeners.
644 //print("ADDING listeners?");
649 var keys = new Gee.ArrayList<string>();
650 var piter = this.node.listeners.map_iterator();
651 while (piter.next() ) {
653 keys.add(piter.get_key());
655 keys.sort(( a, b) => {
656 return ((string)a).collate((string)b);
657 //if (a == b) return 0;
658 //return a < b ? -1 : 1;
662 for (var i = 0; i< keys.size; i++) {
663 var key = keys.get(i);
664 var val = this.node.listeners.get(key);
668 var str = val.strip();
669 var lines = str.split("\n");
670 if (lines.length > 0) {
671 //str = string.joinv("\n" + this.pad + " ", lines);
672 str = string.joinv("\n" + this.pad + indent_str + indent_str , lines);
675 this.out_listeners.set(key.replace("|", "") ,str);
684 public void iterChildren()
688 // finally munge the children...
689 if (this.node.items.size < 1) {
692 var itms = "items : [\n";
694 for(var i = 0; i < this.node.items.size;i++) {
695 var ele = this.node.items.get(i);
696 if (ele.props.has_key("* prop")) {
700 this.out_children.add(ele);
703 itms += "\n"+ this.pad + "]" + "\n";
704 //this.els.add(itms);
707 // finally output listeners...
709 public void xIncludeToString()