]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/xul/staff_client/chrome/content/util/list.js
auto-select top row on append
[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                 if (typeof params.to_top == 'undefined') {
342                         treechildren_node.appendChild( treeitem );
343                 } else {
344                         if (treechildren_node.firstChild) {
345                                 treechildren_node.insertBefore( treeitem, treechildren_node.firstChild );
346                         } else {
347                                 treechildren_node.appendChild( treeitem );
348                         }
349                 }
350                 if (typeof params.no_auto_select == 'undefined') {
351                         try { obj.node.view.selection.select(0); } catch(E) { obj.error.sdump('D_ERROR','tree auto select: ' + E + '\n'); }
352                 }
353                 var treerow = document.createElement('treerow');
354                 treeitem.appendChild( treerow );
355                 treerow.setAttribute('retrieve_id',params.retrieve_id);
356
357                 s += ('tree = ' + this.node + '  treechildren = ' + treechildren_node + '\n');
358                 s += ('treeitem = ' + treeitem + '  treerow = ' + treerow + '\n');
359
360                 if (typeof params.retrieve_row == 'function' || typeof this.retrieve_row == 'function') {
361
362                         obj.put_retrieving_label(treerow);
363                         treerow.addEventListener(
364                                 'flesh',
365                                 function() {
366
367                                         if (treerow.getAttribute('retrieved') == 'true') return; /* already running */
368
369                                         treerow.setAttribute('retrieved','true');
370
371                                         //dump('fleshing = ' + params.retrieve_id + '\n');
372
373                                         function inc_fleshed() {
374                                                 if (treerow.getAttribute('fleshed') == 'true') return; /* already fleshed */
375                                                 treerow.setAttribute('fleshed','true');
376                                                 obj.row_count.fleshed++;
377                                                 if (obj.row_count.fleshed == obj.row_count.total) {
378                                                         if (typeof obj.on_all_fleshed == 'function') {
379                                                                 setTimeout( function() { obj.on_all_fleshed(); }, 0 );
380                                                         }
381                                                 }
382                                         }
383
384                                         params.row_node = treeitem;
385                                         params.on_retrieve = function(p) {
386                                                 try {
387                                                         p.row = params.row;
388                                                         obj._map_row_to_treecell(p,treerow);
389                                                         inc_fleshed();
390                                                 } catch(E) {
391                                                         alert('fixme2: ' + E);
392                                                 }
393                                         }
394
395                                         if (typeof params.retrieve_row == 'function') {
396
397                                                 params.retrieve_row( params );
398
399                                         } else if (typeof obj.retrieve_row == 'function') {
400
401                                                         obj.retrieve_row( params );
402
403                                         } else {
404                                         
405                                                         inc_fleshed();
406                                         }
407                                 },
408                                 false
409                         );
410                         /*
411                         setTimeout(
412                                 function() {
413                                         util.widgets.dispatch('flesh',treerow);
414                                 }, 0
415                         );
416                         */
417                 } else {
418                         obj.put_retrieving_label(treerow);
419                         treerow.addEventListener(
420                                 'flesh',
421                                 function() {
422                                         //dump('fleshing anon\n');
423                                         if (treerow.getAttribute('fleshed') == 'true') return; /* already fleshed */
424                                         obj._map_row_to_treecell(params,treerow);
425                                         treerow.setAttribute('retrieved','true');
426                                         treerow.setAttribute('fleshed','true');
427                                         obj.row_count.fleshed++;
428                                         if (obj.row_count.fleshed == obj.row_count.total) {
429                                                 if (typeof obj.on_all_fleshed == 'function') {
430                                                         setTimeout( function() { obj.on_all_fleshed(); }, 0 );
431                                                 }
432                                         }
433                                 },
434                                 false
435                         );
436                         /*
437                         setTimeout(
438                                 function() {
439                                         util.widgets.dispatch('flesh',treerow);
440                                 }, 0
441                         );
442                         */
443                 }
444                 this.error.sdump('D_LIST',s);
445
446                         try {
447
448                                 if (obj.trim_list && obj.row_count.total >= obj.trim_list) {
449                                         // Remove oldest row
450                                         //if (typeof params.to_bottom != 'undefined') {
451                                         if (typeof params.to_top == 'undefined') {
452                                                 treechildren_node.removeChild( treechildren_node.firstChild );
453                                         } else {
454                                                 treechildren_node.removeChild( treechildren_node.lastChild );
455                                         }
456                                 }
457                         } catch(E) {
458                         }
459
460                 setTimeout( function() { obj.auto_retrieve(); }, 0 );
461
462                 return treeitem;
463         },
464
465         'put_retrieving_label' : function(treerow) {
466                 var obj = this;
467                 try {
468                         /*
469                         var cols_idx = 0;
470                         dump('put_retrieving_label.  columns = ' + js2JSON(obj.columns) + '\n');
471                         while( obj.columns[cols_idx] && obj.columns[cols_idx].hidden && obj.columns[cols_idx].hidden == 'true') {
472                                 dump('\t' + cols_idx);
473                                 var treecell = document.createElement('treecell');
474                                 treerow.appendChild(treecell);
475                                 cols_idx++;
476                         }
477                         */
478                         for (var i = 0; i < obj.columns.length; i++) {
479                         var treecell = document.createElement('treecell'); treecell.setAttribute('label','Retrieving...');
480                         treerow.appendChild(treecell);
481                         }
482                         /*
483                         dump('\t' + cols_idx + '\n');
484                         */
485                 } catch(E) {
486                         alert(E);
487                 }
488         },
489
490         'detect_visible' : function() {
491                 var obj = this;
492                 try {
493                         //dump('detect_visible  obj.node = ' + obj.node + '\n');
494                         /* FIXME - this is a hack.. if the implementation of tree changes, this could break */
495                         try {
496                                 var scrollbar = document.getAnonymousNodes( document.getAnonymousNodes(obj.node)[1] )[1];
497                                 var curpos = scrollbar.getAttribute('curpos');
498                                 var maxpos = scrollbar.getAttribute('maxpos');
499                                 //alert('curpos = ' + curpos + ' maxpos = ' + maxpos + ' obj.curpos = ' + obj.curpos + ' obj.maxpos = ' + obj.maxpos + '\n');
500                                 if ((curpos != obj.curpos) || (maxpos != obj.maxpos)) {
501                                         if ( obj.auto_retrieve() > 0 ) {
502                                                 obj.curpos = curpos; obj.maxpos = maxpos;
503                                         }
504                                 }
505                         } catch(E) {
506                                 obj.error.sdump('D_XULRUNNER', 'List implementation changed? ' + E);
507                         }
508                 } catch(E) { obj.error.sdump('D_ERROR',E); }
509         },
510
511         'detect_visible_polling' : function() {
512                 try {
513                         //alert('detect_visible_polling');
514                         var obj = this;
515                         obj.detect_visible();
516                         setTimeout(function() { try { obj.detect_visible_polling(); } catch(E) { alert(E); } },2000);
517                 } catch(E) {
518                         alert(E);
519                 }
520         },
521
522
523         'auto_retrieve' : function(params) {
524                 var obj = this;
525                 switch (this.node.nodeName) {
526                         case 'tree' : obj._auto_retrieve_tree(params); break;
527                         default: throw('NYI: Need .auto_retrieve() for ' + obj.node.nodeName); break;
528                 }
529         },
530
531         '_auto_retrieve_tree' : function (params) {
532                 var obj = this;
533                 if (!obj.auto_retrieve_in_progress) {
534                         obj.auto_retrieve_in_progress = true;
535                         setTimeout(
536                                 function() {
537                                         try {
538                                                         //alert('auto_retrieve\n');
539                                                         var count = 0;
540                                                         var startpos = obj.node.treeBoxObject.getFirstVisibleRow();
541                                                         var endpos = obj.node.treeBoxObject.getLastVisibleRow();
542                                                         if (startpos > endpos) endpos = obj.node.treeBoxObject.getPageLength();
543                                                         //dump('startpos = ' + startpos + ' endpos = ' + endpos + '\n');
544                                                         for (var i = startpos; i < endpos + 4; i++) {
545                                                                 try {
546                                                                         //dump('trying index ' + i + '\n');
547                                                                         var item = obj.node.contentView.getItemAtIndex(i).firstChild;
548                                                                         if (item && item.getAttribute('retrieved') != 'true' ) {
549                                                                                 //dump('\tgot an unfleshed item = ' + item + ' = ' + item.nodeName + '\n');
550                                                                                 util.widgets.dispatch('flesh',item); count++;
551                                                                         }
552                                                                 } catch(E) {
553                                                                         //dump(i + ' : ' + E + '\n');
554                                                                 }
555                                                         }
556                                                         obj.auto_retrieve_in_progress = false;
557                                                         return count;
558                                         } catch(E) { alert(E); }
559                                 }, 1
560                         );
561                 }
562         },
563
564         'full_retrieve' : function(params) {
565                 var obj = this;
566                 switch (this.node.nodeName) {
567                         case 'tree' : obj._full_retrieve_tree(params); break;
568                         default: throw('NYI: Need .full_retrieve() for ' + obj.node.nodeName); break;
569                 }
570         },
571
572         '_full_retrieve_tree' : function(params) {
573                 var obj = this;
574                 try {
575                         if (obj.row_count.total == obj.row_count.fleshed) {
576                                 //alert('Full retrieve... tree seems to be in sync\n' + js2JSON(obj.row_count));
577                                 if (typeof obj.on_all_fleshed == 'function') {
578                                         setTimeout( function() { obj.on_all_fleshed(); }, 0 );
579                                 } else {
580                                         alert('.full_retrieve called with no callback?');
581                                 }
582                         } else {
583                                 //alert('Full retrieve... syncing tree' + js2JSON(obj.row_count));
584                                 JSAN.use('util.widgets');
585                                 var nodes = obj.treechildren.childNodes;
586                                 for (var i = 0; i < nodes.length; i++) {
587                                         util.widgets.dispatch('flesh',nodes[i].firstChild);
588                                 }
589                         }
590                 } catch(E) {
591                         obj.error.standard_unexpected_error_alert('_full_retrieve_tree',E);
592                 }
593         },
594
595         '_append_to_listbox' : function (params) {
596
597                 var obj = this;
598
599                 if (typeof params.row == 'undefined') throw('util.list.append: Object must contain a row');
600
601                 var s = ('util.list.append: params = ' + (params) + '\n');
602
603                 var listitem = document.createElement('listitem');
604
605                 s += ('listbox = ' + this.node + '  listitem = ' + listitem + '\n');
606
607                 if (typeof params.retrieve_row == 'function' || typeof this.retrieve_row == 'function') {
608
609                         setTimeout(
610                                 function() {
611                                         listitem.setAttribute('retrieve_id',params.retrieve_id);
612                                         //FIXME//Make async and fire when row is visible in list
613                                         var row;
614
615                                         params.row_node = listitem;
616                                         params.on_retrieve = function(row) {
617                                                 params.row = row;
618                                                 obj._map_row_to_listcell(params,listitem);
619                                                 obj.node.appendChild( listitem );
620                                         }
621
622                                         if (typeof params.retrieve_row == 'function') {
623
624                                                 row = params.retrieve_row( params );
625
626                                         } else {
627
628                                                 if (typeof obj.retrieve_row == 'function') {
629
630                                                         row = obj.retrieve_row( params );
631
632                                                 }
633                                         }
634                                 }, 0
635                         );
636                 } else {
637                         this._map_row_to_listcell(params,listitem);
638                         this.node.appendChild( listitem );
639                 }
640
641                 this.error.sdump('D_LIST',s);
642                 return listitem;
643
644         },
645
646         '_map_row_to_treecell' : function(params,treerow) {
647                 var obj = this;
648                 var s = '';
649                 util.widgets.remove_children(treerow);
650
651                 if (typeof params.map_row_to_column == 'function' || typeof this.map_row_to_column == 'function') {
652
653                         for (var i = 0; i < this.columns.length; i++) {
654                                 var treecell = document.createElement('treecell');
655                                 var label = '';
656                                 if (params.skip_columns && (params.skip_columns.indexOf(i) != -1)) {
657                                         treecell.setAttribute('label',label);
658                                         treerow.appendChild( treecell );
659                                         s += ('treecell = ' + treecell + ' with label = ' + label + '\n');
660                                         continue;
661                                 }
662                                 if (params.skip_all_columns_except && (params.skip_all_columns_except.indexOf(i) == -1)) {
663                                         treecell.setAttribute('label',label);
664                                         treerow.appendChild( treecell );
665                                         s += ('treecell = ' + treecell + ' with label = ' + label + '\n');
666                                         continue;
667                                 }
668         
669                                 if (typeof params.map_row_to_column == 'function')  {
670         
671                                         label = params.map_row_to_column(params.row,this.columns[i]);
672         
673                                 } else if (typeof this.map_row_to_column == 'function') {
674         
675                                         label = this.map_row_to_column(params.row,this.columns[i]);
676         
677                                 }
678                                 treecell.setAttribute('label',label ? label : '');
679                                 treerow.appendChild( treecell );
680                                 s += ('treecell = ' + treecell + ' with label = ' + label + '\n');
681                         }
682                 } else if (typeof params.map_row_to_columns == 'function' || typeof this.map_row_to_columns == 'function') {
683
684                         var labels = [];
685
686                         if (typeof params.map_row_to_columns == 'function') {
687
688                                 labels = params.map_row_to_columns(params.row,this.columns);
689
690                         } else if (typeof this.map_row_to_columns == 'function') {
691
692                                 labels = this.map_row_to_columns(params.row,this.columns);
693
694                         }
695                         for (var i = 0; i < labels.length; i++) {
696                                 var treecell = document.createElement('treecell');
697                                 treecell.setAttribute('label',typeof labels[i] == 'string' || typeof labels[i] == 'number' ? labels[i] : '');
698                                 treerow.appendChild( treecell );
699                                 s += ('treecell = ' + treecell + ' with label = ' + labels[i] + '\n');
700                         }
701
702                 } else {
703
704                         throw('No row to column mapping function.');
705                 }
706                 this.error.sdump('D_LIST',s);
707         },
708
709         '_map_row_to_listcell' : function(params,listitem) {
710                 var obj = this;
711                 var s = '';
712                 for (var i = 0; i < this.columns.length; i++) {
713                         var value = '';
714                         if (typeof params.map_row_to_column == 'function')  {
715
716                                 value = params.map_row_to_column(params.row,this.columns[i]);
717
718                         } else {
719
720                                 if (typeof this.map_row_to_column == 'function') {
721
722                                         value = this.map_row_to_column(params.row,this.columns[i]);
723                                 }
724                         }
725                         if (typeof value == 'string' || typeof value == 'number') {
726                                 var listcell = document.createElement('listcell');
727                                 listcell.setAttribute('label',value);
728                                 listitem.appendChild(listcell);
729                                 s += ('listcell = ' + listcell + ' with label = ' + value + '\n');
730                         } else {
731                                 listitem.appendChild(value);
732                                 s += ('listcell = ' + value + ' is really a ' + value.nodeName + '\n');
733                         }
734                 }
735                 this.error.sdump('D_LIST',s);
736         },
737
738         'select_all' : function(params) {
739                 var obj = this;
740                 switch(this.node.nodeName) {
741                         case 'tree' : return this._select_all_from_tree(params); break;
742                         default: throw('NYI: Need ._select_all_from_() for ' + this.node.nodeName); break;
743                 }
744         },
745
746         '_select_all_from_tree' : function(params) {
747                 var obj = this;
748                 this.node.view.selection.selectAll();
749         },
750
751         'retrieve_selection' : function(params) {
752                 var obj = this;
753                 switch(this.node.nodeName) {
754                         case 'tree' : return this._retrieve_selection_from_tree(params); break;
755                         default: throw('NYI: Need ._retrieve_selection_from_() for ' + this.node.nodeName); break;
756                 }
757         },
758
759         '_retrieve_selection_from_tree' : function(params) {
760                 var obj = this;
761                 var list = [];
762                 var start = new Object();
763                 var end = new Object();
764                 var numRanges = this.node.view.selection.getRangeCount();
765                 for (var t=0; t<numRanges; t++){
766                         this.node.view.selection.getRangeAt(t,start,end);
767                         for (var v=start.value; v<=end.value; v++){
768                                 var i = this.node.contentView.getItemAtIndex(v);
769                                 list.push( i );
770                         }
771                 }
772                 return list;
773         },
774
775         'dump' : function(params) {
776                 var obj = this;
777                 switch(this.node.nodeName) {
778                         case 'tree' : return this._dump_tree(params); break;
779                         default: throw('NYI: Need .dump() for ' + this.node.nodeName); break;
780                 }
781         },
782
783         '_dump_tree' : function(params) {
784                 var obj = this;
785                 var dump = [];
786                 for (var i = 0; i < this.treechildren.childNodes.length; i++) {
787                         var row = [];
788                         var treeitem = this.treechildren.childNodes[i];
789                         var treerow = treeitem.firstChild;
790                         for (var j = 0; j < treerow.childNodes.length; j++) {
791                                 row.push( treerow.childNodes[j].getAttribute('label') );
792                         }
793                         dump.push( row );
794                 }
795                 return dump;
796         },
797
798         'dump_with_keys' : function(params) {
799                 var obj = this;
800                 switch(this.node.nodeName) {
801                         case 'tree' : return this._dump_tree_with_keys(params); break;
802                         default: throw('NYI: Need .dump_with_keys() for ' + this.node.nodeName); break;
803                 }
804
805         },
806
807         '_dump_tree_with_keys' : function(params) {
808                 var obj = this;
809                 var dump = [];
810                 for (var i = 0; i < this.treechildren.childNodes.length; i++) {
811                         var row = {};
812                         var treeitem = this.treechildren.childNodes[i];
813                         var treerow = treeitem.firstChild;
814                         for (var j = 0; j < treerow.childNodes.length; j++) {
815                                 row[ obj.columns[j].id ] = treerow.childNodes[j].getAttribute('label');
816                         }
817                         dump.push( row );
818                 }
819                 return dump;
820         },
821
822         'dump_csv' : function(params) {
823                 var obj = this;
824                 switch(this.node.nodeName) {
825                         case 'tree' : return this._dump_tree_csv(params); break;
826                         default: throw('NYI: Need .dump_csv() for ' + this.node.nodeName); break;
827                 }
828
829         },
830
831         '_dump_tree_csv' : function(params) {
832                 var obj = this;
833                 var dump = '';
834                 for (var j = 0; j < obj.columns.length; j++) {
835                         if (obj.node.treeBoxObject.columns.getColumnAt(j).element.getAttribute('hidden') == 'true') {
836                                 /* skip */
837                         } else {
838                                 if (dump) dump += ',';
839                                 dump += '"' + obj.columns[j].label.replace(/"/g, '""') + '"';
840                         }
841                 }
842                 dump += '\r\n';
843                 for (var i = 0; i < this.treechildren.childNodes.length; i++) {
844                         var row = '';
845                         var treeitem = this.treechildren.childNodes[i];
846                         var treerow = treeitem.firstChild;
847                         for (var j = 0; j < treerow.childNodes.length; j++) {
848                                 if (obj.node.treeBoxObject.columns.getColumnAt(j).element.getAttribute('hidden') == 'true') {
849                                         /* skip */
850                                 } else {
851                                         if (row) row += ',';
852                                         row += '"' + treerow.childNodes[j].getAttribute('label').replace(/"/g, '""') + '"';
853                                 }
854                         }
855                         dump +=  row + '\r\n';
856                 }
857                 return dump;
858         },
859
860         'dump_selected_with_keys' : function(params) {
861                 var obj = this;
862                 switch(this.node.nodeName) {
863                         case 'tree' : return this._dump_tree_selection_with_keys(params); break;
864                         default: throw('NYI: Need .dump_selection_with_keys() for ' + this.node.nodeName); break;
865                 }
866
867         },
868
869         '_dump_tree_selection_with_keys' : function(params) {
870                 var obj = this;
871                 var dump = [];
872                 var list = obj._retrieve_selection_from_tree();
873                 for (var i = 0; i < list.length; i++) {
874                         var row = {};
875                         var treeitem = list[i];
876                         var treerow = treeitem.firstChild;
877                         for (var j = 0; j < treerow.childNodes.length; j++) {
878                                 var value = treerow.childNodes[j].getAttribute('label');
879                                 if (params.skip_hidden_columns) if (obj.node.treeBoxObject.columns.getColumnAt(j).element.getAttribute('hidden') == 'true') continue;
880                                 var id = obj.columns[j].id; if (params.labels_instead_of_ids) id = obj.columns[j].label;
881                                 row[ id ] = value;
882                         }
883                         dump.push( row );
884                 }
885                 return dump;
886         },
887
888         'clipboard' : function() {
889                 try {
890                         var obj = this;
891                         var dump = obj.dump_selected_with_keys({'skip_hidden_columns':true,'labels_instead_of_ids':true});
892                         JSAN.use('OpenILS.data'); var data = new OpenILS.data(); data.stash_retrieve();
893                         data.list_clipboard = dump; data.stash('list_clipboard');
894                         JSAN.use('util.window'); var win = new util.window();
895                         win.open(urls.XUL_LIST_CLIPBOARD,'list_clipboard','chrome,resizable,modal');
896                 } catch(E) {
897                         this.error.standard_unexpected_error_alert('clipboard',E);
898                 }
899         },
900
901         'dump_retrieve_ids' : function(params) {
902                 var obj = this;
903                 switch(this.node.nodeName) {
904                         case 'tree' : return this._dump_retrieve_ids_tree(params); break;
905                         default: throw('NYI: Need .dump_retrieve_ids() for ' + this.node.nodeName); break;
906                 }
907         },
908
909         '_dump_retrieve_ids_tree' : function(params) {
910                 var obj = this;
911                 var dump = [];
912                 for (var i = 0; i < this.treechildren.childNodes.length; i++) {
913                         var treeitem = this.treechildren.childNodes[i];
914                         dump.push( treeitem.getAttribute('retrieve_id') );
915                 }
916                 return dump;
917         },
918
919         '_sort_tree' : function(col,sortDir) {
920                 var obj = this;
921                 try {
922                         if (obj.node.getAttribute('no_sort')) {
923                                 return;
924                         }
925                         if (obj.on_all_fleshed) {
926                                 var r = window.confirm('This list is busy rendering/retrieving data.  Abort current action and proceed?');
927                                 if (r) {} else { return; }
928                         }
929                         var col_pos;
930                         for (var i = 0; i < obj.columns.length; i++) { 
931                                 if (obj.columns[i].id == col.id) col_pos = function(a){return a;}(i); 
932                         }
933                         obj.on_all_fleshed = function() {
934                                         try {
935                                                 JSAN.use('util.money');
936                                                 var rows = [];
937                                                 var treeitems = obj.treechildren.childNodes;
938                                                 for (var i = 0; i < treeitems.length; i++) {
939                                                         var treeitem = treeitems[i];
940                                                         var treerow = treeitem.firstChild;
941                                                         var treecell = treerow.childNodes[ col_pos ];
942                                                         value = ( { 'value' : treecell ? treecell.getAttribute('label') : '', 'node' : treeitem } );
943                                                         rows.push( value );
944                                                 }
945                                                 rows = rows.sort( function(a,b) { 
946                                                         a = a.value; b = b.value; 
947                                                         if (col.getAttribute('sort_type')) {
948                                                                 switch(col.getAttribute('sort_type')) {
949                                                                         case 'number' :
950                                                                                 a = Number(a); b = Number(b);
951                                                                         break;
952                                                                         case 'money' :
953                                                                                 a = util.money.dollars_float_to_cents_integer(a);
954                                                                                 b = util.money.dollars_float_to_cents_integer(b);
955                                                                         break;
956                                                                         case 'title' : /* special case for "a" and "the".  doesn't use marc 245 indicator */
957                                                                                 a = String( a ).toUpperCase().replace( /^\s*(THE|A|AN)\s+/, '' );
958                                                                                 b = String( b ).toUpperCase().replace( /^\s*(THE|A|AN)\s+/, '' );
959                                                                         break;
960                                                                         default:
961                                                                                 a = String( a ).toUpperCase();
962                                                                                 b = String( b ).toUpperCase();
963                                                                         break;
964                                                                 }
965                                                         } else {
966                                                                 if (typeof a == 'string' || typeof b == 'string') {
967                                                                         a = String( a ).toUpperCase();
968                                                                         b = String( b ).toUpperCase();
969                                                                 }
970                                                         }
971                                                         if (a < b) return -1; 
972                                                         if (a > b) return 1; 
973                                                         return 0; 
974                                                 } );
975                                                 if (sortDir == 'asc') rows = rows.reverse();
976                                                 while(obj.treechildren.lastChild) obj.treechildren.removeChild( obj.treechildren.lastChild );
977                                                 for (var i = 0; i < rows.length; i++) {
978                                                         obj.treechildren.appendChild( rows[i].node );
979                                                 }
980                                         } catch(E) {
981                                                 obj.error.standard_unexpected_error_alert('sorting',E); 
982                                         }
983                                         setTimeout(function(){ obj.on_all_fleshed = null; },0);
984                                 }
985                         obj.full_retrieve();
986                 } catch(E) {
987                         obj.error.standard_unexpected_error_alert('pre sorting', E);
988                 }
989         },
990
991 }
992 dump('exiting util.list.js\n');