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...
178 var total_nodes = this.out_props.size +
179 this.out_props_array_plain.size +
180 (this.out_listeners.size > 0 ? 1 : 0) +
181 this.out_nodeprops.size +
182 this.out_props_array.size +
183 (this.out_children.size > 0 ? 1 : 0);
188 var iter = this.orderedPropKeys().list_iterator();
191 suffix = total_nodes > 0 ? "," : "";
193 var v = this.out_props.get(k);
195 this.addLine(this.pad + k + " : " + v + suffix, ',');
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.addLine(this.pad + indent_str + k + " : ", '');
210 this.node.setLine(this.cur_line, ";",k);
211 this.addLine( v,',');
215 this.addLine(this.pad + "}" ,',');
219 //------- at this point it is the end of the code relating directly to the object..
221 this.node.line_end = this.cur_line;
227 var niter = this.out_nodeprops.map_iterator();
229 while(niter.next()) {
231 suffix = total_nodes > 0 ? "," : "";
232 var l = this.pad + niter.get_key() + " : " +
233 this.mungeChildNew(this.pad + indent_str, niter.get_value()) + suffix;
234 this.addMultiLine(l);
238 var piter = this.out_props_array.map_iterator();
240 while(piter.next()) {
243 this.addLine(this.pad + piter.get_key() + " : [");
244 var pliter = piter.get_value().list_iterator();
245 while (pliter.next()) {
246 suffix = pliter.has_next() ? "," : "";
247 this.addMultiLine(this.pad + indent_str +
248 this.mungeChildNew(this.pad + indent_str + indent_str, pliter.get()) + suffix);
251 suffix = total_nodes > 0 ? "," : "";
253 this.addLine(this.pad + "]" + suffix);
257 if (this.out_children.size > 0) {
258 this.addLine(this.pad + "items : [" );
259 var cniter = this.out_children.list_iterator();
260 while (cniter.next()) {
261 suffix = cniter.has_next() ? "," : "";
262 this.addMultiLine(this.pad + indent_str +
263 this.mungeChildNew(this.pad + indent_str + indent_str, cniter.get()) + suffix
268 this.addLine(this.pad + "]");
271 if (this.node.props.has_key("* xinclude")) {
272 this.ret += spad + "})";
275 this.ret += spad + "}";
278 this.node.sortLines();
285 * if we end with a ','
289 char last_line_end = 0;
292 * add a line - note we will end up with an extra line break
293 * at beginning of nodes doing this..
295 * @param str = text to add..
296 * @param line_end = 0 (just add a line break)
297 * line_end = ',' and ","
300 public void addLine(string str, char line_end)
302 this.ret += (this.last_line_end == 0 ? "" : this.last_line_end.toString()) + "\n";
303 this.last_line_end = line_end;
304 this.cur_line += str.split("\n").length;
308 //this.ret += "/*%d(%d-%d)*/ ".printf(this.cur_line -1, this.node.line_start,this.node.line_end) + str + "\n";
312 public void closeLine()
314 this.last_line_end = 0;
317 /* public void addMultiLine(str= "")
320 //this.ret += "/ * %d(%d-%d) * / ".printf(this.cur_line, this.node.line_start,this.node.line_end)+ str + "\n";
321 this.ret += str + "\n";
322 this.cur_line += str.split("\n").length;
325 public string mungeChildNew(string pad , Node cnode )
327 var x = new NodeToJs(cnode, this.doubleStringProps, pad, this);
334 * loop through items[] array see if any of the children have '* prop'
335 * -- which means they are a property of this node.
336 * -- ADD TO : this.opt_props_array
340 public void checkChildren ()
344 // look throught he chilren == looking for * prop.. -- fixme might not work..
347 if (!this.node.hasChildren()) {
352 for (var ii =0; ii< this.node.items.size; ii++) {
353 var pl = this.node.items.get(ii);
354 if (!pl.props.has_key("* prop")) {
359 //print(JSON.stringify(pl,null,4));
361 //var prop = pl['*prop'] + '';
362 //delete pl['*prop'];
363 var prop = pl.get("* prop");
364 //print("got prop "+ prop + "\n");
367 if (! Regex.match_simple("\\[\\]$", prop)) {
368 // it's a standard prop..
370 // munge property..??
372 this.out_nodeprops.set(prop, pl);
380 var sprop = prop.replace("[]", "");
381 //print("sprop is : " + sprop + "\n");
383 // it's an array type..
385 if (!this.out_props_array.has_key(sprop)) {
386 this.out_props_array.set(sprop, new Gee.ArrayList<Node>());
390 this.out_props_array.get(sprop).add( pl);
391 //this.ar_props.set(sprop, nstr);
398 * Standardize this crap...
400 * standard properties (use to set)
401 * If they are long values show the dialog..
404 * bool is_xxx :: can show a pulldown.. (true/false)
406 * $ string html = string with value interpolated eg. baseURL + ".."
407 * Clutter.ActorAlign x_align (typed) -- shows pulldowns if type is ENUM?
408 * $ untypedvalue = javascript untyped value...
409 * _ string html ... = translatable..
412 * object properties (not part of the GOjbect being wrapped?
413 * # Gee.ArrayList<Xcls_fileitem> fileitems
418 * methods -- always text editor..
426 * * init -- big string?
428 * event handlers (listeners)
433 * +XXXX -- indicates it's a instance property / not glob...
434 * *XXXX -- skip writing glob property (used as classes that can be created...)
438 public void readProps()
443 if (this.node.props.has_key("$ xns")) {
444 this.out_props.set("'|xns'", "'" + this.node.props.get("$ xns") + "'" );
446 //this.els.add("'|xns' : '" + this.node.props.get("$ xns") + "'");
452 func_regex = new Regex("^\\s+|\\s+$");
453 } catch (RegexError e) {
454 print("failed to build regex");
457 // sort the key's so they always get rendered in the same order..
459 var keys = new Gee.ArrayList<string>();
460 var piter = this.node.props.map_iterator();
461 while (piter.next() ) {
465 this.node.normalize_key(piter.get_key(), out k, out kflag, out ktype);
470 keys.sort(( a, b) => {
471 return ((string)a).collate((string)b);
472 //if (a == b) return 0;
473 //return a < b ? -1 : 1;
476 var has_cms = this.node.has("cms-id");
478 for (var i = 0; i< keys.size; i++) {
479 var key = this.node.get_key(keys.get(i));
480 //("ADD KEY %s\n", key);
485 this.node.normalize_key(key, out k, out kflag, out ktype);
488 var v = this.node.get(key);
491 //if (this.skip.contains(k) ) {
494 if ( Regex.match_simple("\\[\\]$", k)) {
495 // array .. not supported... here?
501 // skip builder stuff. prefixed with '.' .. just like unix fs..
502 if (kflag == ".") { // |. or . -- do not output..
506 // ignore '* prop'; ???
510 // handle cms-id // html
511 if (has_cms && k == "cms-id") {
512 continue; // ignore it...
514 // html must not be a dynamic property...
515 // note - we do not translate this either...
516 if (has_cms && k == "html" && kflag != "$") {
519 this.out_props.set("html", "Pman.Cms.content(" +
520 this.node.quoteString(this.renderer.name + "::" + this.node.get("cms-id")) +
522 this.node.quoteString(v) +
531 if (Lang.isKeyword(leftv) || Lang.isBuiltin(leftv)) {
532 left = "'" + leftv + "'";
533 } else if (Regex.match_simple("[^A-Za-z_]+",leftv)) { // not plain a-z... - quoted.
534 var val = this.node.quoteString(leftv);
536 left = "'" + val.substring(1, val.length-2).replace("'", "\\'") + "'";
542 // next.. is it a function.. or a raw string..
550 // ??? any others that are raw output..
552 // does not hapepnd with arrays..
553 if (v.length < 1) { //if (typeof(el) == 'string' && !obj[i].length) { //skip empty.
560 str = func_regex.replace(v,v.length, 0, "");
562 print("regex failed");
568 var lines = str.split("\n");
570 if (lines.length > 0) {
571 nstr = string.joinv("\n" + this.pad, lines);
572 //nstr = string.joinv("\n", lines);
574 this.out_props.set(left, nstr);
575 //print("==> " + str + "\n");
576 //this.els.add(left + " : "+ nstr);
587 ktype.down() == "boolean"
589 ktype.down() == "bool"
591 ktype.down() == "number"
593 ktype.down() == "int"
594 ) { // boolean or number...?
595 this.out_props.set(left, v.down());
596 //this.els.add(left + " : " + v.down() );
600 // is it a translated string?
606 //if (this.doubleStringProps.size < 1) {
607 // this.els.add(left + this.node.quoteString(v));
611 if ((this.doubleStringProps.index_of(k) > -1) ||
612 (ktype.down() == "string" && k[0] == '_')
615 // then use the translated version...
618 (v.split("\n").length > 1 ?
619 ("\n" + this.pad + string.joinv(this.pad + "\n", v.split("\n")).replace("*/", "* - /") + "\n" + this.pad + "*/ ") :
620 (v.replace("*/", "* - /") + " */")
623 //this.els.add(left + " : _this._strings['" +
624 // GLib.Checksum.compute_for_string (ChecksumType.MD5, v) +
627 this.out_props.set(left, "_this._strings['" +
628 GLib.Checksum.compute_for_string (ChecksumType.MD5, v.strip()) +
633 // otherwise it needs to be encapsulated.. as single quotes..
635 var vv = this.node.quoteString(v);
636 // single quote.. v.substring(1, v.length-1).replace("'", "\\'") + "'";
637 //this.els.add(left + " : " + "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
638 this.out_props.set(left, "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
646 public void readListeners()
649 if (this.node.listeners.size < 1) {
652 // munge the listeners.
653 //print("ADDING listeners?");
658 var keys = new Gee.ArrayList<string>();
659 var piter = this.node.listeners.map_iterator();
660 while (piter.next() ) {
662 keys.add(piter.get_key());
664 keys.sort(( a, b) => {
665 return ((string)a).collate((string)b);
666 //if (a == b) return 0;
667 //return a < b ? -1 : 1;
671 for (var i = 0; i< keys.size; i++) {
672 var key = keys.get(i);
673 var val = this.node.listeners.get(key);
677 var str = val.strip();
678 var lines = str.split("\n");
679 if (lines.length > 0) {
680 //str = string.joinv("\n" + this.pad + " ", lines);
681 str = string.joinv("\n" + this.pad + indent_str + indent_str , lines);
684 this.out_listeners.set(key.replace("|", "") ,str);
693 public void iterChildren()
697 // finally munge the children...
698 if (this.node.items.size < 1) {
701 var itms = "items : [\n";
703 for(var i = 0; i < this.node.items.size;i++) {
704 var ele = this.node.items.get(i);
705 if (ele.props.has_key("* prop")) {
709 this.out_children.add(ele);
712 itms += "\n"+ this.pad + "]" + "\n";
713 //this.els.add(itms);
716 // finally output listeners...
718 public void xIncludeToString()