merge seials-integration [sic] branch into trunk
[working/Evergreen.git] / Open-ILS / xul / staff_client / server / serial / editor_base.js
1 dump('entering serial/editor_base.js\n');
2 // vim:noet:sw=4:ts=4:
3
4 if (typeof serial == 'undefined') serial = {};
5
6 serial.editor_base = {
7
8     'editor_base_init' : function (params) {
9         var obj = this;
10         try {
11             /******************************************************************************************************/
12             /* setup JSAN and some initial libraries */
13
14             netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
15             if (typeof JSAN == 'undefined') {
16                 throw( $('commonStrings').getString('common.jsan.missing') );
17             }
18             JSAN.errorLevel = "die"; // none, warn, or die
19             JSAN.addRepository('/xul/server/');
20             JSAN.use('util.error'); obj.error = new util.error();
21             obj.error.sdump('D_TRACE','my_init() for serial/editor_base.js');
22
23             JSAN.use('util.functional');
24             JSAN.use('OpenILS.data'); obj.data = new OpenILS.data(); obj.data.init({'via':'stash'});
25             JSAN.use('util.network'); obj.network = new util.network();
26
27
28             /******************************************************************************************************/
29             /* base vars */
30
31             obj.docid = xul_param('docid',{'modal_xulG':true});
32             
33             if (typeof params.handle_update == 'undefined') {
34                 obj.handle_update = xul_param('handle_update',{'modal_xulG':true});
35             } else {
36                 obj.handle_update = params.handle_update;
37             }
38
39             obj.trigger_refresh = params.trigger_refresh;
40             obj.refresh_command = params.refresh_command;
41             var fm_type = obj.fm_type;
42             var fm_type_plural = obj.fm_type_plural;
43             var retrieve_function = params.retrieve_function;
44             var retrieve_params = params.retrieve_params;
45             if (!retrieve_params) {
46                 retrieve_params = [];
47             }
48
49             /******************************************************************************************************/
50             /* Get the fm_type ids from various sources and flesh them */
51
52             var fm_type_ids = params[fm_type + '_ids'];
53             if (!fm_type_ids) fm_type_ids = xul_param(fm_type + '_ids',{'concat':true,'JSON2js_if_cgi':true,'JSON2js_if_xulG':true,'JSON2js_if_xpcom':true,'stash_name':'temp_' + fm_type + '_ids','clear_xpcom':true,'modal_xulG':true});
54             if (!fm_type_ids) fm_type_ids = [];
55
56             obj[fm_type_plural] = [];
57             retrieve_params.push(fm_type_ids);
58             if (fm_type_ids.length > 0) obj[fm_type_plural] = obj.network.simple_request(
59                 retrieve_function,
60                 retrieve_params
61             );
62
63
64             /******************************************************************************************************/
65             /* And other fleshed copies if any */
66
67             if (!obj[fm_type_plural]) obj[fm_type_plural] = [];
68             var fms = params[fm_type_plural];
69             if (!fms) fms = xul_param(fm_type_plural,{'concat':true,'JSON2js_if_cgi':true,'JSON2js_if_xpcom':true,'stash_name':'temp_' + fm_type_plural,'clear_xpcom':true,'modal_xulG':true})
70             if (fms) obj[fm_type_plural] = obj[fm_type_plural].concat(fms);
71
72
73             // If we have just one, wrap in array
74             if (!obj[fm_type_plural].length) {
75                 obj[fm_type_plural] = [obj[fm_type_plural]];
76             }
77
78
79             /******************************************************************************************************/
80
81             //obj.init_panes0();
82             obj.init_panes();
83
84             /******************************************************************************************************/
85             /* Is the interface an editor or a viewer, single or multi copy, existing copies or new copies? */
86
87             var do_edit;
88             if (typeof params.do_edit == 'undefined') {
89                 do_edit = xul_param('do_edit',{'modal_xulG':true});
90             } else {
91                 do_edit = params.do_edit;
92             }
93
94             if (do_edit) { 
95
96                 // Editor desired, but let's check permissions
97                 obj.do_edit = false;
98
99                 try {
100                     /* FIXME: add permission check
101                     var check = obj.network.simple_request(
102                         'PERM_MULTI_ORG_CHECK',
103                         [ 
104                             ses(), 
105                             obj.data.list.au[0].id(), 
106                             util.functional.map_list(
107                                 obj[fm_type_plural],
108                                 function (o) {
109                                     var lib;
110                                     var cn_id = o.call_number();
111                                     if (cn_id == -1) {
112                                         lib = o.circ_lib(); // base perms on circ_lib instead of owning_lib if pre-cat
113                                     } else {
114                                         if (! obj.map_acn[ cn_id ]) {
115                                             var req = obj.network.simple_request('FM_ACN_RETRIEVE.authoritative',[ cn_id ]);
116                                             if (typeof req.ilsevent == 'undefined') {
117                                                 obj.map_acn[ cn_id ] = req;
118                                                 lib = obj.map_acn[ cn_id ].owning_lib();
119                                             } else {
120                                                 lib = o.circ_lib();
121                                             }
122                                         } else {
123                                             lib = obj.map_acn[ cn_id ].owning_lib();
124                                         }
125                                     }
126                                     return typeof lib == 'object' ? lib.id() : lib;
127                                 }
128                             ),
129                             obj[fm_type_plural].length == 1 ? [ 'UPDATE_COPY' ] : [ 'UPDATE_COPY', 'UPDATE_BATCH_COPY' ]
130                         ]
131                     ); */
132                     var check = [];
133                     obj.do_edit = check.length == 0;
134                 } catch(E) {
135                     obj.error.standard_unexpected_error_alert('batch permission check',E);
136                 }
137
138                 if (obj.do_edit) {
139                     $(fm_type + '_save').setAttribute('hidden','false'); 
140                 } else {
141                     $('top_nav').setAttribute('hidden','true');
142                 }
143             } else {
144                 $('top_nav').setAttribute('hidden','true');
145             }
146
147
148             if (obj[fm_type_plural].length > 0 && obj[fm_type_plural][0].isnew()) {
149                 obj.mode = 'create';
150                 if (obj.can_have_notes) $(fm_type + '_notes').setAttribute('hidden','true');
151                 $(fm_type + '_save').setAttribute('label', $('serialStrings').getString('staff.serial.' + fm_type + '_editor.create'));
152                 $(fm_type + '_save').setAttribute('accesskey', $('serialStrings').getString('staff.serial.' + fm_type + '_editor.create.accesskey'));
153             } else if (obj.mode == 'create') { // switching from create to modify
154                 obj.mode = 'modify';
155                 if (obj.can_have_notes) $(fm_type + '_notes').setAttribute('hidden','false');
156                 $(fm_type + '_save').setAttribute('label', $('serialStrings').getString('staff.serial.' + fm_type + '_editor.modify'));
157                 $(fm_type + '_save').setAttribute('accesskey', $('serialStrings').getString('staff.serial.' + fm_type + '_editor.modify.accesskey'));
158             }
159 /*else {
160                 obj.panes_and_field_names.left_pane = 
161                     [
162                         [
163                             $('catStrings').getString('staff.cat.copy_editor.status'),
164                             { 
165                                 render: 'typeof fm.status() == "object" ? fm.status().name() : obj.data.hash.ccs[ fm.status() ].name()', 
166                                 input: obj.safe_to_edit_copy_status() ? 'c = function(v){ obj.apply("status",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( util.functional.map_list( obj.data.list.ccs, function(obj) { return [ obj.name(), obj.id(), typeof my_constants.magical_statuses[obj.id()] != "undefined" ? true : false ]; } ).sort() ); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);' : undefined,
167                                 //input: 'c = function(v){ obj.apply("status",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( util.functional.map_list( util.functional.filter_list( obj.data.list.ccs, function(obj) { return typeof my_constants.magical_statuses[obj.id()] == "undefined"; } ), function(obj) { return [ obj.name(), obj.id() ]; } ).sort() ); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
168                             }
169                         ]
170                     ].concat(obj.panes_and_field_names.left_pane);
171             }*/
172
173             if (obj[fm_type_plural].length != 1) {
174                 document.getElementById(fm_type + '_notes').setAttribute('hidden','true');
175             }
176
177             // clear change markers
178             obj.changed = {};
179
180             /******************************************************************************************************/
181             /* Show the Record Details? (only for 'in_modal' mode)*/
182
183             var bdb;
184             if (xul_param('in_modal',{'modal_xulG':true}) && obj.docid) {
185                 bdb = document.getElementById('brief_display_box'); while(bdb.firstChild) bdb.removeChild(bdb.lastChild);
186                 var brief_display = document.createElement('iframe'); bdb.appendChild(brief_display); 
187                 brief_display.setAttribute( 'src', urls.XUL_BIB_BRIEF + '?docid=' + obj.docid); // this is a modal window, so can't push in xulG
188                 brief_display.setAttribute( 'flex','1' );
189             }
190
191             /******************************************************************************************************/
192             /* Backup copies :) */
193
194             obj['original_' + fm_type_plural] = js2JSON( obj[fm_type_plural] );
195
196         } catch(E) {
197             var err_msg = $("commonStrings").getFormattedString('common.exception', ['serial/' + fm_type +'_editor.js - init', E]);
198             try { obj.error.sdump('D_ERROR',err_msg); } catch(E) { dump(err_msg); dump(js2JSON(E)); }
199             alert(err_msg);
200         }
201     },
202
203     /******************************************************************************************************/
204     /* Restore backup copies */
205
206     'editor_base_reset' : function() {
207         var obj = this;
208         var fm_type_plural = obj.fm_type_plural;
209
210         obj.changed = {};
211         obj[fm_type_plural] = JSON2js( obj['original_' + fm_type_plural] );
212         obj.summarize( obj[fm_type_plural] );
213         obj.render();
214     },
215
216     /******************************************************************************************************/
217     /* Apply a value to a specific field on all the copies being edited */
218     /* Don't forget to use util.money.sanitize if dealing with money values */
219
220     'editor_base_apply' : function(field, value, loop_func) {
221         var obj = this;
222         var fm_type_plural = obj.fm_type_plural;
223
224         var do_loop_func = (typeof loop_func == 'function');
225
226         obj.error.sdump('D_TRACE','applying field = <' + field + '>  value = <' + value + '>\n');
227         if (value == '<HACK:KLUDGE:NULL>') value = null;
228         for (var i = 0; i < obj[fm_type_plural].length; i++) {
229             var fm = obj[fm_type_plural][i];
230             try {
231                 fm[field]( value ); fm.ischanged('1');
232                 if (do_loop_func) {
233                     loop_func(fm);
234                 }
235             } catch(E) {
236                 alert(E);
237             }
238         }
239     },
240
241
242     /******************************************************************************************************/
243     /* This loops through all our fieldnames and all the copies, tallying up counts for the different values */
244
245     'editor_base_summarize' : function(my_fms) {
246         var obj = this;
247         /******************************************************************************************************/
248         /* Setup */
249
250         JSAN.use('util.date'); JSAN.use('util.money');
251         obj.summary = {};
252         obj.field_names = [];
253         for (var i in obj.panes_and_field_names) {
254             obj.field_names = obj.field_names.concat( obj.panes_and_field_names[i] );
255         }
256
257         /******************************************************************************************************/
258         /* Loop through the field names */
259
260         for (var i = 0; i < obj.field_names.length; i++) {
261
262             var field_name = obj.field_names[i][0];
263             var render = obj.field_names[i][1].render;
264             var attr = obj.field_names[i][1].attr;
265             var value_key = obj.field_names[i][1].value_key;
266             var dropdown_key = obj.field_names[i][1].dropdown_key;
267             obj.summary[ field_name ] = {};
268
269             /******************************************************************************************************/
270             /* Loop through the copies */
271
272             for (var j = 0; j < my_fms.length; j++) {
273
274                 var fm = my_fms[j];
275                 var cmd = render || ('fm.' + field_name + '();');
276                 var value = '???';
277
278                 /**********************************************************************************************/
279                 /* Try to retrieve the value for this field for this copy */
280
281                 try { 
282                     value = eval( cmd );
283                     if (typeof(value) == 'undefined') {
284                         value = "";
285                     }
286                     if (dropdown_key) {
287                         obj.editor_values[value_key] = eval(dropdown_key);
288                     } else if (value_key) {
289                         obj.editor_values[value_key] = value;
290                     }
291                     if (value == "") {
292                         value = "<Unset>";
293                     }
294                 } catch(E) { 
295                     obj.error.sdump('D_ERROR','Attempted ' + cmd + '\n' +  E + '\n'); 
296                 }
297                 if (typeof value == 'object' && value != null) {
298                     alert('FIXME: field_name = <' + field_name + '>  value = <' + js2JSON(value) + '>\n');
299                 }
300
301                 /**********************************************************************************************/
302                 /* Tally the count */
303
304                 if (obj.summary[ field_name ][ value ]) {
305                     obj.summary[ field_name ][ value ]++;
306                 } else {
307                     obj.summary[ field_name ][ value ] = 1;
308                 }
309             }
310         }
311         obj.error.sdump('D_TRACE','summary = ' + js2JSON(obj.summary) + '\n');
312     },
313
314     /******************************************************************************************************/
315     /* Display the summarized data and inputs for editing */
316
317     'editor_base_render' : function() {
318         var obj = this;
319         var fm_type = obj.fm_type;
320
321         /******************************************************************************************************/
322         /* Library setup and clear any existing interface */
323
324         JSAN.use('util.widgets'); JSAN.use('util.date'); JSAN.use('util.money'); JSAN.use('util.functional');
325
326         for (var i in obj.panes_and_field_names) {
327             var p = document.getElementById(i);
328             if (p) util.widgets.remove_children(p);
329         }
330
331         /******************************************************************************************************/
332         /* Prepare the panes */
333
334         var groupbox; var caption; var vbox; var grid; var rows;
335         
336         /******************************************************************************************************/
337         /* Loop through the field names */
338
339         for (h in obj.panes_and_field_names) {
340             if (!document.getElementById(h)) continue;
341             for (var i = 0; i < obj.panes_and_field_names[h].length; i++) {
342                 try {
343                     var f = obj.panes_and_field_names[h][i]; var fn = f[0]; var attr = f[1].attr;
344                     groupbox = document.createElement('groupbox'); document.getElementById(h).appendChild(groupbox);
345                     if (attr) {
346                         for (var a in attr) {
347                             groupbox.setAttribute(a,attr[a]);
348                         }
349                     }
350                     if (typeof obj.changed[fn] != 'undefined') {
351                         groupbox.setAttribute('class','copy_editor_field_changed');
352                     }
353                     caption = document.createElement('caption'); groupbox.appendChild(caption);
354                     caption.setAttribute('label',fn); caption.setAttribute('id','caption_'+fn);
355                     vbox = document.createElement('vbox'); groupbox.appendChild(vbox);
356                     grid = util.widgets.make_grid( [ { 'flex' : 1 }, {}, {} ] ); vbox.appendChild(grid);
357                     grid.setAttribute('flex','1');
358                     rows = grid.lastChild;
359                     var row;
360                     
361                     /**************************************************************************************/
362                     /* Loop through each value for the field */
363
364                     for (var j in obj.summary[fn]) {
365                         var value = j; var count = obj.summary[fn][j];
366                         row = document.createElement('row'); rows.appendChild(row);
367                         var label1 = document.createElement('description'); row.appendChild(label1);
368                         label1.setAttribute('id',fn + '_label');
369                         //if (obj.special_exception[ fn ]) {
370                         //      obj.special_exception[ fn ]( label1, value );
371                         //} else {
372                             label1.appendChild( document.createTextNode(value) );
373                         //}
374                         var label2 = document.createElement('description'); row.appendChild(label2);
375                         var fm_count;
376                         if (count == 1) {
377                             fm_count = $('serialStrings').getString('staff.serial.' + fm_type +'_editor.count');
378                         } else {
379                             fm_count = $('serialStrings').getFormattedString('staff.serial.' + fm_type +'_editor.count.plural', [count]);
380                         }
381                         label2.appendChild( document.createTextNode(fm_count) );
382                     }
383                     var hbox = document.createElement('hbox'); 
384                     hbox.setAttribute('id',fn);
385                     groupbox.appendChild(hbox);
386                     var hbox2 = document.createElement('hbox');
387                     groupbox.appendChild(hbox2);
388
389                     /**************************************************************************************/
390                     /* Render the input widget */
391
392                     if (f[1].input && obj.do_edit) {
393                         obj.render_input(hbox,f[1]);
394                     }
395
396                 } catch(E) {
397                     obj.error.sdump('D_ERROR','copy editor: ' + E + '\n');
398                 }
399             }
400         }
401         
402         
403         /******************************************************************************************************/
404         /* Synchronize stat cat visibility with library filter menu, and default template selection */
405         JSAN.use('util.file'); 
406         var file = new util.file(fm_type + '_editor_prefs.'+obj.data.server_unadorned);
407         obj[fm_type + '_editor_prefs'] = util.widgets.load_attributes(file);
408         for (var i in obj[fm_type + '_editor_prefs']) {
409             if (i.match(/filter_/) && obj[fm_type + '_editor_prefs'][i].checked == '') {
410                 try { 
411                     obj.toggle_stat_cat_display( document.getElementById(i) ); 
412                 } catch(E) { alert(E); }
413             }
414         }
415         if (obj.template_menu) obj.template_menu.value = obj.template_menu.getAttribute('value');
416
417     },
418
419     /******************************************************************************************************/
420     /* This actually draws the change button and input widget for a given field */
421     'editor_base_render_input' : function(node, blob) {
422         var obj = this;
423         var fm_type_plural = obj.fm_type_plural;
424
425         try {
426             // node = hbox ;    groupbox ->  hbox, hbox
427
428             var groupbox = node.parentNode;
429             var caption = groupbox.firstChild;
430             var vbox = node.previousSibling;
431             var hbox = node;
432             var hbox2 = node.nextSibling;
433
434             var input_cmd = blob.input;
435             var render_cmd = blob.render;
436             var attr = blob.attr;
437
438             var block = false; var first = true;
439
440             function on_mouseover(ev) {
441                 groupbox.setAttribute('style','background: white');
442             }
443
444             function on_mouseout(ev) {
445                 groupbox.setAttribute('style','');
446             }
447
448             vbox.addEventListener('mouseover',on_mouseover,false);
449             vbox.addEventListener('mouseout',on_mouseout,false);
450             groupbox.addEventListener('mouseover',on_mouseover,false);
451             groupbox.addEventListener('mouseout',on_mouseout,false);
452             groupbox.firstChild.addEventListener('mouseover',on_mouseover,false);
453             groupbox.firstChild.addEventListener('mouseout',on_mouseout,false);
454
455             function on_click(ev){
456                 try {
457                     if (block) return; block = true;
458
459                     function post_c(v, unchanged) {
460                         try {
461                             /* dbw2 not needed?
462                             var t = input_cmd.match('apply_stat_cat') ? 'stat_cat' : ( input_cmd.match('apply_owning_lib') ? 'owning_lib' : 'attribute' );
463                             var f;
464                             switch(t) {
465                                 case 'attribute' :
466                                     f = input_cmd.match(/apply.?\("(.+?)",/)[1];
467                                 break;
468                                 case 'stat_cat' :
469                                     f = input_cmd.match(/apply_stat_cat\((.+?),/)[1];
470                                 break;
471                                 case 'owning_lib' :
472                                     f = null;
473                                 break;
474                             }
475                             obj.changed[ hbox.id ] = { 'type' : t, 'field' : f, 'value' : v }; */
476                             if (!unchanged) {
477                                 obj.changed[ hbox.id ] = true;
478                             }
479                             block = false;
480                             setTimeout(
481                                 function() {
482                                     obj.summarize( obj[fm_type_plural] );
483                                     obj.render();
484                                     document.getElementById(caption.id).focus();
485                                 }, 0
486                             );
487                         } catch(E) {
488                             obj.error.standard_unexpected_error_alert('post_c',E);
489                         }
490                     }
491                     var x; var c; eval( input_cmd );
492                     if (x) {
493                         util.widgets.remove_children(vbox);
494                         util.widgets.remove_children(hbox);
495                         util.widgets.remove_children(hbox2);
496                         hbox.appendChild(x);
497                         var apply = document.createElement('button');
498                         apply.setAttribute('label', $('catStrings').getString('staff.cat.copy_editor.apply.label'));
499                         apply.setAttribute('accesskey', $('catStrings').getString('staff.cat.copy_editor.apply.accesskey'));
500                         hbox2.appendChild(apply);
501                         apply.addEventListener('command',function() { c(x.value); },false);
502                         var cancel = document.createElement('button');
503                         cancel.setAttribute('label', $('catStrings').getString('staff.cat.copy_editor.cancel.label'));
504                         cancel.addEventListener('command',function() { setTimeout( function() { obj.summarize( obj[fm_type_plural] ); obj.render(); document.getElementById(caption.id).focus(); }, 0); }, false);
505                         hbox2.appendChild(cancel);
506                         setTimeout( function() { x.focus(); }, 0 );
507                     }
508                 } catch(E) {
509                     obj.error.standard_unexpected_error_alert('render_input',E);
510                 }
511             }
512             vbox.addEventListener('click',on_click, false);
513             hbox.addEventListener('click',on_click, false);
514             caption.addEventListener('click',on_click, false);
515             caption.addEventListener('keypress',function(ev) {
516                 if (ev.keyCode == 13 /* enter */ || ev.keyCode == 77 /* mac enter */) on_click();
517             }, false);
518             caption.setAttribute('style','-moz-user-focus: normal');
519             caption.setAttribute('onfocus','this.setAttribute("class","outline_me")');
520             caption.setAttribute('onblur','this.setAttribute("class","")');
521
522         } catch(E) {
523             obj.error.sdump('D_ERROR',E + '\n');
524         }
525     },
526
527     /******************************************************************************************************/
528     /* save or store the updated fms as appropriate */
529
530     'editor_base_save' : function(update_method) {
531         var obj = this;
532         var fm_type_plural = obj.fm_type_plural;
533         var fm_type= obj.fm_type;
534
535         try {
536             if (obj.handle_update) {
537                 try {
538                     //send fms to the update function
539                     var r = obj.network.request(
540                         'open-ils.serial',
541                         update_method,
542                         [ ses(), obj[fm_type_plural] ]
543                     );
544                     if (typeof r.ilsevent != 'undefined') {
545                         obj.error.standard_unexpected_error_alert('serial ' + fm_type + ' update',r);
546                     } else {
547                         alert($('serialStrings').getString('staff.serial.editor_base.handle_update.success'));
548                         obj.changed = {};
549                         if (obj.trigger_refresh) {
550                             obj.refresh_command();
551                         } else {
552                             obj.render();
553                         }
554                     }
555                     /* FIXME -- revisit the return value here */
556                 } catch(E) {
557                     alert($('serialStrings').getString('staff.serial.editor_base.handle_update.error') + ' ' + js2JSON(E));
558                 }
559             } else if (xul_param('in_modal',{'modal_xulG':true})) {
560                 // TODO: this is to perhaps allow this editor to be called
561                 // in a modal window, but is unfinished functionality
562                 var xulG = {};
563                 xulG[fm_type_plural] = obj[fm_type_plural];
564                 update_modal_xulG(xulG);
565             } else {
566                 obj.data['temp_' + fm_type_plural] = js2JSON( obj[fm_type_plural] );
567                 obj.data.stash('temp_' + fm_type_plural);
568             }
569
570             if (xul_param('in_modal',{'modal_xulG':true})) {
571                 window.close();
572             }
573         } catch(E) {
574             obj.error.standard_unexpected_error_alert(fm_type + '_editor save',E);
575         }
576     },
577
578     /******************************************************************************************************/
579     'editor_base_save_attributes' : function() {
580         var obj = this;
581         var fm_type = obj.fm_type;
582
583         JSAN.use('util.widgets'); JSAN.use('util.file'); var file = new util.file(fm_type + '_editor_prefs.'+obj.data.server_unadorned);
584         var what_to_save = {};
585         for (var i in obj[fm_type + '_editor_prefs']) {
586             what_to_save[i] = [];
587             for (var j in obj[fm_type + '_editor_prefs'][i]) what_to_save[i].push(j);
588         }
589         util.widgets.save_attributes(file, what_to_save );
590     }
591 };
592
593 dump('exiting serial/editor_base.js\n');