2937e9ef15797d2b0e4dc55303149858fcd9263a
[working/Evergreen.git] / Open-ILS / web / reports / oils_rpt_widget.js
1 /* --------------------------------------------------------------------- 
2         Represents a set of values, an inputWidget collects data and a 
3         multi-select displays the data and allows the user to remove items
4         --------------------------------------------------------------------- */
5 function oilsRptSetWidget(args) {
6         this.node = args.node;
7     this.seedValue = args.value;
8         this.inputWidget = new args.inputWidget(args);
9     this.readonly = Boolean(args.readonly);
10     this.asyncInput = args.async;
11         this.dest = elem('select',
12                 {multiple:'multiple','class':'oils_rpt_small_info_selector'});
13     this.dest.disabled = this.readonly;
14 }
15
16 oilsRptSetWidget.prototype.draw = function() {
17
18         this.addButton = elem('input',{type:'submit',value:"Add"})
19         this.delButton = elem('input',{type:'submit',value:"Del"})
20         this.addButton.disabled = this.readonly;
21         this.delButton.disabled = this.readonly;
22
23         var obj = this;
24         this.addButton.onclick = function() {
25                 obj.addDisplayItems(obj.inputWidget.getDisplayValue());
26         }
27
28         this.delButton.onclick = function(){obj.removeSelected()};
29
30         removeChildren(this.node);
31
32     var this_ = this;
33     var post_draw = function() {
34         // propagate the values from the input widget into the our display.
35         if (this_.inputWidget.seedValue)
36             this_.addButton.onclick();
37     }
38
39         this.inputWidget.draw(null, post_draw);
40         this.node.appendChild(elem('br'))
41         this.node.appendChild(this.addButton);
42         this.node.appendChild(this.delButton);
43         this.node.appendChild(elem('br'))
44         this.node.appendChild(this.dest);
45
46     if (!this.asyncInput) post_draw();
47
48 }
49
50 oilsRptSetWidget.prototype.addDisplayItems = function(list) {
51         if( list.constructor != Array ) list = [list];
52         for(var i = 0; i < list.length; i++) {
53                 var item = list[i];
54
55                 /* no dupes */
56                 var exists = false;
57                 iterate(this.dest.options, 
58                         function(o){if(o.getAttribute('value') == item.value) {exists = true; return;}});
59                 if(exists) continue;
60
61                 _debug('Inserting SetWidget values ' + js2JSON(item));
62                 insertSelectorVal(this.dest, -1, item.label, this.objToStr(item.value));
63         }
64 }
65
66 oilsRptSetWidget.prototype.removeSelected = function() {
67         oilsDelSelectedItems(this.dest);
68 }
69
70 oilsRptSetWidget.prototype.getValue = function() {
71         var vals = [];
72         var obj = this;
73         iterate(this.dest, function(i){vals.push(obj.strToObj(i.getAttribute('value')))});
74         return vals;
75 }
76
77 oilsRptSetWidget.prototype.objToStr = function(obj) {
78         if( typeof obj == 'string' ) return obj;
79         //return ':'+obj.transform+':'+obj.params[0];
80         var str = ':'+obj.transform;
81         for( var i = 0; i < obj.params.length; i++ ) 
82                 str += ':' + obj.params[i];
83         _debug("objToStr(): built string " + str);
84         return str;
85
86 }
87
88 oilsRptSetWidget.prototype.strToObj = function(str) {
89         if( str.match(/^:.*/) ) {
90                 var parts = str.split(/:/);
91                 _debug("strToObj(): " + str + ' : ' + parts);
92                 parts.shift();
93                 var tform = parts.shift();
94                 //var tform = str.replace(/^:(.*):.*/,'$1');
95                 //var param = str.replace(/^:.*:(.*)/,'$1');
96                 return { transform : tform, params : parts };
97         }
98         return str;
99 }
100
101
102 /* --------------------------------------------------------------------- 
103         represents a widget that has start and end values.  start and end
104         are gathered from start/end widgets
105         --------------------------------------------------------------------- */
106 function oilsRptBetweenWidget(args) {
107         this.node = args.node;
108         var seedValue = args.value;
109         if (seedValue) args.value = seedValue[0];
110         this.startWidget = new args.startWidget(args);
111         if (seedValue) args.value = seedValue[1];
112         this.endWidget = new args.endWidget(args);
113 }
114 oilsRptBetweenWidget.prototype.draw = function() {
115         removeChildren(this.node);
116         this.startWidget.draw();
117         this.node.appendChild(elem('hr'));
118         this.node.appendChild(elem('div',
119                 {style:'text-align:center;width:100%;font-weight:bold'},' - And - '));
120         this.node.appendChild(elem('hr'));
121         this.endWidget.draw();
122 }
123 oilsRptBetweenWidget.prototype.getValue = function() {
124         return [
125                 this.startWidget.getValue(),
126                 this.endWidget.getValue()
127         ];
128 }
129
130
131
132
133 /* --------------------------------------------------------------------- 
134         ATOMIC WIDGETS
135         --------------------------------------------------------------------- */
136
137
138 /* --------------------------------------------------------------------- 
139         Atomic text input widget
140         --------------------------------------------------------------------- */
141 function oilsRptTextWidget(args) {
142         this.node = args.node;
143     this.seedValue = args.value;
144         this.dest = elem('input',{type:'text',size:12});
145     this.dest.disabled = Boolean(args.readonly);
146         oilsRptMonitorWidget(this.dest);
147 }
148 oilsRptTextWidget.prototype.draw = function() {
149         this.node.appendChild(this.dest);
150     if (this.seedValue) {
151         this.dest.value = this.seedValue;
152         this.dest.onchange(); // validation
153     }
154 }
155
156 /* returns the "real" value for the widget */
157 oilsRptTextWidget.prototype.getValue = function() {
158         return this.dest.value;
159 }
160
161 /* returns the label and "real" value for the widget */
162 oilsRptTextWidget.prototype.getDisplayValue = function() {
163         return { label : this.getValue(), value : this.getValue() };
164 }
165
166
167
168 /* --------------------------------------------------------------------- 
169         Atomic bool input widget
170         --------------------------------------------------------------------- */
171 function oilsRptBoolWidget(args) {
172         this.node = args.node;
173     this.seedValue = args.value;
174         this.selector = elem('select');
175         insertSelectorVal(this.selector, -1,'True','t');
176         insertSelectorVal(this.selector, -1,'False','f');
177     this.selector.disabled = Boolean(args.readonly);
178 }
179
180 oilsRptBoolWidget.prototype.draw = function() {
181         this.node.appendChild(this.selector);
182     if (this.seedValue)  
183         setSelector(this.selector, this.seedValue);
184 }
185
186 /* returns the "real" value for the widget */
187 oilsRptBoolWidget.prototype.getValue = function() {
188         return getSelectorVal(this.selector);
189 }
190
191 /* returns the label and "real" value for the widget */
192 oilsRptBoolWidget.prototype.getDisplayValue = function() {
193         var val = getSelectorVal(this.selector);
194         var label = 'True';
195         if (val == 'f') labal = 'False';
196         return { label : label, value : val };
197 }
198
199
200 /* If you monitor a text widget with this function, it 
201         will style the input differently to indicate the
202         field needs data.  If a regex is provided, it will
203         style the field differently until the data matches 
204         the regex.  The style comes from OILS_RPT_INVALID_DATA. */
205 function oilsRptMonitorWidget(input, regex) {
206         addCSSClass(input, OILS_RPT_INVALID_DATA);
207         var func = function() {
208                 var val = input.value;
209                 if(!val) {
210                         addCSSClass(input, OILS_RPT_INVALID_DATA);
211                 } else {
212                         if( regex ) {
213                                 if( val && val.match(regex) ) 
214                                         removeCSSClass(input, OILS_RPT_INVALID_DATA);
215                                 else
216                                         addCSSClass(input, OILS_RPT_INVALID_DATA);
217                         } else {
218                                 removeCSSClass(input, OILS_RPT_INVALID_DATA);
219                         }
220                 }
221         }
222
223         input.onkeyup = func;
224         input.onchange = func;
225 }
226
227
228
229
230 /* --------------------------------------------------------------------- 
231         Atomic calendar widget
232         --------------------------------------------------------------------- */
233 function oilsRptCalWidget(args) {
234         this.node = args.node;
235         this.calFormat = args.calFormat;
236         this.input = elem('input',{type:'text',size:12});
237     this.seedValue = args.value;
238     this.input.disabled = Boolean(args.readonly);
239
240         oilsRptMonitorWidget(this.input, args.regex);
241
242         if( args.inputSize ) {
243                 this.input.setAttribute('size',args.inputSize);
244                 this.input.setAttribute('maxlength',args.inputSize);
245         }
246 }
247
248 oilsRptCalWidget.prototype.draw = function() {
249         this.button = DOM.generic_calendar_button.cloneNode(true);
250         this.button.id = oilsNextId();
251         this.input.id = oilsNextId();
252
253         this.node.appendChild(this.button);
254         this.node.appendChild(this.input);
255         unHideMe(this.button);
256
257         _debug('making calendar widget with format ' + this.calFormat);
258
259         Calendar.setup({
260                 inputField      : this.input.id,
261                 ifFormat                : this.calFormat,
262                 button          : this.button.id,
263                 align                   : "Tl", 
264                 singleClick     : true
265         });
266
267     if (this.seedValue) {
268         this.input.value = this.seedValue;
269         this.input.onchange(); // validation
270     }
271 }
272
273 oilsRptCalWidget.prototype.getValue = function() {
274         return this.input.value;
275 }
276
277 oilsRptCalWidget.prototype.getDisplayValue = function() {
278         return { label : this.getValue(), value : this.getValue() };
279 }
280
281
282 /* --------------------------------------------------------------------- 
283         Atomic org widget
284         --------------------------------------------------------------------- */
285 function oilsRptOrgSelector(args) {
286         this.node = args.node;
287     this.seedValue = args.value;
288         this.selector = elem('select',
289                 {multiple:'multiple','class':'oils_rpt_small_info_selector'});
290     this.selector.disabled = Boolean(args.readonly);
291 }
292
293 oilsRptOrgSelector.prototype.draw = function(org) {
294         if(!org) org = globalOrgTree;
295         var opt = insertSelectorVal( this.selector, -1, 
296                 org.shortname(), org.id(), null, findOrgDepth(org) );
297         if( org.id() == oilsRptCurrentOrg && !this.seedValue)
298                 opt.selected = true;
299         
300         /* sometimes we need these choices 
301         if( !isTrue(findOrgType(org.ou_type()).can_have_vols()) )
302                 opt.disabled = true;
303                 */
304
305         if( org.children() ) {
306                 for( var c = 0; c < org.children().length; c++ )
307                         this.draw(org.children()[c]);
308         }
309         this.node.appendChild(this.selector);
310
311     if (this.seedValue) {
312         if (dojo.isArray(this.seedValue)) {
313             for (var i = 0; i < this.selector.options.length; i++) {
314                 var opt = this.selector.options[i];
315                 if (this.seedValue.indexOf(opt.value) > -1)
316                     opt.selected = true;
317             }
318         } else {
319             setSelector(this.selector, this.seedValue);
320         }
321     }
322 }
323
324 oilsRptOrgSelector.prototype.getValue = function() {
325         var vals = [];
326         iterate(this.selector, /* XXX this.selector.options?? */
327                 function(o){
328                         if( o.selected )
329                                 vals.push(o.getAttribute('value'))
330                 }
331         );
332         return vals;
333 }
334
335 oilsRptOrgSelector.prototype.getDisplayValue = function() {
336         var vals = [];
337         iterate(this.selector,
338                 function(o){
339                         if( o.selected )
340                                 vals.push({ label : o.innerHTML, value : o.getAttribute('value')});
341                 }
342         );
343         return vals;
344 }
345
346
347 /* --------------------------------------------------------------------- 
348         Atomic age widget
349         --------------------------------------------------------------------- */
350 function oilsRptAgeWidget(args) {
351         this.node = args.node;
352     this.seedValue = args.value;
353         this.count = elem('select');
354         this.type = elem('select');
355     this.count.disabled = Boolean(args.readonly);
356     this.type.disabled = Boolean(args.readonly);
357 }
358
359 oilsRptAgeWidget.prototype.draw = function() {
360
361         //insertSelectorVal(this.count, -1, ' -- Select One -- ', '');
362         for( var i = 1; i < 25; i++ )
363                 insertSelectorVal(this.count, -1, i, i);
364
365         //insertSelectorVal(this.type, -1, ' -- Select One -- ', '');
366         insertSelectorVal(this.type, -1, rpt_strings.WIDGET_DAYS, 'days');
367         insertSelectorVal(this.type, -1, rpt_strings.WIDGET_MONTHS, 'months');
368         insertSelectorVal(this.type, -1, rpt_strings.WIDGET_YEARS, 'years');
369         this.node.appendChild(this.count);
370         this.node.appendChild(this.type);
371
372     if (this.seedValue) { 
373         // e.g. "2months"
374         var count = this.seedValue.match(/(\d+)([^\d]+)/)[1];
375         var type = this.seedValue.match(/(\d+)([^\d]+)/)[2];
376         setSelector(this.count, count);
377         setSelector(this.type, type);
378     }
379 }
380
381 oilsRptAgeWidget.prototype.getValue = function() {
382         var count = getSelectorVal(this.count);
383         var type = getSelectorVal(this.type);
384         return count+''+type;
385 }
386
387 oilsRptAgeWidget.prototype.getDisplayValue = function() {
388         var val = { value : this.getValue() };
389         var label = getSelectorVal(this.count) + ' ';
390         for( var i = 0; i < this.type.options.length; i++ ) {
391                 var opt = this.type.options[i];
392                 if( opt.selected )
393                         label += opt.innerHTML;
394         }
395         val.label = label;
396         return val;
397 }
398
399
400
401 /* --------------------------------------------------------------------- 
402         Atomic substring picker
403         --------------------------------------------------------------------- */
404 function oilsRptSubstrWidget(args) {
405         this.node = args.node
406     this.seedValue = args.value;
407         this.data = elem('input',{type:'text',size:12})
408         this.offset = elem('input',{type:'text',size:5})
409         this.length = elem('input',{type:'text',size:5})
410     this.offset.disabled = Boolean(args.readonly);
411     this.length.disabled = Boolean(args.readonly);
412 }
413
414 oilsRptSubstrWidget.prototype.draw = function() {
415         this.node.appendChild(text('string: '))
416         this.node.appendChild(this.data);
417         this.node.appendChild(elem('br'));
418         this.node.appendChild(text('offset: '))
419         this.node.appendChild(this.offset);
420         this.node.appendChild(elem('br'));
421         this.node.appendChild(text('length: '))
422         this.node.appendChild(this.length);
423
424     if (this.seedValue) { 
425         // TODO: unested; substring currently not supported.
426         this.data.value = this.seedValue[0];
427         this.offset.value = this.seedValue[1];
428         this.length.value = this.seedValue[2];
429     }
430 }
431
432 oilsRptSubstrWidget.prototype.getValue = function() {
433         return {
434                 transform : 'substring',
435                 params : [ this.data.value, this.offset.value, this.length.value ]
436         };
437 }
438
439 oilsRptSubstrWidget.prototype.getDisplayValue = function() {
440         return {
441                 label : this.data.value + ' : ' + this.offset.value + ' : ' + this.length.value,
442                 value : this.getValue()
443         };
444 }
445
446
447 /* --------------------------------------------------------------------- 
448         Atomic number picker
449         --------------------------------------------------------------------- */
450 function oilsRptNumberWidget(args) {
451         this.node = args.node;
452     this.seedValue = args.value;
453         this.size = args.size || 32;
454         this.start = args.start;
455         this.selector = elem('select');
456     this.selector.disabled = Boolean(args.readonly);
457 }
458 oilsRptNumberWidget.prototype.draw = function() {
459         //insertSelectorVal(this.selector, -1, ' -- Select One -- ', '');
460         for( var i = this.start; i < (this.size + this.start); i++ )
461                 insertSelectorVal(this.selector, -1, i, i);
462         this.node.appendChild(this.selector);
463         var obj = this;
464
465     if (this.seedValue)
466         setSelector(this.selector, this.seedValue);
467 }
468
469 oilsRptNumberWidget.prototype.getValue = function() {
470         return getSelectorVal(this.selector);
471 }
472
473 oilsRptNumberWidget.prototype.getDisplayValue = function() {
474         return { label : this.getValue(), value : this.getValue() };
475 }
476
477 function oilsRptNullWidget(args) {
478     this.node = args.node;
479     this.type = args.type;
480 }
481 oilsRptNullWidget.prototype.draw = function() {}
482 oilsRptNullWidget.prototype.getValue = function() {
483     return null;
484 }
485
486 function oilsRptTemplateWidget(args) {
487     this.node = args.node;
488     this.value = args.value;
489 }
490 oilsRptTemplateWidget.prototype.draw = function() {
491     this.node.appendChild(text(''+this.value));
492 }
493
494 /* --------------------------------------------------------------------- 
495         Relative dates widget
496         -------------------------------------------------------------------- */
497 function oilsRptTruncPicker(args) {
498         this.node = args.node;
499         this.type = args.type;
500     this.seedValue = args.value;
501         this.realSpan = elem('span');
502         this.relSpan = elem('span');
503         hideMe(this.relSpan);
504         args.node = this.realSpan;
505         this.calWidget = new oilsRptCalWidget(args);
506         args.node = this.node;
507
508         this.selector = elem('select');
509         insertSelectorVal(this.selector,-1,rpt_strings.WIDGET_REAL_DATE,1);
510         insertSelectorVal(this.selector,-1,rpt_strings.WIDGET_RELATIVE_DATE,2);
511     this.selector.disabled = Boolean(args.readonly);
512
513         this.numberPicker = 
514                 new oilsRptNumberWidget(
515             {node:this.relSpan,size:90,start:1,readonly:args.readonly});
516
517         this.label = 'Day(s)';
518         if(this.type == 'month') this.label = rpt_strings.WIDGET_MONTHS;
519         if(this.type == 'quarter') this.label = rpt_strings.WIDGET_QUARTERS;
520         if(this.type == 'year') this.label = rpt_strings.WIDGET_YEARS;
521         if(this.type == 'date') this.label = rpt_strings.WIDGET_DAYS;
522 }
523
524 oilsRptTruncPicker.prototype.draw = function() {
525         this.node.appendChild(this.selector);
526         this.node.appendChild(this.realSpan);
527         this.node.appendChild(this.relSpan);
528         this.calWidget.draw();
529         this.numberPicker.draw();
530         this.relSpan.appendChild(text(this.label+' ago'));
531
532         var obj = this;
533         this.selector.onchange = function() {
534                 if( getSelectorVal(obj.selector) == 1 ) {
535                         unHideMe(obj.realSpan);
536                         hideMe(obj.relSpan);
537                 } else {
538                         unHideMe(obj.relSpan);
539                         hideMe(obj.realSpan);
540                 }
541         }
542
543     if (seed = this.seedValue) {
544         if (typeof seed == 'string') {
545             this.calWidget.value = seed;
546         } else {
547             // relative date transform
548             if (seed.transform.match(/relative/)) {
549                 setSelector(this.selector, 2)
550                 setSelector(this.numberPicker.selector, 
551                     Math.abs(seed.params[0]));
552                             unHideMe(this.relSpan);
553                             hideMe(this.realSpan);
554             }
555         }
556     }
557 }
558
559 oilsRptTruncPicker.prototype.getValue = function() {
560         if( getSelectorVal(this.selector) == 2) {
561                 var val = this.numberPicker.getValue();
562                 var tform = 'relative_' + this.type;
563                 return { transform : tform, params : ['-'+val] };
564         }
565         return this.calWidget.getValue();
566 }
567
568 oilsRptTruncPicker.prototype.getDisplayValue = function() {
569         if( getSelectorVal(this.selector) == 2) {
570                 var num = this.numberPicker.getValue();
571                 return { label : num +' '+this.label+' ago', value : this.getValue() };
572         }
573         return this.calWidget.getDisplayValue();
574 }
575
576
577 /* --------------------------------------------------------------------- 
578         Atomic remote object picker
579         --------------------------------------------------------------------- */
580
581 function oilsRptRemoteWidget(args) {
582         this.node       = args.node;
583         this.class      = args.class;
584         this.field      = args.field;
585         this.column = args.column;
586     this.seedValue = args.value;
587         this.source = elem('select',
588                 {multiple:'multiple','class':'oils_rpt_small_info_selector'});
589     this.source.disabled = Boolean(args.readonly);
590 }
591
592 // selected is unused
593 oilsRptRemoteWidget.prototype.draw = function(selected, callback) {
594         var orgcol;
595         iterate(oilsIDL[this.class].fields,
596                 function(i) {
597                         if(i.type == 'link' && i.class == 'aou') 
598                                 orgcol = i.name;
599                 }
600         );
601
602         if(orgcol) _debug("found org column for remote widget: " + orgcol);
603
604         var orgs = [];
605         iterate(oilsRptMyOrgs,function(i){orgs.push(i.id());});
606         var req = new Request(OILS_RPT_MAGIC_FETCH, SESSION, {
607                 hint:this.class,
608                 org_column : orgcol,
609                 org : orgs
610         }); 
611
612         var obj = this;
613         this.node.appendChild(this.source);
614         req.callback(function(r){
615         // render selects values from seed data if necessary
616         obj.render(r.getResultObject());
617         if (callback) {
618             // presence of a callback suggests there is a container widget
619             // (e.g. SetWidget) for tracking our values.  Callback lets 
620             // the container extract the selected values, then we deselect
621             // in the input widget (us) since it's redundant.
622             callback();
623             dojo.forEach(obj.source.options, function(o) {
624                 o.selected = false;
625             });
626         }
627     });
628         req.send();
629 }
630
631 oilsRptRemoteWidget.prototype.render = function(objs) {
632         var selector = this.field.selector;
633         objs.sort(
634                 function(a,b){
635                         if (a[selector]() > b[selector]())
636                                 return 1;
637                         else
638                                 return -1;
639                 });
640         for( var i = 0; i < objs.length; i++ ) {
641                 var obj = objs[i];
642                 var label = obj[this.field.selector]();
643                 var value = obj[this.column]();
644                 _debug("inserted remote object "+label + ' : ' + value);
645                 var opt = insertSelectorVal(this.source, -1, label, value);
646         if (this.seedValue && (
647                 this.seedValue.indexOf(Number(value)) > -1 
648                 || this.seedValue.indexOf(''+value) > -1
649             )) {
650             opt.selected = true;
651         }
652         }
653 }
654
655 oilsRptRemoteWidget.prototype.getDisplayValue = function() {
656         var vals = [];
657         iterate(this.source,
658                 function(o){
659                         if( o.selected )
660                                 vals.push({ label : o.innerHTML, value : o.getAttribute('value')});
661                 }
662         );
663         return vals;
664 }
665
666 oilsRptRemoteWidget.prototype.getValue = function() {
667         var vals = [];
668         iterate(this.source,
669                 function(o){
670                         if( o.selected )
671                                 vals.push(o.getAttribute('value'))
672                 }
673         );
674         return vals;
675 }
676
677
678
679
680 /* --------------------------------------------------------------------- 
681         CUSTOM WIDGETS
682         --------------------------------------------------------------------- */
683
684 /* --------------------------------------------------------------------- 
685         custom my-orgs picker 
686         --------------------------------------------------------------------- */
687 function oilsRptMyOrgsWidget(node, orgid, maxorg) {
688         _debug('fetching my orgs with max org of ' + maxorg);
689         this.node = node;
690         this.orgid = orgid;
691         this.maxorg = maxorg || 1;
692         this.active = true;
693         if( maxorg < 1 ) {
694                 this.node.disabled = true;
695                 this.active = false;
696         }
697 }
698
699 oilsRptMyOrgsWidget.prototype.draw = function() {
700         if(!oilsRptMyOrgs) {
701                 var req = new Request(OILS_RPT_FETCH_ORG_FULL_PATH, this.orgid);
702                 var obj = this;
703                 req.callback(
704                         function(r) { obj.drawWidget(r.getResultObject()); }
705                 );
706                 req.send();
707         } else {
708                 this.drawWidget(oilsRptMyOrgs);
709         }
710 }
711
712 oilsRptMyOrgsWidget.prototype.drawWidget = function(orglist) {
713         var sel = this.node;
714         var started = false;
715         oilsRptMyOrgs = orglist;
716         for( var i = 0; i < orglist.length; i++ ) {
717                 var org = orglist[i];
718                 var opt = insertSelectorVal( this.node, -1, 
719                         org.name(), org.id(), null, findOrgDepth(org) );
720                 if( org.id() == this.orgid )
721                         opt.selected = true;
722                 if(!started) {
723                         if( org.id() == this.maxorg ) 
724                                 started = true;
725                         else opt.disabled = true;
726                 }
727         }
728 }
729
730 oilsRptMyOrgsWidget.prototype.getValue = function() {
731         return getSelectorVal(this.node);
732 }
733
734