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