]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/xul/staff_client/chrome/content/util/list.js
column persistence
[Evergreen.git] / Open-ILS / xul / staff_client / chrome / content / util / list.js
1 dump('entering util.list.js\n');
2
3 if (typeof main == 'undefined') main = {};
4 util.list = function (id) {
5
6         this.node = document.getElementById(id);
7
8         if (!this.node) throw('Could not find element ' + id);
9         switch(this.node.nodeName) {
10                 case 'listbox' : 
11                 case 'tree' : break;
12                 case 'richlistbox' :
13                         throw(this.node.nodeName + ' not yet supported'); break;
14                 default: throw(this.node.nodeName + ' not supported'); break;
15         }
16
17         JSAN.use('util.error'); this.error = new util.error();
18
19         return this;
20 };
21
22 util.list.prototype = {
23
24         'row_count' : { 'total' : 0, 'fleshed' : 0 },
25
26         'init' : function (params) {
27
28                 var obj = this;
29
30                 JSAN.use('util.widgets');
31
32                 if (typeof params.map_row_to_column == 'function') obj.map_row_to_column = params.map_row_to_column;
33                 if (typeof params.retrieve_row == 'function') obj.retrieve_row = params.retrieve_row;
34
35                 obj.prebuilt = false;
36                 if (typeof params.prebuilt != 'undefined') obj.prebuilt = params.prebuilt;
37
38                 if (typeof params.columns == 'undefined') throw('util.list.init: No columns');
39                 obj.columns = params.columns;
40
41                 switch(obj.node.nodeName) {
42                         case 'tree' : obj._init_tree(params); break;
43                         case 'listbox' : obj._init_listbox(params); break;
44                         default: throw('NYI: Need ._init() for ' + obj.node.nodeName); break;
45                 }
46         },
47
48         'register_all_fleshed_callback' : function(f) {
49                 this.on_all_fleshed = f;
50         },
51
52         '_init_tree' : function (params) {
53                 var obj = this;
54                 if (this.prebuilt) {
55                 
56                         this.treechildren = this.node.lastChild;        
57                 
58                 } else {
59                         var treecols = document.createElement('treecols');
60                         this.node.appendChild(treecols);
61
62                         for (var i = 0; i < this.columns.length; i++) {
63                                 var treecol = document.createElement('treecol');
64                                 for (var j in this.columns[i]) {
65                                         treecol.setAttribute(j,this.columns[i][j]);
66                                 }
67                                 treecols.appendChild(treecol);
68                                 var splitter = document.createElement('splitter');
69                                 splitter.setAttribute('class','tree-splitter');
70                                 treecols.appendChild(splitter);
71                         }
72
73                         var treechildren = document.createElement('treechildren');
74                         this.node.appendChild(treechildren);
75                         this.treechildren = treechildren;
76                 }
77                 if (typeof params.on_select == 'function') {
78                         this.node.addEventListener(
79                                 'select',
80                                 params.on_select,
81                                 false
82                         );
83                 }
84                 if (typeof params.on_click == 'function') {
85                         this.node.addEventListener(
86                                 'click',
87                                 params.on_click,
88                                 false
89                         );
90                 }
91                 /*
92                 this.node.addEventListener(
93                         'mousemove',
94                         function(ev) { obj.detect_visible(); },
95                         false
96                 );
97                 */
98                 this.node.addEventListener(
99                         'keypress',
100                         function(ev) { obj.auto_retrieve(); },
101                         false
102                 );
103                 this.node.addEventListener(
104                         'click',
105                         function(ev) { obj.auto_retrieve(); },
106                         false
107                 );
108                 window.addEventListener(
109                         'resize',
110                         function(ev) { obj.auto_retrieve(); },
111                         false
112                 );
113                 /* FIXME -- find events on scrollbar to trigger this */
114                 obj.detect_visible_polling();   
115                 /*
116                 var scrollbar = document.getAnonymousNodes( document.getAnonymousNodes(this.node)[1] )[1];
117                 var slider = document.getAnonymousNodes( scrollbar )[2];
118                 alert('scrollbar = ' + scrollbar.nodeName + ' grippy = ' + slider.nodeName);
119                 scrollbar.addEventListener('click',function(){alert('sb click');},false);
120                 scrollbar.addEventListener('command',function(){alert('sb command');},false);
121                 scrollbar.addEventListener('scroll',function(){alert('sb scroll');},false);
122                 slider.addEventListener('click',function(){alert('slider click');},false);
123                 slider.addEventListener('command',function(){alert('slider command');},false);
124                 slider.addEventListener('scroll',function(){alert('slider scroll');},false);
125                 */
126                 this.node.addEventListener('scroll',function(){ obj.auto_retrieve(); },false);
127
128                 this.restores_columns(params);
129         },
130
131         '_init_listbox' : function (params) {
132                 if (this.prebuilt) {
133                 } else {
134                         var listhead = document.createElement('listhead');
135                         this.node.appendChild(listhead);
136
137                         var listcols = document.createElement('listcols');
138                         this.node.appendChild(listcols);
139
140                         for (var i = 0; i < this.columns.length; i++) {
141                                 var listheader = document.createElement('listheader');
142                                 listhead.appendChild(listheader);
143                                 var listcol = document.createElement('listcol');
144                                 listcols.appendChild(listcol);
145                                 for (var j in this.columns[i]) {
146                                         listheader.setAttribute(j,this.columns[i][j]);
147                                         listcol.setAttribute(j,this.columns[i][j]);
148                                 };
149                         }
150                 }
151         },
152
153         'save_columns' : function (params) {
154                 var obj = this;
155                 switch (this.node.nodeName) {
156                         case 'tree' : this._save_columns_tree(params); break;
157                         default: throw('NYI: Need .save_columns() for ' + this.node.nodeName); break;
158                 }
159         },
160
161         '_save_columns_tree' : function (params) {
162                 var obj = this;
163                 try {
164                         var id = obj.node.getAttribute('id'); if (!id) {
165                                 alert("FIXME: The columns for this list cannot be saved because the list has no id.");
166                                 return;
167                         }
168                         var my_cols = {};
169                         var nl = obj.node.getElementsByTagName('treecol');
170                         for (var i = 0; i < nl.length; i++) {
171                                 var col = nl[i];
172                                 var col_id = col.getAttribute('id');
173                                 if (!col_id) {
174                                         alert('FIXME: A column in this list does not have an id and cannot be saved');
175                                         continue;
176                                 }
177                                 var col_hidden = col.getAttribute('hidden'); 
178                                 var col_width = col.getAttribute('width'); 
179                                 var col_ordinal = col.getAttribute('ordinal'); 
180                                 my_cols[ col_id ] = { 'hidden' : col_hidden, 'width' : col_width, 'ordinal' : col_ordinal };
181                         }
182                         netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
183                         JSAN.use('util.file'); var file = new util.file('tree_columns_for_'+window.escape(id));
184                         file.set_object(my_cols);
185                         file.close();
186                         alert('Columns saved.');
187                 } catch(E) {
188                         obj.error.standard_unexpected_error_alert('_save_columns_tree',E);
189                 }
190         },
191
192         'restores_columns' : function (params) {
193                 var obj = this;
194                 switch (this.node.nodeName) {
195                         case 'tree' : this._restores_columns_tree(params); break;
196                         default: throw('NYI: Need .restores_columns() for ' + this.node.nodeName); break;
197                 }
198         },
199
200         '_restores_columns_tree' : function (params) {
201                 var obj = this;
202                 try {
203                         var id = obj.node.getAttribute('id'); if (!id) {
204                                 alert("FIXME: The columns for this list cannot be restored because the list has no id.");
205                                 return;
206                         }
207
208                         netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
209                         JSAN.use('util.file'); var file = new util.file('tree_columns_for_'+window.escape(id));
210                         if (file._file.exists()) {
211                                 var my_cols = file.get_object(); file.close();
212                                 var nl = obj.node.getElementsByTagName('treecol');
213                                 for (var i = 0; i < nl.length; i++) {
214                                         var col = nl[i];
215                                         var col_id = col.getAttribute('id');
216                                         if (!col_id) {
217                                                 alert('FIXME: A column in this list does not have an id and cannot be saved');
218                                                 continue;
219                                         }
220                                         if (typeof my_cols[col_id] != 'undefined') {
221                                                 col.setAttribute('hidden',my_cols[col_id].hidden); 
222                                                 col.setAttribute('width',my_cols[col_id].width); 
223                                                 col.setAttribute('ordinal',my_cols[col_id].ordinal); 
224                                         } else {
225                                                 alert('FIXME: Column ' + col_id + ' did not have a saved state.');
226                                         }
227                                 }
228                         }
229                 } catch(E) {
230                         obj.error.standard_unexpected_error_alert('_restore_columns_tree',E);
231                 }
232         },
233
234         'clear' : function (params) {
235                 var obj = this;
236                 switch (this.node.nodeName) {
237                         case 'tree' : this._clear_tree(params); break;
238                         case 'listbox' : this._clear_listbox(params); break;
239                         default: throw('NYI: Need .clear() for ' + this.node.nodeName); break;
240                 }
241                 this.error.sdump('D_LIST','Clearing list ' + this.node.getAttribute('id') + '\n');
242                 this.row_count.total = 0;
243                 this.row_count.fleshed = 0;
244                 if (typeof obj.on_all_fleshed == 'function') {
245                         setTimeout( function() { obj.on_all_fleshed(); }, 0 );
246                 }
247         },
248
249         '_clear_tree' : function(params) {
250                 var obj = this;
251                 if (obj.error.sdump_levels.D_LIST_DUMP_ON_CLEAR) {
252                         obj.error.sdump('D_LIST_DUMP_ON_CLEAR',obj.dump());
253                 }
254                 if (obj.error.sdump_levels.D_LIST_DUMP_WITH_KEYS_ON_CLEAR) {
255                         obj.error.sdump('D_LIST_DUMP_WITH_KEYS_ON_CLEAR',obj.dump_with_keys());
256                 }
257                 while (obj.treechildren.lastChild) obj.treechildren.removeChild( obj.treechildren.lastChild );
258         },
259
260         '_clear_listbox' : function(params) {
261                 var obj = this;
262                 var items = [];
263                 var nl = this.node.getElementsByTagName('listitem');
264                 for (var i = 0; i < nl.length; i++) {
265                         items.push( nl[i] );
266                 }
267                 for (var i = 0; i < items.length; i++) {
268                         this.node.removeChild(items[i]);
269                 }
270         },
271
272         'append' : function (params) {
273                 var rnode;
274                 var obj = this;
275                 switch (this.node.nodeName) {
276                         case 'tree' : rnode = this._append_to_tree(params); break;
277                         case 'listbox' : rnode = this._append_to_listbox(params); break;
278                         default: throw('NYI: Need .append() for ' + this.node.nodeName); break;
279                 }
280                 if (rnode && params.attributes) {
281                         for (var i in params.attributes) {
282                                 rnode.setAttribute(i,params.attributes[i]);
283                         }
284                 }
285                 this.row_count.total++;
286                 if (this.row_count.fleshed == this.row_count.total) {
287                         if (typeof this.on_all_fleshed == 'function') {
288                                 setTimeout( function() { obj.on_all_fleshed(); }, 0 );
289                         }
290                 }
291                 return rnode;
292         },
293
294         '_append_to_tree' : function (params) {
295
296                 var obj = this;
297
298                 if (typeof params.row == 'undefined') throw('util.list.append: Object must contain a row');
299
300                 var s = ('util.list.append: params = ' + (params) + '\n');
301
302                 var treechildren_node = this.treechildren;
303
304                 if (params.node && params.node.nodeName == 'treeitem') {
305                         params.node.setAttribute('container','true'); /* params.node.setAttribute('open','true'); */
306                         if (params.node.lastChild.nodeName == 'treechildren') {
307                                 treechildren_node = params.node.lastChild;
308                         } else {
309                                 treechildren_node = document.createElement('treechildren');
310                                 params.node.appendChild(treechildren_node);
311                         }
312                 }
313
314                 var treeitem = document.createElement('treeitem');
315                 treeitem.setAttribute('retrieve_id',params.retrieve_id);
316                 treechildren_node.appendChild( treeitem );
317                 var treerow = document.createElement('treerow');
318                 treeitem.appendChild( treerow );
319                 treerow.setAttribute('retrieve_id',params.retrieve_id);
320
321                 s += ('tree = ' + this.node + '  treechildren = ' + treechildren_node + '\n');
322                 s += ('treeitem = ' + treeitem + '  treerow = ' + treerow + '\n');
323
324                 if (typeof params.retrieve_row == 'function' || typeof this.retrieve_row == 'function') {
325
326                         obj.put_retrieving_label(treerow);
327                         treerow.addEventListener(
328                                 'flesh',
329                                 function() {
330
331                                         if (treerow.getAttribute('retrieved') == 'true') return; /* already running */
332
333                                         treerow.setAttribute('retrieved','true');
334
335                                         //dump('fleshing = ' + params.retrieve_id + '\n');
336
337                                         function inc_fleshed() {
338                                                 if (treerow.getAttribute('fleshed') == 'true') return; /* already fleshed */
339                                                 treerow.setAttribute('fleshed','true');
340                                                 obj.row_count.fleshed++;
341                                                 if (obj.row_count.fleshed == obj.row_count.total) {
342                                                         if (typeof obj.on_all_fleshed == 'function') {
343                                                                 setTimeout( function() { obj.on_all_fleshed(); }, 0 );
344                                                         }
345                                                 }
346                                         }
347
348                                         params.row_node = treeitem;
349                                         params.on_retrieve = function(p) {
350                                                 try {
351                                                         p.row = params.row;
352                                                         obj._map_row_to_treecell(p,treerow);
353                                                         inc_fleshed();
354                                                 } catch(E) {
355                                                         alert('fixme2: ' + E);
356                                                 }
357                                         }
358
359                                         if (typeof params.retrieve_row == 'function') {
360
361                                                 params.retrieve_row( params );
362
363                                         } else if (typeof obj.retrieve_row == 'function') {
364
365                                                         obj.retrieve_row( params );
366
367                                         } else {
368                                         
369                                                         inc_fleshed();
370                                         }
371                                 },
372                                 false
373                         );
374                         /*
375                         setTimeout(
376                                 function() {
377                                         util.widgets.dispatch('flesh',treerow);
378                                 }, 0
379                         );
380                         */
381                 } else {
382                         obj.put_retrieving_label(treerow);
383                         treerow.addEventListener(
384                                 'flesh',
385                                 function() {
386                                         //dump('fleshing anon\n');
387                                         if (treerow.getAttribute('fleshed') == 'true') return; /* already fleshed */
388                                         obj._map_row_to_treecell(params,treerow);
389                                         treerow.setAttribute('retrieved','true');
390                                         treerow.setAttribute('fleshed','true');
391                                         obj.row_count.fleshed++;
392                                         if (obj.row_count.fleshed == obj.row_count.total) {
393                                                 if (typeof obj.on_all_fleshed == 'function') {
394                                                         setTimeout( function() { obj.on_all_fleshed(); }, 0 );
395                                                 }
396                                         }
397                                 },
398                                 false
399                         );
400                         /*
401                         setTimeout(
402                                 function() {
403                                         util.widgets.dispatch('flesh',treerow);
404                                 }, 0
405                         );
406                         */
407                 }
408                 this.error.sdump('D_LIST',s);
409
410                 setTimeout( function() { obj.auto_retrieve(); }, 0 );
411
412                 return treeitem;
413         },
414
415         'put_retrieving_label' : function(treerow) {
416                 var obj = this;
417                 try {
418                         /*
419                         var cols_idx = 0;
420                         dump('put_retrieving_label.  columns = ' + js2JSON(obj.columns) + '\n');
421                         while( obj.columns[cols_idx] && obj.columns[cols_idx].hidden && obj.columns[cols_idx].hidden == 'true') {
422                                 dump('\t' + cols_idx);
423                                 var treecell = document.createElement('treecell');
424                                 treerow.appendChild(treecell);
425                                 cols_idx++;
426                         }
427                         */
428                         for (var i = 0; i < obj.columns.length; i++) {
429                         var treecell = document.createElement('treecell'); treecell.setAttribute('label','Retrieving...');
430                         treerow.appendChild(treecell);
431                         }
432                         /*
433                         dump('\t' + cols_idx + '\n');
434                         */
435                 } catch(E) {
436                         alert(E);
437                 }
438         },
439
440         'detect_visible' : function() {
441                 var obj = this;
442                 try {
443                         //dump('detect_visible  obj.node = ' + obj.node + '\n');
444                         /* FIXME - this is a hack.. if the implementation of tree changes, this could break */
445                         var scrollbar = document.getAnonymousNodes( document.getAnonymousNodes(obj.node)[1] )[1];
446                         var curpos = scrollbar.getAttribute('curpos');
447                         var maxpos = scrollbar.getAttribute('maxpos');
448                         //alert('curpos = ' + curpos + ' maxpos = ' + maxpos + ' obj.curpos = ' + obj.curpos + ' obj.maxpos = ' + obj.maxpos + '\n');
449                         if ((curpos != obj.curpos) || (maxpos != obj.maxpos)) {
450                                 if ( obj.auto_retrieve() > 0 ) {
451                                         obj.curpos = curpos; obj.maxpos = maxpos;
452                                 }
453                         }
454                 } catch(E) { obj.error.sdump('D_ERROR',E); }
455         },
456
457         'detect_visible_polling' : function() {
458                 try {
459                         //alert('detect_visible_polling');
460                         var obj = this;
461                         obj.detect_visible();
462                         setTimeout(function() { try { obj.detect_visible_polling(); } catch(E) { alert(E); } },2000);
463                 } catch(E) {
464                         alert(E);
465                 }
466         },
467
468
469         'auto_retrieve' : function(params) {
470                 var obj = this;
471                 switch (this.node.nodeName) {
472                         case 'tree' : obj._auto_retrieve_tree(params); break;
473                         default: throw('NYI: Need .auto_retrieve() for ' + obj.node.nodeName); break;
474                 }
475         },
476
477         '_auto_retrieve_tree' : function (params) {
478                 var obj = this;
479                 if (!obj.auto_retrieve_in_progress) {
480                         obj.auto_retrieve_in_progress = true;
481                         setTimeout(
482                                 function() {
483                                         try {
484                                                         //alert('auto_retrieve\n');
485                                                         var count = 0;
486                                                         var startpos = obj.node.treeBoxObject.getFirstVisibleRow();
487                                                         var endpos = obj.node.treeBoxObject.getLastVisibleRow();
488                                                         if (startpos > endpos) endpos = obj.node.treeBoxObject.getPageLength();
489                                                         //dump('startpos = ' + startpos + ' endpos = ' + endpos + '\n');
490                                                         for (var i = startpos; i < endpos + 4; i++) {
491                                                                 try {
492                                                                         //dump('trying index ' + i + '\n');
493                                                                         var item = obj.node.contentView.getItemAtIndex(i).firstChild;
494                                                                         if (item && item.getAttribute('retrieved') != 'true' ) {
495                                                                                 //dump('\tgot an unfleshed item = ' + item + ' = ' + item.nodeName + '\n');
496                                                                                 util.widgets.dispatch('flesh',item); count++;
497                                                                         }
498                                                                 } catch(E) {
499                                                                         //dump(i + ' : ' + E + '\n');
500                                                                 }
501                                                         }
502                                                         obj.auto_retrieve_in_progress = false;
503                                                         return count;
504                                         } catch(E) { alert(E); }
505                                 }, 1
506                         );
507                 }
508         },
509
510         'full_retrieve' : function(params) {
511                 var obj = this;
512                 switch (this.node.nodeName) {
513                         case 'tree' : obj._full_retrieve_tree(params); break;
514                         default: throw('NYI: Need .full_retrieve() for ' + obj.node.nodeName); break;
515                 }
516         },
517
518         '_full_retrieve_tree' : function(params) {
519                 var obj = this;
520                 try {
521                         if (obj.row_count.total == obj.row_count.fleshed) {
522                                 //alert('Full retrieve... tree seems to be in sync\n' + js2JSON(obj.row_count));
523                                 if (typeof obj.on_all_fleshed == 'function') {
524                                         setTimeout( function() { obj.on_all_fleshed(); }, 0 );
525                                 } else {
526                                         alert('.full_retrieve called with no callback?');
527                                 }
528                         } else {
529                                 //alert('Full retrieve... syncing tree' + js2JSON(obj.row_count));
530                                 JSAN.use('util.widgets');
531                                 var nodes = obj.treechildren.childNodes;
532                                 for (var i = 0; i < nodes.length; i++) {
533                                         util.widgets.dispatch('flesh',nodes[i].firstChild);
534                                 }
535                         }
536                 } catch(E) {
537                         obj.error.standard_unexpected_error_alert('_full_retrieve_tree',E);
538                 }
539         },
540
541         '_append_to_listbox' : function (params) {
542
543                 var obj = this;
544
545                 if (typeof params.row == 'undefined') throw('util.list.append: Object must contain a row');
546
547                 var s = ('util.list.append: params = ' + (params) + '\n');
548
549                 var listitem = document.createElement('listitem');
550
551                 s += ('listbox = ' + this.node + '  listitem = ' + listitem + '\n');
552
553                 if (typeof params.retrieve_row == 'function' || typeof this.retrieve_row == 'function') {
554
555                         setTimeout(
556                                 function() {
557                                         listitem.setAttribute('retrieve_id',params.retrieve_id);
558                                         //FIXME//Make async and fire when row is visible in list
559                                         var row;
560
561                                         params.row_node = listitem;
562                                         params.on_retrieve = function(row) {
563                                                 params.row = row;
564                                                 obj._map_row_to_listcell(params,listitem);
565                                                 obj.node.appendChild( listitem );
566                                         }
567
568                                         if (typeof params.retrieve_row == 'function') {
569
570                                                 row = params.retrieve_row( params );
571
572                                         } else {
573
574                                                 if (typeof obj.retrieve_row == 'function') {
575
576                                                         row = obj.retrieve_row( params );
577
578                                                 }
579                                         }
580                                 }, 0
581                         );
582                 } else {
583                         this._map_row_to_listcell(params,listitem);
584                         this.node.appendChild( listitem );
585                 }
586
587                 this.error.sdump('D_LIST',s);
588                 return listitem;
589
590         },
591
592         '_map_row_to_treecell' : function(params,treerow) {
593                 var obj = this;
594                 var s = '';
595                 util.widgets.remove_children(treerow);
596                 for (var i = 0; i < this.columns.length; i++) {
597                         var treecell = document.createElement('treecell');
598                         var label = '';
599                         if (params.skip_columns && (params.skip_columns.indexOf(i) != -1)) {
600                                 treecell.setAttribute('label',label);
601                                 treerow.appendChild( treecell );
602                                 s += ('treecell = ' + treecell + ' with label = ' + label + '\n');
603                                 continue;
604                         }
605                         if (params.skip_all_columns_except && (params.skip_all_columns_except.indexOf(i) == -1)) {
606                                 treecell.setAttribute('label',label);
607                                 treerow.appendChild( treecell );
608                                 s += ('treecell = ' + treecell + ' with label = ' + label + '\n');
609                                 continue;
610                         }
611                         if (typeof params.map_row_to_column == 'function')  {
612
613                                 label = params.map_row_to_column(params.row,this.columns[i]);
614
615                         } else {
616
617                                 if (typeof this.map_row_to_column == 'function') {
618
619                                         label = this.map_row_to_column(params.row,this.columns[i]);
620
621                                 } else {
622
623                                         throw('No map_row_to_column function');
624
625                                 }
626                         }
627                         treecell.setAttribute('label',label);
628                         treerow.appendChild( treecell );
629                         s += ('treecell = ' + treecell + ' with label = ' + label + '\n');
630                 }
631                 this.error.sdump('D_LIST',s);
632         },
633
634         '_map_row_to_listcell' : function(params,listitem) {
635                 var obj = this;
636                 var s = '';
637                 for (var i = 0; i < this.columns.length; i++) {
638                         var value = '';
639                         if (typeof params.map_row_to_column == 'function')  {
640
641                                 value = params.map_row_to_column(params.row,this.columns[i]);
642
643                         } else {
644
645                                 if (typeof this.map_row_to_column == 'function') {
646
647                                         value = this.map_row_to_column(params.row,this.columns[i]);
648                                 }
649                         }
650                         if (typeof value == 'string' || typeof value == 'number') {
651                                 var listcell = document.createElement('listcell');
652                                 listcell.setAttribute('label',value);
653                                 listitem.appendChild(listcell);
654                                 s += ('listcell = ' + listcell + ' with label = ' + value + '\n');
655                         } else {
656                                 listitem.appendChild(value);
657                                 s += ('listcell = ' + value + ' is really a ' + value.nodeName + '\n');
658                         }
659                 }
660                 this.error.sdump('D_LIST',s);
661         },
662
663         'select_all' : function(params) {
664                 var obj = this;
665                 switch(this.node.nodeName) {
666                         case 'tree' : return this._select_all_from_tree(params); break;
667                         default: throw('NYI: Need ._select_all_from_() for ' + this.node.nodeName); break;
668                 }
669         },
670
671         '_select_all_from_tree' : function(params) {
672                 var obj = this;
673                 this.node.view.selection.selectAll();
674         },
675
676         'retrieve_selection' : function(params) {
677                 var obj = this;
678                 switch(this.node.nodeName) {
679                         case 'tree' : return this._retrieve_selection_from_tree(params); break;
680                         default: throw('NYI: Need ._retrieve_selection_from_() for ' + this.node.nodeName); break;
681                 }
682         },
683
684         '_retrieve_selection_from_tree' : function(params) {
685                 var obj = this;
686                 var list = [];
687                 var start = new Object();
688                 var end = new Object();
689                 var numRanges = this.node.view.selection.getRangeCount();
690                 for (var t=0; t<numRanges; t++){
691                         this.node.view.selection.getRangeAt(t,start,end);
692                         for (var v=start.value; v<=end.value; v++){
693                                 var i = this.node.contentView.getItemAtIndex(v);
694                                 list.push( i );
695                         }
696                 }
697                 return list;
698         },
699
700         'dump' : function(params) {
701                 var obj = this;
702                 switch(this.node.nodeName) {
703                         case 'tree' : return this._dump_tree(params); break;
704                         default: throw('NYI: Need .dump() for ' + this.node.nodeName); break;
705                 }
706         },
707
708         '_dump_tree' : function(params) {
709                 var obj = this;
710                 var dump = [];
711                 for (var i = 0; i < this.treechildren.childNodes.length; i++) {
712                         var row = [];
713                         var treeitem = this.treechildren.childNodes[i];
714                         var treerow = treeitem.firstChild;
715                         for (var j = 0; j < treerow.childNodes.length; j++) {
716                                 row.push( treerow.childNodes[j].getAttribute('label') );
717                         }
718                         dump.push( row );
719                 }
720                 return dump;
721         },
722
723         'dump_with_keys' : function(params) {
724                 var obj = this;
725                 switch(this.node.nodeName) {
726                         case 'tree' : return this._dump_tree_with_keys(params); break;
727                         default: throw('NYI: Need .dump_with_keys() for ' + this.node.nodeName); break;
728                 }
729
730         },
731
732         '_dump_tree_with_keys' : function(params) {
733                 var obj = this;
734                 var dump = [];
735                 for (var i = 0; i < this.treechildren.childNodes.length; i++) {
736                         var row = {};
737                         var treeitem = this.treechildren.childNodes[i];
738                         var treerow = treeitem.firstChild;
739                         for (var j = 0; j < treerow.childNodes.length; j++) {
740                                 row[ obj.columns[j].id ] = treerow.childNodes[j].getAttribute('label');
741                         }
742                         dump.push( row );
743                 }
744                 return dump;
745         },
746
747         'dump_selected_with_keys' : function(params) {
748                 var obj = this;
749                 switch(this.node.nodeName) {
750                         case 'tree' : return this._dump_tree_selection_with_keys(params); break;
751                         default: throw('NYI: Need .dump_selection_with_keys() for ' + this.node.nodeName); break;
752                 }
753
754         },
755
756         '_dump_tree_selection_with_keys' : function(params) {
757                 var obj = this;
758                 var dump = [];
759                 var list = obj._retrieve_selection_from_tree();
760                 for (var i = 0; i < list.length; i++) {
761                         var row = {};
762                         var treeitem = list[i];
763                         var treerow = treeitem.firstChild;
764                         for (var j = 0; j < treerow.childNodes.length; j++) {
765                                 var value = treerow.childNodes[j].getAttribute('label');
766                                 //FIXME
767                                 //if (params.skip_hidden_columns) if (obj.node.firstChild.childNodes[j].getAttribute('hidden')) continue;
768                                 var id = obj.columns[j].id; if (params.labels_instead_of_ids) id = obj.columns[j].label;
769                                 row[ id ] = value;
770                         }
771                         dump.push( row );
772                 }
773                 return dump;
774         },
775
776         'clipboard' : function() {
777                 try {
778                         var obj = this;
779                         var dump = obj.dump_selected_with_keys({'skip_hidden_columns':true,'labels_instead_of_ids':true});
780                         JSAN.use('OpenILS.data'); var data = new OpenILS.data(); data.stash_retrieve();
781                         data.list_clipboard = dump; data.stash('list_clipboard');
782                         JSAN.use('util.window'); var win = new util.window();
783                         win.open(urls.XUL_LIST_CLIPBOARD,'list_clipboard','chrome,resizable,modal');
784                 } catch(E) {
785                         this.error.standard_unexpected_error_alert('clipboard',E);
786                 }
787         },
788
789         'dump_retrieve_ids' : function(params) {
790                 var obj = this;
791                 switch(this.node.nodeName) {
792                         case 'tree' : return this._dump_retrieve_ids_tree(params); break;
793                         default: throw('NYI: Need .dump_retrieve_ids() for ' + this.node.nodeName); break;
794                 }
795         },
796
797         '_dump_retrieve_ids_tree' : function(params) {
798                 var obj = this;
799                 var dump = [];
800                 for (var i = 0; i < this.treechildren.childNodes.length; i++) {
801                         var treeitem = this.treechildren.childNodes[i];
802                         dump.push( treeitem.getAttribute('retrieve_id') );
803                 }
804                 return dump;
805         },
806
807 }
808 dump('exiting util.list.js\n');