]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/xul/staff_client/chrome/content/util/list.js
change D_ALERT log level to D_WARN to avoid unneeded popups
[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                                                         alert('fixme2: ' + E);
426                                                 }
427                                         }
428
429                                         if (typeof params.retrieve_row == 'function') {
430
431                                                 params.retrieve_row( params );
432
433                                         } else if (typeof obj.retrieve_row == 'function') {
434
435                                                         obj.retrieve_row( params );
436
437                                         } else {
438                                         
439                                                         inc_fleshed();
440                                         }
441                                 },
442                                 false
443                         );
444                         /*
445                         setTimeout(
446                                 function() {
447                                         util.widgets.dispatch('flesh',treerow);
448                                 }, 0
449                         );
450                         */
451                 } else {
452                         obj.put_retrieving_label(treerow);
453                         treerow.addEventListener(
454                                 'flesh',
455                                 function() {
456                                         //dump('fleshing anon\n');
457                                         if (treerow.getAttribute('fleshed') == 'true') return; /* already fleshed */
458                                         obj._map_row_to_treecell(params,treerow);
459                                         treerow.setAttribute('retrieved','true');
460                                         treerow.setAttribute('fleshed','true');
461                                         obj.row_count.fleshed++;
462                                         if (obj.row_count.fleshed >= obj.row_count.total) {
463                                                 setTimeout( function() { obj.exec_on_all_fleshed(); }, 0 );
464                                         }
465                                 },
466                                 false
467                         );
468                         /*
469                         setTimeout(
470                                 function() {
471                                         util.widgets.dispatch('flesh',treerow);
472                                 }, 0
473                         );
474                         */
475                 }
476                 this.error.sdump('D_LIST',s);
477
478                         try {
479
480                                 if (obj.trim_list && obj.row_count.total >= obj.trim_list) {
481                                         // Remove oldest row
482                                         //if (typeof params.to_bottom != 'undefined') 
483                                         if (typeof params.to_top == 'undefined') {
484                                                 treechildren_node.removeChild( treechildren_node.firstChild );
485                                         } else {
486                                                 treechildren_node.removeChild( treechildren_node.lastChild );
487                                         }
488                                 }
489                         } catch(E) {
490                         }
491
492                 setTimeout( function() { obj.auto_retrieve(); }, 0 );
493
494                 params.my_node = treeitem;
495                 return params;
496         },
497
498         '_refresh_row_in_tree' : function (params) {
499
500                 var obj = this;
501
502                 if (typeof params.row == 'undefined') throw('util.list.refresh_row: Object must contain a row');
503                 if (typeof params.my_node == 'undefined') throw('util.list.refresh_row: Object must contain a my_node');
504                 if (params.my_node.nodeName != 'treeitem') throw('util.list.refresh_rwo: my_node must be a treeitem');
505
506                 var s = ('util.list.refresh_row: params = ' + (params) + '\n');
507
508                 var treeitem = params.my_node;
509                 treeitem.setAttribute('retrieve_id',params.retrieve_id);
510                 if (typeof params.to_bottom != 'undefined') {
511                         if (typeof params.no_auto_select == 'undefined') {
512                                 if (!obj.auto_select_pending) {
513                                         obj.auto_select_pending = true;
514                                         setTimeout(function() {
515                                                 dump('auto-selecting\n');
516                                                 var idx = Number(obj.node.view.rowCount)-1;
517                                                 try { obj.node.view.selection.select(idx); } catch(E) { obj.error.sdump('D_WARN','tree auto select: ' + E + '\n'); }
518                                                 try { if (typeof params.on_select == 'function') params.on_select(); } catch(E) { obj.error.sdump('D_WARN','tree auto select, on_select: ' + E + '\n'); }
519                                                 obj.auto_select_pending = false;
520                                                 try { util.widgets.dispatch('flesh',obj.node.contentView.getItemAtIndex(idx).firstChild); } catch(E) { obj.error.sdump('D_WARN','tree auto select, flesh: ' + E + '\n'); }
521                                         }, 1000);
522                                 }
523                         }
524                 }
525                 var delete_me = [];
526                 for (var i in treeitem.childNodes) if (treeitem.childNodes[i].nodeName == 'treerow') delete_me.push(treeitem.childNodes[i]);
527                 for (var i = 0; i < delete_me.length; i++) treeitem.removeChild(delete_me[i]);
528                 var treerow = document.createElement('treerow');
529                 treeitem.appendChild( treerow );
530                 treerow.setAttribute('retrieve_id',params.retrieve_id);
531
532                 s += ('tree = ' + this.node + '\n');
533                 s += ('treeitem = ' + treeitem + '  treerow = ' + treerow + '\n');
534
535                 if (typeof params.retrieve_row == 'function' || typeof this.retrieve_row == 'function') {
536
537                         obj.put_retrieving_label(treerow);
538                         treerow.addEventListener(
539                                 'flesh',
540                                 function() {
541
542                                         if (treerow.getAttribute('retrieved') == 'true') return; /* already running */
543
544                                         treerow.setAttribute('retrieved','true');
545
546                                         //dump('fleshing = ' + params.retrieve_id + '\n');
547
548                                         function inc_fleshed() {
549                                                 if (treerow.getAttribute('fleshed') == 'true') return; /* already fleshed */
550                                                 treerow.setAttribute('fleshed','true');
551                                                 obj.row_count.fleshed++;
552                                                 if (obj.row_count.fleshed >= obj.row_count.total) {
553                                                         setTimeout( function() { obj.exec_on_all_fleshed(); }, 0 );
554                                                 }
555                                         }
556
557                                         params.row_node = treeitem;
558                                         params.on_retrieve = function(p) {
559                                                 try {
560                                                         p.row = params.row;
561                                                         obj._map_row_to_treecell(p,treerow);
562                                                         inc_fleshed();
563                                                         var idx = obj.node.contentView.getIndexOfItem( params.row_node );
564                                                         dump('idx = ' + idx + '\n');
565                                                         // if current row is selected, send another select event to re-sync data that the client code fetches on selects
566                                                         if ( obj.node.view.selection.isSelected( idx ) ) {
567                                                                 dump('dispatching select event for on_retrieve for idx = ' + idx + '\n');
568                                                                 util.widgets.dispatch('select',obj.node);
569                                                         }
570                                                 } catch(E) {
571                                                         alert('fixme2: ' + E);
572                                                 }
573                                         }
574
575                                         if (typeof params.retrieve_row == 'function') {
576
577                                                 params.retrieve_row( params );
578
579                                         } else if (typeof obj.retrieve_row == 'function') {
580
581                                                         obj.retrieve_row( params );
582
583                                         } else {
584                                         
585                                                         inc_fleshed();
586                                         }
587                                 },
588                                 false
589                         );
590                         /*
591                         setTimeout(
592                                 function() {
593                                         util.widgets.dispatch('flesh',treerow);
594                                 }, 0
595                         );
596                         */
597                 } else {
598                         obj.put_retrieving_label(treerow);
599                         treerow.addEventListener(
600                                 'flesh',
601                                 function() {
602                                         //dump('fleshing anon\n');
603                                         if (treerow.getAttribute('fleshed') == 'true') return; /* already fleshed */
604                                         obj._map_row_to_treecell(params,treerow);
605                                         treerow.setAttribute('retrieved','true');
606                                         treerow.setAttribute('fleshed','true');
607                                         obj.row_count.fleshed++;
608                                         if (obj.row_count.fleshed >= obj.row_count.total) {
609                                                 setTimeout( function() { obj.exec_on_all_fleshed(); }, 0 );
610                                         }
611                                 },
612                                 false
613                         );
614                         /*
615                         setTimeout(
616                                 function() {
617                                         util.widgets.dispatch('flesh',treerow);
618                                 }, 0
619                         );
620                         */
621                 }
622                 this.error.sdump('D_LIST',s);
623
624                         try {
625
626                                 if (obj.trim_list && obj.row_count.total >= obj.trim_list) {
627                                         // Remove oldest row
628                                         //if (typeof params.to_bottom != 'undefined') 
629                                         if (typeof params.to_top == 'undefined') {
630                                                 treechildren_node.removeChild( treechildren_node.firstChild );
631                                         } else {
632                                                 treechildren_node.removeChild( treechildren_node.lastChild );
633                                         }
634                                 }
635                         } catch(E) {
636                         }
637
638                 setTimeout( function() { obj.auto_retrieve(); }, 0 );
639
640         JSAN.use('util.widgets'); util.widgets.dispatch('select',obj.node);
641
642                 return params;
643         },
644
645         'put_retrieving_label' : function(treerow) {
646                 var obj = this;
647                 try {
648                         /*
649                         var cols_idx = 0;
650                         dump('put_retrieving_label.  columns = ' + js2JSON(obj.columns) + '\n');
651                         while( obj.columns[cols_idx] && obj.columns[cols_idx].hidden && obj.columns[cols_idx].hidden == 'true') {
652                                 dump('\t' + cols_idx);
653                                 var treecell = document.createElement('treecell');
654                                 treerow.appendChild(treecell);
655                                 cols_idx++;
656                         }
657                         */
658                         for (var i = 0; i < obj.columns.length; i++) {
659                         var treecell = document.createElement('treecell'); treecell.setAttribute('label','Retrieving...');
660                         treerow.appendChild(treecell);
661                         }
662                         /*
663                         dump('\t' + cols_idx + '\n');
664                         */
665                 } catch(E) {
666                         alert(E);
667                 }
668         },
669
670         'detect_visible' : function() {
671                 var obj = this;
672                 try {
673                         //dump('detect_visible  obj.node = ' + obj.node + '\n');
674                         /* FIXME - this is a hack.. if the implementation of tree changes, this could break */
675                         try {
676                                 var scrollbar = document.getAnonymousNodes( document.getAnonymousNodes(obj.node)[1] )[1];
677                                 var curpos = scrollbar.getAttribute('curpos');
678                                 var maxpos = scrollbar.getAttribute('maxpos');
679                                 //alert('curpos = ' + curpos + ' maxpos = ' + maxpos + ' obj.curpos = ' + obj.curpos + ' obj.maxpos = ' + obj.maxpos + '\n');
680                                 if ((curpos != obj.curpos) || (maxpos != obj.maxpos)) {
681                                         if ( obj.auto_retrieve() > 0 ) {
682                                                 obj.curpos = curpos; obj.maxpos = maxpos;
683                                         }
684                                 }
685                         } catch(E) {
686                                 obj.error.sdump('D_XULRUNNER', 'List implementation changed? ' + E);
687                         }
688                 } catch(E) { obj.error.sdump('D_ERROR',E); }
689         },
690
691         'detect_visible_polling' : function() {
692                 try {
693                         //alert('detect_visible_polling');
694                         var obj = this;
695                         obj.detect_visible();
696                         setTimeout(function() { try { obj.detect_visible_polling(); } catch(E) { alert(E); } },2000);
697                 } catch(E) {
698                         alert(E);
699                 }
700         },
701
702
703         'auto_retrieve' : function(params) {
704                 var obj = this;
705                 switch (this.node.nodeName) {
706                         case 'tree' : obj._auto_retrieve_tree(params); break;
707                         default: throw('NYI: Need .auto_retrieve() for ' + obj.node.nodeName); break;
708                 }
709         },
710
711         '_auto_retrieve_tree' : function (params) {
712                 var obj = this;
713                 if (!obj.auto_retrieve_in_progress) {
714                         obj.auto_retrieve_in_progress = true;
715                         setTimeout(
716                                 function() {
717                                         try {
718                                                         //alert('auto_retrieve\n');
719                                                         var count = 0;
720                                                         var startpos = obj.node.treeBoxObject.getFirstVisibleRow();
721                                                         var endpos = obj.node.treeBoxObject.getLastVisibleRow();
722                                                         if (startpos > endpos) endpos = obj.node.treeBoxObject.getPageLength();
723                                                         //dump('startpos = ' + startpos + ' endpos = ' + endpos + '\n');
724                                                         for (var i = startpos; i < endpos + 4; i++) {
725                                                                 try {
726                                                                         //dump('trying index ' + i + '\n');
727                                                                         var item = obj.node.contentView.getItemAtIndex(i).firstChild;
728                                                                         if (item && item.getAttribute('retrieved') != 'true' ) {
729                                                                                 //dump('\tgot an unfleshed item = ' + item + ' = ' + item.nodeName + '\n');
730                                                                                 util.widgets.dispatch('flesh',item); count++;
731                                                                         }
732                                                                 } catch(E) {
733                                                                         //dump(i + ' : ' + E + '\n');
734                                                                 }
735                                                         }
736                                                         obj.auto_retrieve_in_progress = false;
737                                                         return count;
738                                         } catch(E) { alert(E); }
739                                 }, 1
740                         );
741                 }
742         },
743
744     'exec_on_all_fleshed' : function() {
745         var obj = this;
746         try {
747             if (obj.on_all_fleshed) {
748                                 if (typeof obj.on_all_fleshed == 'function') {
749                     dump('exec_on_all_fleshed == function\n');
750                                         setTimeout( 
751                         function() { 
752                             try { obj.on_all_fleshed(); } catch(E) { obj.error.standard_unexpected_error_alert('_full_retrieve_tree callback',obj.on_all_fleshed); }
753                         }, 0 
754                     );
755                                 } else if (typeof obj.on_all_fleshed.length != 'undefined') {
756                     dump('exec_on_all_fleshed == array\n');
757                     setTimeout(
758                         function() {
759                             try {
760                                 dump('exec_on_all_fleshed, processing on_all_fleshed array, length = ' + obj.on_all_fleshed.length + '\n');
761                                 var f = obj.on_all_fleshed.pop();
762                                 if (typeof f == 'function') { 
763                                     try { f(); } catch(E) { obj.error.standard_unexpected_error_alert('_full_retrieve_tree callback',f); } 
764                                 }
765                                 if (obj.on_all_fleshed.length > 0) arguments.callee(); 
766                             } catch(E) {
767                                 obj.error.standard_unexpected_error_alert('exec_on_all_fleshed callback error',E);
768                             }
769                         }, 0
770                     ); 
771                                 } else {
772                     obj.error.standard_unexpected_error_alert('unexpected on_all_fleshed object: ', obj.on_all_fleshed);
773                 }
774             }
775         } catch(E) {
776             obj.error.standard_unexpected_error_alert('exec_on_all-fleshed error',E);
777         }
778     },
779
780         'full_retrieve' : function(params) {
781                 var obj = this;
782                 switch (this.node.nodeName) {
783                         case 'tree' : obj._full_retrieve_tree(params); break;
784                         default: throw('NYI: Need .full_retrieve() for ' + obj.node.nodeName); break;
785                 }
786         },
787
788         '_full_retrieve_tree' : function(params) {
789                 var obj = this;
790                 try {
791                         if (obj.row_count.fleshed >= obj.row_count.total) {
792                                 dump('Full retrieve... tree seems to be in sync\n' + js2JSON(obj.row_count) + '\n');
793                 obj.exec_on_all_fleshed();
794                         } else {
795                                 dump('Full retrieve... syncing tree' + js2JSON(obj.row_count) + '\n');
796                                 JSAN.use('util.widgets');
797                                 var nodes = obj.treechildren.childNodes;
798                                 for (var i = 0; i < nodes.length; i++) {
799                                         util.widgets.dispatch('flesh',nodes[i].firstChild);
800                                 }
801                         }
802                 } catch(E) {
803                         obj.error.standard_unexpected_error_alert('_full_retrieve_tree',E);
804                 }
805         },
806
807         '_append_to_listbox' : function (params) {
808
809                 var obj = this;
810
811                 if (typeof params.row == 'undefined') throw('util.list.append: Object must contain a row');
812
813                 var s = ('util.list.append: params = ' + (params) + '\n');
814
815                 var listitem = document.createElement('listitem');
816
817                 s += ('listbox = ' + this.node + '  listitem = ' + listitem + '\n');
818
819                 if (typeof params.retrieve_row == 'function' || typeof this.retrieve_row == 'function') {
820
821                         setTimeout(
822                                 function() {
823                                         listitem.setAttribute('retrieve_id',params.retrieve_id);
824                                         //FIXME//Make async and fire when row is visible in list
825                                         var row;
826
827                                         params.row_node = listitem;
828                                         params.on_retrieve = function(row) {
829                                                 params.row = row;
830                                                 obj._map_row_to_listcell(params,listitem);
831                                                 obj.node.appendChild( listitem );
832                                                 util.widgets.dispatch('select',obj.node);
833                                         }
834
835                                         if (typeof params.retrieve_row == 'function') {
836
837                                                 row = params.retrieve_row( params );
838
839                                         } else {
840
841                                                 if (typeof obj.retrieve_row == 'function') {
842
843                                                         row = obj.retrieve_row( params );
844
845                                                 }
846                                         }
847                                 }, 0
848                         );
849                 } else {
850                         this._map_row_to_listcell(params,listitem);
851                         this.node.appendChild( listitem );
852                 }
853
854                 this.error.sdump('D_LIST',s);
855                 params.my_node = listitem;
856                 return params;
857
858         },
859
860         '_map_row_to_treecell' : function(params,treerow) {
861                 var obj = this;
862                 var s = '';
863                 util.widgets.remove_children(treerow);
864
865                 if (typeof params.map_row_to_column == 'function' || typeof this.map_row_to_column == 'function') {
866
867                         for (var i = 0; i < this.columns.length; i++) {
868                                 var treecell = document.createElement('treecell');
869                                 var label = '';
870                                 if (params.skip_columns && (params.skip_columns.indexOf(i) != -1)) {
871                                         treecell.setAttribute('label',label);
872                                         treerow.appendChild( treecell );
873                                         s += ('treecell = ' + treecell + ' with label = ' + label + '\n');
874                                         continue;
875                                 }
876                                 if (params.skip_all_columns_except && (params.skip_all_columns_except.indexOf(i) == -1)) {
877                                         treecell.setAttribute('label',label);
878                                         treerow.appendChild( treecell );
879                                         s += ('treecell = ' + treecell + ' with label = ' + label + '\n');
880                                         continue;
881                                 }
882         
883                                 if (typeof params.map_row_to_column == 'function')  {
884         
885                                         label = params.map_row_to_column(params.row,this.columns[i]);
886         
887                                 } else if (typeof this.map_row_to_column == 'function') {
888         
889                                         label = this.map_row_to_column(params.row,this.columns[i]);
890         
891                                 }
892                                 treecell.setAttribute('label',label ? label : '');
893                                 treerow.appendChild( treecell );
894                                 s += ('treecell = ' + treecell + ' with label = ' + label + '\n');
895                         }
896                 } else if (typeof params.map_row_to_columns == 'function' || typeof this.map_row_to_columns == 'function') {
897
898                         var labels = [];
899
900                         if (typeof params.map_row_to_columns == 'function') {
901
902                                 labels = params.map_row_to_columns(params.row,this.columns);
903
904                         } else if (typeof this.map_row_to_columns == 'function') {
905
906                                 labels = this.map_row_to_columns(params.row,this.columns);
907
908                         }
909                         for (var i = 0; i < labels.length; i++) {
910                                 var treecell = document.createElement('treecell');
911                                 treecell.setAttribute('label',typeof labels[i] == 'string' || typeof labels[i] == 'number' ? labels[i] : '');
912                                 treerow.appendChild( treecell );
913                                 s += ('treecell = ' + treecell + ' with label = ' + labels[i] + '\n');
914                         }
915
916                 } else {
917
918                         throw('No row to column mapping function.');
919                 }
920                 this.error.sdump('D_LIST',s);
921         },
922
923         '_map_row_to_listcell' : function(params,listitem) {
924                 var obj = this;
925                 var s = '';
926                 for (var i = 0; i < this.columns.length; i++) {
927                         var value = '';
928                         if (typeof params.map_row_to_column == 'function')  {
929
930                                 value = params.map_row_to_column(params.row,this.columns[i]);
931
932                         } else {
933
934                                 if (typeof this.map_row_to_column == 'function') {
935
936                                         value = this.map_row_to_column(params.row,this.columns[i]);
937                                 }
938                         }
939                         if (typeof value == 'string' || typeof value == 'number') {
940                                 var listcell = document.createElement('listcell');
941                                 listcell.setAttribute('label',value);
942                                 listitem.appendChild(listcell);
943                                 s += ('listcell = ' + listcell + ' with label = ' + value + '\n');
944                         } else {
945                                 listitem.appendChild(value);
946                                 s += ('listcell = ' + value + ' is really a ' + value.nodeName + '\n');
947                         }
948                 }
949                 this.error.sdump('D_LIST',s);
950         },
951
952         'select_all' : function(params) {
953                 var obj = this;
954                 switch(this.node.nodeName) {
955                         case 'tree' : return this._select_all_from_tree(params); break;
956                         default: throw('NYI: Need ._select_all_from_() for ' + this.node.nodeName); break;
957                 }
958         },
959
960         '_select_all_from_tree' : function(params) {
961                 var obj = this;
962                 this.node.view.selection.selectAll();
963         },
964
965         'retrieve_selection' : function(params) {
966                 var obj = this;
967                 switch(this.node.nodeName) {
968                         case 'tree' : return this._retrieve_selection_from_tree(params); break;
969                         default: throw('NYI: Need ._retrieve_selection_from_() for ' + this.node.nodeName); break;
970                 }
971         },
972
973         '_retrieve_selection_from_tree' : function(params) {
974                 var obj = this;
975                 var list = [];
976                 var start = new Object();
977                 var end = new Object();
978                 var numRanges = this.node.view.selection.getRangeCount();
979                 for (var t=0; t<numRanges; t++){
980                         this.node.view.selection.getRangeAt(t,start,end);
981                         for (var v=start.value; v<=end.value; v++){
982                                 var i = this.node.contentView.getItemAtIndex(v);
983                                 list.push( i );
984                         }
985                 }
986                 return list;
987         },
988
989         'dump' : function(params) {
990                 var obj = this;
991                 switch(this.node.nodeName) {
992                         case 'tree' : return this._dump_tree(params); break;
993                         default: throw('NYI: Need .dump() for ' + this.node.nodeName); break;
994                 }
995         },
996
997         '_dump_tree' : function(params) {
998                 var obj = this;
999                 var dump = [];
1000                 for (var i = 0; i < this.treechildren.childNodes.length; i++) {
1001                         var row = [];
1002                         var treeitem = this.treechildren.childNodes[i];
1003                         var treerow = treeitem.firstChild;
1004                         for (var j = 0; j < treerow.childNodes.length; j++) {
1005                                 row.push( treerow.childNodes[j].getAttribute('label') );
1006                         }
1007                         dump.push( row );
1008                 }
1009                 return dump;
1010         },
1011
1012         'dump_with_keys' : function(params) {
1013                 var obj = this;
1014                 switch(this.node.nodeName) {
1015                         case 'tree' : return this._dump_tree_with_keys(params); break;
1016                         default: throw('NYI: Need .dump_with_keys() for ' + this.node.nodeName); break;
1017                 }
1018
1019         },
1020
1021         '_dump_tree_with_keys' : function(params) {
1022                 var obj = this;
1023                 var dump = [];
1024                 for (var i = 0; i < this.treechildren.childNodes.length; i++) {
1025                         var row = {};
1026                         var treeitem = this.treechildren.childNodes[i];
1027                         var treerow = treeitem.firstChild;
1028                         for (var j = 0; j < treerow.childNodes.length; j++) {
1029                                 row[ obj.columns[j].id ] = treerow.childNodes[j].getAttribute('label');
1030                         }
1031                         dump.push( row );
1032                 }
1033                 return dump;
1034         },
1035
1036         'dump_csv' : function(params) {
1037                 var obj = this;
1038                 switch(this.node.nodeName) {
1039                         case 'tree' : return this._dump_tree_csv(params); break;
1040                         default: throw('NYI: Need .dump_csv() for ' + this.node.nodeName); break;
1041                 }
1042
1043         },
1044
1045         '_dump_tree_csv' : function(params) {
1046                 var obj = this;
1047                 var dump = '';
1048                 for (var j = 0; j < obj.columns.length; j++) {
1049                         if (obj.node.treeBoxObject.columns.getColumnAt(j).element.getAttribute('hidden') == 'true') {
1050                                 /* skip */
1051                         } else {
1052                                 if (dump) dump += ',';
1053                                 dump += '"' + obj.columns[j].label.replace(/"/g, '""') + '"';
1054                         }
1055                 }
1056                 dump += '\r\n';
1057                 for (var i = 0; i < this.treechildren.childNodes.length; i++) {
1058                         var row = '';
1059                         var treeitem = this.treechildren.childNodes[i];
1060                         var treerow = treeitem.firstChild;
1061                         for (var j = 0; j < treerow.childNodes.length; j++) {
1062                                 if (obj.node.treeBoxObject.columns.getColumnAt(j).element.getAttribute('hidden') == 'true') {
1063                                         /* skip */
1064                                 } else {
1065                                         if (row) row += ',';
1066                                         row += '"' + treerow.childNodes[j].getAttribute('label').replace(/"/g, '""') + '"';
1067                                 }
1068                         }
1069                         dump +=  row + '\r\n';
1070                 }
1071                 return dump;
1072         },
1073
1074     'dump_csv_to_clipboard' : function(params) {
1075         var obj = this;
1076         if (params && params.no_full_retrieve) {
1077             copy_to_clipboard( obj.dump_csv( params ) );
1078         } else {
1079             obj.wrap_in_full_retrieve( function() { copy_to_clipboard( obj.dump_csv( params ) ); } );
1080         }
1081     },
1082
1083     'print' : function(params) {
1084         if (!params) params = {};
1085                 switch(this.node.nodeName) {
1086                         case 'tree' : return this._print_tree(params); break;
1087                         default: throw('NYI: Need ._print() for ' + this.node.nodeName); break;
1088                 }
1089     },
1090
1091     '_print_tree' : function(params) {
1092         var obj = this;
1093         try {
1094                         JSAN.use('OpenILS.data'); var data = new OpenILS.data(); data.stash_retrieve();
1095             if (!params.staff && data.list.au && data.list.au[0]) {
1096                 params.staff = data.list.au[0];
1097             }
1098             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() ]) {
1099                 params.lib = data.hash.aou[ data.list.au[0].ws_ou() ];
1100                 params.lib.children(null);
1101             }
1102             if (params.template && data.print_list_templates[ params.template ]) {
1103                 var template = data.print_list_templates[ params.template ];
1104                 for (var i in template) params[i] = template[i];
1105             }
1106             obj.wrap_in_full_retrieve(
1107                 function() {
1108                     try {
1109                         if (!params.list) params.list = obj.dump_with_keys();
1110                         JSAN.use('util.print'); var print = new util.print();
1111                         print.tree_list( params );
1112                         if (typeof params.callback == 'function') params.callback();
1113                     } catch(E) {
1114                                     obj.error.standard_unexpected_error_alert('inner _print_tree',E);
1115                     }
1116                 }
1117             );
1118             
1119         } catch(E) {
1120                         obj.error.standard_unexpected_error_alert('_print_tree',E);
1121         }
1122     },
1123
1124         'dump_selected_with_keys' : function(params) {
1125                 var obj = this;
1126                 switch(this.node.nodeName) {
1127                         case 'tree' : return this._dump_tree_selection_with_keys(params); break;
1128                         default: throw('NYI: Need .dump_selection_with_keys() for ' + this.node.nodeName); break;
1129                 }
1130
1131         },
1132
1133         '_dump_tree_selection_with_keys' : function(params) {
1134                 var obj = this;
1135                 var dump = [];
1136                 var list = obj._retrieve_selection_from_tree();
1137                 for (var i = 0; i < list.length; i++) {
1138                         var row = {};
1139                         var treeitem = list[i];
1140                         var treerow = treeitem.firstChild;
1141                         for (var j = 0; j < treerow.childNodes.length; j++) {
1142                                 var value = treerow.childNodes[j].getAttribute('label');
1143                                 if (params.skip_hidden_columns) if (obj.node.treeBoxObject.columns.getColumnAt(j).element.getAttribute('hidden') == 'true') continue;
1144                                 var id = obj.columns[j].id; if (params.labels_instead_of_ids) id = obj.columns[j].label;
1145                                 row[ id ] = value;
1146                         }
1147                         dump.push( row );
1148                 }
1149                 return dump;
1150         },
1151
1152         'clipboard' : function() {
1153                 try {
1154                         var obj = this;
1155                         var dump = obj.dump_selected_with_keys({'skip_hidden_columns':true,'labels_instead_of_ids':true});
1156                         JSAN.use('OpenILS.data'); var data = new OpenILS.data(); data.stash_retrieve();
1157                         data.list_clipboard = dump; data.stash('list_clipboard');
1158                         JSAN.use('util.window'); var win = new util.window();
1159                         win.open(urls.XUL_LIST_CLIPBOARD,'list_clipboard','chrome,resizable,modal');
1160                 } catch(E) {
1161                         this.error.standard_unexpected_error_alert('clipboard',E);
1162                 }
1163         },
1164
1165         'dump_retrieve_ids' : function(params) {
1166                 var obj = this;
1167                 switch(this.node.nodeName) {
1168                         case 'tree' : return this._dump_retrieve_ids_tree(params); break;
1169                         default: throw('NYI: Need .dump_retrieve_ids() for ' + this.node.nodeName); break;
1170                 }
1171         },
1172
1173         '_dump_retrieve_ids_tree' : function(params) {
1174                 var obj = this;
1175                 var dump = [];
1176                 for (var i = 0; i < this.treechildren.childNodes.length; i++) {
1177                         var treeitem = this.treechildren.childNodes[i];
1178                         dump.push( treeitem.getAttribute('retrieve_id') );
1179                 }
1180                 return dump;
1181         },
1182
1183     'wrap_in_full_retrieve' : function(f) {
1184         var obj = this;
1185                 if (typeof obj.on_all_fleshed == 'function') { // legacy
1186             obj.on_all_fleshed = [ obj.on_all_fleshed ];
1187                 }
1188         if (! obj.on_all_fleshed) obj.on_all_fleshed = [];
1189         obj.on_all_fleshed.push(f);
1190         obj.full_retrieve();
1191     },
1192
1193         '_sort_tree' : function(col,sortDir) {
1194                 var obj = this;
1195                 try {
1196                         if (obj.node.getAttribute('no_sort')) {
1197                                 return;
1198                         }
1199                         var col_pos;
1200                         for (var i = 0; i < obj.columns.length; i++) { 
1201                                 if (obj.columns[i].id == col.id) col_pos = function(a){return a;}(i); 
1202                         }
1203             obj.wrap_in_full_retrieve(
1204                 function() {
1205                                         try {
1206                                                 JSAN.use('util.money');
1207                                                 var rows = [];
1208                                                 var treeitems = obj.treechildren.childNodes;
1209                                                 for (var i = 0; i < treeitems.length; i++) {
1210                                                         var treeitem = treeitems[i];
1211                                                         var treerow = treeitem.firstChild;
1212                                                         var treecell = treerow.childNodes[ col_pos ];
1213                                                         value = ( { 'value' : treecell ? treecell.getAttribute('label') : '', 'node' : treeitem } );
1214                                                         rows.push( value );
1215                                                 }
1216                                                 rows = rows.sort( function(a,b) { 
1217                                                         a = a.value; b = b.value; 
1218                                                         if (col.getAttribute('sort_type')) {
1219                                                                 switch(col.getAttribute('sort_type')) {
1220                                                                         case 'number' :
1221                                                                                 a = Number(a); b = Number(b);
1222                                                                         break;
1223                                                                         case 'money' :
1224                                                                                 a = util.money.dollars_float_to_cents_integer(a);
1225                                                                                 b = util.money.dollars_float_to_cents_integer(b);
1226                                                                         break;
1227                                                                         case 'title' : /* special case for "a" and "the".  doesn't use marc 245 indicator */
1228                                                                                 a = String( a ).toUpperCase().replace( /^\s*(THE|A|AN)\s+/, '' );
1229                                                                                 b = String( b ).toUpperCase().replace( /^\s*(THE|A|AN)\s+/, '' );
1230                                                                         break;
1231                                                                         default:
1232                                                                                 a = String( a ).toUpperCase();
1233                                                                                 b = String( b ).toUpperCase();
1234                                                                         break;
1235                                                                 }
1236                                                         } else {
1237                                                                 if (typeof a == 'string' || typeof b == 'string') {
1238                                                                         a = String( a ).toUpperCase();
1239                                                                         b = String( b ).toUpperCase();
1240                                                                 }
1241                                                         }
1242                                                         if (a < b) return -1; 
1243                                                         if (a > b) return 1; 
1244                                                         return 0; 
1245                                                 } );
1246                                                 if (sortDir == 'asc') rows = rows.reverse();
1247                                                 while(obj.treechildren.lastChild) obj.treechildren.removeChild( obj.treechildren.lastChild );
1248                                                 for (var i = 0; i < rows.length; i++) {
1249                                                         obj.treechildren.appendChild( rows[i].node );
1250                                                 }
1251                                         } catch(E) {
1252                                                 obj.error.standard_unexpected_error_alert('sorting',E); 
1253                                         }
1254                                 }
1255             );
1256                 } catch(E) {
1257                         obj.error.standard_unexpected_error_alert('pre sorting', E);
1258                 }
1259         },
1260
1261 }
1262 dump('exiting util.list.js\n');