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