8 /******************************************************************************************************/
9 /* setup JSAN and some initial libraries */
11 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
12 if (typeof JSAN == 'undefined') { throw( "The JSAN library object is missing."); }
13 JSAN.errorLevel = "die"; // none, warn, or die
14 JSAN.addRepository('/xul/server/');
15 JSAN.use('util.error'); g.error = new util.error();
16 g.error.sdump('D_TRACE','my_init() for cat/copy_editor.xul');
18 JSAN.use('util.functional');
19 JSAN.use('OpenILS.data'); g.data = new OpenILS.data(); g.data.init({'via':'stash'});
20 JSAN.use('util.network'); g.network = new util.network();
22 g.docid = xul_param('docid',{'modal_xulG':true});
23 g.handle_update = xul_param('handle_update',{'modal_xulG':true});
25 /******************************************************************************************************/
26 /* Get the copy ids from various sources and flesh them */
28 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});
29 if (!copy_ids) copy_ids = [];
31 if (copy_ids.length > 0) g.copies = g.network.simple_request(
32 'FM_ACP_FLESHED_BATCH_RETRIEVE',
36 /******************************************************************************************************/
37 /* And other fleshed copies if any */
39 if (!g.copies) g.copies = [];
40 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})
41 if (c) g.copies = g.copies.concat(c);
43 /******************************************************************************************************/
44 /* We try to retrieve callnumbers for existing copies, but for new copies, we rely on this */
46 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});
49 /******************************************************************************************************/
50 /* Quick fix, this was defined inline in the global scope but now needs g.error and g.copies from my_init */
54 /******************************************************************************************************/
55 /* Is the interface an editor or a viewer, single or multi copy, existing copies or new copies? */
57 if (xul_param('edit',{'modal_xulG':true}) == '1') {
59 // Editor desired, but let's check permissions
63 var check = g.network.simple_request(
64 'PERM_MULTI_ORG_CHECK',
67 g.data.list.au[0].id(),
68 util.functional.map_list(
72 var cn_id = o.call_number();
74 lib = o.circ_lib(); // base perms on circ_lib instead of owning_lib if pre-cat
76 if (! g.map_acn[ cn_id ]) {
77 g.map_acn[ cn_id ] = g.network.simple_request('FM_ACN_RETRIEVE',[ cn_id ]);
79 lib = g.map_acn[ cn_id ].owning_lib();
84 g.copies.length == 1 ? [ 'UPDATE_COPY' ] : [ 'UPDATE_COPY', 'UPDATE_BATCH_COPY' ]
87 g.edit = check.length == 0;
89 g.error.standard_unexpected_error_alert('batch permission check',E);
93 document.getElementById('caption').setAttribute('label','Copy Editor');
94 document.getElementById('save').setAttribute('hidden','false');
95 g.retrieve_templates();
97 $('top_nav').setAttribute('hidden','true');
100 $('top_nav').setAttribute('hidden','true');
103 if (g.copies.length > 0 && g.copies[0].id() < 0) {
104 document.getElementById('copy_notes').setAttribute('hidden','true');
105 g.apply("status",5 /* In Process */);
106 $('save').setAttribute('label','Create Copies');
108 g.panes_and_field_names.left_pane =
113 render: 'typeof fm.status() == "object" ? fm.status().name() : g.data.hash.ccs[ fm.status() ].name()',
114 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,
115 //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);',
118 ].concat(g.panes_and_field_names.left_pane);
121 if (g.copies.length != 1) {
122 document.getElementById('copy_notes').setAttribute('hidden','true');
125 /******************************************************************************************************/
126 /* Show the Record Details? */
129 document.getElementById('brief_display').setAttribute(
131 urls.XUL_BIB_BRIEF + '?docid=' + g.docid
134 document.getElementById('brief_display').setAttribute('hidden','true');
137 /******************************************************************************************************/
138 /* Add stat cats to the panes_and_field_names.right_pane4 */
140 g.populate_stat_cats();
142 /******************************************************************************************************/
143 /* Backup copies :) */
145 g.original_copies = js2JSON( g.copies );
147 /******************************************************************************************************/
150 g.summarize( g.copies );
154 var err_msg = "!! This software has encountered an error. Please tell your friendly " +
155 "system administrator or software developer the following:\ncat/copy_editor.xul\n" + E + '\n';
156 try { g.error.sdump('D_ERROR',err_msg); } catch(E) { dump(err_msg); dump(js2JSON(E)); }
161 /******************************************************************************************************/
162 /* Retrieve Templates */
164 g.retrieve_templates = function() {
166 JSAN.use('util.widgets'); JSAN.use('util.functional');
168 var robj = g.network.simple_request('FM_AUS_RETRIEVE',[ses(),g.data.list.au[0].id()]);
169 if (typeof robj['staff_client.copy_editor.templates'] != 'undefined') {
170 g.templates = robj['staff_client.copy_editor.templates'];
172 util.widgets.remove_children('template_placeholder');
173 var list = util.functional.map_object_to_list( g.templates, function(obj,i) { return [i, i]; } );
175 g.template_menu = util.widgets.make_menulist( list );
176 g.template_menu.setAttribute('id','template_menu');
177 $('template_placeholder').appendChild(g.template_menu);
178 g.template_menu.addEventListener(
180 function() { g.copy_editor_prefs[ 'template_menu' ] = { 'value' : g.template_menu.value }; g.save_attributes(); },
184 g.error.standard_unexpected_error_alert('Error retrieving templates',E);
188 /******************************************************************************************************/
191 g.apply_template = function() {
193 var name = g.template_menu.value;
194 if (g.templates[ name ] != 'undefined') {
195 var template = g.templates[ name ];
196 for (var i in template) {
197 g.changed[ i ] = template[ i ];
198 switch( template[i].type ) {
200 g.apply(template[i].field,template[i].value);
203 if (g.stat_cat_seen[ template[i].field ]) g.apply_stat_cat(template[i].field,template[i].value);
206 g.apply_owning_lib(template[i].value);
210 g.summarize( g.copies );
214 g.error.standard_unexpected_error_alert('Error applying template',E);
218 /******************************************************************************************************/
219 /* Save as Template */
221 g.save_template = function() {
223 var name = window.prompt('Enter template name:','','Save As Template');
225 g.templates[name] = g.changed;
226 var robj = g.network.simple_request(
227 'FM_AUS_UPDATE',[ses(),g.data.list.au[0].id(), { 'staff_client.copy_editor.templates' : g.templates }]
229 if (typeof robj.ilsevent != 'undefined') {
232 alert('Template "' + name + '" saved.');
236 g.retrieve_templates();
238 g.error.standard_unexpected_error_alert('Error saving template',E);
244 g.error.standard_unexpected_error_alert('Error saving template',E);
248 /******************************************************************************************************/
249 /* Delete Template */
251 g.delete_template = function() {
253 var name = g.template_menu.value;
255 if (! window.confirm('Delete template "' + name + '"?') ) return;
256 delete(g.templates[name]);
257 var robj = g.network.simple_request(
258 'FM_AUS_UPDATE',[ses(),g.data.list.au[0].id(), { 'staff_client.copy_editor.templates' : g.templates }]
260 if (typeof robj.ilsevent != 'undefined') {
263 alert('Template "' + name + '" deleted.');
267 g.retrieve_templates();
269 g.error.standard_unexpected_error_alert('Error deleting template',E);
275 g.error.standard_unexpected_error_alert('Error deleting template',E);
279 /******************************************************************************************************/
280 /* Export Templates */
282 g.export_templates = function() {
284 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
285 JSAN.use('util.file'); var f = new util.file('');
286 f.export_file( { 'title' : 'Save Templates File As', 'data' : g.templates } );
288 g.error.standard_unexpected_error_alert('Error exporting templates',E);
292 /******************************************************************************************************/
293 /* Import Templates */
295 g.import_templates = function() {
297 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
298 JSAN.use('util.file'); var f = new util.file('');
299 var temp = f.import_file( { 'title' : 'Import Templates File' } );
301 for (var i in temp) {
303 if (g.templates[i]) {
305 var r = g.error.yns_alert(
306 'Replace the existing template with the imported template?\n' + g.error.pretty_print( js2JSON( temp[i] ) ),
307 'Template ' + i + ' already exists.','Yes','No',null,'Click here'
310 if (r == 0 /* Yes */) g.templates[i] = temp[i];
314 g.templates[i] = temp[i];
320 var r = g.error.yns_alert(
321 'Save all of these imported templates permanently to this account?',
322 'Final Warning', 'Yes', 'No', null, 'Click here'
325 if (r == 0 /* Yes */) {
326 var robj = g.network.simple_request(
327 'FM_AUS_UPDATE',[ses(),g.data.list.au[0].id(), { 'staff_client.copy_editor.templates' : g.templates }]
329 if (typeof robj.ilsevent != 'undefined') {
332 alert('All templates saved.');
336 g.retrieve_templates();
338 g.error.standard_unexpected_error_alert('Error saving templates',E);
344 util.widgets.remove_children('template_placeholder');
345 var list = util.functional.map_object_to_list( g.templates, function(obj,i) { return [i, i]; } );
346 g.template_menu = util.widgets.make_menulist( list );
347 $('template_placeholder').appendChild(g.template_menu);
348 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.");
353 g.error.standard_unexpected_error_alert('Error importing templates',E);
358 /******************************************************************************************************/
359 /* Restore backup copies */
361 g.reset = function() {
363 g.copies = JSON2js( g.original_copies );
364 g.summarize( g.copies );
368 /******************************************************************************************************/
369 /* Apply a value to a specific field on all the copies being edited */
371 g.apply = function(field,value) {
372 g.error.sdump('D_TRACE','applying field = <' + field + '> value = <' + value + '>\n');
373 if (value == '<HACK:KLUDGE:NULL>') value = null;
374 if (field == 'alert_message') { value = value.replace(/^\W+$/g,''); }
375 if (field == 'price' || field == 'deposit_amount') {
376 if (value == '') { value = null; } else { JSAN.use('util.money'); value = util.money.sanitize( value ); }
378 for (var i = 0; i < g.copies.length; i++) {
379 var copy = g.copies[i];
381 copy[field]( value ); copy.ischanged('1');
388 /******************************************************************************************************/
389 /* Apply a stat cat entry to all the copies being edited. An entry_id of < 0 signifies the stat cat is being removed. */
391 g.apply_stat_cat = function(sc_id,entry_id) {
392 g.error.sdump('D_TRACE','sc_id = ' + sc_id + ' entry_id = ' + entry_id + '\n');
393 for (var i = 0; i < g.copies.length; i++) {
394 var copy = g.copies[i];
397 var temp = copy.stat_cat_entries();
398 if (!temp) temp = [];
399 temp = util.functional.filter_list(
402 return (obj.stat_cat() != sc_id);
405 if (entry_id > -1) temp.push(
406 util.functional.find_id_object_in_list(
407 g.data.hash.asc[sc_id].entries(),
411 copy.stat_cat_entries( temp );
414 g.error.standard_unexpected_error_alert('apply_stat_cat',E);
419 /******************************************************************************************************/
420 /* Apply an "owning lib" to all the copies being edited. That is, change and auto-vivicating volumes */
422 g.apply_owning_lib = function(ou_id) {
423 g.error.sdump('D_TRACE','ou_id = ' + ou_id + '\n');
424 for (var i = 0; i < g.copies.length; i++) {
425 var copy = g.copies[i];
427 if (!g.map_acn[copy.call_number()]) {
428 var volume = g.network.simple_request('FM_ACN_RETRIEVE',[ copy.call_number() ]);
429 if (typeof volume.ilsevent != 'undefined') {
430 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);
433 g.map_acn[copy.call_number()] = volume;
435 var old_volume = g.map_acn[copy.call_number()];
436 var acn_id = g.network.simple_request(
437 'FM_ACN_FIND_OR_CREATE',
438 [ses(),old_volume.label(),old_volume.record(),ou_id]
440 if (typeof acn_id.ilsevent != 'undefined') {
441 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);
444 copy.call_number(acn_id);
447 g.error.standard_unexpected_error_alert('apply_stat_cat',E);
452 /******************************************************************************************************/
453 /* This returns true if none of the copies being edited are pre-cats */
455 g.safe_to_change_owning_lib = function() {
458 for (var i = 0; i < g.copies.length; i++) {
459 var cn = g.copies[i].call_number();
460 if (typeof cn == 'object') { cn = cn.id(); }
461 if (cn == -1) { safe = false; }
465 g.error.standard_unexpected_error_alert('safe_to_change_owning_lib?',E);
470 /******************************************************************************************************/
471 /* This returns true if none of the copies being edited have a magical status found in my_constants.magical_statuses */
473 g.safe_to_edit_copy_status = function() {
476 for (var i = 0; i < g.copies.length; i++) {
477 var status = g.copies[i].status(); if (typeof status == 'object') status = status.id();
478 if (typeof my_constants.magical_statuses[ status ] != 'undefined') safe = false;
482 g.error.standard_unexpected_error_alert('safe_to_edit_copy_status?',E);
487 /******************************************************************************************************/
488 /* This concats and uniques all the alert messages for use as the default value for a new alert message */
490 g.populate_alert_message_input = function(tb) {
492 var seen = {}; var s = '';
493 for (var i = 0; i < g.copies.length; i++) {
494 var msg = g.copies[i].alert_message();
496 if (typeof seen[msg] == 'undefined') {
502 tb.setAttribute('value',s);
504 g.error.standard_unexpected_error_alert('populate_alert_message_input',E);
508 /***************************************************************************************************************/
509 /* This returns a list of acpl's appropriate for the copies being edited (and caches them in the global stash) */
511 g.get_acpl_list_for_lib = function(lib_id,but_only_these) {
512 g.data.stash_retrieve();
513 var label = 'acpl_list_for_lib_'+lib_id;
514 if (typeof g.data[label] == 'undefined') {
515 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
516 if (typeof robj.ilsevent != 'undefined') throw(robj);
518 for (var j = 0; j < robj.length; j++) {
519 var my_acpl = robj[j];
520 if (typeof g.data.hash.acpl[ my_acpl.id() ] == 'undefined') {
521 g.data.hash.acpl[ my_acpl.id() ] = my_acpl;
522 g.data.list.acpl.push( my_acpl );
524 var only_this_lib = my_acpl.owning_lib(); if (!only_this_lib) continue;
525 if (typeof only_this_lib == 'object') only_this_lib = only_this_lib.id();
526 if (but_only_these.indexOf( String( only_this_lib ) ) != -1) { // This filters out some of the libraries (usually the descendants)
527 temp_list.push( my_acpl );
530 g.data[label] = temp_list; g.data.stash(label,'hash','list');
532 return g.data[label];
535 /******************************************************************************************************/
536 /* This returns a list of acpl's appropriate for the copies being edited */
538 g.get_acpl_list = function() {
541 JSAN.use('util.functional');
545 /**************************************/
546 /* get owning libs from call numbers */
548 var owning_libs = {};
549 for (var i = 0; i < g.copies.length; i++) {
550 var callnumber = g.copies[i].call_number();
551 if (!callnumber) continue;
552 var cn_id = typeof callnumber == 'object' ? callnumber.id() : callnumber;
554 if (! g.map_acn[ cn_id ]) {
555 g.map_acn[ cn_id ] = g.network.simple_request('FM_ACN_RETRIEVE',[ cn_id ]);
557 var consider_lib = g.map_acn[ cn_id ].owning_lib();
558 if (!consider_lib) continue;
559 owning_libs[ typeof consider_lib == 'object' ? consider_lib.id() : consider_lib ] = true;
563 for (var i in g.callnumbers) {
564 var consider_lib = g.callnumbers[i].owning_lib;
565 if (!consider_lib) continue;
566 owning_libs[ typeof consider_lib == 'object' ? consider_lib.id() : consider_lib ] = true;
570 /***************************************************************************************************/
571 /* now find the first ancestor they all have in common, get the acpl's for it and higher ancestors */
573 JSAN.use('util.fm_utils');
574 var libs = []; for (var i in owning_libs) libs.push(i);
575 if (libs.length > 1) {
576 var ancestor = util.fm_utils.find_common_aou_ancestor( libs );
577 if (typeof ancestor == 'object' && ancestor != null) ancestor = ancestor.id();
580 var ancestors = util.fm_utils.find_common_aou_ancestors( libs );
581 var acpl_list = g.get_acpl_list_for_lib(ancestor, ancestors);
582 if (acpl_list) for (var i = 0; i < acpl_list.length; i++) {
583 if (acpl_list[i] != null) {
584 my_acpls[ typeof acpl_list[i] == 'object' ? acpl_list[i].id() : acpl_list[i] ] = true;
595 for (var i = 0; i < g.copies.length; i++) {
596 var consider_lib = g.copies[i].circ_lib();
597 if (!consider_lib) continue;
598 circ_libs[ typeof consider_lib == 'object' ? consider_lib.id() : consider_lib ] = true;
601 /***************************************************************************************************/
602 /* now find the first ancestor they all have in common, get the acpl's for it and higher ancestors */
604 libs = []; for (var i in circ_libs) libs.push(i);
605 if (libs.length > 0) {
606 var ancestor = util.fm_utils.find_common_aou_ancestor( libs );
607 if (typeof ancestor == 'object' && ancestor != null) ancestor = ancestor.id();
610 var ancestors = util.fm_utils.find_common_aou_ancestors( libs );
611 var acpl_list = g.get_acpl_list_for_lib(ancestor, ancestors);
612 if (acpl_list) for (var i = 0; i < acpl_list.length; i++) {
613 if (acpl_list[i] != null) {
614 my_acpls[ typeof acpl_list[i] == 'object' ? acpl_list[i].id() : acpl_list[i] ] = true;
620 var acpl_list = []; for (var i in my_acpls) acpl_list.push( g.data.hash.acpl[ i ] );
621 return acpl_list.sort(
623 var label_a = g.data.hash.aou[ a.owning_lib() ].shortname() + ' : ' + a.name();
624 var label_b = g.data.hash.aou[ b.owning_lib() ].shortname() + ' : ' + b.name();
625 if (label_a < label_b) return -1;
626 if (label_a > label_b) return 1;
632 g.error.standard_unexpected_error_alert('get_acpl_list',E);
638 /******************************************************************************************************/
639 /* This keeps track of what fields have been edited for styling purposes */
643 /******************************************************************************************************/
644 /* These need data from the middle layer to render */
646 g.special_exception = {
647 'Owning Lib : Call Number' : function(label,value) {
648 JSAN.use('util.widgets');
649 if (value>0) { /* an existing call number */
651 api.FM_ACN_RETRIEVE.app,
652 api.FM_ACN_RETRIEVE.method,
655 var cn = '??? id = ' + value;
657 cn = req.getResultObject();
659 g.error.sdump('D_ERROR','callnumber retrieve: ' + E);
661 util.widgets.set_text(label,g.data.hash.aou[ cn.owning_lib() ].shortname() + ' : ' + cn.label());
664 } else { /* a yet to be created call number */
666 util.widgets.set_text(label,g.data.hash.aou[ g.callnumbers[value].owning_lib ].shortname() + ' : ' + g.callnumbers[value].label);
670 'Creator' : function(label,value) {
671 if (value == null || value == '' || value == 'null') return;
672 g.network.simple_request(
673 'FM_AU_RETRIEVE_VIA_ID',
676 var p = '??? id = ' + value;
678 p = req.getResultObject();
682 g.error.sdump('D_ERROR','patron retrieve: ' + E);
684 JSAN.use('util.widgets');
685 util.widgets.set_text(label,p);
689 'Last Editor' : function(label,value) {
690 if (value == null || value == '' || value == 'null') return;
691 g.network.simple_request(
692 'FM_AU_RETRIEVE_VIA_ID',
695 var p = '??? id = ' + value;
697 p = req.getResultObject();
701 g.error.sdump('D_ERROR','patron retrieve: ' + E);
703 util.widgets.set_text(label,p);
710 /******************************************************************************************************/
711 g.readonly_stat_cat_names = [];
712 g.editable_stat_cat_names = [];
714 /******************************************************************************************************/
715 /* These get show in the left panel */
717 function init_panes() {
718 g.panes_and_field_names = {
725 render: 'fm.barcode();',
731 render: 'util.date.formatted_date( fm.create_date(), "%F");',
737 render: 'fm.creator();',
743 render: 'util.date.formatted_date( fm.edit_date(), "%F");',
749 render: 'fm.editor();',
760 render: 'typeof fm.location() == "object" ? fm.location().name() : g.data.lookup("acpl",fm.location()).name()',
761 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);',
766 "Circulation Library",
768 render: 'typeof fm.circ_lib() == "object" ? fm.circ_lib().shortname() : g.data.hash.aou[ fm.circ_lib() ].shortname()',
769 //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);',
770 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);',
774 "Owning Lib : Call Number",
776 render: 'fm.call_number();',
777 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,
783 render: 'fm.copy_number() == null ? "<Unset>" : fm.copy_number()',
784 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);',
796 render: 'fm.circulate() == null ? "<Unset>" : ( get_bool( fm.circulate() ) ? "Yes" : "No" )',
797 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);',
803 render: 'fm.holdable() == null ? "<Unset>" : ( get_bool( fm.holdable() ) ? "Yes" : "No" )',
804 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);',
810 render: 'fm.age_protect() == null ? "<Unset>" : ( typeof fm.age_protect() == "object" ? fm.age_protect().name() : g.data.hash.crahp[ fm.age_protect() ].name() )',
811 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);',
818 render: 'switch(fm.loan_duration()){ case 1: case "1": "Short"; break; case 2: case "2": "Normal"; break; case 3:case "3": "Long"; break; }',
819 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);',
826 render: 'switch(fm.fine_level()){ case 1: case "1": "Low"; break; case 2: case "2": "Normal"; break; case 3: case "3": "High"; break; }',
827 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);',
834 render: 'fm.circ_as_type() == null ? "<Unset>" : g.data.hash.citm[ fm.circ_as_type() ].value()',
835 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);',
839 "Circulation Modifier",
841 render: 'fm.circ_modifier() == null ? "<Unset>" : fm.circ_modifier()',
842 /*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);',*/
843 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);',
852 render: 'fm.alert_message() == null ? "<Unset>" : fm.alert_message()',
853 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);',
860 render: 'fm.deposit() == null ? "<Unset>" : ( get_bool( fm.deposit() ) ? "Yes" : "No" )',
861 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);',
867 render: 'if (fm.deposit_amount() == null) { "<Unset>"; } else { util.money.sanitize( fm.deposit_amount() ); }',
868 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);',
874 render: 'if (fm.price() == null) { "<Unset>"; } else { util.money.sanitize( fm.price() ); }',
875 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);',
882 render: 'fm.opac_visible() == null ? "<Unset>" : ( get_bool( fm.opac_visible() ) ? "Yes" : "No" )',
883 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);',
889 render: 'fm.ref() == null ? "<Unset>" : ( get_bool( fm.ref() ) ? "Yes" : "No" )',
890 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);',
902 /******************************************************************************************************/
903 /* This loops through all our fieldnames and all the copies, tallying up counts for the different values */
905 g.summarize = function( copies ) {
906 /******************************************************************************************************/
909 JSAN.use('util.date'); JSAN.use('util.money');
912 for (var i in g.panes_and_field_names) {
913 g.field_names = g.field_names.concat( g.panes_and_field_names[i] );
915 g.field_names = g.field_names.concat( g.editable_stat_cat_names );
916 g.field_names = g.field_names.concat( g.readonly_stat_cat_names );
918 /******************************************************************************************************/
919 /* Loop through the field names */
921 for (var i = 0; i < g.field_names.length; i++) {
923 var field_name = g.field_names[i][0];
924 var render = g.field_names[i][1].render;
925 var attr = g.field_names[i][1].attr;
926 g.summary[ field_name ] = {};
928 /******************************************************************************************************/
929 /* Loop through the copies */
931 for (var j = 0; j < copies.length; j++) {
934 var cmd = render || ('fm.' + field_name + '();');
937 /**********************************************************************************************/
938 /* Try to retrieve the value for this field for this copy */
943 g.error.sdump('D_ERROR','Attempted ' + cmd + '\n' + E + '\n');
945 if (typeof value == 'object' && value != null) {
946 alert('FIXME: field_name = <' + field_name + '> value = <' + js2JSON(value) + '>\n');
949 /**********************************************************************************************/
950 /* Tally the count */
952 if (g.summary[ field_name ][ value ]) {
953 g.summary[ field_name ][ value ]++;
955 g.summary[ field_name ][ value ] = 1;
959 g.error.sdump('D_TRACE','summary = ' + js2JSON(g.summary) + '\n');
962 /******************************************************************************************************/
963 /* Display the summarized data and inputs for editing */
965 g.render = function() {
967 /******************************************************************************************************/
968 /* Library setup and clear any existing interface */
970 JSAN.use('util.widgets'); JSAN.use('util.date'); JSAN.use('util.money'); JSAN.use('util.functional');
972 for (var i in g.panes_and_field_names) {
973 var p = document.getElementById(i);
974 if (p) util.widgets.remove_children(p);
977 /******************************************************************************************************/
978 /* Populate the library filter menu for stat cats */
981 for (var i = 0; i < g.panes_and_field_names.right_pane4.length; i++) {
982 sc_libs[ g.panes_and_field_names.right_pane4[i][1].attr.sc_lib ] = true;
985 for (var i in sc_libs) { sc_libs2.push( [ g.data.hash.aou[ i ].shortname(), i ] ); }
987 var x = document.getElementById("stat_cat_lib_filter_menu").firstChild;
988 JSAN.use('util.widgets'); util.widgets.remove_children(x);
989 for (var i = 0; i < sc_libs2.length; i++) {
990 var menuitem = document.createElement('menuitem');
991 menuitem.setAttribute('id','filter_'+sc_libs2[i][1]);
992 menuitem.setAttribute('type','checkbox');
993 menuitem.setAttribute('checked','true');
994 menuitem.setAttribute('label',sc_libs2[i][0]);
995 menuitem.setAttribute('value',sc_libs2[i][1]);
996 menuitem.setAttribute('oncommand','try{g.toggle_stat_cat_display(this);}catch(E){alert(E);}');
997 x.appendChild(menuitem);
1000 /******************************************************************************************************/
1001 /* Prepare the panes */
1003 var groupbox; var caption; var vbox; var grid; var rows;
1005 /******************************************************************************************************/
1006 /* Loop through the field names */
1008 for (h in g.panes_and_field_names) {
1009 if (!document.getElementById(h)) continue;
1010 for (var i = 0; i < g.panes_and_field_names[h].length; i++) {
1012 var f = g.panes_and_field_names[h][i]; var fn = f[0]; var attr = f[1].attr;
1013 groupbox = document.createElement('groupbox'); document.getElementById(h).appendChild(groupbox);
1015 for (var a in attr) {
1016 groupbox.setAttribute(a,attr[a]);
1019 if (typeof g.changed[fn] != 'undefined') groupbox.setAttribute('class','copy_editor_field_changed');
1020 caption = document.createElement('caption'); groupbox.appendChild(caption);
1021 caption.setAttribute('label',fn); caption.setAttribute('id','caption_'+fn);
1022 vbox = document.createElement('vbox'); groupbox.appendChild(vbox);
1023 grid = util.widgets.make_grid( [ { 'flex' : 1 }, {}, {} ] ); vbox.appendChild(grid);
1024 grid.setAttribute('flex','1');
1025 rows = grid.lastChild;
1028 /**************************************************************************************/
1029 /* Loop through each value for the field */
1031 for (var j in g.summary[fn]) {
1032 var value = j; var count = g.summary[fn][j];
1033 row = document.createElement('row'); rows.appendChild(row);
1034 var label1 = document.createElement('description'); row.appendChild(label1);
1035 if (g.special_exception[ fn ]) {
1036 g.special_exception[ fn ]( label1, value );
1038 label1.appendChild( document.createTextNode(value) );
1040 var label2 = document.createElement('description'); row.appendChild(label2);
1041 var unit = count == 1 ? 'copy' : 'copies';
1042 label2.appendChild( document.createTextNode(count + ' ' + unit) );
1044 var hbox = document.createElement('hbox');
1045 hbox.setAttribute('id',fn);
1046 groupbox.appendChild(hbox);
1047 var hbox2 = document.createElement('hbox');
1048 groupbox.appendChild(hbox2);
1050 /**************************************************************************************/
1051 /* Render the input widget */
1053 if (f[1].input && g.edit) {
1054 g.render_input(hbox,f[1]);
1058 g.error.sdump('D_ERROR','copy editor: ' + E + '\n');
1064 /******************************************************************************************************/
1065 /* Synchronize stat cat visibility with library filter menu, and default template selection */
1066 JSAN.use('util.file');
1067 var file = new util.file('copy_editor_prefs.'+g.data.server_unadorned);
1068 g.copy_editor_prefs = util.widgets.load_attributes(file);
1069 for (var i in g.copy_editor_prefs) {
1070 if (i.match(/filter_/) && g.copy_editor_prefs[i].checked == '') {
1072 g.toggle_stat_cat_display( document.getElementById(i) );
1073 } catch(E) { alert(E); }
1076 if (g.template_menu) g.template_menu.value = g.template_menu.getAttribute('value');
1080 /******************************************************************************************************/
1081 /* This actually draws the change button and input widget for a given field */
1082 g.render_input = function(node,blob) {
1084 // node = hbox ; groupbox -> hbox, hbox
1086 var groupbox = node.parentNode;
1087 var caption = groupbox.firstChild;
1088 var vbox = node.previousSibling;
1090 var hbox2 = node.nextSibling;
1092 var input_cmd = blob.input;
1093 var render_cmd = blob.render;
1094 var attr = blob.attr;
1096 var block = false; var first = true;
1098 function on_mouseover(ev) {
1099 groupbox.setAttribute('style','background: white');
1102 function on_mouseout(ev) {
1103 groupbox.setAttribute('style','');
1106 vbox.addEventListener('mouseover',on_mouseover,false);
1107 vbox.addEventListener('mouseout',on_mouseout,false);
1108 groupbox.addEventListener('mouseover',on_mouseover,false);
1109 groupbox.addEventListener('mouseout',on_mouseout,false);
1110 groupbox.firstChild.addEventListener('mouseover',on_mouseover,false);
1111 groupbox.firstChild.addEventListener('mouseout',on_mouseout,false);
1113 function on_click(ev){
1115 if (block) return; block = true;
1117 function post_c(v) {
1119 /* FIXME - kludgy */
1120 var t = input_cmd.match('apply_stat_cat') ? 'stat_cat' : ( input_cmd.match('apply_owning_lib') ? 'owning_lib' : 'attribute' );
1124 f = input_cmd.match(/apply\("(.+?)",/)[1];
1127 f = input_cmd.match(/apply_stat_cat\((.+?),/)[1];
1133 g.changed[ hbox.id ] = { 'type' : t, 'field' : f, 'value' : v };
1137 g.summarize( g.copies );
1139 document.getElementById(caption.id).focus();
1143 g.error.standard_unexpected_error_alert('post_c',E);
1146 var x; var c; eval( input_cmd );
1148 util.widgets.remove_children(vbox);
1149 util.widgets.remove_children(hbox);
1150 util.widgets.remove_children(hbox2);
1151 hbox.appendChild(x);
1152 var apply = document.createElement('button');
1153 apply.setAttribute('label','Apply');
1154 apply.setAttribute('accesskey','A');
1155 hbox2.appendChild(apply);
1156 apply.addEventListener('command',function() { c(x.value); },false);
1157 var cancel = document.createElement('button');
1158 cancel.setAttribute('label','Cancel');
1159 cancel.addEventListener('command',function() { setTimeout( function() { g.summarize( g.copies ); g.render(); document.getElementById(caption.id).focus(); }, 0); }, false);
1160 hbox2.appendChild(cancel);
1161 setTimeout( function() { x.focus(); }, 0 );
1164 g.error.standard_unexpected_error_alert('render_input',E);
1167 vbox.addEventListener('click',on_click, false);
1168 hbox.addEventListener('click',on_click, false);
1169 caption.addEventListener('click',on_click, false);
1170 caption.addEventListener('keypress',function(ev) {
1171 if (ev.keyCode == 13 /* enter */ || ev.keyCode == 77 /* mac enter */) on_click();
1173 caption.setAttribute('style','-moz-user-focus: normal');
1174 caption.setAttribute('onfocus','this.setAttribute("class","outline_me")');
1175 caption.setAttribute('onblur','this.setAttribute("class","")');
1178 g.error.sdump('D_ERROR',E + '\n');
1182 /******************************************************************************************************/
1183 /* store the copies in the global xpcom stash */
1185 g.stash_and_close = function() {
1187 if (g.handle_update) {
1189 var r = g.network.request(
1190 api.FM_ACP_FLESHED_BATCH_UPDATE.app,
1191 api.FM_ACP_FLESHED_BATCH_UPDATE.method,
1192 [ ses(), g.copies, true ]
1194 if (typeof r.ilsevent != 'undefined') {
1195 g.error.standard_unexpected_error_alert('copy update',r);
1197 alert('Items added/modified.');
1199 /* FIXME -- revisit the return value here */
1201 alert('copy update error: ' + js2JSON(E));
1204 //g.data.temp_copies = js2JSON( g.copies );
1205 //g.data.stash('temp_copies');
1206 xulG.copies = g.copies;
1207 update_modal_xulG(xulG);
1210 g.error.standard_unexpected_error_alert('stash and close',E);
1214 /******************************************************************************************************/
1215 /* spawn copy notes interface */
1217 g.copy_notes = function() {
1218 JSAN.use('util.window'); var win = new util.window();
1220 urls.XUL_COPY_NOTES,
1221 //+ '?copy_id=' + window.escape(g.copies[0].id()),
1222 'Copy Notes','chrome,resizable,modal',
1223 { 'copy_id' : g.copies[0].id() }
1227 /******************************************************************************************************/
1228 /* hides or unhides stat cats based on library stat cat filter menu */
1229 g.toggle_stat_cat_display = function(el) {
1231 var visible = el.getAttribute('checked');
1232 var nl = document.getElementsByAttribute('sc_lib',el.getAttribute('value'));
1233 for (var n = 0; n < nl.length; n++) {
1235 nl[n].setAttribute('hidden','false');
1237 nl[n].setAttribute('hidden','true');
1240 g.copy_editor_prefs[ el.getAttribute('id') ] = { 'checked' : visible };
1241 g.save_attributes();
1244 /******************************************************************************************************/
1245 /* This adds a stat cat definition to the stat cat pane for rendering */
1246 g.save_attributes = function() {
1247 JSAN.use('util.widgets'); JSAN.use('util.file'); var file = new util.file('copy_editor_prefs.'+g.data.server_unadorned);
1248 var what_to_save = {};
1249 for (var i in g.copy_editor_prefs) {
1250 what_to_save[i] = [];
1251 for (var j in g.copy_editor_prefs[i]) what_to_save[i].push(j);
1253 util.widgets.save_attributes(file, what_to_save );
1256 /******************************************************************************************************/
1257 /* This adds a stat cat definition to the stat cat pane for rendering */
1258 g.add_stat_cat = function(sc) {
1260 if (typeof g.data.hash.asc == 'undefined') { g.data.hash.asc = {}; g.data.stash('hash'); }
1264 if (typeof sc == 'object') {
1269 if (typeof g.stat_cat_seen[sc_id] != 'undefined') { return; }
1271 g.stat_cat_seen[ sc_id ] = 1;
1273 if (typeof sc != 'object') {
1275 sc = g.network.simple_request(
1276 'FM_ASC_BATCH_RETRIEVE',
1277 [ ses(), [ sc_id ] ]
1282 g.data.hash.asc[ sc.id() ] = sc; g.data.stash('hash');
1284 var label_name = g.data.hash.aou[ sc.owner() ].shortname() + " : " + sc.name();
1289 render: 'var l = util.functional.find_list( fm.stat_cat_entries(), function(e){ return e.stat_cat() == '
1290 + sc.id() + '; } ); l ? l.value() : "<Unset>";',
1291 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()
1292 + '].entries(), function(obj){ return [ obj.value(), obj.id() ]; } ) ).sort() ); '
1293 + 'x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c),false);',
1300 g.panes_and_field_names.right_pane4.push( temp_array );
1302 g.error.standard_unexpected_error_alert('Error adding stat cat to display definition',E);
1306 /******************************************************************************************************/
1307 /* Add stat cats to the panes_and_field_names.right_pane4 */
1308 g.populate_stat_cats = function() {
1310 g.data.stash_retrieve();
1311 g.stat_cat_seen = {};
1313 function get(lib_id,only_these) {
1314 g.data.stash_retrieve();
1315 var label = 'asc_list_for_lib_'+lib_id;
1316 if (typeof g.data[label] == 'undefined') {
1317 var robj = g.network.simple_request('FM_ASC_RETRIEVE_VIA_AOU', [ ses(), lib_id ]);
1318 if (typeof robj.ilsevent != 'undefined') throw(robj);
1320 for (var j = 0; j < robj.length; j++) {
1321 var my_asc = robj[j];
1322 if (typeof g.data.hash.asc == 'undefined') { g.data.hash.asc = {}; }
1323 if (typeof g.data.hash.asc[ my_asc.id() ] == 'undefined') {
1324 g.data.hash.asc[ my_asc.id() ] = my_asc;
1326 var only_this_lib = my_asc.owner(); if (typeof only_this_lib == 'object') only_this_lib = only_this_lib.id();
1327 if (only_these.indexOf( String( only_this_lib ) ) != -1) {
1328 temp_list.push( my_asc );
1331 g.data[label] = temp_list; g.data.stash(label,'hash','list');
1333 return g.data[label];
1336 /* The stat cats for the pertinent library -- this is based on workstation ou */
1337 var label = 'asc_list_for_' + typeof g.data.ws_ou == 'object' ? g.data.ws_ou.id() : g.data.ws_ou;
1338 g.data[ label ] = g.data.list.my_asc; g.data.stash('label');
1339 for (var i = 0; i < g.data.list.my_asc.length; i++) {
1340 g.add_stat_cat( g.data.list.my_asc[i] );
1343 /* For the others, we want to consider the owning libs, circ libs, and any libs that have stat cats already on the copies,
1344 however, if batch editing, we only want to show the ones they have in common. So let's compile the libs */
1346 function add_common_ancestors(sc_libs) {
1347 JSAN.use('util.fm_utils');
1348 var libs = []; for (var i in sc_libs) libs.push(i);
1349 var ancestor = util.fm_utils.find_common_aou_ancestor( libs );
1350 if (typeof ancestor == 'object' && ancestor != null) ancestor = ancestor.id();
1352 var ancestors = util.fm_utils.find_common_aou_ancestors( libs );
1353 var asc_list = get(ancestor, ancestors);
1354 for (var i = 0; i < asc_list.length; i++) {
1355 g.add_stat_cat( asc_list[i] );
1360 /* stat cats based on stat cat entries present on these copies */
1362 for (var i = 0; i < g.copies.length; i++) {
1363 var entries = g.copies[i].stat_cat_entries();
1364 if (!entries) entries = [];
1365 for (var j = 0; j < entries.length; j++) {
1366 var lib = entries[j].owner(); if (typeof lib == 'object') lib = lib.id();
1367 sc_libs[ lib ] = true;
1370 add_common_ancestors(sc_libs); // CAVEAT - if a copy has no stat_cat_entries, it basically gets no vote here
1372 /* stat cats based on Circ Lib */
1374 for (var i = 0; i < g.copies.length; i++) {
1375 var circ_lib = g.copies[i].circ_lib(); if (typeof circ_lib == 'object') circ_lib = circ_lib.id();
1376 sc_libs[ circ_lib ] = true;
1378 add_common_ancestors(sc_libs);
1380 /* stat cats based on Owning Lib */
1382 for (var i = 0; i < g.copies.length; i++) {
1383 var cn_id = g.copies[i].call_number();
1385 if (! g.map_acn[ cn_id ]) {
1386 g.map_acn[ cn_id ] = g.network.simple_request('FM_ACN_RETRIEVE',[ cn_id ]);
1388 var owning_lib = g.map_acn[ cn_id ].owning_lib(); if (typeof owning_lib == 'object') owning_lib = owning_lib.id();
1389 sc_libs[ owning_lib ] = true;
1392 add_common_ancestors(sc_libs); // CAVEAT - if a copy is a pre-cat, it basically gets no vote here
1394 g.panes_and_field_names.right_pane4.sort();
1397 g.error.standard_unexpected_error_alert('Error populating stat cats for display',E);