a2fa0690d5b4edc691d1af28666edbfd3f7dc593
[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                 setTimeout( function() { obj.auto_retrieve(); }, 0 );
441
442                 return treeitem;
443         },
444
445         'put_retrieving_label' : function(treerow) {
446                 var obj = this;
447                 try {
448                         /*
449                         var cols_idx = 0;
450                         dump('put_retrieving_label.  columns = ' + js2JSON(obj.columns) + '\n');
451                         while( obj.columns[cols_idx] && obj.columns[cols_idx].hidden && obj.columns[cols_idx].hidden == 'true') {
452                                 dump('\t' + cols_idx);
453                                 var treecell = document.createElement('treecell');
454                                 treerow.appendChild(treecell);
455                                 cols_idx++;
456                         }
457                         */
458                         for (var i = 0; i < obj.columns.length; i++) {
459                         var treecell = document.createElement('treecell'); treecell.setAttribute('label','Retrieving...');
460                         treerow.appendChild(treecell);
461                         }
462                         /*
463                         dump('\t' + cols_idx + '\n');
464                         */
465                 } catch(E) {
466                         alert(E);
467                 }
468         },
469
470         'detect_visible' : function() {
471                 var obj = this;
472                 try {
473                         //dump('detect_visible  obj.node = ' + obj.node + '\n');
474                         /* FIXME - this is a hack.. if the implementation of tree changes, this could break */
475                         try {
476                                 var scrollbar = document.getAnonymousNodes( document.getAnonymousNodes(obj.node)[1] )[1];
477                                 var curpos = scrollbar.getAttribute('curpos');
478                                 var maxpos = scrollbar.getAttribute('maxpos');
479                                 //alert('curpos = ' + curpos + ' maxpos = ' + maxpos + ' obj.curpos = ' + obj.curpos + ' obj.maxpos = ' + obj.maxpos + '\n');
480                                 if ((curpos != obj.curpos) || (maxpos != obj.maxpos)) {
481                                         if ( obj.auto_retrieve() > 0 ) {
482                                                 obj.curpos = curpos; obj.maxpos = maxpos;
483                                         }
484                                 }
485                         } catch(E) {
486                                 obj.error.sdump('D_XULRUNNER', 'List implementation changed? ' + E);
487                         }
488                 } catch(E) { obj.error.sdump('D_ERROR',E); }
489         },
490
491         'detect_visible_polling' : function() {
492                 try {
493                         //alert('detect_visible_polling');
494                         var obj = this;
495                         obj.detect_visible();
496                         setTimeout(function() { try { obj.detect_visible_polling(); } catch(E) { alert(E); } },2000);
497                 } catch(E) {
498                         alert(E);
499                 }
500         },
501
502
503         'auto_retrieve' : function(params) {
504                 var obj = this;
505                 switch (this.node.nodeName) {
506                         case 'tree' : obj._auto_retrieve_tree(params); break;
507                         default: throw('NYI: Need .auto_retrieve() for ' + obj.node.nodeName); break;
508                 }
509         },
510
511         '_auto_retrieve_tree' : function (params) {
512                 var obj = this;
513                 if (!obj.auto_retrieve_in_progress) {
514                         obj.auto_retrieve_in_progress = true;
515                         setTimeout(
516                                 function() {
517                                         try {
518                                                         //alert('auto_retrieve\n');
519                                                         var count = 0;
520                                                         var startpos = obj.node.treeBoxObject.getFirstVisibleRow();
521                                                         var endpos = obj.node.treeBoxObject.getLastVisibleRow();
522                                                         if (startpos > endpos) endpos = obj.node.treeBoxObject.getPageLength();
523                                                         //dump('startpos = ' + startpos + ' endpos = ' + endpos + '\n');
524                                                         for (var i = startpos; i < endpos + 4; i++) {
525                                                                 try {
526                                                                         //dump('trying index ' + i + '\n');
527                                                                         var item = obj.node.contentView.getItemAtIndex(i).firstChild;
528                                                                         if (item && item.getAttribute('retrieved') != 'true' ) {
529                                                                                 //dump('\tgot an unfleshed item = ' + item + ' = ' + item.nodeName + '\n');
530                                                                                 util.widgets.dispatch('flesh',item); count++;
531                                                                         }
532                                                                 } catch(E) {
533                                                                         //dump(i + ' : ' + E + '\n');
534                                                                 }
535                                                         }
536                                                         obj.auto_retrieve_in_progress = false;
537                                                         return count;
538                                         } catch(E) { alert(E); }
539                                 }, 1
540                         );
541                 }
542         },
543
544         'full_retrieve' : function(params) {
545                 var obj = this;
546                 switch (this.node.nodeName) {
547                         case 'tree' : obj._full_retrieve_tree(params); break;
548                         default: throw('NYI: Need .full_retrieve() for ' + obj.node.nodeName); break;
549                 }
550         },
551
552         '_full_retrieve_tree' : function(params) {
553                 var obj = this;
554                 try {
555                         if (obj.row_count.total == obj.row_count.fleshed) {
556                                 //alert('Full retrieve... tree seems to be in sync\n' + js2JSON(obj.row_count));
557                                 if (typeof obj.on_all_fleshed == 'function') {
558                                         setTimeout( function() { obj.on_all_fleshed(); }, 0 );
559                                 } else {
560                                         alert('.full_retrieve called with no callback?');
561                                 }
562                         } else {
563                                 //alert('Full retrieve... syncing tree' + js2JSON(obj.row_count));
564                                 JSAN.use('util.widgets');
565                                 var nodes = obj.treechildren.childNodes;
566                                 for (var i = 0; i < nodes.length; i++) {
567                                         util.widgets.dispatch('flesh',nodes[i].firstChild);
568                                 }
569                         }
570                 } catch(E) {
571                         obj.error.standard_unexpected_error_alert('_full_retrieve_tree',E);
572                 }
573         },
574
575         '_append_to_listbox' : function (params) {
576
577                 var obj = this;
578
579                 if (typeof params.row == 'undefined') throw('util.list.append: Object must contain a row');
580
581                 var s = ('util.list.append: params = ' + (params) + '\n');
582
583                 var listitem = document.createElement('listitem');
584
585                 s += ('listbox = ' + this.node + '  listitem = ' + listitem + '\n');
586
587                 if (typeof params.retrieve_row == 'function' || typeof this.retrieve_row == 'function') {
588
589                         setTimeout(
590                                 function() {
591                                         listitem.setAttribute('retrieve_id',params.retrieve_id);
592                                         //FIXME//Make async and fire when row is visible in list
593                                         var row;
594
595                                         params.row_node = listitem;
596                                         params.on_retrieve = function(row) {
597                                                 params.row = row;
598                                                 obj._map_row_to_listcell(params,listitem);
599                                                 obj.node.appendChild( listitem );
600                                         }
601
602                                         if (typeof params.retrieve_row == 'function') {
603
604                                                 row = params.retrieve_row( params );
605
606                                         } else {
607
608                                                 if (typeof obj.retrieve_row == 'function') {
609
610                                                         row = obj.retrieve_row( params );
611
612                                                 }
613                                         }
614                                 }, 0
615                         );
616                 } else {
617                         this._map_row_to_listcell(params,listitem);
618                         this.node.appendChild( listitem );
619                 }
620
621                 this.error.sdump('D_LIST',s);
622                 return listitem;
623
624         },
625
626         '_map_row_to_treecell' : function(params,treerow) {
627                 var obj = this;
628                 var s = '';
629                 util.widgets.remove_children(treerow);
630                 for (var i = 0; i < this.columns.length; i++) {
631                         var treecell = document.createElement('treecell');
632                         var label = '';
633                         if (params.skip_columns && (params.skip_columns.indexOf(i) != -1)) {
634                                 treecell.setAttribute('label',label);
635                                 treerow.appendChild( treecell );
636                                 s += ('treecell = ' + treecell + ' with label = ' + label + '\n');
637                                 continue;
638                         }
639                         if (params.skip_all_columns_except && (params.skip_all_columns_except.indexOf(i) == -1)) {
640                                 treecell.setAttribute('label',label);
641                                 treerow.appendChild( treecell );
642                                 s += ('treecell = ' + treecell + ' with label = ' + label + '\n');
643                                 continue;
644                         }
645                         if (typeof params.map_row_to_column == 'function')  {
646
647                                 label = params.map_row_to_column(params.row,this.columns[i]);
648
649                         } else {
650
651                                 if (typeof this.map_row_to_column == 'function') {
652
653                                         label = this.map_row_to_column(params.row,this.columns[i]);
654
655                                 } else {
656
657                                         throw('No map_row_to_column function');
658
659                                 }
660                         }
661                         treecell.setAttribute('label',label);
662                         treerow.appendChild( treecell );
663                         s += ('treecell = ' + treecell + ' with label = ' + label + '\n');
664                 }
665                 this.error.sdump('D_LIST',s);
666         },
667
668         '_map_row_to_listcell' : function(params,listitem) {
669                 var obj = this;
670                 var s = '';
671                 for (var i = 0; i < this.columns.length; i++) {
672                         var value = '';
673                         if (typeof params.map_row_to_column == 'function')  {
674
675                                 value = params.map_row_to_column(params.row,this.columns[i]);
676
677                         } else {
678
679                                 if (typeof this.map_row_to_column == 'function') {
680
681                                         value = this.map_row_to_column(params.row,this.columns[i]);
682                                 }
683                         }
684                         if (typeof value == 'string' || typeof value == 'number') {
685                                 var listcell = document.createElement('listcell');
686                                 listcell.setAttribute('label',value);
687                                 listitem.appendChild(listcell);
688                                 s += ('listcell = ' + listcell + ' with label = ' + value + '\n');
689                         } else {
690                                 listitem.appendChild(value);
691                                 s += ('listcell = ' + value + ' is really a ' + value.nodeName + '\n');
692                         }
693                 }
694                 this.error.sdump('D_LIST',s);
695         },
696
697         'select_all' : function(params) {
698                 var obj = this;
699                 switch(this.node.nodeName) {
700                         case 'tree' : return this._select_all_from_tree(params); break;
701                         default: throw('NYI: Need ._select_all_from_() for ' + this.node.nodeName); break;
702                 }
703         },
704
705         '_select_all_from_tree' : function(params) {
706                 var obj = this;
707                 this.node.view.selection.selectAll();
708         },
709
710         'retrieve_selection' : function(params) {
711                 var obj = this;
712                 switch(this.node.nodeName) {
713                         case 'tree' : return this._retrieve_selection_from_tree(params); break;
714                         default: throw('NYI: Need ._retrieve_selection_from_() for ' + this.node.nodeName); break;
715                 }
716         },
717
718         '_retrieve_selection_from_tree' : function(params) {
719                 var obj = this;
720                 var list = [];
721                 var start = new Object();
722                 var end = new Object();
723                 var numRanges = this.node.view.selection.getRangeCount();
724                 for (var t=0; t<numRanges; t++){
725                         this.node.view.selection.getRangeAt(t,start,end);
726                         for (var v=start.value; v<=end.value; v++){
727                                 var i = this.node.contentView.getItemAtIndex(v);
728                                 list.push( i );
729                         }
730                 }
731                 return list;
732         },
733
734         'dump' : function(params) {
735                 var obj = this;
736                 switch(this.node.nodeName) {
737                         case 'tree' : return this._dump_tree(params); break;
738                         default: throw('NYI: Need .dump() for ' + this.node.nodeName); break;
739                 }
740         },
741
742         '_dump_tree' : function(params) {
743                 var obj = this;
744                 var dump = [];
745                 for (var i = 0; i < this.treechildren.childNodes.length; i++) {
746                         var row = [];
747                         var treeitem = this.treechildren.childNodes[i];
748                         var treerow = treeitem.firstChild;
749                         for (var j = 0; j < treerow.childNodes.length; j++) {
750                                 row.push( treerow.childNodes[j].getAttribute('label') );
751                         }
752                         dump.push( row );
753                 }
754                 return dump;
755         },
756
757         'dump_with_keys' : function(params) {
758                 var obj = this;
759                 switch(this.node.nodeName) {
760                         case 'tree' : return this._dump_tree_with_keys(params); break;
761                         default: throw('NYI: Need .dump_with_keys() for ' + this.node.nodeName); break;
762                 }
763
764         },
765
766         '_dump_tree_with_keys' : function(params) {
767                 var obj = this;
768                 var dump = [];
769                 for (var i = 0; i < this.treechildren.childNodes.length; i++) {
770                         var row = {};
771                         var treeitem = this.treechildren.childNodes[i];
772                         var treerow = treeitem.firstChild;
773                         for (var j = 0; j < treerow.childNodes.length; j++) {
774                                 row[ obj.columns[j].id ] = treerow.childNodes[j].getAttribute('label');
775                         }
776                         dump.push( row );
777                 }
778                 return dump;
779         },
780
781         'dump_selected_with_keys' : function(params) {
782                 var obj = this;
783                 switch(this.node.nodeName) {
784                         case 'tree' : return this._dump_tree_selection_with_keys(params); break;
785                         default: throw('NYI: Need .dump_selection_with_keys() for ' + this.node.nodeName); break;
786                 }
787
788         },
789
790         '_dump_tree_selection_with_keys' : function(params) {
791                 var obj = this;
792                 var dump = [];
793                 var list = obj._retrieve_selection_from_tree();
794                 for (var i = 0; i < list.length; i++) {
795                         var row = {};
796                         var treeitem = list[i];
797                         var treerow = treeitem.firstChild;
798                         for (var j = 0; j < treerow.childNodes.length; j++) {
799                                 var value = treerow.childNodes[j].getAttribute('label');
800                                 //FIXME
801                                 //if (params.skip_hidden_columns) if (obj.node.firstChild.childNodes[j].getAttribute('hidden')) continue;
802                                 var id = obj.columns[j].id; if (params.labels_instead_of_ids) id = obj.columns[j].label;
803                                 row[ id ] = value;
804                         }
805                         dump.push( row );
806                 }
807                 return dump;
808         },
809
810         'clipboard' : function() {
811                 try {
812                         var obj = this;
813                         var dump = obj.dump_selected_with_keys({'skip_hidden_columns':true,'labels_instead_of_ids':true});
814                         JSAN.use('OpenILS.data'); var data = new OpenILS.data(); data.stash_retrieve();
815                         data.list_clipboard = dump; data.stash('list_clipboard');
816                         JSAN.use('util.window'); var win = new util.window();
817                         win.open(urls.XUL_LIST_CLIPBOARD,'list_clipboard','chrome,resizable,modal');
818                 } catch(E) {
819                         this.error.standard_unexpected_error_alert('clipboard',E);
820                 }
821         },
822
823         'dump_retrieve_ids' : function(params) {
824                 var obj = this;
825                 switch(this.node.nodeName) {
826                         case 'tree' : return this._dump_retrieve_ids_tree(params); break;
827                         default: throw('NYI: Need .dump_retrieve_ids() for ' + this.node.nodeName); break;
828                 }
829         },
830
831         '_dump_retrieve_ids_tree' : function(params) {
832                 var obj = this;
833                 var dump = [];
834                 for (var i = 0; i < this.treechildren.childNodes.length; i++) {
835                         var treeitem = this.treechildren.childNodes[i];
836                         dump.push( treeitem.getAttribute('retrieve_id') );
837                 }
838                 return dump;
839         },
840
841         '_sort_tree' : function(col,sortDir) {
842                 var obj = this;
843                 try {
844                         if (obj.node.getAttribute('no_sort')) {
845                                 return;
846                         }
847                         if (obj.on_all_fleshed) {
848                                 var r = window.confirm('This list is busy rendering/retrieving data.  Abort current action and proceed?');
849                                 if (r) {} else { return; }
850                         }
851                         var col_pos;
852                         for (var i = 0; i < obj.columns.length; i++) { 
853                                 if (obj.columns[i].id == col.id) col_pos = function(a){return a;}(i); 
854                         }
855                         obj.on_all_fleshed = function() {
856                                         try {
857                                                 JSAN.use('util.money');
858                                                 var rows = [];
859                                                 var treeitems = obj.treechildren.childNodes;
860                                                 for (var i = 0; i < treeitems.length; i++) {
861                                                         var treeitem = treeitems[i];
862                                                         var treerow = treeitem.firstChild;
863                                                         var treecell = treerow.childNodes[ col_pos ];
864                                                         value = ( { 'value' : treecell ? treecell.getAttribute('label') : '', 'node' : treeitem } );
865                                                         rows.push( value );
866                                                 }
867                                                 rows = rows.sort( function(a,b) { 
868                                                         a = a.value; b = b.value; 
869                                                         if (col.getAttribute('sort_type')) {
870                                                                 switch(col.getAttribute('sort_type')) {
871                                                                         case 'number' :
872                                                                                 a = Number(a); b = Number(b);
873                                                                         break;
874                                                                         case 'money' :
875                                                                                 a = util.money.dollars_float_to_cents_integer(a);
876                                                                                 b = util.money.dollars_float_to_cents_integer(b);
877                                                                         break;
878                                                                         case 'title' : /* special case for "a" and "the".  doesn't use marc 245 indicator */
879                                                                                 a = String( a ).toUpperCase().replace( /^(THE|A)\s+/, '' );
880                                                                                 b = String( b ).toUpperCase().replace( /^(THE|A)\s+/, '' );
881                                                                         break;
882                                                                         default:
883                                                                                 a = String( a ).toUpperCase();
884                                                                                 b = String( a ).toUpperCase();
885                                                                         break;
886                                                                 }
887                                                         }
888                                                         if (a < b) return -1; 
889                                                         if (a > b) return 1; 
890                                                         return 0; 
891                                                 } );
892                                                 if (sortDir == 'asc') rows = rows.reverse();
893                                                 while(obj.treechildren.lastChild) obj.treechildren.removeChild( obj.treechildren.lastChild );
894                                                 for (var i = 0; i < rows.length; i++) {
895                                                         obj.treechildren.appendChild( rows[i].node );
896                                                 }
897                                         } catch(E) {
898                                                 obj.error.standard_unexpected_error_alert('sorting',E); 
899                                         }
900                                         setTimeout(function(){ obj.on_all_fleshed = null; },0);
901                                 }
902                         obj.full_retrieve();
903                 } catch(E) {
904                         obj.error.standard_unexpected_error_alert('pre sorting', E);
905                 }
906         },
907
908 }
909 dump('exiting util.list.js\n');