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