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 NodeToJs : NodeWriter {
21 static uint indent = 1;
22 static string indent_str = " ";
25 Gee.ArrayList<string> doubleStringProps; // need to think if this is a good idea like this
29 Gee.HashMap<string,string> out_props;
30 Gee.HashMap<string,string> out_listeners;
31 Gee.HashMap<string,Node> out_nodeprops;
32 Gee.ArrayList<Node> out_children;
33 Gee.HashMap<string,Gee.ArrayList<Node>> out_props_array;
34 Gee.HashMap<string,Gee.ArrayList<string>> out_props_array_plain;
41 public NodeToJs( JsRender file, Node node, string pad , NodeWriter? parent, Gee.ArrayList<string> doubleStringProps)
44 base(file, node, pad.length, parent);
45 this.doubleStringProps = doubleStringProps;
48 this.node.node_pad = pad;
50 this.out_props = new Gee.HashMap<string,string>();
51 this.out_listeners = new Gee.HashMap<string,string>();
54 this.out_nodeprops = new Gee.HashMap<string,Node>() ;
55 this.out_children = new Gee.ArrayList<Node> ();
57 this.out_props_array = new Gee.HashMap<string,Gee.ArrayList<Node>>(); // filled in by 'checkChildren'
58 this.out_props_array_plain = new Gee.HashMap<string,Gee.ArrayList<string>>() ;
60 this.cur_line = parent == null ? 0 : parent.cur_line ; //-1 as we usuall concat onto the existin gline?
63 this.top = parent == null ? this : parent.top;
66 node.node_lines = new Gee.ArrayList<int>();
67 node.node_lines_map = new Gee.HashMap<int,Node>();
76 public override string munge ( )
78 //return this.mungeToString(this.node);
79 if (this.node.as_source_version > 0 &&
80 this.node.as_source_version == this.node.updated_count &&
81 this.node.as_source_start_line == cur_line &&
82 this.node.as_source != ""
85 return this.node.as_source;
87 this.node.as_source_start_line = cur_line;
90 //this.readArrayProps();
93 if (!this.node.props.has_key("* xinclude")) {
99 // no properties to output...
100 //if (this.els.size < 1) {
106 this.node.as_source_version = this.node.updated_count;
107 this.node.as_source == this.ret;
114 This currently works by creating a key/value array of this.els, which is just an array of properties..
115 this is so that join() works...
119 b) output plan properties.
120 c) output listeners..
122 g) output prop_arrays..
130 public Gee.ArrayList<string> orderedPropKeys() {
132 var ret = new Gee.ArrayList<string> ();
133 var niter = this.out_props.map_iterator();
134 while(niter.next()) {
135 ret.add(niter.get_key());
138 ret.sort(( a, b) => {
139 return ((string)a).collate((string)b);
140 //if (a == b) return 0;
141 //return a < b ? -1 : 1;
145 public Gee.ArrayList<string> orderedListenerKeys() {
147 var ret = new Gee.ArrayList<string> ();
148 var niter = this.out_listeners.map_iterator();
149 while(niter.next()) {
150 ret.add(niter.get_key());
153 ret.sort(( a, b) => {
154 return ((string)a).collate((string)b);
155 //if (a == b) return 0;
156 //return a < b ? -1 : 1;
162 public string mungeOut()
164 this.node.line_start = this.cur_line;
165 this.top.node.setNodeLine(this.cur_line, this.node);
166 var spad = this.pad.substring(0, this.pad.length-indent);
168 if (this.node.props.has_key("* xinclude")) {
169 this.addJsLine("Roo.apply(" + this.node.props.get("* xinclude").val + "._tree(), {",0 );
172 this.addJsLine("{", 0);
175 // output the items...
176 // work out remaining items...
178 // output xns / xtype first..
179 if (this.out_props.has_key("xtype")) {
180 var v = this.out_props.get("xtype");
182 this.node.setLine(this.cur_line, "p","xtype");
183 this.addJsLine(this.pad + "xtype" + " : " + v + suffix, ',');
187 var iter = this.orderedPropKeys().list_iterator();
192 if (k == "xns" || k == "xtype") {
196 var v = this.out_props.get(k);
197 this.node.setLine(this.cur_line, "p",k);
199 this.addJsLine(this.pad + k + " : " + v + suffix, ',');
201 this.node.setLine(this.cur_line, "e", k);
207 if (this.out_listeners.size > 0 ) {
209 this.addJsLine(this.pad + "listeners : {", 0);
210 iter = this.orderedListenerKeys().list_iterator();
215 var v = this.out_listeners.get(k);
217 this.node.setLine(this.cur_line, "l",k); //listener
218 this.addJsLine(this.pad + indent_str + k + " : " + v , ',');
220 this.node.setLine(this.cur_line, "x", k);
224 this.addJsLine(this.pad + "}" ,',');
228 //------- at this point it is the end of the code relating directly to the object..
230 if (this.out_props.has_key("xns")) {
231 var v = this.out_props.get("xns");
233 this.node.setLine(this.cur_line, "p","xns");
234 this.addJsLine(this.pad + "xns" + " : " + v + suffix, ',');
235 this.node.setLine(this.cur_line, "p","| xns");
236 this.addJsLine(this.pad + "'|xns' : '" + v + "'", ',');
237 this.node.setLine(this.cur_line, "e", "xns");
241 this.node.line_end = this.cur_line;
245 var niter = this.out_nodeprops.map_iterator();
247 while(niter.next()) {
249 //print("add str: %s\n", addstr);
250 this.node.setLine(this.cur_line, "p",niter.get_key());
252 var addstr = this.mungeChildNew(this.pad + indent_str, niter.get_value());
253 this.addJsLine(this.pad + niter.get_key() + " : " + addstr, ',');
255 this.node.setLine(this.cur_line, "e", "");
259 var piter = this.out_props_array.map_iterator();
261 while(piter.next()) {
263 this.node.setLine(this.cur_line, "p",piter.get_key());
264 this.addJsLine(this.pad + piter.get_key() + " : [", 0);
266 var pliter = piter.get_value().list_iterator();
267 while (pliter.next()) {
268 var addstr = this.mungeChildNew(this.pad + indent_str + indent_str, pliter.get());
269 this.addJsLine(this.pad + indent_str + addstr, ',');
270 this.node.setLine(this.cur_line, "e", "");
274 this.addJsLine(this.pad + "]" , ',');
279 if (this.out_children.size > 0) {
280 this.addJsLine(this.pad + "items : [" , 0);
281 var cniter = this.out_children.list_iterator();
282 while (cniter.next()) {
283 suffix = cniter.has_next() ? "," : "";
284 var addstr = this.mungeChildNew(this.pad + indent_str + indent_str, cniter.get());
285 this.addJsLine(this.pad + indent_str + addstr, ',');
286 this.node.setLine(this.cur_line, "e", "");
290 this.addJsLine(this.pad + "]",',');
292 this.node.setLine(this.cur_line, "e", "");
294 if (this.node.props.has_key("* xinclude")) {
295 this.addJsLine(spad + "})",0);
298 this.addJsLine( spad + "}", 0);
301 this.node.sortLines();
311 * if we end with a ','
315 char last_line_end = '!';
318 * add a line - note we will end up with an extra line break
319 * at beginning of nodes doing this..
321 * @param str = text to add..
322 * @param line_end = 0 (just add a line break)
323 * line_end = ',' and ","
326 public void addJsLine(string str, char line_end)
328 if (this.last_line_end != '!') {
329 this.output += (this.last_line_end == 0 ? "" : this.last_line_end.to_string()) + "\n";
331 this.last_line_end = line_end;
332 this.cur_line += str.split("\n").length;
336 //this.ret += "/*%d(%d-%d)*/ ".printf(this.cur_line -1, this.node.line_start,this.node.line_end) + str + "\n";
340 public void closeLine() // send this before '}' or ']' to block output of ',' ...
342 this.last_line_end = 0;
346 public string mungeChildNew(string pad , Node cnode )
348 var x = new NodeToJs( this.file, cnode, pad , this, this.doubleStringProps);
356 * loop through items[] array see if any of the children have '* prop'
357 * -- which means they are a property of this node.
358 * -- ADD TO : this.opt_props_array
362 public void checkChildren ()
366 // look throught he chilren == looking for * prop.. -- fixme might not work..
369 if (!this.node.hasChildren()) {
373 var items = this.node.readItems();
374 for (var ii =0; ii< items.size; ii++) {
375 var pl = items.get(ii);
376 if (!pl.props.has_key("* prop")) {
381 //print(JSON.stringify(pl,null,4));
383 //var prop = pl['*prop'] + '';
384 //delete pl['*prop'];
385 var prop = pl.get("* prop");
386 //print("got prop "+ prop + "\n");
389 if (! Regex.match_simple("\\[\\]$", prop)) {
390 // it's a standard prop..
392 // munge property..??
394 this.out_nodeprops.set(prop, pl);
402 var sprop = prop.replace("[]", "");
403 //print("sprop is : " + sprop + "\n");
405 // it's an array type..
407 if (!this.out_props_array.has_key(sprop)) {
408 this.out_props_array.set(sprop, new Gee.ArrayList<Node>());
412 this.out_props_array.get(sprop).add( pl);
413 //this.ar_props.set(sprop, nstr);
420 * Standardize this crap...
422 * standard properties (use to set)
423 * If they are long values show the dialog..
426 * bool is_xxx :: can show a pulldown.. (true/false)
428 * $ string html = string with value interpolated eg. baseURL + ".."
429 * Clutter.ActorAlign x_align (typed) -- shows pulldowns if type is ENUM?
430 * $ untypedvalue = javascript untyped value...
431 * _ string html ... = translatable..
434 * object properties (not part of the GOjbect being wrapped?
435 * # Gee.ArrayList<Xcls_fileitem> fileitems
440 * methods -- always text editor..
448 * * init -- big string?
450 * event handlers (listeners)
455 * +XXXX -- indicates it's a instance property / not glob...
456 * *XXXX -- skip writing glob property (used as classes that can be created...)
460 public void readProps()
466 func_regex = new Regex("^\\s+|\\s+$");
467 } catch (RegexError e) {
468 print("failed to build regex");
471 // sort the key's so they always get rendered in the same order..
473 var keys = new Gee.ArrayList<string>();
474 var piter = this.node.props.map_iterator();
475 while (piter.next() ) {
478 keys.add( piter.get_key()); // since are keys are nice and clean now..
481 keys.sort(( a, b) => {
482 return ((string)a).collate((string)b);
483 //if (a == b) return 0;
484 //return a < b ? -1 : 1;
487 var has_cms = this.node.has("cms-id");
489 for (var i = 0; i< keys.size; i++) {
490 var prop = this.node.get_prop(keys.get(i));
491 //("ADD KEY %s\n", key);
493 var ktype = prop.rtype;
494 var kflag = prop.ptype;
498 //if (this.skip.contains(k) ) {
501 if ( Regex.match_simple("\\[\\]$", k)) {
502 // array .. not supported... here?
507 // skip builder stuff. prefixed with '.' .. just like unix fs..
508 //if (kflag == ".") { // |. or . -- do not output..
511 if (kflag == NodePropType.SPECIAL) {
512 // ignore '* prop'; ???
516 // handle cms-id // html
517 if (has_cms && k == "cms-id") {
518 continue; // ignore it...
520 // html must not be a dynamic property...
521 // note - we do not translate this either...
522 if (has_cms && k == "html" && kflag != NodePropType.RAW) {
525 this.out_props.set("html", "Pman.Cms.content(" +
526 this.node.quoteString(this.file.name + "::" + this.node.get("cms-id")) +
528 this.node.quoteString(v) +
537 if (Lang.isKeyword(leftv) || Lang.isBuiltin(leftv)) {
538 left = "'" + leftv + "'";
539 } else if (Regex.match_simple("[^A-Za-z_]+",leftv)) { // not plain a-z... - quoted.
540 var val = this.node.quoteString(leftv);
542 left = "'" + val.substring(1, val.length-2).replace("'", "\\'") + "'";
548 // next.. is it a function.. or a raw string..
550 kflag == NodePropType.METHOD
552 kflag == NodePropType.RAW
554 ktype == "function" // ??? why woudl return type be function? << messed up..
556 // ??? any others that are raw output..
558 // does not hapepnd with arrays..
559 if (v.length < 1) { //if (typeof(el) == 'string' && !obj[i].length) { //skip empty.
566 str = func_regex.replace(v,v.length, 0, "");
568 print("regex failed");
574 var lines = str.split("\n");
576 if (lines.length > 0) {
577 nstr = string.joinv("\n" + this.pad, lines);
578 //nstr = string.joinv("\n", lines);
580 this.out_props.set(left, nstr);
586 //print("==> " + str + "\n");
587 //this.els.add(left + " : "+ nstr);
598 ktype.down() == "boolean"
600 ktype.down() == "bool"
602 ktype.down() == "number"
604 ktype.down() == "int"
605 ) { // boolean or number...?
606 this.out_props.set(left, v.down());
607 //this.els.add(left + " : " + v.down() );
611 // is it a translated string?
617 // doubleStringProps is a list of keys like 'name' 'title' etc.. that we know can be translated..
619 if ((this.doubleStringProps.index_of(k) > -1) ||
620 (ktype.down() == "string" && k[0] == '_') // strings starting with '_'
623 // then use the translated version...
626 (v.split("\n").length > 1 ?
627 ("\n" + this.pad + string.joinv(this.pad + "\n", v.split("\n")).replace("*/", "* - /") + "\n" + this.pad + "*/ ") :
628 (v.replace("*/", "* - /") + " */")
631 //this.els.add(left + " : _this._strings['" +
632 // GLib.Checksum.compute_for_string (ChecksumType.MD5, v) +
636 // string is stored in Roo.vala
637 var kname = GLib.Checksum.compute_for_string (ChecksumType.MD5, v.strip());
639 this.out_props.set(left, "_this._strings['" + kname + "']" + com);
643 // otherwise it needs to be encapsulated.. as single quotes..
645 var vv = this.node.quoteString(v);
646 // single quote.. v.substring(1, v.length-1).replace("'", "\\'") + "'";
647 //this.els.add(left + " : " + "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
648 this.out_props.set(left, "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
656 public void readListeners()
659 if (this.node.listeners.size < 1) {
662 // munge the listeners.
663 //print("ADDING listeners?");
668 var keys = new Gee.ArrayList<string>();
669 var piter = this.node.listeners.map_iterator();
670 while (piter.next() ) {
672 keys.add(piter.get_key());
674 keys.sort(( a, b) => {
675 return ((string)a).collate((string)b);
676 //if (a == b) return 0;
677 //return a < b ? -1 : 1;
681 for (var i = 0; i< keys.size; i++) {
682 var key = keys.get(i);
683 var val = this.node.listeners.get(key).val;
687 var str = val.strip();
688 var lines = str.split("\n");
689 if (lines.length > 0) {
690 //str = string.joinv("\n" + this.pad + " ", lines);
691 str = string.joinv("\n" + this.pad + indent_str + indent_str , lines);
694 this.out_listeners.set(key.replace("|", "") ,str);
703 public void iterChildren()
706 var items = this.node.readItems();
707 // finally munge the children...
708 if (items.size < 1) {
711 var itms = "items : [\n";
713 for(var i = 0; i < items.size;i++) {
714 var ele = items.get(i);
715 if (ele.props.has_key("* prop")) {
719 this.out_children.add(ele);
722 itms += "\n"+ this.pad + "]" + "\n";
723 //this.els.add(itms);
726 // finally output listeners...
728 public void xIncludeToString()