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