some cleanup to make stat cat and meat grinder additions easier
authorphasefx <phasefx@dcc99617-32d9-48b4-a31d-7c20da2025e4>
Mon, 6 Feb 2006 19:11:11 +0000 (19:11 +0000)
committerphasefx <phasefx@dcc99617-32d9-48b4-a31d-7c20da2025e4>
Mon, 6 Feb 2006 19:11:11 +0000 (19:11 +0000)
git-svn-id: svn://svn.open-ils.org/ILS/trunk@2982 dcc99617-32d9-48b4-a31d-7c20da2025e4

Open-ILS/xul/staff_client/server/cat/copy_editor.js [new file with mode: 0644]
Open-ILS/xul/staff_client/server/cat/copy_editor.xul

diff --git a/Open-ILS/xul/staff_client/server/cat/copy_editor.js b/Open-ILS/xul/staff_client/server/cat/copy_editor.js
new file mode 100644 (file)
index 0000000..c6aa6f3
--- /dev/null
@@ -0,0 +1,591 @@
+var g = {};
+
+function my_init() {
+       try {
+               /******************************************************************************************************/
+               /* setup JSAN and some initial libraries */
+
+               netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+               if (typeof JSAN == 'undefined') { throw( "The JSAN library object is missing."); }
+               JSAN.errorLevel = "die"; // none, warn, or die
+               JSAN.addRepository('/xul/server/');
+               JSAN.use('util.error'); g.error = new util.error();
+               g.error.sdump('D_TRACE','my_init() for cat/copy_editor.xul');
+
+               JSAN.use('OpenILS.data'); g.data = new OpenILS.data(); g.data.init({'via':'stash'});
+               JSAN.use('util.network'); g.network = new util.network();
+
+               g.cgi = new CGI();
+
+               g.session = g.cgi.param('session') || g.cgi.param('ses');
+
+               /******************************************************************************************************/
+               /* Get the copy ids from various sources */
+
+               var copy_ids = [];
+               if (g.cgi.param('copy_ids')) copy_ids = JSON2js( g.cgi.param('copy_ids') );
+               if (!copy_ids) copy_ids = [];
+               if (window.xulG && window.xulG.copy_ids) copy_ids = copy_ids.concat( window.xulG.copy_ids );
+
+               if (copy_ids.length > 0) g.copies = g.network.request(
+                       api.FM_ACP_FLESHED_BATCH_RETRIEVE.app,
+                       api.FM_ACP_FLESHED_BATCH_RETRIEVE.method,
+                       [ copy_ids ]
+               );
+
+               /******************************************************************************************************/
+               /* And fleshed copies if any */
+
+               if (!g.copies) g.copies = [];
+               if (window.xulG && window.xulG.copies) g.copies = g.copies.concat( window.xulG.copies );
+               if (g.cgi.param('copies')) g.copies = g.copies.concat( JSON2js( g.cgi.param('copies') ) );
+
+               /******************************************************************************************************/
+               /* We try to retrieve callnumbers for existing copies, but for new copies, we rely on this */
+
+               if (window.xulG && window.xulG.callnumbers) g.callnumbers = window.xulG.callnumbers;
+               if (g.cgi.param('callnumbers')) g.callnumbers =  JSON2js( g.cgi.param('callnumbers') );
+
+               /******************************************************************************************************/
+               /* Is the interface an editor or a viewer? */
+
+               if (g.cgi.param('edit') == '1') { 
+                       g.edit = true;
+                       document.getElementById('caption').setAttribute('label','Copy Editor'); 
+                       document.getElementById('nav').setAttribute('hidden','false'); 
+               }
+
+               /******************************************************************************************************/
+               /* Do it */
+
+               g.summarize( g.copies );
+               g.render();
+
+       } catch(E) {
+               var err_msg = "!! This software has encountered an error.  Please tell your friendly " +
+                       "system administrator or software developer the following:\ncat/copy_editor.xul\n" + E + '\n';
+               try { g.error.sdump('D_ERROR',err_msg); } catch(E) { dump(err_msg); dump(js2JSON(E)); }
+               alert(err_msg);
+       }
+}
+
+/******************************************************************************************************/
+/* Apply a value to a specific field on all the copies being edited */
+
+g.apply = function(field,value) {
+       g.error.sdump('D_TRACE','field = ' + field + '  value = ' + value + '\n');
+       for (var i = 0; i < g.copies.length; i++) {
+               var copy = g.copies[i];
+               try {
+                       copy[field]( value ); copy.ischanged('1');
+               } catch(E) {
+                       alert(E);
+               }
+       }
+}
+
+/******************************************************************************************************/
+/* These need data from the middle layer to render */
+
+g.special_exception = {
+       'Call Number' : function(label,value) {
+               if (value>0) { /* an existing call number */
+                       g.network.request(
+                               api.FM_ACN_RETRIEVE.app,
+                               api.FM_ACN_RETRIEVE.method,
+                               [ value ],
+                               function(req) {
+                                       var cn = '??? id = ' + value;
+                                       try {
+                                               cn = req.getResultObject().label();
+                                       } catch(E) {
+                                               g.error.sdump('D_ERROR','callnumber retrieve: ' + E);
+                                       }
+                                       label.setAttribute('value',cn);
+                               }
+                       );
+               } else { /* a yet to be created call number */
+                       if (g.callnumbers) {
+                               label.setAttribute('value',g.callnumbers[value]);
+                       }
+               }
+       },
+       'Creator' : function(label,value) {
+               g.network.request(
+                       api.FM_AU_RETRIEVE_VIA_ID.app,
+                       api.FM_AU_RETRIEVE_VIA_ID.method,
+                       [ g.session, value ],
+                       function(req) {
+                               var p = '??? id = ' + value;
+                               try {
+                                       p = req.getResultObject();
+                                       p = p.card().barcode() + ' : ' + p.family_name();
+
+                               } catch(E) {
+                                       g.error.sdump('D_ERROR','patron retrieve: ' + E);
+                               }
+                               label.setAttribute('value',p);
+                       }
+               );
+       },
+       'Last Editor' : function(label,value) {
+               g.network.request(
+                       api.FM_AU_RETRIEVE_VIA_ID.app,
+                       api.FM_AU_RETRIEVE_VIA_ID.method,
+                       [ g.session, value ],
+                       function(req) {
+                               var p = '??? id = ' + value;
+                               try {
+                                       p = req.getResultObject();
+                                       p = p.card().barcode() + ' : ' + p.family_name();
+
+                               } catch(E) {
+                                       g.error.sdump('D_ERROR','patron retrieve: ' + E);
+                               }
+                               label.setAttribute('value',p);
+                       }
+               );
+       }
+
+}
+
+/******************************************************************************************************/
+g.readonly_stat_cat_names = [];
+g.editable_stat_cat_names = [];
+
+/******************************************************************************************************/
+/* These get show in the left panel */
+
+g.left_pane_field_names = [
+       [
+               "Barcode",               
+               {
+                       render: 'fm.barcode();',
+               }
+       ], 
+       [
+               "Call Number",  
+               {
+                       render: 'fm.call_number();',
+               }
+       ],
+       [
+               "Creation Date",
+               { 
+                       render: 'util.date.formatted_date( fm.create_date(), "%F");',
+               }
+       ],
+       [
+               "Last Edit Date",
+               { 
+                       render: 'util.date.formatted_date( fm.edit_date(), "%F");',
+               }
+       ],
+
+];
+
+/******************************************************************************************************/
+/* These get shown in the right panel */
+
+g.right_pane_field_names = [
+       [
+               "Creator",
+               { 
+                       render: 'fm.creator();',
+               }
+       ],
+       [
+               "Last Editor",
+               {
+                       render: 'fm.editor();',
+               }
+       ],
+        [
+               "Circulate as Type",    
+               {       
+                       render: 'fm.circ_as_type();',
+                       input: 'x = document.createElement("textbox"); x.addEventListener("change",function(ev) { g.apply("circ_as_type",ev.target.value); }, false);',
+               } 
+       ],
+       [
+               "Circulation Library",          
+               {       
+                       render: 'fm.circ_lib().shortname();',
+                       input: '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("command",function(ev) { g.apply("circ_lib",ev.target.value); }, false);',
+               } 
+       ],
+       [
+               "Circulation Modifier",
+               {       
+                       render: 'fm.circ_modifier();',
+                       input: 'x = document.createElement("textbox"); x.addEventListener("change",function(ev) { g.apply("circ_modifier",ev.target.value); }, false);',
+               }
+       ],
+       [
+               "Circulate?",
+               {       
+                       render: 'fm.circulate() ? "Yes" : "No";',
+                       input: 'x = util.widgets.make_menulist( [ [ "Yes", "1" ], [ "No", "0" ] ] ); x.addEventListener("command",function(ev) { g.apply("circulate",ev.target.value); }, false);',
+               }
+       ],
+       [
+               "Copy Number",
+               { 
+                       render: 'fm.copy_number();',
+                       input: 'x = document.createElement("textbox"); x.addEventListener("change",function(ev) { g.apply("copy_number",ev.target.value); }, false);',
+               }
+       ],
+       [
+               "Deposit?",
+               { 
+                       render: 'fm.deposit() ? "Yes" : "No";',
+                       input: 'x = util.widgets.make_menulist( [ [ "Yes", "1" ], [ "No", "0" ] ] ); x.addEventListener("command",function(ev) { g.apply("deposit",ev.target.value); }, false);',
+               }
+       ],
+       [
+               "Deposit Amount",
+               { 
+                       render: 'util.money.sanitize( fm.deposit_amount() );',
+                       input: 'x = document.createElement("textbox"); x.addEventListener("change",function(ev) { g.apply("deposit_amount",ev.target.value); }, false);',
+               }
+       ],
+       [
+               "Fine Level",
+               {
+                       render: 'switch(fm.fine_level()){ case 1: "Low"; break; case 2: "Normal"; break; case 3: "High"; break; }',
+                       input: 'x = util.widgets.make_menulist( [ [ "Low", "1" ], [ "Normal", "2" ], [ "High", "3" ] ] ); x.addEventListener("command",function(ev) { g.apply("fine_level",ev.target.value); }, false);',
+               }
+       ],
+       [
+               "Holdable?",
+               { 
+                       render: 'fm.holdable() ? "Yes" : "No";', 
+                       input: 'x = util.widgets.make_menulist( [ [ "Yes", "1" ], [ "No", "0" ] ] ); x.addEventListener("command",function(ev) { g.apply("holdable",ev.target.value); }, false);',
+               }
+       ],
+       [
+               "Loan Duration",
+               { 
+                       render: 'switch(fm.loan_duration()){ case 1: "Short"; break; case 2: "Normal"; break; case 3: "Long"; break; }',
+                       input: 'x = util.widgets.make_menulist( [ [ "Short", "1" ], [ "Normal", "2" ], [ "Long", "3" ] ] ); x.addEventListener("command",function(ev) { g.apply("loan_duration",ev.target.value); }, false);',
+
+               }
+       ],
+       [
+               "Shelving Location",
+               { 
+                       render: 'fm.location().name();', 
+                       input: 'x = util.widgets.make_menulist( util.functional.map_list( g.data.list.acpl, function(obj) { return [ obj.name(), obj.id() ]; }).sort()); x.addEventListener("command",function(ev) { g.apply("location",ev.target.value); }, false);',
+
+               }
+       ],
+       [
+               "OPAC Visible?",
+               { 
+                       render: 'fm.opac_visible() ? "Yes" : "No";', 
+                       input: 'x = util.widgets.make_menulist( [ [ "Yes", "1" ], [ "No", "0" ] ] ); x.addEventListener("command",function(ev) { g.apply("opac_visible",ev.target.value); }, false);',
+               }
+       ],
+       [
+               "Price",
+               { 
+                       render: 'util.money.sanitize( fm.price() );', 
+                       input: 'x = document.createElement("textbox"); x.addEventListener("change",function(ev) { g.apply("deposit_amount",ev.target.value); }, false);',
+               }
+       ],
+       [
+               "Reference?",
+               { 
+                       render: 'fm.ref() ? "Yes" : "No";', 
+                       input: 'x = util.widgets.make_menulist( [ [ "Yes", "1" ], [ "No", "0" ] ] ); x.addEventListener("command",function(ev) { g.apply("ref",ev.target.value); }, false);',
+               }
+       ],
+       [
+               "Status",
+               { 
+                       render: 'fm.status().name();', 
+                       input: 'x = util.widgets.make_menulist( util.functional.map_list( g.data.list.ccs, function(obj) { return [ obj.name(), obj.id() ]; } ).sort() ); x.addEventListener("command",function(ev) { g.apply("status",ev.target.value); }, false);',
+
+
+               }
+       ],
+];
+
+/******************************************************************************************************/
+/* This loops through all our fieldnames and all the copies, tallying up counts for the different values */
+
+g.summarize = function( copies ) {
+       /******************************************************************************************************/
+       /* Setup */
+
+       JSAN.use('util.date'); JSAN.use('util.money');
+       g.summary = {};
+       g.field_names = g.left_pane_field_names;
+       g.field_names = g.field_names.concat( g.right_pane_field_names );
+       g.field_names = g.field_names.concat( g.editable_stat_cat_names );
+       g.field_names = g.field_names.concat( g.readonly_stat_cat_names );
+
+       /******************************************************************************************************/
+       /* Loop through the field names */
+
+       for (var i = 0; i < g.field_names.length; i++) {
+
+               var field_name = g.field_names[i][0];
+               var render = g.field_names[i][1].render;
+               g.summary[ field_name ] = {};
+
+               /******************************************************************************************************/
+               /* Loop through the copies */
+
+               for (var j = 0; j < copies.length; j++) {
+
+                       var fm = copies[j];
+                       var cmd = render || ('fm.' + field_name + '();');
+                       var value = '???';
+
+                       /**********************************************************************************************/
+                       /* Try to retrieve the value for this field for this copy */
+
+                       try { 
+                               value = eval( cmd ); 
+                       } catch(E) { 
+                               g.error.sdump('D_ERROR','Attempted ' + cmd + '\n' +  E + '\n'); 
+                       }
+                       if (typeof value == 'object' && value != null) {
+                               alert('FIXME: field_name = ' + field_name + '  value = ' + js2JSON(value) + '\n');
+                       }
+
+                       /**********************************************************************************************/
+                       /* Tally the count */
+
+                       if (g.summary[ field_name ][ value ]) {
+                               g.summary[ field_name ][ value ]++;
+                       } else {
+                               g.summary[ field_name ][ value ] = 1;
+                       }
+               }
+       }
+       g.error.sdump('D_ERROR','summary = ' + js2JSON(g.summary) + '\n');
+}
+
+/******************************************************************************************************/
+/* Display the summarized data and inputs for editing */
+
+g.render = function() {
+
+       /******************************************************************************************************/
+       /* Library setup and clear any existing interface */
+
+       JSAN.use('util.widgets'); JSAN.use('util.date'); JSAN.use('util.money'); JSAN.use('util.functional');
+
+       var cns = document.getElementById('call_number_summary');
+       util.widgets.remove_children( cns );
+       var bcs = document.getElementById('barcode_summary');
+       util.widgets.remove_children( bcs );
+       var rp = document.getElementById('right_pane');
+       util.widgets.remove_children( rp );
+
+       /******************************************************************************************************/
+       /* Make the call number summary */
+
+       var grid = util.widgets.make_grid( [ { 'flex' : '1' } ] );
+       cns.appendChild(grid);
+       for (var i in g.summary['Call Number']) {
+               var cn_id = i; var count = g.summary['Call Number'][i];
+               var row = document.createElement('row'); grid.lastChild.appendChild(row);
+               var cn_label = document.createElement('label'); row.appendChild(cn_label);
+               g.special_exception['Call Number']( cn_label, cn_id );
+               var count_label = document.createElement('label'); row.appendChild(count_label);
+               var unit = count == 1 ? 'copy' : 'copies';
+               count_label.setAttribute('value',count + ' ' + unit);
+       }
+
+       /******************************************************************************************************/
+       /* List the copy barcodes */
+
+       for (var i in g.summary['Barcode']) {
+               var bc = i;
+               var hbox = document.createElement('hbox'); bcs.appendChild(hbox);
+               var bc_label = document.createElement('label'); hbox.appendChild(bc_label);
+               bc_label.setAttribute('value',bc);
+       }
+
+       /******************************************************************************************************/
+       /* List the other non-editable fields in this pane */
+
+       var groupbox; var caption; var vbox; var grid; var rows;
+       for (var i = 0; i < g.left_pane_field_names.length; i++) {
+               try {
+                       var f = g.left_pane_field_names[i]; var fn = f[0];
+                       if (fn == 'Call Number' || fn == 'Barcode') continue;
+                       groupbox = document.createElement('groupbox'); bcs.parentNode.parentNode.appendChild(groupbox);
+                       caption = document.createElement('caption'); groupbox.appendChild(caption);
+                       caption.setAttribute('label',fn);
+                       vbox = document.createElement('vbox'); groupbox.appendChild(vbox);
+                       grid = util.widgets.make_grid( [ { 'flex' : 1 }, {}, {} ] ); vbox.appendChild(grid);
+                       grid.setAttribute('flex','1');
+                       rows = grid.lastChild;
+                       var row;
+                       
+                       /**************************************************************************************/
+                       /* Loop through each value for the field */
+
+                       for (var j in g.summary[fn]) {
+                               var value = j; var count = g.summary[fn][j];
+                               row = document.createElement('row'); rows.appendChild(row);
+                               var label1 = document.createElement('label'); row.appendChild(label1);
+                               if (g.special_exception[ fn ]) {
+                                       g.special_exception[ fn ]( label1, value );
+                               } else {
+                                       label1.setAttribute('value',value);
+                               }
+                               var label2 = document.createElement('label'); row.appendChild(label2);
+                               var unit = count == 1 ? 'copy' : 'copies';
+                               label2.setAttribute('value',count + ' ' + unit);
+                       }
+                       var hbox = document.createElement('hbox'); 
+                       vbox.appendChild(hbox);
+               } catch(E) {
+                       g.error.sdump('D_ERROR','copy editor: ' + E + '\n');
+               }
+       }
+
+       /******************************************************************************************************/
+       /* Prepare the right panel, which is different for 1-copy view and multi-copy view */
+
+       if (g.copies.length == 1) {
+
+               /******************************************************************************************************/
+               /* 1-copy mode has a single groupbox and each field is a row on a grid */
+
+               var groupbox; var caption; var vbox; var grid; var rows;
+               groupbox = document.createElement('groupbox'); rp.appendChild(groupbox);
+               caption = document.createElement('caption'); groupbox.appendChild(caption);
+               caption.setAttribute('label','Fields');
+               vbox = document.createElement('vbox'); groupbox.appendChild(vbox);
+               grid = util.widgets.make_grid( [ {}, { 'flex' : 1 } ] ); vbox.appendChild(grid);
+               grid.setAttribute('flex','1');
+               rows = grid.lastChild;
+
+               /******************************************************************************************************/
+               /* Loop through the field names */
+
+               for (var i = 0; i < g.right_pane_field_names.length; i++) {
+                       try {
+                               var f = g.right_pane_field_names[i]; var fn = f[0];
+                               var row;
+
+                               /**************************************************************************************/
+                               /* Loop through each value for the field */
+
+                               for (var j in g.summary[fn]) {
+                                       var value = j; var count = g.summary[fn][j];
+                                       row = document.createElement('row'); rows.appendChild(row);
+                                       var label0 = document.createElement('label'); row.appendChild(label0);
+                                       label0.setAttribute('value',fn);
+                                       label0.setAttribute('style','font-weight: bold');
+                                       var label1 = document.createElement('label'); row.appendChild(label1);
+                                       if (g.special_exception[ fn ]) {
+                                               g.special_exception[ fn ]( label1, value );
+                                       } else {
+                                               label1.setAttribute('value',value);
+                                       }
+
+                               }
+
+                               /**************************************************************************************/
+                               /* Render the input widget */
+
+                               var hbox = document.createElement('hbox'); 
+                               row.setAttribute('style','border-bottom: dotted black thin');
+                               row.appendChild(hbox);
+                               if (f[1].input && g.edit) {
+                                       try {
+                                               var spacer = document.createElement('spacer'); hbox.appendChild(spacer);
+                                               spacer.setAttribute('flex','1');
+                                               var x; eval( f[1].input );
+                                               if (x) hbox.appendChild(x);
+
+                                       } catch(E) {
+                                               g.error.sdump('D_ERROR',E + '\n');
+                                       }
+                               }
+
+                       } catch(E) {
+                               g.error.sdump('D_ERROR','copy editor: ' + E + '\n');
+                       }
+               }
+
+       } else {
+
+               /******************************************************************************************************/
+               /* multi-copy mode has a groupbox for each field */
+
+               var groupbox; var caption; var vbox; var grid; var rows;
+               
+               /******************************************************************************************************/
+               /* Loop through the field names */
+
+               for (var i = 0; i < g.right_pane_field_names.length; i++) {
+                       try {
+                               var f = g.right_pane_field_names[i]; var fn = f[0];
+                               groupbox = document.createElement('groupbox'); rp.appendChild(groupbox);
+                               caption = document.createElement('caption'); groupbox.appendChild(caption);
+                               caption.setAttribute('label',fn);
+                               vbox = document.createElement('vbox'); groupbox.appendChild(vbox);
+                               grid = util.widgets.make_grid( [ { 'flex' : 1 }, {}, {} ] ); vbox.appendChild(grid);
+                               grid.setAttribute('flex','1');
+                               rows = grid.lastChild;
+                               var row;
+                               
+                               /**************************************************************************************/
+                               /* Loop through each value for the field */
+
+                               for (var j in g.summary[fn]) {
+                                       var value = j; var count = g.summary[fn][j];
+                                       row = document.createElement('row'); rows.appendChild(row);
+                                       var label1 = document.createElement('label'); row.appendChild(label1);
+                                       if (g.special_exception[ fn ]) {
+                                               g.special_exception[ fn ]( label1, value );
+                                       } else {
+                                               label1.setAttribute('value',value);
+                                       }
+                                       var label2 = document.createElement('label'); row.appendChild(label2);
+                                       var unit = count == 1 ? 'copy' : 'copies';
+                                       label2.setAttribute('value',count + ' ' + unit);
+
+                               }
+                               var hbox = document.createElement('hbox'); 
+                               vbox.appendChild(hbox);
+
+                               /**************************************************************************************/
+                               /* Render the input widget */
+
+                               if (f[1].input && g.edit) {
+                                       try {
+                                               var spacer = document.createElement('spacer'); hbox.appendChild(spacer);
+                                               spacer.setAttribute('flex','1');
+                                               var x; eval( f[1].input );
+                                               if (x) hbox.appendChild(x);
+
+                                       } catch(E) {
+                                               g.error.sdump('D_ERROR',E + '\n');
+                                       }
+                               }
+                       } catch(E) {
+                               g.error.sdump('D_ERROR','copy editor: ' + E + '\n');
+                       }
+               }
+       }
+}
+
+/******************************************************************************************************/
+/* store the copies in the global xpcom stash */
+
+g.stash_and_close = function() {
+       g.data.temp = js2JSON( g.copies );
+       g.error.sdump('D_CAT','in modal window, g.data.temp = \n' + g.data.temp + '\n');
+       g.data.stash('temp');
+       window.close();
+}
+
index 85eb9b5..8fe1475 100644 (file)
        <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
        <!-- BEHAVIOR -->
         <script type="text/javascript">
-               var myPackageDir = 'open_ils_staff_client'; var IAMXUL = true; var g = {};
+               var myPackageDir = 'open_ils_staff_client'; var IAMXUL = true;
        </script>
         <scripts id="openils_util_scripts"/>
 
        <script type="text/javascript" src="/xul/server/main/JSAN.js"/>
-       <script>
-       <![CDATA[
-
-               g.apply = function(field,value) {
-                       g.error.sdump('D_TRACE','field = ' + field + '  value = ' + value + '\n');
-                       for (var i = 0; i < g.copies.length; i++) {
-                               var copy = g.copies[i];
-                               try {
-                                       copy[field]( value ); copy.ischanged('1');
-                               } catch(E) {
-                                       alert(E);
-                               }
-                       }
-               }
-
-               g.special_exception = {
-                       'Call Number' : function(label,value) {
-                               if (value>0) { /* an existing call number */
-                                       g.network.request(
-                                               api.FM_ACN_RETRIEVE.app,
-                                               api.FM_ACN_RETRIEVE.method,
-                                               [ value ],
-                                               function(req) {
-                                                       var cn = '??? id = ' + value;
-                                                       try {
-                                                               cn = req.getResultObject().label();
-                                                       } catch(E) {
-                                                               g.error.sdump('D_ERROR','callnumber retrieve: ' + E);
-                                                       }
-                                                       label.setAttribute('value',cn);
-                                               }
-                                       );
-i                              } else { /* a yet to be created call number */
-                                       if (g.callnumbers) {
-                                               label.setAttribute('value',g.callnumbers[value]);
-                                       }
-                               }
-                       },
-                       'Creator' : function(label,value) {
-                               g.network.request(
-                                       api.FM_AU_RETRIEVE_VIA_ID.app,
-                                       api.FM_AU_RETRIEVE_VIA_ID.method,
-                                       [ g.session, value ],
-                                       function(req) {
-                                               var p = '??? id = ' + value;
-                                               try {
-                                                       p = req.getResultObject();
-                                                       p = p.card().barcode() + ' : ' + p.family_name();
-
-                                               } catch(E) {
-                                                       g.error.sdump('D_ERROR','patron retrieve: ' + E);
-                                               }
-                                               label.setAttribute('value',p);
-                                       }
-                               );
-                       },
-                       'Last Editor' : function(label,value) {
-                               g.network.request(
-                                       api.FM_AU_RETRIEVE_VIA_ID.app,
-                                       api.FM_AU_RETRIEVE_VIA_ID.method,
-                                       [ g.session, value ],
-                                       function(req) {
-                                               var p = '??? id = ' + value;
-                                               try {
-                                                       p = req.getResultObject();
-                                                       p = p.card().barcode() + ' : ' + p.family_name();
-
-                                               } catch(E) {
-                                                       g.error.sdump('D_ERROR','patron retrieve: ' + E);
-                                               }
-                                               label.setAttribute('value',p);
-                                       }
-                               );
-                       }
-
-               }
-
-               g.readonly_field_names = [
-                       [
-                               "Barcode",               
-                               {
-                                       render: 'fm.barcode();',
-                               }
-                       ], 
-                       [
-                               "Call Number",  
-                               {
-                                       render: 'fm.call_number();',
-                               }
-                       ],
-               ];
-               g.editable_field_names = [
-                        [
-                               "Circulate as Type",    
-                               {       
-                                       render: 'fm.circ_as_type();',
-                                       input: 'x = document.createElement("textbox"); x.addEventListener("change",function(ev) { g.apply("circ_as_type",ev.target.value); }, false);',
-                               } 
-                       ],
-                       [
-                               "Circulation Library",          
-                               {       
-                                       render: 'fm.circ_lib().shortname();',
-                                       input: '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("command",function(ev) { g.apply("circ_lib",ev.target.value); }, false);',
-                               } 
-                       ],
-                       [
-                               "Circulation Modifier",
-                               {       
-                                       render: 'fm.circ_modifier();',
-                                       input: 'x = document.createElement("textbox"); x.addEventListener("change",function(ev) { g.apply("circ_modifier",ev.target.value); }, false);',
-                               }
-                       ],
-                       [
-                               "Circulate?",
-                               {       
-                                       render: 'fm.circulate() ? "Yes" : "No";',
-                                       input: 'x = util.widgets.make_menulist( [ [ "Yes", "1" ], [ "No", "0" ] ] ); x.addEventListener("command",function(ev) { g.apply("circulate",ev.target.value); }, false);',
-                               }
-                       ],
-                       [
-                               "Copy Number",
-                               { 
-                                       render: 'fm.copy_number();',
-                                       input: 'x = document.createElement("textbox"); x.addEventListener("change",function(ev) { g.apply("copy_number",ev.target.value); }, false);',
-                               }
-                       ],
-                       [
-                               "Creation Date",
-                               { 
-                                       render: 'util.date.formatted_date( fm.create_date(), "%F");',
-                               }
-                       ],
-                       [
-                               "Creator",
-                               { 
-                                       render: 'fm.creator();',
-                               }
-                       ],
-                       [
-                               "Deposit?",
-                               { 
-                                       render: 'fm.deposit() ? "Yes" : "No";',
-                                       input: 'x = util.widgets.make_menulist( [ [ "Yes", "1" ], [ "No", "0" ] ] ); x.addEventListener("command",function(ev) { g.apply("deposit",ev.target.value); }, false);',
-                               }
-                       ],
-                       [
-                               "Deposit Amount",
-                               { 
-                                       render: 'util.money.sanitize( fm.deposit_amount() );',
-                                       input: 'x = document.createElement("textbox"); x.addEventListener("change",function(ev) { g.apply("deposit_amount",ev.target.value); }, false);',
-                               }
-                       ],
-                       [
-                               "Last Edit Date",
-                               { 
-                                       render: 'util.date.formatted_date( fm.edit_date(), "%F");',
-                               }
-                       ],
-                       [
-                               "Last Editor",
-                               {
-                                       render: 'fm.editor();',
-                               }
-                       ],
-                       [
-                               "Fine Level",
-                               {
-                                       render: 'switch(fm.fine_level()){ case 1: "Low"; break; case 2: "Normal"; break; case 3: "High"; break; }',
-                                       input: 'x = util.widgets.make_menulist( [ [ "Low", "1" ], [ "Normal", "2" ], [ "High", "3" ] ] ); x.addEventListener("command",function(ev) { g.apply("fine_level",ev.target.value); }, false);',
-                               }
-                       ],
-                       [
-                               "Holdable?",
-                               { 
-                                       render: 'fm.holdable() ? "Yes" : "No";', 
-                                       input: 'x = util.widgets.make_menulist( [ [ "Yes", "1" ], [ "No", "0" ] ] ); x.addEventListener("command",function(ev) { g.apply("holdable",ev.target.value); }, false);',
-                               }
-                       ],
-                       [
-                               "Loan Duration",
-                               { 
-                                       render: 'switch(fm.loan_duration()){ case 1: "Short"; break; case 2: "Normal"; break; case 3: "Long"; break; }',
-                                       input: 'x = util.widgets.make_menulist( [ [ "Short", "1" ], [ "Normal", "2" ], [ "Long", "3" ] ] ); x.addEventListener("command",function(ev) { g.apply("loan_duration",ev.target.value); }, false);',
-
-                               }
-                       ],
-                       [
-                               "Shelving Location",
-                               { 
-                                       render: 'fm.location().name();', 
-                                       input: 'x = util.widgets.make_menulist( util.functional.map_list( g.data.list.acpl, function(obj) { return [ obj.name(), obj.id() ]; }).sort()); x.addEventListener("command",function(ev) { g.apply("location",ev.target.value); }, false);',
-
-                               }
-                       ],
-                       [
-                               "OPAC Visible?",
-                               { 
-                                       render: 'fm.opac_visible() ? "Yes" : "No";', 
-                                       input: 'x = util.widgets.make_menulist( [ [ "Yes", "1" ], [ "No", "0" ] ] ); x.addEventListener("command",function(ev) { g.apply("opac_visible",ev.target.value); }, false);',
-                               }
-                       ],
-                       [
-                               "Price",
-                               { 
-                                       render: 'util.money.sanitize( fm.price() );', 
-                                       input: 'x = document.createElement("textbox"); x.addEventListener("change",function(ev) { g.apply("deposit_amount",ev.target.value); }, false);',
-                               }
-                       ],
-                       [
-                               "Reference?",
-                               { 
-                                       render: 'fm.ref() ? "Yes" : "No";', 
-                                       input: 'x = util.widgets.make_menulist( [ [ "Yes", "1" ], [ "No", "0" ] ] ); x.addEventListener("command",function(ev) { g.apply("ref",ev.target.value); }, false);',
-                               }
-                       ],
-                       [
-                               "Status",
-                               { 
-                                       render: 'fm.status().name();', 
-                                       input: 'x = util.widgets.make_menulist( util.functional.map_list( g.data.list.ccs, function(obj) { return [ obj.name(), obj.id() ]; } ).sort() ); x.addEventListener("command",function(ev) { g.apply("status",ev.target.value); }, false);',
-
-
-                               }
-                       ],
-               ];
-
-               function my_init() {
-                       try {
-                               netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
-                               if (typeof JSAN == 'undefined') { throw( "The JSAN library object is missing."); }
-                               JSAN.errorLevel = "die"; // none, warn, or die
-                               JSAN.addRepository('/xul/server/');
-                               JSAN.use('util.error'); g.error = new util.error();
-                               g.error.sdump('D_TRACE','my_init() for cat/copy_editor.xul');
-
-                               JSAN.use('OpenILS.data'); g.data = new OpenILS.data(); g.data.init({'via':'stash'});
-
-                               g.cgi = new CGI();
-
-                               g.session = g.cgi.param('session') || g.cgi.param('ses');
-
-                               var copy_ids = [];
-                               if (g.cgi.param('copy_ids')) copy_ids = JSON2js( g.cgi.param('copy_ids') );
-                               if (!copy_ids) copy_ids = [];
-                               if (window.xulG && window.xulG.copy_ids) copy_ids = copy_ids.concat( window.xulG.copy_ids );
-
-                               if (g.cgi.param('edit') == '1') { 
-                                       g.edit = true;
-                                       document.getElementById('caption').setAttribute('label','Copy Editor'); 
-                                       document.getElementById('nav').setAttribute('hidden','false'); 
-                               }
-
-                               JSAN.use('util.network'); g.network = new util.network();
-
-                               if (copy_ids.length > 0) g.copies = g.network.request(
-                                       api.FM_ACP_FLESHED_BATCH_RETRIEVE.app,
-                                       api.FM_ACP_FLESHED_BATCH_RETRIEVE.method,
-                                       [ copy_ids ]
-                               );
-
-                               if (!g.copies) g.copies = [];
-                               if (window.xulG && window.xulG.copies) g.copies = g.copies.concat( window.xulG.copies );
-                               if (g.cgi.param('copies')) g.copies = g.copies.concat( JSON2js( g.cgi.param('copies') ) );
-
-                               if (window.xulG && window.xulG.callnumbers) g.callnumbers = window.xulG.callnumbers;
-                               if (g.cgi.param('callnumbers')) g.callnumbers =  JSON2js( g.cgi.param('callnumbers') );
-
-                               g.summarize( g.copies );
-                               g.render();
-
-                       } catch(E) {
-                               var err_msg = "!! This software has encountered an error.  Please tell your friendly " +
-                                       "system administrator or software developer the following:\ncat/copy_editor.xul\n" + E + '\n';
-                               try { g.error.sdump('D_ERROR',err_msg); } catch(E) { dump(err_msg); dump(js2JSON(E)); }
-                               alert(err_msg);
-                       }
-               }
-
-               g.summarize = function( copies ) {
-                       JSAN.use('util.date'); JSAN.use('util.money');
-                       g.summary = {};
-                       g.field_names = g.readonly_field_names.concat( g.editable_field_names );
-                       for (var i = 0; i < g.field_names.length; i++) {
-                               var field_name = g.field_names[i][0];
-                               var render = g.field_names[i][1].render;
-                               g.summary[ field_name ] = {};
-                               for (var j = 0; j < copies.length; j++) {
-                                       var fm = copies[j];
-                                       var cmd = render || ('fm.' + field_name + '();');
-                                       g.error.sdump('D_TRACE',cmd + ' = ');
-                                       var value = '???';
-                                       try { value = eval( cmd ); } catch(E) { g.error.sdump('D_ERROR', E + '\n'); }
-                                       g.error.sdump('D_TRACE',value + '\n');
-                                       if (typeof value == 'object' && value != null) {
-                                               g.error.sdump('D_ERROR','field_name = ' + field_name + '  value = ' + js2JSON(value) + '\n');
-                                               /*
-                                               try { value = value.id(); } catch(E) { 
-                                                       g.error.sdump('D_ERROR',E + '\n'); 
-                                                       try { 
-                                                               g.error.sdump('D_ERROR',js2JSON(value) + '\n'); 
-                                                       } catch(F) { 
-                                                               g.error.sdump('D_ERROR',F); 
-                                                       }
-                                               }
-                                               */
-                                       }
-                                       if (g.summary[ field_name ][ value ]) {
-                                               g.summary[ field_name ][ value ]++;
-                                       } else {
-                                               g.summary[ field_name ][ value ] = 1;
-                                       }
-                               }
-                       }
-                       g.error.sdump('D_ERROR','summary = ' + js2JSON(g.summary) + '\n');
-               }
-
-               g.render = function() {
-
-                       JSAN.use('util.widgets'); JSAN.use('util.date'); JSAN.use('util.money'); JSAN.use('util.functional');
-
-                       /* clear */
-                       var cns = document.getElementById('call_number_summary');
-                       util.widgets.remove_children( cns );
-                       var bcs = document.getElementById('barcode_summary');
-                       util.widgets.remove_children( bcs );
-                       var rp = document.getElementById('right_pane');
-                       util.widgets.remove_children( rp );
-
-                       var grid = util.widgets.make_grid( [ { 'flex' : '1' } ] );
-                       cns.appendChild(grid);
-                       for (var i in g.summary['Call Number']) {
-                               var cn_id = i; var count = g.summary['Call Number'][i];
-                               var row = document.createElement('row'); grid.lastChild.appendChild(row);
-                               var cn_label = document.createElement('label'); row.appendChild(cn_label);
-                               g.special_exception['Call Number']( cn_label, cn_id );
-                               var count_label = document.createElement('label'); row.appendChild(count_label);
-                               var unit = count == 1 ? 'copy' : 'copies';
-                               count_label.setAttribute('value',count + ' ' + unit);
-                       }
-
-                       for (var i in g.summary['Barcode']) {
-                               var bc = i;
-                               var hbox = document.createElement('hbox'); bcs.appendChild(hbox);
-                               var bc_label = document.createElement('label'); hbox.appendChild(bc_label);
-                               bc_label.setAttribute('value',bc);
-                       }
-
-               
-                       var groupbox; var caption; var vbox; var grid; var rows;
-                       if (g.copies.length == 1) {
-                               groupbox = document.createElement('groupbox'); rp.appendChild(groupbox);
-                               caption = document.createElement('caption'); groupbox.appendChild(caption);
-                               caption.setAttribute('label','Fields');
-                               vbox = document.createElement('vbox'); groupbox.appendChild(vbox);
-                               grid = util.widgets.make_grid( [ {}, { 'flex' : 1 } ] ); vbox.appendChild(grid);
-                               grid.setAttribute('flex','1');
-                               rows = grid.lastChild;
-                       }       
-                       for (var i = 0; i < g.editable_field_names.length; i++) {
-                               try {
-                                       var f = g.editable_field_names[i]; var fn = f[0];
-                                       if (g.copies.length != 1) {
-                                               groupbox = document.createElement('groupbox'); rp.appendChild(groupbox);
-                                               caption = document.createElement('caption'); groupbox.appendChild(caption);
-                                               caption.setAttribute('label',fn);
-                                               vbox = document.createElement('vbox'); groupbox.appendChild(vbox);
-                                               grid = util.widgets.make_grid( [ { 'flex' : 1 }, {}, {} ] ); vbox.appendChild(grid);
-                                               grid.setAttribute('flex','1');
-                                               rows = grid.lastChild;
-                                       }
-                                       var row;
-                                       for (var j in g.summary[fn]) {
-                                               var value = j; var count = g.summary[fn][j];
-                                               row = document.createElement('row'); rows.appendChild(row);
-                                               if (g.copies.length==1) {
-                                                       var label0 = document.createElement('label'); row.appendChild(label0);
-                                                       label0.setAttribute('value',fn);
-                                                       label0.setAttribute('style','font-weight: bold');
-                                               }
-                                               var label1 = document.createElement('label'); row.appendChild(label1);
-                                               if (g.special_exception[ fn ]) {
-                                                       g.special_exception[ fn ]( label1, value );
-                                               } else {
-                                                       label1.setAttribute('value',value);
-                                               }
-                                               if (g.copies.length!=1) {
-                                                       var label2 = document.createElement('label'); row.appendChild(label2);
-                                                       var unit = count == 1 ? 'copy' : 'copies';
-                                                       label2.setAttribute('value',count + ' ' + unit);
-                                               }
-                                       }
-                                       var hbox = document.createElement('hbox'); 
-                                       if (g.copies.length==1) {
-                                               row.setAttribute('style','border-bottom: dotted black thin');
-                                               row.appendChild(hbox);
-                                       } else {
-                                               vbox.appendChild(hbox);
-                                       }
-                                       if (f[1].input && g.edit) {
-                                               try {
-                                                       var spacer = document.createElement('spacer'); hbox.appendChild(spacer);
-                                                       spacer.setAttribute('flex','1');
-                                                       var x; eval( f[1].input );
-                                                       if (x) hbox.appendChild(x);
-
-                                               } catch(E) {
-                                                       g.error.sdump('D_ERROR',E + '\n');
-                                               }
-                                       }
-                               } catch(E) {
-                                       g.error.sdump('D_ERROR','copy editor: ' + E + '\n');
-                               }
-                       }
-               }
-
-               g.stash_and_close = function() {
-                       g.data.temp = js2JSON( g.copies );
-                       g.error.sdump('D_CAT','in modal window, g.data.temp = \n' + g.data.temp + '\n');
-                       g.data.stash('temp');
-                       window.close();
-               }
-
-       ]]>
-       </script>
+       <script type="text/javascript" src="copy_editor.js"/>
 
        <groupbox flex="1" style="overflow: auto">
                <caption id="caption" label="Copy Viewer"/>