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);
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.addLine(this.pad + indent_str + k + " : ", '');
203 this.node.setLine(this.cur_line, "l",k);
204 this.addLine( 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()) {
224 suffix = total_nodes > 0 ? "," : "";
225 var l = this.pad + niter.get_key() + " : " +
226 this.mungeChildNew(this.pad + indent_str, niter.get_value()) + suffix;
227 this.addMultiLine(l);
231 var piter = this.out_props_array.map_iterator();
233 while(piter.next()) {
236 this.addLine(this.pad + piter.get_key() + " : [");
237 var pliter = piter.get_value().list_iterator();
238 while (pliter.next()) {
239 suffix = pliter.has_next() ? "," : "";
240 this.addMultiLine(this.pad + indent_str +
241 this.mungeChildNew(this.pad + indent_str + indent_str, pliter.get()) + suffix);
244 suffix = total_nodes > 0 ? "," : "";
246 this.addLine(this.pad + "]" + suffix);
250 if (this.out_children.size > 0) {
251 this.addLine(this.pad + "items : [" );
252 var cniter = this.out_children.list_iterator();
253 while (cniter.next()) {
254 suffix = cniter.has_next() ? "," : "";
255 this.addMultiLine(this.pad + indent_str +
256 this.mungeChildNew(this.pad + indent_str + indent_str, cniter.get()) + suffix
261 this.addLine(this.pad + "]");
264 if (this.node.props.has_key("* xinclude")) {
265 this.ret += spad + "})";
268 this.ret += spad + "}";
271 this.node.sortLines();
278 * if we end with a ','
282 char last_line_end = 0;
285 * add a line - note we will end up with an extra line break
286 * at beginning of nodes doing this..
288 * @param str = text to add..
289 * @param line_end = 0 (just add a line break)
290 * line_end = ',' and ","
293 public void addLine(string str, char line_end)
295 this.ret += (this.last_line_end == 0 ? "" : this.last_line_end.to_string()) + "\n";
296 this.last_line_end = line_end;
297 this.cur_line += str.split("\n").length;
301 //this.ret += "/*%d(%d-%d)*/ ".printf(this.cur_line -1, this.node.line_start,this.node.line_end) + str + "\n";
305 public void closeLine() // send this before '}' or ']' to block output of ',' ...
307 this.last_line_end = 0;
310 /* public void addMultiLine(str= "")
313 //this.ret += "/ * %d(%d-%d) * / ".printf(this.cur_line, this.node.line_start,this.node.line_end)+ str + "\n";
314 this.ret += str + "\n";
315 this.cur_line += str.split("\n").length;
318 public string mungeChildNew(string pad , Node cnode )
320 var x = new NodeToJs(cnode, this.doubleStringProps, pad, this);
327 * loop through items[] array see if any of the children have '* prop'
328 * -- which means they are a property of this node.
329 * -- ADD TO : this.opt_props_array
333 public void checkChildren ()
337 // look throught he chilren == looking for * prop.. -- fixme might not work..
340 if (!this.node.hasChildren()) {
345 for (var ii =0; ii< this.node.items.size; ii++) {
346 var pl = this.node.items.get(ii);
347 if (!pl.props.has_key("* prop")) {
352 //print(JSON.stringify(pl,null,4));
354 //var prop = pl['*prop'] + '';
355 //delete pl['*prop'];
356 var prop = pl.get("* prop");
357 //print("got prop "+ prop + "\n");
360 if (! Regex.match_simple("\\[\\]$", prop)) {
361 // it's a standard prop..
363 // munge property..??
365 this.out_nodeprops.set(prop, pl);
373 var sprop = prop.replace("[]", "");
374 //print("sprop is : " + sprop + "\n");
376 // it's an array type..
378 if (!this.out_props_array.has_key(sprop)) {
379 this.out_props_array.set(sprop, new Gee.ArrayList<Node>());
383 this.out_props_array.get(sprop).add( pl);
384 //this.ar_props.set(sprop, nstr);
391 * Standardize this crap...
393 * standard properties (use to set)
394 * If they are long values show the dialog..
397 * bool is_xxx :: can show a pulldown.. (true/false)
399 * $ string html = string with value interpolated eg. baseURL + ".."
400 * Clutter.ActorAlign x_align (typed) -- shows pulldowns if type is ENUM?
401 * $ untypedvalue = javascript untyped value...
402 * _ string html ... = translatable..
405 * object properties (not part of the GOjbect being wrapped?
406 * # Gee.ArrayList<Xcls_fileitem> fileitems
411 * methods -- always text editor..
419 * * init -- big string?
421 * event handlers (listeners)
426 * +XXXX -- indicates it's a instance property / not glob...
427 * *XXXX -- skip writing glob property (used as classes that can be created...)
431 public void readProps()
436 if (this.node.props.has_key("$ xns")) {
437 this.out_props.set("'|xns'", "'" + this.node.props.get("$ xns") + "'" );
439 //this.els.add("'|xns' : '" + this.node.props.get("$ xns") + "'");
445 func_regex = new Regex("^\\s+|\\s+$");
446 } catch (RegexError e) {
447 print("failed to build regex");
450 // sort the key's so they always get rendered in the same order..
452 var keys = new Gee.ArrayList<string>();
453 var piter = this.node.props.map_iterator();
454 while (piter.next() ) {
458 this.node.normalize_key(piter.get_key(), out k, out kflag, out ktype);
463 keys.sort(( a, b) => {
464 return ((string)a).collate((string)b);
465 //if (a == b) return 0;
466 //return a < b ? -1 : 1;
469 var has_cms = this.node.has("cms-id");
471 for (var i = 0; i< keys.size; i++) {
472 var key = this.node.get_key(keys.get(i));
473 //("ADD KEY %s\n", key);
478 this.node.normalize_key(key, out k, out kflag, out ktype);
481 var v = this.node.get(key);
484 //if (this.skip.contains(k) ) {
487 if ( Regex.match_simple("\\[\\]$", k)) {
488 // array .. not supported... here?
494 // skip builder stuff. prefixed with '.' .. just like unix fs..
495 if (kflag == ".") { // |. or . -- do not output..
499 // ignore '* prop'; ???
503 // handle cms-id // html
504 if (has_cms && k == "cms-id") {
505 continue; // ignore it...
507 // html must not be a dynamic property...
508 // note - we do not translate this either...
509 if (has_cms && k == "html" && kflag != "$") {
512 this.out_props.set("html", "Pman.Cms.content(" +
513 this.node.quoteString(this.renderer.name + "::" + this.node.get("cms-id")) +
515 this.node.quoteString(v) +
524 if (Lang.isKeyword(leftv) || Lang.isBuiltin(leftv)) {
525 left = "'" + leftv + "'";
526 } else if (Regex.match_simple("[^A-Za-z_]+",leftv)) { // not plain a-z... - quoted.
527 var val = this.node.quoteString(leftv);
529 left = "'" + val.substring(1, val.length-2).replace("'", "\\'") + "'";
535 // next.. is it a function.. or a raw string..
543 // ??? any others that are raw output..
545 // does not hapepnd with arrays..
546 if (v.length < 1) { //if (typeof(el) == 'string' && !obj[i].length) { //skip empty.
553 str = func_regex.replace(v,v.length, 0, "");
555 print("regex failed");
561 var lines = str.split("\n");
563 if (lines.length > 0) {
564 nstr = string.joinv("\n" + this.pad, lines);
565 //nstr = string.joinv("\n", lines);
567 this.out_props.set(left, nstr);
568 //print("==> " + str + "\n");
569 //this.els.add(left + " : "+ nstr);
580 ktype.down() == "boolean"
582 ktype.down() == "bool"
584 ktype.down() == "number"
586 ktype.down() == "int"
587 ) { // boolean or number...?
588 this.out_props.set(left, v.down());
589 //this.els.add(left + " : " + v.down() );
593 // is it a translated string?
599 //if (this.doubleStringProps.size < 1) {
600 // this.els.add(left + this.node.quoteString(v));
604 if ((this.doubleStringProps.index_of(k) > -1) ||
605 (ktype.down() == "string" && k[0] == '_')
608 // then use the translated version...
611 (v.split("\n").length > 1 ?
612 ("\n" + this.pad + string.joinv(this.pad + "\n", v.split("\n")).replace("*/", "* - /") + "\n" + this.pad + "*/ ") :
613 (v.replace("*/", "* - /") + " */")
616 //this.els.add(left + " : _this._strings['" +
617 // GLib.Checksum.compute_for_string (ChecksumType.MD5, v) +
620 this.out_props.set(left, "_this._strings['" +
621 GLib.Checksum.compute_for_string (ChecksumType.MD5, v.strip()) +
626 // otherwise it needs to be encapsulated.. as single quotes..
628 var vv = this.node.quoteString(v);
629 // single quote.. v.substring(1, v.length-1).replace("'", "\\'") + "'";
630 //this.els.add(left + " : " + "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
631 this.out_props.set(left, "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
639 public void readListeners()
642 if (this.node.listeners.size < 1) {
645 // munge the listeners.
646 //print("ADDING listeners?");
651 var keys = new Gee.ArrayList<string>();
652 var piter = this.node.listeners.map_iterator();
653 while (piter.next() ) {
655 keys.add(piter.get_key());
657 keys.sort(( a, b) => {
658 return ((string)a).collate((string)b);
659 //if (a == b) return 0;
660 //return a < b ? -1 : 1;
664 for (var i = 0; i< keys.size; i++) {
665 var key = keys.get(i);
666 var val = this.node.listeners.get(key);
670 var str = val.strip();
671 var lines = str.split("\n");
672 if (lines.length > 0) {
673 //str = string.joinv("\n" + this.pad + " ", lines);
674 str = string.joinv("\n" + this.pad + indent_str + indent_str , lines);
677 this.out_listeners.set(key.replace("|", "") ,str);
686 public void iterChildren()
690 // finally munge the children...
691 if (this.node.items.size < 1) {
694 var itms = "items : [\n";
696 for(var i = 0; i < this.node.items.size;i++) {
697 var ele = this.node.items.get(i);
698 if (ele.props.has_key("* prop")) {
702 this.out_children.add(ele);
705 itms += "\n"+ this.pad + "]" + "\n";
706 //this.els.add(itms);
709 // finally output listeners...
711 public void xIncludeToString()