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, ',');
189 this.node.setLine(this.cur_line, "e", "");
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 , ',');
206 this.node.setLine(this.cur_line, "e", "");
210 this.addLine(this.pad + "}" ,',');
214 //------- at this point it is the end of the code relating directly to the object..
218 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 (niter.get_key() == "xns") {
229 this.addLine(this.pad + "'|xns' : '" + addstr + "'", ',');
232 this.node.setLine(this.cur_line, "e", "");
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, ',');
246 this.node.setLine(this.cur_line, "e", "");
249 this.addLine(this.pad + "]" , ',');
253 if (this.out_children.size > 0) {
254 this.addLine(this.pad + "items : [" , 0);
255 var cniter = this.out_children.list_iterator();
256 while (cniter.next()) {
257 suffix = cniter.has_next() ? "," : "";
258 var addstr = this.mungeChildNew(this.pad + indent_str + indent_str, cniter.get());
259 this.addLine(this.pad + indent_str + addstr, ',');
260 this.node.setLine(this.cur_line, "e", "");
264 this.addLine(this.pad + "]",',');
266 this.node.setLine(this.cur_line, "e", "");
268 if (this.node.props.has_key("* xinclude")) {
269 this.addLine(spad + "})",0);
272 this.addLine( spad + "}", 0);
275 this.node.sortLines();
285 * if we end with a ','
289 char last_line_end = '!';
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 if (this.last_line_end != '!') {
303 this.ret += (this.last_line_end == 0 ? "" : this.last_line_end.to_string()) + "\n";
305 this.last_line_end = line_end;
306 this.cur_line += str.split("\n").length;
310 //this.ret += "/*%d(%d-%d)*/ ".printf(this.cur_line -1, this.node.line_start,this.node.line_end) + str + "\n";
314 public void closeLine() // send this before '}' or ']' to block output of ',' ...
316 this.last_line_end = 0;
319 /* public void addMultiLine(str= "")
322 //this.ret += "/ * %d(%d-%d) * / ".printf(this.cur_line, this.node.line_start,this.node.line_end)+ str + "\n";
323 this.ret += str + "\n";
324 this.cur_line += str.split("\n").length;
327 public string mungeChildNew(string pad , Node cnode )
329 var x = new NodeToJs(cnode, this.doubleStringProps, pad, this);
336 * loop through items[] array see if any of the children have '* prop'
337 * -- which means they are a property of this node.
338 * -- ADD TO : this.opt_props_array
342 public void checkChildren ()
346 // look throught he chilren == looking for * prop.. -- fixme might not work..
349 if (!this.node.hasChildren()) {
354 for (var ii =0; ii< this.node.items.size; ii++) {
355 var pl = this.node.items.get(ii);
356 if (!pl.props.has_key("* prop")) {
361 //print(JSON.stringify(pl,null,4));
363 //var prop = pl['*prop'] + '';
364 //delete pl['*prop'];
365 var prop = pl.get("* prop");
366 //print("got prop "+ prop + "\n");
369 if (! Regex.match_simple("\\[\\]$", prop)) {
370 // it's a standard prop..
372 // munge property..??
374 this.out_nodeprops.set(prop, pl);
382 var sprop = prop.replace("[]", "");
383 //print("sprop is : " + sprop + "\n");
385 // it's an array type..
387 if (!this.out_props_array.has_key(sprop)) {
388 this.out_props_array.set(sprop, new Gee.ArrayList<Node>());
392 this.out_props_array.get(sprop).add( pl);
393 //this.ar_props.set(sprop, nstr);
400 * Standardize this crap...
402 * standard properties (use to set)
403 * If they are long values show the dialog..
406 * bool is_xxx :: can show a pulldown.. (true/false)
408 * $ string html = string with value interpolated eg. baseURL + ".."
409 * Clutter.ActorAlign x_align (typed) -- shows pulldowns if type is ENUM?
410 * $ untypedvalue = javascript untyped value...
411 * _ string html ... = translatable..
414 * object properties (not part of the GOjbect being wrapped?
415 * # Gee.ArrayList<Xcls_fileitem> fileitems
420 * methods -- always text editor..
428 * * init -- big string?
430 * event handlers (listeners)
435 * +XXXX -- indicates it's a instance property / not glob...
436 * *XXXX -- skip writing glob property (used as classes that can be created...)
440 public void readProps()
446 func_regex = new Regex("^\\s+|\\s+$");
447 } catch (RegexError e) {
448 print("failed to build regex");
451 // sort the key's so they always get rendered in the same order..
453 var keys = new Gee.ArrayList<string>();
454 var piter = this.node.props.map_iterator();
455 while (piter.next() ) {
459 this.node.normalize_key(piter.get_key(), out k, out kflag, out ktype);
464 keys.sort(( a, b) => {
465 return ((string)a).collate((string)b);
466 //if (a == b) return 0;
467 //return a < b ? -1 : 1;
470 var has_cms = this.node.has("cms-id");
472 for (var i = 0; i< keys.size; i++) {
473 var key = this.node.get_key(keys.get(i));
474 //("ADD KEY %s\n", key);
479 this.node.normalize_key(key, out k, out kflag, out ktype);
482 var v = this.node.get(key);
485 //if (this.skip.contains(k) ) {
488 if ( Regex.match_simple("\\[\\]$", k)) {
489 // array .. not supported... here?
495 // skip builder stuff. prefixed with '.' .. just like unix fs..
496 if (kflag == ".") { // |. or . -- do not output..
500 // ignore '* prop'; ???
504 // handle cms-id // html
505 if (has_cms && k == "cms-id") {
506 continue; // ignore it...
508 // html must not be a dynamic property...
509 // note - we do not translate this either...
510 if (has_cms && k == "html" && kflag != "$") {
513 this.out_props.set("html", "Pman.Cms.content(" +
514 this.node.quoteString(this.renderer.name + "::" + this.node.get("cms-id")) +
516 this.node.quoteString(v) +
525 if (Lang.isKeyword(leftv) || Lang.isBuiltin(leftv)) {
526 left = "'" + leftv + "'";
527 } else if (Regex.match_simple("[^A-Za-z_]+",leftv)) { // not plain a-z... - quoted.
528 var val = this.node.quoteString(leftv);
530 left = "'" + val.substring(1, val.length-2).replace("'", "\\'") + "'";
536 // next.. is it a function.. or a raw string..
544 // ??? any others that are raw output..
546 // does not hapepnd with arrays..
547 if (v.length < 1) { //if (typeof(el) == 'string' && !obj[i].length) { //skip empty.
554 str = func_regex.replace(v,v.length, 0, "");
556 print("regex failed");
562 var lines = str.split("\n");
564 if (lines.length > 0) {
565 nstr = string.joinv("\n" + this.pad, lines);
566 //nstr = string.joinv("\n", lines);
568 this.out_props.set(left, nstr);
574 //print("==> " + str + "\n");
575 //this.els.add(left + " : "+ nstr);
586 ktype.down() == "boolean"
588 ktype.down() == "bool"
590 ktype.down() == "number"
592 ktype.down() == "int"
593 ) { // boolean or number...?
594 this.out_props.set(left, v.down());
595 //this.els.add(left + " : " + v.down() );
599 // is it a translated string?
605 //if (this.doubleStringProps.size < 1) {
606 // this.els.add(left + this.node.quoteString(v));
610 if ((this.doubleStringProps.index_of(k) > -1) ||
611 (ktype.down() == "string" && k[0] == '_')
614 // then use the translated version...
617 (v.split("\n").length > 1 ?
618 ("\n" + this.pad + string.joinv(this.pad + "\n", v.split("\n")).replace("*/", "* - /") + "\n" + this.pad + "*/ ") :
619 (v.replace("*/", "* - /") + " */")
622 //this.els.add(left + " : _this._strings['" +
623 // GLib.Checksum.compute_for_string (ChecksumType.MD5, v) +
626 this.out_props.set(left, "_this._strings['" +
627 GLib.Checksum.compute_for_string (ChecksumType.MD5, v.strip()) +
632 // otherwise it needs to be encapsulated.. as single quotes..
634 var vv = this.node.quoteString(v);
635 // single quote.. v.substring(1, v.length-1).replace("'", "\\'") + "'";
636 //this.els.add(left + " : " + "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
637 this.out_props.set(left, "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
645 public void readListeners()
648 if (this.node.listeners.size < 1) {
651 // munge the listeners.
652 //print("ADDING listeners?");
657 var keys = new Gee.ArrayList<string>();
658 var piter = this.node.listeners.map_iterator();
659 while (piter.next() ) {
661 keys.add(piter.get_key());
663 keys.sort(( a, b) => {
664 return ((string)a).collate((string)b);
665 //if (a == b) return 0;
666 //return a < b ? -1 : 1;
670 for (var i = 0; i< keys.size; i++) {
671 var key = keys.get(i);
672 var val = this.node.listeners.get(key);
676 var str = val.strip();
677 var lines = str.split("\n");
678 if (lines.length > 0) {
679 //str = string.joinv("\n" + this.pad + " ", lines);
680 str = string.joinv("\n" + this.pad + indent_str + indent_str , lines);
683 this.out_listeners.set(key.replace("|", "") ,str);
692 public void iterChildren()
696 // finally munge the children...
697 if (this.node.items.size < 1) {
700 var itms = "items : [\n";
702 for(var i = 0; i < this.node.items.size;i++) {
703 var ele = this.node.items.get(i);
704 if (ele.props.has_key("* prop")) {
708 this.out_children.add(ele);
711 itms += "\n"+ this.pad + "]" + "\n";
712 //this.els.add(itms);
715 // finally output listeners...
717 public void xIncludeToString()