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;
49 this.node.node_pad = pad;
51 //this.els = new Gee.ArrayList<string>();
52 //this.ar_props = new Gee.HashMap<string,string>();
56 // this is the bit that causes issues - we have to output as we go, otherwise we
57 // can not work out which line is which...
59 this.out_props = new Gee.HashMap<string,string>();
60 this.out_listeners = new Gee.HashMap<string,string>();
63 this.out_nodeprops = new Gee.HashMap<string,Node>() ;
64 this.out_children = new Gee.ArrayList<Node> ();
66 this.out_props_array = new Gee.HashMap<string,Gee.ArrayList<Node>>(); // filled in by 'checkChildren'
67 this.out_props_array_plain = new Gee.HashMap<string,Gee.ArrayList<string>>() ;
71 this.cur_line = parent == null ? 0 : parent.cur_line ; //-1 as we usuall concat onto the existin gline?
73 this.renderer = parent.renderer;
76 this.top = parent == null ? this : parent.top;
79 node.node_lines = new Gee.ArrayList<int>();
80 node.node_lines_map = new Gee.HashMap<int,Node>();
89 public string munge ( )
91 //return this.mungeToString(this.node);
92 if (this.node.as_source_version > 0 &&
93 this.node.as_source_version == this.node.updated_count
94 && this.node.as_source != ""
96 return this.node.as_source;
101 //this.readArrayProps();
102 this.readListeners();
104 if (!this.node.props.has_key("* xinclude")) {
110 // no properties to output...
111 //if (this.els.size < 1) {
117 this.node.as_source_version = this.node.updated_count;
118 this.node.as_source == this.ret;
125 This currently works by creating a key/value array of this.els, which is just an array of properties..
126 this is so that join() works...
130 b) output plan properties.
131 c) output listeners..
133 g) output prop_arrays..
141 public Gee.ArrayList<string> orderedPropKeys() {
143 var ret = new Gee.ArrayList<string> ();
144 var niter = this.out_props.map_iterator();
145 while(niter.next()) {
146 ret.add(niter.get_key());
149 ret.sort(( a, b) => {
150 return ((string)a).collate((string)b);
151 //if (a == b) return 0;
152 //return a < b ? -1 : 1;
156 public Gee.ArrayList<string> orderedListenerKeys() {
158 var ret = new Gee.ArrayList<string> ();
159 var niter = this.out_listeners.map_iterator();
160 while(niter.next()) {
161 ret.add(niter.get_key());
164 ret.sort(( a, b) => {
165 return ((string)a).collate((string)b);
166 //if (a == b) return 0;
167 //return a < b ? -1 : 1;
173 public string mungeOut()
175 this.node.line_start = this.cur_line;
176 this.top.node.setNodeLine(this.cur_line, this.node);
177 var spad = this.pad.substring(0, this.pad.length-indent);
179 if (this.node.props.has_key("* xinclude")) {
180 this.addLine("Roo.apply(" + this.node.props.get("* xinclude").val + "._tree(), {",0 );
183 this.addLine("{", 0);
186 // output the items...
187 // work out remaining items...
189 // output xns / xtype first..
190 if (this.out_props.has_key("xtype")) {
191 var v = this.out_props.get("xtype");
193 this.node.setLine(this.cur_line, "p","xtype");
194 this.addLine(this.pad + "xtype" + " : " + v + suffix, ',');
198 var iter = this.orderedPropKeys().list_iterator();
203 if (k == "xns" || k == "xtype") {
207 var v = this.out_props.get(k);
208 this.node.setLine(this.cur_line, "p",k);
210 this.addLine(this.pad + k + " : " + v + suffix, ',');
212 this.node.setLine(this.cur_line, "e", k);
218 if (this.out_listeners.size > 0 ) {
220 this.addLine(this.pad + "listeners : {", 0);
221 iter = this.orderedListenerKeys().list_iterator();
226 var v = this.out_listeners.get(k);
228 this.node.setLine(this.cur_line, "l",k); //listener
229 this.addLine(this.pad + indent_str + k + " : " + v , ',');
231 this.node.setLine(this.cur_line, "x", k);
235 this.addLine(this.pad + "}" ,',');
239 //------- at this point it is the end of the code relating directly to the object..
241 if (this.out_props.has_key("xns")) {
242 var v = this.out_props.get("xns");
244 this.node.setLine(this.cur_line, "p","xns");
245 this.addLine(this.pad + "xns" + " : " + v + suffix, ',');
246 this.node.setLine(this.cur_line, "p","| xns");
247 this.addLine(this.pad + "'|xns' : '" + v + "'", ',');
248 this.node.setLine(this.cur_line, "e", "xns");
252 this.node.line_end = this.cur_line;
256 var niter = this.out_nodeprops.map_iterator();
258 while(niter.next()) {
260 //print("add str: %s\n", addstr);
261 this.node.setLine(this.cur_line, "p",niter.get_key());
263 var addstr = this.mungeChildNew(this.pad + indent_str, niter.get_value());
264 this.addLine(this.pad + niter.get_key() + " : " + addstr, ',');
266 this.node.setLine(this.cur_line, "e", "");
270 var piter = this.out_props_array.map_iterator();
272 while(piter.next()) {
274 this.node.setLine(this.cur_line, "p",piter.get_key());
275 this.addLine(this.pad + piter.get_key() + " : [", 0);
277 var pliter = piter.get_value().list_iterator();
278 while (pliter.next()) {
279 var addstr = this.mungeChildNew(this.pad + indent_str + indent_str, pliter.get());
280 this.addLine(this.pad + indent_str + addstr, ',');
281 this.node.setLine(this.cur_line, "e", "");
285 this.addLine(this.pad + "]" , ',');
290 if (this.out_children.size > 0) {
291 this.addLine(this.pad + "items : [" , 0);
292 var cniter = this.out_children.list_iterator();
293 while (cniter.next()) {
294 suffix = cniter.has_next() ? "," : "";
295 var addstr = this.mungeChildNew(this.pad + indent_str + indent_str, cniter.get());
296 this.addLine(this.pad + indent_str + addstr, ',');
297 this.node.setLine(this.cur_line, "e", "");
301 this.addLine(this.pad + "]",',');
303 this.node.setLine(this.cur_line, "e", "");
305 if (this.node.props.has_key("* xinclude")) {
306 this.addLine(spad + "})",0);
309 this.addLine( spad + "}", 0);
312 this.node.sortLines();
322 * if we end with a ','
326 char last_line_end = '!';
329 * add a line - note we will end up with an extra line break
330 * at beginning of nodes doing this..
332 * @param str = text to add..
333 * @param line_end = 0 (just add a line break)
334 * line_end = ',' and ","
337 public void addLine(string str, char line_end)
339 if (this.last_line_end != '!') {
340 this.ret += (this.last_line_end == 0 ? "" : this.last_line_end.to_string()) + "\n";
342 this.last_line_end = line_end;
343 this.cur_line += str.split("\n").length;
347 //this.ret += "/*%d(%d-%d)*/ ".printf(this.cur_line -1, this.node.line_start,this.node.line_end) + str + "\n";
351 public void closeLine() // send this before '}' or ']' to block output of ',' ...
353 this.last_line_end = 0;
356 /* public void addMultiLine(str= "")
359 //this.ret += "/ * %d(%d-%d) * / ".printf(this.cur_line, this.node.line_start,this.node.line_end)+ str + "\n";
360 this.ret += str + "\n";
361 this.cur_line += str.split("\n").length;
364 public string mungeChildNew(string pad , Node cnode )
366 var x = new NodeToJs(cnode, this.doubleStringProps, pad, this);
373 * loop through items[] array see if any of the children have '* prop'
374 * -- which means they are a property of this node.
375 * -- ADD TO : this.opt_props_array
379 public void checkChildren ()
383 // look throught he chilren == looking for * prop.. -- fixme might not work..
386 if (!this.node.hasChildren()) {
390 var items = this.node.readItems();
391 for (var ii =0; ii< items.size; ii++) {
392 var pl = items.get(ii);
393 if (!pl.props.has_key("* prop")) {
398 //print(JSON.stringify(pl,null,4));
400 //var prop = pl['*prop'] + '';
401 //delete pl['*prop'];
402 var prop = pl.get("* prop");
403 //print("got prop "+ prop + "\n");
406 if (! Regex.match_simple("\\[\\]$", prop)) {
407 // it's a standard prop..
409 // munge property..??
411 this.out_nodeprops.set(prop, pl);
419 var sprop = prop.replace("[]", "");
420 //print("sprop is : " + sprop + "\n");
422 // it's an array type..
424 if (!this.out_props_array.has_key(sprop)) {
425 this.out_props_array.set(sprop, new Gee.ArrayList<Node>());
429 this.out_props_array.get(sprop).add( pl);
430 //this.ar_props.set(sprop, nstr);
437 * Standardize this crap...
439 * standard properties (use to set)
440 * If they are long values show the dialog..
443 * bool is_xxx :: can show a pulldown.. (true/false)
445 * $ string html = string with value interpolated eg. baseURL + ".."
446 * Clutter.ActorAlign x_align (typed) -- shows pulldowns if type is ENUM?
447 * $ untypedvalue = javascript untyped value...
448 * _ string html ... = translatable..
451 * object properties (not part of the GOjbect being wrapped?
452 * # Gee.ArrayList<Xcls_fileitem> fileitems
457 * methods -- always text editor..
465 * * init -- big string?
467 * event handlers (listeners)
472 * +XXXX -- indicates it's a instance property / not glob...
473 * *XXXX -- skip writing glob property (used as classes that can be created...)
477 public void readProps()
483 func_regex = new Regex("^\\s+|\\s+$");
484 } catch (RegexError e) {
485 print("failed to build regex");
488 // sort the key's so they always get rendered in the same order..
490 var keys = new Gee.ArrayList<string>();
491 var piter = this.node.props.map_iterator();
492 while (piter.next() ) {
495 keys.add( piter.get_key()); // since are keys are nice and clean now..
498 keys.sort(( a, b) => {
499 return ((string)a).collate((string)b);
500 //if (a == b) return 0;
501 //return a < b ? -1 : 1;
504 var has_cms = this.node.has("cms-id");
506 for (var i = 0; i< keys.size; i++) {
507 var prop = this.node.get_prop(keys.get(i));
508 //("ADD KEY %s\n", key);
510 var ktype = prop.rtype;
511 var kflag = prop.ptype;
515 //if (this.skip.contains(k) ) {
518 if ( Regex.match_simple("\\[\\]$", k)) {
519 // array .. not supported... here?
524 // skip builder stuff. prefixed with '.' .. just like unix fs..
525 //if (kflag == ".") { // |. or . -- do not output..
528 if (kflag == NodePropType.SPECIAL) {
529 // ignore '* prop'; ???
533 // handle cms-id // html
534 if (has_cms && k == "cms-id") {
535 continue; // ignore it...
537 // html must not be a dynamic property...
538 // note - we do not translate this either...
539 if (has_cms && k == "html" && kflag != NodePropType.RAW) {
542 this.out_props.set("html", "Pman.Cms.content(" +
543 this.node.quoteString(this.renderer.name + "::" + this.node.get("cms-id")) +
545 this.node.quoteString(v) +
554 if (Lang.isKeyword(leftv) || Lang.isBuiltin(leftv)) {
555 left = "'" + leftv + "'";
556 } else if (Regex.match_simple("[^A-Za-z_]+",leftv)) { // not plain a-z... - quoted.
557 var val = this.node.quoteString(leftv);
559 left = "'" + val.substring(1, val.length-2).replace("'", "\\'") + "'";
565 // next.. is it a function.. or a raw string..
567 kflag == NodePropType.METHOD
569 kflag == NodePropType.RAW
571 ktype == "function" // ??? why woudl return type be function? << messed up..
573 // ??? any others that are raw output..
575 // does not hapepnd with arrays..
576 if (v.length < 1) { //if (typeof(el) == 'string' && !obj[i].length) { //skip empty.
583 str = func_regex.replace(v,v.length, 0, "");
585 print("regex failed");
591 var lines = str.split("\n");
593 if (lines.length > 0) {
594 nstr = string.joinv("\n" + this.pad, lines);
595 //nstr = string.joinv("\n", lines);
597 this.out_props.set(left, nstr);
603 //print("==> " + str + "\n");
604 //this.els.add(left + " : "+ nstr);
615 ktype.down() == "boolean"
617 ktype.down() == "bool"
619 ktype.down() == "number"
621 ktype.down() == "int"
622 ) { // boolean or number...?
623 this.out_props.set(left, v.down());
624 //this.els.add(left + " : " + v.down() );
628 // is it a translated string?
634 // doubleStringProps is a list of keys like 'name' 'title' etc.. that we know can be translated..
636 if ((this.doubleStringProps.index_of(k) > -1) ||
637 (ktype.down() == "string" && k[0] == '_') // strings starting with '_'
640 // then use the translated version...
643 (v.split("\n").length > 1 ?
644 ("\n" + this.pad + string.joinv(this.pad + "\n", v.split("\n")).replace("*/", "* - /") + "\n" + this.pad + "*/ ") :
645 (v.replace("*/", "* - /") + " */")
648 //this.els.add(left + " : _this._strings['" +
649 // GLib.Checksum.compute_for_string (ChecksumType.MD5, v) +
653 // string is stored in Roo.vala
654 var kname = GLib.Checksum.compute_for_string (ChecksumType.MD5, v.strip());
656 this.out_props.set(left, "_this._strings['" + kname + "']" + com);
660 // otherwise it needs to be encapsulated.. as single quotes..
662 var vv = this.node.quoteString(v);
663 // single quote.. v.substring(1, v.length-1).replace("'", "\\'") + "'";
664 //this.els.add(left + " : " + "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
665 this.out_props.set(left, "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
673 public void readListeners()
676 if (this.node.listeners.size < 1) {
679 // munge the listeners.
680 //print("ADDING listeners?");
685 var keys = new Gee.ArrayList<string>();
686 var piter = this.node.listeners.map_iterator();
687 while (piter.next() ) {
689 keys.add(piter.get_key());
691 keys.sort(( a, b) => {
692 return ((string)a).collate((string)b);
693 //if (a == b) return 0;
694 //return a < b ? -1 : 1;
698 for (var i = 0; i< keys.size; i++) {
699 var key = keys.get(i);
700 var val = this.node.listeners.get(key).val;
704 var str = val.strip();
705 var lines = str.split("\n");
706 if (lines.length > 0) {
707 //str = string.joinv("\n" + this.pad + " ", lines);
708 str = string.joinv("\n" + this.pad + indent_str + indent_str , lines);
711 this.out_listeners.set(key.replace("|", "") ,str);
720 public void iterChildren()
723 var items = this.node.readItems();
724 // finally munge the children...
725 if (items.size < 1) {
728 var itms = "items : [\n";
730 for(var i = 0; i < items.size;i++) {
731 var ele = items.get(i);
732 if (ele.props.has_key("* prop")) {
736 this.out_children.add(ele);
739 itms += "\n"+ this.pad + "]" + "\n";
740 //this.els.add(itms);
743 // finally output listeners...
745 public void xIncludeToString()