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