]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/xul/staff_client/server/cat/copy_editor.js
316f281cad34f12bd5fbbb8300c1f7aa95589fb0
[Evergreen.git] / Open-ILS / xul / staff_client / server / cat / copy_editor.js
1 // vim:noet:sw=4:ts=4
2 var g = {};
3 g.map_acn = {};
4
5 var xulG = {};
6
7 function $(id) { return document.getElementById(id); }
8
9 function my_init() {
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'); g.error = new util.error();
21                 g.error.sdump('D_TRACE','my_init() for cat/copy_editor.xul');
22
23                 JSAN.use('util.functional');
24                 JSAN.use('OpenILS.data'); g.data = new OpenILS.data(); g.data.init({'via':'stash'});
25                 JSAN.use('util.network'); g.network = new util.network();
26
27                 g.docid = xul_param('docid',{'modal_xulG':true});
28                 g.handle_update = xul_param('handle_update',{'modal_xulG':true});
29
30                 /******************************************************************************************************/
31                 /* Get the copy ids from various sources and flesh them */
32
33                 var copy_ids = xul_param('copy_ids',{'concat':true,'JSON2js_if_cgi':true,'JSON2js_if_xulG':true,'JSON2js_if_xpcom':true,'stash_name':'temp_copy_ids','clear_xpcom':true,'modal_xulG':true});
34                 if (!copy_ids) copy_ids = [];
35
36                 if (copy_ids.length > 0) g.copies = g.network.simple_request(
37                         'FM_ACP_FLESHED_BATCH_RETRIEVE.authoritative',
38                         [ copy_ids ]
39                 );
40
41                 /******************************************************************************************************/
42                 /* And other fleshed copies if any */
43
44                 if (!g.copies) g.copies = [];
45                 var c = xul_param('copies',{'concat':true,'JSON2js_if_cgi':true,'JSON2js_if_xpcom':true,'stash_name':'temp_copies','clear_xpcom':true,'modal_xulG':true})
46                 if (c) g.copies = g.copies.concat(c);
47
48                 /******************************************************************************************************/
49                 /* We try to retrieve callnumbers for existing copies, but for new copies, we rely on this */
50
51                 g.callnumbers = xul_param('callnumbers',{'concat':true,'JSON2js_if_cgi':true,'JSON2js_if_xpcom':true,'stash_name':'temp_callnumbers','clear_xpcom':true,'modal_xulG':true});
52
53
54                 /******************************************************************************************************/
55                 /* Quick fix, this was defined inline in the global scope but now needs g.error and g.copies from my_init */
56
57         init_panes();
58
59                 /******************************************************************************************************/
60                 /* Is the interface an editor or a viewer, single or multi copy, existing copies or new copies? */
61
62                 if (xul_param('edit',{'modal_xulG':true}) == '1') { 
63
64             // Editor desired, but let's check permissions
65                         g.edit = false;
66
67             try {
68                 var check = g.network.simple_request(
69                     'PERM_MULTI_ORG_CHECK',
70                     [ 
71                         ses(), 
72                         g.data.list.au[0].id(), 
73                         util.functional.map_list(
74                             g.copies,
75                             function (o) {
76                                 var lib;
77                                 var cn_id = o.call_number();
78                                 if (cn_id == -1) {
79                                     lib = o.circ_lib(); // base perms on circ_lib instead of owning_lib if pre-cat
80                                 } else {
81                                     if (! g.map_acn[ cn_id ]) {
82                                         var req = g.network.simple_request('FM_ACN_RETRIEVE.authoritative',[ cn_id ]);
83                                         if (typeof req.ilsevent == 'undefined') {
84                                             g.map_acn[ cn_id ] = req;
85                                             lib = g.map_acn[ cn_id ].owning_lib();
86                                         } else {
87                                             lib = o.circ_lib();
88                                         }
89                                     } else {
90                                         lib = g.map_acn[ cn_id ].owning_lib();
91                                     }
92                                 }
93                                 return typeof lib == 'object' ? lib.id() : lib;
94                             }
95                         ),
96                         g.copies.length == 1 ? [ 'UPDATE_COPY' ] : [ 'UPDATE_COPY', 'UPDATE_BATCH_COPY' ]
97                     ]
98                 );
99                 g.edit = check.length == 0;
100             } catch(E) {
101                 g.error.standard_unexpected_error_alert('batch permission check',E);
102             }
103
104                         if (g.edit) {
105                 $('caption').setAttribute('label', $('catStrings').getString('staff.cat.copy_editor.caption')); 
106                         $('save').setAttribute('hidden','false'); 
107                         g.retrieve_templates();
108             } else {
109                             $('top_nav').setAttribute('hidden','true');
110             }
111                 } else {
112                         $('top_nav').setAttribute('hidden','true');
113                 }
114
115                 if (g.copies.length > 0 && g.copies[0].id() < 0) {
116                         document.getElementById('copy_notes').setAttribute('hidden','true');
117                         g.apply("status",5 /* In Process */);
118                         $('save').setAttribute('label', $('catStrings').getString('staff.cat.copy_editor.create_copies'));
119                 } else {
120                         g.panes_and_field_names.left_pane = 
121                                 [
122                                         [
123                                                 $('catStrings').getString('staff.cat.copy_editor.status'),
124                                                 { 
125                                                         render: 'typeof fm.status() == "object" ? fm.status().name() : g.data.hash.ccs[ fm.status() ].name()', 
126                                                         input: g.safe_to_edit_copy_status() ? 'c = function(v){ g.apply("status",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( util.functional.map_list( g.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,
127                                                         //input: 'c = function(v){ g.apply("status",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( util.functional.map_list( util.functional.filter_list( g.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);',
128                                                 }
129                                         ]
130                                 ].concat(g.panes_and_field_names.left_pane);
131                 }
132
133                 if (g.copies.length != 1) {
134                         document.getElementById('copy_notes').setAttribute('hidden','true');
135                 }
136
137                 /******************************************************************************************************/
138                 /* Show the Record Details? */
139
140                 if (g.docid) {
141                         document.getElementById('brief_display').setAttribute(
142                                 'src',
143                                 urls.XUL_BIB_BRIEF + '?docid=' + g.docid
144                         );
145                 } else {
146                         document.getElementById('brief_display').setAttribute('hidden','true');
147                 }
148
149                 /******************************************************************************************************/
150                 /* Add stat cats to the panes_and_field_names.right_pane4 */
151
152         g.populate_stat_cats();
153
154                 /******************************************************************************************************/
155                 /* Backup copies :) */
156
157                 g.original_copies = js2JSON( g.copies );
158
159                 /******************************************************************************************************/
160                 /* Do it */
161
162                 g.summarize( g.copies );
163                 g.render();
164
165         } catch(E) {
166                 var err_msg = $("commonStrings").getFormattedString('common.exception', ['cat/copy_editor.js', E]);
167                 try { g.error.sdump('D_ERROR',err_msg); } catch(E) { dump(err_msg); dump(js2JSON(E)); }
168                 alert(err_msg);
169         }
170 }
171
172 /******************************************************************************************************/
173 /* Retrieve Templates */
174
175 g.retrieve_templates = function() {
176         try {
177                 JSAN.use('util.widgets'); JSAN.use('util.functional');
178                 g.templates = {};
179                 var robj = g.network.simple_request('FM_AUS_RETRIEVE',[ses(),g.data.list.au[0].id()]);
180                 if (typeof robj['staff_client.copy_editor.templates'] != 'undefined') {
181                         g.templates = robj['staff_client.copy_editor.templates'];
182                 }
183                 util.widgets.remove_children('template_placeholder');
184                 var list = util.functional.map_object_to_list( g.templates, function(obj,i) { return [i, i]; } );
185
186                 g.template_menu = util.widgets.make_menulist( list );
187         g.template_menu.setAttribute('id','template_menu');
188                 $('template_placeholder').appendChild(g.template_menu);
189         g.template_menu.addEventListener(
190             'command',
191             function() { g.copy_editor_prefs[ 'template_menu' ] = { 'value' : g.template_menu.value }; g.save_attributes(); },
192             false
193         );
194         } catch(E) {
195                 g.error.standard_unexpected_error_alert($('catStrings').getString('staff.cat.copy_editor.retrieve_templates.error'), E);
196         }
197 }
198
199 /******************************************************************************************************/
200 /* Apply Template */
201
202 g.apply_template = function() {
203         try {
204                 var name = g.template_menu.value;
205                 if (g.templates[ name ] != 'undefined') {
206                         var template = g.templates[ name ];
207                         for (var i in template) {
208                                 g.changed[ i ] = template[ i ];
209                                 switch( template[i].type ) {
210                                         case 'attribute' :
211                                                 g.apply(template[i].field,template[i].value);
212                                         break;
213                                         case 'stat_cat' :
214                                                 if (g.stat_cat_seen[ template[i].field ]) g.apply_stat_cat(template[i].field,template[i].value);
215                                         break;
216                                         case 'owning_lib' :
217                                                 g.apply_owning_lib(template[i].value);
218                                         break;
219                                 }
220                         }
221                         g.summarize( g.copies );
222                         g.render();
223                 }
224         } catch(E) {
225                 g.error.standard_unexpected_error_alert($('catStrings').getString('staff.cat.copy_editor.apply_templates.error'), E);
226         }
227 }
228
229 /******************************************************************************************************/
230 /* Save as Template */
231
232 g.save_template = function() {
233         try {
234                 var name = window.prompt(
235                         $('catStrings').getString('staff.cat.copy_editor.save_as_template.prompt'),
236                         '',
237                         $('catStrings').getString('staff.cat.copy_editor.save_as_template.title')
238                 );
239                 if (!name) return;
240                 g.templates[name] = g.changed;
241                 var robj = g.network.simple_request(
242                         'FM_AUS_UPDATE',[ses(),g.data.list.au[0].id(), { 'staff_client.copy_editor.templates' : g.templates }]
243                 );
244                 if (typeof robj.ilsevent != 'undefined') {
245                         throw(robj);
246                 } else {
247                         alert($('catStrings').getFormattedString('staff.cat.copy_editor.save_as_template.success', [name]));
248                         setTimeout(
249                                 function() {
250                                         try {
251                                                 g.retrieve_templates();
252                                         } catch(E) {
253                                                 g.error.standard_unexpected_error_alert($('catStrings').getString('staff.cat.copy_editor.save_as_template.error'), E);
254                                         }
255                                 },0
256                         );
257                 }
258         } catch(E) {
259                 g.error.standard_unexpected_error_alert($('catStrings').getString('staff.cat.copy_editor.save_as_template.error'), E);
260         }
261 }
262
263 /******************************************************************************************************/
264 /* Delete Template */
265
266 g.delete_template = function() {
267         try {
268                 var name = g.template_menu.value;
269                 if (!name) return;
270                 if (! window.confirm($('catStrings').getFormattedString('staff.cat.copy_editor.delete_template.confirm', [name])) return;
271                 delete(g.templates[name]);
272                 var robj = g.network.simple_request(
273                         'FM_AUS_UPDATE',[ses(),g.data.list.au[0].id(), { 'staff_client.copy_editor.templates' : g.templates }]
274                 );
275                 if (typeof robj.ilsevent != 'undefined') {
276                         throw(robj);
277                 } else {
278                         alert($('catStrings').getFormattedString('staff.cat.copy_editor.delete_template.confirm', [name]));
279                         setTimeout(
280                                 function() {
281                                         try {
282                                                 g.retrieve_templates();
283                                         } catch(E) {
284                                                 g.error.standard_unexpected_error_alert($('catStrings').getString('staff.cat.copy_editor.delete_template.error'), E);
285                                         }
286                                 },0
287                         );
288                 }
289         } catch(E) {
290                 g.error.standard_unexpected_error_alert($('catStrings').getString('staff.cat.copy_editor.delete_template.error'), E);
291         }
292 }
293
294 /******************************************************************************************************/
295 /* Export Templates */
296
297 g.export_templates = function() {
298         try {
299                 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
300                 JSAN.use('util.file'); var f = new util.file('');
301         f.export_file( { 'title' : $('catStrings').getString('staff.cat.copy_editor.export_templates.title'), 'data' : g.templates } );
302         } catch(E) {
303                 g.error.standard_unexpected_error_alert($('catStrings').getString('staff.cat.copy_editor.export_templates.error'), E);
304         }
305 }
306
307 /******************************************************************************************************/
308 /* Import Templates */
309
310 g.import_templates = function() {
311         try {
312                 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
313                 JSAN.use('util.file'); var f = new util.file('');
314         var temp = f.import_file( { 'title' : $('catStrings').getString('staff.cat.copy_editor.import_templates.title') } );
315                 if (temp) {
316                         for (var i in temp) {
317
318                                 if (g.templates[i]) {
319
320                                         var r = g.error.yns_alert(
321                                                 $('catStrings').getString('staff.cat.copy_editor.import_templates.replace.prompt') + '\n' + g.error.pretty_print( js2JSON( temp[i] ) ),
322                                                 $('catStrings').getFormattedString('staff.cat.copy_editor.import_templates.replace.title', [i]),
323                                                 $('catStrings').getString('staff.cat.copy_editor.import_templates.replace.yes'),
324                                                 $('catStrings').getString('staff.cat.copy_editor.import_templates.replace.no'),
325                                                 null,
326                                                 $('catStrings').getString('staff.cat.copy_editor.import_templates.replace.click_here')
327                                         );
328
329                                         if (r == 0 /* Yes */) g.templates[i] = temp[i];
330
331                                 } else {
332
333                                         g.templates[i] = temp[i];
334
335                                 }
336
337                         }
338
339                         var r = g.error.yns_alert(
340                                 'Save all of these imported templates permanently to this account?',
341                                 'Final Warning', 'Yes', 'No', null, 'Click here'
342                         );
343
344                         if (r == 0 /* Yes */) {
345                                 var robj = g.network.simple_request(
346                                         'FM_AUS_UPDATE',[ses(),g.data.list.au[0].id(), { 'staff_client.copy_editor.templates' : g.templates }]
347                                 );
348                                 if (typeof robj.ilsevent != 'undefined') {
349                                         throw(robj);
350                                 } else {
351                                         alert('All templates saved.');
352                                         setTimeout(
353                                                 function() {
354                                                         try {
355                                                                 g.retrieve_templates();
356                                                         } catch(E) {
357                                                                 g.error.standard_unexpected_error_alert('Error saving templates',E);
358                                                         }
359                                                 },0
360                                         );
361                                 }
362                         } else {
363                                 util.widgets.remove_children('template_placeholder');
364                                 var list = util.functional.map_object_to_list( g.templates, function(obj,i) { return [i, i]; } );
365                                 g.template_menu = util.widgets.make_menulist( list );
366                                 $('template_placeholder').appendChild(g.template_menu);
367                                 alert("Note: These imported templates will get saved along with any new template you try to create, but if that doesn't happen, then these templates will disappear with the next invocation of the item attribute editor.");
368                         }
369
370                 }
371         } catch(E) {
372                 g.error.standard_unexpected_error_alert('Error importing templates',E);
373         }
374 }
375
376
377 /******************************************************************************************************/
378 /* Restore backup copies */
379
380 g.reset = function() {
381         g.changed = {};
382         g.copies = JSON2js( g.original_copies );
383         g.summarize( g.copies );
384         g.render();
385 }
386
387 /******************************************************************************************************/
388 /* Apply a value to a specific field on all the copies being edited */
389
390 g.apply = function(field,value) {
391         g.error.sdump('D_TRACE','applying field = <' + field + '>  value = <' + value + '>\n');
392         if (value == '<HACK:KLUDGE:NULL>') value = null;
393         if (field == 'alert_message') { value = value.replace(/^\W+$/g,''); }
394         if (field == 'price' || field == 'deposit_amount') {
395                 if (value == '') { value = null; } else { JSAN.use('util.money'); value = util.money.sanitize( value ); }
396         }
397         for (var i = 0; i < g.copies.length; i++) {
398                 var copy = g.copies[i];
399                 try {
400                         copy[field]( value ); copy.ischanged('1');
401                 } catch(E) {
402                         alert(E);
403                 }
404         }
405 }
406
407 /******************************************************************************************************/
408 /* Apply a stat cat entry to all the copies being edited.  An entry_id of < 0 signifies the stat cat is being removed. */
409
410 g.apply_stat_cat = function(sc_id,entry_id) {
411         g.error.sdump('D_TRACE','sc_id = ' + sc_id + '  entry_id = ' + entry_id + '\n');
412         for (var i = 0; i < g.copies.length; i++) {
413                 var copy = g.copies[i];
414                 try {
415                         copy.ischanged('1');
416                         var temp = copy.stat_cat_entries();
417                         if (!temp) temp = [];
418                         temp = util.functional.filter_list(
419                                 temp,
420                                 function (obj) {
421                                         return (obj.stat_cat() != sc_id);
422                                 }
423                         );
424                         if (entry_id > -1) temp.push( 
425                                 util.functional.find_id_object_in_list( 
426                                         g.data.hash.asc[sc_id].entries(), 
427                                         entry_id
428                                 )
429                         );
430                         copy.stat_cat_entries( temp );
431
432                 } catch(E) {
433                         g.error.standard_unexpected_error_alert('apply_stat_cat',E);
434                 }
435         }
436 }
437
438 /******************************************************************************************************/
439 /* Apply an "owning lib" to all the copies being edited.  That is, change and auto-vivicating volumes */
440
441 g.apply_owning_lib = function(ou_id) {
442         g.error.sdump('D_TRACE','ou_id = ' + ou_id + '\n');
443         for (var i = 0; i < g.copies.length; i++) {
444                 var copy = g.copies[i];
445                 try {
446                         if (!g.map_acn[copy.call_number()]) {
447                                 var volume = g.network.simple_request('FM_ACN_RETRIEVE.authoritative',[ copy.call_number() ]);
448                                 if (typeof volume.ilsevent != 'undefined') {
449                                         g.error.standard_unexpected_error_alert('Error retrieving Volume information for copy ' + copy.barcode() + ".  The owning library for this copy won't be changed.",volume);
450                                         continue;
451                                 }
452                                 g.map_acn[copy.call_number()] = volume;
453                         }
454                         var old_volume = g.map_acn[copy.call_number()];
455                         var acn_id = g.network.simple_request(
456                                 'FM_ACN_FIND_OR_CREATE',
457                                 [ses(),old_volume.label(),old_volume.record(),ou_id]
458                         );
459                         if (typeof acn_id.ilsevent != 'undefined') {
460                                 g.error.standard_unexpected_error_alert('Error changing owning lib for copy ' + copy.barcode() + ".  The owning library for this copy won't be changed.",acn_id);
461                                 continue;
462                         }
463                         copy.call_number(acn_id);
464                         copy.ischanged('1');
465                 } catch(E) {
466                         g.error.standard_unexpected_error_alert('apply_stat_cat',E);
467                 }
468         }
469 }
470
471 /******************************************************************************************************/
472 /* This returns true if none of the copies being edited are pre-cats */
473
474 g.safe_to_change_owning_lib = function() {
475         try {
476                 var safe = true;
477                 for (var i = 0; i < g.copies.length; i++) {
478                         var cn = g.copies[i].call_number();
479                         if (typeof cn == 'object') { cn = cn.id(); }
480                         if (cn == -1) { safe = false; }
481                 }
482                 return safe;
483         } catch(E) {
484         g.error.standard_unexpected_error_alert('safe_to_change_owning_lib?',E);
485                 return false;
486         }
487 }
488
489 /******************************************************************************************************/
490 /* This returns true if none of the copies being edited have a magical status found in my_constants.magical_statuses */
491
492 g.safe_to_edit_copy_status = function() {
493         try {
494                 var safe = true;
495                 for (var i = 0; i < g.copies.length; i++) {
496                         var status = g.copies[i].status(); if (typeof status == 'object') status = status.id();
497                         if (typeof my_constants.magical_statuses[ status ] != 'undefined') safe = false;
498                 }
499                 return safe;
500         } catch(E) {
501                 g.error.standard_unexpected_error_alert('safe_to_edit_copy_status?',E);
502                 return false;
503         }
504 }
505
506 /******************************************************************************************************/
507 /* This concats and uniques all the alert messages for use as the default value for a new alert message */
508
509 g.populate_alert_message_input = function(tb) {
510         try {
511                 var seen = {}; var s = '';
512                 for (var i = 0; i < g.copies.length; i++) {
513                         var msg = g.copies[i].alert_message(); 
514                         if (msg) {
515                                 if (typeof seen[msg] == 'undefined') {
516                                         s += msg + '\n';
517                                         seen[msg] = true;
518                                 }
519                         }
520                 }
521                 tb.setAttribute('value',s);
522         } catch(E) {
523                 g.error.standard_unexpected_error_alert('populate_alert_message_input',E);
524         }
525 }
526
527 /***************************************************************************************************************/
528 /* This returns a list of acpl's appropriate for the copies being edited (and caches them in the global stash) */
529
530 g.get_acpl_list_for_lib = function(lib_id,but_only_these) {
531     g.data.stash_retrieve();
532     var label = 'acpl_list_for_lib_'+lib_id;
533     if (typeof g.data[label] == 'undefined') {
534         var robj = g.network.simple_request('FM_ACPL_RETRIEVE', [ lib_id ]); // This returns acpl's for all ancestors and descendants as well as the lib
535         if (typeof robj.ilsevent != 'undefined') throw(robj);
536         var temp_list = [];
537         for (var j = 0; j < robj.length; j++) {
538             var my_acpl = robj[j];
539             if (typeof g.data.hash.acpl[ my_acpl.id() ] == 'undefined') {
540                 g.data.hash.acpl[ my_acpl.id() ] = my_acpl;
541                 g.data.list.acpl.push( my_acpl );
542             }
543             var only_this_lib = my_acpl.owning_lib(); if (!only_this_lib) continue;
544             if (typeof only_this_lib == 'object') only_this_lib = only_this_lib.id();
545             if (but_only_these.indexOf( String( only_this_lib ) ) != -1) { // This filters out some of the libraries (usually the descendants)
546                 temp_list.push( my_acpl );
547             }
548         }
549         g.data[label] = temp_list; g.data.stash(label,'hash','list');
550     }
551     return g.data[label];
552 }
553
554 /******************************************************************************************************/
555 /* This returns a list of acpl's appropriate for the copies being edited */
556
557 g.get_acpl_list = function() {
558         try {
559
560                 JSAN.use('util.functional');
561
562         var my_acpls = {};
563
564         /**************************************/
565         /* get owning libs from call numbers */
566
567                 var owning_libs = {}; 
568                 for (var i = 0; i < g.copies.length; i++) {
569             var callnumber = g.copies[i].call_number();
570             if (!callnumber) continue;
571                         var cn_id = typeof callnumber == 'object' ? callnumber.id() : callnumber;
572                         if (cn_id > 0) {
573                                 if (! g.map_acn[ cn_id ]) {
574                                         var req = g.network.simple_request('FM_ACN_RETRIEVE.authoritative',[ cn_id ]);
575                     if (typeof req.ilsevent == 'undefined') {
576                                         g.map_acn[ cn_id ] = req;
577                     } else {
578                         continue;
579                     }
580                                 }
581                 var consider_lib = g.map_acn[ cn_id ].owning_lib();
582                 if (!consider_lib) continue;
583                 owning_libs[ typeof consider_lib == 'object' ? consider_lib.id() : consider_lib ] = true;
584                         }
585                 }
586                 if (g.callnumbers) {
587                         for (var i in g.callnumbers) {
588                 var consider_lib = g.callnumbers[i].owning_lib;
589                 if (!consider_lib) continue;
590                 owning_libs[ typeof consider_lib == 'object' ? consider_lib.id() : consider_lib ] = true;
591                         }
592                 }
593
594         /***************************************************************************************************/
595         /* now find the first ancestor they all have in common, get the acpl's for it and higher ancestors */
596
597                 JSAN.use('util.fm_utils');
598         var libs = []; for (var i in owning_libs) libs.push(i);
599         if (libs.length > 0) {
600             var ancestor = util.fm_utils.find_common_aou_ancestor( libs );
601             if (typeof ancestor == 'object' && ancestor != null) ancestor = ancestor.id();
602
603             if (ancestor) {
604                 var ancestors = util.fm_utils.find_common_aou_ancestors( libs );
605                 var acpl_list = g.get_acpl_list_for_lib(ancestor, ancestors);
606                 if (acpl_list) for (var i = 0; i < acpl_list.length; i++) {
607                     if (acpl_list[i] != null) {
608                         my_acpls[ typeof acpl_list[i] == 'object' ? acpl_list[i].id() : acpl_list[i] ] = true;
609                     }
610                 }
611             }
612         }
613         
614         /*****************/
615         /* get circ libs */
616
617         var circ_libs = {};
618
619         for (var i = 0; i < g.copies.length; i++) {
620             var consider_lib = g.copies[i].circ_lib();
621             if (!consider_lib) continue;
622             circ_libs[ typeof consider_lib == 'object' ? consider_lib.id() : consider_lib ] = true;
623         }
624
625         /***************************************************************************************************/
626         /* now find the first ancestor they all have in common, get the acpl's for it and higher ancestors */
627
628         libs = []; for (var i in circ_libs) libs.push(i);
629         if (libs.length > 0) {
630                 var ancestor = util.fm_utils.find_common_aou_ancestor( libs );
631                 if (typeof ancestor == 'object' && ancestor != null) ancestor = ancestor.id();
632
633                 if (ancestor) {
634                     var ancestors = util.fm_utils.find_common_aou_ancestors( libs );
635                         var acpl_list = g.get_acpl_list_for_lib(ancestor, ancestors);
636                 if (acpl_list) for (var i = 0; i < acpl_list.length; i++) {
637                     if (acpl_list[i] != null) {
638                         my_acpls[ typeof acpl_list[i] == 'object' ? acpl_list[i].id() : acpl_list[i] ] = true;
639                     }
640                 }
641             }
642         }
643
644         var acpl_list = []; for (var i in my_acpls) acpl_list.push( g.data.hash.acpl[ i ] );
645         return acpl_list.sort(
646             function(a,b) {
647                 var label_a = g.data.hash.aou[ a.owning_lib() ].shortname() + ' : ' + a.name();
648                 var label_b = g.data.hash.aou[ b.owning_lib() ].shortname() + ' : ' + b.name();
649                 if (label_a < label_b) return -1;
650                 if (label_a > label_b) return 1;
651                 return 0;
652             }
653         );
654         
655         } catch(E) {
656                 g.error.standard_unexpected_error_alert('get_acpl_list',E);
657                 return [];
658         }
659 }
660
661
662 /******************************************************************************************************/
663 /* This keeps track of what fields have been edited for styling purposes */
664
665 g.changed = {};
666
667 /******************************************************************************************************/
668 /* These need data from the middle layer to render */
669
670 g.special_exception = {
671         'Owning Lib : Call Number' : function(label,value) {
672                 JSAN.use('util.widgets');
673                 if (value>0) { /* an existing call number */
674                         g.network.simple_request(
675                                 'FM_ACN_RETRIEVE.authoritative',
676                                 [ value ],
677                                 function(req) {
678                                         var cn = '??? id = ' + value;
679                                         try {
680                                                 cn = req.getResultObject();
681                                         } catch(E) {
682                                                 g.error.sdump('D_ERROR','callnumber retrieve: ' + E);
683                                         }
684                                         util.widgets.set_text(label,g.data.hash.aou[ cn.owning_lib() ].shortname() + ' : ' + cn.label());
685                                 }
686                         );
687                 } else { /* a yet to be created call number */
688                         if (g.callnumbers) {
689                                 util.widgets.set_text(label,g.data.hash.aou[ g.callnumbers[value].owning_lib ].shortname() + ' : ' + g.callnumbers[value].label);
690                         }
691                 }
692         },
693         'Creator' : function(label,value) {
694                 if (value == null || value == '' || value == 'null') return;
695                 g.network.simple_request(
696                         'FM_AU_RETRIEVE_VIA_ID',
697                         [ ses(), value ],
698                         function(req) {
699                                 var p = '??? id = ' + value;
700                                 try {
701                                         p = req.getResultObject();
702                                         p = p.usrname();
703
704                                 } catch(E) {
705                                         g.error.sdump('D_ERROR','patron retrieve: ' + E);
706                                 }
707                                 JSAN.use('util.widgets');
708                                 util.widgets.set_text(label,p);
709                         }
710                 );
711         },
712         'Last Editor' : function(label,value) {
713                 if (value == null || value == '' || value == 'null') return;
714                 g.network.simple_request(
715                         'FM_AU_RETRIEVE_VIA_ID',
716                         [ ses(), value ],
717                         function(req) {
718                                 var p = '??? id = ' + value;
719                                 try {
720                                         p = req.getResultObject();
721                                         p = p.usrname();
722
723                                 } catch(E) {
724                                         g.error.sdump('D_ERROR','patron retrieve: ' + E);
725                                 }
726                                 util.widgets.set_text(label,p);
727                         }
728                 );
729         }
730
731 }
732
733 /******************************************************************************************************/
734 g.readonly_stat_cat_names = [];
735 g.editable_stat_cat_names = [];
736
737 /******************************************************************************************************/
738 /* These get show in the left panel */
739
740 function init_panes() {
741 g.panes_and_field_names = {
742
743         'left_pane' :
744 [
745         [
746                 "Barcode",               
747                 {
748                         render: 'fm.barcode();',
749                 }
750         ], 
751         [
752                 "Creation Date",
753                 { 
754                         render: 'util.date.formatted_date( fm.create_date(), "%F");',
755                 }
756         ],
757         [
758                 "Creator",
759                 { 
760                         render: 'fm.creator();',
761                 }
762         ],
763         [
764                 "Last Edit Date",
765                 { 
766                         render: 'util.date.formatted_date( fm.edit_date(), "%F");',
767                 }
768         ],
769         [
770                 "Last Editor",
771                 {
772                         render: 'fm.editor();',
773                 }
774         ],
775
776 ],
777
778 'right_pane' :
779 [
780         [
781                 "Shelving Location",
782                 { 
783                         render: 'typeof fm.location() == "object" ? fm.location().name() : g.data.lookup("acpl",fm.location()).name()', 
784                         input: 'c = function(v){ g.apply("location",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( util.functional.map_list( g.get_acpl_list(), function(obj) { return [ g.data.hash.aou[ obj.owning_lib() ].shortname() + " : " + obj.name(), obj.id() ]; }).sort()); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
785
786                 }
787         ],
788         [
789                 "Circulation Library",          
790                 {       
791                         render: 'typeof fm.circ_lib() == "object" ? fm.circ_lib().shortname() : g.data.hash.aou[ fm.circ_lib() ].shortname()',
792                         //input: 'c = function(v){ g.apply("circ_lib",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( util.functional.map_list( util.functional.filter_list(g.data.list.my_aou, function(obj) { return g.data.hash.aout[ obj.ou_type() ].can_have_vols(); }), function(obj) { return [ obj.shortname(), obj.id() ]; }).sort() ); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
793                         input: 'c = function(v){ g.apply("circ_lib",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( util.functional.map_list( g.data.list.aou, function(obj) { var sname = obj.shortname(); for (i = sname.length; i < 20; i++) sname += " "; return [ obj.name() ? sname + " " + obj.name() : obj.shortname(), obj.id(), ( ! get_bool( g.data.hash.aout[ obj.ou_type() ].can_have_vols() ) ), ( g.data.hash.aout[ obj.ou_type() ].depth() * 2), ]; }), g.data.list.au[0].ws_ou()); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
794                 } 
795         ],
796         [
797                 "Owning Lib : Call Number",     
798                 {
799                         render: 'fm.call_number();',
800                         input: g.safe_to_change_owning_lib() ? 'c = function(v){ g.apply_owning_lib(v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( util.functional.map_list( g.data.list.aou, function(obj) { var sname = obj.shortname(); for (i = sname.length; i < 20; i++) sname += " "; return [ obj.name() ? sname + " " + obj.name() : obj.shortname(), obj.id(), ( ! get_bool( g.data.hash.aout[ obj.ou_type() ].can_have_vols() ) ), ( g.data.hash.aout[ obj.ou_type() ].depth() * 2), ]; }), g.data.list.au[0].ws_ou()); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);' : undefined,
801                 }
802         ],
803         [
804                 "Copy Number",
805                 { 
806                         render: 'fm.copy_number() == null ? "<Unset>" : fm.copy_number()',
807                         input: 'c = function(v){ g.apply("copy_number",v); if (typeof post_c == "function") post_c(v); }; x = document.createElement("textbox"); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
808                 }
809         ],
810
811
812 ],
813
814 'right_pane2' :
815 [
816         [
817                 "Circulate?",
818                 {       
819                         render: 'fm.circulate() == null ? "<Unset>" : ( get_bool( fm.circulate() ) ? "Yes" : "No" )',
820                         input: 'c = function(v){ g.apply("circulate",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( [ [ "Yes", get_db_true() ], [ "No", get_db_false() ] ] ); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
821                 }
822         ],
823         [
824                 "Holdable?",
825                 { 
826                         render: 'fm.holdable() == null ? "<Unset>" : ( get_bool( fm.holdable() ) ? "Yes" : "No" )', 
827                         input: 'c = function(v){ g.apply("holdable",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( [ [ "Yes", get_db_true() ], [ "No", get_db_false() ] ] ); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
828                 }
829         ],
830         [
831                 "Age Protection",
832                 {
833                         render: 'fm.age_protect() == null ? "<Unset>" : ( typeof fm.age_protect() == "object" ? fm.age_protect().name() : g.data.hash.crahp[ fm.age_protect() ].name() )', 
834                         input: 'c = function(v){ g.apply("age_protect",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( [ [ "<Remove Protection>", "<HACK:KLUDGE:NULL>" ] ].concat( util.functional.map_list( g.data.list.crahp, function(obj) { return [ obj.name(), obj.id() ]; }).sort() ) ); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
835                 }
836
837         ],
838         [
839                 "Loan Duration",
840                 { 
841                         render: 'switch(Number(fm.loan_duration())){ case 1: "Short"; break; case 2: "Normal"; break; case 3: "Long"; break; }',
842                         input: 'c = function(v){ g.apply("loan_duration",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( [ [ "Short", "1" ], [ "Normal", "2" ], [ "Long", "3" ] ] ); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
843
844                 }
845         ],
846         [
847                 "Fine Level",
848                 {
849                         render: 'switch(Number(fm.fine_level())){ case 1: "Low"; break; case 2: "Normal"; break; case 3: "High"; break; }',
850                         input: 'c = function(v){ g.apply("fine_level",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( [ [ "Low", "1" ], [ "Normal", "2" ], [ "High", "3" ] ] ); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
851                 }
852         ],
853
854          [
855                 "Circulate as Type",    
856                 {       
857                         render: 'fm.circ_as_type() == null ? "<Unset>" : g.data.hash.citm[ fm.circ_as_type() ].value()',
858                         input: 'c = function(v){ g.apply("circ_as_type",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( util.functional.map_list( g.data.list.citm, function(n){return [ n.code() + " - " + n.value(), n.code()];} ).sort() ); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
859                 } 
860         ],
861         [
862                 "Circulation Modifier",
863                 {       
864                         render: 'fm.circ_modifier() == null ? "<Unset>" : fm.circ_modifier()',
865                         /*input: 'c = function(v){ g.apply("circ_modifier",v); if (typeof post_c == "function") post_c(v); }; x = document.createElement("textbox"); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',*/
866                         input: 'c = function(v){ g.apply("circ_modifier",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( util.functional.map_list( g.data.list.circ_modifier, function(obj) { return [ obj, obj ]; } ).sort() ); x.setAttribute("editable","true"); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
867                 }
868         ],
869 ],
870
871 'right_pane3' :
872 [       [
873                 "Alert Message",
874                 {
875                         render: 'fm.alert_message() == null ? "<Unset>" : fm.alert_message()',
876                         input: 'c = function(v){ g.apply("alert_message",v); if (typeof post_c == "function") post_c(v); }; x = document.createElement("textbox"); x.setAttribute("multiline",true); g.populate_alert_message_input(x); x.addEventListener("apply",function(f){ return function(ev) { f( ev.target.value ); } }(c), false);',
877                 }
878         ],
879
880         [
881                 "Deposit?",
882                 { 
883                         render: 'fm.deposit() == null ? "<Unset>" : ( get_bool( fm.deposit() ) ? "Yes" : "No" )',
884                         input: 'c = function(v){ g.apply("deposit",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( [ [ "Yes", get_db_true() ], [ "No", get_db_false() ] ] ); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
885                 }
886         ],
887         [
888                 "Deposit Amount",
889                 { 
890                         render: 'if (fm.deposit_amount() == null) { "<Unset>"; } else { util.money.sanitize( fm.deposit_amount() ); }',
891                         input: 'c = function(v){ g.apply("deposit_amount",v); if (typeof post_c == "function") post_c(v); }; x = document.createElement("textbox"); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
892                 }
893         ],
894         [
895                 "Price",
896                 { 
897                         render: 'if (fm.price() == null) { "<Unset>"; } else { util.money.sanitize( fm.price() ); }', 
898                         input: 'c = function(v){ g.apply("price",v); if (typeof post_c == "function") post_c(v); }; x = document.createElement("textbox"); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
899                 }
900         ],
901
902         [
903                 "OPAC Visible?",
904                 { 
905                         render: 'fm.opac_visible() == null ? "<Unset>" : ( get_bool( fm.opac_visible() ) ? "Yes" : "No" )', 
906                         input: 'c = function(v){ g.apply("opac_visible",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( [ [ "Yes", get_db_true() ], [ "No", get_db_false() ] ] ); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
907                 }
908         ],
909         [
910                 "Reference?",
911                 { 
912                         render: 'fm.ref() == null ? "<Unset>" : ( get_bool( fm.ref() ) ? "Yes" : "No" )', 
913                         input: 'c = function(v){ g.apply("ref",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( [ [ "Yes", get_db_true() ], [ "No", get_db_false() ] ] ); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
914                 }
915         ],
916 ],
917
918 'right_pane4' : 
919 [
920 ]
921
922 };
923 }
924
925 /******************************************************************************************************/
926 /* This loops through all our fieldnames and all the copies, tallying up counts for the different values */
927
928 g.summarize = function( copies ) {
929         /******************************************************************************************************/
930         /* Setup */
931
932         JSAN.use('util.date'); JSAN.use('util.money');
933         g.summary = {};
934         g.field_names = [];
935         for (var i in g.panes_and_field_names) {
936                 g.field_names = g.field_names.concat( g.panes_and_field_names[i] );
937         }
938         g.field_names = g.field_names.concat( g.editable_stat_cat_names );
939         g.field_names = g.field_names.concat( g.readonly_stat_cat_names );
940
941         /******************************************************************************************************/
942         /* Loop through the field names */
943
944         for (var i = 0; i < g.field_names.length; i++) {
945
946                 var field_name = g.field_names[i][0];
947                 var render = g.field_names[i][1].render;
948         var attr = g.field_names[i][1].attr;
949                 g.summary[ field_name ] = {};
950
951                 /******************************************************************************************************/
952                 /* Loop through the copies */
953
954                 for (var j = 0; j < copies.length; j++) {
955
956                         var fm = copies[j];
957                         var cmd = render || ('fm.' + field_name + '();');
958                         var value = '???';
959
960                         /**********************************************************************************************/
961                         /* Try to retrieve the value for this field for this copy */
962
963                         try { 
964                                 value = eval( cmd ); 
965                         } catch(E) { 
966                                 g.error.sdump('D_ERROR','Attempted ' + cmd + '\n' +  E + '\n'); 
967                         }
968                         if (typeof value == 'object' && value != null) {
969                                 alert('FIXME: field_name = <' + field_name + '>  value = <' + js2JSON(value) + '>\n');
970                         }
971
972                         /**********************************************************************************************/
973                         /* Tally the count */
974
975                         if (g.summary[ field_name ][ value ]) {
976                                 g.summary[ field_name ][ value ]++;
977                         } else {
978                                 g.summary[ field_name ][ value ] = 1;
979                         }
980                 }
981         }
982         g.error.sdump('D_TRACE','summary = ' + js2JSON(g.summary) + '\n');
983 }
984
985 /******************************************************************************************************/
986 /* Display the summarized data and inputs for editing */
987
988 g.render = function() {
989
990         /******************************************************************************************************/
991         /* Library setup and clear any existing interface */
992
993         JSAN.use('util.widgets'); JSAN.use('util.date'); JSAN.use('util.money'); JSAN.use('util.functional');
994
995         for (var i in g.panes_and_field_names) {
996                 var p = document.getElementById(i);
997                 if (p) util.widgets.remove_children(p);
998         }
999
1000         /******************************************************************************************************/
1001         /* Populate the library filter menu for stat cats */
1002
1003     var sc_libs = {};
1004     for (var i = 0; i < g.panes_and_field_names.right_pane4.length; i++) {
1005         sc_libs[ g.panes_and_field_names.right_pane4[i][1].attr.sc_lib ] = true;
1006     }
1007     var sc_libs2 = [];
1008     for (var i in sc_libs) { sc_libs2.push( [ g.data.hash.aou[ i ].shortname(), i ] ); }
1009     sc_libs2.sort();
1010     var x = document.getElementById("stat_cat_lib_filter_menu").firstChild;
1011     JSAN.use('util.widgets'); util.widgets.remove_children(x);
1012     for (var i = 0; i < sc_libs2.length; i++) {
1013         var menuitem = document.createElement('menuitem');
1014         menuitem.setAttribute('id','filter_'+sc_libs2[i][1]);
1015         menuitem.setAttribute('type','checkbox');
1016         menuitem.setAttribute('checked','true');
1017         menuitem.setAttribute('label',sc_libs2[i][0]);
1018         menuitem.setAttribute('value',sc_libs2[i][1]);
1019         menuitem.setAttribute('oncommand','try{g.toggle_stat_cat_display(this);}catch(E){alert(E);}');
1020         x.appendChild(menuitem);
1021     }
1022
1023         /******************************************************************************************************/
1024         /* Prepare the panes */
1025
1026         var groupbox; var caption; var vbox; var grid; var rows;
1027         
1028         /******************************************************************************************************/
1029         /* Loop through the field names */
1030
1031         for (h in g.panes_and_field_names) {
1032                 if (!document.getElementById(h)) continue;
1033                 for (var i = 0; i < g.panes_and_field_names[h].length; i++) {
1034                         try {
1035                                 var f = g.panes_and_field_names[h][i]; var fn = f[0]; var attr = f[1].attr;
1036                                 groupbox = document.createElement('groupbox'); document.getElementById(h).appendChild(groupbox);
1037                 if (attr) {
1038                     for (var a in attr) {
1039                         groupbox.setAttribute(a,attr[a]);
1040                     }
1041                 }
1042                                 if (typeof g.changed[fn] != 'undefined') groupbox.setAttribute('class','copy_editor_field_changed');
1043                                 caption = document.createElement('caption'); groupbox.appendChild(caption);
1044                                 caption.setAttribute('label',fn); caption.setAttribute('id','caption_'+fn);
1045                                 vbox = document.createElement('vbox'); groupbox.appendChild(vbox);
1046                                 grid = util.widgets.make_grid( [ { 'flex' : 1 }, {}, {} ] ); vbox.appendChild(grid);
1047                                 grid.setAttribute('flex','1');
1048                                 rows = grid.lastChild;
1049                                 var row;
1050                                 
1051                                 /**************************************************************************************/
1052                                 /* Loop through each value for the field */
1053
1054                                 for (var j in g.summary[fn]) {
1055                                         var value = j; var count = g.summary[fn][j];
1056                                         row = document.createElement('row'); rows.appendChild(row);
1057                                         var label1 = document.createElement('description'); row.appendChild(label1);
1058                                         if (g.special_exception[ fn ]) {
1059                                                 g.special_exception[ fn ]( label1, value );
1060                                         } else {
1061                                                 label1.appendChild( document.createTextNode(value) );
1062                                         }
1063                                         var label2 = document.createElement('description'); row.appendChild(label2);
1064                                         var unit = count == 1 ? 'copy' : 'copies';
1065                                         label2.appendChild( document.createTextNode(count + ' ' + unit) );
1066                                 }
1067                                 var hbox = document.createElement('hbox'); 
1068                                 hbox.setAttribute('id',fn);
1069                                 groupbox.appendChild(hbox);
1070                                 var hbox2 = document.createElement('hbox');
1071                                 groupbox.appendChild(hbox2);
1072
1073                                 /**************************************************************************************/
1074                                 /* Render the input widget */
1075
1076                                 if (f[1].input && g.edit) {
1077                                         g.render_input(hbox,f[1]);
1078                                 }
1079
1080                         } catch(E) {
1081                                 g.error.sdump('D_ERROR','copy editor: ' + E + '\n');
1082                         }
1083                 }
1084         }
1085     
1086     
1087         /******************************************************************************************************/
1088         /* Synchronize stat cat visibility with library filter menu, and default template selection */
1089     JSAN.use('util.file'); 
1090         var file = new util.file('copy_editor_prefs.'+g.data.server_unadorned);
1091         g.copy_editor_prefs = util.widgets.load_attributes(file);
1092     for (var i in g.copy_editor_prefs) {
1093         if (i.match(/filter_/) && g.copy_editor_prefs[i].checked == '') {
1094             try { 
1095                 g.toggle_stat_cat_display( document.getElementById(i) ); 
1096             } catch(E) { alert(E); }
1097         }
1098     }
1099     if (g.template_menu) g.template_menu.value = g.template_menu.getAttribute('value');
1100
1101 }
1102
1103 /******************************************************************************************************/
1104 /* This actually draws the change button and input widget for a given field */
1105 g.render_input = function(node,blob) {
1106         try {
1107                 // node = hbox ;    groupbox ->  hbox, hbox
1108
1109                 var groupbox = node.parentNode;
1110                 var caption = groupbox.firstChild;
1111                 var vbox = node.previousSibling;
1112                 var hbox = node;
1113                 var hbox2 = node.nextSibling;
1114
1115                 var input_cmd = blob.input;
1116                 var render_cmd = blob.render;
1117         var attr = blob.attr;
1118
1119                 var block = false; var first = true;
1120
1121                 function on_mouseover(ev) {
1122                         groupbox.setAttribute('style','background: white');
1123                 }
1124
1125                 function on_mouseout(ev) {
1126                         groupbox.setAttribute('style','');
1127                 }
1128
1129                 vbox.addEventListener('mouseover',on_mouseover,false);
1130                 vbox.addEventListener('mouseout',on_mouseout,false);
1131                 groupbox.addEventListener('mouseover',on_mouseover,false);
1132                 groupbox.addEventListener('mouseout',on_mouseout,false);
1133                 groupbox.firstChild.addEventListener('mouseover',on_mouseover,false);
1134                 groupbox.firstChild.addEventListener('mouseout',on_mouseout,false);
1135
1136                 function on_click(ev){
1137                         try {
1138                                 if (block) return; block = true;
1139
1140                                 function post_c(v) {
1141                                         try {
1142                                                 /* FIXME - kludgy */
1143                                                 var t = input_cmd.match('apply_stat_cat') ? 'stat_cat' : ( input_cmd.match('apply_owning_lib') ? 'owning_lib' : 'attribute' );
1144                                                 var f;
1145                                                 switch(t) {
1146                                                         case 'attribute' :
1147                                                                 f = input_cmd.match(/apply\("(.+?)",/)[1];
1148                                                         break;
1149                                                         case 'stat_cat' :
1150                                                                 f = input_cmd.match(/apply_stat_cat\((.+?),/)[1];
1151                                                         break;
1152                                                         case 'owning_lib' :
1153                                                                 f = null;
1154                                                         break;
1155                                                 }
1156                                                 g.changed[ hbox.id ] = { 'type' : t, 'field' : f, 'value' : v };
1157                                                 block = false;
1158                                                 setTimeout(
1159                                                         function() {
1160                                                                 g.summarize( g.copies );
1161                                                                 g.render();
1162                                                                 document.getElementById(caption.id).focus();
1163                                                         }, 0
1164                                                 );
1165                                         } catch(E) {
1166                                                 g.error.standard_unexpected_error_alert('post_c',E);
1167                                         }
1168                                 }
1169                                 var x; var c; eval( input_cmd );
1170                                 if (x) {
1171                                         util.widgets.remove_children(vbox);
1172                                         util.widgets.remove_children(hbox);
1173                                         util.widgets.remove_children(hbox2);
1174                                         hbox.appendChild(x);
1175                                         var apply = document.createElement('button');
1176                                         apply.setAttribute('label','Apply');
1177                                         apply.setAttribute('accesskey','A');
1178                                         hbox2.appendChild(apply);
1179                                         apply.addEventListener('command',function() { c(x.value); },false);
1180                                         var cancel = document.createElement('button');
1181                                         cancel.setAttribute('label','Cancel');
1182                                         cancel.addEventListener('command',function() { setTimeout( function() { g.summarize( g.copies ); g.render(); document.getElementById(caption.id).focus(); }, 0); }, false);
1183                                         hbox2.appendChild(cancel);
1184                                         setTimeout( function() { x.focus(); }, 0 );
1185                                 }
1186                         } catch(E) {
1187                                 g.error.standard_unexpected_error_alert('render_input',E);
1188                         }
1189                 }
1190                 vbox.addEventListener('click',on_click, false);
1191                 hbox.addEventListener('click',on_click, false);
1192                 caption.addEventListener('click',on_click, false);
1193                 caption.addEventListener('keypress',function(ev) {
1194                         if (ev.keyCode == 13 /* enter */ || ev.keyCode == 77 /* mac enter */) on_click();
1195                 }, false);
1196                 caption.setAttribute('style','-moz-user-focus: normal');
1197                 caption.setAttribute('onfocus','this.setAttribute("class","outline_me")');
1198                 caption.setAttribute('onblur','this.setAttribute("class","")');
1199
1200         } catch(E) {
1201                 g.error.sdump('D_ERROR',E + '\n');
1202         }
1203 }
1204
1205 /******************************************************************************************************/
1206 /* store the copies in the global xpcom stash */
1207
1208 g.stash_and_close = function() {
1209         try {
1210                 if (g.handle_update) {
1211                         try {
1212                                 var r = g.network.request(
1213                                         api.FM_ACP_FLESHED_BATCH_UPDATE.app,
1214                                         api.FM_ACP_FLESHED_BATCH_UPDATE.method,
1215                                         [ ses(), g.copies, true ]
1216                                 );
1217                                 if (typeof r.ilsevent != 'undefined') {
1218                                         g.error.standard_unexpected_error_alert('copy update',r);
1219                                 } else {
1220                                         alert('Items added/modified.');
1221                                 }
1222                                 /* FIXME -- revisit the return value here */
1223                         } catch(E) {
1224                                 alert('copy update error: ' + js2JSON(E));
1225                         }
1226                 }
1227                 //g.data.temp_copies = js2JSON( g.copies );
1228                 //g.data.stash('temp_copies');
1229                 xulG.copies = g.copies;
1230                 update_modal_xulG(xulG);
1231                 window.close();
1232         } catch(E) {
1233                 g.error.standard_unexpected_error_alert('stash and close',E);
1234         }
1235 }
1236
1237 /******************************************************************************************************/
1238 /* spawn copy notes interface */
1239
1240 g.copy_notes = function() {
1241         JSAN.use('util.window'); var win = new util.window();
1242         win.open(
1243                 urls.XUL_COPY_NOTES, 
1244                 //+ '?copy_id=' + window.escape(g.copies[0].id()),
1245                 'Copy Notes','chrome,resizable,modal',
1246                 { 'copy_id' : g.copies[0].id() }
1247         );
1248 }
1249
1250 /******************************************************************************************************/
1251 /* hides or unhides stat cats based on library stat cat filter menu */
1252 g.toggle_stat_cat_display = function(el) {
1253     if (!el) return;
1254     var visible = el.getAttribute('checked');
1255     var nl = document.getElementsByAttribute('sc_lib',el.getAttribute('value'));
1256     for (var n = 0; n < nl.length; n++) {
1257         if (visible) {
1258             nl[n].setAttribute('hidden','false');
1259         } else {
1260             nl[n].setAttribute('hidden','true');
1261         }
1262     }
1263     g.copy_editor_prefs[ el.getAttribute('id') ] = { 'checked' : visible };
1264     g.save_attributes();
1265 }
1266
1267 /******************************************************************************************************/
1268 /* This adds a stat cat definition to the stat cat pane for rendering */
1269 g.save_attributes = function() {
1270         JSAN.use('util.widgets'); JSAN.use('util.file'); var file = new util.file('copy_editor_prefs.'+g.data.server_unadorned);
1271     var what_to_save = {};
1272     for (var i in g.copy_editor_prefs) {
1273         what_to_save[i] = [];
1274         for (var j in g.copy_editor_prefs[i]) what_to_save[i].push(j);
1275     }
1276         util.widgets.save_attributes(file, what_to_save );
1277 }
1278
1279 /******************************************************************************************************/
1280 /* This adds a stat cat definition to the stat cat pane for rendering */
1281 g.add_stat_cat = function(sc) {
1282     try {
1283                 if (typeof g.data.hash.asc == 'undefined') { g.data.hash.asc = {}; g.data.stash('hash'); }
1284
1285                 var sc_id = sc;
1286
1287                 if (typeof sc == 'object') {
1288
1289                         sc_id = sc.id();
1290                 }
1291
1292                 if (typeof g.stat_cat_seen[sc_id] != 'undefined') { return; }
1293
1294                 g.stat_cat_seen[ sc_id ] = 1;
1295
1296                 if (typeof sc != 'object') {
1297
1298                         sc = g.network.simple_request(
1299                                 'FM_ASC_BATCH_RETRIEVE',
1300                                 [ ses(), [ sc_id ] ]
1301                         )[0];
1302
1303                 }
1304
1305                 g.data.hash.asc[ sc.id() ] = sc; g.data.stash('hash');
1306
1307                 var label_name = g.data.hash.aou[ sc.owner() ].shortname() + " : " + sc.name();
1308
1309                 var temp_array = [
1310                         label_name,
1311                         {
1312                                 render: 'var l = util.functional.find_list( fm.stat_cat_entries(), function(e){ return e.stat_cat() == ' 
1313                                         + sc.id() + '; } ); l ? l.value() : "<Unset>";',
1314                                 input: 'c = function(v){ g.apply_stat_cat(' + sc.id() + ',v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( [ [ "<Remove Stat Cat>", -1 ] ].concat( util.functional.map_list( g.data.hash.asc[' + sc.id() 
1315                                         + '].entries(), function(obj){ return [ obj.value(), obj.id() ]; } ) ).sort() ); '
1316                                         + 'x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c),false);',
1317                 attr: {
1318                     sc_lib: sc.owner(),
1319                 }
1320                         }
1321                 ];
1322
1323                 g.panes_and_field_names.right_pane4.push( temp_array );
1324         } catch(E) {
1325                 g.error.standard_unexpected_error_alert('Error adding stat cat to display definition',E);
1326     }
1327 }
1328
1329 /******************************************************************************************************/
1330 /* Add stat cats to the panes_and_field_names.right_pane4 */
1331 g.populate_stat_cats = function() {
1332     try {
1333         g.data.stash_retrieve();
1334                 g.stat_cat_seen = {};
1335
1336                 function get(lib_id,only_these) {
1337             g.data.stash_retrieve();
1338                         var label = 'asc_list_for_lib_'+lib_id;
1339                         if (typeof g.data[label] == 'undefined') {
1340                                 var robj = g.network.simple_request('FM_ASC_RETRIEVE_VIA_AOU', [ ses(), lib_id ]);
1341                                 if (typeof robj.ilsevent != 'undefined') throw(robj);
1342                                 var temp_list = [];
1343                                 for (var j = 0; j < robj.length; j++) {
1344                                         var my_asc = robj[j];
1345                     if (typeof g.data.hash.asc == 'undefined') { g.data.hash.asc = {}; }
1346                                         if (typeof g.data.hash.asc[ my_asc.id() ] == 'undefined') {
1347                                                 g.data.hash.asc[ my_asc.id() ] = my_asc;
1348                                         }
1349                     var only_this_lib = my_asc.owner(); if (typeof only_this_lib == 'object') only_this_lib = only_this_lib.id();
1350                                         if (only_these.indexOf( String( only_this_lib ) ) != -1) {
1351                                                 temp_list.push( my_asc );
1352                                         }
1353                                 }
1354                                 g.data[label] = temp_list; g.data.stash(label,'hash','list');
1355                         }
1356                         return g.data[label];
1357                 }
1358
1359                 /* The stat cats for the pertinent library -- this is based on workstation ou */
1360         var label = 'asc_list_for_' + typeof g.data.ws_ou == 'object' ? g.data.ws_ou.id() : g.data.ws_ou;
1361         g.data[ label ] = g.data.list.my_asc; g.data.stash('label');
1362                 for (var i = 0; i < g.data.list.my_asc.length; i++) {
1363                         g.add_stat_cat( g.data.list.my_asc[i] );
1364                 }
1365
1366         /* For the others, we want to consider the owning libs, circ libs, and any libs that have stat cats already on the copies,
1367             however, if batch editing, we only want to show the ones they have in common.  So let's compile the libs  */
1368
1369         function add_common_ancestors(sc_libs) {
1370             JSAN.use('util.fm_utils'); 
1371             var libs = []; for (var i in sc_libs) libs.push(i);
1372             var ancestor = util.fm_utils.find_common_aou_ancestor( libs );
1373             if (typeof ancestor == 'object' && ancestor != null) ancestor = ancestor.id();
1374             if (ancestor) {
1375                 var ancestors = util.fm_utils.find_common_aou_ancestors( libs );
1376                 var asc_list = get(ancestor, ancestors);
1377                 for (var i = 0; i < asc_list.length; i++) {
1378                     g.add_stat_cat( asc_list[i] );
1379                 }
1380             }
1381         }
1382
1383                 /* stat cats based on stat cat entries present on these copies */
1384         var sc_libs = {};
1385                 for (var i = 0; i < g.copies.length; i++) {
1386                         var entries = g.copies[i].stat_cat_entries();
1387                         if (!entries) entries = [];
1388                         for (var j = 0; j < entries.length; j++) {
1389                 var lib = entries[j].owner(); if (typeof lib == 'object') lib = lib.id();
1390                                 sc_libs[ lib ] = true;
1391                         }
1392         }
1393         add_common_ancestors(sc_libs); // CAVEAT - if a copy has no stat_cat_entries, it basically gets no vote here
1394
1395         /* stat cats based on Circ Lib */
1396         sc_libs = {};
1397                 for (var i = 0; i < g.copies.length; i++) {
1398             var circ_lib = g.copies[i].circ_lib(); if (typeof circ_lib == 'object') circ_lib = circ_lib.id();
1399             sc_libs[ circ_lib ] = true;
1400         }
1401         add_common_ancestors(sc_libs);
1402
1403         /* stat cats based on Owning Lib */
1404         sc_libs = {};
1405                 for (var i = 0; i < g.copies.length; i++) {
1406             var cn_id = g.copies[i].call_number();
1407                         if (cn_id > 0) {
1408                                 if (! g.map_acn[ cn_id ]) {
1409                     var req = g.network.simple_request('FM_ACN_RETRIEVE.authoritative',[ cn_id ]);
1410                     if (typeof req.ilsevent == 'undefined') {
1411                                         g.map_acn[ cn_id ] = req;
1412                     } else {
1413                         continue;
1414                     }
1415                                 }
1416                 var owning_lib = g.map_acn[ cn_id ].owning_lib(); if (typeof owning_lib == 'object') owning_lib = owning_lib.id();
1417                 sc_libs[ owning_lib ] = true;
1418                         }
1419                 }
1420         add_common_ancestors(sc_libs); // CAVEAT - if a copy is a pre-cat, it basically gets no vote here
1421
1422         g.panes_and_field_names.right_pane4.sort();
1423
1424     } catch(E) {
1425         g.error.standard_unexpected_error_alert('Error populating stat cats for display',E);
1426     }
1427 }
1428
1429