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(), {");
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.addMultiLine(this.pad + k + " : " + v + suffix);
200 if (this.out_listeners.size > 0 ) {
202 this.addLine(this.pad + "listeners : {");
203 iter = this.orderedListenerKeys().list_iterator();
205 var sz = this.out_listeners.size;
208 suffix = sz > 0 ? "," : "";
210 var v = this.out_listeners.get(k);
211 this.addMultiLine(this.pad + indent_str + k + " : " + v + suffix);
213 suffix = total_nodes > 0 ? "," : "";
214 this.addLine(this.pad + "}" + suffix);
218 //------- at this point it is the end of the code relating directly to the object..
220 this.node.line_end = this.cur_line;
226 var niter = this.out_nodeprops.map_iterator();
228 while(niter.next()) {
230 suffix = total_nodes > 0 ? "," : "";
231 var l = this.pad + niter.get_key() + " : " +
232 this.mungeChildNew(this.pad + indent_str, niter.get_value()) + suffix;
233 this.addMultiLine(l);
237 var piter = this.out_props_array.map_iterator();
239 while(piter.next()) {
242 this.addLine(this.pad + piter.get_key() + " : [");
243 var pliter = piter.get_value().list_iterator();
244 while (pliter.next()) {
245 suffix = pliter.has_next() ? "," : "";
246 this.addMultiLine(this.pad + indent_str +
247 this.mungeChildNew(this.pad + indent_str + indent_str, pliter.get()) + suffix);
250 suffix = total_nodes > 0 ? "," : "";
252 this.addLine(this.pad + "]" + suffix);
256 if (this.out_children.size > 0) {
257 this.addLine(this.pad + "items : [" );
258 var cniter = this.out_children.list_iterator();
259 while (cniter.next()) {
260 suffix = cniter.has_next() ? "," : "";
261 this.addMultiLine(this.pad + indent_str +
262 this.mungeChildNew(this.pad + indent_str + indent_str, cniter.get()) + suffix
267 this.addLine(this.pad + "]");
270 if (this.node.props.has_key("* xinclude")) {
271 this.ret += spad + "})";
274 this.ret += spad + "}";
277 this.node.sortLines();
284 * if we end with a ','
288 char last_line_end = 0;
291 * add a line - note we will end up with an extra line break
292 * at beginning of nodes doing this..
294 * @param str = text to add..
295 * @param line_end = 0 (just add a line break)
296 * line_end = ',' and ","
299 public void addLine(string str, char line_end)
301 this.ret += (this.line_end == 0 ? "" : this.last_line_end) + "\n";
303 this.ret += str+ "\n";
304 //this.ret += "/*%d(%d-%d)*/ ".printf(this.cur_line -1, this.node.line_start,this.node.line_end) + str + "\n";
309 public void addMultiLine(string str= "")
312 //this.ret += "/*%d(%d-%d)*/ ".printf(this.cur_line, this.node.line_start,this.node.line_end)+ str + "\n";
313 this.ret += str + "\n";
314 this.cur_line += str.split("\n").length;
317 public string mungeChildNew(string pad , Node cnode )
319 var x = new NodeToJs(cnode, this.doubleStringProps, pad, this);
326 * loop through items[] array see if any of the children have '* prop'
327 * -- which means they are a property of this node.
328 * -- ADD TO : this.opt_props_array
332 public void checkChildren ()
336 // look throught he chilren == looking for * prop.. -- fixme might not work..
339 if (!this.node.hasChildren()) {
344 for (var ii =0; ii< this.node.items.size; ii++) {
345 var pl = this.node.items.get(ii);
346 if (!pl.props.has_key("* prop")) {
351 //print(JSON.stringify(pl,null,4));
353 //var prop = pl['*prop'] + '';
354 //delete pl['*prop'];
355 var prop = pl.get("* prop");
356 //print("got prop "+ prop + "\n");
359 if (! Regex.match_simple("\\[\\]$", prop)) {
360 // it's a standard prop..
362 // munge property..??
364 this.out_nodeprops.set(prop, pl);
372 var sprop = prop.replace("[]", "");
373 //print("sprop is : " + sprop + "\n");
375 // it's an array type..
377 if (!this.out_props_array.has_key(sprop)) {
378 this.out_props_array.set(sprop, new Gee.ArrayList<Node>());
382 this.out_props_array.get(sprop).add( pl);
383 //this.ar_props.set(sprop, nstr);
390 * Standardize this crap...
392 * standard properties (use to set)
393 * If they are long values show the dialog..
396 * bool is_xxx :: can show a pulldown.. (true/false)
398 * $ string html = string with value interpolated eg. baseURL + ".."
399 * Clutter.ActorAlign x_align (typed) -- shows pulldowns if type is ENUM?
400 * $ untypedvalue = javascript untyped value...
401 * _ string html ... = translatable..
404 * object properties (not part of the GOjbect being wrapped?
405 * # Gee.ArrayList<Xcls_fileitem> fileitems
410 * methods -- always text editor..
418 * * init -- big string?
420 * event handlers (listeners)
425 * +XXXX -- indicates it's a instance property / not glob...
426 * *XXXX -- skip writing glob property (used as classes that can be created...)
430 public void readProps()
435 if (this.node.props.has_key("$ xns")) {
436 this.out_props.set("'|xns'", "'" + this.node.props.get("$ xns") + "'" );
438 //this.els.add("'|xns' : '" + this.node.props.get("$ xns") + "'");
444 func_regex = new Regex("^\\s+|\\s+$");
445 } catch (RegexError e) {
446 print("failed to build regex");
449 // sort the key's so they always get rendered in the same order..
451 var keys = new Gee.ArrayList<string>();
452 var piter = this.node.props.map_iterator();
453 while (piter.next() ) {
457 this.node.normalize_key(piter.get_key(), out k, out kflag, out ktype);
462 keys.sort(( a, b) => {
463 return ((string)a).collate((string)b);
464 //if (a == b) return 0;
465 //return a < b ? -1 : 1;
468 var has_cms = this.node.has("cms-id");
470 for (var i = 0; i< keys.size; i++) {
471 var key = this.node.get_key(keys.get(i));
472 //("ADD KEY %s\n", key);
477 this.node.normalize_key(key, out k, out kflag, out ktype);
480 var v = this.node.get(key);
483 //if (this.skip.contains(k) ) {
486 if ( Regex.match_simple("\\[\\]$", k)) {
487 // array .. not supported... here?
493 // skip builder stuff. prefixed with '.' .. just like unix fs..
494 if (kflag == ".") { // |. or . -- do not output..
498 // ignore '* prop'; ???
502 // handle cms-id // html
503 if (has_cms && k == "cms-id") {
504 continue; // ignore it...
506 // html must not be a dynamic property...
507 // note - we do not translate this either...
508 if (has_cms && k == "html" && kflag != "$") {
511 this.out_props.set("html", "Pman.Cms.content(" +
512 this.node.quoteString(this.renderer.name + "::" + this.node.get("cms-id")) +
514 this.node.quoteString(v) +
523 if (Lang.isKeyword(leftv) || Lang.isBuiltin(leftv)) {
524 left = "'" + leftv + "'";
525 } else if (Regex.match_simple("[^A-Za-z_]+",leftv)) { // not plain a-z... - quoted.
526 var val = this.node.quoteString(leftv);
528 left = "'" + val.substring(1, val.length-2).replace("'", "\\'") + "'";
534 // next.. is it a function.. or a raw string..
542 // ??? any others that are raw output..
544 // does not hapepnd with arrays..
545 if (v.length < 1) { //if (typeof(el) == 'string' && !obj[i].length) { //skip empty.
552 str = func_regex.replace(v,v.length, 0, "");
554 print("regex failed");
560 var lines = str.split("\n");
562 if (lines.length > 0) {
563 nstr = string.joinv("\n" + this.pad, lines);
564 //nstr = string.joinv("\n", lines);
566 this.out_props.set(left, nstr);
567 //print("==> " + str + "\n");
568 //this.els.add(left + " : "+ nstr);
579 ktype.down() == "boolean"
581 ktype.down() == "bool"
583 ktype.down() == "number"
585 ktype.down() == "int"
586 ) { // boolean or number...?
587 this.out_props.set(left, v.down());
588 //this.els.add(left + " : " + v.down() );
592 // is it a translated string?
598 //if (this.doubleStringProps.size < 1) {
599 // this.els.add(left + this.node.quoteString(v));
603 if ((this.doubleStringProps.index_of(k) > -1) ||
604 (ktype.down() == "string" && k[0] == '_')
607 // then use the translated version...
610 (v.split("\n").length > 1 ?
611 ("\n" + this.pad + string.joinv(this.pad + "\n", v.split("\n")).replace("*/", "* - /") + "\n" + this.pad + "*/ ") :
612 (v.replace("*/", "* - /") + " */")
615 //this.els.add(left + " : _this._strings['" +
616 // GLib.Checksum.compute_for_string (ChecksumType.MD5, v) +
619 this.out_props.set(left, "_this._strings['" +
620 GLib.Checksum.compute_for_string (ChecksumType.MD5, v.strip()) +
625 // otherwise it needs to be encapsulated.. as single quotes..
627 var vv = this.node.quoteString(v);
628 // single quote.. v.substring(1, v.length-1).replace("'", "\\'") + "'";
629 //this.els.add(left + " : " + "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
630 this.out_props.set(left, "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
638 public void readListeners()
641 if (this.node.listeners.size < 1) {
644 // munge the listeners.
645 //print("ADDING listeners?");
650 var keys = new Gee.ArrayList<string>();
651 var piter = this.node.listeners.map_iterator();
652 while (piter.next() ) {
654 keys.add(piter.get_key());
656 keys.sort(( a, b) => {
657 return ((string)a).collate((string)b);
658 //if (a == b) return 0;
659 //return a < b ? -1 : 1;
663 for (var i = 0; i< keys.size; i++) {
664 var key = keys.get(i);
665 var val = this.node.listeners.get(key);
669 var str = val.strip();
670 var lines = str.split("\n");
671 if (lines.length > 0) {
672 //str = string.joinv("\n" + this.pad + " ", lines);
673 str = string.joinv("\n" + this.pad + indent_str + indent_str , lines);
676 this.out_listeners.set(key.replace("|", "") ,str);
685 public void iterChildren()
689 // finally munge the children...
690 if (this.node.items.size < 1) {
693 var itms = "items : [\n";
695 for(var i = 0; i < this.node.items.size;i++) {
696 var ele = this.node.items.get(i);
697 if (ele.props.has_key("* prop")) {
701 this.out_children.add(ele);
704 itms += "\n"+ this.pad + "]" + "\n";
705 //this.els.add(itms);
708 // finally output listeners...
710 public void xIncludeToString()