]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/xul/staff_client/chrome/content/util/list.js
I think this might be harmless if this happens
[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         '_init_tree' : function (params) {
50                 var obj = this;
51                 if (this.prebuilt) {
52                 
53                         this.treechildren = this.node.lastChild;        
54                 
55                 } else {
56                         var treecols = document.createElement('treecols');
57                         this.node.appendChild(treecols);
58                         this.treecols = treecols;
59
60                         for (var i = 0; i < this.columns.length; i++) {
61                                 var treecol = document.createElement('treecol');
62                                 for (var j in this.columns[i]) {
63                                         treecol.setAttribute(j,this.columns[i][j]);
64                                 }
65                                 treecols.appendChild(treecol);
66                                 treecol.addEventListener(
67                                         'click', 
68                                         function(ev) {
69                                                 function do_it() {
70                                                         var sortDir = ev.target.getAttribute('sortDir') || 'desc';
71                                                         if (sortDir == 'desc') sortDir = 'asc'; else sortDir = 'desc';
72                                                         ev.target.setAttribute('sortDir',sortDir);
73                                                         obj._sort_tree(ev.target,sortDir);
74                                                 }
75
76                                                 if (obj.row_count.total != obj.row_count.fleshed && (obj.row_count.total - obj.row_count.fleshed) > 50) {
77                                                         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?');
78
79                                                         if (r) {
80                                                                 setTimeout( do_it, 0 );
81                                                         }
82                                                 } else {
83                                                                 setTimeout( do_it, 0 );
84                                                 }
85                                         },
86                                         false
87                                 );
88                                 var splitter = document.createElement('splitter');
89                                 splitter.setAttribute('class','tree-splitter');
90                                 treecols.appendChild(splitter);
91                         }
92
93                         var treechildren = document.createElement('treechildren');
94                         this.node.appendChild(treechildren);
95                         this.treechildren = treechildren;
96                 }
97                 if (typeof params.on_select == 'function') {
98                         this.node.addEventListener(
99                                 'select',
100                                 params.on_select,
101                                 false
102                         );
103                 }
104                 if (typeof params.on_click == 'function') {
105                         this.node.addEventListener(
106                                 'click',
107                                 params.on_click,
108                                 false
109                         );
110                 }
111                 /*
112                 this.node.addEventListener(
113                         'mousemove',
114                         function(ev) { obj.detect_visible(); },
115                         false
116                 );
117                 */
118                 this.node.addEventListener(
119                         'keypress',
120                         function(ev) { obj.auto_retrieve(); },
121                         false
122                 );
123                 this.node.addEventListener(
124                         'click',
125                         function(ev) { obj.auto_retrieve(); },
126                         false
127                 );
128                 window.addEventListener(
129                         'resize',
130                         function(ev) { obj.auto_retrieve(); },
131                         false
132                 );
133                 /* FIXME -- find events on scrollbar to trigger this */
134                 obj.detect_visible_polling();   
135                 /*
136                 var scrollbar = document.getAnonymousNodes( document.getAnonymousNodes(this.node)[1] )[1];
137                 var slider = document.getAnonymousNodes( scrollbar )[2];
138                 alert('scrollbar = ' + scrollbar.nodeName + ' grippy = ' + slider.nodeName);
139                 scrollbar.addEventListener('click',function(){alert('sb click');},false);
140                 scrollbar.addEventListener('command',function(){alert('sb command');},false);
141                 scrollbar.addEventListener('scroll',function(){alert('sb scroll');},false);
142                 slider.addEventListener('click',function(){alert('slider click');},false);
143                 slider.addEventListener('command',function(){alert('slider command');},false);
144                 slider.addEventListener('scroll',function(){alert('slider scroll');},false);
145                 */
146                 this.node.addEventListener('scroll',function(){ obj.auto_retrieve(); },false);
147
148                 this.restores_columns(params);
149         },
150
151         '_init_listbox' : function (params) {
152                 if (this.prebuilt) {
153                 } else {
154                         var listhead = document.createElement('listhead');
155                         this.node.appendChild(listhead);
156
157                         var listcols = document.createElement('listcols');
158                         this.node.appendChild(listcols);
159
160                         for (var i = 0; i < this.columns.length; i++) {
161                                 var listheader = document.createElement('listheader');
162                                 listhead.appendChild(listheader);
163                                 var listcol = document.createElement('listcol');
164                                 listcols.appendChild(listcol);
165                                 for (var j in this.columns[i]) {
166                                         listheader.setAttribute(j,this.columns[i][j]);
167                                         listcol.setAttribute(j,this.columns[i][j]);
168                                 };
169                         }
170                 }
171         },
172
173         'save_columns' : function (params) {
174                 var obj = this;
175                 switch (this.node.nodeName) {
176                         case 'tree' : this._save_columns_tree(params); break;
177                         default: throw('NYI: Need .save_columns() for ' + this.node.nodeName); break;
178                 }
179         },
180
181         '_save_columns_tree' : function (params) {
182                 var obj = this;
183                 try {
184                         var id = obj.node.getAttribute('id'); if (!id) {
185                                 alert("FIXME: The columns for this list cannot be saved because the list has no id.");
186                                 return;
187                         }
188                         var my_cols = {};
189                         var nl = obj.node.getElementsByTagName('treecol');
190                         for (var i = 0; i < nl.length; i++) {
191                                 var col = nl[i];
192                                 var col_id = col.getAttribute('id');
193                                 if (!col_id) {
194                                         alert('FIXME: A column in this list does not have an id and cannot be saved');
195                                         continue;
196                                 }
197                                 var col_hidden = col.getAttribute('hidden'); 
198                                 var col_width = col.getAttribute('width'); 
199                                 var col_ordinal = col.getAttribute('ordinal'); 
200                                 my_cols[ col_id ] = { 'hidden' : col_hidden, 'width' : col_width, 'ordinal' : col_ordinal };
201                         }
202                         netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
203                         JSAN.use('util.file'); var file = new util.file('tree_columns_for_'+window.escape(id));
204                         file.set_object(my_cols);
205                         file.close();
206                         alert('Columns saved.');
207                 } catch(E) {
208                         obj.error.standard_unexpected_error_alert('_save_columns_tree',E);
209                 }
210         },
211
212         'restores_columns' : function (params) {
213                 var obj = this;
214                 switch (this.node.nodeName) {
215                         case 'tree' : this._restores_columns_tree(params); break;
216                         default: throw('NYI: Need .restores_columns() for ' + this.node.nodeName); break;
217                 }
218         },
219
220         '_restores_columns_tree' : function (params) {
221                 var obj = this;
222                 try {
223                         var id = obj.node.getAttribute('id'); if (!id) {
224                                 alert("FIXME: The columns for this list cannot be restored because the list has no id.");
225                                 return;
226                         }
227
228                         netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
229                         JSAN.use('util.file'); var file = new util.file('tree_columns_for_'+window.escape(id));
230                         if (file._file.exists()) {
231                                 var my_cols = file.get_object(); file.close();
232                                 var nl = obj.node.getElementsByTagName('treecol');
233                                 for (var i = 0; i < nl.length; i++) {
234                                         var col = nl[i];
235                                         var col_id = col.getAttribute('id');
236                                         if (!col_id) {
237                                                 alert('FIXME: A column in this list does not have an id and cannot be saved');
238                                                 continue;
239                                         }
240                                         if (typeof my_cols[col_id] != 'undefined') {
241                                                 col.setAttribute('hidden',my_cols[col_id].hidden); 
242                                                 col.setAttribute('width',my_cols[col_id].width); 
243                                                 col.setAttribute('ordinal',my_cols[col_id].ordinal); 
244                                         } else {
245                                                 obj.error.sdump('D_ERROR','WARNING: Column ' + col_id + ' did not have a saved state.');
246                                         }
247                                 }
248                         }
249                 } catch(E) {
250                         obj.error.standard_unexpected_error_alert('_restore_columns_tree',E);
251                 }
252         },
253
254         'clear' : function (params) {
255                 var obj = this;
256                 switch (this.node.nodeName) {
257                         case 'tree' : this._clear_tree(params); break;
258                         case 'listbox' : this._clear_listbox(params); break;
259                         default: throw('NYI: Need .clear() for ' + this.node.nodeName); break;
260                 }
261                 this.error.sdump('D_LIST','Clearing list ' + this.node.getAttribute('id') + '\n');
262                 this.row_count.total = 0;
263                 this.row_count.fleshed = 0;
264                 setTimeout( function() { obj.exec_on_all_fleshed(); }, 0 );
265         },
266
267         '_clear_tree' : function(params) {
268                 var obj = this;
269                 if (obj.error.sdump_levels.D_LIST_DUMP_ON_CLEAR) {
270                         obj.error.sdump('D_LIST_DUMP_ON_CLEAR',obj.dump());
271                 }
272                 if (obj.error.sdump_levels.D_LIST_DUMP_WITH_KEYS_ON_CLEAR) {
273                         obj.error.sdump('D_LIST_DUMP_WITH_KEYS_ON_CLEAR',obj.dump_with_keys());
274                 }
275                 while (obj.treechildren.lastChild) obj.treechildren.removeChild( obj.treechildren.lastChild );
276         },
277
278         '_clear_listbox' : function(params) {
279                 var obj = this;
280                 var items = [];
281                 var nl = this.node.getElementsByTagName('listitem');
282                 for (var i = 0; i < nl.length; i++) {
283                         items.push( nl[i] );
284                 }
285                 for (var i = 0; i < items.length; i++) {
286                         this.node.removeChild(items[i]);
287                 }
288         },
289
290         'append' : function (params) {
291                 var rnode;
292                 var obj = this;
293                 switch (this.node.nodeName) {
294                         case 'tree' : rparams = this._append_to_tree(params); break;
295                         case 'listbox' : rparams = this._append_to_listbox(params); break;
296                         default: throw('NYI: Need .append() for ' + this.node.nodeName); break;
297                 }
298                 if (rparams && params.attributes) {
299                         for (var i in params.attributes) {
300                                 rparams.my_node.setAttribute(i,params.attributes[i]);
301                         }
302                 }
303                 this.row_count.total++;
304                 if (this.row_count.fleshed == this.row_count.total) {
305                         setTimeout( function() { obj.exec_on_all_fleshed(); }, 0 );
306                 }
307                 return rparams;
308         },
309         
310         'refresh_row' : function (params) {
311                 var rnode;
312                 var obj = this;
313                 switch (this.node.nodeName) {
314                         case 'tree' : rparams = this._refresh_row_in_tree(params); break;
315                         default: throw('NYI: Need .refresh_row() for ' + this.node.nodeName); break;
316                 }
317                 if (rparams && params.attributes) {
318                         for (var i in params.attributes) {
319                                 rparams.my_node.setAttribute(i,params.attributes[i]);
320                         }
321                 }
322         this.row_count.fleshed--;
323                 return rparams;
324         },
325
326
327         '_append_to_tree' : function (params) {
328
329                 var obj = this;
330
331                 if (typeof params.row == 'undefined') throw('util.list.append: Object must contain a row');
332
333                 var s = ('util.list.append: params = ' + (params) + '\n');
334
335                 var treechildren_node = this.treechildren;
336
337                 if (params.node && params.node.nodeName == 'treeitem') {
338                         params.node.setAttribute('container','true'); /* params.node.setAttribute('open','true'); */
339                         if (params.node.lastChild.nodeName == 'treechildren') {
340                                 treechildren_node = params.node.lastChild;
341                         } else {
342                                 treechildren_node = document.createElement('treechildren');
343                                 params.node.appendChild(treechildren_node);
344                         }
345                 }
346
347                 var treeitem = document.createElement('treeitem');
348                 treeitem.setAttribute('retrieve_id',params.retrieve_id);
349                 if (typeof params.to_bottom != 'undefined') {
350                         treechildren_node.appendChild( treeitem );
351                         if (typeof params.no_auto_select == 'undefined') {
352                                 if (!obj.auto_select_pending) {
353                                         obj.auto_select_pending = true;
354                                         setTimeout(function() {
355                                                 dump('auto-selecting\n');
356                                                 var idx = Number(obj.node.view.rowCount)-1;
357                                                 try { obj.node.view.selection.select(idx); } catch(E) { obj.error.sdump('D_WARN','tree auto select: ' + E + '\n'); }
358                                                 try { if (typeof params.on_select == 'function') params.on_select(); } catch(E) { obj.error.sdump('D_WARN','tree auto select, on_select: ' + E + '\n'); }
359                                                 obj.auto_select_pending = false;
360                                                 try { util.widgets.dispatch('flesh',obj.node.contentView.getItemAtIndex(idx).firstChild); } catch(E) { obj.error.sdump('D_WARN','tree auto select, flesh: ' + E + '\n'); }
361                                         }, 1000);
362                                 }
363                         }
364                 } else {
365                         if (treechildren_node.firstChild) {
366                                 treechildren_node.insertBefore( treeitem, treechildren_node.firstChild );
367                         } else {
368                                 treechildren_node.appendChild( treeitem );
369                         }
370                         if (typeof params.no_auto_select == 'undefined') {
371                                 if (!obj.auto_select_pending) {
372                                         obj.auto_select_pending = true;
373                                         setTimeout(function() {
374                                                 try { obj.node.view.selection.select(0); } catch(E) { obj.error.sdump('D_WARN','tree auto select: ' + E + '\n'); }
375                                                 try { if (typeof params.on_select == 'function') params.on_select(); } catch(E) { obj.error.sdump('D_WARN','tree auto select, on_select: ' + E + '\n'); }
376                                                 obj.auto_select_pending = false;
377                                                 try { util.widgets.dispatch('flesh',obj.node.contentView.getItemAtIndex(0).firstChild); } catch(E) { obj.error.sdump('D_WARN','tree auto select, flesh: ' + E + '\n'); }
378                                         }, 1000);
379                                 }
380                         }
381                 }
382                 var treerow = document.createElement('treerow');
383                 treeitem.appendChild( treerow );
384                 treerow.setAttribute('retrieve_id',params.retrieve_id);
385
386                 s += ('tree = ' + this.node + '  treechildren = ' + treechildren_node + '\n');
387                 s += ('treeitem = ' + treeitem + '  treerow = ' + treerow + '\n');
388
389                 if (typeof params.retrieve_row == 'function' || typeof this.retrieve_row == 'function') {
390
391                         obj.put_retrieving_label(treerow);
392                         treerow.addEventListener(
393                                 'flesh',
394                                 function() {
395
396                                         if (treerow.getAttribute('retrieved') == 'true') return; /* already running */
397
398                                         treerow.setAttribute('retrieved','true');
399
400                                         //dump('fleshing = ' + params.retrieve_id + '\n');
401
402                                         function inc_fleshed() {
403                                                 if (treerow.getAttribute('fleshed') == 'true') return; /* already fleshed */
404                                                 treerow.setAttribute('fleshed','true');
405                                                 obj.row_count.fleshed++;
406                                                 if (obj.row_count.fleshed >= obj.row_count.total) {
407                                                         setTimeout( function() { obj.exec_on_all_fleshed(); }, 0 );
408                                                 }
409                                         }
410
411                                         params.row_node = treeitem;
412                                         params.on_retrieve = function(p) {
413                                                 try {
414                                                         p.row = params.row;
415                                                         obj._map_row_to_treecell(p,treerow);
416                                                         inc_fleshed();
417                                                         var idx = obj.node.contentView.getIndexOfItem( params.row_node );
418                                                         dump('idx = ' + idx + '\n');
419                                                         // if current row is selected, send another select event to re-sync data that the client code fetches on selects
420                                                         if ( obj.node.view.selection.isSelected( idx ) ) {
421                                                                 dump('dispatching select event for on_retrieve for idx = ' + idx + '\n');
422                                                                 util.widgets.dispatch('select',obj.node);
423                                                         }
424                                                 } catch(E) {
425                             // Let's not alert on this for now.  Getting contentView has no properties in record buckets under certain conditions
426                                                         dump('fixme2: ' + E + '\n');
427                                                 }
428                                         }
429
430                                         if (typeof params.retrieve_row == 'function') {
431
432                                                 params.retrieve_row( params );
433
434                                         } else if (typeof obj.retrieve_row == 'function') {
435
436                                                         obj.retrieve_row( params );
437
438                                         } else {
439                                         
440                                                         inc_fleshed();
441                                         }
442                                 },
443                                 false
444                         );
445                         /*
446                         setTimeout(
447                                 function() {
448                                         util.widgets.dispatch('flesh',treerow);
449                                 }, 0
450                         );
451                         */
452                 } else {
453                         obj.put_retrieving_label(treerow);
454                         treerow.addEventListener(
455                                 'flesh',
456                                 function() {
457                                         //dump('fleshing anon\n');
458                                         if (treerow.getAttribute('fleshed') == 'true') return; /* already fleshed */
459                                         obj._map_row_to_treecell(params,treerow);
460                                         treerow.setAttribute('retrieved','true');
461                                         treerow.setAttribute('fleshed','true');
462                                         obj.row_count.fleshed++;
463                                         if (obj.row_count.fleshed >= obj.row_count.total) {
464                                                 setTimeout( function() { obj.exec_on_all_fleshed(); }, 0 );
465                                         }
466                                 },
467                                 false
468                         );
469                         /*
470                         setTimeout(
471                                 function() {
472                                         util.widgets.dispatch('flesh',treerow);
473                                 }, 0
474                         );
475                         */
476                 }
477                 this.error.sdump('D_LIST',s);
478
479                         try {
480
481                                 if (obj.trim_list && obj.row_count.total >= obj.trim_list) {
482                                         // Remove oldest row
483                                         //if (typeof params.to_bottom != 'undefined') 
484                                         if (typeof params.to_top == 'undefined') {
485                                                 treechildren_node.removeChild( treechildren_node.firstChild );
486                                         } else {
487                                                 treechildren_node.removeChild( treechildren_node.lastChild );
488                                         }
489                                 }
490                         } catch(E) {
491                         }
492
493                 setTimeout( function() { obj.auto_retrieve(); }, 0 );
494
495                 params.my_node = treeitem;
496                 return params;
497         },
498
499         '_refresh_row_in_tree' : function (params) {
500
501                 var obj = this;
502
503                 if (typeof params.row == 'undefined') throw('util.list.refresh_row: Object must contain a row');
504                 if (typeof params.my_node == 'undefined') throw('util.list.refresh_row: Object must contain a my_node');
505                 if (params.my_node.nodeName != 'treeitem') throw('util.list.refresh_rwo: my_node must be a treeitem');
506
507                 var s = ('util.list.refresh_row: params = ' + (params) + '\n');
508
509                 var treeitem = params.my_node;
510                 treeitem.setAttribute('retrieve_id',params.retrieve_id);
511                 if (typeof params.to_bottom != 'undefined') {
512                         if (typeof params.no_auto_select == 'undefined') {
513                                 if (!obj.auto_select_pending) {
514                                         obj.auto_select_pending = true;
515                                         setTimeout(function() {
516                                                 dump('auto-selecting\n');
517                                                 var idx = Number(obj.node.view.rowCount)-1;
518                                                 try { obj.node.view.selection.select(idx); } catch(E) { obj.error.sdump('D_WARN','tree auto select: ' + E + '\n'); }
519                                                 try { if (typeof params.on_select == 'function') params.on_select(); } catch(E) { obj.error.sdump('D_WARN','tree auto select, on_select: ' + E + '\n'); }
520                                                 obj.auto_select_pending = false;
521                                                 try { util.widgets.dispatch('flesh',obj.node.contentView.getItemAtIndex(idx).firstChild); } catch(E) { obj.error.sdump('D_WARN','tree auto select, flesh: ' + E + '\n'); }
522                                         }, 1000);
523                                 }
524                         }
525                 }
526                 var delete_me = [];
527                 for (var i in treeitem.childNodes) if (treeitem.childNodes[i].nodeName == 'treerow') delete_me.push(treeitem.childNodes[i]);
528                 for (var i = 0; i < delete_me.length; i++) treeitem.removeChild(delete_me[i]);
529                 var treerow = document.createElement('treerow');
530                 treeitem.appendChild( treerow );
531                 treerow.setAttribute('retrieve_id',params.retrieve_id);
532
533                 s += ('tree = ' + this.node + '\n');
534                 s += ('treeitem = ' + treeitem + '  treerow = ' + treerow + '\n');
535
536                 if (typeof params.retrieve_row == 'function' || typeof this.retrieve_row == 'function') {
537
538                         obj.put_retrieving_label(treerow);
539                         treerow.addEventListener(
540                                 'flesh',
541                                 function() {
542
543                                         if (treerow.getAttribute('retrieved') == 'true') return; /* already running */
544
545                                         treerow.setAttribute('retrieved','true');
546
547                                         //dump('fleshing = ' + params.retrieve_id + '\n');
548
549                                         function inc_fleshed() {
550                                                 if (treerow.getAttribute('fleshed') == 'true') return; /* already fleshed */
551                                                 treerow.setAttribute('fleshed','true');
552                                                 obj.row_count.fleshed++;
553                                                 if (obj.row_count.fleshed >= obj.row_count.total) {
554                                                         setTimeout( function() { obj.exec_on_all_fleshed(); }, 0 );
555                                                 }
556                                         }
557
558                                         params.row_node = treeitem;
559                                         params.on_retrieve = function(p) {
560                                                 try {
561                                                         p.row = params.row;
562                                                         obj._map_row_to_treecell(p,treerow);
563                                                         inc_fleshed();
564                                                         var idx = obj.node.contentView.getIndexOfItem( params.row_node );
565                                                         dump('idx = ' + idx + '\n');
566                                                         // if current row is selected, send another select event to re-sync data that the client code fetches on selects
567                                                         if ( obj.node.view.selection.isSelected( idx ) ) {
568                                                                 dump('dispatching select event for on_retrieve for idx = ' + idx + '\n');
569                                                                 util.widgets.dispatch('select',obj.node);
570                                                         }
571                                                 } catch(E) {
572                             // Let's not alert on this for now.  Getting contentView has no properties in record buckets under certain conditions
573                                                         dump('fixme2: ' + E + '\n');
574                                                 }
575                                         }
576
577                                         if (typeof params.retrieve_row == 'function') {
578
579                                                 params.retrieve_row( params );
580
581                                         } else if (typeof obj.retrieve_row == 'function') {
582
583                                                         obj.retrieve_row( params );
584
585                                         } else {
586                                         
587                                                         inc_fleshed();
588                                         }
589                                 },
590                                 false
591                         );
592                         /*
593                         setTimeout(
594                                 function() {
595                                         util.widgets.dispatch('flesh',treerow);
596                                 }, 0
597                         );
598                         */
599                 } else {
600                         obj.put_retrieving_label(treerow);
601                         treerow.addEventListener(
602                                 'flesh',
603                                 function() {
604                                         //dump('fleshing anon\n');
605                                         if (treerow.getAttribute('fleshed') == 'true') return; /* already fleshed */
606                                         obj._map_row_to_treecell(params,treerow);
607                                         treerow.setAttribute('retrieved','true');
608                                         treerow.setAttribute('fleshed','true');
609                                         obj.row_count.fleshed++;
610                                         if (obj.row_count.fleshed >= obj.row_count.total) {
611                                                 setTimeout( function() { obj.exec_on_all_fleshed(); }, 0 );
612                                         }
613                                 },
614                                 false
615                         );
616                         /*
617                         setTimeout(
618                                 function() {
619                                         util.widgets.dispatch('flesh',treerow);
620                                 }, 0
621                         );
622                         */
623                 }
624                 this.error.sdump('D_LIST',s);
625
626                         try {
627
628                                 if (obj.trim_list && obj.row_count.total >= obj.trim_list) {
629                                         // Remove oldest row
630                                         //if (typeof params.to_bottom != 'undefined') 
631                                         if (typeof params.to_top == 'undefined') {
632                                                 treechildren_node.removeChild( treechildren_node.firstChild );
633                                         } else {
634                                                 treechildren_node.removeChild( treechildren_node.lastChild );
635                                         }
636                                 }
637                         } catch(E) {
638                         }
639
640                 setTimeout( function() { obj.auto_retrieve(); }, 0 );
641
642         JSAN.use('util.widgets'); util.widgets.dispatch('select',obj.node);
643
644                 return params;
645         },
646
647         'put_retrieving_label' : function(treerow) {
648                 var obj = this;
649                 try {
650                         /*
651                         var cols_idx = 0;
652                         dump('put_retrieving_label.  columns = ' + js2JSON(obj.columns) + '\n');
653                         while( obj.columns[cols_idx] && obj.columns[cols_idx].hidden && obj.columns[cols_idx].hidden == 'true') {
654                                 dump('\t' + cols_idx);
655                                 var treecell = document.createElement('treecell');
656                                 treerow.appendChild(treecell);
657                                 cols_idx++;
658                         }
659                         */
660                         for (var i = 0; i < obj.columns.length; i++) {
661                         var treecell = document.createElement('treecell'); treecell.setAttribute('label','Retrieving...');
662                         treerow.appendChild(treecell);
663                         }
664                         /*
665                         dump('\t' + cols_idx + '\n');
666                         */
667                 } catch(E) {
668                         alert(E);
669                 }
670         },
671
672         'detect_visible' : function() {
673                 var obj = this;
674                 try {
675                         //dump('detect_visible  obj.node = ' + obj.node + '\n');
676                         /* FIXME - this is a hack.. if the implementation of tree changes, this could break */
677                         try {
678                                 var scrollbar = document.getAnonymousNodes( document.getAnonymousNodes(obj.node)[1] )[1];
679                                 var curpos = scrollbar.getAttribute('curpos');
680                                 var maxpos = scrollbar.getAttribute('maxpos');
681                                 //alert('curpos = ' + curpos + ' maxpos = ' + maxpos + ' obj.curpos = ' + obj.curpos + ' obj.maxpos = ' + obj.maxpos + '\n');
682                                 if ((curpos != obj.curpos) || (maxpos != obj.maxpos)) {
683                                         if ( obj.auto_retrieve() > 0 ) {
684                                                 obj.curpos = curpos; obj.maxpos = maxpos;
685                                         }
686                                 }
687                         } catch(E) {
688                                 obj.error.sdump('D_XULRUNNER', 'List implementation changed? ' + E);
689                         }
690                 } catch(E) { obj.error.sdump('D_ERROR',E); }
691         },
692
693         'detect_visible_polling' : function() {
694                 try {
695                         //alert('detect_visible_polling');
696                         var obj = this;
697                         obj.detect_visible();
698                         setTimeout(function() { try { obj.detect_visible_polling(); } catch(E) { alert(E); } },2000);
699                 } catch(E) {
700                         alert(E);
701                 }
702         },
703
704
705         'auto_retrieve' : function(params) {
706                 var obj = this;
707                 switch (this.node.nodeName) {
708                         case 'tree' : obj._auto_retrieve_tree(params); break;
709                         default: throw('NYI: Need .auto_retrieve() for ' + obj.node.nodeName); break;
710                 }
711         },
712
713         '_auto_retrieve_tree' : function (params) {
714                 var obj = this;
715                 if (!obj.auto_retrieve_in_progress) {
716                         obj.auto_retrieve_in_progress = true;
717                         setTimeout(
718                                 function() {
719                                         try {
720                                                         //alert('auto_retrieve\n');
721                                                         var count = 0;
722                                                         var startpos = obj.node.treeBoxObject.getFirstVisibleRow();
723                                                         var endpos = obj.node.treeBoxObject.getLastVisibleRow();
724                                                         if (startpos > endpos) endpos = obj.node.treeBoxObject.getPageLength();
725                                                         //dump('startpos = ' + startpos + ' endpos = ' + endpos + '\n');
726                                                         for (var i = startpos; i < endpos + 4; i++) {
727                                                                 try {
728                                                                         //dump('trying index ' + i + '\n');
729                                                                         var item = obj.node.contentView.getItemAtIndex(i).firstChild;
730                                                                         if (item && item.getAttribute('retrieved') != 'true' ) {
731                                                                                 //dump('\tgot an unfleshed item = ' + item + ' = ' + item.nodeName + '\n');
732                                                                                 util.widgets.dispatch('flesh',item); count++;
733                                                                         }
734                                                                 } catch(E) {
735                                                                         //dump(i + ' : ' + E + '\n');
736                                                                 }
737                                                         }
738                                                         obj.auto_retrieve_in_progress = false;
739                                                         return count;
740                                         } catch(E) { alert(E); }
741                                 }, 1
742                         );
743                 }
744         },
745
746     'exec_on_all_fleshed' : function() {
747         var obj = this;
748         try {
749             if (obj.on_all_fleshed) {
750                                 if (typeof obj.on_all_fleshed == 'function') {
751                     dump('exec_on_all_fleshed == function\n');
752                                         setTimeout( 
753                         function() { 
754                             try { obj.on_all_fleshed(); } catch(E) { obj.error.standard_unexpected_error_alert('_full_retrieve_tree callback',obj.on_all_fleshed); }
755                         }, 0 
756                     );
757                                 } else if (typeof obj.on_all_fleshed.length != 'undefined') {
758                     dump('exec_on_all_fleshed == array\n');
759                     setTimeout(
760                         function() {
761                             try {
762                                 dump('exec_on_all_fleshed, processing on_all_fleshed array, length = ' + obj.on_all_fleshed.length + '\n');
763                                 var f = obj.on_all_fleshed.pop();
764                                 if (typeof f == 'function') { 
765                                     try { f(); } catch(E) { obj.error.standard_unexpected_error_alert('_full_retrieve_tree callback',f); } 
766                                 }
767                                 if (obj.on_all_fleshed.length > 0) arguments.callee(); 
768                             } catch(E) {
769                                 obj.error.standard_unexpected_error_alert('exec_on_all_fleshed callback error',E);
770                             }
771                         }, 0
772                     ); 
773                                 } else {
774                     obj.error.standard_unexpected_error_alert('unexpected on_all_fleshed object: ', obj.on_all_fleshed);
775                 }
776             }
777         } catch(E) {
778             obj.error.standard_unexpected_error_alert('exec_on_all-fleshed error',E);
779         }
780     },
781
782         'full_retrieve' : function(params) {
783                 var obj = this;
784                 switch (this.node.nodeName) {
785                         case 'tree' : obj._full_retrieve_tree(params); break;
786                         default: throw('NYI: Need .full_retrieve() for ' + obj.node.nodeName); break;
787                 }
788         },
789
790         '_full_retrieve_tree' : function(params) {
791                 var obj = this;
792                 try {
793                         if (obj.row_count.fleshed >= obj.row_count.total) {
794                                 dump('Full retrieve... tree seems to be in sync\n' + js2JSON(obj.row_count) + '\n');
795                 obj.exec_on_all_fleshed();
796                         } else {
797                                 dump('Full retrieve... syncing tree' + js2JSON(obj.row_count) + '\n');
798                                 JSAN.use('util.widgets');
799                                 var nodes = obj.treechildren.childNodes;
800                                 for (var i = 0; i < nodes.length; i++) {
801                                         util.widgets.dispatch('flesh',nodes[i].firstChild);
802                                 }
803                         }
804                 } catch(E) {
805                         obj.error.standard_unexpected_error_alert('_full_retrieve_tree',E);
806                 }
807         },
808
809         '_append_to_listbox' : function (params) {
810
811                 var obj = this;
812
813                 if (typeof params.row == 'undefined') throw('util.list.append: Object must contain a row');
814
815                 var s = ('util.list.append: params = ' + (params) + '\n');
816
817                 var listitem = document.createElement('listitem');
818
819                 s += ('listbox = ' + this.node + '  listitem = ' + listitem + '\n');
820
821                 if (typeof params.retrieve_row == 'function' || typeof this.retrieve_row == 'function') {
822
823                         setTimeout(
824                                 function() {
825                                         listitem.setAttribute('retrieve_id',params.retrieve_id);
826                                         //FIXME//Make async and fire when row is visible in list
827                                         var row;
828
829                                         params.row_node = listitem;
830                                         params.on_retrieve = function(row) {
831                                                 params.row = row;
832                                                 obj._map_row_to_listcell(params,listitem);
833                                                 obj.node.appendChild( listitem );
834                                                 util.widgets.dispatch('select',obj.node);
835                                         }
836
837                                         if (typeof params.retrieve_row == 'function') {
838
839                                                 row = params.retrieve_row( params );
840
841                                         } else {
842
843                                                 if (typeof obj.retrieve_row == 'function') {
844
845                                                         row = obj.retrieve_row( params );
846
847                                                 }
848                                         }
849                                 }, 0
850                         );
851                 } else {
852                         this._map_row_to_listcell(params,listitem);
853                         this.node.appendChild( listitem );
854                 }
855
856                 this.error.sdump('D_LIST',s);
857                 params.my_node = listitem;
858                 return params;
859
860         },
861
862         '_map_row_to_treecell' : function(params,treerow) {
863                 var obj = this;
864                 var s = '';
865                 util.widgets.remove_children(treerow);
866
867                 if (typeof params.map_row_to_column == 'function' || typeof this.map_row_to_column == 'function') {
868
869                         for (var i = 0; i < this.columns.length; i++) {
870                                 var treecell = document.createElement('treecell');
871                                 var label = '';
872                                 if (params.skip_columns && (params.skip_columns.indexOf(i) != -1)) {
873                                         treecell.setAttribute('label',label);
874                                         treerow.appendChild( treecell );
875                                         s += ('treecell = ' + treecell + ' with label = ' + label + '\n');
876                                         continue;
877                                 }
878                                 if (params.skip_all_columns_except && (params.skip_all_columns_except.indexOf(i) == -1)) {
879                                         treecell.setAttribute('label',label);
880                                         treerow.appendChild( treecell );
881                                         s += ('treecell = ' + treecell + ' with label = ' + label + '\n');
882                                         continue;
883                                 }
884         
885                                 if (typeof params.map_row_to_column == 'function')  {
886         
887                                         label = params.map_row_to_column(params.row,this.columns[i]);
888         
889                                 } else if (typeof this.map_row_to_column == 'function') {
890         
891                                         label = this.map_row_to_column(params.row,this.columns[i]);
892         
893                                 }
894                                 treecell.setAttribute('label',label ? label : '');
895                                 treerow.appendChild( treecell );
896                                 s += ('treecell = ' + treecell + ' with label = ' + label + '\n');
897                         }
898                 } else if (typeof params.map_row_to_columns == 'function' || typeof this.map_row_to_columns == 'function') {
899
900                         var labels = [];
901
902                         if (typeof params.map_row_to_columns == 'function') {
903
904                                 labels = params.map_row_to_columns(params.row,this.columns);
905
906                         } else if (typeof this.map_row_to_columns == 'function') {
907
908                                 labels = this.map_row_to_columns(params.row,this.columns);
909
910                         }
911                         for (var i = 0; i < labels.length; i++) {
912                                 var treecell = document.createElement('treecell');
913                                 treecell.setAttribute('label',typeof labels[i] == 'string' || typeof labels[i] == 'number' ? labels[i] : '');
914                                 treerow.appendChild( treecell );
915                                 s += ('treecell = ' + treecell + ' with label = ' + labels[i] + '\n');
916                         }
917
918                 } else {
919
920                         throw('No row to column mapping function.');
921                 }
922                 this.error.sdump('D_LIST',s);
923         },
924
925         '_map_row_to_listcell' : function(params,listitem) {
926                 var obj = this;
927                 var s = '';
928                 for (var i = 0; i < this.columns.length; i++) {
929                         var value = '';
930                         if (typeof params.map_row_to_column == 'function')  {
931
932                                 value = params.map_row_to_column(params.row,this.columns[i]);
933
934                         } else {
935
936                                 if (typeof this.map_row_to_column == 'function') {
937
938                                         value = this.map_row_to_column(params.row,this.columns[i]);
939                                 }
940                         }
941                         if (typeof value == 'string' || typeof value == 'number') {
942                                 var listcell = document.createElement('listcell');
943                                 listcell.setAttribute('label',value);
944                                 listitem.appendChild(listcell);
945                                 s += ('listcell = ' + listcell + ' with label = ' + value + '\n');
946                         } else {
947                                 listitem.appendChild(value);
948                                 s += ('listcell = ' + value + ' is really a ' + value.nodeName + '\n');
949                         }
950                 }
951                 this.error.sdump('D_LIST',s);
952         },
953
954         'select_all' : function(params) {
955                 var obj = this;
956                 switch(this.node.nodeName) {
957                         case 'tree' : return this._select_all_from_tree(params); break;
958                         default: throw('NYI: Need ._select_all_from_() for ' + this.node.nodeName); break;
959                 }
960         },
961
962         '_select_all_from_tree' : function(params) {
963                 var obj = this;
964                 this.node.view.selection.selectAll();
965         },
966
967         'retrieve_selection' : function(params) {
968                 var obj = this;
969                 switch(this.node.nodeName) {
970                         case 'tree' : return this._retrieve_selection_from_tree(params); break;
971                         default: throw('NYI: Need ._retrieve_selection_from_() for ' + this.node.nodeName); break;
972                 }
973         },
974
975         '_retrieve_selection_from_tree' : function(params) {
976                 var obj = this;
977                 var list = [];
978                 var start = new Object();
979                 var end = new Object();
980                 var numRanges = this.node.view.selection.getRangeCount();
981                 for (var t=0; t<numRanges; t++){
982                         this.node.view.selection.getRangeAt(t,start,end);
983                         for (var v=start.value; v<=end.value; v++){
984                                 var i = this.node.contentView.getItemAtIndex(v);
985                                 list.push( i );
986                         }
987                 }
988                 return list;
989         },
990
991         'dump' : function(params) {
992                 var obj = this;
993                 switch(this.node.nodeName) {
994                         case 'tree' : return this._dump_tree(params); break;
995                         default: throw('NYI: Need .dump() for ' + this.node.nodeName); break;
996                 }
997         },
998
999         '_dump_tree' : function(params) {
1000                 var obj = this;
1001                 var dump = [];
1002                 for (var i = 0; i < this.treechildren.childNodes.length; i++) {
1003                         var row = [];
1004                         var treeitem = this.treechildren.childNodes[i];
1005                         var treerow = treeitem.firstChild;
1006                         for (var j = 0; j < treerow.childNodes.length; j++) {
1007                                 row.push( treerow.childNodes[j].getAttribute('label') );
1008                         }
1009                         dump.push( row );
1010                 }
1011                 return dump;
1012         },
1013
1014         'dump_with_keys' : function(params) {
1015                 var obj = this;
1016                 switch(this.node.nodeName) {
1017                         case 'tree' : return this._dump_tree_with_keys(params); break;
1018                         default: throw('NYI: Need .dump_with_keys() for ' + this.node.nodeName); break;
1019                 }
1020
1021         },
1022
1023         '_dump_tree_with_keys' : function(params) {
1024                 var obj = this;
1025                 var dump = [];
1026                 for (var i = 0; i < this.treechildren.childNodes.length; i++) {
1027                         var row = {};
1028                         var treeitem = this.treechildren.childNodes[i];
1029                         var treerow = treeitem.firstChild;
1030                         for (var j = 0; j < treerow.childNodes.length; j++) {
1031                                 row[ obj.columns[j].id ] = treerow.childNodes[j].getAttribute('label');
1032                         }
1033                         dump.push( row );
1034                 }
1035                 return dump;
1036         },
1037
1038         'dump_csv' : function(params) {
1039                 var obj = this;
1040                 switch(this.node.nodeName) {
1041                         case 'tree' : return this._dump_tree_csv(params); break;
1042                         default: throw('NYI: Need .dump_csv() for ' + this.node.nodeName); break;
1043                 }
1044
1045         },
1046
1047         '_dump_tree_csv' : function(params) {
1048                 var obj = this;
1049                 var dump = '';
1050                 for (var j = 0; j < obj.columns.length; j++) {
1051                         if (obj.node.treeBoxObject.columns.getColumnAt(j).element.getAttribute('hidden') == 'true') {
1052                                 /* skip */
1053                         } else {
1054                                 if (dump) dump += ',';
1055                                 dump += '"' + obj.columns[j].label.replace(/"/g, '""') + '"';
1056                         }
1057                 }
1058                 dump += '\r\n';
1059                 for (var i = 0; i < this.treechildren.childNodes.length; i++) {
1060                         var row = '';
1061                         var treeitem = this.treechildren.childNodes[i];
1062                         var treerow = treeitem.firstChild;
1063                         for (var j = 0; j < treerow.childNodes.length; j++) {
1064                                 if (obj.node.treeBoxObject.columns.getColumnAt(j).element.getAttribute('hidden') == 'true') {
1065                                         /* skip */
1066                                 } else {
1067                                         if (row) row += ',';
1068                                         row += '"' + treerow.childNodes[j].getAttribute('label').replace(/"/g, '""') + '"';
1069                                 }
1070                         }
1071                         dump +=  row + '\r\n';
1072                 }
1073                 return dump;
1074         },
1075
1076     'dump_csv_to_clipboard' : function(params) {
1077         var obj = this;
1078         if (typeof params == 'undefined') params = {};
1079         if (params.no_full_retrieve) {
1080             copy_to_clipboard( obj.dump_csv( params ) );
1081         } else {
1082             obj.wrap_in_full_retrieve( function() { copy_to_clipboard( obj.dump_csv( params ) ); } );
1083         }
1084     },
1085
1086     'dump_csv_to_printer' : function(params) {
1087         var obj = this;
1088         JSAN.use('util.print'); var print = new util.print();
1089         if (typeof params == 'undefined') params = {};
1090         if (params.no_full_retrieve) {
1091             print.simple( obj.dump_csv( params ), {'content_type':'text/plain'} );
1092         } else {
1093             obj.wrap_in_full_retrieve( 
1094                 function() { 
1095                     print.simple( obj.dump_csv( params ), {'content_type':'text/plain'} );
1096                 }
1097             );
1098         }
1099     },
1100
1101     'dump_csv_to_file' : function(params) {
1102         var obj = this;
1103         JSAN.use('util.file'); var f = new util.file();
1104         if (typeof params == 'undefined') params = {};
1105         if (params.no_full_retrieve) {
1106             params.data = obj.dump_csv( params );
1107             params.not_json = true;
1108             if (!params.title) params.title = 'Save List CSV As';
1109             f.export_file( params );
1110         } else {
1111             obj.wrap_in_full_retrieve( 
1112                 function() { 
1113                     params.data = obj.dump_csv( params );
1114                     params.not_json = true;
1115                     if (!params.title) params.title = 'Save List CSV As';
1116                     f.export_file( params );
1117                 }
1118             );
1119         }
1120     },
1121
1122     'print' : function(params) {
1123         if (!params) params = {};
1124                 switch(this.node.nodeName) {
1125                         case 'tree' : return this._print_tree(params); break;
1126                         default: throw('NYI: Need ._print() for ' + this.node.nodeName); break;
1127                 }
1128     },
1129
1130     '_print_tree' : function(params) {
1131         var obj = this;
1132         try {
1133                         JSAN.use('OpenILS.data'); var data = new OpenILS.data(); data.stash_retrieve();
1134             if (!params.staff && data.list.au && data.list.au[0]) {
1135                 params.staff = data.list.au[0];
1136             }
1137             if (!params.lib && data.list.au && data.list.au[0] && data.list.au[0].ws_ou() && data.hash.aou && data.hash.aou[ data.list.au[0].ws_ou() ]) {
1138                 params.lib = data.hash.aou[ data.list.au[0].ws_ou() ];
1139                 params.lib.children(null);
1140             }
1141             if (params.template && data.print_list_templates[ params.template ]) {
1142                 var template = data.print_list_templates[ params.template ];
1143                 for (var i in template) params[i] = template[i];
1144             }
1145             obj.wrap_in_full_retrieve(
1146                 function() {
1147                     try {
1148                         if (!params.list) params.list = obj.dump_with_keys();
1149                         JSAN.use('util.print'); var print = new util.print();
1150                         print.tree_list( params );
1151                         if (typeof params.callback == 'function') params.callback();
1152                     } catch(E) {
1153                                     obj.error.standard_unexpected_error_alert('inner _print_tree',E);
1154                     }
1155                 }
1156             );
1157             
1158         } catch(E) {
1159                         obj.error.standard_unexpected_error_alert('_print_tree',E);
1160         }
1161     },
1162
1163         'dump_selected_with_keys' : function(params) {
1164                 var obj = this;
1165                 switch(this.node.nodeName) {
1166                         case 'tree' : return this._dump_tree_selection_with_keys(params); break;
1167                         default: throw('NYI: Need .dump_selection_with_keys() for ' + this.node.nodeName); break;
1168                 }
1169
1170         },
1171
1172         '_dump_tree_selection_with_keys' : function(params) {
1173                 var obj = this;
1174                 var dump = [];
1175                 var list = obj._retrieve_selection_from_tree();
1176                 for (var i = 0; i < list.length; i++) {
1177                         var row = {};
1178                         var treeitem = list[i];
1179                         var treerow = treeitem.firstChild;
1180                         for (var j = 0; j < treerow.childNodes.length; j++) {
1181                                 var value = treerow.childNodes[j].getAttribute('label');
1182                                 if (params.skip_hidden_columns) if (obj.node.treeBoxObject.columns.getColumnAt(j).element.getAttribute('hidden') == 'true') continue;
1183                                 var id = obj.columns[j].id; if (params.labels_instead_of_ids) id = obj.columns[j].label;
1184                                 row[ id ] = value;
1185                         }
1186                         dump.push( row );
1187                 }
1188                 return dump;
1189         },
1190
1191         'clipboard' : function() {
1192                 try {
1193                         var obj = this;
1194                         var dump = obj.dump_selected_with_keys({'skip_hidden_columns':true,'labels_instead_of_ids':true});
1195                         JSAN.use('OpenILS.data'); var data = new OpenILS.data(); data.stash_retrieve();
1196                         data.list_clipboard = dump; data.stash('list_clipboard');
1197                         JSAN.use('util.window'); var win = new util.window();
1198                         win.open(urls.XUL_LIST_CLIPBOARD,'list_clipboard','chrome,resizable,modal');
1199             window.focus(); // sometimes the main window will lower after a clipboard action
1200                 } catch(E) {
1201                         this.error.standard_unexpected_error_alert('clipboard',E);
1202                 }
1203         },
1204
1205         'dump_retrieve_ids' : function(params) {
1206                 var obj = this;
1207                 switch(this.node.nodeName) {
1208                         case 'tree' : return this._dump_retrieve_ids_tree(params); break;
1209                         default: throw('NYI: Need .dump_retrieve_ids() for ' + this.node.nodeName); break;
1210                 }
1211         },
1212
1213         '_dump_retrieve_ids_tree' : function(params) {
1214                 var obj = this;
1215                 var dump = [];
1216                 for (var i = 0; i < this.treechildren.childNodes.length; i++) {
1217                         var treeitem = this.treechildren.childNodes[i];
1218                         dump.push( treeitem.getAttribute('retrieve_id') );
1219                 }
1220                 return dump;
1221         },
1222
1223     'wrap_in_full_retrieve' : function(f) {
1224         var obj = this;
1225                 if (typeof obj.on_all_fleshed == 'function') { // legacy
1226             obj.on_all_fleshed = [ obj.on_all_fleshed ];
1227                 }
1228         if (! obj.on_all_fleshed) obj.on_all_fleshed = [];
1229         obj.on_all_fleshed.push(f);
1230         obj.full_retrieve();
1231     },
1232
1233         '_sort_tree' : function(col,sortDir) {
1234                 var obj = this;
1235                 try {
1236                         if (obj.node.getAttribute('no_sort')) {
1237                                 return;
1238                         }
1239                         var col_pos;
1240                         for (var i = 0; i < obj.columns.length; i++) { 
1241                                 if (obj.columns[i].id == col.id) col_pos = function(a){return a;}(i); 
1242                         }
1243             obj.wrap_in_full_retrieve(
1244                 function() {
1245                                         try {
1246                                                 JSAN.use('util.money');
1247                                                 var rows = [];
1248                                                 var treeitems = obj.treechildren.childNodes;
1249                                                 for (var i = 0; i < treeitems.length; i++) {
1250                                                         var treeitem = treeitems[i];
1251                                                         var treerow = treeitem.firstChild;
1252                                                         var treecell = treerow.childNodes[ col_pos ];
1253                                                         value = ( { 'value' : treecell ? treecell.getAttribute('label') : '', 'node' : treeitem } );
1254                                                         rows.push( value );
1255                                                 }
1256                                                 rows = rows.sort( function(a,b) { 
1257                                                         a = a.value; b = b.value; 
1258                                                         if (col.getAttribute('sort_type')) {
1259                                                                 switch(col.getAttribute('sort_type')) {
1260                                                                         case 'number' :
1261                                                                                 a = Number(a); b = Number(b);
1262                                                                         break;
1263                                                                         case 'money' :
1264                                                                                 a = util.money.dollars_float_to_cents_integer(a);
1265                                                                                 b = util.money.dollars_float_to_cents_integer(b);
1266                                                                         break;
1267                                                                         case 'title' : /* special case for "a" and "the".  doesn't use marc 245 indicator */
1268                                                                                 a = String( a ).toUpperCase().replace( /^\s*(THE|A|AN)\s+/, '' );
1269                                                                                 b = String( b ).toUpperCase().replace( /^\s*(THE|A|AN)\s+/, '' );
1270                                                                         break;
1271                                                                         default:
1272                                                                                 a = String( a ).toUpperCase();
1273                                                                                 b = String( b ).toUpperCase();
1274                                                                         break;
1275                                                                 }
1276                                                         } else {
1277                                                                 if (typeof a == 'string' || typeof b == 'string') {
1278                                                                         a = String( a ).toUpperCase();
1279                                                                         b = String( b ).toUpperCase();
1280                                                                 }
1281                                                         }
1282                                                         if (a < b) return -1; 
1283                                                         if (a > b) return 1; 
1284                                                         return 0; 
1285                                                 } );
1286                                                 if (sortDir == 'asc') rows = rows.reverse();
1287                                                 while(obj.treechildren.lastChild) obj.treechildren.removeChild( obj.treechildren.lastChild );
1288                                                 for (var i = 0; i < rows.length; i++) {
1289                                                         obj.treechildren.appendChild( rows[i].node );
1290                                                 }
1291                                         } catch(E) {
1292                                                 obj.error.standard_unexpected_error_alert('sorting',E); 
1293                                         }
1294                                 }
1295             );
1296                 } catch(E) {
1297                         obj.error.standard_unexpected_error_alert('pre sorting', E);
1298                 }
1299         }
1300
1301 }
1302 dump('exiting util.list.js\n');