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").val + "._tree(), {",0 );
173 this.addLine("{", 0);
176 // output the items...
177 // work out remaining items...
179 // output xns / xtype first..
180 if (this.out_props.has_key("xtype")) {
181 var v = this.out_props.get("xtype");
183 this.node.setLine(this.cur_line, "p","xtype");
184 this.addLine(this.pad + "xtype" + " : " + v + suffix, ',');
188 var iter = this.orderedPropKeys().list_iterator();
193 if (k == "xns" || k == "xtype") {
197 var v = this.out_props.get(k);
198 this.node.setLine(this.cur_line, "p",k);
200 this.addLine(this.pad + k + " : " + v + suffix, ',');
202 this.node.setLine(this.cur_line, "e", k);
208 if (this.out_listeners.size > 0 ) {
210 this.addLine(this.pad + "listeners : {", 0);
211 iter = this.orderedListenerKeys().list_iterator();
216 var v = this.out_listeners.get(k);
218 this.node.setLine(this.cur_line, "l",k); //listener
219 this.addLine(this.pad + indent_str + k + " : " + v , ',');
221 this.node.setLine(this.cur_line, "x", k);
225 this.addLine(this.pad + "}" ,',');
229 //------- at this point it is the end of the code relating directly to the object..
231 if (this.out_props.has_key("xns")) {
232 var v = this.out_props.get("xns");
234 this.node.setLine(this.cur_line, "p","xns");
235 this.addLine(this.pad + "xns" + " : " + v + suffix, ',');
236 this.node.setLine(this.cur_line, "p","| xns");
237 this.addLine(this.pad + "'|xns' : '" + v + "'", ',');
238 this.node.setLine(this.cur_line, "e", "xns");
242 this.node.line_end = this.cur_line;
246 var niter = this.out_nodeprops.map_iterator();
248 while(niter.next()) {
250 //print("add str: %s\n", addstr);
251 this.node.setLine(this.cur_line, "p",niter.get_key());
253 var addstr = this.mungeChildNew(this.pad + indent_str, niter.get_value());
254 this.addLine(this.pad + niter.get_key() + " : " + addstr, ',');
256 this.node.setLine(this.cur_line, "e", "");
260 var piter = this.out_props_array.map_iterator();
262 while(piter.next()) {
264 this.node.setLine(this.cur_line, "p",piter.get_key());
265 this.addLine(this.pad + piter.get_key() + " : [", 0);
267 var pliter = piter.get_value().list_iterator();
268 while (pliter.next()) {
269 var addstr = this.mungeChildNew(this.pad + indent_str + indent_str, pliter.get());
270 this.addLine(this.pad + indent_str + addstr, ',');
271 this.node.setLine(this.cur_line, "e", "");
275 this.addLine(this.pad + "]" , ',');
280 if (this.out_children.size > 0) {
281 this.addLine(this.pad + "items : [" , 0);
282 var cniter = this.out_children.list_iterator();
283 while (cniter.next()) {
284 suffix = cniter.has_next() ? "," : "";
285 var addstr = this.mungeChildNew(this.pad + indent_str + indent_str, cniter.get());
286 this.addLine(this.pad + indent_str + addstr, ',');
287 this.node.setLine(this.cur_line, "e", "");
291 this.addLine(this.pad + "]",',');
293 this.node.setLine(this.cur_line, "e", "");
295 if (this.node.props.has_key("* xinclude")) {
296 this.addLine(spad + "})",0);
299 this.addLine( spad + "}", 0);
302 this.node.sortLines();
312 * if we end with a ','
316 char last_line_end = '!';
319 * add a line - note we will end up with an extra line break
320 * at beginning of nodes doing this..
322 * @param str = text to add..
323 * @param line_end = 0 (just add a line break)
324 * line_end = ',' and ","
327 public void addLine(string str, char line_end)
329 if (this.last_line_end != '!') {
330 this.ret += (this.last_line_end == 0 ? "" : this.last_line_end.to_string()) + "\n";
332 this.last_line_end = line_end;
333 this.cur_line += str.split("\n").length;
337 //this.ret += "/*%d(%d-%d)*/ ".printf(this.cur_line -1, this.node.line_start,this.node.line_end) + str + "\n";
341 public void closeLine() // send this before '}' or ']' to block output of ',' ...
343 this.last_line_end = 0;
346 /* public void addMultiLine(str= "")
349 //this.ret += "/ * %d(%d-%d) * / ".printf(this.cur_line, this.node.line_start,this.node.line_end)+ str + "\n";
350 this.ret += str + "\n";
351 this.cur_line += str.split("\n").length;
354 public string mungeChildNew(string pad , Node cnode )
356 var x = new NodeToJs(cnode, this.doubleStringProps, pad, this);
363 * loop through items[] array see if any of the children have '* prop'
364 * -- which means they are a property of this node.
365 * -- ADD TO : this.opt_props_array
369 public void checkChildren ()
373 // look throught he chilren == looking for * prop.. -- fixme might not work..
376 if (!this.node.hasChildren()) {
380 var items = this.node.readItems();
381 for (var ii =0; ii< items.size; ii++) {
382 var pl = items.get(ii);
383 if (!pl.props.has_key("* prop")) {
388 //print(JSON.stringify(pl,null,4));
390 //var prop = pl['*prop'] + '';
391 //delete pl['*prop'];
392 var prop = pl.get("* prop");
393 //print("got prop "+ prop + "\n");
396 if (! Regex.match_simple("\\[\\]$", prop)) {
397 // it's a standard prop..
399 // munge property..??
401 this.out_nodeprops.set(prop, pl);
409 var sprop = prop.replace("[]", "");
410 //print("sprop is : " + sprop + "\n");
412 // it's an array type..
414 if (!this.out_props_array.has_key(sprop)) {
415 this.out_props_array.set(sprop, new Gee.ArrayList<Node>());
419 this.out_props_array.get(sprop).add( pl);
420 //this.ar_props.set(sprop, nstr);
427 * Standardize this crap...
429 * standard properties (use to set)
430 * If they are long values show the dialog..
433 * bool is_xxx :: can show a pulldown.. (true/false)
435 * $ string html = string with value interpolated eg. baseURL + ".."
436 * Clutter.ActorAlign x_align (typed) -- shows pulldowns if type is ENUM?
437 * $ untypedvalue = javascript untyped value...
438 * _ string html ... = translatable..
441 * object properties (not part of the GOjbect being wrapped?
442 * # Gee.ArrayList<Xcls_fileitem> fileitems
447 * methods -- always text editor..
455 * * init -- big string?
457 * event handlers (listeners)
462 * +XXXX -- indicates it's a instance property / not glob...
463 * *XXXX -- skip writing glob property (used as classes that can be created...)
467 public void readProps()
473 func_regex = new Regex("^\\s+|\\s+$");
474 } catch (RegexError e) {
475 print("failed to build regex");
478 // sort the key's so they always get rendered in the same order..
480 var keys = new Gee.ArrayList<string>();
481 var piter = this.node.props.map_iterator();
482 while (piter.next() ) {
485 keys.add( piter.get_key()); // since are keys are nice and clean now..
488 keys.sort(( a, b) => {
489 return ((string)a).collate((string)b);
490 //if (a == b) return 0;
491 //return a < b ? -1 : 1;
494 var has_cms = this.node.has("cms-id");
496 for (var i = 0; i< keys.size; i++) {
497 var prop = this.node.get_prop(keys.get(i));
498 //("ADD KEY %s\n", key);
500 var ktype = prop.rtype;
501 var kflag = prop.ptype;
505 //if (this.skip.contains(k) ) {
508 if ( Regex.match_simple("\\[\\]$", k)) {
509 // array .. not supported... here?
514 // skip builder stuff. prefixed with '.' .. just like unix fs..
515 //if (kflag == ".") { // |. or . -- do not output..
518 if (kflag == NodePropType.SPECIAL) {
519 // ignore '* prop'; ???
523 // handle cms-id // html
524 if (has_cms && k == "cms-id") {
525 continue; // ignore it...
527 // html must not be a dynamic property...
528 // note - we do not translate this either...
529 if (has_cms && k == "html" && kflag != NodePropType.RAW) {
532 this.out_props.set("html", "Pman.Cms.content(" +
533 this.node.quoteString(this.renderer.name + "::" + this.node.get("cms-id")) +
535 this.node.quoteString(v) +
544 if (Lang.isKeyword(leftv) || Lang.isBuiltin(leftv)) {
545 left = "'" + leftv + "'";
546 } else if (Regex.match_simple("[^A-Za-z_]+",leftv)) { // not plain a-z... - quoted.
547 var val = this.node.quoteString(leftv);
549 left = "'" + val.substring(1, val.length-2).replace("'", "\\'") + "'";
555 // next.. is it a function.. or a raw string..
557 kflag == NodePropType.METHOD
559 kflag == NodePropType.RAW
561 ktype == "function" // ??? why woudl return type be function? << messed up..
563 // ??? any others that are raw output..
565 // does not hapepnd with arrays..
566 if (v.length < 1) { //if (typeof(el) == 'string' && !obj[i].length) { //skip empty.
573 str = func_regex.replace(v,v.length, 0, "");
575 print("regex failed");
581 var lines = str.split("\n");
583 if (lines.length > 0) {
584 nstr = string.joinv("\n" + this.pad, lines);
585 //nstr = string.joinv("\n", lines);
587 this.out_props.set(left, nstr);
593 //print("==> " + str + "\n");
594 //this.els.add(left + " : "+ nstr);
605 ktype.down() == "boolean"
607 ktype.down() == "bool"
609 ktype.down() == "number"
611 ktype.down() == "int"
612 ) { // boolean or number...?
613 this.out_props.set(left, v.down());
614 //this.els.add(left + " : " + v.down() );
618 // is it a translated string?
624 // doubleStringProps is a list of keys like 'name' 'title' etc.. that we know can be translated..
626 if ((this.doubleStringProps.index_of(k) > -1) ||
627 (ktype.down() == "string" && k[0] == '_') // strings starting with '_'
630 // then use the translated version...
633 (v.split("\n").length > 1 ?
634 ("\n" + this.pad + string.joinv(this.pad + "\n", v.split("\n")).replace("*/", "* - /") + "\n" + this.pad + "*/ ") :
635 (v.replace("*/", "* - /") + " */")
638 //this.els.add(left + " : _this._strings['" +
639 // GLib.Checksum.compute_for_string (ChecksumType.MD5, v) +
643 // string is stored in Roo.vala
644 var kname = GLib.Checksum.compute_for_string (ChecksumType.MD5, v.strip());
646 this.out_props.set(left, "_this._strings['" + kname + "']" + com);
650 // otherwise it needs to be encapsulated.. as single quotes..
652 var vv = this.node.quoteString(v);
653 // single quote.. v.substring(1, v.length-1).replace("'", "\\'") + "'";
654 //this.els.add(left + " : " + "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
655 this.out_props.set(left, "'" + vv.substring(1, vv.length-2).replace("'", "\\'") + "'");
663 public void readListeners()
666 if (this.node.listeners.size < 1) {
669 // munge the listeners.
670 //print("ADDING listeners?");
675 var keys = new Gee.ArrayList<string>();
676 var piter = this.node.listeners.map_iterator();
677 while (piter.next() ) {
679 keys.add(piter.get_key());
681 keys.sort(( a, b) => {
682 return ((string)a).collate((string)b);
683 //if (a == b) return 0;
684 //return a < b ? -1 : 1;
688 for (var i = 0; i< keys.size; i++) {
689 var key = keys.get(i);
690 var val = this.node.listeners.get(key).val;
694 var str = val.strip();
695 var lines = str.split("\n");
696 if (lines.length > 0) {
697 //str = string.joinv("\n" + this.pad + " ", lines);
698 str = string.joinv("\n" + this.pad + indent_str + indent_str , lines);
701 this.out_listeners.set(key.replace("|", "") ,str);
710 public void iterChildren()
713 var items = this.node.readItems();
714 // finally munge the children...
715 if (items.size < 1) {
718 var itms = "items : [\n";
720 for(var i = 0; i < items.size;i++) {
721 var ele = items.get(i);
722 if (ele.props.has_key("* prop")) {
726 this.out_children.add(ele);
729 itms += "\n"+ this.pad + "]" + "\n";
730 //this.els.add(itms);
733 // finally output listeners...
735 public void xIncludeToString()